"""
VASPのDOSCARファイルに基づき、キャリア密度とフェルミ準位を計算するスクリプト。
このスクリプトは、VASP計算で得られた状態密度 (DOS) データを用いて、半導体のキャリア輸送特性を評価します。
具体的には、電子密度、正孔密度、イオン化されたドナー・アクセプター密度、ホール係数などを、
温度の関数として、またはフェルミ準位の関数として計算します。
また、有効状態密度や特性温度などのパラメータも導出します。
関連リンク: :doc:`EF-T-DOS_usage`
"""
import os
import sys
from math import exp, sqrt, log
import numpy as np
from numpy import arange
from scipy import integrate # 数値積分関数 integrateを読み込む
from scipy import optimize # newton関数はscipy.optimizeモジュールに入っている
from scipy.interpolate import interp1d
from matplotlib import pyplot as plt
import csv # savecsv, read_csv関数で使用されるが、元のコードにはimportがなかったため追加。ルール1との兼ね合いで注意。ただし、これらの関数は現在未使用。
# constants
pi = 3.14159265358979323846
h = 6.6260755e-34 # Js";
hbar = 1.05459e-34 # "Js";
c = 2.99792458e8 # m/s";
e = 1.60218e-19 # C";
kB = 1.380658e-23 # JK<sup>-1</sup>";
me = 9.1093897e-31 # kg";
Debug = 0
#mode: 'EF', 'T'
mode = 'T'
# EF range for mode = 'EF'
T0 = 300 # K
dEFmin = -0.2 # measured from EV, eV
dEFmax = 0.2 # measured from EC, eV
nEF = 50
EFmin = None
EFmax = None
EFstep = None
# Temperature range for mode = 'T'
Tmin = 300 # K
Tmax = 600
nT = 11
Tstep = None
# Sample information (DOS.data is converted to states/eV/Vcell)
Vcell = 212.309 # A^3
# read from DOSCAR
file = "DOS.dat"
E_raw = None
dos_raw = None
nDOS = None
Emin = None
Emax = None
Estep = None
EV = None
EC = None
dEA = 0.32 # Acceptor level measured from EV, eV
NA = 0.8e17
dED = -0.28 # Donor level measured from EC, eV
ND = 0.5e17
EA = None # Acceptor states are not considered if EA = None
ED = None # Donor states are not considered if ED = None
# other parameters
EF0 = 0.4 # EF at charge neutral
Egth = 0.01 # Critera DOS value to search band edges
# integration parameters
# Integration range w.r.t. kBT
Einteg0 = -3.0
Einteg1 = 3.0
nrange = 6.0
# Relative accuracy of the quad functin
eps = 1.0e-5
nmaxdiv = 150
# Bisection method parameters
# Initial search range
dEbisec = 0.4
# EPS
epsbisec = 1.0e-7
# max iteration number
nmaxiter = 200
# Output cycle
iprintiterval = 1
# graph configuration
fontsize = 16
legend_fontsize = 12
#=============================
# Treat argments
#=============================
[ドキュメント]
def pfloat(str):
"""
文字列を浮動小数点数に変換する。
変換できない場合はNoneを返す。
:param str (str): 変換する文字列。
:returns: float または None: 変換された浮動小数点数、またはNone。
"""
try:
return float(str)
except:
return None
[ドキュメント]
def pint(str):
"""
文字列を整数に変換する。
変換できない場合はNoneを返す。
:param str (str): 変換する文字列。
:returns: int または None: 変換された整数、またはNone。
"""
try:
return int(str)
except:
return None
[ドキュメント]
def getarg(position, defval = None):
"""
コマンドライン引数を取得する。
指定された位置の引数が存在しない場合、デフォルト値を返す。
:param position (int): 取得する引数の位置(0はスクリプト名)。
:param defval (Any, optional): 引数が存在しない場合に返すデフォルト値。デフォルトはNone。
:returns: str または Any: 取得した引数、またはデフォルト値。
"""
try:
return sys.argv[position]
except:
return defval
[ドキュメント]
def getfloatarg(position, defval = None):
"""
コマンドライン引数を浮動小数点数として取得する。
`getarg`で取得した文字列を`pfloat`で浮動小数点数に変換する。
:param position (int): 取得する引数の位置。
:param defval (float, optional): 引数が存在しない場合に返すデフォルト値。デフォルトはNone。
:returns: float または None: 変換された浮動小数点数、またはNone。
"""
return pfloat(getarg(position, defval))
[ドキュメント]
def getintarg(position, defval = None):
"""
コマンドライン引数を整数として取得する。
`getarg`で取得した文字列を`pint`で整数に変換する。
:param position (int): 取得する引数の位置。
:param defval (int, optional): 引数が存在しない場合に返すデフォルト値。デフォルトはNone。
:returns: int または None: 変換された整数、またはNone。
"""
return pint(getarg(position, defval))
[ドキュメント]
def usage():
"""
スクリプトの正しい使用方法を標準出力に表示する。
コマンドライン引数の形式と例を示す。
"""
global mode
global Vcell, T0, EF0
global file, E_raw, dos_raw, nDOS, Emin, Emax, Estep
global dEFmin, dEFmax, EFmin, EFmax, nEF, EFstep
print("")
print("Usage: Variables in () are optional")
print(" python {} mode (file args)".format(sys.argv[0], ))
print(" (i) python {} T file Tmin Tmax nT)".format(sys.argv[0]))
print(" ex: {} T {} {} {} {}".format(sys.argv[0], file, Tmin, Tmax, nT))
print(" (ii) python {} EF file nEF)".format(sys.argv[0]))
print(" ex: {} EF {} {}".format(sys.argv[0], file, nEF))
[ドキュメント]
def updatevars():
"""
コマンドライン引数に基づいてグローバル変数を更新する。
`sys.argv`を解析し、`mode`, `file`, `Tmin`, `Tmax`, `nT`, `nEF`などの
グローバル変数を設定する。引数が不十分な場合は`usage`を呼び出して終了する。
"""
global mode
global Vcell, T0, EF0
global file, E_raw, dos_raw, nDOS, Emin, Emax, Estep
global dEFmin, dEFmax, EFmin, EFmax, nEF, EFstep
global Tmin, Tmax, nT, Tstep
argv = sys.argv
if len(argv) == 1:
usage()
exit()
mode = getarg( 1, mode)
file = getarg( 2, file)
if mode == 'EF':
nEF = getintarg(3, nEF)
if mode == 'T':
Tmin = getintarg(3, Tmin)
Tmax = getintarg(4, Tmax)
nT = getintarg(5, nT)
Tstep = (Tmax - Tmin) / (nT - 1)
#=============================
# other functions
#=============================
[ドキュメント]
def savecsv(outfile, header, datalist):
"""
データをCSVファイルに保存する。
ヘッダーとデータリストを行ごとに書き出す。
:param outfile (str): 出力ファイル名。
:param header (list[str]): CSVファイルのヘッダー行。
:param datalist (list[list[Any]]): 列ごとのデータを含むリストのリスト。
:returns: None
"""
try:
print("Write to [{}]".format(outfile))
f = open(outfile, 'w')
except:
# except IOError:
print("Error: Can not write to [{}]".format(outfile))
else:
fout = csv.writer(f, lineterminator='\n')
fout.writerow(header)
# fout.writerows(data)
for i in range(0, len(datalist[0])):
a = []
for j in range(len(datalist)):
a.append(datalist[j][i])
fout.writerow(a)
f.close()
[ドキュメント]
def read_csv(infile, xmin = None, xmax = None, delimiter = ','):
"""
CSVファイルからデータを読み込む。
指定されたファイルからヘッダーと2列の数値データを読み込み、
xminとxmaxの範囲でフィルタリングする。
:param infile (str): 入力ファイル名。
:param xmin (float, optional): x値の下限(Noneの場合はフィルタリングなし)。デフォルトはNone。
:param xmax (float, optional): x値の上限(Noneの場合はフィルタリングなし)。デフォルトはNone。
:param delimiter (str, optional): 列の区切り文字。デフォルトは','。
:returns: tuple[list[str], list[float], list[float]]: ヘッダー、xデータ、yデータのタプル。
"""
print("xrange=", xmin, xmax)
data = []
try:
infp = open(infile, "r")
f = csv.reader(infp, delimiter = delimiter)
header = next(f)
print("header=", header)
for j in range(len(header)):
data.append([])
for row in f:
x = pfloat(row[0])
if xmin is not None and xmin <= x <= xmax:
y = pfloat(row[1])
data[0].append(x)
data[1].append(y)
except:
print("Error: Can not read [{}]".format(infile))
exit()
return header, data[0], data[1]
[ドキュメント]
def FindBandEdges(E, DOS, EF0, Egth):
"""
DOSデータから価電子帯上端 (EV) と伝導帯下端 (EC) を見つける。
フェルミ準位の初期値EF0を基準に、DOS値がEgthを超える範囲を探索してEVとECを特定する。
:param E (numpy.ndarray): エネルギー値の配列。
:param DOS (numpy.ndarray): DOS値の配列。
:param EF0 (float): フェルミ準位の初期値 (eV)。
:param Egth (float): バンド端を決定するためのDOSのスレッショルド値。
:returns: tuple[float, float]: 価電子帯上端EVと伝導帯下端ECのタプル。
"""
EV = 1.0e30
EC = 1.0e30
i0 = -1
for i in range(len(E)):
if E[i] >= EF0:
i0 = i
break
for i in range(i0, 0, -1):
if DOS[i] > Egth:
EV = E[i+1]
break
for i in range(i0, len(E), +1):
if DOS[i] > Egth:
EC = E[i-1]
break
return EV, EC
[ドキュメント]
def rieman(x0, dx, y, xmin, xmax):
"""
リーマン和を用いて数値積分を行う。
xminからxmaxの範囲で、等間隔のデータyに対してリーマン和を計算する。
:param x0 (float): データの開始x値。
:param dx (float): x軸のステップ幅。
:param y (list[float]): 積分対象のy値のリスト。
:param xmin (float): 積分範囲の下限。
:param xmax (float): 積分範囲の上限。
:returns: float: 計算された積分値。
"""
S = 0.0
for i in range(len(y)):
x = x0 + i * dx
if x < xmin or xmax < x:
continue
S += y[i]
return S * dx
# define the DOS function
[ドキュメント]
def DOS(E):
"""
指定されたエネルギーにおける状態密度 (DOS) を補間して返す。
グローバル変数`E_raw`と`dos_raw`を用いて、`scipy.interpolate.interp1d`による3次スプライン補間を行う。
DOSのE範囲外が指定された場合はエラーで終了する。
:param E (float): エネルギー (eV)。
:returns: float: 補間されたDOS値 (states/eV/Vcell単位をcm^-3に変換済み)。
"""
global E_raw, dos_raw
global Emin, Emax, Estep, nDOS
if E < Emin or Emax < E:
print("Error in f_dos: Given E={} exceeds the DOS E range [{}, {}]".format(E, Emin, Emax))
exit()
fdos = interp1d(E_raw, dos_raw, kind = 'cubic')
return fdos(E)
[ドキュメント]
def fe(E, T, EF):
"""
フェルミ・ディラック分布関数を計算する。
エネルギーE、温度T、フェルミ準位EFにおける電子の占有確率を返す。
T=0Kの場合はステップ関数として処理される。
:param E (float): エネルギー (eV)。
:param T (float): 温度 (K)。
:param EF (float): フェルミ準位 (eV)。
:returns: float: フェルミ・ディラック分布関数の値。
"""
global e, kB
if T == 0.0:
if E < EF:
return 1.0
else:
return 0.0
return 1.0 / (exp((E - EF) * e / kB / T) + 1.0)
[ドキュメント]
def fh(E, T, EF):
"""
正孔の占有確率(1 - fe)を計算する。
フェルミ・ディラック分布関数`fe`を用いて正孔の占有確率を計算する。
:param E (float): エネルギー (eV)。
:param T (float): 温度 (K)。
:param EF (float): フェルミ準位 (eV)。
:returns: float: 正孔の占有確率。
"""
return 1.0 - fe(E, T, EF)
[ドキュメント]
def DOSfe(E, T, EF):
"""
DOSとフェルミ・ディラック分布関数の積を計算する。
`DOS(E)`と`fe(E, T, EF)`の積を返す。これは電子密度計算の被積分関数となる。
:param E (float): エネルギー (eV)。
:param T (float): 温度 (K)。
:param EF (float): フェルミ準位 (eV)。
:returns: float: `DOS(E) * fe(E, T, EF)` の値。
"""
return DOS(E) * fe(E, T, EF)
[ドキュメント]
def DOSfh(E, T, EF):
"""
DOSと正孔の占有確率の積を計算する。
`DOS(E)`と`fh(E, T, EF)`の積を返す。これは正孔密度計算の被積分関数となる。
:param E (float): エネルギー (eV)。
:param T (float): 温度 (K)。
:param EF (float): フェルミ準位 (eV)。
:returns: float: `DOS(E) * fh(E, T, EF)` の値。
"""
return DOS(E) * fh(E, T, EF)
[ドキュメント]
def integrate(func, E0, E1, h):
"""
台形公式を用いて数値積分を行う。
指定された関数`func`を`E0`から`E1`まで、ステップ幅`h`で台形公式により数値積分する。
:param func (callable): 積分対象の関数。
:param E0 (float): 積分範囲の下限。
:param E1 (float): 積分範囲の上限。
:param h (float): 積分ステップ幅。
:returns: list[float, float]: 積分結果とダミーのエラー値 (-1.0) のリスト。
"""
n = int((E1 - E0) / h + 1.000001)
h = (E1 - E0) / (n - 1)
y = [func(E0 + i * h) for i in range(n)]
S = 0.5 * (y[0] + y[n-1]) + sum(y[1:n-1])
return [h * S, -1.0]
[ドキュメント]
def Ne(T, EF, E0, E1):
"""
指定された範囲における電子密度を計算する。
`DOSfe`関数を`E0`から`E1`まで積分し、電子数を求める。
:param T (float): 温度 (K)。
:param EF (float): フェルミ準位 (eV)。
:param E0 (float): 積分範囲の下限。
:param E1 (float): 積分範囲の上限。
:returns: float: 計算された電子密度。
"""
global Estep
# N = integrate.quad(lambda E: DOSfe(E, T, EF), E0, E1, limit = nmaxdiv, epsrel = eps)
N = integrate(lambda E: DOSfe(E, T, EF), E0, E1, Estep)
if Debug:
print(" Ne integ error: {}".format(N[1]))
return N[0]
[ドキュメント]
def Nh(T, EF, E0, E1):
"""
指定された範囲における正孔密度を計算する。
`DOSfh`関数を`E0`から`E1`まで積分し、正孔数を求める。
:param T (float): 温度 (K)。
:param EF (float): フェルミ準位 (eV)。
:param E0 (float): 積分範囲の下限。
:param E1 (float): 積分範囲の上限。
:returns: float: 計算された正孔密度。
"""
global Estep
# N = integrate.quad(lambda E: DOSfh(E, T, EF), E0, E1, limit = nmaxdiv, epsrel = eps)
N = integrate(lambda E: DOSfh(E, T, EF), E0, E1, Estep)
if Debug:
print(" Nh integ error: {}".format(N[1]))
return N[0]
[ドキュメント]
def NDp(EF, T):
"""
イオン化されたドナー密度を計算する。
ドナー準位`ED`とドナー濃度`ND`、フェルミ・ディラック分布を用いてイオン化率を計算する。
:param EF (float): フェルミ準位 (eV)。
:param T (float): 温度 (K)。
:returns: float: イオン化されたドナー密度。
"""
global ND, ED, kB
return ND * (1.0 - fe(ED, T, EF))
[ドキュメント]
def NAm(EF, T):
"""
イオン化されたアクセプター密度を計算する。
アクセプター準位`EA`とアクセプター濃度`NA`、フェルミ・ディラック分布を用いてイオン化率を計算する。
:param EF (float): フェルミ準位 (eV)。
:param T (float): 温度 (K)。
:returns: float: イオン化されたアクセプター密度。
"""
global NA, EA, kB
n = NA * fe(EA, T, EF)
# print("n=", EF, T, NA, EA, n)
return n
[ドキュメント]
def CalEF(T, EF0, E0, E1, totNe):
"""
電荷中性条件に基づいてフェルミ準位を計算する (現在未使用の関数)。
`scipy.optimize.newton`メソッドを使用して、電子密度`Ne`が`totNe`に等しくなるフェルミ準位`EF`を見つける。
:param T (float): 温度 (K)。
:param EF0 (float): フェルミ準位の初期推測値 (eV)。
:param E0 (float): 積分範囲の下限。
:param E1 (float): 積分範囲の上限。
:param totNe (float): 目標とする全電子密度。
:returns: float: 電荷中性条件を満たすフェルミ準位。
"""
EF = optimize.newton(lambda EF: Ne(T, EF, E0, E1) - totNe, EF0)
return EF
[ドキュメント]
def exec_T():
"""
温度の関数としてキャリア密度とフェルミ準位を計算・プロットする。
指定された温度範囲でフェルミ準位を二分法で繰り返し決定し、
電子密度、正孔密度、イオン化ドナー/アクセプター密度、ホール係数などを計算する。
結果をプロットし、特性温度も算出する。
:returns: int または None: エラー発生時に0を返す。
"""
global mode, T0, EF0
global file, E_raw, dos_raw
global nDOS, Emin, Emax, Estep
global EV, EC
global dEFmin, dEFmax, EFmin, EFmax, nEF, EFstep
global dEA, NA, dED, ND, EA, ED
xT = []
xTinv = []
yEF = []
yEint = []
yEentropy = []
yEFa = []
yNe = []
yNh = []
yNDp = []
yNAm = []
yRH = []
EFprev = EF0
#初期範囲
EFmin = EF0 - dEbisec
EFmax = EF0 + dEbisec
dEFbisec = 0.1
print ("%5s\t%8s\t%14s" % ("T(K)", "EF(eV)", "Ne(0 K) (check)"))
for iT in range(nT):
T = Tmin + iT * Tstep
print("")
print("iT {:03d}: {} K".format(iT, T))
kBTe = kB * T / e
dE = nrange * kBTe
E0 = EV - dE
E1 = EC + dE
if E0 < Emin or Emax < E1:
print("*** Integration range error: [{}, {}] exceeds DOS E range [{}, {}]".format(E0, E1, Emin, Emax))
exit()
Ne0K = Ne(0.0, EF0, E0, EF0)
# まず、EFmin,EFmaxにおけるΔQを計算し、それぞれが正・負あるいは負・生となっていることを確認する
print("E range:", EF0, E1)
# Nemin = Ne(T, EFmin, EF0, E1)
Nemin = Ne(T, EFmin, EC - Estep, E1)
# Nhmin = Nh(T, EFmin, E0, EF0)
Nhmin = Nh(T, EFmin, E0, EV + Estep)
NAmmin = NAm(EFmin, T)
NDpmin = NDp(EFmin, T)
dQmin = Nemin + NAmmin - Nhmin - NDpmin
# Nemax = Ne(T, EFmax, EF0, E1)
Nemax = Ne(T, EFmax, EC - Estep, E1)
# Nhmax = Nh(T, EFmax, E0, EF0)
Nhmax = Nh(T, EFmax, E0, EV + Estep)
NAmmax = NAm(EFmax, T)
NDpmax = NDp(EFmax, T)
dQmax = Nemax + NAmmax - Nhmax - NDpmax
print(" min (EF,Ne,Nh,NDp,NAm,dQ)=({:6.4f},{:10.4g},{:10.4g},{:10.4g},{:10.4g},{:10.4g})"
.format(EFmin, Nemin, Nhmin, NDpmin, NAmmin, dQmin))
print(" max (EF,Ne,Nh,NDp,NAm,dQ)=({:6.4f},{:10.4g},{:10.4g},{:10.4g},{:10.4g},{:10.4g})"
.format(EFmax, Nemax, Nhmax, NDpmax, NAmmax, dQmax))
# dQmin = Ne(EFmin, T) + NAm(EFmin, T) - Nh(EFmin, T) - NDp(EFmin, T)
# dQmax = Ne(EFmax, T) + NAm(EFmax, T) - Nh(EFmax, T) - NDp(EFmax, T)
# print(" EFmin = {:12.8f} dQmin = {:12.4g}".format(EFmin, dQmin))
# print(" EFmax = {:12.8f} dQmax = {:12.4g}".format(EFmax, dQmax))
print(" dQ=", dQmin, dQmax)
print(" dQmin*dQmax=", dQmin * dQmax)
if dQmin * dQmax > 0.0:
print("Error: Initial Emin and Emax should be chosen as dQmin * dQmax < 0")
return 0
# 2分法開始
for i in range(nmaxiter):
EFhalf = (EFmin + EFmax) / 2.0
# Neh = Ne(T, EFhalf, EF0, E1)
Neh = Ne(T, EFhalf, EC - Estep, E1)
# Nhh = Nh(T, EFhalf, E0, EF0)
Nhh = Nh(T, EFhalf, E0, EV + Estep)
NAmh = NAm(EFhalf, T)
NDph = NDp(EFhalf, T)
dQhalf = Neh + NAmh - Nhh - NDph
print(" iter {:03d}: half (EF,Ne,Nh,NDp,NAm,dQ)=({:6.4f},{:10.4g},{:10.4g},{:10.4g},{:10.4g},{:10.4g})"
.format(i, EFhalf, Neh, Nhh, NDph, NAmh, dQhalf))
# EFの精度がepsより小さくなったら計算終了
if abs(EFmin - EFhalf) < epsbisec and abs(EFmax - EFhalf) < epsbisec:
# print(" Success: Convergence reached at EF = {}".format(EFhalf))
break
if dQmin * dQhalf < 0.0:
EFmax = EFhalf
dQmax = dQhalf
else:
EFmin = EFhalf
dQmin = dQhalf
else:
print(" Failed: Convergence did not reach")
return 0
EF = EFhalf
RH = 1.0 / e * (Nhh - Neh) / pow(Nhh + Neh, 2.0)
xT.append(T)
xTinv.append(1000.0 / T)
yEF.append(EF)
yNe.append(Neh)
yNh.append(Nhh)
yNDp.append(NDph)
yNAm.append(NAmh)
yRH.append(RH)
print(" ***(T,EF,Ne,Nh,NDp,NAm,RH)=({:6.4f},{:6.4g},{:10.4g},{:10.4g},{:10.4g},{:10.4g},{:10.4g})"
.format(T, EF, Neh, Nhh, NDph, NAmh, RH))
EFprev = EF
EFmin = EF - dEFbisec
EFmax = EF + dEFbisec
print ("%5.0f\t%12.6g" % (T, EF))
print("")
yEaNe = []
yEaNeTh = []
yEaNh = []
yEaNhTh = []
print ("%8s %8s %8s %8s %8s %8s %12s %10s %12s %12s %12s"
% ("T(K)", "EF(eV)", "Ea(Ne,meV)", "Ea(Ne,T1/2)", "Ea(Nh,meV)", "Ea(Nh,T1/2)", "Ne", "Nh", "ND+", "NA-", "RH"))
for i in range(nT):
if i == 0:
i1 = i
i2 = i+1
elif i == nT - 1:
i1 = i-1
i2 = i
else:
i1 = i-1
i2 = i+1
Th1 = sqrt(xT[i1])
Th2 = sqrt(xT[i2])
dT = 1.0 / (xTinv[i2] - xTinv[i1])
EaNe = (log(yNe[i2]) - log(yNe[i1])) * dT # meV
EaNeTh = (log(yNe[i2]/Th2) - log(yNe[i1]/Th1)) * dT # meV
EaNh = (log(yNh[i2]) - log(yNh[i1])) * dT # meV
EaNhTh = (log(yNh[i2]/Th2) - log(yNh[i1]/Th1)) * dT # meV
yEaNe.append (-1000.0 * 1000.0 * kB / e * EaNe)
yEaNeTh.append(-1000.0 * 1000.0 * kB / e * EaNeTh)
yEaNh.append (-1000.0 * 1000.0 * kB / e * EaNh)
yEaNhTh.append(-1000.0 * 1000.0 * kB / e * EaNhTh)
print("%5.3f %8.3f %8.3g %8.3g %8.3g %8.3g " % (xT[i], yEF[i], yEaNe[i], yEaNeTh[i], yEaNh[i], yEaNhTh[i])
+ "%12g %12g %12g %12g %12g" % (yNe[i], yNh[i], yNDp[i], yNAm[i], yRH[i]))
#=============================
# Plot graphs
#=============================
fig = plt.figure(figsize = (12, 8))
ax1 = fig.add_subplot(2, 3, 1)
ax2 = fig.add_subplot(2, 3, 2)
ax3 = fig.add_subplot(2, 3, 4)
ax4 = fig.add_subplot(2, 3, 3)
ax5 = fig.add_subplot(2, 3, 6)
ax1.plot(xT, yEF, label = 'EF', color = 'black')
ax1.set_xlabel("T (K)", fontsize = fontsize)
ax1.set_ylabel("E$_F$ (eV)", fontsize = fontsize)
ax1.legend(fontsize = legend_fontsize)
ax2.plot(E_raw, dos_raw, label = 'DOS', color = 'black')
ax2.plot([EF0, EF0], [min(dos_raw), max(dos_raw)], color = 'red', linestyle = 'dashed')
ax2.set_xlabel("E (eV)", fontsize = fontsize)
ax2.set_ylabel("DOS (states/cm$^3$)", fontsize = fontsize)
ax2.legend(fontsize = legend_fontsize)
ax3.plot(xT, yRH, label = 'RH', color = 'black', marker = 'o')
ax3.set_xlabel("T (K)", fontsize = fontsize)
ax3.set_ylabel("R$_H$ (C$^{-1}$cm$^{-3}$)", fontsize = fontsize)
ax3.legend(fontsize = legend_fontsize)
ax4.plot(xTinv, yNe, label = 'Ne', color = 'black', marker = 'o')
ax4.plot(xTinv, yNh, label = 'Nh', color = 'red', marker = 'o')
ax4.plot(xTinv, yNDp, label = 'ND$^+$', color = 'blue', marker = 'x')
ax4.plot(xTinv, yNAm, label = 'NA$^-$', color = 'green', marker = 'x')
ax4.set_yscale('log')
# ax4.plot([0, 0], ax3.get_ylim(), color = 'red', linestyle = 'dashed', linewidth = 0.5)
ax4.set_xlabel("1000/T (K$^{-1}$)", fontsize = fontsize)
ax4.set_ylabel("N (cm$^{-3}$)", fontsize = fontsize)
ax4.legend(fontsize = legend_fontsize)
ax5.plot(xTinv, yEaNe, label = 'Ea(Ne)', color = 'black', marker = 'o')
ax5.plot(xTinv, yEaNeTh, label = 'Ea(Ne*T$^{-0.5}$)', color = 'black', marker = 'o')
ax5.plot(xTinv, yEaNh, label = 'Ea(Nh)', color = 'red', marker = 'o')
ax5.plot(xTinv, yEaNhTh, label = 'Ea(Nh*T$^{-0.5})', color = 'red', marker = 'o')
ax5.set_xlabel("1000/T (K$^{-1}$)", fontsize = fontsize)
ax5.set_ylabel("Ea (meV)", fontsize = fontsize)
ax5.legend(fontsize = legend_fontsize)
ax1.tick_params(labelsize = fontsize)
ax2.tick_params(labelsize = fontsize)
ax3.tick_params(labelsize = fontsize)
ax4.tick_params(labelsize = fontsize)
ax5.tick_params(labelsize = fontsize)
# Rearange the graph axes so that they are not overlapped
plt.tight_layout()
# plt.pause(0.1)
print("Close the graph window to terminate this program")
plt.show()
print("")
usage()
print("")
# print("Press ENTER to exit>>", end = '')
# input()
[ドキュメント]
def exec_EF():
"""
フェルミ準位の関数としてキャリア密度などを計算・プロットする。
指定されたフェルミ準位範囲で電子密度、正孔密度、イオン化ドナー/アクセプター密度、
ホール係数などを計算する。結果をプロットし、有効状態密度や特性温度も算出する。
:returns: None
"""
global mode, T0, EF0
global file, E_raw, dos_raw
global nDOS, Emin, Emax, Estep
global EV, EC
global dEFmin, dEFmax, EFmin, EFmax, nEF, EFstep
global dEA, NA, dED, ND, EA, ED
kBTe = kB * T0 / e
dE = nrange * kBTe
E0 = EV - dE
E1 = EC + dE
xEF = []
yNe = []
yNh = []
yNDp = []
yNAm = []
yRH = []
EFprev = EF0
print("at {} K".format(T0))
print ("%5s\t%8s\t%14s\t%14s\t%14s\t%14s" % ("EF(eV)", "Ne", "Nh", "ND+", "NA-", "RH"))
for iEF in range(nEF):
EF = EFmin + iEF * EFstep
dE = nrange * kBTe
# Ne1 = Ne(T0, EF, EF0, E1)
Ne1 = Ne(T0, EF, EC - Estep, E1)
# Nh1 = Nh(T0, EF, E0, EF0)
Nh1 = Nh(T0, EF, E0, EV + Estep)
NDp1 = NDp(EF, T0)
NAm1 = NAm(EF, T0)
RH = 1.0 / e * (Nh1 - Ne1) / pow(Nh1 + Ne1, 2.0)
xEF.append(EF)
yNe.append(Ne1)
yNh.append(Nh1)
yNDp.append(NDp1)
yNAm.append(NAm1)
yRH.append(RH)
print ("%10.4f\t%12.6g\t%12.6g\t%12.6g\t%12.6g\t%12.6g" % (EF, Ne1, Nh1, NDp1, NAm1, RH))
yT0Ne = []
yT0Nh = []
print("")
print ("%5s\t%8s\t%12s\t%12s\t%12s\t%12s\t%12s\t%12s" % ("EF(eV)", "Ne", "Nh", "ND+", "NA-", "RH", "T0(Ne)", "T0(Nh)"))
for i in range(nEF):
if i == 0:
i1 = i
i2 = i+1
elif i == nEF - 1:
i1 = i-1
i2 = i
else:
i1 = i-1
i2 = i+1
dEF = xEF[i2] - xEF[i1]
dlnNedEF = (log(yNe[i2]) - log(yNe[i1])) / dEF
dlnNhdEF = (log(yNh[i2]) - log(yNh[i1])) / dEF
T0Ne = 1.0 / kB / dlnNedEF * e
T0Nh = -1.0 / kB / dlnNhdEF * e
yT0Ne.append(T0Ne)
yT0Nh.append(T0Nh)
print ("%10.4f\t%10.4g\t%10.4g\t%10.4g\t%10.4g\t%10.4g\t%10.4g\t%10.4g" % (EF, Ne1, Nh1, NDp1, NAm1, RH, T0Ne, T0Nh))
print("")
Emid = (EV + EC) / 2.0
print("Effective density of states at the mid gap {} eV:".format(Emid))
fNe = interp1d(xEF, yNe, kind = 'cubic')
fNh = interp1d(xEF, yNh, kind = 'cubic')
dlnNedEF = (log(fNe(Emid + Estep)) - log(fNe(Emid - Estep))) / 2.0 / Estep
dlnNhdEF = (log(fNh(Emid + Estep)) - log(fNh(Emid - Estep))) / 2.0 / Estep
T0Ne = 1.0 / kB / dlnNedEF * e
T0Nh = -1.0 / kB / dlnNhdEF * e
NC = fNe(Emid) * exp((EC - Emid) * e / kB / T0Ne)
NV = fNh(Emid) * exp((Emid - EV) * e / kB / T0Nh)
print(" NC = {} cm-3 (T0 = {} K)".format(NC, T0Nh))
print(" NV = {} cm-3 (T0 = {} K)".format(NV, T0Nh))
xEFapprox = [EFmin + i * EFstep for i in range(nEF)]
yNeapprox = [NC * exp(-(EC - xEFapprox[i]) * e / kB / T0) for i in range(nEF)]
yNhapprox = [NV * exp(-(xEFapprox[i] - EV) * e / kB / T0) for i in range(nEF)]
# calculate DC(E)*fe(E), DV(E)*(1-fe(E))
print("")
print("Calculate fe(E), fh(E)=1-fe(E), DC(E)*fe(E), DV(E)*fh(E) at EF={} eV, T={} K".format(EF0, T0))
yfe = []
yfh = []
yDOSfe = []
yDOSfh = []
for i in range(len(E_raw)):
E = E_raw[i]
yfe.append(fe(E, T0, EF0))
yfh.append(fh(E, T0, EF0))
if E >= EC:
yDOSfe.append(DOSfe(E, T0, EF0))
else:
yDOSfe.append(0.0)
if E <= EV:
yDOSfh.append(DOSfh(E, T0, EF0))
else:
yDOSfh.append(0.0)
#=============================
# Plot graphs
#=============================
fig = plt.figure(figsize = (12, 8))
ax2 = fig.add_subplot(2, 2, 1)
ax2r = ax2.twinx()
ax3 = fig.add_subplot(2, 2, 2)
ax4 = fig.add_subplot(2, 2, 3)
ax1 = fig.add_subplot(2, 2, 4)
ax2.plot(E_raw, dos_raw, label = 'DOS', color = 'black')
ax2.plot(E_raw, yDOSfe, label = 'D(E)fe', color = 'blue', linewidth = 0.5)
ax2.plot(E_raw, yDOSfh, label = 'D(E)fh', color = 'red', linewidth = 0.5)
ax2.plot([EF0, EF0], [min(dos_raw), max(dos_raw)], color = 'red', linestyle = 'dashed')
ax2r.plot(E_raw, yfe, label = 'fe', color = 'blue', linewidth = 0.5, linestyle = 'dashed')
ax2r.plot(E_raw, yfh, label = 'fh', color = 'red', linewidth = 0.5, linestyle = 'dashed')
ax2.set_ylim([0.0, max(dos_raw) * 1.1])
ax2r.set_ylim([0.0, 2.0])
ax2.set_xlabel("E (eV)", fontsize = fontsize)
ax2.set_ylabel("DOS (states/cm$^3$)", fontsize = fontsize)
ax2r.set_ylabel("fe, fh", fontsize = fontsize)
# ax2.legend()
h2l, l2l = ax2.get_legend_handles_labels()
h2r, l2r = ax2r.get_legend_handles_labels()
ax2.legend(h2l + h2r, l2l + l2r, fontsize = legend_fontsize)
ax3.plot(xEF, yNe, label = 'Ne', color = 'black') #, marker = 'o')
ax3.plot(xEF, yNh, label = 'Nh', color = 'red') #, marker = 'x')
ax3.plot(xEF, yNDp, label = 'ND$^+$', color = 'blue') #, marker = 'x')
ax3.plot(xEF, yNAm, label = 'NA$^-$', color = 'green') #, marker = 'x')
ax3.plot(xEFapprox, yNeapprox, label = 'Ne(Boltz)', color = 'gray', linewidth = 0.5) #, marker = 'x')
ax3.plot(xEFapprox, yNhapprox, label = 'Nh(Boltz)', color = 'purple', linewidth = 0.5) #, marker = 'x')
ax3.plot([EV, EV], ax3.get_ylim(), color = 'red', linestyle = 'dashed', linewidth = 0.5)
ax3.plot([EC, EC], ax3.get_ylim(), color = 'red', linestyle = 'dashed', linewidth = 0.5)
ax3.set_yscale('log')
ax3.set_xlabel("EF (eV)", fontsize = fontsize)
ax3.set_ylabel("N (cm$^{-3}$)", fontsize = fontsize)
ax3.legend(fontsize = legend_fontsize)
ax4.plot(xEF, yRH, label = 'R$_H$', color = 'black', marker = 'o')
ax4.set_xlabel("EF (eV)", fontsize = fontsize)
ax4.set_ylabel("R$_H$ (C$^{-1}$cm$^{-3}$)", fontsize = fontsize)
ax4.legend(fontsize = legend_fontsize)
ax1.plot(xEF, yT0Ne, label = 'T$_0$(Ne)', color = 'black') #, marker = 'o')
ax1.plot(xEF, yT0Nh, label = 'T$_0$(Nh)', color = 'red') #, marker = 'x')
ax1.plot([EV, EV], ax3.get_ylim(), color = 'red', linestyle = 'dashed', linewidth = 0.5)
ax1.plot([EC, EC], ax3.get_ylim(), color = 'red', linestyle = 'dashed', linewidth = 0.5)
ax1.set_ylim([0.0, 2.0*T0])
ax1.set_xlabel("EF (eV)", fontsize = fontsize)
ax1.set_ylabel("T$_0$ (K)", fontsize = fontsize)
ax1.legend(fontsize = legend_fontsize)
ax1.tick_params(labelsize = fontsize)
ax2.tick_params(labelsize = fontsize)
ax2r.tick_params(labelsize = fontsize)
ax3.tick_params(labelsize = fontsize)
ax4.tick_params(labelsize = fontsize)
# Rearange the graph axes so that they are not overlapped
plt.tight_layout()
# plt.pause(0.1)
print("Close the graph window to terminate this program")
plt.show()
print("")
usage()
print("")
# print("Press ENTER to exit>>", end = '')
# input()
[ドキュメント]
def main():
"""
スクリプトのメイン実行関数。
コマンドライン引数を処理し、DOSファイルを読み込み、バンド端やドーピング準位を設定する。
その後、`mode`変数に応じて`exec_T`または`exec_EF`を呼び出し、
キャリア計算と結果のプロットを実行する。
:returns: None
"""
global mode, T0, EF0
global Vcell, file, E_raw, dos_raw
global nDOS, Emin, Emax, Estep
global EV, EC
global dEFmin, dEFmax, EFmin, EFmax, nEF, EFstep
global dEA, NA, dED, ND, EA, ED
updatevars()
print("mode: ", mode)
print("DOS configuration")
print(" Read from [{}]".format(file))
E_raw, dos_raw = np.genfromtxt(file, skip_header=1, usecols=(0,1), unpack=True)
nDOS = len(E_raw)
for i in range(len(E_raw)):
dos_raw[i] *= 1.0 / (Vcell * 1.0e-24)
Emin = E_raw[0]
Emax = E_raw[nDOS-1]
Estep = E_raw[1] - E_raw[0]
print(" E range: {} - {}, {} eV step".format(Emin, Emax, Estep))
print("")
EV, EC = FindBandEdges(E_raw, dos_raw, EF0, Egth)
Eg = EC - EV
print(" Band edge: EV={} EC={} Eg={} eV".format(EV, EC, Eg))
EFmin = EV + dEFmin
EFmax = EC + dEFmax
EFstep = (EFmax - EFmin) / (nEF - 1)
ED = EC + dED
EA = EV + dEA
print(" Donor level : {} eV, {} cm^-3".format(ED, ND))
print(" Acceptor level: {} eV, {} cm^-3".format(EA, NA))
print("")
print("EF dependence")
print(" EF range: {} - {}, {} eV step".format(EFmin, EFmax, EFstep))
print("")
print("T dependence")
print(" Temperature range: {} - {}, {} K step".format(Tmin, Tmax, Tstep))
print("")
print("Integration configuration")
print(" Integration E range: {} - {} eV, or EF+-{}*kBT eV".format(Einteg0, Einteg1, nrange))
print(" quad() parameters")
print(" releps=", eps)
print(" nmaxdiv=", nmaxdiv)
print("")
print("EF at 0 K = ", EF0, " eV")
Ne0K = Ne(0.0, EF0, -5.0, EF0)
# Ne0K = Ne(0.0, EF0, Einteg0, EF0)
print(" Ne at 0 K = ", Ne0K, " /u.c.")
print("")
if mode == 'T':
exec_T()
elif mode == 'EF':
exec_EF()
else:
print("")
print("Error: Invalid mode [{}]".format(mode))
if __name__ == '__main__':
main()