Individual peak profile fitting of Si XRD data.
# ippf_Si.py
# 個別ピークデータの抽出と描画,
# scipy.optimize.leastsq() メソッドによる非対称化 Voigt 曲線あてはめ
# Peak profile model
# defined as an asymmetrized function of the convolution of the Lorentzian function
# with a symmetric function defined by standard deviation and kurtosis
#
# Coded by T. Ida, Apr. 14, 2023
# Updated by T. Ida, May 10, 2023
# Updated by T. Ida, Apr. 28, 2024
#
import sys
import numpy as np
import scipy.special as sp
import scipy.optimize as opt
import matplotlib.pyplot as plt
import warnings
from scipy.optimize import OptimizeWarning
def main():
# 入力ファイル名の指定...
sRaw = '00raw.csv' # 処理前ファイル名
sDCT = '01dct.csv' # 処理後ファイル名
deg_hw = 1.0 # 参照ピーク位置 ± deg_hw の範囲のデータを抽出する
# 入力ファイルからのデータの読み込み...
source1 = np.loadtxt(sRaw,dtype='float',delimiter=',') # 処理前データの読み込み
x = source1[:,0] # x は各行 0 列目
y1 = source1[:,1] # y (raw) は各行 1 列目
source2 = np.loadtxt(sDCT,dtype='float',delimiter=',') # 処理後データの読み込み
y2 = source2[:,1] # y (DCT) は各行 1 列目
# 単純な誤差モデル(正当化できるかは不明...)
err = np.sqrt(np.maximum(1,y2))
dx = x[1] - x[0] # 標本点の間隔
nP = x.size # 標本点の数
# Peak data [2theta, d, I, h, k, l] for Si & LaB6 / Cu K_alpha
# (NIST SRM640d & SRM660b)
srm = 640 # SRM640d
if (srm == 640): # SRM 640 シリーズ Si の場合
peaks=[[28.441,3.1380,100,1,1,1],[47.300,1.9200,60,2,2,0]]
peaks+=[[56.119,1.6380,35,3,1,1],[69.126,1.3570,8,4,0,0]]
peaks+=[[76.371,1.2460,13,3,3,1],[88.024,1.1083,17,4,2,2]]
peaks+=[[94.946,1.0450,9,5,1,1],[106.700,0.9599,5,4,4,0]]
peaks+=[[114.082,0.9178,11,5,3,1],[127.532,0.8586,9,6,2,0]]
peaks+=[[136.877,0.8281,5,5,3,3]]
else: # SRM 660 シリーズ LaB6 の場合
peaks=[[21.358,4.157660,54,1,0,0],[30.385,2.939180,100,1,1,0]]
peaks+=[[37.442,2.399800,41,1,1,1],[43.507,2.077980,22,2,0,0]]
peaks+=[[48.958,1.858620,46,2,1,0],[53.989,1.696870,24,2,1,1]]
peaks+=[[63.219,1.469700,8,2,2,0],[67.548,1.385340,23,3,0,0]]
peaks+=[[71.746,1.314350,16,3,1,0],[75.844,1.253290,10,3,1,1]]
peaks+=[[79.870,1.200010,2,2,2,2],[83.846,1.152880,6,3,2,0]]
peaks+=[[87.792,1.110970,13,3,2,1],[95.672,1.039280,2,4,0,0]]
peaks+=[[99.643,1.008220,8,4,1,0],[103.661,0.979826,7,4,1,1]]
peaks+=[[107.749,0.953625,3,3,3,1],[111.934,0.929477,4,4,2,0]]
peaks+=[[116.245,0.907086,9,4,2,1],[120.723,0.886245,3,3,3,2]]
peaks+=[[130.409,0.848500,3,4,2,2],[135.800,0.831399,3,5,0,0]]
#
peaks = np.array(peaks) # NumPy 配列 (11行6列)または(22行6列)
nRow = peaks.shape[0] # 行数
nCol = peaks.shape[1] # 列数
ttRef = np.zeros(nRow) # 参照ピーク位置用の NumPy 配列の確保
ttOpt = np.zeros(nRow) # 最適化ピーク位置用の NumPy 配列の確保
indRef = np.arange(nRow,dtype='int') # 参照番号用の整数配列の確保
deltaTT = np.zeros(nRow) # Δ2Θ
err_ttOpt = np.zeros(nRow) # 最適化ピーク位置の推定誤差用の NumPy 配列の確保
gamOpt = np.zeros(nRow) # 非対称性パラメータ用の NumPy 配列の確保
err_gamOpt = np.zeros(nRow) # 非対称性パラメータ推定誤差用の NumPy 配列の確保
#
iRow = 0 # 参照ピークデータの行番号
iOpt = 0 # 有効なピークの番号
while (iRow < nRow):
ttRef[iOpt] = peaks[iRow][0] # 初期ピーク位置
# ピーク位置周辺データの抽出
jFirst = np.maximum(0,np.floor((peaks[iRow][0] - deg_hw - x[0]) / dx).astype(int))
jLast = np.minimum(nP,np.ceil((peaks[iRow][0] + deg_hw - x[0]) / dx).astype(int))
xsub, y1sub, y2sub = x[jFirst:jLast], y1[jFirst:jLast], y2[jFirst:jLast]
errsub = err[jFirst:jLast]
# はじめの推定値
bkg = 0.5*(y2[jFirst]+y2[jLast]) # 定数背景強度推定値
intn = (y2[jFirst:jLast].mean()-bkg)*dx*(jLast-jFirst) # ピーク積分強度推定値
xpeak = x[np.round(0.5*(jFirst+jLast)).astype(int)] # ピーク位置推定値
sigm = 0.1 # Gauss 型成分標準偏差初期値
w = 180/np.pi*0.00035*np.tan(xpeak*np.pi/360) # Lorentz 型成分半値半幅初期値
w_sigm = w/sigm # Lorentzian 半値半幅と Gaussian 標準偏差の比
gam = -0.01 # 裁断指数函数型成分の減衰幅初期値
gam_sigm = gam/sigm # 裁断指数函数型成分減衰幅と Gaussian 標準偏差の比
param = np.array([bkg,intn,xpeak,sigm,w_sigm,gam_sigm]) # 初期推定パラメータ (NumPy 配列)
param0 = np.copy(param) # 初期推定パラメータの複製・保持
#
# 最適化アルゴリズム
# method = 'curve_fit' # 'curve_fit','leastsq',or 'minimize(BFGS)',...
method = 'leastsq' # 'curve_fit','leastsq',or 'minimize(BFGS)',...
# method = 'minimize(BFGS)' # 'curve_fit','leastsq',or 'minimize(BFGS)',...
#
# 初期推定値についてのコンソール表示
sText = "{:d}".format(peaks[iRow][3].astype(int)) # index h
sText += "{:d}".format(peaks[iRow][4].astype(int)) # index k
sText += "{:d}".format(peaks[iRow][5].astype(int)) # index l
print("*****")
print('Optimization for ' + sText + '-reflection, '+method+' method')
print('Initial Guess...')
print(' Background : {:.0f}'.format(bkg))
print(' Integrated intensity : {:.0f}'.format(intn))
print(' Peak location : {:.4f}'.format(xpeak))
print(' Gaussian sigma : {:.4f}'.format(sigm))
print(' Lorentzian HWHM : {:.4f}'.format(w))
print(' Asymmetry: {:.4f}'.format(gam))
chi_sq = f_cost(param,xsub,y2sub,errsub) # コスト函数
print(' ----')
print(' chi**2 = ',chi_sq)
# 曲線当て嵌め
opt_results={'success':False,'popt':np.array([]),'psigma':np.array([]),'nfev':0} # 辞書型変数の初期化
warnings.simplefilter("error", OptimizeWarning)
try:
if (method == 'curve_fit'): # "scipy.optimize.curve_fit()" method
popt,pcov,infodict,mesg,ier = opt.curve_fit(f_model,xsub,y2sub,p0=param0,sigma=errsub, \
absolute_sigma=True,method='lm',full_output=True)
psigma = np.sqrt(np.diag(pcov))
opt_results={'success':True,'popt':popt,'psigma':psigma,'nfev':infodict['nfev']}
elif (method == 'leastsq'): # "scipy.optimize.leastsq()" method
popt,pcov,infodict,mesg,ier = opt.leastsq(f_residual,param0,args=(xsub,y2sub,errsub),maxfev=2800,full_output=True)
# maxfev: default value = (6+1)*200
psigma = np.sqrt(np.diag(pcov))
opt_results={'success':True,'popt':popt,'psigma':psigma,'nfev':infodict['nfev']}
elif (method == 'minimize(BFGS)'): # "scipy.optimize.minimize()" method
result = opt.minimize(f_cost,param0,args=(xsub,y2sub,errsub),method='BFGS')
popt = result.x
psigma = np.sqrt(np.diag(result.hess_inv))
nfev = result.nfev
opt_results={'success':True,'popt':popt,'psigma':psigma,'nfev':nfev}
except ValueError as e: # "ValueError" が起こった場合
sys.stderr.write("*** ValueError in "+method+" ***\n")
sys.stderr.write(str(e) + "\n")
opt_results={'success':False,'popt':np.array([]),'psigma':np.array([])}
except OptimizeWarning as e: # "OptimizeWarning" が起こった場合
sys.stderr.write("*** OptimizeWarning in "+method+" ***\n")
sys.stderr.write(str(e) + "\n")
opt_results={'success':False,'popt':np.array([]),'psigma':np.array([])}
except Exception as e:
sys.stderr.write("*** Exception in "+method+" ***\n")
sys.stderr.write(str(e) + "\n")
opt_results={'success':False,'popt':np.array([]),'psigma':np.array([])}
# print('opt_results = ',opt_results)
if (opt_results['success']): # 最適化に成功した場合 ...
# コンソール出力...
print("=====")
print('Optimization succeeded.')
popt = opt_results['popt']
psigma = opt_results['psigma']
# 最適化の結果 ...
print('Optimized ...')
print(' Background : {:.0f}'.format(popt[0]),end='')
print(' ± {:.0f}'.format(psigma[0]))
print(' Integrated intensity : {:.0f}'.format(popt[1]),end='')
print(' ± {:.0f}'.format(psigma[1]))
ttRef[iOpt] = peaks[iRow][0]
ttOpt[iOpt] = popt[2] # Peak 2Theta optimized
err_ttOpt[iOpt] = psigma[2] # Estimated error in peak 2Theta
print(' Peak location : {:.4f}'.format(popt[2]),end='')
print(' ± {:.4f}'.format(psigma[2]))
sigm_opt = popt[3]
sigm_sigma = psigma[3]
print(' Gaussian sigma : {:.4f}'.format(sigm_opt),end='')
print(' ± {:.4f}'.format(sigm_sigma))
w_opt = popt[4] * sigm_opt
w_sigma = w_opt * np.sqrt((psigma[4]/popt[4])**2 + (sigm_sigma/sigm_opt)**2)
print(' Lorentzian HWHM : {:.4f}'.format(w_opt),end='')
print(' ± {:.4f}'.format(w_sigma))
gamOpt[iOpt] = popt[5]*popt[3]
err_gamOpt[iOpt] = popt[5]*popt[3] * np.sqrt((psigma[5]/popt[5])**2 + (psigma[3]/popt[3])**2)
print(' Asymmetry: {:.4f}'.format(gamOpt[iOpt]),end='')
print(' ± {:.4f}'.format(err_gamOpt[iOpt]))
print(' ----')
chi_sq = f_cost(popt,xsub,y2sub,errsub) # コスト函数
print(' chi**2 = ',chi_sq)
print(' Number of function calls = ',opt_results['nfev'])
#
fit = f_model_AsymVoigt(xsub,popt) # fit curve
res = y2sub - fit # residual
# y1 と y2 の最大の値 ...
y_max = np.max(y1[jFirst:jLast])
y_max = np.maximum(y_max,np.max(y2[jFirst:jLast]))
# グラフにテキストを書き入れる位置の指定 ...
xText = x[jFirst]+(x[jLast]-x[jFirst])*0.05
yText = y_max * 0.9
#
###################################################
# Make a fitting figure with a difference plot
# 差プロットを付けたピーク形状フィッティング図の描画 (ピークシフト)
###################################################
plt.rcParams['font.family'] = 'Serif' # セリフ体のフォントを使う
plt.rcParams['mathtext.fontset'] = 'stix' # 数式には stix フォントを使う
# plt.rcParams['font.size'] = 9 # フォントサイズは 9 pt
fig = plt.figure(figsize=(6.4,3.6)) # 幅 6.4 インチ,高さ 3.6 インチ
gs = fig.add_gridspec(nrows=2,ncols=1,hspace=0.0,height_ratios=(7,3))
# 2つのパネル を上下 7:3 で配置
ax0 = fig.add_subplot(gs[0,0]) # 0 行目 (フィッティング図)
ax1 = fig.add_subplot(gs[1,0]) # 1 行目 (差プロット)
#######################################
# ピーク形状フィッティング図(散布図と折れ線グラフ)
#######################################
# 逆畳込的処理前の強度図形,折れ線グラフ.
ax0.plot(xsub,y1sub,label= 'raw') # 折れ線グラフ,凡例ラベル設定
# 逆畳込的処理後の強度図形,散布図(マーカーでプロット)
ax0.scatter(xsub,y2sub,label= 'DCT',marker='o',color='orange') # 散布図,凡例ラベル設定
ax0.xaxis.set_label_position('top')
ax0.xaxis.tick_top()
ax0.minorticks_on() # 補助目盛表示
ax0.tick_params(length=6) # 目盛線の長さ 6 pt (約 2.1 mm)
ax0.plot(xsub,fit,label='fit',color='green')
ax0.text(xText,yText,sText+'\n'+method+' method')
ax0.axvline(x=peaks[iRow][0],linewidth=0.5) # 垂直線描画
# ax0.set_xlabel('$2\Theta (^\circ)$') # x 軸ラベル
ax0.set_ylabel('Intensity (counts)') # y 軸ラベル
ax0.set_xlim(x[jFirst],x[jLast]) # x 軸描画範囲
ax0.set_ylim(0,y_max/0.9) # y 軸描画範囲
handle, label = ax0.get_legend_handles_labels() # 凡例のハンドルとラベル
# print('handle = ',handle)
# print('label = ',label)
handle=[handle[0],handle[2],handle[1]] # 凡例の順序を変える
label=[label[0],label[2],label[1]] # 凡例ラベルの順序を変える
# print('handle = ',handle)
# print('label = ',label)
ax0.legend(handle,label)
#####################
# 差プロット(折れ線グラフ)
#####################
ax1.plot(xsub,res,label = 'residual',color='orange')
ax1.axvline(x=peaks[iRow][0],linewidth=0.5) # 垂直線描画
ax1.axhline(y=0,linewidth=0.5) # 水平線描画
ax1.set_xlim(x[jFirst],x[jLast])
res_y_max = y_max/0.9*3/7*0.5
ax1.set_ylim(-res_y_max,res_y_max)
ax1.set_ylabel('Difference (counts)',labelpad=20)
ax1.tick_params(labelleft=False)
ax1.set_xlabel('$2\Theta (^\circ)$')
ax1.minorticks_on() # 補助目盛表示
ax0.tick_params(length=6) # 目盛線の長さ 6 pt (約 2.1 mm)
plt.subplots_adjust(hspace=0.0)
ax1.legend() # 凡例描画
#
file_name = 'fig{0:03}.png'.format(iOpt+1)
plt.savefig(file_name,dpi=600)
plt.show()
#
iOpt += 1 # 最適化成功インデックスを 1 増やす
else: # 最適化に失敗した場合
print('Optimization failed for ' + sText + '-reflection')
iRow += 1
nOpt = iOpt # 最適化に成功したピークの数
print("Number of optimized/total peaks : ",nOpt,"/",nRow)
indRef = np.resize(indRef,nOpt)
ttRef = np.resize(ttRef,nOpt)
ttOpt = np.resize(ttOpt,nOpt)
deltaTT = ttOpt - ttRef
err_ttOpt = np.resize(err_ttOpt,nOpt)
gamOpt = np.resize(gamOpt,nOpt)
err_gamOpt = np.resize(err_gamOpt,nOpt)
#
########################################
# 素朴ピークシフトモデルによるピークシフトのモデル化
########################################
print('')
print("*****")
print('Optimization for peak shift by a naive model')
# 初期推定値についてのコンソール表示
param0 = np.array([0.01,0.01])
print("*****")
print('Initial Guess...')
print(' Δ2Θ_0 (º) : {:.4f}'.format(param0[0]))
print(' Specimen displacement (mm) : {:.4f}'.format(param0[1]))
res = f_res_naive(param0,ttRef,deltaTT,err_ttOpt) # 残差
chi_sq = np.sum(res**2)
print(' chi**2 = ',chi_sq)
print("Optimization starts...")
# print('ttRef = ',ttRef)
# print('deltaTT = ',deltaTT)
# print('err_ttOpt = ',err_ttOpt)
opt_results = {'success':False,'popt':np.array([]),'psigma': np.array([])}
try:
# popt,pcov = opt.curve_fit(f_model_naive,ttRef,deltaTT,err_ttOpt,p0=param0,sigma=err_gamOpt,method='lm')
popt,pcov,infodict,mesg,ier = opt.leastsq(f_res_naive,param0,args=(ttRef,deltaTT,err_ttOpt),maxfev=2800,full_output=True)
psigma = np.sqrt(np.diag(pcov))
opt_results = {'success':True,'popt':popt,'psigma':psigma}
except ValueError as e:
print("Optimization failed")
sys.stderr.write("*** ValueError in "+method+" ***\n")
sys.stderr.write(str(e) + "\n")
print('')
opt_results = {'success':False,'popt':np.array([]),'psigma': np.array([])}
except OptimizeWarning as e:
print("Optimization failed")
sys.stderr.write("*** OptimizeWarning in "+method+" ***\n")
sys.stderr.write(str(e) + "\n")
print('')
opt_results = {'success': False,'popt':np.array([]),'psigma': np.array([])}
if (opt_results['success']):
print("Optimization finished.")
popt = opt_results['popt']
psigma = opt_results['psigma']
print(' Δ2Θ_0 = {:.4f}º ± {:.4f}º'.format(popt[0],psigma[0]))
print(' Specimen displacement: {:.4f} ± {:.4f} mm'.format(popt[1],psigma[1]))
res = f_res_naive(popt,ttRef,deltaTT,err_ttOpt) # 残差
chi_sq = np.sum(res**2)
print(' chi**2 = ',chi_sq)
#
fig = plt.figure(figsize=(6.4,3.6)) # 寸法を指定して figure を作成
gs = fig.add_gridspec(nrows=2,ncols=1,hspace=0.0,height_ratios=(7,3))
# 2つのパネル を上下 7:3 で配置
ax0 = fig.add_subplot(gs[0,0]) # 0 行目(上パネル) (フィッティング図)
ax1 = fig.add_subplot(gs[1,0]) # 1 行目(下パネル)(差プロット)
#######################################################################
# (上パネル)ピークシフト Δ2Θ の回折角依存性とフィッティング図(散布図と折れ線グラフ) の描画
#######################################################################
ax0.xaxis.tick_top() # 上パネルでは x軸目盛を上辺に
ax0.minorticks_on() # 補助目盛表示
ax0.set_xlabel(r'$2\Theta\ (^\circ)$') # x軸ラベル: 2Θ (º)
ax0.set_ylabel(r'$\Delta 2\Theta\ (^\circ)$') # y軸ラベル: Δ2Θ (º)
# ピークシフトの回折角依存性 Δ2Θ,散布図(誤差棒つきマーカーでプロット)
error_bar_label = r'$\Delta 2 \Theta \,(^\circ)$' # ピークシフト,マーカー凡例ラベル
ax0.errorbar(ttRef,deltaTT,yerr=err_ttOpt,fmt='o',capsize=5,label=error_bar_label)
# 当て嵌め曲線(折れ線グラフ)
dttFit_x = np.linspace(ttRef[0],ttRef[-1],400)
dttFit_y = f_model_naive(dttFit_x,popt[0],popt[1])
fit_curve_label = 'Naive model' # 当て嵌め曲線凡例ラベル
ax0.plot(dttFit_x,dttFit_y,label=fit_curve_label)
ax0.axhline(y=0.0,linewidth=0.5) # 水平線描画
# ピークシフトと回折ピーク角の最大値と最小値 ...
x_min = np.min(ttRef)
x_max = np.max(ttRef)
y_min = np.minimum(np.min(deltaTT),np.min(dttFit_y))
y_max = np.maximum(np.max(deltaTT),np.max(dttFit_y))
# x軸と y軸の描画範囲...
ax0_x_min = np.maximum(0.0,x_min-(x_max-x_min)*0.1)
ax0_x_max = np.minimum(180.0,x_max+(x_max-x_min)*0.1)
ax0_y_min = y_min-(y_max-y_min)*0.1
ax0_y_max = y_max+(y_max-y_min)*0.1
ax0.set_xlim(ax0_x_min,ax0_x_max) # x 軸描画範囲
ax0.set_ylim(ax0_y_min,ax0_y_max) # y 軸描画範囲
handle, label = ax0.get_legend_handles_labels() # 凡例のハンドルとラベル
# print('handle,label = ',handle,lsigma)
handle=[handle[1],handle[0]] # 凡例の順序を変える
label=[label[1],label[0]] # 凡例ラベルの順序を変える
ax0.legend(handle,label) # 上パネル凡例表示
#######################################
# (下パネル)ピークシフトモデルについての差プロット
#######################################
ttRes = deltaTT - f_model_naive(ttRef,popt[0],popt[1])
ax1.plot(ttRef,ttRes,label='residual')
ax1.minorticks_on()
ax1.tick_params(length=6)
ax1.set_xlabel(r'$2\Theta\ (^\circ)$') # x軸ラベル 2Θ (º)
ax1.set_ylabel(r'${\rm Difference}\ (^\circ)$') # y軸ラベル Difference (º)
ax1.set_xlim(ax0_x_min,ax0_x_max) # x軸描画範囲は ax0 と揃える
ax1_y_max = (ax0_y_max-ax0_y_min)*3.0/7.0/2 # ax0 と尺度を揃える
ax1.set_ylim(-ax1_y_max,ax1_y_max) # y軸描画範囲
ax1.axhline(y=0,linewidth=0.5) # 水平線描画
ax1.legend() # 下パネル凡例表示
#
plt.savefig('fig{0:03}.png'.format(nOpt+1),dpi=600)
plt.show()
delTT = ttOpt - ttRef
################################
# 非対称性パラメータの角度依存性の表示
################################
print('')
print("*****")
print("Weighted average of asymmetry parameter...")
numer = np.sum(gamOpt/err_gamOpt**2) # numerator
denom = np.sum(1.0/err_gamOpt**2) # denominator
avg_gamOpt = numer / denom
print(" Average of asymmetry (º) : {0:4f}".format(avg_gamOpt))
print(" -----")
###################################################
# Show a figure with average level
# 平均値表示を付けたピーク形状フィッティング図の描画
# (非対称パラメータ)
###################################################
# 非対称性パラメータの回折角依存性 散布図(誤差棒つきマーカーでプロット)
fig = plt.figure(figsize=(6.4,3.6)) # 幅 6.4 インチ,高さ 3.6 インチ
gs = fig.add_gridspec(nrows=1,ncols=1)
ax0 = fig.add_subplot(gs[0,0]) # 0 行目 (フィッティング図)
error_bar_label = r'$\gamma \ (^\circ)$' # ピークシフト,マーカー凡例ラベル
ax0.errorbar(ttRef,gamOpt,yerr=err_gamOpt,fmt='o',capsize=5,label=error_bar_label)
# 平均線描画
avg_x = np.array([ttRef[0],ttRef[-1]])
avg_y = np.array([avg_gamOpt,avg_gamOpt])
avg_line_label = 'Average' # 平均線凡例ラベル
ax0.plot(avg_x,avg_y,label=avg_line_label)
# ゼロ線描画
ax0.axhline(y=0.0,linewidth=0.5)
# 回折ピーク角と非対称パラメータの最大値と最小値 ...
x_min = np.min(ttRef)
x_max = np.max(ttRef)
y_min = np.min(gamOpt)
y_max = np.max(gamOpt)
ax0_x_min = np.maximum(0.0,x_min-(x_max-x_min)*0.1) # ± 10 % の余白をつける
ax0_x_max = np.minimum(180.0,x_max+(x_max-x_min)*0.1)
ax0_y_min = np.minimum(0,y_min-(y_max-y_min)*0.1)
ax0_y_max = np.maximum(0,y_max+(y_max-y_min)*0.1)
ax0.minorticks_on() # 補助目盛表示
ax0.set_xlabel(r'$2\Theta\ (^\circ)$') # x軸ラベル: 2Θ (º)
ax0.set_ylabel(r'$\gamma\ (^\circ)$') # y軸ラベル: Δ2Θ (º)
ax0.set_xlim(ax0_x_min,ax0_x_max) # x 軸描画範囲
ax0.set_ylim(ax0_y_min,ax0_y_max) # y 軸描画範囲
ax0.xaxis.set_ticks_position('both')
ax0.yaxis.set_ticks_position('both')
handle, label = ax0.get_legend_handles_labels() # 凡例のハンドルとラベル
handle=[handle[1],handle[0]] # 凡例の順序を変える
label=[label[1],label[0]] # 凡例ラベルの順序を変える
ax0.legend(handle,label) # 上パネル凡例表示
plt.savefig('fig{0:03}.png'.format(nOpt+2),dpi=600)
plt.show()
#****************************************************
# Model function for scipy.optimize.curve_fit()
#****************************************************
def f_model(data_x,*param): # モデル函数
# param: parameters to be optimized (NumPy array)
# data_x: x data (NumPy arrays)
return f_model_AsymVoigt(data_x, param)
#**********************************************
# Residual function for scipy.optimize.leastsq
#**********************************************
def f_residual(param,*data): # 残差函数
# param: parameters to be optimized (NumPy array)
data_x,data_y,data_e = data
# data_x, data_y, data_e: x, y, error data (NumPy arrays)
fit_y = f_model_AsymVoigt(data_x, tuple(param))
residual = (data_y - fit_y) / data_e
return residual
#***********************************************
# Cost function for scipy.optimize.minimize
#***********************************************
def f_cost(param,data_x,data_y,data_e): # コスト函数
residual = f_residual(param,data_x,data_y,data_e)
chisq = np.sum(residual**2) # 重み付き平方和
# print('chisq = ',chisq)
return chisq
#*******************************
# Asymmetrized Voigtian profile
#*******************************
def asym_voigt_profile(x, sigm, w, gam):
ans = np.where(gam != 0, asym_sub(x,sigm,w,gam), sp.voigt_profile(x,sigm,w))
return ans
def asym_sub(x, sigm, w, gam):
n_x = x.size
n_z = 20
dz = 1.0 / n_z
# (1/n_z) * \sum_{i=1}^{n_z} fVoigt(x+gam*ln(z),sigm,w) * dz
z = dz/2 + np.linspace(0,1.0,num=n_z,endpoint=False,dtype='float')
a = np.repeat(x,n_z)
a = np.reshape(a,(n_x,n_z))
b = np.tile(z,n_x)
b = np.reshape(b,(n_x,n_z))
y = np.resize(gam*np.log(b),(n_x,n_z))
x_y = a + gam*np.log(b)
c = sp.voigt_profile(x_y,sigm,w) * dz
ans = np.sum(c, axis=1)
return ans
######################################
# Asymmetrized Voigtian profile model
######################################
def f_model_AsymVoigt(x, args):
b,I,x0,sigm,w_sigm,gam_sigm = args
# b: background
# I: intensity
# x0: peak location
# sigm: sigma of non-Lorentzian component
# w_sigm: Lorentzian HWHM / sigm
# gam_sigm: asymmetry parameter / sigm
return b + I * asym_voigt_profile(x - x0, sigm, w_sigm*sigm, gam_sigm*sigm)
################################################
# Naive model for peak shift ピークシフトの素朴モデル
################################################
# def f_model_naive(degTT,*param):
# degDelTT0,sDis = param
def f_model_naive(degTT,degDelTT0,sDis):
# degTT: 2Theta (deg.) 回折ピーク位置
# degDelTT0: Delta 2 Theta_0 (deg.) オフセット誤差
# sDis: specimen displacement (mm) 試料位置ずれ
R = 150 # goniometer radius (mm)
return degDelTT0 + (360/np.pi)*sDis*np.cos(degTT*np.pi/360)/R
def f_res_naive(param,ttRef,deltaTT,errTT): # 残差函数
dTTobs = deltaTT
dTTcal = f_model_naive(ttRef,param[0],param[1])
residual = (dTTobs-dTTcal)/errTT # 重み付き残差
return residual
##################
# Asymmetry model
##################
def f_model_asymmetry(degTT,muInv):
R =150
return -muInv*np.sin(degTT*np.pi/180)/(2*R)*180/np.pi
def f_res_asymmetry(param,ttRef,gamOpt,err_gamOpt):
gamCal = f_model_asymmetry(ttRef,param[0])
residual = (gamOpt-gamCal)/err_gamOpt
return residual
# Test routine ...
# testx = np.arange(-1.0,1.0,0.01)
# testy = fLorSymFit(testx, 10, 100, 0.01, 0.1,0.1,4)
# fig, ax = plt.subplots()
# ax.plot(testx,testy)
# plt.show()
main()