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

kronig_penney.py をダウンロード

kronig_penney.py
kronig_penney.py
  1import sys
  2import numpy as np
  3from numpy import sqrt, exp, sin, cos, tan, cosh, sinh
  4import numpy.linalg as LA 
  5from pprint import pprint
  6import csv
  7from matplotlib import pyplot as plt
  8
  9"""
 101D band calculation by Kronig-Penney model
 11
 12Kronig-Penneyモデルによる1次元バンド計算を行うスクリプト。
 13半導体の電子バンド構造を、矩形ポテンシャルを用いたKronig-Penneyモデルで計算し、
 14その結果をグラフ表示、バンド図プロット、波動関数プロットのいずれかのモードで出力する。
 15
 16関連リンク: :doc:`kronig_penney_usage`
 17"""
 18
 19#===================================
 20# physical constants
 21#===================================
 22pi   = 3.14159265358979323846
 23pi2  = 2.0 * pi
 24h    = 6.6260755e-34    # Js";
 25hbar = 1.05459e-34      # "Js";
 26c    = 2.99792458e8     # m/s";
 27e    = 1.60218e-19      # C";
 28e0   = 8.854418782e-12; # C<sup>2</sup>N<sup>-1</sup>m<sup>-2</sup>";
 29kB   = 1.380658e-23     # JK<sup>-1</sup>";
 30me   = 9.1093897e-31    # kg";
 31R    = 8.314462618      # J/K/mol
 32a0   = 5.29177e-11      # m";
 33
 34
 35#========================
 36# global configuration
 37#========================
 38mode = 'graph'   # graph|band|wf
 39
 40#========================
 41# Crystal definition
 42#========================
 43# Si
 44a  = 5.4064  # angstrom, lattice parameter
 45
 46#========================
 47# Potential
 48#========================
 49bwidth =  0.5 # A,  barrier width
 50bpot   = 10.0 # eV, barrier height
 51
 52#=====================================
 53# 解を走査するグラフ表示
 54#=====================================
 55kg     = 0.0  # k point to be plotted
 56# 解を走査するエネルギー範囲
 57Emin   = 0.0
 58Emax   = 9.5
 59# グラフを表示するエネルギー点数
 60nE     = 51
 61# 解を走査するエネルギー点数
 62nEsearch = nE
 63# Secant法パラメータ
 64eps      = 1.0e-8
 65nmaxiter = 100
 66dump     = 0.0
 67
 68#========================
 69# Band
 70#========================
 71kmin = -0.5  # in pi/a
 72kmax =  0.5  # in pi/a
 73nk = 21
 74
 75# プロットするエネルギー範囲
 76Erange = [0.0, 10.0]    # eV
 77
 78# リストに保存する準位最大数
 79nMaxLevel = 15
 80
 81#========================
 82# Wave function
 83#========================
 84#波動関数を描画するx範囲
 85xwmin  = 0.0      # A
 86xwmax  = 3.0 * a  # A
 87nxw    = 101
 88#描画する波動関数の波数
 89kw     = 0.0
 90#描画する波動関数の準位番号
 91iLevel = 0
 92
 93#===================================
 94# figure configuration
 95#===================================
 96figsize = (6, 8)
 97fontsize        = 12
 98legend_fontsize = 8
 99
100
101#==============================================
102# fundamental functions
103#==============================================
104def pfloat(str):
105    """
106    概要: 文字列をfloat型に安全に変換する。
107    詳細説明: 変換できない場合はNoneを返す。
108    :param str: str 変換する文字列。
109    :returns: float または None 変換されたfloat値、またはNone。
110    """
111    try:
112        return float(str)
113    except:
114        return None
115
116def pint(str):
117    """
118    概要: 文字列をint型に安全に変換する。
119    詳細説明: 変換できない場合はNoneを返す。
120    :param str: str 変換する文字列。
121    :returns: int または None 変換されたint値、またはNone。
122    """
123    try:
124        return int(str)
125    except:
126        return None
127
128def getarg(position, defval = None):
129    """
130    概要: コマンドライン引数を安全に取得する。
131    詳細説明: 指定された位置の引数が存在しない場合、デフォルト値を返す。
132    :param position: int 取得する引数の位置(インデックス)。
133    :param defval: Any 引数が存在しない場合に返すデフォルト値。
134    :returns: Any 指定された位置の引数、またはデフォルト値。
135    """
136    try:
137        return sys.argv[position]
138    except:
139        return defval
140
141def getfloatarg(position, defval = None):
142    """
143    概要: コマンドライン引数をfloat型に変換して安全に取得する。
144    詳細説明: 指定された位置の引数が存在しない場合やfloatに変換できない場合、デフォルト値を返す。
145    :param position: int 取得する引数の位置(インデックス)。
146    :param defval: float または None 引数が存在しない場合や変換できない場合に返すデフォルト値。
147    :returns: float または None 変換されたfloat値の引数、またはデフォルト値。
148    """
149    return pfloat(getarg(position, defval))
150
151def getintarg(position, defval = None):
152    """
153    概要: コマンドライン引数をint型に変換して安全に取得する。
154    詳細説明: 指定された位置の引数が存在しない場合やintに変換できない場合、デフォルト値を返す。
155    :param position: int 取得する引数の位置(インデックス)。
156    :param defval: int または None 引数が存在しない場合や変換できない場合に返すデフォルト値。
157    :returns: int または None 変換されたint値の引数、またはデフォルト値。
158    """
159    return pint(getarg(position, defval))
160
161def round01(x, a):
162    """
163    概要: 数値を区間 [0, a) にマッピングし、周期的なオフセットを計算する。
164    詳細説明: x = n * a + x0 となるような x0 と整数 n を計算する。x0 は 0 <= x0 < a の範囲に収まる。
165    :param x: float マッピング対象の数値。
166    :param a: float 周期の幅。
167    :returns: tuple (float, int) x を a で割った余り x0 と整数 n のタプル。
168    """
169    if x >= 0.0:
170        n = int(x / a)
171    else:
172        n = int(x / a) - 1
173    x0 = x - n * a
174    return x0, n
175
176def usage():
177    """
178    概要: スクリプトの正しい使用方法を標準出力に表示する。
179    詳細説明: コマンドライン引数の形式といくつかの使用例を示す。
180    """
181    print("")
182    print("Usage: Variables in () are optional")
183    print("  python {}".format(sys.argv[0]))
184    print("  python {} (graph a bwidth bpot k Emin Emax nE)".format(sys.argv[0]))
185    print("  python {} (band a bwidth bpot nG kmin kmax nk)".format(sys.argv[0]))
186    print("  python {} (wf a bwidth bpot kw iLevel xwmin xwmax nxw)".format(sys.argv[0]))
187    print("     ex: python {} {} {} {} {} {} {} {} {}"
188            .format(sys.argv[0], 'graph', a, bwidth, bpot, kg, Emin, Emax, nE))
189    print("     ex: python {} {} {} {} {} {} {} {}"
190            .format(sys.argv[0], 'band', a, bwidth, bpot, kmin, kmax, nk))
191    print("     ex: python {} {} {} {} {} {} {} {} {} {}"
192            .format(sys.argv[0], 'wf', a, bwidth, bpot, kw, iLevel, xwmin, xwmax, nxw))
193
194def terminate(message = None):
195    """
196    概要: エラーメッセージを表示してプログラムを終了する。
197    詳細説明: usage() 関数を呼び出した後、指定されたメッセージ(任意)を表示してプログラムを終了する。
198    :param message: str または None 表示するエラーメッセージ(オプション)。
199    """
200    print("")
201    if message is not None:
202        print("")
203        print(message)
204        print("")
205
206    usage()
207    print("")
208    exit()
209
210#==============================================
211# update default values by startup arguments
212#==============================================
213argv = sys.argv
214#if len(argv) == 1:
215#    terminate()
216
217mode    = getarg     (1, mode)
218a       = getfloatarg(2, a)
219bwidth  = getfloatarg(3, bwidth)
220bpot    = getfloatarg(4, bpot)
221
222if mode == 'graph':
223    kg      = getfloatarg(5, kg)
224    Emin    = getfloatarg(6, Emin)
225    Emax    = getfloatarg(7, Emax)
226    nE      = getintarg  (8, nE)
227elif mode == 'band':
228    kmin = getfloatarg( 5, kmin)
229    kmax = getfloatarg( 6, kmax)
230    nk   = getintarg  ( 7, nk)
231elif mode == 'wf':
232    kw     = getfloatarg( 5, kw)
233    iLevel = getintarg  ( 6, iLevel)
234    xwmin  = getfloatarg( 7, xwmin)
235    xwmax  = getfloatarg( 8, xwmax)
236    nxw    = getintarg  (9, nxw)
237
238
239def pot(x):
240    """
241    概要: 矩形ポテンシャル関数を定義する。
242    詳細説明: 0 <= x < a - bwidth の範囲ではポテンシャルは0、a - bwidth <= x < a の範囲では bpot のポテンシャルを返す。周期 a を持つ。
243    :param x: float 位置 (A)。
244    :returns: float 指定された位置 x におけるポテンシャル値 (eV)。
245    """
246    global a
247    global bwidth, bpot
248    
249    xred, nred = round01(x, a)
250
251    if a - bwidth <= xred < a:
252        return bpot
253    return 0.0
254
255def build_potential(xmin, xstep, n):
256    """
257    概要: 指定された範囲でポテンシャルプロファイルを作成する。
258    詳細説明: xmin から開始し、n 個の点に対して xstep 間隔でポテンシャル値を計算する。
259    :param xmin: float 最初のx座標 (A)。
260    :param xstep: float x座標のステップ幅 (A)。
261    :param n: int 計算する点の数。
262    :returns: tuple (numpy.ndarray, numpy.ndarray) x座標の配列と対応するポテンシャル値の配列のタプル。
263    """
264    xpot = np.empty(n)
265    ypot = np.empty(n)
266    for i in range(n):
267        xx = xmin + i * xstep
268        xpot[i] = xx
269        ypot[i] = pot(xx)
270    return xpot, ypot
271
272def cal_delta(E, k, w, b, V0):
273    """
274    概要: Kronig-Penneyモデルのエネルギー方程式の左辺 delta(E) を計算する。
275    詳細説明: エネルギー E と波数 k、ポテンシャルパラメータ w、b、V0 を用いて、
276              周期的な結晶における電子のエネルギー準位を決定する方程式の値を計算する。
277              delta(E) = cos(ka) となる E が許容エネルギーとなる。
278    :param E: float 電子のエネルギー (eV)。
279    :param k: float ブロッホ波数 (単位: pi/a)。
280    :param w: float ポテンシャル井戸の幅 (A)。
281    :param b: float ポテンシャル障壁の幅 (A)。
282    :param V0: float ポテンシャル障壁の高さ (eV)。
283    :returns: float Kronig-Penneyモデル方程式の左辺の値。
284    """
285    alpha = sqrt(2.0 * me * E * e) / hbar
286    beta  = sqrt(2.0 * me * (V0 - E) * e) / hbar
287    ka = k * pi2
288    alphaw = alpha * w * 1.0e-10
289    betab  = beta  * b * 1.0e-10
290    delta = (beta*beta - alpha*alpha)/2.0/alpha/beta * sin(alphaw) * sinh(betab) \
291          + cos(alphaw) * cosh(betab) \
292          - cos(ka)
293#    print("a=", E, ka, alphaw, betab, delta)
294    return delta
295
296def check_ci(ci, kw, Ei, w, b, V0, eps, IsPrint = 0):
297    """
298    概要: 波束係数 ci がKronig-Penneyモデルの境界条件を満たしているか検証する (デバッグ用)。
299    詳細説明: 4つの境界条件式に ci を代入し、その結果が eps より小さいかを確認する。
300              満たさない場合はエラーで終了する。
301    :param ci: list of complex 波動関数の係数リスト。
302    :param kw: float ブロッホ波数 (単位: pi/a)。
303    :param Ei: float 電子のエネルギー (eV)。
304    :param w: float ポテンシャル井戸の幅 (A)。
305    :param b: float ポテンシャル障壁の幅 (A)。
306    :param V0: float ポテンシャル障壁の高さ (eV)。
307    :param eps: float 許容誤差。
308    :param IsPrint: int 詳細を出力するかどうかのフラグ (0: なし, 1: あり)。
309    """
310    alpha = sqrt(2.0 * me * Ei * e) / hbar
311    beta  = sqrt(2.0 * me * (V0 - Ei) * e) / hbar
312    ka = kw * pi2
313    lambda_ = exp(1.0j * ka)
314    alphaw = alpha * w * 1.0e-10
315    betab  = beta  * b * 1.0e-10
316    alpha *= 1.0e-10
317    beta  *= 1.0e-10
318
319    Passed = 1
320    vmax = 0.0
321    if 1:
322            Mij = np.empty([4, 4], dtype = complex)
323            M3ij = np.empty([3, 3], dtype = complex)
324            V3i  = np.empty([3, 1], dtype = complex)
325            Mij[0, 0] = Mij[0, 1] = 1.0
326            Mij[0, 2] = Mij[0, 3] = -1.0
327            Mij[1, 0] =  1.0j * alpha
328            Mij[1, 1] = -1.0j * alpha
329            Mij[1, 2] = -beta
330            Mij[1, 3] =  beta
331            Mij[2, 0] =  exp( 1.0j * alphaw)
332            Mij[2, 1] =  exp(-1.0j * alphaw)
333            Mij[2, 2] = -lambda_ * exp(-betab)
334            Mij[2, 3] = -lambda_ * exp( betab)
335            Mij[3, 0] =  1.0j * alpha * exp( 1.0j * alphaw)
336            Mij[3, 1] = -1.0j * alpha * exp(-1.0j * alphaw)
337            Mij[3, 2] = -lambda_ * beta * exp(-betab)
338            Mij[3, 3] =  lambda_ * beta * exp( betab)
339            if IsPrint:
340                for i in range(4):
341                    print("  ci[{}] = {:12.4g}+j{:12.4g}".format(i, ci[i].real, ci[i].imag))
342            for i in range(4):
343                v = Mij[i, 0] * ci[0] + Mij[i, 1] * ci[1] + Mij[i, 2] * ci[2] + Mij[i, 3] * ci[3]
344                v = abs(v)
345                if IsPrint:
346                    print("  abs(Mij@ci[{}]) = {}".format(i, v), eps)
347                if v > eps:
348                    Passed = 0
349                if v > vmax:
350                    vmax = v
351
352    if not Passed:
353        print("Error: Mij @ ci is not zero: abs(Mij@ci)={} > eps={}".format(vmax, eps))
354        exit()
355
356
357def refine_E(E0, E1, nmaxiter, eps, dump, k, w, b, V0, IsPrint = 0):
358    """
359    概要: Secant法を用いてKronig-Penneyモデルの許容エネルギー E を高精度化する。
360    詳細説明: cal_delta(E)=0 となる E をSecant法で探索し、収束したエネルギー値を返す。
361    :param E0: float 探索範囲の下限エネルギー (eV)。
362    :param E1: float 探索範囲の上限エネルギー (eV)。
363    :param nmaxiter: int 最大繰り返し回数。
364    :param eps: float 収束判定の許容誤差。
365    :param dump: float 収束を助けるための微小な値。
366    :param k: float ブロッホ波数 (単位: pi/a)。
367    :param w: float ポテンシャル井戸の幅 (A)。
368    :param b: float ポテンシャル障壁の幅 (A)。
369    :param V0: float ポテンシャル障壁の高さ (eV)。
370    :param IsPrint: int 詳細を出力するかどうかのフラグ (0: なし, 1: あり)。
371    :returns: tuple (float, float, float) または (None, None, None)
372              収束したエネルギー (float)、最終的なエネルギー変化 (float)、最終的なdelta値 (float) のタプル。
373              収束しない場合はNoneのタプル。
374    """
375    delta0 = cal_delta(E0, k, w, b, V0)
376    delta1 = cal_delta(E1, k, w, b, V0)
377    for i in range(nmaxiter):
378        diff = (delta1 - delta0) / (E1 - E0)
379        if diff >= 0.0:
380            diff += dump
381        else:
382            diff = -(abs(diff) + dump)    
383
384        dE = -delta1 / diff
385        E2 = E1 + dE
386        delta2 = cal_delta(E2, k, w, b, V0)
387
388        if abs(dE) < eps:
389            if IsPrint:
390                print("  converged at E = {:12.6g} with dE = {:12.6g}  delta = {:12.6g}"
391                        .format(E2, dE, delta2))
392            return E2, dE, delta2
393        else:
394            E0 = E1
395            E1 = E2
396            delta0 = delta1
397            delta1 = delta2
398            continue
399    else:
400        print("  Not converged for {} iterations.".format(nmaxiter))
401        print("    E = {:12.6g} with dE = {:12.6g}  delta = {:12.6g}".format(E2, dE, delta2))
402        return None, None, None
403    
404def find_Elist(Emin, Emax, nEsearch, k, w, b, V0):
405    """
406    概要: 指定されたエネルギー範囲でKronig-Penneyモデルの許容エネルギー準位と対応する係数を見つける。
407    詳細説明: Emin から Emax までを nEsearch 分割して cal_delta(E) の符号反転を探索し、
408              Secant法で厳密なエネルギー値を特定する。同時に波動関数の係数 ci も計算する。
409    :param Emin: float 探索するエネルギー範囲の最小値 (eV)。
410    :param Emax: float 探索するエネルギー範囲の最大値 (eV)。
411    :param nEsearch: int エネルギー範囲の探索ステップ数。
412    :param k: float ブロッホ波数 (単位: pi/a)。
413    :param w: float ポテンシャル井戸の幅 (A)。
414    :param b: float ポテンシャル障壁の幅 (A)。
415    :param V0: float ポテンシャル障壁の高さ (eV)。
416    :returns: tuple (list of float, list of list of complex)
417              許容エネルギー準位のリストと、各準位に対応する波動関数係数のリストのタプル。
418    """
419#    nEsearch *= 100
420    Estep = (Emax - Emin) / (nEsearch - 1)
421#    print("Estep=", Estep)
422    d0 = None
423    iband = 0
424    Elist = []
425    Alist = []
426    for iE in range(nEsearch):
427        E = Emin + iE * Estep
428        if E == 0.0:
429            continue
430        if V0 <= E:
431            break
432
433        delta = cal_delta(E, k, w, b, V0)
434
435        if d0 is None:
436            d0 = delta
437            continue
438        if d0 * delta < 0.0:
439            d0 = delta
440
441#            print("  E[{}]={:12.6g} eV  delta={:8.4g}".format(iband, E, delta))
442            E, dE, delta0 = refine_E(E - Estep, E, nmaxiter, eps, dump, k, w, b, V0, IsPrint = 0)
443            print("  E[{}]={:12.6g} eV  dE={:12.6g} delta={:12.6g}".format(iband, E, dE, delta0))
444
445            Elist.append(E)
446#            Elist.append(E - 0.5 * Estep)
447
448            alpha = sqrt(2.0 * me * E * e) / hbar
449            beta  = sqrt(2.0 * me * (V0 - E) * e) / hbar
450            ka = k * pi2
451            lambda_ = exp(1.0j * ka)
452            alphaw = alpha * w * 1.0e-10
453            betab  = beta  * b * 1.0e-10
454            alpha *= 1.0e-10
455            beta  *= 1.0e-10
456
457            Mij = np.empty([4, 4], dtype = complex)
458            M3ij = np.empty([3, 3], dtype = complex)
459            V3i  = np.empty([3, 1], dtype = complex)
460            Mij[0, 0] = Mij[0, 1] = 1.0
461            Mij[0, 2] = Mij[0, 3] = -1.0
462            Mij[1, 0] =  1.0j * alpha
463            Mij[1, 1] = -1.0j * alpha
464            Mij[1, 2] = -beta
465            Mij[1, 3] =  beta
466            Mij[2, 0] =  exp( 1.0j * alphaw)
467            Mij[2, 1] =  exp(-1.0j * alphaw)
468            Mij[2, 2] = -lambda_ * exp(-betab)
469            Mij[2, 3] = -lambda_ * exp( betab)
470            Mij[3, 0] =  1.0j * alpha * exp( 1.0j * alphaw)
471            Mij[3, 1] = -1.0j * alpha * exp(-1.0j * alphaw)
472            Mij[3, 2] = -lambda_ * beta * exp(-betab)
473            Mij[3, 3] =  lambda_ * beta * exp( betab)
474
475            A = 1.0
476            M3ij[0, 0] = Mij[1, 1]
477            M3ij[0, 1] = Mij[1, 2]
478            M3ij[0, 2] = Mij[1, 3]
479            M3ij[1, 0] = Mij[2, 1]
480            M3ij[1, 1] = Mij[2, 2]
481            M3ij[1, 2] = Mij[2, 3]
482            M3ij[2, 0] = Mij[3, 1]
483            M3ij[2, 1] = Mij[3, 2]
484            M3ij[2, 2] = Mij[3, 3]
485            V3i[0, 0] = -A * Mij[1, 0]
486            V3i[1, 0] = -A * Mij[2, 0]
487            V3i[2, 0] = -A * Mij[3, 0]
488
489            Ai = LA.solve(M3ij, V3i)
490
491            ci = [A, Ai[0, 0], Ai[1, 0], Ai[2, 0]]
492#            check_ci(ci, k, E, w, b, V0, 3.0e-3, IsPrint = 0)
493            Alist.append(ci)
494
495            E += Estep
496
497    return Elist, Alist
498
499def cal_wavefunction(ci, x, kw, Ei, w, b, V0):
500    """
501    概要: 指定された係数 ci を用いて、Kronig-Penneyモデルの波動関数 Psi(x) を計算する。
502    詳細説明: ブロッホの定理に基づき Psi(x) = exp(ikx) * u(x) の形式で波動関数を構築する。
503              u(x) は周期関数で、ポテンシャル障壁内外で異なる形を取る。
504    :param ci: list of complex 波動関数の係数リスト。
505    :param x: float 波動関数を評価する位置 (A)。
506    :param kw: float ブロッホ波数 (単位: pi/a)。
507    :param Ei: float 電子のエネルギー (eV)。
508    :param w: float ポテンシャル井戸の幅 (A)。
509    :param b: float ポテンシャル障壁の幅 (A)。
510    :param V0: float ポテンシャル障壁の高さ (eV)。
511    :returns: complex 位置 x における複素波動関数の値。
512    """
513    IsPrint = 1
514
515    a = w + b
516
517    xmin = -b
518    xmax = w
519    x0, n = round01(x, a)
520    if x0 < -xmin:
521        x0 += a
522    if x0 >= xmax:
523        x0 -= a
524    if not xmin <= x0 < xmax:
525        print("Error: x0 out of range: x={:8.4g} {} x0={:8.4g} w={:8.4g} b={:8.4g}".format(x, n, x0, w, b))
526        exit()
527
528#    if IsPrint:
529#        print("x={:8.4g} {} x0={:8.4g} w={:8.4g} b={:8.4g}".format(x, n, x0, w, b))
530
531#    check_ci(ci, kw, Ei, w, b, V0, 3.0e-3)
532
533    alpha = sqrt(2.0 * me * Ei * e) / hbar
534    beta  = sqrt(2.0 * me * (V0 - Ei) * e) / hbar
535    alpha *= 1.0e-10
536    beta  *= 1.0e-10
537    phase0 = pi2 / a * kw * x0
538    kph0   = exp(1.0j * phase0)
539
540# Calculate the periodic function u(x) from phi(x) in -b <= x < w
541    if xmin <= x0 < 0.0:    # in barrier, defined in -b <= x < 0, w <= x < a
542        f = ci[2] * exp(beta * x0) + ci[3] * exp(-beta * x0)
543        u = f / kph0
544    else:           # in well, defined in 0 <= x < w
545        f = ci[0] * exp(1.0j * alpha * x0) + ci[1] * exp(-1.0j * alpha * x0)
546        u = f / kph0
547
548# Calculate Bloch function phi(x) = exp(ikx) * u(x)
549    f = exp(1.0j * pi2 / a * kw * x) * u
550
551    return f + 0.0j
552# デバッグ用: 周期関数部分 u(x) を返す
553#    return u + 0.0j
554
555
556def wf():
557    """
558    概要: Kronig-Penneyモデルにおける波動関数を計算し、プロットする。
559    詳細説明: 特定の波数 kw と準位番号 iLevel に対応するエネルギー準位を計算し、
560              その波動関数を xwmin から xwmax の範囲で描画する。
561              ポテンシャルプロファイルも重ねて表示する。
562    """
563    global mode
564    global a
565    global bwidth, bpot
566    global nEsearch, nMaxLevel
567    global kw, iLevel
568    global xwmin, xwmax, nxw
569
570    xwstep = (xwmax - xwmin) / (nxw - 1)
571    Estep = bpot / (nEsearch - 1)
572
573    print("")
574    print("=== Input parameterss ===")
575    print("mode:", mode)
576    print("a=", a, "A")
577    print("Wave function to be plotted: k = {}  iLevel = {}".format(kw, iLevel))
578    print("x range: {} - {} at {} step, {} points".format(xwmin, xwmax, xwstep, nxw))
579    print("potential: w={} A  h={} eV".format(bwidth, bpot))
580
581    print("")
582    V0 = bpot
583    b  = bwidth
584    w  = a - b
585
586    print("")
587    print("at k={:8.4g}".format(kw))
588
589    Elist, Alist = find_Elist(0.0, V0, nEsearch, kw, w, b, V0)
590
591    xplot, yplot = build_potential(xwmin, xwstep, nxw)
592
593    print("")
594    print("=== Calculate wave function ===")
595    print("Energy levels:", Elist, "eV")
596    print("at k = {}".format(kw))
597    print("{}-th energy level".format(iLevel))
598    Ei = Elist[iLevel]
599    ci = Alist[iLevel]
600
601    print("  E = {:12.6g} eV".format(Elist[iLevel]))
602    print("  A = {:12.4g}+j{:12.4g}".format(ci[0].real, ci[0].imag))
603    print("  B = {:12.4g}+j{:12.4g}".format(ci[1].real, ci[1].imag))
604    print("  C = {:12.4g}+j{:12.4g}".format(ci[2].real, ci[2].imag))
605    print("  D = {:12.4g}+j{:12.4g}".format(ci[3].real, ci[3].imag))
606    sumci = abs(ci[0] + ci[1] - ci[2] - ci[3])
607    print("  sum(ci) = {:12.4e}".format(sumci))
608    alpha = sqrt(2.0 * me * Ei * e) / hbar * 1.0e-10
609    beta  = sqrt(2.0 * me * (V0 - Ei) * e) / hbar * 1.0e-10
610    print("  alpha = {:12.6g} A^-1".format(alpha))
611    print("  beta  = {:12.6g} A^-1".format(beta))
612
613    print("")
614    print("Normalization")
615    nxintg = int(a / xwstep + 1.0001)
616    xintgstep = a / (nxintg - 1)
617    chg = 0.0
618    for i in range(nxintg):
619        x = 0.0 + i * xintgstep
620        yval = cal_wavefunction(ci, x, kw, Ei, w, b, V0)
621        chg += yval * yval.conjugate()
622
623    chg = chg.real * xintgstep
624    kywf = 1.0 / sqrt(chg)
625    print("integ(|psi(x)|^2) = ", chg)
626    print("Normalization coefficient = ", kywf)
627    for i in range(4):
628        ci[i] *= kywf
629    print("  A = {:12.4g}+j{:12.4g}".format(ci[0].real, ci[0].imag))
630    print("  B = {:12.4g}+j{:12.4g}".format(ci[1].real, ci[1].imag))
631    print("  C = {:12.4g}+j{:12.4g}".format(ci[2].real, ci[2].imag))
632    print("  D = {:12.4g}+j{:12.4g}".format(ci[3].real, ci[3].imag))
633    
634    ywf = np.empty(nxw, dtype = complex)
635    for i in range(nxw):
636        x = xwmin + i * xwstep
637        ywf[i] = cal_wavefunction(ci, x, kw, Ei, w, b, V0)
638
639    charge = [(ywf[i] * ywf[i].conjugate()).real for i in range(nxw)]
640
641    fig = plt.figure(figsize = (16, 4)) #figsize)
642    ax2 = fig.add_subplot(1, 1, 1)
643#    ax2 = fig.add_subplot(2, 1, 2)
644    ax1 = ax2.twinx()
645
646    ax1.set_xlim([xwmin, xwmax])
647    ax1.plot(xplot, yplot, linewidth = 0.5, label = 'U(x)')
648    ax1.plot(ax1.get_xlim(), [0.0, 0.0], color = 'r', linestyle = 'dashed', linewidth = 0.5)
649    ax2.set_xlim([xwmin, xwmax])
650    ax2.plot(xplot,  ywf.real, color = 'r',     linewidth = 1.5, label = "real")
651    ax2.plot(xplot,  ywf.imag, color = 'b',     linewidth = 1.5, label = "imaginary")
652    ax2.plot(xplot,  charge,   color = 'black', linewidth = 0.5, label = "charge")
653    ax2.plot(ax1.get_xlim(), [0.0, 0.0], color = 'r', linestyle = 'dashed', linewidth = 0.5)
654    ax1.set_xlabel("x (A)", fontsize = fontsize)
655    ax1.set_ylabel("U(x)", fontsize = fontsize)
656    ax2.set_xlabel("x (A)", fontsize = fontsize)
657    ax2.set_ylabel("$\Psi$($x$)", fontsize = fontsize)
658
659    handler1, label1 = ax1.get_legend_handles_labels()
660    handler2, label2 = ax2.get_legend_handles_labels()
661    ax2.legend(handler1 + handler2, label1 + label2, loc = 2, borderaxespad = 0.0, fontsize = legend_fontsize)
662#    ax2.legend(fontsize = legend_fontsize)
663
664    ax1.tick_params(labelsize = fontsize)
665    ax2.tick_params(labelsize = fontsize)
666    plt.tight_layout()
667
668    plt.pause(0.1)
669    print("Press ENTER to exit>>", end = '')
670    input()
671
672    terminate()
673
674def band():
675    """
676    概要: Kronig-Penneyモデルのエネルギーバンド構造を計算し、プロットする。
677    詳細説明: 指定された波数範囲 (kmin から kmax) で複数の k 点に対して許容エネルギー準位を計算し、
678              E-k図(バンド構造)をプロットする。
679    """
680    global mode
681    global a
682    global bwidth, bpot
683    global kmin, kmax, nk
684    global nEsearch, nMaxLevel
685
686    kstep = (kmax - kmin) / (nk - 1)
687
688    print("")
689    print("=== Input parameterss ===")
690    print("mode:", mode)
691    print("a=", a, "A")
692    print("potential: w={} A  h={} eV".format(bwidth, bpot))
693    print("k range: {} - {} at {} step, {} points".format(kmin, kmax, kstep, nk))
694    print("")
695
696    print("")
697    V0 = bpot
698    b  = bwidth
699    w  = a - b
700
701    xk = [kmin + i * kstep for i in range(nk)]
702    yE = np.zeros([nMaxLevel, nk])
703    nMaxBand = 0
704    for ik in range(nk):
705        k = kmin + ik * kstep
706        print("at k={:8.4g}".format(k))
707
708        Elist, Alist = find_Elist(0.0, V0, nEsearch, k, w, b, V0)
709        n = len(Elist)
710        if n > nMaxBand:
711            nMaxBand = n
712        for iband in range(min(n, nMaxLevel)):
713            yE[iband][ik] = Elist[iband]
714
715
716    fig = plt.figure(figsize = figsize)
717    ax1 = fig.add_subplot(1, 1, 1)
718
719    ax1.set_xlim([-0.5, 0.5])
720    ax1.set_ylim(Erange)
721#    ax1.set_ylim([0.0, ax1.get_ylim()[1]])
722
723    for iL in range(nMaxBand):
724         ax1.plot(xk, yE[iL], linestyle = '', marker = 'o', markersize = 5.0,
725                        markerfacecolor = 'none', markeredgecolor = 'black', markeredgewidth = 0.5)
726    ax1.set_xlabel("$k$ $(\pi$$/a)$", fontsize = fontsize)
727    ax1.set_ylabel("E (eV)", fontsize = fontsize)
728    ax1.legend(fontsize = legend_fontsize)
729    plt.tight_layout()
730
731    plt.pause(0.1)
732    
733    print("Press ENTER to exit>>", end = '')
734    input()
735   
736    terminate()
737
738def graphview():
739    """
740    概要: Kronig-Penneyモデルの許容エネルギー方程式の delta(E) 関数をプロットする。
741    詳細説明: 特定の波数 kg に対して、エネルギー E の関数としての cal_delta(E) の値を計算し、
742              グラフ表示する。delta(E)=0 となる点が許容エネルギー準位を示す。
743    """
744    global mode
745    global a
746    global bwidth, bpot
747    global Emin, Emax, nE
748
749    Estep = (Emax - Emin) / (nE - 1)
750
751    V0 = bpot
752    b  = bwidth
753    w  = a - b
754
755    print("")
756    print("=== Input parameterss ===")
757    print("mode:", mode)
758    print("a=", a, "A")
759    print("  barrier: w={} A  h={} eV".format(b, V0))
760    print("  well   : w={} A  h={} eV".format(w, 0.0))
761    print("Energy range: {} - {}, {} eV step  {} points".format(Emin, Emax, Estep, nE))
762    print("at k = {}".format(kg))
763    print("")
764
765    xE = []
766    yD = []
767    for i in range(1, nE):
768        E = Emin + i * Estep
769        if V0 <= E:
770            break
771
772        delta = cal_delta(E, kg, w, b, V0)
773
774        xE.append(E)
775        yD.append(delta)
776
777    fig = plt.figure(figsize = figsize)
778    ax1 = fig.add_subplot(1, 1, 1)
779
780    ax1.plot(xE, yD)
781    ax1.set_xlim([Emin, Emax])
782    ax1.plot([Emin, Emax], [0.0, 0.0], linestyle = 'dashed', color = 'r', linewidth = 0.5)
783    ax1.set_xlabel("E (eV)", fontsize = fontsize)
784    ax1.set_ylabel("delta", fontsize = fontsize)
785#    ax1.legend(fontsize = legend_fontsize)
786    ax1.tick_params(labelsize = fontsize)
787    plt.tight_layout()
788
789    plt.pause(0.1)
790    print("Press ENTER to exit>>", end = '')
791    input()
792
793    terminate()
794
795def main():
796    """
797    概要: プログラムのエントリポイント。
798    詳細説明: コマンドライン引数で指定された mode に応じて、graphview、band、または wf 関数を呼び出す。
799              無効なモードが指定された場合はエラーメッセージを表示して終了する。
800    """
801    global mode
802    
803    if mode == 'graph':
804        graphview()
805    elif mode == 'band':
806        band()
807    elif mode == 'wf':
808        wf()
809    else:
810        terminate("Error: Invalid mode [{}]".format(mode))
811
812
813if __name__ == "__main__":
814    main()