crystal_MP_simple.py ダウンロード/コピー
crystal_MP_simple.py
crystal_MP_simple.py
1"""
2概要: マデルングポテンシャルを単純総和法で計算するスクリプト。
3詳細説明:
4 このスクリプトは、指定された結晶構造(格子定数とサイト情報)に基づき、
5 単純総和法を用いてマデルングポテンシャルを計算します。
6 計算されたポテンシャルは、中心イオンからの距離の関数としてプロットされます。
7 tkcrystalbase.pyモジュールに依存します。
8
9関連リンク:
10 crystal_MP_simple_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
17import matplotlib.pyplot as plt
18
19from tkcrystalbase import *
20
21
22pi = 3.14159265358979323846
23pi2 = pi + pi
24torad = 0.01745329251944 # rad/deg";
25todeg = 57.29577951472 # deg/rad";
26basee = 2.71828183
27
28h = 6.6260755e-34 # Js";
29h_bar = 1.05459e-34 # "Js";
30hbar = h_bar
31c = 2.99792458e8 # m/s";
32e = 1.60218e-19 # C";
33me = 9.1093897e-31 # kg";
34mp = 1.6726231e-27 # kg";
35mn = 1.67495e-27 # kg";
36u0 = 4.0 * 3.14*1e-7; # . "Ns<sup>2</sup>C<sup>-2</sup>";
37e0 = 8.854418782e-12; # C<sup>2</sup>N<sup>-1</sup>m<sup>-2</sup>";
38e2_4pie0 = 2.30711e-28 # Nm<sup>2</sup>";
39a0 = 5.29177e-11 # m";
40kB = 1.380658e-23 # JK<sup>-1</sup>";
41NA = 6.0221367e23 # mol<sup>-1</sup>";
42R = 8.31451 # J/K/mol";
43F = 96485.3 # C/mol";
44g = 9.81 # m/s2";
45
46
47
48# Lattice parameters (angstrom and degree)
49#lattice_parameters = [ 5.62, 5.62, 5.62, 60.0, 60.0, 60.0]
50lattice_parameters = [ 5.62, 5.62, 5.62, 90.0, 90.0, 90.0]
51
52# Site information (atom name, site label, atomic number, atomic mass, charge, radius, color, position)
53sites = [
54 ['Na', 'Na1', 11, 22.98997, +1.0, 0.7, 'red', np.array([0.0, 0.0, 0.0])]
55 ,['Na', 'Na2', 11, 22.98997, +1.0, 0.7, 'red', np.array([0.0, 0.5, 0.5])]
56 ,['Na', 'Na3', 11, 22.98997, +1.0, 0.7, 'red', np.array([0.5, 0.0, 0.5])]
57 ,['Na', 'Na4', 11, 22.98997, +1.0, 0.7, 'red', np.array([0.5, 0.5, 0.0])]
58 ,['Cl', 'Cl1', 17, 35.4527, -1.0, 1.4, 'blue', np.array([0.5, 0.0, 0.0])]
59 ,['Cl', 'Cl2', 17, 35.4527, -1.0, 1.4, 'blue', np.array([0.5, 0.5, 0.5])]
60 ,['Cl', 'Cl3', 17, 35.4527, -1.0, 1.4, 'blue', np.array([0.0, 0.0, 0.5])]
61 ,['Cl', 'Cl4', 17, 35.4527, -1.0, 1.4, 'blue', np.array([0.0, 0.5, 0.0])]
62 ]
63
64# r range
65rmin = 0.1
66rmax = 100.0
67nr = 101
68
69
70# Figure configuration
71figsize = (6, 6)
72
73rstep = (rmax - rmin) / (nr - 1)
74
75
76def usage():
77 """
78 概要: スクリプトの正しい使用方法を表示します。
79 詳細説明: コマンドライン引数rmaxとnrの指定方法をユーザーに示します。
80 :returns: なし
81 :rtype: None
82 """
83 print("")
84 print("Usage: python {} rmax nr".format(argv[0]))
85 print(" ex: python {} {} {}".format(argv[0], rmax, nr))
86 print("")
87
88def terminate():
89 """
90 概要: エラー発生時に使用方法を表示してスクリプトを終了します。
91 詳細説明: usage関数を呼び出して使用方法を出力した後、システムを終了します。
92 :returns: なし (スクリプトが終了するため)
93 :rtype: None
94 """
95 usage()
96 exit()
97
98
99def draw_box(ax, aij, nrange, color = 'black'):
100 """
101 概要: 結晶の単位格子境界を3Dプロットに描画します。
102 詳細説明: 単位格子の辺を黒線(または指定された色)で描画します。
103 この関数はdraw_unitcellから呼び出されますが、nrangeは現在の実装では使用されていません。
104 引数:
105 :param ax: matplotlibの3D軸オブジェクト。
106 :type ax: matplotlib.axes._subplots.Axes3DSubplot
107 :param aij: (3, 3)のndarray、格子ベクトルa, b, cを表す。
108 :type aij: numpy.ndarray
109 :param nrange: 描画する単位格子の範囲。[[xmin, xmax], [ymin, ymax], [zmin, zmax]]の形式。(現状未使用)
110 :type nrange: list of list of float
111 :param color: 描画する線の色。デフォルトは'black'。
112 :type color: str
113 戻り値:
114 :returns: なし
115 :rtype: None
116 """
117# (0,0,0) -> ax
118 ax.plot([0.0, aij[0][0]],
119 [0.0, aij[0][1]],
120 [0.0, aij[0][2]], color = color)
121# (0,0,0) -> ay
122 ax.plot([0.0, aij[1][0]],
123 [0.0, aij[1][1]],
124 [0.0, aij[1][2]], color = color)
125# (0,0,0) -> az
126 ax.plot([0.0, aij[2][0]],
127 [0.0, aij[2][1]],
128 [0.0, aij[2][2]], color = color)
129
130# ax -> ax + ay
131 ax.plot([aij[0][0], aij[0][0] + aij[1][0]],
132 [aij[0][1], aij[0][1] + aij[1][1]],
133 [aij[0][2], aij[0][2] + aij[1][2]], color = color)
134# ax -> ax + az
135 ax.plot([aij[0][0], aij[0][0] + aij[2][0]],
136 [aij[0][1], aij[0][1] + aij[2][1]],
137 [aij[0][2], aij[0][2] + aij[2][2]], color = color)
138
139# ay -> ay + ax
140 ax.plot([aij[1][0], aij[1][0] + aij[0][0]],
141 [aij[1][1], aij[1][1] + aij[0][1]],
142 [aij[1][2], aij[1][2] + aij[0][2]], color = color)
143# ay -> ay + az
144 ax.plot([aij[1][0], aij[1][0] + aij[2][0]],
145 [aij[1][1], aij[1][1] + aij[2][1]],
146 [aij[1][2], aij[1][2] + aij[2][2]], color = color)
147
148# az -> az + ax
149 ax.plot([aij[2][0], aij[2][0] + aij[0][0]],
150 [aij[2][1], aij[2][1] + aij[0][1]],
151 [aij[2][2], aij[2][2] + aij[0][2]], color = color)
152# az -> ax + ay
153 ax.plot([aij[2][0], aij[2][0] + aij[1][0]],
154 [aij[2][1], aij[2][1] + aij[1][1]],
155 [aij[2][2], aij[2][2] + aij[1][2]], color = color)
156
157# ax + ay -> ax + ay + az
158 ax.plot([aij[0][0] + aij[1][0], aij[0][0] + aij[1][0] + aij[2][0]],
159 [aij[0][1] + aij[1][1], aij[0][1] + aij[1][1] + aij[2][1]],
160 [aij[0][2] + aij[1][2], aij[0][2] + aij[1][2] + aij[2][2]], color = color)
161
162# ax + az -> ax + ay + az
163 ax.plot([aij[0][0] + aij[2][0], aij[0][0] + aij[1][0] + aij[2][0]],
164 [aij[0][1] + aij[2][1], aij[0][1] + aij[1][1] + aij[2][1]],
165 [aij[0][2] + aij[2][2], aij[0][2] + aij[1][2] + aij[2][2]], color = color)
166
167# ay + az -> ax + ay + az
168 ax.plot([aij[1][0] + aij[2][0], aij[0][0] + aij[1][0] + aij[2][0]],
169 [aij[1][1] + aij[2][1], aij[0][1] + aij[1][1] + aij[2][1]],
170 [aij[1][2] + aij[2][2], aij[0][2] + aij[1][2] + aij[2][2]], color = color)
171
172def draw_unitcell(ax, sites, aij, nrange, color = 'black'):
173 """
174 概要: 結晶の単位格子とその中の原子を3Dプロットに描画します。
175 詳細説明: draw_box関数を呼び出して単位格子を描画し、その後、sitesリスト内の原子を分数座標から
176 デカルト座標に変換してプロットします。nrangeは描画する単位格子の範囲を指定しますが、
177 このスクリプトのmain関数では現在呼び出されていません。
178 引数:
179 :param ax: matplotlibの3D軸オブジェクト。
180 :type ax: matplotlib.axes._subplots.Axes3DSubplot
181 :param sites: サイト情報のリスト。各サイトは[atom_name, site_label, atomic_number, atomic_mass, charge, radius, color, position]の形式。
182 :type sites: list of list
183 :param aij: (3, 3)のndarray、格子ベクトルa, b, cを表す。
184 :type aij: numpy.ndarray
185 :param nrange: 描画する単位格子の範囲。[[xmin, xmax], [ymin, ymax], [zmin, zmax]]の形式。
186 :type nrange: list of list of int
187 :param color: 単位格子を描画する線の色。デフォルトは'black'。
188 :type color: str
189 戻り値:
190 :returns: なし
191 :rtype: None
192 """
193 draw_box(ax, aij, nrange, color)
194
195 if sites is None:
196 return
197
198 for site in sites:
199 name, label, z, M, q, r, color, pos = site
200 pos01 = [reduce01(pos[0]), reduce01(pos[1]), reduce01(pos[2])]
201 for iz in range(int(nrange[2][0]) - 1, int(nrange[2][1]) + 1):
202 for iy in range(int(nrange[1][0]) - 1, int(nrange[1][1]) + 1):
203 for ix in range(int(nrange[0][0]) - 1, int(nrange[0][1]) + 1):
204 posn = [pos01[0] + ix, pos01[1] + iy, pos01[2] + iz]
205 if posn[0] < nrange[0][0] or nrange[0][1] < posn[0] \
206 or posn[1] < nrange[1][0] or nrange[1][1] < posn[1] \
207 or posn[2] < nrange[2][0] or nrange[2][1] < posn[2]:
208 continue
209
210 x, y, z = fractional_to_cartesian(posn, aij)
211 ax.scatter([x], [y], [z], marker = 'o', c = color, s = kr *r)
212
213
214def main():
215 """
216 概要: マデルングポテンシャルの計算と結果のプロットを実行します。
217 詳細説明:
218 格子定数から格子ベクトルや逆格子ベクトルを計算し、その情報を表示します。
219 指定された範囲rmax内で、原点にあるイオンに対するマデルングポテンシャルを
220 単純総和法で計算します。
221 計算されたポテンシャルは、距離rに対するグラフとして表示されます。
222 プログラム起動時のコマンドライン引数でrmaxとnrを設定することができます。
223 戻り値:
224 :returns: なし
225 :rtype: None
226 """
227 print("")
228 print("Lattice parameters:", lattice_parameters)
229 aij = cal_lattice_vectors(lattice_parameters)
230 print("Lattice vectors:")
231 print(" ax: ({:10.4g}, {:10.4g}, {:10.4g}) A".format(aij[0][0], aij[0][1], aij[0][2]))
232 print(" ay: ({:10.4g}, {:10.4g}, {:10.4g}) A".format(aij[1][0], aij[1][1], aij[1][2]))
233 print(" az: ({:10.4g}, {:10.4g}, {:10.4g}) A".format(aij[2][0], aij[2][1], aij[2][2]))
234 inf = cal_metrics(lattice_parameters)
235 gij = inf['gij']
236 print("Metric tensor:")
237 print(" gij: ({:10.4g}, {:10.4g}, {:10.4g}) A".format(*gij[0]))
238 print(" ({:10.4g}, {:10.4g}, {:10.4g}) A".format(*gij[1]))
239 print(" ({:10.4g}, {:10.4g}, {:10.4g}) A".format(*gij[2]))
240 volume = cal_volume(aij)
241 print("Volume: {:12.4g} A^3".format(volume))
242
243 print("")
244 print("Unit cell volume: {:12.4g} A^3".format(volume))
245 Raij = cal_reciprocal_lattice_vectors(aij)
246 Rlatt = cal_reciprocal_lattice_parameters(Raij)
247 Rinf = cal_metrics(Rlatt)
248 Rgij = Rinf['gij']
249 print("Reciprocal lattice parameters:", Rlatt)
250 print("Reciprocal lattice vectors:")
251 print(" Rax: ({:10.4g}, {:10.4g}, {:10.4g}) A^-1".format(*Raij[0]))
252 print(" Ray: ({:10.4g}, {:10.4g}, {:10.4g}) A^-1".format(*Raij[1]))
253 print(" Raz: ({:10.4g}, {:10.4g}, {:10.4g}) A^-1".format(*Raij[2]))
254 print("Reciprocal lattice metric tensor:")
255 print(" Rgij: ({:10.4g}, {:10.4g}, {:10.4g}) A^-1".format(*Rgij[0]))
256 print(" ({:10.4g}, {:10.4g}, {:10.4g}) A^-1".format(*Rgij[1]))
257 print(" ({:10.4g}, {:10.4g}, {:10.4g}) A^-1".format(*Rgij[2]))
258 Rvolume = cal_volume(Raij)
259 print("Reciprocal unit cell volume: {:12.4g} A^-3".format(Rvolume))
260
261# Calculate the range of unit cells
262 nxmax = int(rmax / lattice_parameters[0]) + 1
263 nymax = int(rmax / lattice_parameters[1]) + 1
264 nzmax = int(rmax / lattice_parameters[2]) + 1
265 print("")
266 print("nmax:", nxmax, nymax, nzmax)
267
268# Calculate Madelung potential around the zero-th ion
269# First store differential potential to MPdiff
270 rlist = [rmin + i * rstep for i in range(nr)]
271 MPdiff = np.zeros(nr)
272 name0, label0, z0, M0, q0, r0, color0, pos0 = sites[0]
273 Ke = e * e / 4.0 / pi / e0 # in MKS
274 for iz in range(-nzmax, nzmax+1):
275 for iy in range(-nymax, nymax+1):
276 for ix in range(-nxmax, nxmax+1):
277 for isite1 in range(len(sites)):
278 site1 = sites[isite1]
279 name1, label1, z1, M1, q1, r1, color1, pos1 = site1
280 r = distance(pos0, pos1 + np.array([ix, iy, iz]), gij)
281 ir = int((r - rmin) / rstep)
282 if r < rmin or ir < 0 or nr <= ir:
283 continue
284
285 MPdiff[ir] += Ke * q1 / (r * 1.0e-10) / e # in eV
286
287# print(" {:4} ({:8.4g}, {:8.4g}, {:8.4g}) - {:4} ({:8.4g}, {:8.4g}, {:8.4g}) + ({:2d}, {:2d}, {:2d}): dis = {:10.4g} A"
288# .format(label0, pos0[0], pos0[1], pos0[2], label1, pos1[0], pos1[1], pos1[2], ix, iy, iz, dis))
289
290 print("")
291 print("r (A) Madelung potential (eV)")
292 MP = np.empty(nr)
293 MP[0] = MPdiff[0]
294 print("{:10.4g} {:12.6g}".format(rlist[0], MP[0]))
295 for i in range (1, len(MPdiff)):
296 MP[i] = MP[i-1] + MPdiff[i]
297 print("{:10.4g} {:12.6g}".format(rlist[i], MP[i]))
298
299 fig = plt.figure(figsize = figsize)
300 ax = fig.add_subplot(111)
301
302 ax.plot(rlist, MP)
303 ax.set_xlabel('r / angstrom')
304 ax.set_ylabel('Electrostatic potential / eV')
305
306 plt.show()
307
308
309 terminate()
310
311
312if __name__ == '__main__':
313 argv = sys.argv
314 narg = len(argv)
315 if narg >= 2:
316 rmax = float(argv[1])
317 if narg >= 3:
318 nr = int(argv[2])
319
320 main()