statistical_physics.EF_T_semiconductor のソースコード

"""
非縮退半導体のEFの温度依存性を二分法で計算。
"""
import sys
import numpy as np
from numpy import sin, cos, tan, pi, exp
from matplotlib import pyplot as plt

# 定数
e  = 1.60218e-19      # C
kB = 1.380658e-23     # JK-1

# semiconductor parameters (Default values)
Ev = 0.0             # 価電子帯上端エネルギー eV
Ec = 1.0             # 伝導帯下端エネルギー
Nv = 1.2e19          # 価電子帯有効状態密度
Nc = 2.1e18          # 伝導帯有効状態密度
EA = 0.05            # アクセプター準位
NA = 1.0e15          # アクセプター密度 cm-3
ED = 0.95            # ドナー準位 (Ec - 0.05)
ND = 1.0e16          # ドナー密度

# Temperature range
Tmin  =   50  # K
Tmax  = 1000
Tstep =   10

# 計算パラメータ
eps   = 1.0e-5       # EFの誤差がepsより小さくなったら計算終了
nmaxiter = 200       # 二分法の最大繰り返し数
iprintinterval = 1   # 繰り返し中に途中経過を出力するサイクル数

# --- 物理関数群 ---

[ドキュメント] def fe(E, EF, T): """Fermi-Dirac分布関数""" global e, kB if T == 0.0: return 1.0 if E < EF else 0.0 return 1.0 / (exp((E - EF) * e / kB / T) + 1.0)
[ドキュメント] def Ne_func(EF, T, Nc_val, Ec_val): """伝導帯電子密度 (非縮退近似)""" global e, kB if T == 0.0: return 0.0 return Nc_val * exp(-(Ec_val - EF) * e / kB / T)
[ドキュメント] def Nh_func(EF, T, Nv_val, Ev_val): """価電子帯正孔密度 (非縮退近似)""" global e, kB if T == 0.0: return 0.0 return Nv_val * exp(-(EF - Ev_val) * e / kB / T)
[ドキュメント] def NDp_func(EF, T, ND_val, ED_val): """イオン化ドナー密度""" return ND_val * (1.0 - fe(ED_val, EF, T))
[ドキュメント] def NAm_func(EF, T, NA_val, EA_val): """イオン化アクセプター密度""" return NA_val * fe(EA_val, EF, T)
# --- メイン処理 ---
[ドキュメント] def main(): global Ev, Ec, Nv, Nc, EA, NA, ED, ND global Tmin, Tmax, Tstep, eps, nmaxiter, iprintinterval # コマンドライン引数の処理 argv = sys.argv if len(argv) > 1: try: if len(argv) >= 2: EA = float(argv[1]) if len(argv) >= 3: NA = float(argv[2]) if len(argv) >= 4: ED = float(argv[3]) if len(argv) >= 5: ND = float(argv[4]) if len(argv) >= 6: Ec = float(argv[5]) if len(argv) >= 7: Nv = float(argv[6]) if len(argv) >= 8: Nc = float(argv[7]) except ValueError: print("Error: Arguments must be numeric.") return else: print("usage: python EF-T-semiconductor.py EA NA ED ND Ec Nv Nc") print(" ex: python EF-T-semiconductor.py 0.05 1.0e15 0.95 1.0e16 1.0 1.2e19 2.1e18") # 引数なしでもデフォルト値で続行するため exit はしない nT = int((Tmax - Tmin) / Tstep + 1) print("Solution of EF by bisection method") print("Ev=%f Ec=%f eV" % (Ev, Ec)) print("Nv=%e Nc=%e cm-3" % (Nv, Nc)) print("NA=%f cm-3 EA=%f eV" % (NA, EA)) print("ND=%f cm-3 ED=%f eV" % (ND, ED)) print("") xT = [] xInvT = [] yEF = [] yNe = [] yNh = [] yNAm = [] yNDp = [] print(" T(K) \t EF(eV)\tNe(cm-3)\tNh(cm-3)\tNA+(cm-3)\tND-(cm-3)") for iT in range(nT): T = Tmin + iT * Tstep # 初期範囲として、価電子帯下と伝導帯上を設定 EFmin_val = Ev - 1.0 EFmax_val = Ec + 1.0 # 電荷中性条件: dQ = n + Na- - p - Nd+ = 0 を解く def get_dQ(target_EF): return (Ne_func(target_EF, T, Nc, Ec) + NAm_func(target_EF, T, NA, EA) - Nh_func(target_EF, T, Nv, Ev) - NDp_func(target_EF, T, ND, ED)) dQmin = get_dQ(EFmin_val) dQmax = get_dQ(EFmax_val) if dQmin * dQmax > 0.0: print(f"Error at T={T}: Initial range does not bracket the root.") continue # 二分法開始 EFhalf = (EFmin_val + EFmax_val) / 2.0 for i in range(nmaxiter): EFhalf = (EFmin_val + EFmax_val) / 2.0 dQhalf = get_dQ(EFhalf) if abs(EFmin_val - EFhalf) < eps and abs(EFmax_val - EFhalf) < eps: break if dQmin * dQhalf < 0.0: EFmax_val = EFhalf else: EFmin_val = EFhalf dQmin = dQhalf else: print(f"Warning: T={T} did not converge.") # 結果の保存(最終ステップの値を使用) Neh = Ne_func(EFhalf, T, Nc, Ec) Nhh = Nh_func(EFhalf, T, Nv, Ev) NAmh = NAm_func(EFhalf, T, NA, EA) NDph = NDp_func(EFhalf, T, ND, ED) xT.append(T) xInvT.append(1000.0 / T) yEF.append(EFhalf) yNe.append(Neh) yNh.append(Nhh) yNAm.append(NAmh) yNDp.append(NDph) print("%8.4f\t%10.6f\t%12.4g\t%12.4g\t%12.4g\t%12.4g" % (T, EFhalf, Neh, Nhh, NAmh, NDph)) # --- グラフ描画 --- fig = plt.figure(figsize=(12, 5)) ax1 = fig.add_subplot(1, 2, 1) ax2 = fig.add_subplot(1, 2, 2) # 左:EF vs T ax1.plot(xT, yEF, label='EF', color='blue') ax1.axhline(Ev, color='red', linestyle='--', label='Ev') ax1.axhline(Ec, color='red', linestyle='--', label='Ec') ax1.set_xlabel("T (K)") ax1.set_ylabel("EF (eV)") ax1.set_ylim([Ev - 0.2, Ec + 0.2]) ax1.legend() # 右:Carrier Densities vs 1000/T (Arrhenius plot) ax2.plot(xInvT, yNe, label='n (electrons)') ax2.plot(xInvT, yNh, label='p (holes)') ax2.plot(xInvT, yNAm, label='Na- (ionized acc.)', linestyle=':') ax2.plot(xInvT, yNDp, label='Nd+ (ionized don.)', linestyle=':') ax2.set_yscale("log") ax2.set_xlabel("1000/T (K^-1)") ax2.set_ylabel("Density (cm^-3)") all_y = yNe + yNh + yNAm + yNDp if all_y: ax2.set_ylim([1e8, max(all_y) * 2]) ax2.legend() plt.subplots_adjust(wspace=0.4) plt.show(block=False) input("Press ENTER to exit>>")
if __name__ == "__main__": main()