XRD_diffraction_angles.py ダウンロード/コピー
XRD_diffraction_angles.py をダウンロード
XRD_diffraction_angles.py
XRD_diffraction_angles.py
1"""
2X線回折のブラッグ角を計算するスクリプト。
3
4詳細説明:
5このスクリプトは、CIF (Crystallographic Information File) 形式の結晶構造データから、
6X線回折におけるブラッグ角 (2θ)、面間隔 (d)、およびミラー指数 (hkl) を計算します。
7指定されたX線源の波長と最大2θ角に基づき、可能なすべての回折ピークを探索し、
8結果をソートして標準出力およびログファイルに出力します。
9
10関連リンク: :doc:`XRD_diffraction_angles_usage`
11"""
12import sys
13import os
14from numpy import sin, cos, tan, arcsin, arccos, arctan, exp, log, sqrt
15import numpy as np
16from numpy import linalg as la
17from mpl_toolkits.mplot3d import Axes3D
18import matplotlib.pyplot as plt
19
20
21from tklib.tkapplication import tkApplication
22from tklib.tkutils import terminate, pint, pfloat, getarg, getintarg, getfloatarg
23from tklib.tkfile import tkFile
24from tklib.tkcrystal.tkcif import tkCIF
25from tklib.tkcrystal.tkcrystal import tkCrystal
26from tklib.tkcrystal.tkatomtype import tkAtomType
27from tkcrystalbase import *
28
29
30WAVELENGTHS = {
31 "CuKa": 1.54184,
32 "CuKa2": 1.54439,
33 "CuKa1": 1.54056,
34 "CuKb1": 1.39222,
35 "MoKa": 0.71073,
36 "MoKa2": 0.71359,
37 "MoKa1": 0.70930,
38 "MoKb1": 0.63229,
39 "CrKa": 2.29100,
40 "CrKa2": 2.29361,
41 "CrKa1": 2.28970,
42 "CrKb1": 2.08487,
43 "FeKa": 1.93735,
44 "FeKa2": 1.93998,
45 "FeKa1": 1.93604,
46 "FeKb1": 1.75661,
47 "CoKa": 1.79026,
48 "CoKa2": 1.79285,
49 "CoKa1": 1.78896,
50 "CoKb1": 1.63079,
51 "AgKa": 0.560885,
52 "AgKa2": 0.563813,
53 "AgKa1": 0.559421,
54 "AgKb1": 0.497082,
55}
56
57
58infile = 'SrTiO3.cif'
59
60Xray_source = 'CuKa1'
61
62# G min to remove the origin of reciprocal space
63Gmin = 1.0e-5
64
65# 2Theta max
66Q2max = 150.0 # degree in 2Theta
67
68# Structure
69# Lattice parameters (angstrom and degree)
70#lattice_parameters = [ 5.62, 5.62, 5.62, 60.0, 60.0, 60.0]
71#sites = [
72# ['Na', 'Na1', 11, 22.98997, +1.0, 0.7, 'red', np.array([0.0, 0.0, 0.0])]
73# ]
74
75
76figsize = (8, 4)
77fontsize = 12
78
79
80#==========================================
81# Main prgram
82#==========================================
83app = tkApplication()
84infile = getarg( 1, infile)
85Xray_source = getarg( 2, Xray_source)
86Q2max = getfloatarg(3, Q2max)
87
88
89def main():
90 """
91 プログラムの主要な処理を実行し、X線回折のブラッグ角を計算して結果を出力します。
92
93 詳細説明:
94 この関数は、以下のステップでX線回折角の計算と出力を行います。
95 1. コマンドライン引数から入力ファイル名、X線源、最大2θ角を取得します。
96 2. ログファイルをオープンし、標準出力もそこにリダイレクトします。
97 3. 入力CIFファイルを読み込み、結晶構造情報を取得します。
98 4. 単位格子の情報(格子定数、格子系、格子ベクトルなど)を表示します。
99 5. 結晶サイト情報(原子の種類、位置など)を処理します。
100 6. 指定された最大2θ角に基づき、回折可能なhkl範囲を決定します。
101 7. 各hkl指数について、逆格子空間での距離Gを計算し、それから面間隔dと2θ角を計算します。
102 8. 計算された回折角、面間隔、hkl指数をリストに格納し、2θ角でソートします。
103 9. 重複する2θ角を持つピークをまとめ、多重度とともに整形して出力します。
104 10. プログラム終了時に一時停止します。
105
106 :returns: None
107 """
108 global lattice_parameters, sites
109
110 logfile = app.replace_path(infile, template = ["{dirname}", "{filebody}-out.txt"])
111 print(f"Open logfile [{logfile}]")
112 app.redirect(targets = ["stdout", logfile], mode = 'w')
113
114 print("")
115 print(f"input: {infile}")
116 print(f"log file: {logfile}")
117 print(f"X-ray source: {Xray_source}")
118 try:
119 wl = float(Xray_source)
120 except:
121 wl = WAVELENGTHS.get(Xray_source, None)
122 if wl:
123 print(f" wavelength: {wl} angstrom")
124 print(f"2Theta max: {Q2max:9.5g} degree")
125
126 print("")
127 print("Read [{}]".format(infile))
128 cif = tkCIF()
129 cif.debug = False
130 cifdata = cif.ReadCIF(infile, find_valid_structure = True)
131 cif.Close()
132 if not cifdata:
133 terminate("Error: Could not get cifdat from infile [{}]".format(infile))
134
135# cifdata.print()
136 cry = cifdata.GetCrystal()
137 cry.PrintInf()
138
139 lattice_parameters = cry.LatticeParameters()
140 ls = cry.lattice_system()
141 la = cry.lattice_axis()
142 aij = cry.LatticeVectors()
143 gij, Rgij = cry.Metrics()
144 volume = cry.Volume()
145
146 Raij = cry.ReciprocalLatticeVectors()
147 Rlatt = cry.ReciprocalLatticeParameters()
148 Rvolume = cry.ReciprocalVolume()
149
150 print("")
151 print("Lattice parameters:", lattice_parameters)
152 print(f"Lattice system: {ls}")
153 print(f"Unitcell axis system: {la}")
154
155 """
156 print("Lattice vectors:")
157 print(" ax: ({:10.4g}, {:10.4g}, {:10.4g}) A".format(aij[0][0], aij[0][1], aij[0][2]))
158 print(" ay: ({:10.4g}, {:10.4g}, {:10.4g}) A".format(aij[1][0], aij[1][1], aij[1][2]))
159 print(" az: ({:10.4g}, {:10.4g}, {:10.4g}) A".format(aij[2][0], aij[2][1], aij[2][2]))
160 print("Metric tensor:")
161 print(" gij: ({:10.4g}, {:10.4g}, {:10.4g}) A".format(*gij[0]))
162 print(" ({:10.4g}, {:10.4g}, {:10.4g}) A".format(*gij[1]))
163 print(" ({:10.4g}, {:10.4g}, {:10.4g}) A".format(*gij[2]))
164 print("Unit cell volume: {:12.4g} A^3".format(volume))
165
166 print("")
167 print("Reciprocal lattice parameters:", Rlatt)
168 print("Reciprocal lattice vectors:")
169 print(" Rax: ({:10.4g}, {:10.4g}, {:10.4g}) A^-1".format(*Raij[0]))
170 print(" Ray: ({:10.4g}, {:10.4g}, {:10.4g}) A^-1".format(*Raij[1]))
171 print(" Raz: ({:10.4g}, {:10.4g}, {:10.4g}) A^-1".format(*Raij[2]))
172 print("Reciprocal lattice metric tensor:")
173 print(" Rgij: ({:10.4g}, {:10.4g}, {:10.4g}) A^-1".format(*Rgij[0]))
174 print(" ({:10.4g}, {:10.4g}, {:10.4g}) A^-1".format(*Rgij[1]))
175 print(" ({:10.4g}, {:10.4g}, {:10.4g}) A^-1".format(*Rgij[2]))
176 print("Reciprocal unit cell volume: {:12.4g} A^-3".format(Rvolume))
177 """
178
179 sites = []
180 AtomTypes = cry.AtomTypeList()
181 AtomSites = cry.ExpandedAtomSiteList()
182 allsites = []
183 for atom in AtomSites:
184 label = atom.Label()
185 name = atom.AtomNameOnly()
186 z = 1 #atom.AtomicNumber()
187 q = atom.Charge()
188 pos = atom.Position()
189 xc, yc, zc = cry.FractionalToCartesian(*pos)
190 if q >= 0.0:
191 r = 0.7
192 color = 'red'
193 M = 1.0
194 else:
195 r = 1.4
196 color = 'blue'
197 M = 1.0
198
199 sites.append([name, label, z, M, q, r, color, pos])
200
201 dmin = wl / 2.0 / sin(0.5 * Q2max * torad)
202 hmax = int(lattice_parameters[0] / dmin)
203 kmax = int(lattice_parameters[1] / dmin)
204 lmax = int(lattice_parameters[2] / dmin)
205
206 print("")
207 print("hkl range:", hmax, kmax, lmax)
208
209# Calculate diffraction angles and store them in qlist list variable
210 org = np.array([0.0, 0.0, 0.0])
211 qlist = []
212 for l in range(-lmax, lmax+1):
213 for k in range(-kmax, kmax+1):
214 for h in range(-hmax, hmax+1):
215# Calculate distance in reciprocal space between (0, 0, 0) and (h, k, l)
216 G = distance(org, np.array([h, k, l]), Rgij)
217 if G < Gmin:
218 continue
219
220# Calculate lattice space from G
221 d = 1.0 / G
222
223 sinQ = wl / 2.0 / d
224 if sinQ >= 1.0:
225 continue
226
227# Calculate diffraction angle 2Theta
228 Q2 = 2.0 * todeg * arcsin(sinQ)
229 if Q2 > Q2max:
230 continue
231
232 qlist.append([Q2, d, h, k, l])
233# print(" 2Q={:12.4g} d={:12.6g} ({:3d} {:3d} {:3d})".format(Q2, d, h, k, l))
234
235# Sort rlist by 2Theta (x[0] priority)
236 qlist.sort(key = lambda x: (x[0], x[2], x[3], x[4]))
237
238 peak_identical = {}
239 for qinf in qlist:
240 Q2, d, h, k, l = qinf
241
242 h2, k2, l2 = cry.normalize_hkl(h, k, l)
243 if la == 'hexagonal' or la == 'trigonal':
244 key = f"{Q2:10.6f}:{h2}_{k2}_{-h2-k2}_{l2}"
245 else:
246 key = f"{Q2:10.6f}:{h2}_{k2}_{l2}"
247
248 if peak_identical.get(key, None):
249 peak_identical[key].append({ "Q2": Q2, "h": h, "k": k, "l": l, "d": d})
250 else:
251 peak_identical[key] = [{ "Q2": Q2, "h": h, "k": k, "l": l, "d": d}]
252
253 print("")
254 print("Diffraction angle, d, h, k, l:")
255 if la == 'hexagonal':
256# print(f"{'h':2} {'k':2} {'l':2} {'dhkl':10} {'2Theta':8}")
257 print(f"{'h':>2} {'k':>2} {'i':>2} {'l':>2} {'m':>2} {'dhkl':^10} {'2Theta':^8}")
258 else:
259 print(f"{'h':>2} {'k':>2} {'l':>2} {'m':>2} {'dhkl':^10} {'2Theta':^8}")
260 for key in sorted(peak_identical.keys()):
261 p_key = peak_identical[key]
262 p = peak_identical[key][len(p_key)-1]
263 h = p['h']
264 k = p['k']
265 l = p['l']
266 m = cry.multiplicity_hkl(h, k, l) #len(peak_identical[key])
267 Q2 = p['Q2']
268 d = p['d']
269 if la == 'hexagonal':
270# print(f"{h:2} {k:2} {l:2} {d:10.6f} {Q2:8.6f}")
271 print(f"{h:2} {k:2} {-h-k:2} {l:2} {m:2} {d:10.6f} {Q2:8.6f}")
272 else:
273 print(f"{h:2} {k:2} {l:2} {m:2} {d:10.6f} {Q2:8.6f}")
274
275 print("")
276 app.terminate(pause = True)
277
278
279if __name__ == '__main__':
280 main()