xrd_fit.py ダウンロード/コピー

xrd_fit.py をダウンロード

xrd_fit.py
xrd_fit.py
   1"""
   2X線回折(XRD)データ解析スクリプト
   3
   4概要:
   5このスクリプトは、実験的に測定されたX線回折(XRD)パターンと、結晶構造情報ファイル(CIF)から計算された理論的なXRDパターンを比較・分析するためのツールです。
   6主な機能として、パターンプロット、相関分析、オーバーラップチェック、およびLASSO回帰による混合相の定量フィッティングを提供します。
   7
   8詳細説明:
   9本スクリプトは、様々なモードでXRDデータ解析を実行します。
  10- `plot_all`: 実験XRDパターンと各CIF由来の理論XRDパターンを個別のグラフに表示します。
  11- `plot_one`: 実験XRDパターンと各CIF由来の回折ピーク位置を一つのグラフに表示します。
  12- `plot_one2`: 実験XRDパターンと各CIF由来の理論XRDパターンを一つのグラフに表示します。
  13- `overwrap`: 異なるCIFパターン間のオーバーラップ(重なり)を評価します。
  14- `CIFcorrelation`: CIFパターン間の相関係数を評価します。
  15- `correlation`: 実験パターンとCIFパターン間の相関係数を評価します。
  16- `fit`: LASSO回帰を用いて実験パターンを背景とCIFパターンでフィッティングし、各相の寄与を定量化します。
  17スマアリング(ブロードニング)効果や背景処理もサポートしています。
  18
  19関連リンク:
  20:doc:`xrd_fit_usage`
  21"""
  22
  23import os
  24import sys
  25import glob
  26import numpy as np
  27from numpy import exp, log, sin, cos, tan, arcsin, arccos, arctan, pi
  28from scipy.special import legendre
  29from scipy.interpolate import interp1d
  30from matplotlib import pyplot as plt
  31import pandas as pd
  32from sklearn.preprocessing import StandardScaler
  33from sklearn.linear_model import Lasso
  34from sklearn.metrics import mean_absolute_error, mean_squared_error
  35
  36
  37from tklib.tkapplication import tkApplication
  38from tklib.tkfile import tkFile
  39from tklib.tkutils import getarg, getintarg, getfloatarg, pint, pfloat, split_file_path, replace_path
  40from tklib.tkvariousdata import tkVariousData
  41from tklib.tksci.tksci import Gaussian, Lorentzian, GaussLorentz
  42from tklib.tksci.tkmatrix import make_matrix1, make_matrix2, make_matrix3
  43from tklib.tkcrystal.tkxrd import Xray_wavelengths
  44from tklib.tkgraphic.tkplotevent import tkPlotEvent
  45
  46
  47# Xray_wavelengths
  48# URL: https://github.com/materialsproject/pymatgen/blob/v2023.5.10/pymatgen/analysis/diffraction/xrd.py
  49# 'CuKa', 'CuKa2', 'CuKa1', 'CuKb1', 'MoKa', 'MoKa2', 'MoKa1', 'MoKb1',
  50# 'CrKa', 'CrKa2', 'CrKa1', 'CrKb1', 'FeKa', 'FeKa2', 'FeKa1', 'FeKb1', 'CoKa', 'CoKa2', 'CoKa1', 'CoKb1',
  51# 'AgKa', 'AgKa2', 'AgKa1', 'AgKb1'
  52
  53
  54
  55usage_str = '''
  56"  (i) usage: python {} mode input_path cif_dir wavelength Q2min Q2max Q2step fwhm yscale BG_order LASSO_alpha".format(sys.argv[0])
  57"         ex: python {} plot_all  {} {} {} {} {} {} {} {}".format(sys.argv[0], cparams.infile, cparams.wavelength, cparams.xmin, cparams.xmax, cparams.xstep, cparams.fwhm, cparams.yscale, cparams.BGorder, cparams.alpha)
  58"         ex: python {} plot_one  {} {} {} {} {} {} {} {}".format(sys.argv[0], cparams.infile, cparams.wavelength, cparams.xmin, cparams.xmax, cparams.xstep, cparams.fwhm, cparams.yscale, cparams.BGorder, cparams.alpha)
  59"         ex: python {} plot_one2 {} {} {} {} {} {} {} {} {}".format(sys.argv[0], cparams.infile, cparams.wavelength, cparams.xmin, cparams.xmax, cparams.xstep, cparams.fwhm, cparams.yscale, cparams.BGorder, cparams.alpha)
  60"         ex: python {} fit {} {} {} {} {} {} {} {} {}".format(sys.argv[0], cparams.infile, cparams.wavelength, cparams.xmin, cparams.xmax, cparams.xstep, cparams.fwhm, cparams.yscale, cparams.BGorder, cparams.alpha)
  61'''[1:-1]
  62
  63
  64
  65#================================
  66# global parameters
  67#================================
  68module_names = []
  69modules = []
  70
  71markers = ['o', 's', '+', 'x', 'D', 'v', '^', '<', '>', '8', 'h', 'H']
  72colors  = ['black', 'red', 'blue', 'darkgreen', 'darkorange', 'hotpink', 'lightgreen', 'cyan', 'yellow', 'magenta', 'chocolate', 
  73           'navy', 'slategray', 'olive' ]
  74
  75figsize       = (12, 8)
  76figsize_one   = (10, 6)
  77figsize_coeff = (8, 6)
  78fontsize            = 12
  79legend_fontsize     = 8
  80legend_fontsize_one = 12
  81
  82
  83#=============================
  84# Treat argments
  85#=============================
  86def usage(app = None, cparams = None):
  87    """
  88    コマンドライン引数の使い方を表示する。
  89
  90    詳細説明:
  91    アプリケーションとパラメータオブジェクトから利用方法文字列を取得し、整形して標準出力に出力する。
  92
  93    :param app: tkApplicationのインスタンス。
  94    :param cparams: 設定パラメータを保持するオブジェクト。
  95    :returns: なし
  96    """
  97    cparams = app.get_params()
  98#    app.usage(infile = cparams.infile)
  99    for s in app.usage_str.split('\n'):
 100        cmd = 'print({})'.format(s.rstrip())
 101        eval(cmd)
 102
 103def initialize(app = None, cparams = None):
 104    """
 105    アプリケーションのパラメータを初期値で設定する。
 106
 107    詳細説明:
 108    デバッグフラグ、プラグインディレクトリ、モード、入力ファイルパス、CIFファイルパス、X線波長、
 109    2θ範囲、FWHM、ガウス比率、Yスケール、背景次数、LASSOのalphaなどの初期値を設定する。
 110
 111    :param app: tkApplicationのインスタンス。
 112    :param cparams: 設定パラメータを保持するオブジェクト。
 113    :returns: なし
 114    """
 115    cparams.debug = 0
 116    cparams.findvalidstructure = True
 117
 118    cparams.plugin_dir = 'filter'
 119
 120    cparams.mode = 'plot'
 121
 122    cparams.infile    = '*.txt'
 123    cparams.cif_files = 'data/*.*'
 124
 125    cparams.beam = 'X-ray'
 126    cparams.wavelength = "CuKa"
 127    cparams.xmin  = 20.0
 128    cparams.xmax  = 120.0
 129    cparams.xstep = 0.02
 130    cparams.fwhm   = 0.2
 131    cparams.Gfraction = 0.5
 132
 133    cparams.fwhm_smear      = 0.0
 134    cparams.Gfraction_smear = 0.0
 135
 136    cparams.yscale  = 'linear'
 137    cparams.BGorder = 3
 138    cparams.alpha   = 0.1
 139
 140def update_vars(app = None, cparams = None):
 141    """
 142    コマンドライン引数に基づいて設定パラメータを更新する。
 143
 144    詳細説明:
 145    `sys.argv` から引数を取得し、`cparams` オブジェクトの対応する属性に設定する。
 146    数値型引数は適切な型に変換される。
 147
 148    :param app: tkApplicationのインスタンス。
 149    :param cparams: 設定パラメータを保持するオブジェクト。
 150    :returns: なし
 151    """
 152#    if getarg(2, None) is None:
 153#        app.terminate(usage = usage)
 154    
 155    cparams.mode            = getarg(1, cparams.mode)
 156    cparams.plugin_dir      = getarg(2, cparams.plugin_dir)
 157    cparams.infile          = getarg(3, cparams.infile)
 158    cparams.cif_files       = getarg(4, cparams.cif_files)
 159    cparams.wavelength      = getarg(5, cparams.wavelength)
 160    cparams.xmin            = getfloatarg(6, cparams.xmin)
 161    cparams.xmax            = getfloatarg(7, cparams.xmax)
 162    cparams.xstep           = getfloatarg(8, cparams.xstep)
 163    cparams.fwhm            = getfloatarg(9, cparams.fwhm)
 164    cparams.Gfraction       = getfloatarg(10, cparams.Gfraction)
 165    cparams.fwhm_smear      = getfloatarg(11, cparams.fwhm_smear)
 166    cparams.Gfraction_smear = getfloatarg(12, cparams.Gfraction_smear)
 167    cparams.yscale          = getarg     (13, cparams.yscale)
 168    cparams.BGorder         = getintarg  (14, cparams.BGorder)
 169    cparams.alpha           = getfloatarg(15, cparams.alpha)
 170
 171def read_file(path, app, cparams):
 172    """
 173    指定されたパスのファイルを読み込み、その内容を処理する。
 174
 175    詳細説明:
 176    登録されているモジュールを順に試行し、ファイルのタイプを判別する。
 177    ファイルタイプが一致したモジュールを使用してデータを読み込み、必要に応じて変換する。
 178
 179    :param path: str. 読み込むファイルのパス。
 180    :param app: tkApplicationのインスタンス。
 181    :param cparams: 設定パラメータを保持するオブジェクト。
 182    :returns: tuple. (`module`, `inf`). `module` はファイルを読み込んだモジュールオブジェクト、
 183              `inf` は読み込まれたデータを含む辞書。ファイルが読み込めない場合は `(None, None)`.
 184    """
 185    module = None
 186    for i in range(len(modules)):
 187        name = module_names[i]
 188        m = modules[i]
 189
 190        file_type  = m.check_file_type(path, app = app, cparams = cparams)
 191#        file_type  = app.call(m, "check_file_type", path, app = app, cparams = cparams)
 192        print(f"try [{name}] for [{path}]: file_type={file_type}")
 193        if file_type is not None and 'Error' not in file_type:
 194            print("   type matched.")
 195            module = m
 196            break
 197
 198    if module is None:
 199        return None, None
 200
 201#    inf = app.call(module, "read_data", path, app = app, cparams = cparams)
 202    inf = module.read_data(path, app = app, cparams = cparams)
 203
 204    return module, inf
 205
 206def read_all_files(app, cparams, input_only = False):
 207    """
 208    入力XRDファイルとすべてのCIFファイルを読み込む。
 209
 210    詳細説明:
 211    `cparams.infile` で指定された入力XRDファイルを読み込み、その2θ範囲に基づいて解析範囲を調整する。
 212    `cparams.cif_files` で指定されたマスクに一致するCIFファイルをすべて読み込み、変換処理を行う。
 213    `input_only` が`True`の場合、入力ファイルのみを読み込む。
 214
 215    :param app: tkApplicationのインスタンス。
 216    :param cparams: 設定パラメータを保持するオブジェクト。
 217    :param input_only: bool. Trueの場合、入力ファイルのみを読み込む。
 218    :returns: dict. 読み込まれたモジュールとデータを含む辞書。
 219              キーは "module_input", "module_cif", "inf_input", "inf_cif_list"。
 220    """
 221    print("")
 222    print(f"read_all_files(): Read input file [{cparams.infile}]")
 223    module_input, inf_input = read_file(cparams.infile, app, cparams)
 224    if module_input:
 225#        module_input.print_data(inf_input)
 226        inf_input = module_input.convert(inf_input, cparams = cparams)
 227#        save_data([cparams.outfile], inf_input, cparams = cparams)
 228#        app.call(module_input, "plot_data", inf_input, cparams = cparams)
 229    else:
 230        app.terminate(f"Error in read_all_files(): Could not read [{cparams.infile}]", pause = True)
 231
 232    xQ2_infile  = inf_input["data_list"][0]
 233    xmin = min(xQ2_infile)
 234    xmax = max(xQ2_infile)
 235    xstep = xQ2_infile[1] - xQ2_infile[0]
 236    print(f"  2Theta range: {xmin} - {xmax}, {xstep} step")
 237    print(f"  fwhm: {cparams.fwhm}")
 238    print(f"  Gaussian fraction: {cparams.Gfraction}")
 239
 240    cparams.xmin  = max([cparams.xmin, xmin])
 241    cparams.xmax  = min([cparams.xmax, xmax])
 242    cparams.xstep = xstep
 243    print(f"2Theta range to be calculated: {cparams.xmin} - {cparams.xmax} degrees, {cparams.xstep} step")
 244
 245    if input_only:
 246        inf = {
 247            "module_input": module_input,
 248            "inf_input"   : inf_input,
 249            }
 250        return inf
 251
 252    cif_mask = cparams.cif_files
 253    files = glob.glob(cif_mask)
 254    print("")
 255    print(f"Read cif and xlsx files from [{cif_mask}]")
 256    inf_cif_list = []
 257    module_cif = None
 258    for f in files:
 259        print("")
 260        print(f"  Read [{f}]")
 261        dirname, basename, filebody, ext = split_file_path(f)
 262        if len(filebody) == 0 or filebody[0] == '~':
 263            print(f"    [{basename}] has '~' at the first character. may be a temprary file. skip")
 264            continue
 265        if '-out.' in basename.lower():
 266            print(f"    [{basename}] include '-out.'. maybe an output file of some program. skip")
 267            continue
 268
 269        module_cif, inf_cif = read_file(f, app, cparams)
 270        if module_cif:
 271            print(f"    File [{f}] is red by [{module_cif.name}] module")
 272#            app.call(module_cif, "print_data", inf_cif)
 273            inf = app.call(module_cif, "convert", inf_cif, app = app, cparams = cparams)
 274#            app.call(module_cif, "plot_data", inf_cif, cparams = cparams)
 275
 276        inf_cif_list.append(inf_cif)
 277
 278    inf = {
 279        "module_input": module_input,
 280        "module_cif"  : module_cif,
 281        "inf_input"   : inf_input,
 282        "inf_cif_list": inf_cif_list,
 283        }
 284
 285    return inf
 286
 287def max_none(x):
 288    """
 289    None値を含むリストまたはイテラブルの最大値を返す。
 290
 291    詳細説明:
 292    入力されたリスト `x` の要素からNoneを除外し、残りの数値の最大値を計算する。
 293    数値要素が一つもない場合は、非常に小さい負の値を返す。
 294
 295    :param x: list or iterable. 数値とNoneを含むリストまたはイテラブル。
 296    :returns: float. 最大値。
 297    """
 298    m = -1.0e100
 299    for v in x:
 300        if v is not None and m < v:
 301            m = v
 302    return m
 303
 304def min_none(x):
 305    """
 306    None値を含むリストまたはイテラブルの最小値を返す。
 307
 308    詳細説明:
 309    入力されたリスト `x` の要素からNoneを除外し、残りの数値の最小値を計算する。
 310    数値要素が一つもない場合は、非常に大きい正の値を返す。
 311
 312    :param x: list or iterable. 数値とNoneを含むリストまたはイテラブル。
 313    :returns: float. 最小値。
 314    """
 315    m = 1.0e100
 316    for v in x:
 317        if v is not None and m > v:
 318            m = v
 319    return m
 320
 321def normalize_none(l, Amin = 0.0, Amax = 1.0, vmin = None, vmax = None):
 322    """
 323    None値を含むリストの要素を指定された範囲に正規化する。
 324
 325    詳細説明:
 326    リスト `l` の数値要素を `[vmin, vmax]` の範囲から `[Amin, Amax]` の範囲に線形変換して正規化する。
 327    `vmin` または `vmax` が指定されない場合は、リスト内のNone以外の要素から自動的に計算される。
 328    None値は変更されない。
 329
 330    :param l: list. 数値とNoneを含むリスト。
 331    :param Amin: float. 正規化後の最小値。デフォルトは 0.0。
 332    :param Amax: float. 正規化後の最大値。デフォルトは 1.0。
 333    :param vmin: float, optional. 元データの最小値。指定しない場合は自動計算。
 334    :param vmax: float, optional. 元データの最大値。指定しない場合は自動計算。
 335    :returns: list. 正規化されたリスト。
 336    """
 337    if vmax is None:
 338        vmax = max_none(l)
 339    if vmin is None:
 340        vmin = min_none(l)
 341    if vmax - vmin == 0.0:
 342        vmax = vmin + 1.0
 343
 344    for i in range(len(l)):
 345        if l[i] is None:
 346            continue
 347
 348        l[i] = (l[i] - vmin) / (vmax - vmin) * (Amax - Amin) + Amin
 349
 350    return l
 351
 352def normalize(l, Amin = 0.0, Amax = 1.0, vmin = None, vmax = None):
 353    """
 354    数値リストの要素を指定された範囲に正規化する。
 355
 356    詳細説明:
 357    リスト `l` の要素を `[vmin, vmax]` の範囲から `[Amin, Amax]` の範囲に線形変換して正規化する。
 358    `vmin` または `vmax` が指定されない場合は、リスト内の要素から自動的に計算される。
 359    入力リストがNoneの場合はNoneを返す。
 360
 361    :param l: list. 数値のリスト。
 362    :param Amin: float. 正規化後の最小値。デフォルトは 0.0。
 363    :param Amax: float. 正規化後の最大値。デフォルトは 1.0。
 364    :param vmin: float, optional. 元データの最小値。指定しない場合は自動計算。
 365    :param vmax: float, optional. 元データの最大値。指定しない場合は自動計算。
 366    :returns: list or None. 正規化されたリスト、または入力がNoneの場合はNone。
 367    """
 368    if l is None:
 369        return None
 370        
 371    if vmax is None:
 372        vmax = max(l)
 373    if vmin is None:
 374        vmin = min(l)
 375    if vmax - vmin == 0.0:
 376        vmax = vmin + 1.0
 377
 378    for i in range(len(l)):
 379        l[i] = (l[i] - vmin) / (vmax - vmin) * (Amax - Amin) + Amin
 380
 381    return l
 382
 383def plot_all(inf, app, cparams):
 384    """
 385    実験XRDパターンとCIFからの理論パターンを個別のサブプロットに表示する。
 386
 387    詳細説明:
 388    入力された実験XRDパターンを最上段のサブプロットに表示し、
 389    その後、読み込まれた各CIFファイルから計算されたXRDパターンをそれぞれ異なるサブプロットに表示する。
 390    各理論パターンには回折ピーク位置もプロットされる。すべてのプロットは共通の2θ軸を共有する。
 391
 392    :param inf: dict. 読み込まれたデータを含む辞書。
 393    :param app: tkApplicationのインスタンス。
 394    :param cparams: 設定パラメータを保持するオブジェクト。
 395    :returns: なし
 396    """
 397    print("")
 398    print("#########################################")
 399    print("  Plot one XRD curve in a different box")
 400    print("#########################################")
 401    module_input = inf["module_input"]
 402    module_cif   = inf["module_cif"]
 403    inf_input    = inf["inf_input"]
 404    inf_cif_list = inf["inf_cif_list"]
 405    
 406    if module_cif is None:
 407        app.terminate(f"Error in plot_all(): CIFファイルが見つかりませんでした", pause = True)
 408
 409    ncif = len(inf_cif_list)
 410    print("")
 411    print("plot")
 412    print(f"yscale: {cparams.yscale}")
 413    print("# of cif data:", ncif)
 414
 415    sample_infile = inf_input["sample_name"]
 416    xQ2_infile  = inf_input["data_list"][0]
 417    yobs_infile = inf_input["data_list"][1]
 418    if len(inf_input["data_list"]) >= 3:
 419        ysim_infile = inf_input["data_list"][2]
 420    else:
 421        ysim_infile = None
 422    
 423    vmax = max(yobs_infile)
 424    vmin = min(yobs_infile)
 425    yobs_infile = normalize(yobs_infile, Amin = 0.0, Amax = 1.0, vmin = vmin, vmax = vmax)
 426    if ysim_infile is not None:
 427        ysim_infile = normalize(ysim_infile, Amin = 0.0, Amax = 1.0, vmin = vmin, vmax = vmax)
 428
 429    fig, axes = plt.subplots(ncif + 1, 1, figsize = figsize, sharex=True, gridspec_kw={'hspace': 0})
 430    plot_event = tkPlotEvent(plt, distance = 'x')
 431    ncolors  = len(colors)
 432    nmarkers = len(markers)
 433
 434    ax0 = axes[0]
 435    ax0.tick_params(labelsize = fontsize)
 436    ax0.set_yticks([])
 437    ax0.set_xlabel(None)
 438    ax_bottom = axes[ncif]
 439    
 440    for i in range(1, ncif + 1):
 441        ax = axes[i]
 442        ax.tick_params(labelsize = fontsize)
 443        if i < ncif:
 444#            ax.set_xticks([])
 445            ax.set_xlabel(None)
 446        ax.set_yticks([])
 447
 448    ax = axes[0]
 449    ax.set_title(f"{sample_infile}")
 450    ax.tick_params(labelsize = fontsize)
 451    ax.plot(xQ2_infile, yobs_infile, label = "obs", color = colors[0], linewidth = 0.5)
 452    if ysim_infile is not None:
 453        ax.plot(xQ2_infile, ysim_infile, label = "sim", color = colors[1], linewidth = 0.3)
 454    ax.axhline(0.0, linestyle = 'dashed', color = 'red', linewidth = 0.5)
 455    if cparams.yscale == 'log':
 456        ax.set_yscale('log')
 457        if ysim_infile is None:
 458            ymax = max(yobs_infile)
 459        else:
 460            ymax = max([max(ysim_infile), max(yobs_infile)])
 461        ax.set_ylim([1.0e-4 * ymax, ax.get_ylim()[1]])
 462    ax.legend(fontsize = legend_fontsize)
 463    
 464    for i in range(1, ncif + 1):
 465        inf_cif = inf_cif_list[i - 1]
 466        xQ2, xrd_cal = inf_cif["conv_data"]
 467        filename = inf_cif["filename"]
 468        dirname, basename, filebody, ext = split_file_path(filename)
 469
 470        src  = inf_cif["diffractions"]["source"]
 471        Q2   = inf_cif["diffractions"]["Q2"]
 472        dhkl = inf_cif["diffractions"]["dhkl"]
 473        hkl  = inf_cif["diffractions"]["hkl"]
 474        mul  = inf_cif["diffractions"].get("mul", None)
 475        if mul is None:
 476            mul = [0 for i in range(len(src))]
 477        Int  = inf_cif["diffractions"]["intensity"]
 478        ndiffractions = len(Q2)
 479
 480        vmax = max(xrd_cal)
 481        vmin = min(xrd_cal)
 482        xrd_cal = normalize(xrd_cal, Amin = 0.0, Amax = 1.0, vmin = vmin, vmax = vmax)
 483        Int     = normalize(Int, Amin = 0.0, Amax = 1.0)
 484
 485        ax = axes[i]
 486        color  = colors[(i - 1) % ncolors]
 487        marker = markers[(i - 1) % nmarkers]
 488        phase = [filebody] * ndiffractions
 489
 490        ax.plot(xQ2, xrd_cal, label = filebody, color = 'black', linewidth = 0.5)
 491        data  = ax.plot(Q2, Int, linestyle = '', marker = marker, markerfacecolor = color, markeredgecolor = color, markersize = 2.0)
 492        data0 = ax0.plot(Q2, Int, linestyle = '', marker = marker, markerfacecolor = color, markeredgecolor = color, markersize = 2.0)
 493        for j in range(ndiffractions):
 494#           data0 = ax0.plot([Q2[j], Q2[j]], [0.0, Int[j]], linestyle = 'dashed', color = color, linewidth = 0.5)
 495            ax.plot([Q2[j], Q2[j]], [0.0, Int[j]], linestyle = 'dashed', color = color, linewidth = 0.5)
 496            ax0.plot([Q2[j], Q2[j]], [0.0, Int[j]], linestyle = 'dashed', color = color, linewidth = 0.5)
 497
 498#plot_event.add_data({"label": "diffraction", "plot_type": "2D", "axis": ax, "data": [Q2, dhkl, hkl, mul, Int]})
 499        hkl_str = [f"{hkl[i][0]} {hkl[i][1]} {hkl[i][2]}" for i in range(ndiffractions)]
 500        plot_event.add_data({"label": "diffraction", "plot_type": "2D", "axis": ax, "data": data,
 501                                 "x": Q2, 'y': Int, "xlabel": "2Theta", "ylabel": "Intensity",
 502                                 "xlist": [src, phase, Q2, dhkl,  hkl_str, mul, Int],
 503                                 "xlabels": ['X-ray', 'phase', 'Q2', 'dhkl', 'hkl', 'multiplicity', 'Intensity']
 504                                 })
 505        plot_event.add_data({"label": "diffraction", "plot_type": "2D", "axis": ax0, "data": data0,
 506                                 "x": Q2, 'y': Int, "xlabel": "2Theta", "ylabel": "Intensity",
 507                                 "xlist": [src, phase, Q2, dhkl,  hkl_str, mul, Int],
 508                                 "xlabels": ['X-ray', 'phase', 'Q2', 'dhkl', 'hkl', 'multiplicity', 'Intensity']
 509                                 })
 510
 511        ax.axhline(0.0, linestyle = 'dashed', color = 'red', linewidth = 0.5)
 512        if cparams.yscale == 'log':
 513            ax.set_yscale('log')
 514            ymax = max(xrd_cal)
 515            ax.set_ylim([1.0e-4 * ymax, ax.get_ylim()[1]])
 516        ax.legend(fontsize = legend_fontsize)
 517
 518    xmin = max([inf_input["xmin"], cparams.xmin])
 519    xmax = min([inf_input["xmax"], cparams.xmax])
 520
 521    for ax in axes:
 522        for q2 in range(int(xmin), int(xmax)):
 523            if q2 % 5 == 0:
 524                ax.axvline(q2, linestyle = 'dotted', linewidth = 0.5, color = 'black')
 525            else:
 526                ax.axvline(q2, linestyle = 'dotted', linewidth = 0.3, color = 'gray')
 527
 528    ax.set_xlim(xmin, xmax)
 529
 530    plot_event.register_click(fig) #callback = lambda event: plot_event.onclick(event))
 531#    plot_event.register_event(fig, event = "button_press_event", 
 532#                    callback = lambda event: plot_event.onclick(event))
 533
 534    ax_bottom.set_xlabel(r'2$\theta$', fontsize = fontsize)
 535    axes[int(ncif/2)].set_ylabel('Intensity', fontsize = fontsize)
 536    ax.legend(fontsize = legend_fontsize)
 537
 538    plt.tight_layout()
 539    plt.pause(0.1)
 540
 541    input("Press ENTER to terminate>>")
 542
 543def plot_input(inf, app, cparams):
 544    """
 545    入力された実験XRDパターンのみをプロットする。
 546
 547    詳細説明:
 548    `inf` から実験XRDデータを取得し、一つのグラフにプロットする。
 549    Y軸のスケールは `cparams.yscale` に従う。
 550
 551    :param inf: dict. 読み込まれたデータを含む辞書。
 552    :param app: tkApplicationのインスタンス。
 553    :param cparams: 設定パラメータを保持するオブジェクト。
 554    :returns: なし
 555    """
 556    print("")
 557    print("#########################################")
 558    print("  Plot input XRD curve")
 559    print("#########################################")
 560    module_input = inf["module_input"]
 561    inf_input    = inf["inf_input"]
 562
 563    print("")
 564    print("plot")
 565    print(f"yscale: {cparams.yscale}")
 566
 567    sample_infile = inf_input["sample_name"]
 568    xQ2_infile  = inf_input["data_list"][0]
 569    yobs_infile = inf_input["data_list"][1]
 570
 571    fig = plt.figure(figsize = figsize_one)
 572    ax = fig.add_subplot(1, 1, 1)
 573    ax.tick_params(labelsize = fontsize)
 574
 575    ax.set_title(f"{sample_infile}")
 576    ax.plot(xQ2_infile, yobs_infile, label = "obs", color = colors[0], linewidth = 0.5)
 577#    ax.legend(fontsize = legend_fontsize)
 578
 579    Qmin = max([inf_input["xmin"], cparams.xmin])
 580    Qmax = min([inf_input["xmax"], cparams.xmax])
 581    ax.set_xlim(Qmin, Qmax)
 582    ax.set_xlabel('2$\\theta$ ($\\degree$)', fontsize = fontsize)
 583    ax.set_ylabel('Intensity', fontsize = fontsize)
 584    if cparams.yscale == 'log':
 585       ax.set_yscale('log')
 586#       ymax = max(yobs_infile)
 587#       ax.set_ylim([1.0e-4 * ymax, ax.get_ylim()[1]])
 588
 589    plt.tight_layout()
 590    plt.pause(0.1)
 591
 592    input("Press ENTER to terminate>>")
 593
 594def plot_one(inf, app, cparams):
 595    """
 596    実験XRDパターンとCIFからの回折ピーク位置を同じグラフに表示する。
 597
 598    詳細説明:
 599    入力された実験XRDパターンと、読み込まれた各CIFファイルから計算された回折ピーク位置(強度は正規化)を
 600    同一のグラフにプロットする。これにより、実験パターンと各結晶相のピーク位置の関係を一度に確認できる。
 601
 602    :param inf: dict. 読み込まれたデータを含む辞書。
 603    :param app: tkApplicationのインスタンス。
 604    :param cparams: 設定パラメータを保持するオブジェクト。
 605    :returns: なし
 606    """
 607    print("")
 608    print("#########################################")
 609    print("  Plot input XRD curve and cif diffraction angles in one graph")
 610    print("#########################################")
 611    module_input = inf["module_input"]
 612    module_cif   = inf["module_cif"]
 613    inf_input    = inf["inf_input"]
 614    inf_cif_list = inf["inf_cif_list"]
 615
 616    if module_cif is None:
 617        app.terminate(f"Error in plot_all(): CIFファイルが見つかりませんでした", pause = True)
 618
 619    ncif = len(inf_cif_list)
 620    print("")
 621    print("plot")
 622    print(f"yscale: {cparams.yscale}")
 623    print("# of cif data:", ncif)
 624
 625    sample_infile = inf_input["sample_name"]
 626    xQ2_infile  = inf_input["data_list"][0]
 627    yobs_infile = inf_input["data_list"][1]
 628    if len(inf_input["data_list"]) >= 3:
 629        ysim_infile = inf_input["data_list"][2]
 630    else:
 631        ysim_infile = None
 632
 633    vmax = max(yobs_infile)
 634    vmin = min(yobs_infile)
 635    yobs_infile = normalize(yobs_infile, Amin = 0.0, Amax = 1.0, vmin = vmin, vmax = vmax)
 636    if ysim_infile is not None:
 637        ysim_infile = normalize(ysim_infile, Amin = 0.0, Amax = 1.0, vmin = vmin, vmax = vmax)
 638
 639    fig = plt.figure(figsize = figsize_one)
 640    plot_event = tkPlotEvent(plt, distance = 'x')
 641    ncolors  = len(colors)
 642    nmarkers = len(markers)
 643
 644    ax = fig.add_subplot(1, 1, 1)
 645#    ax.set_xticks([])
 646    ax.set_yticks([])
 647    ax.set_xlabel(None)
 648    ax.set_title(f"{sample_infile}")
 649    ax.tick_params(labelsize = fontsize)
 650    ax.plot(xQ2_infile, yobs_infile, label = "obs", color = colors[0], linewidth = 0.5)
 651    if ysim_infile is not None:
 652        ax.plot(xQ2_infile, ysim_infile, label = "sim", color = colors[1], linewidth = 0.3)
 653    ax.legend(fontsize = legend_fontsize)
 654
 655    if ysim_infile is None:
 656        ymax = max(yobs_infile)
 657    else:
 658        ymax = max([max(yobs_infile), max(ysim_infile)])
 659    for i in range(1, ncif + 1):
 660        inf_cif = inf_cif_list[i - 1]
 661        xQ2, xrd_cal = inf_cif["conv_data"]
 662        filename = inf_cif["filename"]
 663        dirname, basename, filebody, ext = split_file_path(filename)
 664
 665        src  = inf_cif["diffractions"]["source"]
 666        Q2   = inf_cif["diffractions"]["Q2"]
 667        dhkl = inf_cif["diffractions"]["dhkl"]
 668        hkl  = inf_cif["diffractions"]["hkl"]
 669        mul  = inf_cif["diffractions"].get("mul", None)
 670        if mul is None:
 671            mul = [0 for i in range(len(src))]
 672        Int  = inf_cif["diffractions"]["intensity"]
 673        ndiffractions = len(Q2)
 674
 675        vmax = max(xrd_cal)
 676        vmin = min(xrd_cal)
 677        xrd_cal = normalize(xrd_cal, Amin = 0.0, Amax = 1.0, vmin = vmin, vmax = vmax)
 678        Int     = normalize(Int, Amin = 0.0, Amax = 1.0, vmin = vmin, vmax = vmax)
 679
 680        color  = colors[(i - 1) % ncolors]
 681        marker = markers[(i - 1) % nmarkers]
 682        phase = [filebody] * ndiffractions
 683
 684        data0  = ax.plot(Q2, Int, linestyle = '', marker = marker, markerfacecolor = color, markeredgecolor = color, markersize = 2.0)
 685        ymax = max([max(Int), ymax])
 686        for j in range(ndiffractions):
 687#           data0 = ax.plot([Q2[j], Q2[j]], [0.0, Int[j]], linestyle = 'dashed', color = color, linewidth = 0.5)
 688           ax.plot([Q2[j], Q2[j]], [0.0, Int[j]], linestyle = 'dashed', color = color, linewidth = 0.5)
 689
 690        if len(hkl[0]) == 3:
 691            hkl_str = [f"{hkl[i][0]} {hkl[i][1]} {hkl[i][2]}" for i in range(ndiffractions)]
 692        else:
 693            hkl_str = [f"{hkl[i][0]} {hkl[i][1]} ({hkl[i][2]}) {hkl[i][3]}" for i in range(ndiffractions)]
 694        plot_event.add_data({"label": "diffraction", "plot_type": "2D", "axis": ax, "data": data0,
 695                                 "x": Q2, 'y': Int, "xlabel": "2Theta", "ylabel": "Intensity",
 696                                 "xlist": [src, phase, Q2, dhkl,  hkl_str, mul, Int],
 697                                 "xlabels": ["X-ray", 'phase', 'Q2', 'dhkl', 'hkl', 'multiplicity', 'Intensity']
 698                                 })
 699
 700        ymax = max([max(Int), ymax])
 701        ax.legend(fontsize = legend_fontsize)
 702
 703    plot_event.register_click(fig) #callback = lambda event: plot_event.onclick(event))
 704#    plot_event.register_event(fig, event = "button_press_event", 
 705#                    callback = lambda event: plot_event.onclick(event))
 706
 707    Qmin = max([inf_input["xmin"], cparams.xmin])
 708    Qmax = min([inf_input["xmax"], cparams.xmax])
 709    ax.set_xlim(Qmin, Qmax)
 710    ax.set_ylabel('Intensity', fontsize = fontsize)
 711    if cparams.yscale == 'log':
 712        ax.set_yscale('log')
 713        ax.set_ylim([1.0e-4 * ymax, ax.get_ylim()[1]])
 714
 715    plt.tight_layout()
 716    plt.pause(0.1)
 717
 718    input("Press ENTER to terminate>>")
 719
 720def plot_one2(inf, app, cparams):
 721    """
 722    実験XRDパターンとCIFからの理論XRDパターンを同じグラフに表示する。
 723
 724    詳細説明:
 725    入力された実験XRDパターンと、読み込まれた各CIFファイルから計算された理論XRDパターン(ブロードニング適用済み)を
 726    同一のグラフにプロットする。これにより、実験パターンと各結晶相の回折プロファイルの形状を一度に比較できる。
 727
 728    :param inf: dict. 読み込まれたデータを含む辞書。
 729    :param app: tkApplicationのインスタンス。
 730    :param cparams: 設定パラメータを保持するオブジェクト。
 731    :returns: なし
 732    """
 733    print("")
 734    print("#########################################")
 735    print("  Plot XRD curves in one graph")
 736    print("#########################################")
 737    module_input = inf["module_input"]
 738    module_cif   = inf["module_cif"]
 739    inf_input    = inf["inf_input"]
 740    inf_cif_list = inf["inf_cif_list"]
 741
 742    if module_cif is None:
 743        app.terminate(f"Error in plot_all(): CIFファイルが見つかりませんでした", pause = True)
 744
 745    ncif = len(inf_cif_list)
 746    print("")
 747    print("plot")
 748    print(f"yscale: {cparams.yscale}")
 749    print("# of cif data:", ncif)
 750
 751    sample_infile = inf_input["sample_name"]
 752    xQ2_infile  = inf_input["data_list"][0]
 753    yobs_infile = inf_input["data_list"][1]
 754    if len(inf_input["data_list"]) >= 3:
 755        ysim_infile = inf_input["data_list"][2]
 756    else:
 757        ysim_infile = None
 758    
 759    vmax = max(yobs_infile)
 760    vmin = min(yobs_infile)
 761    yobs_infile = normalize(yobs_infile, Amin = 0.0, Amax = 1.0, vmin = vmin, vmax = vmax)
 762    if ysim_infile is not None:
 763        ysim_infile = normalize(inf_input["data_list"][2], Amin = 0.0, Amax = 1.0, vmin = vmin, vmax = vmax)
 764
 765    fig = plt.figure(figsize = figsize_one)
 766    plot_event = tkPlotEvent(plt, distance = 'x')
 767    ncolors  = len(colors)
 768    nmarkers = len(markers)
 769
 770    ax = fig.add_subplot(1, 1, 1)
 771#    ax.set_xticks([])
 772    ax.set_yticks([])
 773    ax.set_xlabel(None)
 774    ax.set_title(f"{sample_infile}")
 775    ax.tick_params(labelsize = fontsize)
 776    ax.plot(xQ2_infile, yobs_infile, label = "obs", color = colors[0], linewidth = 1.0)
 777#    ax.plot(xQ2_infile, ysim_infile, label = "sim", color = colors[1], linewidth = 0.3)
 778
 779    ymax = max(yobs_infile)
 780    for i in range(1, ncif + 1):
 781        inf_cif = inf_cif_list[i - 1]
 782        xQ2, xrd_cal = inf_cif["conv_data"]
 783        filename = inf_cif["filename"]
 784        dirname, basename, filebody, ext = split_file_path(filename)
 785
 786        vmax = max(xrd_cal)
 787        vmin = min(xrd_cal)
 788        xrd_cal = normalize(xrd_cal, Amin = 0.0, Amax = 1.0, vmin = vmin, vmax = vmax)
 789
 790        ax.plot(xQ2, xrd_cal, label = filebody, linestyle = '-', color = colors[i % ncolors], linewidth = 0.5)
 791        ymax = max([ymax, max(xrd_cal)])
 792
 793    Qmin = max([inf_input["xmin"], cparams.xmin])
 794    Qmax = min([inf_input["xmax"], cparams.xmax])
 795    ax.set_xlim(Qmin, Qmax)
 796    ax.set_ylabel('Intensity', fontsize = fontsize)
 797    if cparams.yscale == 'log':
 798        ax.set_yscale('log')
 799        ax.set_ylim([1.0e-4 * ymax, ax.get_ylim()[1]])
 800    ax.legend(fontsize = legend_fontsize)
 801
 802    plt.tight_layout()
 803    plt.pause(0.1)
 804
 805    input("Press ENTER to terminate>>")
 806
 807def convolution(x, y, filter, nskip = 1):
 808    """
 809    データ `y` を指定された `filter` で畳み込む。
 810
 811    詳細説明:
 812    離散的な畳み込み演算を実行し、`y` の各点において `filter` を適用して
 813    平滑化された(またはブロードニングされた)データ `yc` を生成する。
 814    `nskip` パラメータで畳み込みの計算間隔を制御し、結果のデータ点数を減らすことができる。
 815
 816    :param x: list or array. x座標のリスト。
 817    :param y: list or array. 畳み込み対象のy座標のリスト。
 818    :param filter: list or array. 畳み込みに使用するフィルタの配列。
 819    :param nskip: int. 畳み込み計算をスキップする間隔。1の場合、すべての点で計算する。
 820    :returns: tuple. (`_x`, `_y`). 畳み込み後のx座標とy座標のリスト。`y` がNoneの場合はNoneを返す。
 821    """
 822    if y is None:
 823        return None
 824
 825    ny = len(y)
 826    nconv = len(filter) // 2
 827    yc = np.zeros(ny)
 828    for i in range(0, ny, nskip):
 829        for idf in range(-nconv, nconv + 1):
 830            i1 = i + idf
 831            if i1 % nskip == 0 and 0 <= i1 < ny:
 832                yc[i] += filter[idf] * y[i1]
 833
 834    _x = [x[i]  for i in range(0,  ny, nskip)]
 835    _y = [yc[i] for i in range(0,  ny, nskip)]
 836
 837    return _x, _y
 838#    return x, yc
 839
 840def collect_data(inf, app, cparams, is_print = True):
 841    """
 842    フィッティングや相関分析のために、実験および理論XRDデータを収集・前処理する。
 843
 844    詳細説明:
 845    入力された実験XRDデータと各CIFファイルから計算された理論XRDデータを統一された2θ範囲に補間し、
 846    スマアリング(畳み込み)と正規化を適用する。さらに、背景多項式のための基底関数も準備する。
 847
 848    :param inf: dict. 読み込まれたデータを含む辞書。
 849    :param app: tkApplicationのインスタンス。
 850    :param cparams: 設定パラメータを保持するオブジェクト。
 851    :param is_print: bool. 処理の詳細をコンソールに出力するかどうか。
 852    :returns: tuple. (Q2_list, yobs_infile, ysim_infile, bg_names, bg_list, sample_names, ycif_list, labels_train, x_train)。
 853              - `Q2_list`: list. 共通の2θ座標のリスト。
 854              - `yobs_infile`: list. 前処理された実験XRD強度データ。
 855              - `ysim_infile`: list or None. 前処理されたシミュレーションXRD強度データ(存在する場合)。
 856              - `bg_names`: list. 背景多項式のラベルのリスト。
 857              - `bg_list`: list. 背景多項式の基底関数(レジェンドル多項式)のリスト。
 858              - `sample_names`: list. CIFファイルから取得した試料名またはファイルボディ名のリスト。
 859              - `ycif_list`: list. 各CIFファイルから計算され前処理された理論XRD強度データのリスト。
 860              - `labels_train`: list. 学習データ(x_train)の各列に対応するラベルのリスト。
 861              - `x_train`: list. 学習データとなる背景多項式と理論XRD強度のリスト。
 862    """
 863    module_input = inf["module_input"]
 864    inf_input    = inf["inf_input"]
 865    inf_cif_list = inf["inf_cif_list"]
 866    ncif      = len(inf_cif_list)
 867    nspectrum = inf_input["nspectrum"]
 868    
 869    if ncif < 1:
 870        app.terminate(f"\nError: CIF file is not found.\n", pause = True)
 871
 872    fwhm = cparams.fwhm_smear
 873    Gf   = cparams.Gfraction_smear
 874    dQ2 = 12.0 * fwhm
 875    nconv = int(dQ2 / cparams.xstep + 1.00001)
 876    filter = [0.0] * (2 * nconv + 1)
 877    if fwhm <= 0.0:
 878        wGL = 1.0
 879    else:
 880        wGL = GaussLorentz(0.0, 0.0, fwhm / 2.0, C0 = 1.0, Gfraction = Gf, Gwratio = 1.0, A = None)
 881    for i in range(-nconv, nconv + 1):
 882        if fwhm <= 0.0:
 883            filter[i] = 0.0
 884        else:
 885            filter[i] = GaussLorentz(0.0, i * cparams.xstep, fwhm / 2.0, C0 = 1.0, Gfraction = Gf, Gwratio = 1.0, A = None) / wGL
 886
 887    sample_infile = inf_input["sample_name"]
 888    xQ2_infile  = inf_input["data_list"][0]
 889    nx_in = len(xQ2_infile)
 890    dx = xQ2_infile[1] - xQ2_infile[0]
 891    nskip = int(fwhm / dx / 5.0 + 1.0e-5)
 892    if nskip == 0:
 893        nskip = 1
 894    minnx = 500
 895    if int(nx_in / nskip) < minnx:
 896        nskip = int(nx_in / minnx)
 897#    nskip = 1    
 898
 899    yobs_infile = inf_input["data_list"][1]
 900    if nspectrum >= 3:
 901        ysim_infile = inf_input["data_list"][2]
 902    else:
 903        ysim_infile = None
 904
 905    if cparams.yscale == 'log':
 906        yobs_infile = log(yobs_infile)
 907        if ysim_infile is not None:
 908            ysim_infile = log(ysim_infile)
 909    if fwhm > 0.0:
 910        _xQ2_infile, yobs_infile = convolution(xQ2_infile, yobs_infile, filter, nskip = nskip)
 911        if ysim_infile is not None:
 912            _xQ2_infile, ysim_infile = convolution(xQ2_infile, ysim_infile, filter, nskip = nskip)
 913    else:
 914        _xQ2_infile = xQ2_infile
 915
 916    vmax = max(yobs_infile)
 917    vmin = min(yobs_infile)
 918    yobs_infile = normalize(yobs_infile, Amin = 0.0, Amax = 1.0, vmin = vmin, vmax = vmax)
 919    if ysim_infile is not None:
 920        ysim_infile = normalize(ysim_infile, Amin = 0.0, Amax = 1.0, vmin = vmin, vmax = vmax)
 921
 922    print(f"Read CIF data from {ncif} files")
 923    xQ2 = None
 924    ycif_list    = []
 925    sample_names = []
 926    for i in range(1, ncif + 1):
 927        inf_cif = inf_cif_list[i - 1]
 928        xQ2, xrd_cal = inf_cif["conv_data"]
 929        maxy = abs(max(xrd_cal))
 930        if cparams.yscale == 'log':
 931            xrd_cal = log(xrd_cal + maxy * 1.0e-7)
 932
 933        if fwhm > 0.0:
 934            _xQ2, xrd_cal = convolution(xQ2, xrd_cal, filter, nskip = nskip)
 935        else:
 936            _xQ2 = xQ2
 937
 938        xrd_cal = normalize(xrd_cal, Amin = 0.0, Amax = 1.0, vmin = vmin, vmax = vmax)
 939        xrd_cal = normalize(xrd_cal, Amin = 0.0, Amax = 1.0)
 940
 941        filename = inf_cif["filename"]
 942        dirname, basename, filebody, ext = split_file_path(filename)
 943
 944        ycif_list.append(xrd_cal)
 945        sample_names.append(filebody)
 946
 947    nx = len(xQ2)
 948    xmin = min(xQ2)
 949    xmax = max(xQ2)
 950    xstep = (xmax - xmin) / (nx - 1)
 951    x_list  = []
 952    Q2_list = []
 953    for i in range(nx):
 954        x_list.append(-1 + i * xstep)
 955        Q2_list.append(xmin + i * xstep)
 956
 957    func1d = interp1d(_xQ2_infile, yobs_infile, bounds_error = False, fill_value = (0.0, 0.0))
 958#        func1d = interp1d(xQ2_infile, yobs_infile, bounds_error = False, fill_value = (0.0, 0.0))
 959    yobs_infile = func1d(Q2_list)
 960    if ysim_infile is not None:
 961        func1d = interp1d(_xQ2_infile, ysim_infile, bounds_error = False, fill_value = (0.0, 0.0))
 962#            func1d = interp1d(xQ2_infile, ysim_infile, bounds_error = False, fill_value = (0.0, 0.0))
 963        ysim_infile = func1d(Q2_list)
 964    for i in range(1, ncif + 1):
 965        func1d = interp1d(_xQ2, ycif_list[i-1], bounds_error = False, fill_value = (0.0, 0.0))
 966#            func1d = interp1d(xQ2, ycif_list[i-1], bounds_error = False, fill_value = (0.0, 0.0))
 967        ycif_list[i-1] = func1d(Q2_list)
 968
 969    bg_list = []
 970    for order in range(cparams.BGorder + 1):
 971        poly1d = legendre(order)
 972        bg_list.append(poly1d(x_list))
 973
 974    if is_print:
 975        print("# of cif data:", ncif)
 976        print("BG order   :", cparams.BGorder)
 977
 978    bg_names = [f"BG_order_{i}" for i in range(cparams.BGorder + 1)] # BG_order+1個の多項式基底
 979    x_train      = bg_list.copy()
 980    labels_train = bg_names.copy()
 981    x_train.extend(ycif_list)
 982    labels_train.extend(sample_names) # CIFファイルの名前を追加
 983
 984    return Q2_list, yobs_infile, ysim_infile, bg_names, bg_list, sample_names, ycif_list, labels_train, x_train
 985
 986def check_overwrap(inf, app, cparams):
 987    """
 988    異なるCIFファイル由来のXRDパターン間のオーバーラップ(重なり)をチェックし、可視化する。
 989
 990    詳細説明:
 991    各CIFファイルの回折ピークが、他のCIFファイルから生成された回折パターンとどの程度重なっているかを計算し、
 992    その結果をプロットする。これにより、複数の結晶相が混在する場合に、各相のピークが互いに干渉する度合いを評価できる。
 993
 994    :param inf: dict. 読み込まれたデータを含む辞書。
 995    :param app: tkApplicationのインスタンス。
 996    :param cparams: 設定パラメータを保持するオブジェクト。
 997    :returns: なし
 998    """
 999    print("")
1000    print("#########################################")
1001    print("  Check overwrap between cif XRD patterns")
1002    print("#########################################")
1003    print(f"  smearing: fwhm={cparams.fwhm_smear} Gaussian fraction={cparams.Gfraction_smear}")
1004    inf_cif_list = inf["inf_cif_list"]
1005
1006    Q2_list, yobs_infile, ysim_infile, bg_names, bg_list, sample_names, ycif_list, labels_train, x_train = collect_data(inf, app, cparams)
1007    nx   = len(Q2_list)
1008    Qmin = Q2_list[0]
1009    Qmax = Q2_list[nx - 1]
1010    ncif = len(ycif_list)
1011    nxtrain = len(x_train)
1012
1013    fwhm = cparams.fwhm + cparams.fwhm_smear
1014    Gf   = cparams.Gfraction_smear
1015    dQ2 = fwhm # FWHMを考慮した畳み込み幅
1016    nconv = int(dQ2 / cparams.xstep + 1.00001)
1017    filter = [0.0] * (2 * nconv + 1)
1018    if fwhm <= 0.0:
1019        wGL = 1.0
1020    else:
1021        # FWHMの半分を半値幅として使用
1022        wGL = GaussLorentz(0.0, 0.0, fwhm / 2.0, C0 = 1.0, Gfraction = Gf, Gwratio = 1.0, A = None)
1023    for i in range(-nconv, nconv + 1):
1024        if fwhm <= 0.0:
1025            filter[i] = 0.0
1026        else:
1027            filter[i] = GaussLorentz(0.0, i * cparams.xstep, fwhm / 2.0, C0 = 1.0, Gfraction = Gf, Gwratio = 1.0, A = None) / wGL
1028#    print("nconv=", nconv)
1029#    print("filter=", filter)
1030
1031    # 構造のピークが他の構造のブロードニングされたパターンに与える影響を計算
1032    corr = make_matrix3(ncif, ncif, nx)
1033    for ic in range(ncif):
1034        inf = inf_cif_list[ic]
1035        Q2  = inf["diffractions"]["Q2"]
1036        Int = inf["diffractions"]["intensity"]
1037        nd = len(Q2)
1038        ndiffractions = len(Q2)
1039
1040        for ic2 in range(ncif):
1041            if ic == ic2:
1042                continue
1043    
1044            ycif = ycif_list[ic2] # 他のCIFパターン
1045
1046            for id in range(nd):
1047                Q20 = Q2[id] # 現在のCIFのピーク位置
1048
1049                id0 = None
1050                for i in range(nx):
1051                    if Q20 <= Q2_list[i]:
1052                        id0 = i
1053                        break
1054                
1055                if id0 is None:
1056                    continue
1057
1058                # ピーク位置 Q20 を中心に、ブロードニングフィルターを適用
1059                for idf in range(-nconv, nconv + 1):
1060                    i1 = id0 + idf
1061                    if 0 <= i1 < nx:
1062                        # 現在のピーク強度 * フィルタ値 * 他のCIFパターンの強度
1063                        # これはオーバーラップの度合いを示す
1064                        v = filter[idf] * Int[id] * ycif[i1]
1065                        if corr[ic][ic2][i1] is None:
1066                            corr[ic][ic2][i1] = v
1067                        else:
1068                            corr[ic][ic2][i1] += v
1069#    print("corr=", corr)
1070
1071    fig, axes = plt.subplots(ncif, 1, figsize = figsize, sharex=True, gridspec_kw={'hspace': 0})
1072    plot_event = tkPlotEvent(plt, distance = 'x')
1073    ncolors  = len(colors)
1074    nmarkers = len(markers)
1075
1076    for ic in range(ncif):
1077        inf = inf_cif_list[ic]
1078        src  = inf["diffractions"]["source"]
1079        Q2   = inf["diffractions"]["Q2"]
1080        dhkl = inf["diffractions"]["dhkl"]
1081        hkl  = inf["diffractions"]["hkl"]
1082        mul  = inf["diffractions"].get("mul", None)
1083        if mul is None:
1084            mul = [0 for i in range(len(src))]
1085        Int  = inf["diffractions"]["intensity"]
1086        ndiffractions = len(Q2)
1087        filename = inf["filename"]
1088        dirname, basename, filebody, ext = split_file_path(filename)
1089        phase = [filebody] * ndiffractions
1090
1091        ax = axes[ic]
1092        ax.tick_params(labelsize = fontsize)
1093        ax2 = ax.twinx()
1094        ax2.tick_params(labelsize = 0)
1095        ycif = ycif_list[ic]
1096        ax.plot([], [], label = filebody, linestyle = '') # フェーズ名を表示するためのダミープロット
1097        ax2.plot(Q2_list, ycif, picker = True, linestyle = 'dashed', color = 'black', linewidth = 0.5)
1098
1099        for ic2 in range(ncif):
1100            if ic == ic2:
1101                continue
1102
1103            inf_cif2 = inf_cif_list[ic2]
1104            filename = inf_cif2["filename"]
1105            dirname, basename, filebody, ext = split_file_path(filename)
1106
1107            color  = colors[ic2 % ncolors]
1108            marker = markers[ic2 % ncolors]
1109
1110            y = corr[ic][ic2]
1111            vmax = max_none(y)
1112            vmin = min_none(y)
1113            y   = normalize_none(y, Amin = 0.0, Amax = 1.0, vmin = vmin, vmax = vmax)
1114            Int = normalize(Int, Amin = 0.0, Amax = 1.0) # ピーク強度を正規化
1115
1116            ax.plot(Q2_list, y, label = filebody, picker = True, color = color, linewidth = 1.0) # オーバーラッププロット
1117
1118        data = ax.plot(Q2, Int, linestyle = '', marker = marker, markerfacecolor = color, markeredgecolor = 'black', markersize = 2.0)
1119        for j in range(ndiffractions):
1120             ax.plot([Q2[j], Q2[j]], [0.0, Int[j]], linestyle = 'dashed', color = 'black', linewidth = 0.5)
1121
1122        hkl_str = [f"{hkl[i][0]} {hkl[i][1]} {hkl[i][2]}" for i in range(ndiffractions)]
1123        plot_event.add_data({"label": "diffraction", "plot_type": "2D", "axis": ax, "data": data,
1124                                 "x": Q2, 'y': Int, "xlabel": "2Theta", "ylabel": "Intensity",
1125                                 "xlist": [src, phase, Q2, dhkl,  hkl_str, mul, Int],
1126                                 "xlabels": ['X-ray', 'phase', 'Q2', 'dhkl', 'hkl', 'multiplicity', 'Intensity']
1127                                 })
1128
1129        ax.set_xlim([Qmin, Qmax])
1130        ax.legend(fontsize = legend_fontsize)
1131        if cparams.yscale == 'log':
1132            ax.set_yscale('log')
1133            ax.set_ylim([1.0e-4, ax.get_ylim()[1]])
1134        ax2.set_yscale('linear') # ax2のスケールは常にリニア
1135
1136    ax.set_xlabel(r'2$\theta$', fontsize = fontsize)
1137
1138    plot_event.register_click(fig) # callback = lambda event: plot_event.onclick(event))
1139    plot_event.register_pick(fig) # callback = lambda event: plot_event.onclick(event))
1140
1141    plt.tight_layout()
1142    plt.pause(0.1)
1143
1144    input("Press ENTER to terminate>>")
1145
1146def CIF_correlation(inf, app, cparams):
1147    """
1148    CIFファイルから生成されたXRDパターン間の相関係数を計算し、可視化する。
1149
1150    詳細説明:
1151    すべてのCIFファイルから生成された理論XRDパターン間の相関係数を計算し、結果をテキストで出力する。
1152    また、各CIFパターンと入力実験パターンとの相関係数を、スマアリングFWHMを変化させながらプロットし、
1153    最も相関の高いスマアリング条件や、異なる相間の類似性を評価する。
1154
1155    :param inf: dict. 読み込まれたデータを含む辞書。
1156    :param app: tkApplicationのインスタンス。
1157    :param cparams: 設定パラメータを保持するオブジェクト。
1158    :returns: なし
1159    """
1160    print("")
1161    print("#########################################")
1162    print("  Check correlation between cif XRD patterns")
1163    print("#########################################")
1164    print(f"  smearing: fwhm={cparams.fwhm_smear} Gaussian fraction={cparams.Gfraction_smear}")
1165    inf_cif_list = inf["inf_cif_list"]
1166
1167    Q2_list, yobs_infile, ysim_infile, bg_names, bg_list, sample_names, ycif_list, labels_train, x_train = collect_data(inf, app, cparams)
1168    xlist   = [yobs_infile]
1169    xlabels = ["obs"]
1170    xlist.extend(ycif_list)
1171    xlabels.extend(sample_names)
1172    nx = len(xlist)
1173    dx = Q2_list[1] - Q2_list[0]
1174
1175#相関係数
1176    print("")
1177    print("Correlation coefficients:")
1178    corr = np.zeros([nx, nx])
1179    for i in range(nx):
1180        for j in range(i, nx):
1181            corr[i][j] = np.dot(xlist[i], xlist[j])
1182    for i in range(nx):
1183        for j in range(i + 1, nx):
1184            corr[i][j] /= np.sqrt(corr[i][i] * corr[j][j])
1185            if corr[i][j] >= 0.9:
1186                print(f" identical? {corr[i][j]:8.4f}: ({i:2}-{j:2}): ({xlabels[i]:20})-({xlabels[j]:20})")
1187            elif corr[i][j] >= 0.8:
1188                print(f"    similar {corr[i][j]:8.4f}: ({i:2}-{j:2}): ({xlabels[i]:20})-({xlabels[j]:20})")
1189            elif corr[i][j] >= 0.5:
1190                print(f"      ***** {corr[i][j]:8.4f}: ({i:2}-{j:2}): ({xlabels[i]:20})-({xlabels[j]:20})")
1191            elif corr[i][j] >= 0.5:
1192                print(f"      ***** {corr[i][j]:8.4f}: ({i:2}-{j:2}): ({xlabels[i]:20})-({xlabels[j]:20})")
1193            elif corr[i][j] >= 0.4:
1194                print(f"       **** {corr[i][j]:8.4f}: ({i:2}-{j:2}): ({xlabels[i]:20})-({xlabels[j]:20})")
1195            elif corr[i][j] >= 0.3:
1196                print(f"        *** {corr[i][j]:8.4f}: ({i:2}-{j:2}): ({xlabels[i]:20})-({xlabels[j]:20})")
1197            else:
1198                print(f"            {corr[i][j]:8.4f}: ({i:2}-{j:2}): ({xlabels[i]:20})-({xlabels[j]:20})")
1199
1200# input dataとの相関係数をsmearing fwhmを変えながらプロット
1201    print("")
1202    print("Correlation coefficients with input data with varied smearing fwhm:")
1203    fwhm_smear_original = cparams.fwhm_smear
1204    fwhm_list = np.arange(0.0, 1.001, 0.2)
1205    corr_list = make_matrix2(nx, len(fwhm_list))
1206    for i in range(len(fwhm_list)):
1207        _fwhm = fwhm_list[i]
1208        cparams.fwhm_smear = _fwhm
1209        print(f"  Calculating for fwhm={_fwhm}")
1210        Q2_list0, yobs_infile0, ysim_infile0, bg_names0, bg_list0, sample_names0, ycif_list0, labels_train0, x_train0 \
1211                                    = collect_data(inf, app, cparams, is_print = False)
1212
1213        nskip = int((_fwhm / 5.0) / dx + 1.0e-5)
1214#        nskip = 1
1215
1216        xlist0 = [[v for i, v in enumerate(yobs_infile0) if i % nskip == 0]]
1217        for y in ycif_list0:
1218            xlist0.append([v for i, v in enumerate(y) if i % nskip == 0])
1219#        xlist0   = [yobs_infile]
1220#        xlist0.extend(ycif_list0)
1221        nx_sampled = len(xlist0)
1222        print(f"    _fwhm={_fwhm}  dx={dx}  nskip={nskip}  nx={len(xlist0[0])}")
1223        
1224#        nskip = int((_fwhm / 4.0) / dx)
1225#        print("nskip = ", nskip)
1226#        print("")
1227        
1228        norm = make_matrix1(nx_sampled)
1229        for j in range(nx_sampled):
1230            print(f"    calculating {j}-th diagonal norm")
1231            norm[j] = np.sqrt(np.dot(xlist0[j], xlist0[j]))
1232        for j in range(nx_sampled):
1233            print(f"    calculating obs - {j}-th non-diagonal correlation")
1234            corr_list[j][i] = np.dot(xlist0[0], xlist0[j]) / norm[0] / norm[j]
1235            
1236    cparams.fwhm_smear = fwhm_smear_original
1237
1238    print("")
1239    print("plot")
1240
1241    fig, ax = plt.subplots(1, 1, figsize = figsize_one)
1242    plot_event = tkPlotEvent(plt, distance = 'x')
1243    ncolors  = len(colors)
1244
1245    for i in range(1, nx): # "obs"を除いたCIFパターンとobsの相関をプロット
1246        ax.tick_params(labelsize = fontsize)
1247        ax.plot(fwhm_list, corr_list[i], label = xlabels[i], picker = True, color = colors[i % ncolors], linewidth = 1.0)
1248
1249    ax.legend(fontsize = legend_fontsize)
1250    ax.set_xlabel(r'fwhm ($\degree$)', fontsize = fontsize)
1251    ax.set_ylabel(r'Correlation coefficient', fontsize = fontsize)
1252
1253    plot_event.register_pick(fig) # callback = lambda event: plot_event.onclick(event))
1254
1255    plt.tight_layout()
1256    plt.pause(0.1)
1257
1258    print("")
1259    input("Press ENTER to terminate>>")
1260
1261
1262def correlation(inf, app, cparams):
1263    """
1264    入力された実験XRDパターンとCIFファイルから生成された理論パターンとの相関係数を計算し、可視化する。
1265
1266    詳細説明:
1267    実験XRDパターンと、読み込まれた各CIFファイルから計算された理論XRDパターン間の相関係数を計算し、
1268    結果をテキストで出力する。さらに、これらの相関係数をスマアリングFWHMを変化させながらプロットし、
1269    最も相関の高い相や適切なスマアリング条件を評価する。
1270
1271    :param inf: dict. 読み込まれたデータを含む辞書。
1272    :param app: tkApplicationのインスタンス。
1273    :param cparams: 設定パラメータを保持するオブジェクト。
1274    :returns: なし
1275    """
1276    print("")
1277    print("#########################################")
1278    print("  Check correlation between the input XRD and the cif XRD patterns")
1279    print("#########################################")
1280    print(f"  smearing: fwhm={cparams.fwhm_smear} Gaussian fraction={cparams.Gfraction_smear}")
1281    inf_cif_list = inf["inf_cif_list"]
1282
1283    Q2_list, yobs_infile, ysim_infile, bg_names, bg_list, sample_names, ycif_list, labels_train, x_train = collect_data(inf, app, cparams)
1284    xlist   = [yobs_infile]
1285    xlabels = ["obs"]
1286    xlist.extend(ycif_list)
1287    xlabels.extend(sample_names)
1288    nx = len(xlist)
1289    dx = Q2_list[1] - Q2_list[0]
1290
1291#相関係数
1292    print("")
1293    print("Correlation coefficients:")
1294    corr = np.zeros([nx, nx])
1295    for i in range(nx):
1296        for j in range(i, nx):
1297            corr[i][j] = np.dot(xlist[i], xlist[j])
1298    for i in range(nx):
1299        for j in range(i + 1, nx):
1300            corr[i][j] /= np.sqrt(corr[i][i] * corr[j][j])
1301            if corr[i][j] >= 0.9:
1302                print(f" identical? {corr[i][j]:8.4f}: ({i:2}-{j:2}): ({xlabels[i]:20})-({xlabels[j]:20})")
1303            elif corr[i][j] >= 0.8:
1304                print(f"    similar {corr[i][j]:8.4f}: ({i:2}-{j:2}): ({xlabels[i]:20})-({xlabels[j]:20})")
1305            elif corr[i][j] >= 0.5:
1306                print(f"      ***** {corr[i][j]:8.4f}: ({i:2}-{j:2}): ({xlabels[i]:20})-({xlabels[j]:20})")
1307            elif corr[i][j] >= 0.5:
1308                print(f"      ***** {corr[i][j]:8.4f}: ({i:2}-{j:2}): ({xlabels[i]:20})-({xlabels[j]:20})")
1309            elif corr[i][j] >= 0.4:
1310                print(f"       **** {corr[i][j]:8.4f}: ({i:2}-{j:2}): ({xlabels[i]:20})-({xlabels[j]:20})")
1311            elif corr[i][j] >= 0.3:
1312                print(f"        *** {corr[i][j]:8.4f}: ({i:2}-{j:2}): ({xlabels[i]:20})-({xlabels[j]:20})")
1313            else:
1314                print(f"            {corr[i][j]:8.4f}: ({i:2}-{j:2}): ({xlabels[i]:20})-({xlabels[j]:20})")
1315
1316# input dataとの相関係数をsmearing fwhmを変えながらプロット
1317    print("")
1318    print("Correlation coefficients with input data with varied smearing fwhm:")
1319    fwhm_smear_original = cparams.fwhm_smear
1320    fwhm_list = np.arange(0.0, 1.001, 0.2)
1321    corr_list = make_matrix2(nx, len(fwhm_list))
1322    for i in range(len(fwhm_list)):
1323        _fwhm = fwhm_list[i]
1324        cparams.fwhm_smear = _fwhm
1325        print("")
1326        print(f"  Calculating for fwhm={_fwhm}")
1327        Q2_list0, yobs_infile0, ysim_infile0, bg_names0, bg_list0, sample_names0, ycif_list0, labels_train0, x_train0 \
1328                                    = collect_data(inf, app, cparams, is_print = False)
1329
1330        nskip = int((_fwhm / 5.0) / dx + 1.0e-5)
1331#        nskip = 1
1332
1333        xlist0 = [[v for i, v in enumerate(yobs_infile0) if i % nskip == 0]]
1334        for y in ycif_list0:
1335            xlist0.append([v for i, v in enumerate(y) if i % nskip == 0])
1336#        xlist0   = [yobs_infile]
1337#        xlist0.extend(ycif_list0)
1338        nx_sampled = len(xlist0)
1339        print(f"    _fwhm={_fwhm}  dx={dx}  nskip={nskip}  nx={len(xlist0[0])}")
1340
1341        norm = make_matrix1(nx_sampled)
1342        for j in range(nx_sampled):
1343            print(f"    calculating {j}-th diagonal norm")
1344            norm[j] = np.sqrt(np.dot(xlist0[j], xlist0[j]))
1345        for j in range(nx_sampled):
1346            print(f"    calculating obs - {j}-th non-diagonal correlation")
1347            corr_list[j][i] = np.dot(xlist0[0], xlist0[j]) / norm[0] / norm[j]
1348            
1349    cparams.fwhm_smear = fwhm_smear_original
1350
1351
1352    print("")
1353    print("plot")
1354
1355    fig, ax = plt.subplots(1, 1, figsize = figsize_one)
1356    plot_event = tkPlotEvent(plt, distance = 'x')
1357    ncolors  = len(colors)
1358
1359    for i in range(1, nx): # "obs"を除いたCIFパターンとobsの相関をプロット
1360        ax.tick_params(labelsize = fontsize)
1361        ax.plot(fwhm_list, corr_list[i], label = xlabels[i], picker = True, color = colors[i % ncolors], linewidth = 1.0)
1362
1363    ax.legend(fontsize = legend_fontsize)
1364    ax.set_xlabel(r'smearing FWHM ($\degree$)', fontsize = fontsize)
1365    ax.set_ylabel(r'Correlation coefficient', fontsize = fontsize)
1366
1367    plot_event.register_pick(fig) # callback = lambda event: plot_event.onclick(event))
1368
1369    plt.tight_layout()
1370    plt.pause(0.1)
1371
1372    print("")
1373    input("Press ENTER to terminate>>")
1374
1375def fit(inf, app, cparams):
1376    """
1377    LASSO回帰を用いて実験XRDパターンを背景とCIF由来の理論パターンでフィッティングする。
1378
1379    詳細説明:
1380    実験XRDパターンを目的変数、背景多項式と各CIFファイルから計算された理論XRDパターンを説明変数として、
1381    LASSO回帰を実行する。異なるalpha値での係数変化とRMSEの変化をプロットし、最終的に指定されたalpha値での
1382    フィッティング結果(係数、RMSE、MAV)とフィッティングされたパターンをプロットで表示する。
1383
1384    :param inf: dict. 読み込まれたデータを含む辞書。
1385    :param app: tkApplicationのインスタンス。
1386    :param cparams: 設定パラメータを保持するオブジェクト。
1387    :returns: なし
1388    """
1389    print("")
1390    print("#########################################")
1391    print("  Fitting by LASSO")
1392    print("#########################################")
1393    print(f"  alpha={cparams.alpha}")
1394    print(f"  smearing: FWHM={cparams.fwhm_smear} Gaussian fraction={cparams.Gfraction_smear}")
1395    print("yscale:", cparams.yscale)
1396
1397    Q2_list, yobs_infile, ysim_infile, bg_names, bg_list, sample_names, ycif_list, labels_train, x_train \
1398                            = collect_data(inf, app, cparams)
1399    nx   = len(Q2_list)
1400    ncif = len(ycif_list)
1401    nxtrain = len(x_train)
1402
1403    print("")
1404    print("LASSO alpha:", cparams.alpha)
1405    x_train = np.array(x_train).T
1406
1407    alpha0 = 1.0e-8
1408    ntry = 80
1409    print("")
1410    print(f"Lasso regression with varied alpha: start from {alpha0}")
1411    # BG項のラベルも含むため、labels_trainの先頭から表示
1412    print(f"{'':42}", labels_train)
1413    _alpha = alpha0
1414    alpha_list = []
1415    c_list     = []
1416    RMSE_list  = []
1417    maxC = None
1418    for i in range(ntry):
1419        model = Lasso(alpha = _alpha, fit_intercept = False)
1420        model.fit(x_train, yobs_infile)
1421        yfit = model.predict(x_train)
1422        MSE  = mean_squared_error(yobs_infile, yfit)
1423        RMSE = np.sqrt(MSE)
1424
1425        alpha_list.append(_alpha)
1426        c_list.append(model.coef_)
1427        RMSE_list.append(RMSE)
1428
1429        s = ["{:10.4g}".format(v) for v in model.coef_]
1430        s = " ".join(s)
1431        print(f"  {i:2}  alpha={_alpha:8.2g} RMSE={RMSE:12.4g}   coeff={s}")
1432        if maxC is None:
1433            maxC = max(abs(max(model.coef_)), abs(min(model.coef_))) if len(model.coef_) > 0 else 0.0
1434        else:
1435            if len(model.coef_) > 0 and abs(max(model.coef_)) < 1.0e-5 * maxC and abs(min(model.coef_)) < 1.0e-5 * maxC:
1436                break
1437        
1438        _alpha *= 2.0
1439
1440    plot_event = tkPlotEvent(plt, distance = 'x')
1441
1442    print("")
1443    print("plot LASSO analysis")
1444    # coeffプロットはBG項も含めて全係数を表示するが、凡例はCIF名のみ
1445    print("sample_names=", sample_names) 
1446    fig_lasso = plt.figure(figsize = figsize_coeff)
1447    ax  = fig_lasso.add_subplot(1, 1, 1)
1448    ax2 = ax.twinx()
1449    ax.tick_params(labelsize = fontsize)
1450    ax2.tick_params(labelsize = fontsize)
1451
1452    ax.plot(alpha_list, RMSE_list, label = 'RMSE', picker = True, linestyle = 'dashed', color = 'black', linewidth = 1.5)
1453    
1454    # BG項の係数もプロットする場合
1455    # for j in range(len(bg_names)):
1456    #     ax2.plot(alpha_list, np.array(c_list).T[j], label = bg_names[j], picker = True, linewidth = 0.8, linestyle = 'dotted')
1457
1458    # CIF項の係数をプロット
1459    for i in range(len(sample_names)):
1460        # model.coef_ のうち、CIF成分に対応するインデックスは BGorder + 1 以降
1461        ax2.plot(alpha_list, np.array(c_list).T[cparams.BGorder + 1 + i], label = sample_names[i], picker = True, linewidth = 1.0, color = colors[i % ncolors])
1462    ax2.axhline(0.0, linestyle = 'dashed', color = 'red', linewidth = 0.5)
1463
1464    ax.set_xlabel('alpha', fontsize = fontsize)
1465    ax.set_ylabel('RMSE', fontsize = fontsize)
1466    ax2.set_ylabel('coefficient', fontsize = fontsize)
1467    ax.set_xscale('log')
1468    ax.set_yscale('log')
1469    ax2.legend(fontsize = legend_fontsize_one)
1470
1471    plt.tight_layout()
1472    plt.pause(0.1)
1473
1474    model = Lasso(alpha = cparams.alpha, fit_intercept = False)
1475    model.fit(x_train, yobs_infile)
1476    yfit = model.predict(x_train)
1477
1478    print("")
1479    print(f"Lasso regression with alpha={cparams.alpha}")
1480    print(f"  Coefficients:")
1481#    print(f"  coefficients:", model.coef_)
1482    yBG = np.zeros(nx)
1483    for order in range(cparams.BGorder + 1):
1484        print(f"    {bg_names[order]}: {model.coef_[order]:10.6g}")
1485        yBG += model.coef_[order] * bg_list[order] # BG多項式の寄与を計算
1486
1487    # CIF項の係数と、それらを乗じたパターンを更新
1488    for i in range(len(sample_names)):
1489        c = model.coef_[cparams.BGorder + 1 + i] # BG項の後にCIF項が続く
1490        print(f"    {sample_names[i]:20}: {c:10.6g}")
1491        # collect_dataでycif_listは正規化された状態なので、ここで係数を乗じる
1492        ycif_list[i] *= c
1493
1494    MAE  = mean_absolute_error(yobs_infile, yfit)
1495    MSE  = mean_squared_error(yobs_infile, yfit)
1496    RMSE = np.sqrt(MSE)
1497    print(f"  MAE : {MAE:12.4g}")
1498    print(f"  MSE : {MSE:12.4g}")
1499    print(f"  RMSE: {RMSE:12.4g}")
1500
1501    print("")
1502    print("plot XRD profiles")
1503    fig = plt.figure(figsize = figsize_one)
1504    ncolors  = len(colors)
1505    ax = fig.add_subplot(1, 1, 1)
1506    ax.tick_params(labelsize = fontsize)
1507
1508    ax.plot(Q2_list, yobs_infile, label = "obs", picker = True, linestyle = '', 
1509                marker = 'o', markersize = 2.0, markerfacecolor = colors[0], markeredgecolor = colors[0])
1510    ax.plot(Q2_list, yBG, label = "background", picker = True, linestyle = '-', color = 'cyan', linewidth = 0.5)
1511#    ax.plot(Q2_list, yobs_infile, label = "obs", color = colors[0], linewidth = 1.5)
1512    ax.plot(Q2_list, yfit, label = "fit", picker = True, linestyle = 'dashed', color = colors[1], linewidth = 1.5)
1513#    ax.plot(Q2_list, ysim_infile, label = "obs", color = colors[1], linewidth = 0.5)
1514    # フィッティングされた各CIFパターンをプロット
1515    for i in range(len(sample_names)):
1516        ax.plot(Q2_list, ycif_list[i], label = f"{sample_names[i]}", picker = True, color = colors[(i+2) % ncolors ], linewidth = 1.2) # +2 は obs, fit, BGを避けるため
1517
1518#    for order in range(cparams.BGorder):
1519#        ax.plot(Q2_list, bg_list[order], label = f"{order+1}-th order", color = colors[(ncif+2+order) % ncolors], linewidth = 0.5)
1520
1521    ax.set_xlabel('$2\\theta$', fontsize = fontsize)
1522    if cparams.yscale == 'log':
1523        ax.set_ylabel('log($y$)', fontsize = fontsize)
1524    else:
1525        ax.set_ylabel('$y$', fontsize = fontsize)
1526    ax.legend(fontsize = legend_fontsize_one)
1527
1528    plot_event.register_pick(fig_lasso) # callback = lambda event: plot_event.onclick(event))
1529    plot_event.register_pick(fig) # callback = lambda event: plot_event.onclick(event))
1530
1531    
1532    plt.tight_layout()
1533    plt.pause(0.1)
1534
1535    input("Press ENTER to terminate>>")
1536
1537def main():
1538    """
1539    メイン関数。XRD解析スクリプトのエントリポイント。
1540
1541    詳細説明:
1542    アプリケーションの初期化、パラメータの読み込みと更新、プラグインモジュールのロード、
1543    および指定されたモードに応じたXRD解析処理(プロット、相関分析、フィッティングなど)を実行する。
1544    処理のログはファイルにも出力される。
1545
1546    :returns: なし
1547    """
1548    global module_names, modules
1549
1550#==================================================================
1551# Initialize parameters
1552#==================================================================
1553    app     = tkApplication(usage_str  = usage_str, globals = globals(), locals = locals())
1554    cparams = app.get_params()
1555
1556    logfile = app.replace_path(None, template = ["{dirname}", "{filebody}-out.txt"])
1557#    logfile = app.replace_path(cparams.infile, template = ["{dirname}", "{filebody}-out.txt"])
1558    print(f"Open logfile [{logfile}]")
1559    app.redirect(targets = ["stdout", logfile], mode = 'w')
1560
1561    initialize(app, cparams)
1562    update_vars(app, cparams)
1563
1564    cparams.outfile = replace_path(cparams.infile, template = os.path.join("{dirname}", "{filebody}-out.txt"))
1565
1566    print("")
1567    print( "==========================================================================")
1568    print(" Convert CIF file to powder XRD pattern")
1569    print( "==========================================================================")
1570    print(f"mode: {cparams.mode}")
1571    print(f"Plug-in dir: {cparams.plugin_dir}")
1572    print(f"Input  file: {cparams.infile}")
1573    print(f"Output file: {cparams.outfile}")
1574
1575# Load modules
1576    print("")
1577    print(f"Load modules:")
1578    module_names, modules = app.load_modules(cparams.plugin_dir, "*.py", target = "read_data", is_print = True)
1579    for m in modules:
1580#        if hasattr(m, "initialize"):
1581#            print(f"initialize {m.name}")
1582#            m.initialize(app, cparams)
1583        input_type  = m.get_input_type(app = app, cparams = cparams)
1584        output_type = m.get_output_type(app = app, cparams = cparams)
1585        print(f"  {m.name}: input_type={input_type}  output_type={output_type}")
1586
1587    if cparams.mode == 'plot':
1588        inf = read_all_files(app, cparams, input_only = True)
1589        plot_input(inf, app, cparams)
1590    elif cparams.mode == 'overwrap':
1591        inf = read_all_files(app, cparams)
1592        check_overwrap(inf, app, cparams)
1593    elif cparams.mode == 'CIFcorrelation':
1594        inf = read_all_files(app, cparams)
1595        CIF_correlation(inf, app, cparams)
1596    elif cparams.mode == 'correlation':
1597        inf = read_all_files(app, cparams)
1598        correlation(inf, app, cparams)
1599    elif cparams.mode == 'plot_all':
1600        inf = read_all_files(app, cparams)
1601        plot_all(inf, app, cparams)
1602    elif cparams.mode == 'plot_one':
1603        inf = read_all_files(app, cparams)
1604        plot_one(inf, app, cparams)
1605    elif cparams.mode == 'plot_one2':
1606        inf = read_all_files(app, cparams)
1607        plot_one2(inf, app, cparams)
1608    elif cparams.mode == 'fit':
1609        inf = read_all_files(app, cparams)
1610        fit(inf, app, cparams)
1611
1612    app.terminate(usage = usage)
1613
1614
1615if __name__ == "__main__":
1616    main()