xrd_pymatgen.py ダウンロード/コピー
xrd_pymatgen.py
xrd_pymatgen.py
1#Pymatgenチュートリアル⑥ XRDのシミュレーションをする https://qiita.com/ojiya/items/1b154c3698cff91c8a2b
2
3"""
4X線回折(XRD)パターンシミュレーションスクリプト。
5
6概要:
7 Pymatgenライブラリを使用して結晶のX線回折パターンをシミュレーションし、結果をプロット・保存します。
8
9詳細説明:
10 本スクリプトは、CIFファイルから結晶構造を読み込み、指定されたX線源(例: CuKa1)と2θ範囲でXRDパターンを計算します。
11 計算された個々の回折ピークに対してガウス関数を適用し、指定された半値半幅 (FWHM) でスムージングされたスペクトルを生成します。
12 結果の回折ピーク情報と生成されたスペクトルデータは、標準出力、指定されたログファイル、およびExcelファイルに保存されます。
13 また、計算されたスペクトルと個々のピークはmatplotlibによってプロットされ、視覚的に表示されます。
14 コマンドライン引数により、入力CIFファイル、X線源、2θの最小値・最大値・ステップ、FWHMなどを柔軟に設定できます。
15
16関連リンク:
17 - Pymatgen XRDCalculator ドキュメント:
18 https://pymatgen.org/pymatgen.analysis.diffraction.xrd.html
19 - Pymatgenチュートリアル(Qiita):
20 https://qiita.com/ojiya/items/1b154c3698cff91c8a2b
21 - Pymatgen XRDソースコード (GitHub):
22 https://github.com/materialsproject/pymatgen/blob/v2023.5.10/pymatgen/analysis/diffraction/xrd.py
23"""
24
25import math
26from math import exp
27import numpy as np
28import scipy as sp
29import scipy.special
30import matplotlib.pyplot as plt
31
32from pymatgen.core.periodic_table import Element
33from pymatgen.io.cif import CifParser
34from pymatgen.core.structure import Structure
35from pymatgen.core.lattice import Lattice
36from pymatgen.analysis.diffraction.xrd import XRDCalculator, WAVELENGTHS
37from pymatgen.analysis.diffraction.tem import TEMCalculator
38from pymatgen.analysis.diffraction.neutron import NDCalculator
39
40from tklib.tkapplication import tkApplication
41from tklib.tkutils import terminate, pint, pfloat, getarg, getintarg, getfloatarg
42from tklib.tkvariousdata import tkVariousData
43from tklib.tkgraphic.tkplotevent import tkPlotEvent
44
45
46infile = 'SrTiO3.cif'
47
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"""
53WAVELENGTHS = {
54 "CuKa": 1.54184,
55 "CuKa2": 1.54439,
56 "CuKa1": 1.54056,
57 "CuKb1": 1.39222,
58 "MoKa": 0.71073,
59 "MoKa2": 0.71359,
60 "MoKa1": 0.70930,
61 "MoKb1": 0.63229,
62 "CrKa": 2.29100,
63 "CrKa2": 2.29361,
64 "CrKa1": 2.28970,
65 "CrKb1": 2.08487,
66 "FeKa": 1.93735,
67 "FeKa2": 1.93998,
68 "FeKa1": 1.93604,
69 "FeKb1": 1.75661,
70 "CoKa": 1.79026,
71 "CoKa2": 1.79285,
72 "CoKa1": 1.78896,
73 "CoKb1": 1.63079,
74 "AgKa": 0.560885,
75 "AgKa2": 0.563813,
76 "AgKa1": 0.559421,
77 "AgKb1": 0.497082,
78}
79"""
80
81Xray_source = 'CuKa1'
82
83Q2min = 10.0
84Q2max = 80.0
85Q2step = 0.02
86
87fwhm = 0.05
88
89figsize = (8, 4)
90fontsize = 12
91
92
93app = tkApplication()
94infile = getarg( 1, infile)
95Xray_source = getarg( 2, Xray_source)
96Q2min = getfloatarg( 3, Q2min)
97Q2max = getfloatarg( 4, Q2max)
98Q2step = getfloatarg( 5, Q2step)
99fwhm = getfloatarg( 6, fwhm)
100
101
102#==========================================
103# Main prgram
104#==========================================
105def Gaussian(x, x0, whalf):
106 """
107 ガウス関数を計算します。
108
109 概要:
110 ピークの中心と半値半幅を用いてガウス分布の値を計算します。
111 この関数はXRDパターンシミュレーションにおいて、各回折ピークの形状を表現するために使用されます。
112
113 Parameters:
114 :param x: float or numpy.ndarray - ガウス関数を評価する点または点の配列。
115 :param x0: float - ガウス関数のピーク中心(位置)。
116 :param whalf: float - ガウス関数の半値半幅 (FWHM)。
117
118 Returns:
119 :returns: float or numpy.ndarray - `x`におけるガウス関数の値。
120 """
121 a = whalf / 0.832554611
122 X = (x - x0) / a
123 return exp(-X * X)
124
125
126#==========================================
127# Main routine
128#==========================================
129def main():
130 """
131 XRDパターンシミュレーションのメイン処理を実行します。
132
133 概要:
134 CIFファイルから結晶構造を読み込み、X線回折パターンを計算して可視化・保存します。
135
136 詳細説明:
137 1. コマンドライン引数またはデフォルト設定から、入力CIFファイル、X線源、
138 2θ範囲、スペクトルの半値半幅(FWHM)などのパラメータを読み込みます。
139 2. ログファイルおよび出力Excelファイルのパスを設定し、標準出力をログファイルにもリダイレクトします。
140 3. Pymatgenの`CifParser`でCIFファイルを解析し、結晶構造オブジェクトを取得します。
141 4. `XRDCalculator`を使用して、指定されたX線源と2θ範囲で回折ピークデータを計算します。
142 5. 各回折ピークに対して`Gaussian`関数を適用し、スムージングされたXRDスペクトルを生成します。
143 6. 計算された回折ピークの詳細と生成されたスペクトルデータを標準出力とExcelファイルに保存します。
144 7. matplotlibを使用して、スムージングされたXRDスペクトルと個々の回折ピークをプロットし、表示します。
145 プロットにはインタラクティブなイベントハンドラが設定されており、カーソル位置のデータ情報を表示できます。
146 8. スクリプトの終了時に一時停止します。
147
148 Parameters:
149 None (コマンドライン引数から設定を読み込みます)
150
151 Returns:
152 None
153 """
154 logfile = app.replace_path(infile, template = ["{dirname}", "{filebody}-out.txt"])
155 print(f"Open logfile [{logfile}]")
156 app.redirect(targets = ["stdout", logfile], mode = 'w')
157
158 outxlsxfile = app.replace_path(infile, template = ["{dirname}", "{filebody}-pxrd.xlsx"])
159 nQ2 = int((Q2max - Q2min) / Q2step + 1.000001)
160 dQ2 = 6.0 * fwhm
161 nconv = int(dQ2 / Q2step + 1.00001)
162
163 print("")
164 print(f"input: {infile}")
165 print(f"log file: {logfile}")
166 print(f"(output xlsx file: {outxlsxfile}")
167 print(f"X-ray source: {Xray_source}")
168 try:
169 wl = float(Xray_source)
170 except:
171 wl = WAVELENGTHS.get(Xray_source, None)
172 if wl:
173 print(f" wavelength: {wl} angstrom")
174 print(f"2Theta range: {Q2min:9.5g} - {Q2max:9.5g} at {Q2step:9.5g} degree ({nQ2} points)")
175 print(f"2Theta range for a peak: {dQ2:9.5g} degree for {nconv} points")
176 print(f" FWHM of Gaussian functin: {fwhm} degree")
177
178 x = np.arange(10,60,0.02)
179
180 parser = CifParser(infile)
181 mat = parser.get_structures(primitive=False, symmetrized=False)[0]
182
183 xrd = XRDCalculator(wavelength = Xray_source) #, debye_waller_factors = {Element('Sr'): 0.2, Element('O'): 0.6})
184 Q2min_diffraction = max([ 0.0, Q2min - dQ2])
185 Q2max_diffraction = min([180.0, Q2max + dQ2])
186 diffractions = xrd.get_pattern(mat, two_theta_range = (Q2min_diffraction, Q2max_diffraction))
187#diffractions.as_dict()
188
189 ndiffractions = len(diffractions.x)
190 Q2 = diffractions.x
191 dhkl = diffractions.d_hkls
192 hkl = [diffractions.hkls[i][0]['hkl'] for i in range(ndiffractions)]
193 mul = [diffractions.hkls[i][0]['multiplicity'] for i in range(ndiffractions)]
194# y includes mul, LP and T
195 Int = [diffractions.y[i] for i in range(ndiffractions)]
196
197 print("")
198 print(f"Diffractions: {ndiffractions} diffractions")
199 print(f"{'h':2} {'k':2} {'l':2} {'m':2} {'dhkl':8} {'2Theta':8} {'Intensity'}")
200 for i in range(len(Q2)):
201 h = hkl[i][0]
202 k = hkl[i][1]
203 if len(hkl[i]) == 4:
204 l = hkl[i][3]
205 else:
206 l = hkl[i][2]
207 print(f"{h:2} {k:2} {l:2} {mul[i]:2} {dhkl[i]:8.6f} {Q2[i]:8.6f} {Int[i]:8.4f}")
208# print(" diffractions.hkls[i]=", diffractions.hkls[i])
209
210 print("")
211 print("Calculate specrum")
212 xQ2 = np.arange(Q2min, Q2max + Q2step, Q2step)
213 xrd_cal = np.zeros(nQ2)
214 for idf in range(ndiffractions):
215 idx0 = int((Q2[idf] - Q2min) / Q2step)
216
217 for j in range(idx0 - nconv, idx0 + nconv + 1):
218 if j < 0 or j >= nQ2:
219 continue
220
221 xrd_cal[j] += Int[idf] * Gaussian(xQ2[j], Q2[idf], fwhm)
222
223 print("")
224 print(f"Save to [{outxlsxfile}]")
225 tkVariousData().to_excel(outxlsxfile, ['2Theta (degree)', 'Intensity'], [xQ2, xrd_cal])
226
227 mode = ''
228 if mode == 'xrd':
229 xrd_pattern = xrd.plot_structures(
230 [mat],
231 two_theta_range=[Q2min, Q2max],
232 show = True,
233# savefig="si_xrd.pdf"
234 )
235 exit()
236 elif mode == 'nd':
237 nd = NDCalculator()
238 nd_pattern = nd.plot_structures(
239 [mat],
240 two_theta_range=[Q2min, Q2max],
241 show = True,
242# savefig="si_nd.pdf"
243 )
244 exit()
245 elif mode == 'tem':
246 tem = TEMCalculator()
247 tem_pattern = tem.get_plot_2d(
248 mat
249 )
250 tem_pattern.write_image("si_tem.pdf")
251 exit()
252
253#=====================
254# plot
255#=====================
256
257 fig = plt.figure(figsize = figsize)
258 plot_event = tkPlotEvent(plt)
259
260 ax = fig.add_subplot(111)
261 ax.set_title(f"{infile} - source: {Xray_source}")
262 ax.tick_params(labelsize = fontsize)
263
264 ax.plot(xQ2, xrd_cal, color = 'black', linewidth = 0.5)
265 data = ax.plot(Q2, Int, linestyle = '', marker = 'o', markerfacecolor = 'red', markeredgecolor = 'red', markersize = 3.0)
266
267#plot_event.add_data({"label": "diffraction", "plot_type": "2D", "axis": ax, "data": [Q2, dhkl, hkl, mul, Int]})
268 hkl_str = [f"{hkl[i][0]} {hkl[i][1]} {hkl[i][2]}" for i in range(ndiffractions)]
269 plot_event.add_data({"label": "diffraction", "plot_type": "2D", "axis": ax, "data": data,
270 "x": Q2, 'y': Int, "xlabel": "2Theta", "ylabel": "Intensity",
271 "xlist": [Q2, dhkl, hkl_str, mul, Int],
272 "xlabels": ['Q2', 'dhkl', 'hkl', 'multiplicity', 'Intensity']
273 })
274
275 ax.set_xlim(Q2min, Q2max)
276
277 ax.set_xlabel(r'2$\theta$', fontsize = fontsize)
278 ax.set_ylabel('Intensity', fontsize = fontsize)
279
280 plot_event.register_event(fig, event = "button_press_event",
281 callback = lambda event: plot_event.onclick(event))
282
283 plt.tight_layout()
284 plt.pause(0.1)
285
286 app.terminate(pause = True)
287
288
289if __name__ == "__main__":
290 main()