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

crystal_convert_cell.py をダウンロード

crystal_convert_cell.py
crystal_convert_cell.py
  1"""
  2単位格子を異なる基底ベクトルに変換し、変換前後の単位格子を描画するスクリプトです。
  3
  4概要:
  5    このスクリプトは、指定された結晶構造の単位格子を、異なる基底ベクトル(例えばプリミティブセル)を持つ
  6    新しい単位格子に変換し、その結果を可視化します。
  7詳細説明:
  8    変換前と変換後の単位格子の格子パラメータ、格子ベクトル、体積、原子サイト情報を計算して標準出力に表示します。
  9    さらに、matplotlibの3Dプロット機能を使用して両方の単位格子と原子配置を視覚的に表示します。
 10    コマンドライン引数で結晶の種類、変換モード、原子の描画サイズを調整できます。
 11    このスクリプトは tkcrystalbase.py モジュールに依存しています。
 12関連リンク:
 13    crystal_convert_cell_usage
 14"""
 15import sys
 16import os
 17import copy
 18from numpy import sin, cos, tan, arcsin, arccos, arctan, exp, log, sqrt
 19import numpy as np
 20from numpy import linalg as la
 21
 22from tkcrystalbase import *
 23
 24
 25# Crystal name
 26crystal_name = 'FCC'
 27# Conversion mode
 28conversion_mode = 'FCCPrim'
 29
 30# Lattice parameters (angstrom and degree)
 31#lattice_parameters = [ 5.62, 5.62, 5.62, 60.0, 60.0, 60.0]
 32lattice_parameters = [ 5.62, 5.62, 5.62, 90.0, 90.0, 90.0]
 33
 34# Site information (atom name, site label, atomic number, atomic mass, charge, radius, color, position)
 35crystal_sites = [
 36         ['Na', 'Na1', 11, 22.98997, +1.0, 0.7, 'red',  np.array([0.0, 0.0, 0.0])]
 37        ,['Na', 'Na2', 11, 22.98997, +1.0, 0.7, 'red',  np.array([0.0, 0.5, 0.5])]
 38        ,['Na', 'Na3', 11, 22.98997, +1.0, 0.7, 'red',  np.array([0.5, 0.0, 0.5])]
 39        ,['Na', 'Na4', 11, 22.98997, +1.0, 0.7, 'red',  np.array([0.5, 0.5, 0.0])]
 40        ]
 41
 42# Coefficient for atomic size to plot
 43kr = 1000.0
 44
 45# Distance to judge identical atom site, in angstrom
 46rmin = 0.1
 47
 48# Range of unit cells to draw crystal structure
 49nrange = [[-0.1, 1.1], [-0.1, 1.1], [-0.1, 1.1]]
 50
 51# Figure configuration
 52figsize = (12, 12)
 53
 54# Atom color for converted cell
 55converted_atom_color = 'gray'
 56
 57def get_crystal(name):
 58    """
 59    概要:
 60        指定された結晶名に対応する格子パラメータとサイト情報を返します。
 61    詳細説明:
 62        サポートされている結晶タイプはFCC、BCC、Hex、Rhombです。
 63    引数:
 64        :param name: 結晶名。 'FCC', 'BCC', 'Hex', 'Rhomb' のいずれか。
 65        :type name: str
 66    戻り値:
 67        :returns: 格子定数 (lattice_parameters) と原子サイト情報 (crystal_sites) のタプル。
 68        :rtype: tuple
 69    """
 70    if name == 'FCC':
 71        return lattice_parameters, crystal_sites
 72    if name == 'BCC':
 73        return lattice_parameters, \
 74                [ ['Na', 'Na1', 11, 22.98997, +1.0, 0.7, 'red',  np.array([0.0, 0.0, 0.0])]
 75                 ,['Na', 'Na2', 11, 22.98997, +1.0, 0.7, 'red',  np.array([0.5, 0.5, 0.5])] ]
 76    if name == 'Hex':
 77        return [ 5.62, 5.62, 4.00, 90.0, 90.0, 120.0], \
 78                [ ['Na', 'Na1', 11, 22.98997, +1.0, 0.7, 'red',  np.array([0.0, 0.0, 0.0])] ]
 79    if name == 'Rhomb':
 80        return [ 5.62, 5.62, 5.62, 70.0, 70.0, 70.0], \
 81                [ ['Na', 'Na1', 11, 22.98997, +1.0, 0.7, 'red',  np.array([0.0, 0.0, 0.0])] ]
 82
 83def get_conversion_matrix(key):
 84    """
 85    概要:
 86        指定された変換キーに対応する変換行列を返します。
 87    詳細説明:
 88        FCCプリミティブセル、BCCプリミティブセル、面心格子、斜方晶系-菱面体晶、
 89        菱面体晶-六方晶などの様々な結晶学的変換行列をサポートします。
 90    引数:
 91        :param key: 変換モードのキー。 'FCCPrim', 'BCCPrim', 'ACenterPrim', 'BCenterPrim', 'CCenterPrim', 'RhombHex', 'HexRhomb', 'HexOrtho' のいずれか。
 92        :type key: str
 93    戻り値:
 94        :returns: 3x3の変換行列。
 95        :rtype: numpy.ndarray
 96    """
 97    if key == 'FCCPrim':
 98        return np.array([ [ 0.5, 0.5,   0], 
 99                 [ 0,   0.5, 0.5],
100                 [ 0.5,   0, 0.5] ])
101    elif key == 'BCCPrim':
102        return np.array([ [-0.5, 0.5, 0.5], 
103                 [ 0.5,-0.5, 0.5],
104                 [ 0.5, 0.5,-0.5] ])
105    elif key == 'ACenterPrim':
106        return np.array([ [ 1.0, 0.0, 0.0], 
107                 [ 0.0, 0.5, 0.5],
108                 [ 0.0,-0.5, 0.5] ])
109    elif key == 'BCenterPrim':
110        return np.array([ [ 0.5, 0.0, 0.5], 
111                 [ 0.0, 1.0, 0.0],
112                 [-0.5, 0.0, 0.5] ])
113    elif key == 'CCenterPrim':
114        return np.array([ [ 0.5, 0.5, 0.0], 
115                 [-0.5, 0.5, 0.0],
116                 [ 0.0, 0.0, 1.0] ])
117    elif key == 'RhombHex':
118        return np.array([ [ 1.0, -1.0,  0.0], 
119                 [ 0.0,  1.0, -1.0],
120                 [ 1.0,  1.0,  1.0] ])
121    elif key == 'HexRhomb':
122        return np.array([ [ 2.0/3, 1.0/3, 1.0/3],
123                 [-1.0/3, 1.0/3, 1.0/3],
124                 [-1.0/3,-2.0/3, 1.0/3] ])
125    elif key == 'HexOrtho':
126        return np.array([ [ 1.0, 0.0, 0.0], 
127                 [ 1.0, 2.0, 0.0],
128                 [ 0.0, 0.0, 1.0] ])
129
130
131def usage():
132    """
133    概要:
134        スクリプトの正しい使用方法を標準出力に表示します。
135    詳細説明:
136        この関数は、期待されるコマンドライン引数とその説明、および具体的な使用例をユーザーに提示します。
137    戻り値:
138        :returns: なし
139        :rtype: None
140    """
141    print("")
142    print("Usage: python {} crystal_name conversion_mode kRatom".format(argv[0]))
143    print("   crystal_name   : 'FCC',     'BCC',     'Hex',                  'Rhomb'")
144    print("   conversion_mode: 'FCCPrim', 'BCCPrim', 'HexOrtho', 'HexRhomb', 'RhombHex'")
145    print("   kRatom         : Coefficient of atomic radius to draw")
146    print("ex: python {} {} {} {}".format(argv[0], crystal_name, conversion_mode, kr))
147    print("")
148
149def terminate():
150    """
151    概要:
152        usageメッセージを表示し、スクリプトを終了します。
153    詳細説明:
154        エラー発生時や不正な引数が渡された際に呼び出され、ユーザーに正しい使い方を案内した後にプログラムを終了させます。
155    戻り値:
156        :returns: なし
157        :rtype: None
158    """
159    usage()
160    exit()
161
162
163def main():
164    """
165    概要:
166        単位格子の変換と描画のメイン処理を実行します。
167    詳細説明:
168        結晶情報と変換行列を取得し、変換前後の格子パラメータ、格子ベクトル、体積、原子サイト情報を計算して標準出力に表示します。
169        その後、変換前後の単位格子と原子配置をmatplotlibの3Dプロットで可視化します。
170        tkcrystalbase モジュール内のヘルパー関数を利用して、幾何学的計算と描画を行います。
171    戻り値:
172        :returns: なし
173        :rtype: None
174    """
175    latt, sites = get_crystal(crystal_name)
176    tij = get_conversion_matrix(conversion_mode)
177
178    print("")
179    print("Crystal name:", crystal_name)
180    print("  Lattice parameters: {:8.4g} {:8.4g} {:8.4g} {:8.4g} {:8.4g} {:8.4g}".format(*latt))
181    aij = cal_lattice_vectors(latt)
182    print("Lattice vectors:")
183    print("  ax: ({:10.4g}, {:10.4g}, {:10.4g}) A".format(aij[0][0], aij[0][1], aij[0][2]))
184    print("  ay: ({:10.4g}, {:10.4g}, {:10.4g}) A".format(aij[1][0], aij[1][1], aij[1][2]))
185    print("  az: ({:10.4g}, {:10.4g}, {:10.4g}) A".format(aij[2][0], aij[2][1], aij[2][2]))
186    inf = cal_metrics(latt)
187    gij = inf['gij']
188    print("Metric tensor:")
189    print("  gij: ({:10.4g}, {:10.4g}, {:10.4g}) A".format(*gij[0]))
190    print("       ({:10.4g}, {:10.4g}, {:10.4g}) A".format(*gij[1]))
191    print("       ({:10.4g}, {:10.4g}, {:10.4g}) A".format(*gij[2]))
192    volume = cal_volume(aij)
193    print("Volume: {:12.4g} A^3".format(volume))
194    print("Sites:")
195    for s in sites:
196        print("  {:4}: {:4}: ({:8.3g}, {:8.3g}, {:8.3g}) Z={:6.3g}".format(s[0], s[1], s[7][0], s[7][1], s[7][2], s[4]))
197
198    print("")
199    print("Conversion mode:", conversion_mode)
200    print("(tij) = |{:8.3g}, {:8.3g}, {:8.3g}|".format(tij[0][0], tij[0][1], tij[0][2]))
201    print("        |{:8.3g}, {:8.3g}, {:8.3g}|".format(tij[1][0], tij[1][1], tij[1][2]))
202    print("        |{:8.3g}, {:8.3g}, {:8.3g}|".format(tij[2][0], tij[2][1], tij[2][2]))
203    acij = convert_lattice_vectors(aij, tij)
204    print("  Converted lattice vectors:")
205    print("    acx: ({:10.4g}, {:10.4g}, {:10.4g}) A".format(acij[0][0], acij[0][1], acij[0][2]))
206    print("    acy: ({:10.4g}, {:10.4g}, {:10.4g}) A".format(acij[1][0], acij[1][1], acij[1][2]))
207    print("    acz: ({:10.4g}, {:10.4g}, {:10.4g}) A".format(acij[2][0], acij[2][1], acij[2][2]))
208    lattc = cal_lattice_parameters_from_lattice_vectors(acij)
209    print("    Lattice parameters: {:8.4g} {:8.4g} {:8.4g} {:8.4g} {:8.4g} {:8.4g}".format(*lattc))
210    infc = cal_metrics(lattc)
211    gcij = infc['gij']
212    print("  Metric tensor:")
213    print("    gcij: ({:10.4g}, {:10.4g}, {:10.4g}) A".format(*gcij[0]))
214    print("          ({:10.4g}, {:10.4g}, {:10.4g}) A".format(*gcij[1]))
215    print("          ({:10.4g}, {:10.4g}, {:10.4g}) A".format(*gcij[2]))
216    volumec = cal_volume(acij)
217    print("  Volumec: {:12.4g} A^3".format(volumec))
218    print("  Sites:")
219    #tij_x2xc = get_conversion_matrix_from_tij(tij, 'x2xc')
220    tij_x2xc = np.linalg.inv(tij).transpose()
221    print("  (tij:x to xc) = |{:8.3g}, {:8.3g}, {:8.3g}|".format(tij_x2xc[0][0], tij_x2xc[0][1], tij_x2xc[0][2]))
222    print("                  |{:8.3g}, {:8.3g}, {:8.3g}|".format(tij_x2xc[1][0], tij_x2xc[1][1], tij_x2xc[1][2]))
223    print("                  |{:8.3g}, {:8.3g}, {:8.3g}|".format(tij_x2xc[2][0], tij_x2xc[2][1], tij_x2xc[2][2]))
224    print("  Converted sites:")
225    csites = []
226    for site in sites:
227        name, label, z, M, q, r, color, pos = copy.deepcopy(site)
228        pos01 = [reduce01(pos[0]), reduce01(pos[1]), reduce01(pos[2])]
229        for iz in range(int(nrange[2][0]) - 1, int(nrange[2][1]) + 1):
230         for iy in range(int(nrange[1][0]) - 1, int(nrange[1][1]) + 1):
231          for ix in range(int(nrange[0][0]) - 1, int(nrange[0][1]) + 1):
232            posc = convert_fractional_coordinates_by_tij([pos01[0] + ix, pos01[1] + iy, pos01[2] + iz], tij)
233            posn = [reduce01(posc[0]), reduce01(posc[1]), reduce01(posc[2])]
234            if add_site(csites, [name, label, z, M, q, r * 0.3, color, posn], gcij, rmin):
235                print("  {:4}: {:4}: ({:8.3g}, {:8.3g}, {:8.3g}) Z={:6.3g}".format(name, label, *posn, q))
236    
237    allsites = []
238    for site in sites:
239        name, label, z, M, q, r, color, pos = copy.deepcopy(site)
240        pos01 = [reduce01(pos[0]), reduce01(pos[1]), reduce01(pos[2])]
241        for iz in range(int(nrange[2][0]) - 1, int(nrange[2][1]) + 1):
242         for iy in range(int(nrange[1][0]) - 1, int(nrange[1][1]) + 1):
243          for ix in range(int(nrange[0][0]) - 1, int(nrange[0][1]) + 1):
244            posn = [pos01[0] + ix, pos01[1] + iy, pos01[2] + iz]
245            if -0.1 <= posn[0] <= 1.1 and -0.1 <= posn[1] <= 1.1 and -0.1 <= posn[2] <= 1.1:
246                add_site(allsites, [name, label, z, M, q, r, color, posn], gij, rmin)
247
248    allcsites = []
249    for site in csites:
250        name, label, z, M, q, r, color, pos = copy.deepcopy(site)
251        pos01 = [reduce01(pos[0]), reduce01(pos[1]), reduce01(pos[2])]
252        for iz in range(int(nrange[2][0]) - 1, int(nrange[2][1]) + 1):
253         for iy in range(int(nrange[1][0]) - 1, int(nrange[1][1]) + 1):
254          for ix in range(int(nrange[0][0]) - 1, int(nrange[0][1]) + 1):
255            posn = [pos01[0] + ix, pos01[1] + iy, pos01[2] + iz]
256            if -0.1 <= posn[0] <= 1.1 and -0.1 <= posn[1] <= 1.1 and -0.1 <= posn[2] <= 1.1:
257                add_site(allcsites, [name, label, z, M, q, r, color, posn], gcij, rmin)
258
259    print("")
260    print("All sites to draw:")
261    for s in allsites:
262        print("  {:4}: {:4}: ({:8.3g}, {:8.3g}, {:8.3g}) Z={:6.3g}".format(s[0], s[1], s[7][0], s[7][1], s[7][2], s[4]))
263
264    print("")
265    print("All converted sites to draw:")
266    for s in allcsites:
267        print("  {:4}: {:4}: ({:8.3g}, {:8.3g}, {:8.3g}) Z={:6.3g}".format(s[0], s[1], s[7][0], s[7][1], s[7][2], s[4]))
268
269
270    fig = plt.figure(figsize = figsize)
271    ax = fig.add_subplot(111, projection='3d')
272
273# Real space unit cell
274    draw_unitcell(ax, allsites,  aij,  nrange, kr, linecolor = 'black')
275    draw_unitcell(ax, allcsites, acij, nrange, kr, linecolor = 'blue', atomcolor = converted_atom_color)
276
277# Note: set_aspect() is not implemented for 3D plots
278#    ax.set_aspect('equal','box')
279    xlim =ax.get_xlim()
280    ylim =ax.get_ylim()
281    zlim =ax.get_zlim()
282    lim = [min([xlim[0], ylim[0], zlim[0]]), max([xlim[1], ylim[1], zlim[1]])]
283    ax.set_xlim(lim)
284    ax.set_ylim(lim)
285    ax.set_zlim(lim)
286#    ax.set_xticks(np.linspace(*lim, 0))
287#    ax.set_yticks(np.linspace(*lim, 0))
288#    ax.set_zticks(np.linspace(*lim, 0))
289
290    plt.show()
291
292    print("")
293    
294
295    terminate()
296
297
298if __name__ == '__main__':
299    argv = sys.argv
300    narg = len(argv)
301    if narg >= 2:
302        crystal_name    = argv[1]
303    if narg >= 3:
304        conversion_mode = argv[2]
305    if narg >= 4:
306        kr = float(argv[3])
307
308    main()