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

free_electron_band.py をダウンロード

free_electron_band.py
free_electron_band.py
  1import sys
  2from pprint import pprint
  3import numpy as np
  4from numpy import sqrt, exp, sin, cos, tan, pi
  5import numpy.linalg as LA 
  6import csv
  7from matplotlib import pyplot as plt
  8
  9"""
 10概要:
 11    自由電子モデルを用いたバンド計算を行うモジュール。
 12
 13関連リンク:
 14    free_electron_band_usage
 15"""
 16
 17#===================================
 18# physical constants
 19#===================================
 20pi   = 3.14159265358979323846
 21pi2  = 2.0 * pi
 22h    = 6.6260755e-34    # Js";
 23hbar = 1.05459e-34      # "Js";
 24c    = 2.99792458e8     # m/s";
 25e    = 1.60218e-19      # C";
 26e0   = 8.854418782e-12; # C<sup>2</sup>N<sup>-1</sup>m<sup>-2</sup>";
 27kB   = 1.380658e-23     # JK<sup>-1</sup>";
 28me   = 9.1093897e-31    # kg";
 29R    = 8.314462618      # J/K/mol
 30a0   = 5.29177e-11      # m";
 31
 32
 33#========================
 34# Crystal definition
 35#========================
 36# Si
 37a  = 5.4064             # angstrom, lattice parameter
 38rg = np.zeros([3, 3])
 39rg[0][0] = pow(2.0 * pi / a, 2) # reciprocal space metric in angstrom^-2
 40rg[1][1] = rg[0][0]
 41rg[2][2] = rg[0][0]
 42
 43# 有効質量
 44meff = 1.0 # in me
 45
 46# E(k) = KE * k*k, E(k) in eV, k in angstrom^1
 47KE = hbar * hbar / 2.0 / (meff * me) * 1.0e20 / e
 48print("KE = ", KE)
 49
 50#========================
 51# Band plot parameters
 52#========================
 53# バンド構造をプロットするk点の軌跡: [kx, ky, kz, k点名称]
 54klist = [
 55#      [-0.5,  0.0, 0.0,  "-X"]
 56#    , [0.0,  0.0, 0.0,  r"$\Gamma$"]
 57#    , [ 0.5,  0.0, 0.0,  "X"]
 58      [0.5,  0.0, 1.0, "W"]
 59    , [0.5,  0.5, 0.5,  "L"]
 60    , [0.0,  0.0, 0.0,  r"$\Gamma$"]
 61    , [0.0,  0.0, 1.0,  "X"]
 62    , [0.5,  0.0, 1.0,  "W"]
 63    , [0.75, 0.0, 0.75, "K"]
 64    ]
 65# プロットするバンド構造E(k)のk点数の概数
 66nk = 101
 67
 68# Ehkl(k)を計算するhkl範囲
 69hrange = [-3, 3]
 70krange = [-3, 3]
 71lrange = [-3, 3]
 72#hrange = [0, 0]
 73#krange = [0, 0]
 74#lrange = [0, 0]
 75
 76# プロットするエネルギー範囲
 77Erange = [0.0, 10.0]    # eV
 78
 79
 80#===================================
 81# figure configuration
 82#===================================
 83figsize = (6, 8)
 84#figsize = (6, 4)
 85fontsize        = 16
 86legend_fontsize = 8
 87
 88
 89#==============================================
 90# fundamental functions
 91#==============================================
 92def pfloat(str):
 93    """
 94    概要:
 95        文字列を実数値に変換する。
 96    詳細説明:
 97        実数値に変換できない文字列をfloat()で変換するとエラーになってプログラムが終了するため、
 98        この関数は変換できなかったらNoneを返すが、プログラムは終了させない。
 99    引数:
100        :param str: 変換元の文字列
101        :type str: str
102    戻り値:
103        :returns: 変換された実数値、変換できない場合はNone
104        :rtype: float or None
105    """
106    try:
107        return float(str)
108    except:
109        return None
110
111def pint(str):
112    """
113    概要:
114        文字列を整数値に変換する。
115    詳細説明:
116        pfloat()と同様に、変換できない文字列が渡されてもエラー終了させずNoneを返す。
117    引数:
118        :param str: 変換元の文字列
119        :type str: str
120    戻り値:
121        :returns: 変換された整数値、変換できない場合はNone
122        :rtype: int or None
123    """
124    try:
125        return int(str)
126    except:
127        return None
128
129def getarg(position, defval = None):
130    """
131    概要:
132        起動時のコマンドライン引数を安全に取得する。
133    詳細説明:
134        sys.argvに対して範囲外のインデックスを指定してもエラー終了せず、デフォルト値を返す。
135    引数:
136        :param position: 取得したい引数のインデックス
137        :type position: int
138        :param defval: 範囲外アクセス時に返すデフォルト値
139        :type defval: Any
140    戻り値:
141        :returns: 取得した引数の文字列、またはデフォルト値
142        :rtype: str or Any
143    """
144    try:
145        return sys.argv[position]
146    except:
147        return defval
148
149def getfloatarg(position, defval = None):
150    """
151    概要:
152        起動時引数を取得し、実数に変換して返す。
153    引数:
154        :param position: 取得したい引数のインデックス
155        :type position: int
156        :param defval: 範囲外アクセス時や変換失敗時に返すデフォルト値
157        :type defval: Any
158    戻り値:
159        :returns: 変換された実数値、またはデフォルト値
160        :rtype: float or Any
161    """
162    return pfloat(getarg(position, defval))
163
164def getintarg(position, defval = None):
165    """
166    概要:
167        起動時引数を取得し、整数値に変換して返す。
168    引数:
169        :param position: 取得したい引数のインデックス
170        :type position: int
171        :param defval: 範囲外アクセス時や変換失敗時に返すデフォルト値
172        :type defval: Any
173    戻り値:
174        :returns: 変換された整数値、またはデフォルト値
175        :rtype: int or Any
176    """
177    return pint(getarg(position, defval))
178
179def usage():
180    """
181    概要:
182        プログラムの使用方法を表示する。
183    詳細説明:
184        コンソール上に使い方のメッセージを出力する。
185    戻り値:
186        :returns: なし
187        :rtype: None
188    """
189    print("")
190    print("Usage:")
191#    print("  python {}".format(sys.argv[0]))
192
193def terminate(message = None):
194    """
195    概要:
196        メッセージを表示し、プログラムを安全に終了する。
197    詳細説明:
198        任意のメッセージと使用方法を表示した上で exit() を呼び出す。
199    引数:
200        :param message: 終了前に表示する文字列(Noneの場合は表示しない)
201        :type message: str or None
202    戻り値:
203        :returns: なし
204        :rtype: None
205    """
206    print("")
207    if message is not None:
208        print("")
209        print(message)
210        print("")
211
212    usage()
213    print("")
214    exit()
215
216def cal_kdistance(rg, k0, k1):
217    """
218    概要:
219        逆格子の計量テンソルを用いて、2つのk点間の距離を計算する。
220    引数:
221        :param rg: 逆格子の計量テンソル(3x3行列)
222        :type rg: list or numpy.ndarray
223        :param k0: 始点のk点座標 [kx, ky, kz]
224        :type k0: list
225        :param k1: 終点のk点座標 [kx, ky, kz]
226        :type k1: list
227    戻り値:
228        :returns: 2点間の距離
229        :rtype: float
230    """
231    dkx = k1[0] - k0[0]
232    dky = k1[1] - k0[1]
233    dkz = k1[2] - k0[2]
234    r2  = rg[0][0] * dkx*dkx + rg[1][1] * dky*dky + rg[2][2] * dkz*dkz
235    r2 += 2.0 * (rg[0][1] * dkx*dky + rg[1][2] * dky*dkz + rg[2][0] * dky*dkx)
236
237    return sqrt(r2)
238
239def cal_E(k, Ghkl):
240    """
241    概要:
242        k点座標と逆格子ベクトルから自由電子のエネルギーを計算する(単位: eV)。
243    詳細説明:
244        kおよびGhklは内部座標系で与える。
245    引数:
246        :param k: k点の内部座標 [kx, ky, kz]
247        :type k: list
248        :param Ghkl: 逆格子ベクトル [h, k, l]
249        :type Ghkl: list
250    戻り値:
251        :returns: 計算されたエネルギー値 (eV)
252        :rtype: float
253    """
254    global rg
255
256    kabs2  = rg[0][0] * (k[0] + Ghkl[0])**2 # in angstrom^-2
257    kabs2 += rg[1][1] * (k[1] + Ghkl[1])**2
258    kabs2 += rg[2][2] * (k[2] + Ghkl[2])**2
259    E = KE * kabs2  # in eV
260    return E
261
262def get_dklist(klist, nk):
263    """
264    概要:
265        プロットするk点軌跡の各距離と、最初のk点からの累積距離リストを計算する。
266    詳細説明:
267        バンド構造プロット用に、k点の名称リストも合わせて生成する。
268    引数:
269        :param klist: [kx, ky, kz, 名称] の形式を持つk点の軌跡リスト
270        :type klist: list
271        :param nk: プロットするk点の概算数
272        :type nk: int
273    戻り値:
274        :returns: dklist, ktotal_list, ktotal_namelist, ktotal のタプル。
275                  dklistは各k点間の距離リスト。
276                  ktotal_listは最初のk点からの累積距離リスト。
277                  ktotal_namelistはk点の名称リスト。
278                  ktotalは累積距離の合計。
279        :rtype: tuple
280    """
281    print("")
282
283# list of k distances from the first k point of each k region
284    dklist     = []
285# list of k distance from the first k point of the k list
286    ktotal = 0.0
287    ktotal_list     = []
288    ktotal_namelist = []
289    ktotal_list.append(0.0)
290    ktotal_namelist.append(klist[0][3])
291    for i in range(1, len(klist)):
292        print("k [{:<10s}: ({:6.4f}, {:6.4f}, {:6.4f}] to [{:<10s}:({:6.4f}, {:6.4f}, {:6.4f}]"
293            .format(
294                klist[i-1][3], klist[i-1][0], klist[i-1][1], klist[i-1][2], 
295                klist[i][3],   klist[i][0],   klist[i][1],   klist[i][2]))
296
297        dk_ = cal_kdistance(rg,
298                [klist[i-1][0], klist[i-1][1], klist[i-1][2]], 
299                [klist[i][0],   klist[i][1],   klist[i][2]] )
300        ktotal += dk_
301        dklist.append(dk_)
302        ktotal_list.append(ktotal)
303        ktotal_namelist.append(klist[i][3])
304#        print("  dk={}  ktotal={}".format(dk_, ktotal))
305
306    return dklist, ktotal_list, ktotal_namelist, ktotal
307
308def get_cal_klist(klist, nk):
309    """
310    概要:
311        指定されたk点軌跡から、等間隔になるように計算用のk点リストを生成する。
312    詳細説明:
313        プロットに必要な累積距離リストやk点名称などのデータもあわせて作成する。
314    引数:
315        :param klist: バンド構造をプロットするk点の軌跡リスト
316        :type klist: list
317        :param nk: プロットするk点数の概数
318        :type nk: int
319    戻り値:
320        :returns: xk, xkvec, ktotallist, ktotal_namelist, res のタプル。
321                  xkは各計算k点の累積距離リスト。
322                  xkvecは各計算k点の座標リスト。
323                  ktotallistは区切りとなるk点の累積距離リスト。
324                  ktotal_namelistは区切りとなるk点の名称リスト。
325                  resは距離情報や分割数情報を持つ辞書。
326        :rtype: tuple
327    """
328    dklist, ktotallist, ktotal_namelist, ktotal = get_dklist(klist, nk)
329    kstep = ktotal / nk
330
331    nklist = []
332    xk = []
333    xkvec = []
334    ktotal_ = 0.0
335    for i in range(1, len(klist)):
336        nk_ = int(dklist[i-1] / kstep + 1.00001)
337        nklist.append(nk_)
338        if i == len(klist) - 1:
339            ndiv = nk_ - 1
340        else: 
341            ndiv = nk_    
342        kstepx_ = (klist[i][0] - klist[i-1][0]) / ndiv
343        kstepy_ = (klist[i][1] - klist[i-1][1]) / ndiv
344        kstepz_ = (klist[i][2] - klist[i-1][2]) / ndiv
345
346        print("k: {:<10s} - {:<10s}".format(klist[i-1][3], klist[i][3]), end = '')
347        print(": nk={:3d} ktotal={:6.4f} kstep=({:6.4f}, {:6.4f}, {:6.4f})"
348                .format(nklist[i-1], ktotal_, kstepx_, kstepy_, kstepz_))
349
350        dk_ = cal_kdistance(rg, [0.0, 0.0, 0.0], [kstepx_,kstepy_, kstepz_])
351        for j in range(nklist[i-1]):
352            kx = klist[i-1][0] + j * kstepx_
353            ky = klist[i-1][1] + j * kstepy_
354            kz = klist[i-1][2] + j * kstepz_
355
356            xk.append(ktotal_)
357            xkvec.append([kx, ky, kz])
358            ktotal_ += dk_
359
360    res = {"dklist": dklist, "nklist": nklist, "ktotal": ktotal, "kstep": kstep}
361
362    return xk, xkvec, ktotallist, ktotal_namelist, res
363
364def get_cal_Elist(xkvec, hrange, krange, lrange):
365    """
366    概要:
367        計算用k点リストと逆格子ベクトルの範囲から、各k点でのエネルギーリストを計算する。
368    引数:
369        :param xkvec: 計算を行うk点座標 [kx, ky, kz] のリスト
370        :type xkvec: list
371        :param hrange: 逆格子ベクトルhの計算範囲 [最小値, 最大値]
372        :type hrange: list
373        :param krange: 逆格子ベクトルkの計算範囲 [最小値, 最大値]
374        :type krange: list
375        :param lrange: 逆格子ベクトルlの計算範囲 [最小値, 最大値]
376        :type lrange: list
377    戻り値:
378        :returns: 各k点におけるエネルギー群が格納された入れ子リスト
379        :rtype: list
380    """
381    yE = []
382    for i in range(len(xkvec)):
383        kx = xkvec[i][0]
384        ky = xkvec[i][1]
385        kz = xkvec[i][2]
386        Elist = []
387        for ih in range(hrange[0], hrange[1]+1):
388            for ik in range(krange[0], krange[1]+1):
389                for il in range(lrange[0], lrange[1]+1):
390                    E = cal_E([kx, ky, kz], [ih, ik, il])
391                    Elist.append(E)
392
393        yE.append(Elist)
394
395    return yE
396
397def plot_band(axis, xk, yE, Erange, ktotallist, ktotal_namelist):    
398    """
399    概要:
400        計算した自由電子のバンド構造をグラフにプロットする。
401    引数:
402        :param axis: matplotlibのAxesオブジェクト
403        :type axis: matplotlib.axes.Axes
404        :param xk: プロットするk点の累積距離のリスト
405        :type xk: list
406        :param yE: 各k点に対応するエネルギーのリスト(入れ子でも可)
407        :type yE: list
408        :param Erange: プロットするエネルギーの表示範囲 [最小値, 最大値]
409        :type Erange: list
410        :param ktotallist: k点境界における、最初のk点からの距離の和のリスト
411        :type ktotallist: list
412        :param ktotal_namelist: k点境界における、k点の名称のリスト
413        :type ktotal_namelist: list
414    戻り値:
415        :returns: なし
416        :rtype: None
417    """
418# 表示範囲は決め打ち
419    axis.set_xlim([min(xk), max(xk)])
420    axis.set_ylim(Erange)
421
422# バンド構造をプロット
423    axis.plot(xk, yE, linestyle = 'none', 
424                marker = 'o', markerfacecolor = 'none', markeredgecolor = 'black', 
425                markeredgewidth = 0.5, markersize = 2.0)
426
427# Γ点、BZ境界の縦線を引く
428    for i in range(1, len(ktotallist)-1):
429        axis.plot([ktotallist[i], ktotallist[i]], Erange, 
430                    linestyle = '-', color = 'black', linewidth = 0.5)
431
432# k軸の目盛りにk点の名称を表示する
433# グラフ枠が一つであれば plt.xtics()で設定できる
434# axisに対しては、.setpでattributeを直接書き換える必要があるらしい
435    plt.setp(axis, xticks = ktotallist, xticklabels = ktotal_namelist)
436    axis.set_xlabel("k", fontsize = fontsize)
437    axis.set_ylabel("E (eV)", fontsize = fontsize)
438    axis.tick_params(labelsize = fontsize)
439
440
441def main():
442    """
443    概要:
444        自由電子バンド計算のメインルーチン。
445    詳細説明:
446        格子定数やプロットパラメータを設定し、k点のリストとエネルギーを計算して
447        matplotlibを用いてバンド図を描画する。
448    戻り値:
449        :returns: なし
450        :rtype: None
451    """
452    global a, rg
453    global nk, klist
454
455    print("")
456    print("Lattice parameters: ({}) A".format(a))
457    print("Effective mass: {} me".format(meff))
458    print("Reciprocal lattice metric [A^-2]:")
459    pprint(rg)
460    print("khl range: h=[{}, {}] k=[{}, {}] l=[{}, {}]"
461            .format(*hrange, *krange, *lrange))
462    print("Plot E range: {} - {} eV".format(*Erange))
463
464    xk, xkvec, ktotallist, ktotal_namelist, res = get_cal_klist(klist, nk)
465
466    print("")
467    print("k vectors")
468    print(" k_total: {} A^-1".format(res["ktotal"]))
469    print(" nk     : ", nk)
470    print(" kstep  : {}".format(res["kstep"]))
471    print(" dklist")
472    pprint(res["dklist"])
473    print(" ktotallist:")
474    pprint(ktotallist)
475    print(" nk_list:", res["nklist"])
476#    print("xk")
477#    pprint(xk)
478
479    yE = get_cal_Elist(xkvec, hrange, krange, lrange)
480
481    print("")
482    print("plot")
483    
484    fig = plt.figure(figsize = figsize)
485    ax1 = fig.add_subplot(1, 1, 1)
486
487    plot_band(ax1, xk, yE, Erange, ktotallist, ktotal_namelist)
488
489    plt.tight_layout()
490
491    plt.pause(0.1)
492    print("Press ENTER to exit>>", end = '')
493    input()
494
495    terminate()
496
497
498if __name__ == "__main__":
499    main()