vasp_defect.py ダウンロード/コピー
vasp_defect.py
vasp_defect.py
1"""
2概要:
3 VASPのDOSCARファイルに基づきキャリア密度とフェルミ準位を計算するスクリプト。
4詳細説明:
5 このスクリプトは、VASP計算から得られたDOSCAR、OUTCAR、EIGENVAL、POSCARファイル、
6 および欠陥形成エネルギーのExcelファイルを利用して、半導体の電子・正孔キャリア密度、
7 欠陥密度、およびフェルミ準位の温度依存性またはフェルミ準位依存性を計算・解析します。
8 特に、温度変化に伴うフェルミ準位の自己無撞着計算や、欠陥の電荷状態による形成エネルギーの変化、
9 遷移準位の特定、およびこれらの特性のグラフ表示をサポートします。
10関連リンク:
11 vasp_defect_usage
12"""
13import os
14import sys
15import glob
16import re
17from math import exp, sqrt, log
18
19try:
20 import tklib.tkimport as imp
21except Exception as e:
22 print()
23 print("######################################################################")
24 print("########### ERROR ERROR ERROR ERROR ERROR ERROR #####################")
25 print("######################################################################")
26 print(f"# Failed to import [tklib.tkimport] module ({e}).")
27 print(f"# Add [tkProg]{os.sep}tklib{os.sep}python to PYTHONPATH variable")
28 print(f"# Current PYTHONPATH:", sys.path)
29 print("######################################################################")
30 input("Press ENTER to terminate>>\n")
31 exit()
32
33np = imp.import_lib("numpy", stop_by_error = False)
34scipy = imp.import_lib("scipy", stop_by_error = False)
35mpl = imp.import_lib("matplotlib", stop_by_error = False)
36tk = imp.import_lib("tkinter", stop_by_error = False)
37
38from numpy import arange
39from scipy import integrate # 数値積分関数 integrateを読み込む
40from scipy import optimize # newton関数はscipy.optimizeモジュールに入っている
41from scipy.interpolate import interp1d
42from matplotlib import pyplot as plt
43
44from tklib.tkfile import tkFile
45import tklib.tkre as tkre
46from tklib.tkutils import mprint, IsDir, IsFile, SplitFilePath, modify_path, delete_file, minmax_xy
47from tklib.tkutils import terminate, safe_getelement, pint, pfloat, getarg, getintarg, getfloatarg
48from tklib.tkutils import colors
49from tklib.tkapplication import tkApplication
50from tklib.tkparams import tkParams
51from tklib.tksci.tksci import Reduce01, Round
52from tklib.tksci.tkmatrix import make_matrix1, make_matrix2, make_matrix3
53from tklib.tkcrystal.tkcif import tkCIF, tkCIFData
54from tklib.tkcrystal.tkcrystal import tkCrystal
55from tklib.tkcrystal.tkvasp import tkVASP
56from tklib.tksci import tkequation
57import tklib.tkcsv as csv # csvモジュールを使用するためにtkcsvをcsvとしてimport
58from tklib.tkexcel import tkExcel
59from tklib.tkgraphic.tkplotevent import tkPlotEvent
60from tklib.tkgui.tksimple_gui import get_window_from_plt, CustomDialog_with_config
61
62
63# constants
64pi = 3.14159265358979323846
65h = 6.6260755e-34 # Js";
66hbar = 1.05459e-34 # "Js";
67c = 2.99792458e8 # m/s";
68e = 1.60218e-19 # C";
69kB = 1.380658e-23 # JK<sup>-1</sup>";
70me = 9.1093897e-31 # kg";
71log10 = log(10.0)
72
73#==================================
74# global variables
75#==================================
76Debug = 0
77
78# option flag to stop ISPIN=2 visualization
79stop_spin_polarized = False
80
81#mode: 'EF', 'T'
82mode = 'EF'
83#mode = 'T'
84
85# plot_mode: 'all', 'min'
86plot_mode = 'all'
87plot_mode = 'min'
88
89# index of point to be plotted
90iPoint = 0
91
92# files
93outputfile = None
94outfp = None
95dH_path = 'input.xlsx'
96CAR_path = '.'
97DOSCAR_path = None
98CAR_path_defect = None
99
100T0 = 300 # K, Electron tempeature
101Tdef = 300 # K, Defect frozen tempearture
102EFdef = 'eq' # eV, EF at Tdef to freeze defect densities. 'eq' will calculate EF,eq at Tdef
103
104# EF range for mode = 'EF'
105dEFmin = -0.2 # measured from EV, eV
106dEFmax = 0.2 # measured from EC, eV
107nEF = 101
108
109# Temperature range for mode = 'T'
110Tmin = 300 # K
111Tmax = 600
112nT = 11
113Tstep = None
114
115# Plot configuration
116view_Emin = -5.0 # eV
117view_Emax = 10.0
118view_dHmax = 5.0
119view_Nmin = 1.0e5 # cm-3
120colors = ['#000000', '#ff0000', '#00aa00', '#0000ff', '#aaaa00', '#ff00ff', '#00ffff', '#aa0000', '#00aa00', '#0000aa']
121#colors = ['k', 'r', 'g', 'b', 'y', 'm', 'c']
122fontsize = 16
123legend_fontsize = 8
124
125# integration parameters
126# integrator: 'interpolate', 'raw'
127integrator = 'interpolate'
128#integrator = 'raw'
129
130# Integration range w.r.t. kBT
131Einteg0 = -3.0
132Einteg1 = 3.0
133nrange = 6.0
134iEinteg0 = None
135iEinteg1 = None
136
137# Bisection method parameters
138# Initial search range
139#eps_bisec = 1.0e-3
140#nmaxiter_bisection = 300
141eps_bisec = 1.0e-5
142nmaxiter_bisection = 100
143
144# Newton method parmeters
145dump_newton = 0.5
146h_newton = 1.0e-8
147nmaxiter_newton = 0
148eps_newton = 1.0e-5
149
150iprintinterval = 1
151
152
153# Global variables
154Vcell = None # A^3
155E_raw = None
156dos_raw = None
157dos_norm = None
158nDOS = None
159
160E_norm = None
161Enorm_min = None
162Enorm_max = None
163Enorm_step = None
164
165Emin = None
166Emax = None
167Estep = None
168
169EV = None
170EC = None
171
172EFmin = None
173EFmax = None
174EFstep = None
175
176dHmin = None
177dHmax = None
178
179# exponent threshold
180nexp = 100.0
181
182ignore_warning = False
183
184
185app = tkApplication()
186
187
188#==================================
189# Definition of tkDefect class
190#==================================
191class tkDefect():
192 """
193 概要:
194 個々の欠陥サイトの特性を保持するクラス。
195 詳細説明:
196 欠陥の原子ラベル、サイトラベル、電荷、エントロピー、基準サイト数、
197 ドープ濃度、および複数の形成エンタルピー値を管理します。
198 """
199 def __init__(self, **args):
200 """
201 概要:
202 tkDefectクラスのコンストラクタ。
203 詳細説明:
204 欠陥の各種プロパティを初期化します。
205 引数:
206 :param args: 欠陥のプロパティを格納するキーワード引数辞書。
207 :type args: dict
208 """
209 self.labels = safe_getelement(args, "labels")
210 self.plabels = safe_getelement(args, "plabels")
211 self.names = safe_getelement(args, "names")
212
213 self.atom_label = safe_getelement(args, "atom_label")
214 self.site_label = safe_getelement(args, "site_label")
215 self.charge_label = safe_getelement(args, "charge_label")
216 self.entropy_label = safe_getelement(args, "entropy_label")
217 self.N0_label = safe_getelement(args, "N0_label")
218 self.Ndoped_label = safe_getelement(args, "Ndoped_label")
219 self.dH0_labels = safe_getelement(args, "dH0_labels")
220
221 self.atom = safe_getelement(args, "atom")
222 self._site = safe_getelement(args, "site")
223 self.name = safe_getelement(args, "name")
224 self.charge = safe_getelement(args, "charge")
225 self.entropy = safe_getelement(args, "entropy")
226 self.N0 = safe_getelement(args, "N0")
227 self.Ndoped = safe_getelement(args, "Ndoped")
228 self.dH0s = safe_getelement(args, "dH0s")
229
230 @property
231 def site(self):
232 """
233 概要:
234 欠陥サイトのプロパティを取得します。
235 戻り値:
236 :returns: 欠陥サイトの名前。
237 :rtype: str
238 """
239 return self._site
240
241class tkDefects():
242 """
243 概要:
244 複数の欠陥情報を管理するクラス。
245 詳細説明:
246 Excelファイルから欠陥形成エネルギーを読み込み、セル体積を考慮して
247 欠陥密度、キャリア密度、フェルミ準位の計算を行います。
248 また、欠陥の遷移準位の探索と保存も担当します。
249 """
250 def __init__(self, **args):
251 """
252 概要:
253 tkDefectsクラスのコンストラクタ。
254 詳細説明:
255 欠陥に関する各種リストやパスを初期化します。
256 引数:
257 :param args: 欠陥のプロパティを格納するキーワード引数辞書。
258 :type args: dict
259 """
260 self.path = None
261 self.file_version = None
262 self.V = None # in A^3
263 self.labels = safe_getelement(args, "labels")
264 self.plabels = safe_getelement(args, "plabels")
265 self.atoms = safe_getelement(args, "atoms")
266 self.sites = safe_getelement(args, "sites")
267 self.names = safe_getelement(args, "names")
268 self.defects = []
269
270 def get_names(self) -> list[str]:
271 """
272 概要:
273 登録されているユニークな欠陥名のリストを取得します。
274 戻り値:
275 :returns: ユニークな欠陥名のリスト。
276 :rtype: list
277 """
278 names = {}
279 for i in range(self.ndefects):
280 d = self.defects[i]
281 names[d.name] = 1
282 return list(names.keys())
283
284 def cal_dE(self, iPoint: int, idefect: int, T: float, EF: float) -> float:
285 """
286 概要:
287 特定の欠陥の形成エネルギー(dE)を計算します。
288 詳細説明:
289 指定されたポイント、欠陥ID、温度、フェルミ準位に基づいて、
290 欠陥の形成エンタルピーと電荷によるエネルギー項を合計します。
291 引数:
292 :param iPoint: 計算に使用する形成エンタルピーのインデックス。
293 :type iPoint: int
294 :param idefect: 欠陥リスト内の欠陥のインデックス。
295 :type idefect: int
296 :param T: 温度 (K)。
297 :type T: float
298 :param EF: フェルミ準位 (eV)。
299 :type EF: float
300 戻り値:
301 :returns: 計算された欠陥形成エネルギー (eV)。
302 :rtype: float
303 """
304 d = self.defects[idefect]
305 dE = d.dH0s[iPoint] + d.charge * EF
306# print("dE=", d.entropy, d.entropy * kB / e * T, d.dH0s[iPoint], dE)
307 return dE
308
309 '''
310 def cal_dG(self, iPoint, idefect, T, EF):
311 d = self.defects[idefect]
312 dG = d.dH0s[iPoint] + d.charge * EF - d.entropy * kB / e * T
313# print("dG=", d.entropy, d.entropy * kB / e * T, d.dH0s[iPoint], dG)
314 return dG
315 '''
316
317 def calculate_total_defect_densities(self, Tdef: float, EF: float, ECmin: float, ECmax: float, EVmin: float, EVmax: float) -> tuple[dict, list, dict, list, float]:
318 """
319 概要:
320 指定された欠陥凍結温度とフェルミ準位で、全欠陥の密度を計算します。
321 詳細説明:
322 各欠陥サイトの分配関数を考慮し、個々の欠陥の電荷状態に応じた密度と、
323 全欠陥による総電荷を算出します。
324 戻り値は各サイトの分配関数辞書、各欠陥状態の密度リスト、Tdefにおける総欠陥密度の辞書、
325 各欠陥状態の形成エネルギーリスト、全欠陥による総電荷のタプルです。
326 引数:
327 :param Tdef: 欠陥が凍結している温度 (K)。
328 :type Tdef: float
329 :param EF: フェルミ準位 (eV)。
330 :type EF: float
331 :param ECmin: 伝導帯のエネルギー下限 (eV)。
332 :type ECmin: float
333 :param ECmax: 伝導帯のエネルギー上限 (eV)。
334 :type ECmax: float
335 :param EVmin: 価電子帯のエネルギー下限 (eV)。
336 :type EVmin: float
337 :param EVmax: 価電子帯のエネルギー上限 (eV)。
338 :type EVmax: float
339 戻り値:
340 :returns: 分配関数辞書、密度リスト、総密度辞書、形成エネルギーリスト、総電荷のタプル。
341 :rtype: tuple
342 """
343# print("*calculate_total_defect_densities at Tdef = {} K:".format(Tdef))
344 kBTedef = kB * Tdef / e
345
346# Calculate distribution function
347 ZS = {}
348 for id in range(self.ndefects):
349# print("calculate_defect_densities: id=", id, self.defects[id].site)
350 d = self.defects[id]
351 ZS[d.site] = 1.0
352# exit()
353
354 for id in range(self.ndefects):
355 d = self.defects[id]
356# dH = d.dH0s[iPoint] + d.charge * EF
357 dE = self.cal_dE(iPoint, id, Tdef, EF)
358 Kdef = dE / kBTedef
359# dG = self.cal_dG(iPoint, id, Tdef, EF)
360# Kdef = dG / kBTedef
361 if Kdef > nexp:
362 Kdef = nexp
363 elif Kdef <= -nexp:
364 Kdef = -nexp
365 ZS[d.site] += exp(-Kdef)
366
367# Calculate defect densities
368 Nds = []
369 dEs = []
370# dGs = []
371 Qtot = 0.0
372 for id in range(self.ndefects):
373 d = self.defects[id]
374# dH = d.dH0s[iPoint] + d.charge * EF
375 dE = self.cal_dE(iPoint, id, Tdef, EF)
376 Ke = dE / kBTedef
377# dG = self.cal_dG(iPoint, id, Tdef, EF)
378# Ke = dG / kBTedef
379 if Ke > nexp:
380 Ke = nexp
381 elif Ke < -nexp:
382 Ke = -nexp
383 n = d.N0 / (self.V * 1.0e-24) * exp(-Ke) / ZS[d.site]
384 Nds.append(n)
385 dEs.append(dE)
386# dGs.append(dG)
387 Qtot += d.charge * n
388# print("T, K=", id, d.atom, d.site, d.charge, dH, Tdef, Kdef)
389# print(" n=", dH, n, d.charge, Qtot)
390
391 NdsTdef = {}
392 for id in range(self.ndefects):
393 d = self.defects[id]
394 label = "{}_{}".format(d.atom, d.site)
395 labelq = "{}^{}".format(label, d.charge)
396 if labelq in NdsTdef.keys(): # Original: if label in NdsTdef.keys():
397# NdsTdef[label] += Nds[id]
398 NdsTdef[labelq] += Nds[id]
399 else:
400# NdsTdef[label] = Nds[id]
401 NdsTdef[labelq] = Nds[id]
402
403 return ZS, Nds, NdsTdef, dEs, Qtot
404# return ZS, Nds, NdsTdef, dGs, Qtot
405
406 def calculate_charged_defect_densities(self, Te: float, NdsTdef: dict, EF: float, ECmin: float, ECmax: float, EVmin: float, EVmax: float) -> tuple[dict, list, dict, list, float]:
407 """
408 概要:
409 電子温度と凍結された欠陥密度を用いて、電荷を帯びた欠陥密度を計算します。
410 詳細説明:
411 まず、各サイトの全欠陥密度を合計し、電子温度での各電荷状態の分配関数を計算します。
412 その後、各電荷状態の欠陥密度と、全欠陥による総電荷を算出します。
413 戻り値は各サイトの分配関数辞書、各欠陥状態の密度リスト、Tdefにおける総欠陥密度辞書、
414 各欠陥状態の形成エネルギーリスト、全欠陥による総電荷のタプルです。
415 引数:
416 :param Te: 電子温度 (K)。
417 :type Te: float
418 :param NdsTdef: 欠陥凍結温度Tdefで計算された総欠陥密度の辞書 (cm^-3)。
419 :type NdsTdef: dict
420 :param EF: フェルミ準位 (eV)。
421 :type EF: float
422 :param ECmin: 伝導帯のエネルギー下限 (eV)。
423 :type ECmin: float
424 :param ECmax: 伝導帯のエネルギー上限 (eV)。
425 :type ECmax: float
426 :param EVmin: 価電子帯のエネルギー下限 (eV)。
427 :type EVmin: float
428 :param EVmax: 価電子帯のエネルギー上限 (eV)。
429 :type EVmax: float
430 戻り値:
431 :returns: 分配関数辞書、密度リスト、総密度辞書、形成エネルギーリスト、総電荷のタプル。
432 :rtype: tuple
433 """
434# print("*calculate_charged_defect_densities at Te = {} K:".format(Te))
435 kBTe = kB * Te / e
436
437# calculate total defect densities including all charged states
438 NdsTdef_tot = {}
439 for id in range(self.ndefects):
440 d = self.defects[id]
441 label = f"{d.atom}_{d.site}"
442 labelq = f"{d.atom}_{d.site}^{d.charge}"
443
444 if label not in NdsTdef_tot.keys():
445 NdsTdef_tot[label] = NdsTdef[labelq]
446 else:
447 NdsTdef_tot[label] += NdsTdef[labelq]
448
449 ZSA = {}
450 for label, N in NdsTdef_tot.items():
451 ZSA[label] = 0.0
452 '''
453 for id in range(self.ndefects):
454 d = self.defects[id]
455 label = "{}_{}".format(d.atom, d.site)
456 ZSA[label] = 0.0
457 '''
458
459 for id in range(self.ndefects):
460 d = self.defects[id]
461 label = "{}_{}".format(d.atom, d.site)
462
463# dH = d.dH0s[iPoint] + d.charge * EF
464 dE = self.cal_dE(iPoint, id, Te, EF)
465 Ke = dE / kBTe
466# dG = self.cal_dG(iPoint, id, Te, EF)
467# Ke = dG / kBTe
468 if Ke > nexp:
469 Ke = nexp
470 elif Ke <= -nexp:
471 Ke = -nexp
472
473 ZSA[label] += exp(-Ke)
474
475# Calculate defect densities
476 Nds = make_matrix1(self.ndefects)
477 dEs = make_matrix1(self.ndefects)
478# dGs = make_matrix1(self.ndefects)
479 Qtot = 0.0
480 for id in range(self.ndefects):
481 d = self.defects[id]
482 label = f"{d.atom}_{d.site}"
483 labelq = f"{d.atom}_{d.site}^{d.charge}"
484
485# dH = d.dH0s[iPoint] + d.charge * EF
486 dE = self.cal_dE(iPoint, id, Te, EF)
487 Ke = dE / kBTe
488# dG = self.cal_dG(iPoint, id, Te, EF)
489# Ke = dG / kBTe
490# print("k=", kBTe, dH, d.N0, d.N0 * exp(-dH / kBTedef), self.V)
491 if Ke > nexp:
492 Ke = nexp
493 elif Ke <= -nexp:
494 Ke = -nexp
495
496# n = NdsTdef[labelq] * exp(-Ke) / ZSA[label] # cm^-3
497# n = NdsTdef[label] * exp(-Ke) / ZSA[label] # cm^-3
498 n = NdsTdef_tot[label] * exp(-Ke) / ZSA[label] # cm^-3
499 Nds[id] = n
500 dEs[id] = dE
501# dGs[id] = dG
502 Qtot += d.charge * n
503
504# print("NdsTdef=", NdsTdef)
505# print("NdsTdef_tot=", NdsTdef_tot)
506# print("ZSA=", ZSA)
507# print("Nds=", Nds)
508# exit()
509
510 return ZSA, Nds, NdsTdef, dEs, Qtot
511# return ZSA, Nds, NdsTdef, dGs, Qtot
512
513 def calculate_defect_densities(self, T: float, Tdef: float, NdsTdef: dict | None, EF: float, ECmin: float, ECmax: float, EVmin: float, EVmax: float) -> tuple[dict, list, dict, list, float]:
514 """
515 概要:
516 欠陥の密度を計算します。
517 詳細説明:
518 NdsTdefがNoneの場合は、凍結されていない欠陥の総密度を計算します。
519 NdsTdefが与えられている場合は、凍結された総欠陥密度を基に、電荷を帯びた欠陥密度を計算します。
520 戻り値は分配関数辞書、密度リスト、総密度辞書、形成エネルギーリスト、総電荷のタプルです。
521 引数:
522 :param T: 電子温度 (K)。
523 :type T: float
524 :param Tdef: 欠陥が凍結している温度 (K)。
525 :type Tdef: float
526 :param NdsTdef: 欠陥凍結温度Tdefで計算された総欠陥密度の辞書。Noneの場合、未凍結として計算。
527 :type NdsTdef: dict | None
528 :param EF: フェルミ準位 (eV)。
529 :type EF: float
530 :param ECmin: 伝導帯のエネルギー下限 (eV)。
531 :type ECmin: float
532 :param ECmax: 伝導帯のエネルギー上限 (eV)。
533 :type ECmax: float
534 :param EVmin: 価電子帯のエネルギー下限 (eV)。
535 :type EVmin: float
536 :param EVmax: 価電子帯のエネルギー上限 (eV)。
537 :type EVmax: float
538 戻り値:
539 :returns: 分配関数辞書、密度リスト、総密度辞書、形成エネルギーリスト、総電荷のタプル。
540 :rtype: tuple
541 """
542 if NdsTdef is None:
543 return self.calculate_total_defect_densities(Tdef, EF, ECmin, ECmax, EVmin, EVmax)
544 else:
545 return self.calculate_charged_defect_densities(T, NdsTdef, EF, ECmin, ECmax, EVmin, EVmax)
546
547 def calculate_densities(self, T: float, Tdef: float, NdsTdef: dict | None, EF: float, ECmin: float, ECmax: float, EVmin: float, EVmax: float) -> tuple[dict, float, float, list, dict, list, float]:
548 """
549 概要:
550 電子、正孔、および欠陥のキャリア密度と総電荷を計算します。
551 詳細説明:
552 電子温度が欠陥凍結温度以下の場合、欠陥密度はTdefで凍結されます。
553 TがTdefより高い場合、欠陥密度はTで再計算されます。
554 最終的に各サイトの分配関数、電子密度、正孔密度、各欠陥密度リスト、総密度辞書、
555 各欠陥の形成エネルギーリスト、および総電荷のタプルを返します。
556 引数:
557 :param T: 電子温度 (K)。
558 :type T: float
559 :param Tdef: 欠陥が凍結している温度 (K)。
560 :type Tdef: float
561 :param NdsTdef: 欠陥凍結温度Tdefで計算された総欠陥密度の辞書。Noneの場合、未凍結として計算。
562 :type NdsTdef: dict | None
563 :param EF: フェルミ準位 (eV)。
564 :type EF: float
565 :param ECmin: 伝導帯のエネルギー下限 (eV)。
566 :type ECmin: float
567 :param ECmax: 伝導帯のエネルギー上限 (eV)。
568 :type ECmax: float
569 :param EVmin: 価電子帯のエネルギー下限 (eV)。
570 :type EVmin: float
571 :param EVmax: 価電子帯のエネルギー上限 (eV)。
572 :type EVmax: float
573 戻り値:
574 :returns: 分配関数辞書、電子密度、正孔密度、欠陥密度リスト、総密度辞書、形成エネルギーリスト、総電荷のタプル。
575 :rtype: tuple
576 """
577# If T <= Tdef, defect densities are frozen at Tdef
578# If T > Tdef, defect densities are recalculated at T
579 if T <= Tdef:
580 Z, Nds, NdsTdef, dGs, Qtot = self.calculate_defect_densities(T, Tdef, NdsTdef, EF, ECmin, ECmax, EVmin, EVmax)
581 else:
582 Z, Nds, NdsTdef, dGs, Qtot = self.calculate_defect_densities(T, T, None, EF, ECmin, ECmax, EVmin, EVmax)
583
584 ne = Ne(T, EF, ECmin, ECmax)
585 nh = Nh(T, EF, EVmin, EVmax)
586 Qtot += nh - ne
587
588 return Z, ne, nh, Nds, NdsTdef, dGs, Qtot
589
590 def dQ(self, T: float, Tdef: float, NdsTdef: dict | None, EF: float, ECmin: float, ECmax: float, EVmin: float, EVmax: float) -> float:
591 """
592 概要:
593 全電荷の中性条件からの偏差(dQ)を計算します。
594 詳細説明:
595 calculate_densitiesメソッドを呼び出して、電子、正孔、欠陥の密度から総電荷を計算し、
596 その値を返します。この関数はフェルミ準位探索のための目的関数として使用されます。
597 引数:
598 :param T: 電子温度 (K)。
599 :type T: float
600 :param Tdef: 欠陥が凍結している温度 (K)。
601 :type Tdef: float
602 :param NdsTdef: 欠陥凍結温度Tdefで計算された総欠陥密度の辞書。
603 :type NdsTdef: dict | None
604 :param EF: フェルミ準位 (eV)。
605 :type EF: float
606 :param ECmin: 伝導帯のエネルギー下限 (eV)。
607 :type ECmin: float
608 :param ECmax: 伝導帯のエネルギー上限 (eV)。
609 :type ECmax: float
610 :param EVmin: 価電子帯のエネルギー下限 (eV)。
611 :type EVmin: float
612 :param EVmax: 価電子帯のエネルギー上限 (eV)。
613 :type EVmax: float
614 戻り値:
615 :returns: 全電荷の偏差(単位電荷)。
616 :rtype: float
617 """
618 Z, ne, nh, Nds, NdsTdef, dGs, Qtot = self.calculate_densities(T, Tdef, NdsTdef, EF, ECmin, ECmax, EVmin, EVmax)
619 return Qtot
620
621 def diff(self, h: float, T: float, Tdef: float, NdsTdef: dict | None, EF: float, ECmin: float, ECmax: float, EVmin: float, EVmax: float) -> float:
622 """
623 概要:
624 全電荷の中性条件からの偏差(dQ)のフェルミ準位による数値微分を計算します。
625 詳細説明:
626 中心差分法を用いてdQ/dEFを計算します。これはニュートン法のヤコビアンとして使用されます。
627 引数:
628 :param h: 微小なEFの摂動量 (eV)。
629 :type h: float
630 :param T: 電子温度 (K)。
631 :type T: float
632 :param Tdef: 欠陥が凍結している温度 (K)。
633 :type Tdef: float
634 :param NdsTdef: 欠陥凍結温度Tdefで計算された総欠陥密度の辞書。
635 :type NdsTdef: dict | None
636 :param EF: フェルミ準位 (eV)。
637 :type EF: float
638 :param ECmin: 伝導帯のエネルギー下限 (eV)。
639 :type ECmin: float
640 :param ECmax: 伝導帯のエネルギー上限 (eV)。
641 :type ECmax: float
642 :param EVmin: 価電子帯のエネルギー下限 (eV)。
643 :type EVmin: float
644 :param EVmax: 価電子帯のエネルギー上限 (eV)。
645 :type EVmax: float
646 戻り値:
647 :returns: dQのEFによる数値微分。
648 :rtype: float
649 """
650 dQp = self.dQ(T, Tdef, NdsTdef, EF + h, ECmin, ECmax, EVmin, EVmax)
651 dQm = self.dQ(T, Tdef, NdsTdef, EF - h, ECmin, ECmax, EVmin, EVmax)
652 return (dQp - dQm) / 2.0 / h
653
654 def find_EF(self, T: float, Tdef: float, NdsTdef: dict | None, ECmin: float, ECmax: float, EVmin: float, EVmax: float,
655 callbackfunc: callable = None,
656 eps_bisec: float = 1.0e-1, nmaxiter_bisection: int = 300, initial: list[float] = [-1.0, 4.0],
657 eps_newton: float = 1.0e-6, nmaxiter_newton: int = 300, dump_newton: float = 0.3, h_newton: float = 1.0e-6,
658 is_print: bool = False) -> tuple[float | None, float | None]:
659 """
660 概要:
661 全電荷中性条件を満たすフェルミ準位を探索します。
662 詳細説明:
663 二分法とニュートン法を組み合わせて、全電荷がゼロとなるフェルミ準位を探索します。
664 最初に広い範囲で二分法を実行し、その後、より精密なニュートン法を適用します。
665 戻り値は収束したフェルミ準位と、そのときの全電荷の値のタプルです。
666 収束しない場合はNoneのタプルを返します。
667 引数:
668 :param T: 電子温度 (K)。
669 :type T: float
670 :param Tdef: 欠陥が凍結している温度 (K)。
671 :type Tdef: float
672 :param NdsTdef: 欠陥凍結温度Tdefで計算された総欠陥密度の辞書。
673 :type NdsTdef: dict | None
674 :param ECmin: 伝導帯のエネルギー下限 (eV)。
675 :type ECmin: float
676 :param ECmax: 伝導帯のエネルギー上限 (eV)。
677 :type ECmax: float
678 :param EVmin: 価電子帯のエネルギー下限 (eV)。
679 :type EVmin: float
680 :param EVmax: 価電子帯のエネルギー上限 (eV)。
681 :type EVmax: float
682 :param callbackfunc: 探索の各ステップで呼び出されるコールバック関数。
683 :type callbackfunc: callable | None
684 :param eps_bisec: 二分法の収束判定閾値。
685 :type eps_bisec: float
686 :param nmaxiter_bisection: 二分法の最大反復回数。
687 :type nmaxiter_bisection: int
688 :param initial: 二分法の初期探索範囲のリスト。
689 :type initial: list
690 :param eps_newton: ニュートン法の収束判定閾値。
691 :type eps_newton: float
692 :param nmaxiter_newton: ニュートン法の最大反復回数。
693 :type nmaxiter_newton: int
694 :param dump_newton: ニュートン法のダンプ係数。
695 :type dump_newton: float
696 :param h_newton: ニュートン法の数値微分のための微小量。
697 :type h_newton: float
698 :param is_print: 途中経過をコンソールに出力するかどうか。
699 :type is_print: bool
700 戻り値:
701 :returns: 収束したフェルミ準位と全電荷の値のタプル。
702 :rtype: tuple
703 """
704 EF = (initial[0] + initial[1]) / 2.0
705
706 if nmaxiter_bisection > 0:
707 solver = tkequation.Equation(
708 debug_explicit = 1,
709 method = 'bisection',
710 func = lambda EF: self.dQ(T, Tdef, NdsTdef, EF, ECmin, ECmax, EVmin, EVmax),
711 xa = initial[0],
712 xb = initial[1],
713 nmaxiter = nmaxiter_bisection,
714 eps = eps_bisec,
715 callback = callbackfunc,
716 isprint = 0
717 )
718 x = solver.solve()
719 if solver.iter >= 0:
720 if is_print:
721 print(" Convergence reached in bisection proc at iter = {}, x = {:10.6g}, f = {:8.4g}"
722 .format(solver.iter, solver.x, solver.f))
723 else:
724 print("")
725 print("*** Error: Convergence is not reached")
726 return None, None
727
728 EF = solver.x
729 dQh = solver.f
730
731 if nmaxiter_newton > 0:
732 solver = tkequation.Equation(
733 debug_explicit = 1,
734 method = 'newton',
735 func = lambda EF: self.dQ(T, Tdef, NdsTdef, EF, ECmin, ECmax, EVmin, EVmax),
736 diff1func = lambda EF: self.diff(h_newton, T0, Tdef, NdsTdef, EF, ECmin, ECmax, EVmin, EVmax),
737 x0 = EF,
738 dump = dump_newton,
739 nmaxiter = nmaxiter_newton,
740 eps = eps_newton,
741 callback = callbackfunc,
742 isprint = 0
743 )
744 x = solver.solve()
745 if solver.iter >= 0:
746 if is_print:
747 print(" Convergence reached in bisection proc at iter = {}, x = {:10.6g}, f = {:8.4g}"
748 .format(solver.iter, solver.x, solver.f))
749 else:
750 print("")
751 print("*** Error: Convergence is not reached")
752 return None, None
753
754 EF = solver.x
755 dQh = solver.f
756
757 EF = solver.x
758 dQh = solver.f
759
760 return EF, dQh
761
762 def find_all_transition_EF(self, idx0: int, groupdata: list[dict]) -> list[float]:
763 """
764 概要:
765 特定の欠陥状態から他のすべての欠陥状態への遷移準位を計算します。
766 詳細説明:
767 欠陥形成エネルギーの交点から、異なる電荷状態間の遷移準位を特定します。
768 電荷が同じ場合は非常に大きな値を返します。
769 引数:
770 :param idx0: 基準となる欠陥状態のインデックス。
771 :type idx0: int
772 :param groupdata: 特定の欠陥名に属する全欠陥状態のデータリスト。
773 :type groupdata: list
774 戻り値:
775 :returns: 各欠陥状態への遷移準位のリスト。
776 :rtype: list
777 """
778 print(f" find_all_transition_EF: idx0={idx0}")
779 ngroup = len(groupdata)
780 d0 = groupdata[idx0]
781 q0 = d0["charge"]
782 H0 = d0["dH0"]
783
784 EF_list = []
785 for idx1 in range(ngroup):
786 d1 = groupdata[idx1]
787 q1 = d1["charge"]
788
789 if idx1 == idx0 or q1 == q0:
790 EF_list.append(1.0e100)
791 continue
792
793 H1 = d1["dH0"]
794 EF1 = (H0 - H1) / (q1 - q0)
795 EF_list.append(EF1)
796
797 return EF_list
798
799 def find_next_transition_EF(self, idx0: int, prevEF: float, EFmin: float, EFmax: float, groupdata: list[dict]) -> tuple[int | None, float]:
800 """
801 概要:
802 現在のフェルミ準位より大きく、指定範囲内の次の遷移準位を探索します。
803 詳細説明:
804 find_all_transition_EFを使用してすべての遷移準位を計算し、
805 現在のフェルミ準位よりも大きく、かつ探索範囲内で最小の遷移準位を特定します。
806 戻り値は次の遷移準位のインデックスとフェルミ準位値のタプルです。
807 引数:
808 :param idx0: 基準となる欠陥状態のインデックス。
809 :type idx0: int
810 :param prevEF: 現在のフェルミ準位 (eV)。
811 :type prevEF: float
812 :param EFmin: フェルミ準位の最小探索値 (eV)。
813 :type EFmin: float
814 :param EFmax: フェルミ準位の最大探索値 (eV)。
815 :type EFmax: float
816 :param groupdata: 特定の欠陥名に属する全欠陥状態のデータリスト。
817 :type groupdata: list
818 戻り値:
819 :returns: 次の遷移準位のインデックスとフェルミ準位値のタプル。
820 :rtype: tuple
821 """
822 print(f" find_next_transition_EF(): idx0={idx0} prevEF={prevEF:.4f} EFmin={EFmin:.4f} EFmax={EFmax:.4f}")
823 EF_list = self.find_all_transition_EF(idx0, groupdata)
824 print(f" prevEF={prevEF} EF_list=", EF_list)
825
826 nextidx = None
827 nextEF = EFmax
828 for i, EF in enumerate(EF_list):
829 if EF < EFmin or EFmax < EF or EF <= prevEF: continue
830
831 if EF < nextEF:
832 nextidx = i
833 nextEF = EF
834
835# print(f" next: idx={nextidx} at EF={nextEF}")
836 return nextidx, nextEF
837
838 def find_order(self, groupdata: list[dict], idx: int, EF: float, dH: float) -> tuple[int, int, float]:
839 """
840 概要:
841 特定のフェルミ準位における、ある欠陥状態の形成エンタルピーの順位を計算します。
842 詳細説明:
843 同じ欠陥名に属するすべての電荷状態について形成エンタルピーを計算し、
844 入力されたペアがその中で何番目に低い形成エンタルピーを持つかを決定します。
845 戻り値は順位、最小の形成エンタルピーを持つインデックス、およびその値のタプルです。
846 引数:
847 :param groupdata: 特定の欠陥名に属する全欠陥状態のデータリスト。
848 :type groupdata: list
849 :param idx: 現在の欠陥状態のインデックス。
850 :type idx: int
851 :param EF: フェルミ準位 (eV)。
852 :type EF: float
853 :param dH: 現在の欠陥状態の形成エンタルピー (eV)。
854 :type dH: float
855 戻り値:
856 :returns: 順位、最小エンタルピーのインデックス、最小エンタルピー値のタプル。
857 :rtype: tuple
858 """
859 print(f" find_oder() for idx={idx}, EF={EF}, dH={dH}:")
860 ndata = len(groupdata)
861 dH2 = []
862 q_list = []
863 for id2 in range(ndata):
864 d2 = groupdata[id2]
865 q2 = d2["charge"]
866 H2 = d2["dH0"]
867 dH2.append(H2 + q2 * EF)
868 q_list.append(q2)
869# print(f"find_order: id2={id2}: {d2['name']} q={d2['charge']} dH={d2['dH0']}")
870# print(" id2={:2}: EF1={:8.3g} dH1={:8.3g} dH2={:8.3g}".format(id2, EF1, dH1, dH2[id2]))
871
872 print(" dH2=", [float(f"{v:8.6g}") for v in dH2], " for q=", q_list)
873 iorder = 0
874 for id2 in range(ndata):
875# print("order:", iorder, dH2[id2], dH)
876 if dH2[id2] < dH - 1.0e-8:
877 print(f" current: idx={idx} ({EF:.6f}, {dH:.6g}): for id2={id2}: dH2[id2]={dH2[id2]:.6g} < dH={dH:.6g}")
878 iorder += 1
879
880 idxmin = idx
881 dHmin = dH
882 for id2 in range(ndata):
883 if dH2[id2] < dHmin:
884 dHmin = dH2[id2]
885 idxmin = id2
886
887 print(f" *found dHmin={dHmin:8.6g} at EF={EF}: for idxmin={idxmin:2}, iorder={iorder:2}")
888
889# iorder: (EF, dH)が同じグループで何番目か
890# idxmin, dHmin: 同じグループでEFにおいて最小のdHをもつidx,dH
891 return iorder, idxmin, dHmin
892
893
894 def find_all_transitions(self, name: str, groupdata: list[dict], EFmin: float, EFmax: float) -> tuple[list[list], list[list]]:
895 """
896 概要:
897 特定の欠陥名に属するすべての遷移点と最小形成エンタルピー曲線上の点を探索します。
898 詳細説明:
899 フェルミ準位をEFminからEFmaxまで変化させながら、各電荷状態の形成エンタルピーを計算し、
900 形成エンタルピーが最小となる電荷状態が変化する遷移準位を特定します。
901 戻り値はすべての遷移点のリストと、最小形成エンタルピー曲線上の主要な点のリストのタプルです。
902 引数:
903 :param name: 欠陥名。
904 :type name: str
905 :param groupdata: 特定の欠陥名に属する全欠陥状態のデータリスト。
906 :type groupdata: list
907 :param EFmin: フェルミ準位の最小探索値 (eV)。
908 :type EFmin: float
909 :param EFmax: フェルミ準位の最大探索値 (eV)。
910 :type EFmax: float
911 戻り値:
912 :returns: すべての遷移点のリストと最小形成エンタルピー曲線上の点のリストのタプル。
913 :rtype: tuple
914 """
915 print(" find_all_transitions():")
916 eps = 1.0e-6
917 ngroupdata = len(groupdata)
918 allpts = []
919 minpts = []
920
921 idxmin = 0
922 d0 = groupdata[idxmin]
923 q0 = d0["charge"]
924 H0 = d0["dH0"]
925
926 print()
927 print(f"defect name: {d0['name']}")
928
929 iorder, idxmin, dHmin = self.find_order(groupdata, idxmin, EF = EFmin, dH = H0 + q0 * EFmin)
930 allpts.append([EFmin, dHmin, q0, groupdata[idxmin]["charge"], 0, idxmin])
931 minpts.append([EFmin, dHmin, q0, groupdata[idxmin]["charge"], 0, idxmin])
932 print(f" ** first point #1: (EF, dH)=({EFmin:.6f}, {dHmin:.6g}) q={q0} idx=0")
933 print()
934
935 prevEF = EFmin
936 prevq = None
937 count = 2
938 while 1:
939 nextidx, nextEF = self.find_next_transition_EF(idxmin, prevEF, EFmin, EFmax, groupdata)
940 if nextidx is None: break
941 print(f" nextidx={nextidx} EF={nextEF:.6g}")
942
943 d0 = groupdata[idxmin]
944 q0 = d0["charge"]
945 H0 = d0["dH0"]
946 dH1 = H0 + q0 * nextEF
947 d1 = groupdata[nextidx]
948 q1 = d1['charge']
949 iorder, idxmin, dHmin = self.find_order(groupdata, idxmin, nextEF, dH1)
950 print(f" ** next point #{count}: iorder={iorder} (EF, dH)=({nextEF:.6f}, {dHmin:.6g}) idx={nextidx} next_q={q1}")
951 print()
952
953 allpts.append([nextEF, dH1, q0, q1, iorder, idxmin])
954# if prevq is None or (prevq > q0):
955 if iorder == 0:
956 minpts.append([nextEF, dH1, q0, q1, iorder, idxmin])
957
958 prevEF = nextEF
959 prevq = q0
960 count += 1
961
962 # 最後のEFmax点での最小dHを計算し追加
963 # ここでは、最後に決定されたidxmin (EFmaxにおける最小dHの電荷状態) を使用
964 if ngroupdata > 0:
965 last_idx = idxmin # 最後の遷移点での最小dHを持つidx
966 last_d = groupdata[last_idx]
967 last_q = last_d["charge"]
968 last_H = last_d["dH0"]
969 dH_at_EFmax = last_H + last_q * EFmax
970 iorder_at_EFmax, idxmin_at_EFmax, dHmin_at_EFmax = self.find_order(groupdata, last_idx, EFmax, dH_at_EFmax)
971 else:
972 dH_at_EFmax = 1.0e100 # 欠陥データがない場合は大きな値を設定
973 iorder_at_EFmax, idxmin_at_EFmax, dHmin_at_EFmax = 0, 0, 1.0e100 # デフォルト値を設定
974
975 allpts.append([EFmax, dHmin_at_EFmax, last_q, groupdata[idxmin_at_EFmax]["charge"], iorder_at_EFmax, idxmin_at_EFmax])
976 minpts.append([EFmax, dHmin_at_EFmax, last_q, groupdata[idxmin_at_EFmax]["charge"], iorder_at_EFmax, idxmin_at_EFmax])
977
978# if name == 'O_Ge1':
979# if name == 'V_O1':
980# print("allpts=", allpts)
981# print("minpts=", minpts)
982# exit()
983
984 return allpts, minpts
985
986 def get_transitionlevel_lists(self) -> tuple[list[str], list[list[list]], list[list[list]]]:
987 """
988 概要:
989 全てのユニークな欠陥名に対して、遷移準位のリストを生成します。
990 詳細説明:
991 各欠陥名について遷移準位を計算し、すべての形成エンタルピー曲線上の点と、
992 最小曲線上の点を取得します。戻り値は欠陥名のリスト、全遷移点のリストのリスト、
993 および最小曲線上の点のリストのリストのタプルです。
994 戻り値:
995 :returns: 欠陥名リスト、全遷移点リスト、最小曲線上の点リストのタプル。
996 :rtype: tuple
997 """
998 names = self.get_names()
999 allpts_list = []
1000 minpts_list = []
1001 for iname in range(len(names)):
1002 name = names[iname]
1003 groupdata = self.get_groupdata(name, iPoint)
1004 allpts, minpts = self.find_all_transitions(name, groupdata, EFmin, EFmax)
1005 allpts_list.append(allpts)
1006 minpts_list.append(minpts)
1007# exit()
1008
1009 return names, allpts_list, minpts_list
1010
1011 def save_allpts(self, path: str) -> int | None:
1012 """
1013 概要:
1014 全ての欠陥状態の形成エンタルピーとフェルミ準位の関係をExcelファイルに保存します。
1015 詳細説明:
1016 各欠陥の電荷状態について、フェルミ準位の範囲における形成エンタルピーの線形関係を計算し、
1017 Excelシートに書き出します。
1018 引数:
1019 :param path: 出力Excelファイルのパス。
1020 :type path: str
1021 戻り値:
1022 :returns: ファイル書き込みが成功すれば1、失敗すればNone。
1023 :rtype: int | None
1024 """
1025 allwb = tkExcel(path, 'w')
1026 if not allwb:
1027 return None
1028
1029 for id in range(self.ndefects):
1030 d = self.defects[id]
1031 name = d.name
1032 q = d.charge
1033 allwb.Print([name, q])
1034 allwb.Print(["EF(eV)", "dH(eV)"])
1035 allwb.Print([EFmin, d.dH0s[iPoint] + q * EFmin])
1036 allwb.Print([EFmax, d.dH0s[iPoint] + q * EFmax])
1037
1038 allwb.Close()
1039 return 1
1040
1041 def save_minpts(self, path: str) -> int | None:
1042 """
1043 概要:
1044 各欠陥タイプにおける最小形成エンタルピー曲線上の点をExcelファイルに保存します。
1045 詳細説明:
1046 フェルミ準位に対して形成エンタルピーが最小となる曲線上の遷移準位点とその情報を
1047 Excelシートに書き出します。
1048 引数:
1049 :param path: 出力Excelファイルのパス。
1050 :type path: str
1051 戻り値:
1052 :returns: ファイル書き込みが成功すれば1、失敗すればNone。
1053 :rtype: int | None
1054 """
1055 minwb = tkExcel(path, 'w')
1056 if not minwb:
1057 return None
1058
1059 names = self.get_names()
1060 for iname in range(len(names)):
1061 name = names[iname]
1062 groupdata = self.get_groupdata(name, iPoint)
1063 allpts, minpts = self.find_all_transitions(name, groupdata, EFmin, EFmax)
1064
1065 minwb.Print([name])
1066 minwb.Print(["EF(eV)", "dH(eV)", "q(-)", "q(+)"])
1067
1068 for ipt in range(len(minpts)):
1069 minwb.Print([minpts[ipt][0], minpts[ipt][1], minpts[ipt][2], minpts[ipt][3]])
1070
1071 minwb.Close()
1072 return 1
1073
1074 def get_groupdata(self, name: str, iPoint: int) -> list[dict]:
1075 """
1076 概要:
1077 指定された欠陥名に属する全電荷状態のデータを取得します。
1078 詳細説明:
1079 指定された名前と一致する欠陥を抽出し、それらの情報を辞書形式でリストにまとめます。
1080 リストは電荷の降順にソートされます。
1081 引数:
1082 :param name: 抽出する欠陥名。
1083 :type name: str
1084 :param iPoint: 使用する形成エンタルピーのインデックス。
1085 :type iPoint: int
1086 戻り値:
1087 :returns: 抽出された欠陥データのリスト。
1088 :rtype: list
1089 """
1090 groupdata = []
1091 for i in range(self.ndefects):
1092 d = self.defects[i]
1093 if name != d.name: continue
1094
1095 p = {
1096 'defects': d,
1097 'atom' : d.atom,
1098 'site' : d.site,
1099 'name' : d.name,
1100 'charge' : d.charge,
1101 'dH0' : d.dH0s[iPoint]
1102 }
1103 groupdata.append(p)
1104 groupdata.sort(key = lambda x: -x["charge"])
1105 return groupdata
1106
1107 def SetVolume(self, V: float) -> float:
1108 """
1109 概要:
1110 ユニットセルの体積を設定します。
1111 引数:
1112 :param V: ユニットセルの体積 (A^3)。
1113 :type V: float
1114 戻り値:
1115 :returns: 設定されたユニットセルの体積 (A^3)。
1116 :rtype: float
1117 """
1118 self.V = V
1119 return self.V
1120
1121 def Volume(self) -> float | None:
1122 """
1123 概要:
1124 ユニットセルの体積を取得します。
1125 戻り値:
1126 :returns: 設定されたユニットセルの体積 (A^3)。
1127 :rtype: float | None
1128 """
1129 return self.V
1130
1131 def add(self, defect: 'tkDefect'):
1132 """
1133 概要:
1134 欠陥オブジェクトをリストに追加します。
1135 引数:
1136 :param defect: 追加するtkDefectオブジェクト。
1137 :type defect: tkDefect
1138 """
1139 self.defects.append(defect)
1140
1141 def get(self, idx: int):
1142 """
1143 概要:
1144 指定されたインデックスの欠陥オブジェクトを取得します。
1145 引数:
1146 :param idx: 取得する欠陥のインデックス。
1147 :type idx: int
1148 戻り値:
1149 :returns: 指定されたインデックスのtkDefectオブジェクト。
1150 :rtype: tkDefect | None
1151 """
1152 try:
1153 return self.defects[idx]
1154 except:
1155 print("")
1156 print("Warning in tkDefects.get: No defect for index=", idx)
1157 print("")
1158 return None
1159
1160 def Print(self, PrintLabels: bool = False, outfp: object = None):
1161 """
1162 概要:
1163 登録されている欠陥情報を標準出力または指定されたファイルポインタに出力します。
1164 詳細説明:
1165 欠陥の数、ポイント数、セル体積、および各欠陥の詳細情報を整形して表示します。
1166 引数:
1167 :param PrintLabels: ラベル情報を出力するかどうか。
1168 :type PrintLabels: bool
1169 :param outfp: 出力先のファイルポインタ。
1170 :type outfp: object | None
1171 """
1172 mprint("# of defect points: ", self.npoints, fp = outfp)
1173 mprint("# of defects : ", self.ndefects, fp = outfp)
1174 if self.V:
1175 mprint("Unit cell volume: {:10.6g} A^3".format(self.V))
1176 if PrintLabels:
1177 mprint("labels : ", self.labels, fp = outfp)
1178 mprint("plabels: ", self.plabels, fp = outfp)
1179 mprint("names : ", self.names, fp = outfp)
1180 mprint(" {:>6} ({:>2} {:>4}) {:>5} {:^6} {:^8} {:^12} ".format("Defect", "at", "site", "q", "dS/kB", "N0(sites)", "Ndoped(cm^-3)"), end = '', fp = outfp)
1181 for j in range(self.npoints):
1182 mprint(" {:^10}".format("dH0(eV) @ {}".format(self.plabels[j])), end = '', fp = outfp)
1183 mprint("", fp = outfp)
1184
1185 for i in range(self.ndefects):
1186 d = self.get(i)
1187 mprint(f" {d.name:6} ({d.atom:2} {d.site:4}): {d.charge:5} {d.entropy:6.2g} {d.N0:8.4g} {d.Ndoped:12.4g} ", end = '', fp = outfp)
1188 for j in range(self.npoints):
1189 mprint(" {:10.6g}".format(d.dH0s[j]), end = '', fp = outfp)
1190 mprint("", fp = outfp)
1191
1192 def read_excel(self, infile: str, V: float | None) -> 'tkDefects':
1193 """
1194 概要:
1195 欠陥形成エンタルピーを含むExcelファイルを読み込みます。
1196 詳細説明:
1197 指定されたExcelファイルから欠陥に関する情報を抽出し、ファイル形式のバージョンを
1198 自動判別して適切にデータをパースします。セル体積Vが与えられた場合はそれを設定します。
1199 引数:
1200 :param infile: 欠陥形成エンタルピーを含むExcelファイルのパス。
1201 :type infile: str
1202 :param V: ユニットセルの体積 (A^3)。
1203 :type V: float | None
1204 戻り値:
1205 :returns: 読み込まれたデータで構築されたtkDefectsオブジェクト自身。
1206 :rtype: tkDefects
1207 """
1208 self.path = infile
1209
1210 if V:
1211 self.V = V
1212
1213 try:
1214 wb = tkExcel(infile, 'r')
1215 except:
1216 app.terminate("Error to read [{}]".format(infile), pause = True)
1217
1218 version = wb.Get(0, 0)
1219# print("version:", version)
1220
1221 if 'Version' in version:
1222 line0 = 0
1223 col0 = 1
1224 nlabels0 = 6
1225 else:
1226 line0 = 0
1227 col0 = 0
1228 nlabels0 = 5
1229
1230 labels = []
1231 idx = col0
1232 while 1:
1233 v = wb.Get(line0, idx)
1234 if v is None:
1235 break
1236
1237 labels.append(v)
1238 idx += 1
1239
1240 nlabels = len(labels)
1241
1242 values = []
1243 for i in range(nlabels):
1244 values.append([])
1245
1246# print("")
1247 iline = line0 + 1
1248 is_last = False
1249 while 1:
1250 for irow in range(nlabels):
1251 v = wb.Get(iline, irow + col0)
1252# print("iline=", iline, v)
1253 if v is None or v == '':
1254 is_last = True
1255 break
1256
1257 if irow <= 1 + col0:
1258 values[irow].append(v)
1259 else:
1260 values[irow].append(pfloat(v))
1261
1262 if is_last:
1263 break
1264 iline += 1
1265
1266 wb.Close()
1267
1268# print("Labels:", labels)
1269# print("values=", values)
1270
1271 ndefects = len(values[0])
1272 npoints = nlabels - nlabels0
1273 plabels = labels[nlabels0:]
1274 labels = labels[0:nlabels0]
1275
1276 atoms = values[0]
1277 sites = values[1]
1278 charges = values[2]
1279 if 'Version' in version:
1280 entropies = values[3]
1281 N0s = values[4]
1282 Nds = values[5]
1283 pvalues = values[6:]
1284 else:
1285 entropies = make_matrix1(ndefects, 0.0)
1286 N0s = values[3]
1287 Nds = values[4]
1288 pvalues = values[5:]
1289
1290 defects = self
1291 defects.file_version = version
1292 defects.ndefects = ndefects
1293 defects.npoints = npoints
1294 defects.labels = labels
1295 defects.plabels = plabels
1296 defects.atoms = atoms
1297 defects.sites = sites
1298 defects.names = []
1299 print("atoms=", defects.atoms)
1300 print("sites=", defects.sites)
1301 for i in range(defects.ndefects):
1302 defects.names.append(defects.atoms[i] + '_' + defects.sites[i])
1303 for i in range(defects.ndefects):
1304 d = tkDefect()
1305 d.atom_label = labels[0]
1306 d.site_label = labels[1]
1307 d.charge_label = labels[2]
1308 if 'Version' in version:
1309 d.entropy_label = labels[3]
1310 d.N0_label = labels[4]
1311 d.Ndoped_label = labels[5]
1312 else:
1313 d.entropy_label = 'S/kB'
1314 d.N0_label = labels[3]
1315 d.Ndoped_label = labels[4]
1316 d.dH0_labels = plabels
1317 d.atom = defects.atoms[i]
1318 d._site = defects.sites[i]
1319 d.name = defects.names[i]
1320 d.charge = charges[i]
1321 d.entropy = entropies[i]
1322 d.N0 = N0s[i]
1323# if self.V:
1324# d.N0 = N0s[i] / (self.V * 1.0e-24)
1325# else:
1326# d.N0 = N0s[i]
1327 d.Ndoped = Nds[i]
1328 d.dH0s = []
1329 for j in range(npoints):
1330 d.dH0s.append(pvalues[j][i])
1331 defects.add(d)
1332
1333 return defects
1334
1335
1336#=============================
1337# Treat argments
1338#=============================
1339def usage(app: 'tkApplication' = None):
1340 """
1341 概要:
1342 スクリプトのコマンドライン引数と使用方法を表示します。
1343 詳細説明:
1344 プログラムの実行に必要な引数とその例を示します。
1345 特にモードに応じた引数の指定方法を説明します。
1346 引数:
1347 :param app: tkApplicationのインスタンス。
1348 :type app: tkApplication | None
1349 """
1350 global CAR_path
1351
1352 if CAR_path == '':
1353 CAR_path = '.'
1354
1355 print("")
1356 print("Usage: Variables in () are optional")
1357 print(" Input files: INCAR, POSCAR, POTCAR, OUTCAR, DOSCAR, EIGENVAL of perfect crystal model")
1358 print(" Excel (.xlsx) file of defect formation energies")
1359 print(" python {} mode (other args)".format(sys.argv[0], ))
1360 print(" NOTE: If T(electron) <= T(defect) defect densities are frozen at T(defect)")
1361 print(" If T(electron) > T(defect) defect densities are calculated at T(electron)")
1362 print(" (i) python {} EF [min|all] dH_path CAR_path CAR_path(defect) iPoint T(electron) T(defect) EF(defect) EFmin EFmax nEF".format(sys.argv[0]))
1363 print(" ex: python {} EF min {} {} {} {} {} {} {} {} {} {}"
1364 .format(sys.argv[0], dH_path, CAR_path, CAR_path_defect, iPoint, T0, Tdef, EFdef, EFmin, EFmax, nEF))
1365 print(" ex: python {} EF all {} {} {} {} {} {} {} {} {} {}"
1366 .format(sys.argv[0], dH_path, CAR_path, CAR_path_defect, iPoint, T0, Tdef, EFdef, EFmin, EFmax, nEF))
1367 print(" (ii) python {} T dH_path, CAR_path iPoint T(defect) Tmin Tmax nT".format(sys.argv[0]))
1368 print(" ex: python {} T {} {} {} {} {} {} {} {} {}"
1369 .format(sys.argv[0], dH_path, CAR_path, CAR_path_defect, iPoint, Tdef, EFdef, Tmin, Tmax, nT))
1370
1371def updatevars():
1372 """
1373 概要:
1374 コマンドライン引数からグローバル変数を更新します。
1375 詳細説明:
1376 sys.argvを解析し、実行モードに応じて欠陥データパス、VASP計算パス、
1377 温度範囲、フェルミ準位範囲などのグローバル設定値を更新します。
1378 """
1379 global mode, plot_mode
1380 global dH_path, CAR_path, Vcell, CAR_path_defect
1381 global nDOS, Emin, Emax, Estep
1382 global T0, Tdef, EFdef, EF0
1383 global EFmin, EFmax, nEF, EFstep, dHmin, dHmax
1384 global Tmin, Tmax, nT, Tstep
1385 global iPoint
1386 global ignore_warning
1387
1388 argv = sys.argv
1389 if len(argv) == 1:
1390 usage()
1391 exit()
1392
1393 mode = getarg( 1, mode)
1394 if mode == 'EF':
1395 plot_mode = getarg( 2, plot_mode)
1396 dH_path = getarg( 3, dH_path)
1397 CAR_path = getarg( 4, CAR_path)
1398 CAR_path_defect = getarg( 5, CAR_path_defect)
1399 iPoint = getintarg ( 6, iPoint)
1400 T0 = getfloatarg( 7, T0)
1401 Tdef = getfloatarg( 8, Tdef)
1402 EFdef = getarg ( 9, EFdef)
1403 EFmin = getarg(10, EFmin)
1404 EFmax = getarg(11, EFmax)
1405 EFmin = pfloat(EFmin, None)
1406 EFmax = pfloat(EFmax, None)
1407 nEF = getintarg (12, nEF)
1408 dHmin = getarg(13, dHmin)
1409 dHmax = getarg(14, dHmax)
1410 dHmin = pfloat(dHmin, None)
1411 dHmax = pfloat(dHmax, None)
1412 ignore_warning = getintarg(15, ignore_warning)
1413
1414# EFstep = (EFmax - EFmin) / (nEF - 1)
1415 if mode == 'T':
1416 dH_path = getarg ( 2, dH_path)
1417 CAR_path = getarg ( 3, CAR_path)
1418 CAR_path_defect = getarg ( 4, CAR_path_defect)
1419 iPoint = getintarg ( 5, iPoint)
1420 Tdef = getfloatarg( 6, Tdef)
1421 EFdef = getarg ( 7, EFdef)
1422 Tmin = getfloatarg( 8, Tmin)
1423 Tmax = getfloatarg( 9, Tmax)
1424 nT = getintarg (10, nT)
1425
1426 ignore_warning = getarg(11, ignore_warning,)
1427
1428 Tstep = (Tmax - Tmin) / (nT - 1)
1429
1430#=============================
1431# other functions
1432#=============================
1433def savecsv(outfile: str, header: list[str], datalist: list[list[float]]):
1434 """
1435 概要:
1436 データリストをCSVファイルに保存します。
1437 詳細説明:
1438 ヘッダー行とそれに続くデータ行をCSV形式で指定されたファイルに書き込みます。
1439 データは行ごとに整形されて出力されます。
1440 引数:
1441 :param outfile: 出力CSVファイルのパス。
1442 :type outfile: str
1443 :param header: CSVファイルのヘッダー行のリスト。
1444 :type header: list
1445 :param datalist: 書き込むデータ。
1446 :type datalist: list
1447 """
1448 try:
1449 print("Write to [{}]".format(outfile))
1450 f = open(outfile, 'w')
1451 except:
1452# except IOError:
1453 print("Error: Can not write to [{}]".format(outfile))
1454 else:
1455 fout = csv.writer(f, lineterminator='\n')
1456 fout.writerow(header)
1457# fout.writerows(data)
1458 for i in range(0, len(datalist[0])):
1459 a = []
1460 for j in range(len(datalist)):
1461 a.append(datalist[j][i])
1462 fout.writerow(a)
1463 f.close()
1464
1465def read_csv(infile: str, xmin: float | None = None, xmax: float | None = None, delimiter: str = ',') -> tuple[list[str], list[float], list[float]]:
1466 """
1467 概要:
1468 CSVファイルを読み込み、指定された範囲のデータを抽出します。
1469 詳細説明:
1470 最初の列をx軸データ、2番目の列をy軸データとして読み込みます。
1471 xminとxmaxが指定されている場合、x軸データがその範囲内の行のみを抽出します。
1472 戻り値はヘッダー行のリスト、x軸データのリスト、y軸データのリストのタプルです。
1473 引数:
1474 :param infile: 入力CSVファイルのパス。
1475 :type infile: str
1476 :param xmin: x軸データの最小値。
1477 :type xmin: float | None
1478 :param xmax: x軸データの最大値。
1479 :type xmax: float | None
1480 :param delimiter: CSVファイルの区切り文字。
1481 :type delimiter: str
1482 戻り値:
1483 :returns: ヘッダーリストと抽出されたx軸データリスト、y軸データリストのタプル。
1484 :rtype: tuple
1485 """
1486 print("xrange=", xmin, xmax)
1487 data = []
1488 try:
1489 infp = open(infile, "r")
1490 f = csv.reader(infp, delimiter = delimiter)
1491 header = next(f)
1492 print("header=", header)
1493 for j in range(len(header)):
1494 data.append([])
1495
1496 for row in f:
1497 x = pfloat(row[0])
1498 if xmin is not None and xmin <= x <= xmax:
1499 y = pfloat(row[1])
1500 data[0].append(x)
1501 data[1].append(y)
1502 except:
1503 print("Error: Can not read [{}]".format(infile))
1504 exit()
1505 return header, data[0], data[1]
1506
1507# define the DOS function
1508fdos = None
1509def DOS(E: float) -> float:
1510 """
1511 概要:
1512 補間関数を使用して、指定されたエネルギーにおける状態密度を返します。
1513 詳細説明:
1514 状態密度は予めVASPのDOSCARファイルから読み込まれ、fdosとして補間関数が設定されています。
1515 入力エネルギーが定義範囲外の場合、エラーメッセージを出力してプログラムを終了します。
1516 引数:
1517 :param E: 状態密度を評価するエネルギー (eV)。
1518 :type E: float
1519 戻り値:
1520 :returns: 指定されたエネルギーにおける状態密度 (states/cm^3)。
1521 :rtype: float
1522 """
1523 global E_norm, dos_raw
1524 global Enorm_min, Enorm_max, Enorm_step, nDOS
1525 global fdos
1526
1527 if E < Enorm_min or Enorm_max < E:
1528 app.terminate("Error in DOS(E): Given E={} exceeds the DOS E range [{}, {}]".format(E, Enorm_min, Enorm_max), usage = usage, pause = True)
1529 exit()
1530
1531 return fdos(E)
1532
1533# Fermi-Dirac distribution function
1534def fe(E: float, T: float, EF: float) -> float:
1535 """
1536 概要:
1537 フェルミ・ディラック分布関数を計算します(電子占有確率)。
1538 詳細説明:
1539 指定されたエネルギー、温度、フェルミ準位において、
1540 電子がそのエネルギー準位を占有する確率を計算します。
1541 引数:
1542 :param E: エネルギー (eV)。
1543 :type E: float
1544 :param T: 温度 (K)。
1545 :type T: float
1546 :param EF: フェルミ準位 (eV)。
1547 :type EF: float
1548 戻り値:
1549 :returns: 電子占有確率。
1550 :rtype: float
1551 """
1552 global e, kB
1553
1554 if T == 0.0:
1555 if E < EF:
1556 return 1.0
1557 elif E == EF:
1558 return 0.5
1559 else:
1560 return 0.0
1561
1562 k = (E - EF) * e / kB / T
1563 if k > nexp:
1564 k = nexp
1565 if k < -nexp:
1566 k = -nexp
1567 return 1.0 / (exp(k) + 1.0)
1568
1569def fh(E: float, T: float, EF: float) -> float:
1570 """
1571 概要:
1572 フェルミ・ディラック分布関数に基づいて正孔の占有確率を計算します。
1573 詳細説明:
1574 電子占有確率を用いて、正孔の占有確率を計算します。
1575 引数:
1576 :param E: エネルギー (eV)。
1577 :type E: float
1578 :param T: 温度 (K)。
1579 :type T: float
1580 :param EF: フェルミ準位 (eV)。
1581 :type EF: float
1582 戻り値:
1583 :returns: 正孔占有確率。
1584 :rtype: float
1585 """
1586 return 1.0 - fe(E, T, EF)
1587
1588# Define the function to be integrated
1589def DOSfe(E: float, T: float, EF: float) -> float:
1590 """
1591 概要:
1592 状態密度と電子のフェルミ・ディラック分布関数の積を計算します。
1593 詳細説明:
1594 これは電子密度を計算するための積分の被積分関数となります。
1595 引数:
1596 :param E: エネルギー (eV)。
1597 :type E: float
1598 :param T: 温度 (K)。
1599 :type T: float
1600 :param EF: フェルミ準位 (eV)。
1601 :type EF: float
1602 戻り値:
1603 :returns: 状態密度と電子占有確率の積の値。
1604 :rtype: float
1605 """
1606 return DOS(E) * fe(E, T, EF)
1607
1608def DOSfh(E: float, T: float, EF: float) -> float:
1609 """
1610 概要:
1611 状態密度と正孔のフェルミ・ディラック分布関数の積を計算します。
1612 詳細説明:
1613 これは正孔密度を計算するための積分の被積分関数となります。
1614 引数:
1615 :param E: エネルギー (eV)。
1616 :type E: float
1617 :param T: 温度 (K)。
1618 :type T: float
1619 :param EF: フェルミ準位 (eV)。
1620 :type EF: float
1621 戻り値:
1622 :returns: 状態密度と正孔占有確率の積の値。
1623 :rtype: float
1624 """
1625 return DOS(E) * fh(E, T, EF)
1626
1627def integrate_trapezoid(func: callable, E0: float, E1: float, h: float) -> float:
1628 """
1629 概要:
1630 関数を台形則で数値積分します。
1631 詳細説明:
1632 指定された関数funcを積分範囲[E0, E1]で刻み幅hを用いて台形則で数値積分します。
1633 引数:
1634 :param func: 積分する関数。引数としてエネルギーを取り、値を返します。
1635 :type func: callable
1636 :param E0: 積分範囲の開始エネルギー (eV)。
1637 :type E0: float
1638 :param E1: 積分範囲の終了エネルギー (eV)。
1639 :type E1: float
1640 :param h: 積分刻み幅 (eV)。
1641 :type h: float
1642 戻り値:
1643 :returns: 計算された積分の値。
1644 :rtype: float
1645 """
1646 n = int((E1 - E0) / h + 1.000001)
1647 y = [func(E0 + i * h) for i in range(n+1)]
1648
1649 S = 0.5 * (y[0] + y[n]) + sum(y[1:n])
1650 return h * S
1651
1652
1653def integrate_trapezoid(func: callable, E0: float, E1: float, h: float) -> float:
1654 """
1655 概要:
1656 関数を台形則で数値積分します(再定義版)。
1657 詳細説明:
1658 指定された関数funcを積分範囲[E0, E1]で刻み幅hを用いて台形則で数値積分します。
1659 この関数は上の同名関数を上書きします。積分刻み幅は再計算されます。
1660 引数:
1661 :param func: 積分する関数。引数としてエネルギーを取り、値を返します。
1662 :type func: callable
1663 :param E0: 積分範囲の開始エネルギー (eV)。
1664 :type E0: float
1665 :param E1: 積分範囲の終了エネルギー (eV)。
1666 :type E1: float
1667 :param h: 積分刻み幅 (eV)。
1668 :type h: float
1669 戻り値:
1670 :returns: 計算された積分の値。
1671 :rtype: float
1672 """
1673 n = int((E1 - E0) / h + 1.000001)
1674 h = (E1 - E0) / n
1675 y = [func(E0 + i * h) for i in range(n+1)]
1676
1677 S = 0.5 * (y[0] + y[n]) + sum(y[1:n])
1678 return h * S
1679
1680def integrate_trapezoid_by_list(y: list[float], h: float) -> float:
1681 """
1682 概要:
1683 データ点のリストを台形則で数値積分します。
1684 詳細説明:
1685 予め計算された関数値のリストyと刻み幅hを用いて、台形則で数値積分を行います。
1686 引数:
1687 :param y: 積分する関数値のリスト。
1688 :type y: list
1689 :param h: データ間の刻み幅。
1690 :type h: float
1691 戻り値:
1692 :returns: 計算された積分の値。
1693 :rtype: float
1694 """
1695 n = len(y)
1696 S = 0.5 * (y[0] + y[n-1]) + sum(y[1:n-1])
1697 return h * S
1698
1699def convert_range2index(E0: float, E1: float) -> tuple[int, int]:
1700 """
1701 概要:
1702 エネルギー範囲を状態密度データ配列のインデックス範囲に変換します。
1703 詳細説明:
1704 与えられたエネルギーに対応する状態密度データ配列の開始・終了インデックスを計算します。
1705 引数:
1706 :param E0: エネルギー範囲の開始値 (eV)。
1707 :type E0: float
1708 :param E1: エネルギー範囲の終了値 (eV)。
1709 :type E1: float
1710 戻り値:
1711 :returns: 開始インデックスと終了インデックスのタプル。
1712 :rtype: tuple
1713 """
1714 global Eraw_step, Eraw_min, Eraw_max
1715
1716 i0 = (int)((E0 - Enorm_min) / Enorm_step)
1717 i1 = (int)((E1 - Enorm_min) / Enorm_step + 0.99999)
1718# print("E0, E1, i0, i1 = ", E0, E1, i0, i1)
1719# exit()
1720
1721 return i0, i1
1722
1723def Ne(T: float, EF: float, E0: float, E1: float) -> float:
1724 """
1725 概要:
1726 指定されたエネルギー範囲における電子密度を計算します。
1727 詳細説明:
1728 温度とフェルミ準位における電子のDOSfe関数を積分範囲で数値積分することにより
1729 電子密度を計算します。
1730 引数:
1731 :param T: 温度 (K)。
1732 :type T: float
1733 :param EF: フェルミ準位 (eV)。
1734 :type EF: float
1735 :param E0: 積分範囲の開始エネルギー (eV)。
1736 :type E0: float
1737 :param E1: 積分範囲の終了エネルギー (eV)。
1738 :type E1: float
1739 戻り値:
1740 :returns: 計算された電子密度 (cm^-3)。
1741 :rtype: float
1742 """
1743 global Enorm_step, Enorm_min, Enorm_max
1744 global integrator
1745
1746 if integrator == 'interpolate':
1747 N = integrate_trapezoid(lambda E: DOSfe(E, T, EF), E0, E1, Estep)
1748 else:
1749 iEinteg0, iEinteg1 = convert_range2index(E0, E1)
1750 flist = [dos_raw[iE] * fe(Emin + iE * Estep, T, EF) for iE in range(iEinteg0, iEinteg1 + 1)]
1751 N = integrate_trapezoid_by_list(flist, Estep)
1752
1753 return N
1754
1755def Nh(T: float, EF: float, E0: float, E1: float) -> float:
1756 """
1757 概要:
1758 指定されたエネルギー範囲における正孔密度を計算します。
1759 詳細説明:
1760 温度とフェルミ準位における正孔のDOSfh関数を積分範囲で数値積分することにより
1761 正孔密度を計算します。
1762 引数:
1763 :param T: 温度 (K)。
1764 :type T: float
1765 :param EF: フェルミ準位 (eV)。
1766 :type EF: float
1767 :param E0: 積分範囲の開始エネルギー (eV)。
1768 :type E0: float
1769 :param E1: 積分範囲の終了エネルギー (eV)。
1770 :type E1: float
1771 戻り値:
1772 :returns: 計算された正孔密度 (cm^-3)。
1773 :rtype: float
1774 """
1775 global Estep, Emin
1776 global integrator
1777
1778 if integrator == 'interpolate':
1779 N = integrate_trapezoid(lambda E: DOSfh(E, T, EF), E0, E1, Estep)
1780 else:
1781 iEinteg0, iEinteg1 = convert_range2index(E0, E1)
1782 flist = [dos_raw[iE] * fh(Emin + iE * Estep, T, EF) for iE in range(iEinteg0, iEinteg1 + 1)]
1783 N = integrate_trapezoid_by_list(flist, Estep)
1784
1785 return N
1786
1787
1788def FindBandEdges(E: list[float], DOS: list[float], Emidgap: float, DOSth: float) -> tuple[float, float]:
1789 """
1790 概要:
1791 状態密度データから価電子帯上端と伝導帯下端を探索します。
1792 詳細説明:
1793 状態密度データと対応するエネルギーを基に、バンドギャップ中央から
1794 状態密度の閾値を超える点を探索し、価電子帯上端と伝導帯下端を決定します。
1795 引数:
1796 :param E: エネルギーのリスト (eV)。
1797 :type E: list
1798 :param DOS: 状態密度のリスト。
1799 :type DOS: list
1800 :param Emidgap: バンドギャップの中央エネルギーの推定値 (eV)。
1801 :type Emidgap: float
1802 :param DOSth: 状態密度がこの値を超えたときにバンド端と判定する閾値。
1803 :type DOSth: float
1804 戻り値:
1805 :returns: 価電子帯上端と伝導帯下端のエネルギーのタプル。
1806 :rtype: tuple
1807 """
1808 EV = 1.0e30
1809 EC = 1.0e30
1810 i0 = -1
1811 for i in range(len(E)):
1812 if E[i] >= Emidgap:
1813 i0 = i
1814 break
1815
1816 for i in range(i0, 0, -1):
1817 if DOS[i] > DOSth:
1818 EV = E[i+1]
1819 break
1820
1821 for i in range(i0, len(E), +1):
1822 if DOS[i] > DOSth:
1823 EC = E[i-1]
1824 break
1825
1826 return EV, EC
1827
1828def diff(h: float, T: float, EF: float, ECmin: float, ECmax: float, EVmin: float, EVmax: float, N0: float) -> float:
1829 """
1830 概要:
1831 全電荷中性条件からの偏差(dQ)のフェルミ準位による数値微分を計算します(レガシー版)。
1832 詳細説明:
1833 この関数は現在使用されていないようです。tkDefectsクラスのdiffメソッドが使用されています。
1834 dQ関数が未定義のため、この関数は実行されません。
1835 引数:
1836 :param h: 微小なEFの摂動量 (eV)。
1837 :type h: float
1838 :param T: 電子温度 (K)。
1839 :type T: float
1840 :param EF: フェルミ準位 (eV)。
1841 :type EF: float
1842 :param ECmin: 伝導帯のエネルギー下限 (eV)。
1843 :type ECmin: float
1844 :param ECmax: 伝導帯のエネルギー上限 (eV)。
1845 :type ECmax: float
1846 :param EVmin: 価電子帯のエネルギー下限 (eV)。
1847 :type EVmin: float
1848 :param EVmax: 価電子帯のエネルギー上限 (eV)。
1849 :type EVmax: float
1850 :param N0: (現在未使用)。
1851 :type N0: float
1852 戻り値:
1853 :returns: dQのEFによる数値微分。
1854 :rtype: float
1855 """
1856 # dQ関数が定義されていないため、この関数は現状では機能しない
1857 # ここでは仮の戻り値として0を返すか、エラーを出すべきだが、既存コード変更禁止のためそのまま
1858 return 0.0 # (dQ(T, EF + h, ECmin, ECmax, EVmin, EVmax, N0) - dQ(T, EF - h, ECmin, ECmax, EVmin, EVmax, N0)) / 2.0 / h
1859
1860def callbackfunc(obj: object):
1861 """
1862 概要:
1863 数値計算ソルバーの進行状況を表示するためのコールバック関数。
1864 詳細説明:
1865 ソルバーの反復回数、現在の解、関数値、およびステップサイズをコンソールに出力します。
1866 引数:
1867 :param obj: ソルバーオブジェクトのインスタンス。
1868 :type obj: object
1869 戻り値:
1870 :returns: 常に1を返します。
1871 :rtype: int
1872 """
1873 if obj.xa is None:
1874 print(f"Iter {obj.iter:5d}: x: {obj.x:>12.6g} f: {obj.f:>12.6g}, dx = {obj.dx:>10.4g}")
1875 else:
1876 print(f"Iter {obj.iter:5d}: x: {obj.x:>12.6g} f: {obj.f:>12.6g} in [{obj.xa:>12.6g}, {obj.xb:>12.6g}], dx = {obj.dx:>10.4g}")
1877 return 1
1878
1879
1880def read_files(vasp: 'tkVASP', cry: 'tkCrystal', cry_defect: 'tkCrystal', defects: 'tkDefects', POSCAR_path: str, OUTCAR_path: str, EIGENVAL_path: str, DOSCAR_path: str, fp: object = outfp) -> tuple[dict, dict, dict, dict, float, float, float, float, float]:
1881 """
1882 概要:
1883 VASPの計算結果ファイルを読み込み、関連情報を設定します。
1884 詳細説明:
1885 結晶構造、セル体積、フェルミ準位、バンド端、状態密度データを読み込み、
1886 それらをグローバル変数に設定します。また、POSCARと欠陥データのサイト数の一致をチェックします。
1887 戻り値はOUTCAR情報辞書、EIGENVAL情報辞書、バンド端情報辞書、状態密度情報辞書、
1888 価電子帯上端、伝導帯下端、バンドギャップ、HOMO準位、LUMO準位のタプルです。
1889 引数:
1890 :param vasp: tkVASPオブジェクトのインスタンス。
1891 :type vasp: tkVASP
1892 :param cry: 完全結晶のtkCrystalオブジェクトのインスタンス。
1893 :type cry: tkCrystal
1894 :param cry_defect: 欠陥結晶のtkCrystalオブジェクトのインスタンス。
1895 :type cry_defect: tkCrystal
1896 :param defects: tkDefectsオブジェクトのインスタンス。
1897 :type defects: tkDefects
1898 :param POSCAR_path: 完全結晶のPOSCARファイルのパス。
1899 :type POSCAR_path: str
1900 :param OUTCAR_path: OUTCARファイルのパス。
1901 :type OUTCAR_path: str
1902 :param EIGENVAL_path: EIGENVALファイルのパス。
1903 :type EIGENVAL_path: str
1904 :param DOSCAR_path: DOSCARファイルのパス。
1905 :type DOSCAR_path: str
1906 :param fp: 出力先のファイルポインタ。
1907 :type fp: object | None
1908 戻り値:
1909 :returns: VASPファイルから読み込んだ情報のタプル。
1910 :rtype: tuple
1911 """
1912 global EF0
1913 global Vcell
1914 global E_raw, E_norm, dos_raw, nDOS, Enorm_min, Enorm_max, Enorm_step, Emin, Emax, Estep
1915 global EFmin, EFmax, nEF, EFstep
1916 global EV, EC
1917 global fdos
1918
1919 mprint("", fp = outfp)
1920 mprint("Crystal structure from [{}]".format(POSCAR_path), fp = outfp)
1921 a, b, c, alpha, beta, gamm = cry.LatticeParameters()
1922 Vcell = cry.Volume()
1923# cry.PrintInf("cell")
1924 mprint(" cell: {:12.8f} {:12.8f} {:12.8f} A {:10.6f} {:10.6f} {:10.6f}".format(a, b, c, alpha, beta, gamm), fp = outfp)
1925 mprint(" volume: {:12.6f} A^-3".format(Vcell), fp = outfp)
1926
1927 ret = check_atom_sites(cry_defect, defects, fp = outfp)
1928# ret = check_atom_sites(cry, defects, fp = outfp)
1929 if not ret:
1930 mprint("", fp = outfp)
1931 app.terminate("", usage = usage, pause = True)
1932
1933 mprint("", fp = outfp)
1934 mprint("Read [{}]".format(OUTCAR_path), fp = outfp)
1935 outcarinf = vasp.read_outcar_inf(OUTCAR_path)
1936 EF0 = outcarinf["EF"]
1937 ISPIN = outcarinf["ISPIN"]
1938 mprint("Information in OUTCAR:", fp = outfp)
1939 mprint(" ISPIN: ", ISPIN, fp = outfp)
1940 mprint(" EF = {} eV corrected to 0".format(EF0), fp = outfp)
1941 if stop_spin_polarized and ISPIN == 2:
1942 mprint("", fp = outfp)
1943 mprint("Error: ISPIN=2 is not implemented", fp = outfp)
1944 mprint("", fp = outfp)
1945 app.terminate("", usage = usage, pause = True)
1946
1947 mprint("", fp = outfp)
1948 mprint("Read EV, EC, Eg with EF0={} eV from [{}]".format(EF0, EIGENVAL_path), fp = outfp)
1949 eigenvalinf = vasp.read_eigenval(EIGENVAL_path, EF = 0.0)
1950 nk = eigenvalinf["nk"]
1951 nLevels = eigenvalinf["nLevels"]
1952 mprint("k points in EIGENVAL:", fp = outfp)
1953 print("nk=", nk)
1954 print("nLevels=", nLevels)
1955
1956 bandedgeinf = vasp.find_band_edges_from_eigenval(EF0 = EF0, eigenvalinf = eigenvalinf, ISPIN = ISPIN)
1957 EV = bandedgeinf["EV"]
1958 EC = bandedgeinf["EC"]
1959 Eg = bandedgeinf["Eg"]
1960 EHOMO = bandedgeinf["EHOMO"]
1961 ELUMO = bandedgeinf["ELUMO"]
1962
1963 mprint("", fp = outfp)
1964 mprint("*** Read Total DOS from [{}]".format(DOSCAR_path), fp = outfp)
1965# E_raw is measured from EF in OUTCAR
1966# E_raw, dos_raw = np.genfromtxt(DOSCAR_path, skip_header=6, usecols=(0,1), unpack=True)
1967# nDOS = len(E_raw)
1968 dosinf = vasp.read_doscar(DOSCAR_path, cry, IsSpinPolarized = ISPIN, IsNonCollinear = None, EF = 0.0)
1969 E_raw = dosinf["E"]
1970 dos_raw = dosinf["TotalDOS"]
1971 nDOS = dosinf["nE"]
1972 for i in range(len(E_raw)):
1973 dos_raw[i] *= 1.0 / (Vcell * 1.0e-24)
1974 Emin = E_raw[0]
1975 Emax = E_raw[nDOS-1]
1976 Estep = (E_raw[nDOS-1] - E_raw[0]) / (nDOS - 1)
1977 mprint(" DOS E range : {} - {}, {} eV step".format(Emin, Emax, Estep), fp = outfp)
1978
1979# E_norm is measured from EV
1980 E_norm = []
1981 for i in range(len(E_raw)):
1982 E_norm.append(E_raw[i] - EV)
1983 Enorm_min = E_norm[0]
1984 Enorm_max = E_norm[nDOS-1]
1985 Enorm_step = (E_norm[nDOS-1] - E_norm[0]) / (nDOS - 1)
1986 Emin -= EV
1987 Emax -= EV
1988 mprint(" DOS E range normalized to EV = 0: {} - {}, {} eV step".format(Emin, Emax, Estep), fp = outfp)
1989# fdos = interp1d(E_norm, dos_raw, kind = 'cubic')
1990 fdos = interp1d(E_norm, dos_raw, kind = 'linear')
1991
1992 mprint("", fp = outfp)
1993 mprint("*** Find band edges from eigenvalinf", fp = outfp)
1994 mprint("Band edges from EIGENVAL : EV={:10.6f} EC={:10.6f} Eg={:10.6f} EF0={:10.6f} eV"
1995 .format(EV, EC, Eg, EF0), fp = outfp)
1996 EF0 -= EV
1997 EC -= EV
1998 EHOMO -= EV
1999 ELUMO -= EV
2000 EV = 0.0
2001 mprint("Band edges normalized by EV = 0: EV={:10.6f} EC={:10.6f} Eg={:10.6f} EF0={:10.6f} eV"
2002 .format(EV, EC, Eg, EF0), fp = outfp)
2003
2004 return outcarinf, eigenvalinf, bandedgeinf, dosinf, EV, EC, Eg, EHOMO, ELUMO
2005
2006def check_atom_sites(cry: 'tkCrystal', defects: 'tkDefects', fp: object = None) -> bool:
2007 """
2008 概要:
2009 POSCARファイルと欠陥形成エネルギーのExcelファイルで指定された原子サイト数が一致するかをチェックします。
2010 詳細説明:
2011 POSCARから原子サイト数を取得し、Excelファイルから読み込まれた欠陥データの基準サイト数と比較します。
2012 不一致がある場合、ユーザーに続行するか終了するかを問い合わせる警告メッセージを表示します。
2013 引数:
2014 :param cry: 結晶構造情報を持つtkCrystalオブジェクト。
2015 :type cry: tkCrystal
2016 :param defects: 欠陥情報を持つtkDefectsオブジェクト。
2017 :type defects: tkDefects
2018 :param fp: 出力先のファイルポインタ。
2019 :type fp: object | None
2020 戻り値:
2021 :returns: サイト数チェックを続行する場合True、終了する場合False。
2022 :rtype: bool
2023 """
2024# Check atom sites
2025 mprint("", fp = outfp)
2026 mprint("Check atom sites", fp = outfp)
2027 mprint(" from POSCAR:", fp = outfp)
2028# AtomSites = cry.ExpandedAtomSiteList()
2029# for atom in AtomSites:
2030# label = atom.Label()
2031# pos = atom.Position()
2032# print(" {} ({}, {}, {})".format(label, *pos))
2033 AtomTypes = cry.AtomTypeList()
2034 nsites = {}
2035 for t in AtomTypes:
2036 type = t.AtomTypeOnly()
2037 nsites[type] = cry.count_by_type(type, mode = 'short', target = 'expanded')
2038
2039 for type in nsites.keys():
2040 mprint(" {:4} {:4d}".format(type, nsites[type]))
2041 mprint(" from {}:".format(dH_path), fp = outfp)
2042 checked = {}
2043 is_error = ''
2044 site_names = list(nsites.keys())
2045 for d in defects.defects:
2046 if d.site not in checked.keys():
2047 mprint(" {:4} {:4f}".format(d.site, d.N0))
2048 site_name = d.site
2049 checked[site_name] = 1
2050 if d.site not in site_names:
2051 m = re.match(r'([A-Z][a-z]?)', d.site)
2052 if m:
2053# d.site = m.groups()[0]
2054 site_name = m.groups()[0]
2055
2056 if site_name in nsites.keys():
2057 ns = nsites[site_name]
2058# ns = nsites[d.site]
2059 else:
2060 ns = 0
2061
2062 if abs(d.N0 - ns) > 1.0e-3:
2063 is_error += "{} ({} != {}), ".format(d.site, d.N0, ns)
2064
2065 if not ignore_warning and is_error != '':
2066 mprint("", fp = outfp)
2067 mprint("========================================================================", fp = outfp)
2068 mprint(f"Error: Site numbers mismatch between POSCAR and {dH_path}", fp = outfp)
2069 mprint(f" {is_error}")
2070 mprint("========================================================================", fp = outfp)
2071 print("")
2072 print(f"Choose continue: Enter continue to disregard this error")
2073 print(f"Choose stop : Enter stop to terminate this run, and correct site numbers in {dH_path}")
2074 print(f" so as to corresponds to POSCAR")
2075 while 1:
2076 print(" coninue or stop>>", end = '')
2077 answer = input()
2078 if answer == 'continue':
2079 break
2080 elif answer == 'stop':
2081 return False
2082 return True
2083
2084def print_transition_level(defects: 'tkDefects') -> tuple[str, list[list[list]], list[list[list]]]:
2085 """
2086 概要:
2087 欠陥の遷移準位を計算し、出力ファイルに保存して表示します。
2088 詳細説明:
2089 tkDefectsオブジェクトから遷移準位のリストを取得し、
2090 すべての形成エンタルピー曲線と最小形成エンタルピー曲線をExcelファイルに保存します。
2091 戻り値は最後の欠陥名、全遷移点リスト、最小曲線上の点リストのタプルです。
2092 引数:
2093 :param defects: 欠陥情報を持つtkDefectsオブジェクト。
2094 :type defects: tkDefects
2095 戻り値:
2096 :returns: 欠陥名と遷移点リストのタプル。
2097 :rtype: tuple
2098 """
2099#==============================================
2100# Transition levels
2101#==============================================
2102 print("")
2103 print("Find transition levels")
2104 dHEF_all_xlsx = modify_path(dH_path, '-dH-EF-all-{}.xlsx'.format(defects.plabels[iPoint]))
2105 dHEF_min_xlsx = modify_path(dH_path, '-dH-EF-min-{}.xlsx'.format(defects.plabels[iPoint]))
2106 print(" Remove [{}]".format(dHEF_all_xlsx))
2107 delete_file(dHEF_all_xlsx)
2108 defects.save_allpts(dHEF_all_xlsx)
2109
2110 print(" Remove [{}]".format(dHEF_min_xlsx))
2111 delete_file(dHEF_min_xlsx)
2112 minwb = tkExcel(dHEF_min_xlsx, 'w')
2113 defects.save_minpts(dHEF_min_xlsx)
2114
2115 names, allpts_list, minpts_list = defects.get_transitionlevel_lists()
2116
2117 for iname in range(len(names)):
2118 name = names[iname]
2119# groupdata = defects.get_groupdata(name, iPoint)
2120 allpts = allpts_list[iname]
2121 minpts = minpts_list[iname]
2122
2123 mprint("", fp = outfp)
2124 for ipt in range(len(allpts)):
2125 if abs(allpts[ipt][2] - allpts[ipt][3]) > 1.0e-3:
2126 mprint(" ***all point: {:6}({:4}/{:4}) EF={:10.4f} eV dH={:10.4f} eV iorder={} idx={}"
2127 .format(name, allpts[ipt][2], allpts[ipt][3],
2128 allpts[ipt][0], allpts[ipt][1], allpts[ipt][4], allpts[ipt][5]), fp = outfp)
2129 mprint("", fp = outfp)
2130 for ipt in range(len(minpts)):
2131 if abs(minpts[ipt][2] - minpts[ipt][3]) > 1.0e-3:
2132 mprint(" ***min point: {:6}({:4}/{:4}) EF={:10.4f} eV dH={:10.4f} eV iorder={} idx={}"
2133 .format(name, minpts[ipt][2], minpts[ipt][3], minpts[ipt][0], minpts[ipt][1],
2134 minpts[ipt][4], minpts[ipt][5]), fp = outfp)
2135
2136 return name, allpts_list, minpts_list
2137
2138def exec_T():
2139 """
2140 概要:
2141 温度依存性の計算とプロットを実行します。
2142 詳細説明:
2143 欠陥形成エネルギーのExcelファイルとVASP計算結果ファイルを読み込み、
2144 様々な温度における電子・正孔密度、欠陥密度、およびフェルミ準位を計算します。
2145 計算結果はExcelファイルに保存され、グラフとして可視化されます。
2146 """
2147 global mode, plot_mode, T0, EF0
2148 global dH_path, CAR_path, CAR_path_defect, DOSCAR_path, Vcell
2149 global E_raw, E_norm, dos_raw, nDOS, Enorm_min, Enorm_max, Enorm_step, Emin, Emax, Estep
2150 global EV, EC
2151 global EFmin, EFmax, nEF, EFstep
2152 global Einteg0
2153 global nmaxiter_bisection, eps_bisec
2154 global nmaxiter_newton, eps_newton
2155 global fdos
2156 global outfp # グローバル変数としてoutfpを宣言
2157
2158 kBTe = kB * T0 / e
2159 kBTedef = kB * Tdef / e
2160 if T0 < Tdef:
2161 dE = nrange * kBTedef
2162 else:
2163 dE = nrange * kBTe
2164
2165 vasp = tkVASP()
2166 CAR_path = vasp.getdir(CAR_path)
2167 INCAR_path = vasp.get_INCAR(CAR_path)
2168 POSCAR_path = vasp.get_POSCAR(CAR_path)
2169 CONTCAR_path = vasp.get_CONTCAR(CAR_path)
2170 OUTCAR_path = vasp.get_OUTCAR(CAR_path)
2171 EIGENVAL_path = vasp.get_VASPPath(CAR_path, 'EIGENVAL')
2172 DOSCAR_path = vasp.get_VASPPath(CAR_path, 'DOSCAR')
2173
2174 CAR_path_defect = vasp.getdir(CAR_path_defect)
2175 POSCAR_path_defect = vasp.get_POSCAR(CAR_path_defect)
2176
2177 print("")
2178 print("*** Read crystal structure from [{}]".format(POSCAR_path))
2179 cry = vasp.read_poscar(POSCAR_path)
2180# cry.PrintInf("cell")
2181 Vcell = cry.Volume()
2182 mprint(" volume: {:12.6f} A^-3".format(Vcell))
2183
2184 mprint("Crystal structure of defect model from [{}]".format(POSCAR_path_defect))
2185 cry_d = vasp.read_poscar(POSCAR_path_defect)
2186# a_d, b_d, c_d, alpha_d, beta_d, gamm_d = cry_d.LatticeParameters()
2187 Vcell_d = cry_d.Volume()
2188# cry.PrintInf("cell")
2189# mprint(" cell: {:12.8f} {:12.8f} {:12.8f} A {:10.6f} {:10.6f} {:10.6f}".format(a_d, b_d, c_d, alpha_d, beta_d, gamm_d), fp = outfp)
2190 mprint(" volume: {:12.6f} A^-3".format(Vcell_d))
2191
2192 print("")
2193 print("Read defect formation enthalpies from [{}]".format(dH_path))
2194 defects = tkDefects()
2195# defects.SetVolume(Vcell)
2196 defects.read_excel(dH_path, Vcell * int(Vcell_d / Vcell + 0.2))
2197 defects.Print()
2198
2199 print("")
2200 outputfile = modify_path(dH_path, '-out-{}.txt'.format(defects.plabels[iPoint]))
2201 print(" Remove [{}]".format(outputfile))
2202 delete_file(outputfile)
2203 T_xlsx = modify_path(dH_path, '-T-{}.xlsx'.format(defects.plabels[iPoint]))
2204 delete_file(T_xlsx)
2205
2206 print("")
2207 print("*** Open output file {}".format(outputfile))
2208 outfp = open(outputfile, 'w')
2209 if outfp is None:
2210 app.terminate("Error: Can not write to [{}]".format(outputfile), pause = True)
2211
2212 mprint("", fp = outfp)
2213 mprint("=======================================================", fp = outfp)
2214 mprint(" Calculate T dependence of semiconductor properties", fp = outfp)
2215 mprint("=======================================================", fp = outfp)
2216 mprint("mode : ", mode, fp = outfp)
2217
2218 mprint("", fp = outfp)
2219 mprint("Files:", fp = outfp)
2220 mprint(" dH input : ", dH_path, fp = outfp)
2221 mprint("", fp = outfp)
2222 mprint("Files:", fp = outfp)
2223 mprint(" dH input : ", dH_path, fp = outfp)
2224 mprint(" Output : ", outputfile, fp = outfp)
2225
2226 mprint("", fp = outfp)
2227 mprint("Defects red from [{}]".format(dH_path), fp = outfp)
2228 defects.Print(outfp = outfp)
2229 mprint("", fp = outfp)
2230 mprint("To be analyzed for Point {}".format(defects.plabels[iPoint]), fp = outfp)
2231 mprint("T(defects): {} K".format(Tdef))
2232
2233 mprint("", fp = outfp)
2234 mprint("Temperature range: {} - {} K, {} points".format(Tmin, Tmax, nT), fp = outfp)
2235
2236 mprint("", fp = outfp)
2237 mprint("VASP files :", fp = outfp)
2238 mprint(" CAR dir : ", CAR_path, fp = outfp)
2239 mprint(" INCAR : ", INCAR_path, fp = outfp)
2240 mprint(" POSCAR : ", POSCAR_path, fp = outfp)
2241 mprint(" CONTCAR : ", CONTCAR_path, fp = outfp)
2242 mprint(" OUTCAR : ", OUTCAR_path, fp = outfp)
2243 mprint(" EIGENVAL : ", EIGENVAL_path)
2244 mprint(" DOSCAR : ", DOSCAR_path)
2245 mprint(" POSCAR(defect): ", POSCAR_path_defect, fp = outfp)
2246
2247 outcarinf, eigenvalinf, bandedgeinf, dosinf, EV, EC, Eg, EHOMO, ELUMO = \
2248 read_files(vasp, cry, cry_d, defects, POSCAR_path, OUTCAR_path, EIGENVAL_path, DOSCAR_path, fp = outfp)
2249
2250 EFmin = EV + dEFmin
2251 EFmax = EC + dEFmax
2252 EFstep = (EFmax - EFmin) / (nEF - 1)
2253
2254 mprint("", fp = outfp)
2255 mprint("EF range to save to transition level files", fp = outfp)
2256 mprint(" EF range: {} - {}, {} eV step".format(EFmin, EFmax, EFstep), fp = outfp)
2257
2258 mprint("", fp = outfp)
2259 mprint("T dependence", fp = outfp)
2260 mprint(" T range: {} - {}, {} K step".format(Tmin, Tmax, Tstep), fp = outfp)
2261
2262 mprint("", fp = outfp)
2263 mprint("Integration configuration", fp = outfp)
2264 mprint(" Integration E range: {} - {} eV, or EF+-{}*kBT eV".format(Einteg0, Einteg1, nrange), fp = outfp)
2265
2266 ECmin = EC - Estep
2267 ECmax = EC + dE
2268 EVmin = EV - dE
2269 EVmax = EV + Estep
2270
2271#==============================================
2272# Print transition levels
2273#==============================================
2274 name, allpts_list, minpts_list = print_transition_level(defects)
2275
2276#==============================================
2277# Start calculations
2278#==============================================
2279# At Tdef
2280 EFmin_ini = EV + dEFmin
2281 EFmax_ini = EC + dEFmax
2282 mprint("", fp = outfp)
2283 mprint("Calculate defect densities at T(defects) = {} K".format(Tdef))
2284 mprint(" Calculate EF,eq: Bisecton method: eps = {}, nmaxiter = {}".format(eps_bisec, nmaxiter_bisection), fp = outfp)
2285 mprint(" Initial EF range: {:12.4f} - {:12.4f} eV".format(EFmin_ini, EFmax_ini))
2286 mprint(" Newton method : eps = {}, nmaxiter = {}, dump = {}".format(eps_newton, nmaxiter_newton, dump_newton), fp = outfp)
2287 if EFdef == 'eq':
2288 EFeqTdef, dQ = defects.find_EF(Tdef, Tdef, None, ECmin, ECmax, EVmin, EVmax,
2289 callbackfunc = callbackfunc,
2290 eps_bisec = eps_bisec, nmaxiter_bisection = nmaxiter_bisection, initial = [EFmin_ini, EFmax_ini],
2291 eps_newton = eps_newton, nmaxiter_newton = nmaxiter_newton, dump_newton = dump_newton,
2292 h_newton = h_newton
2293 )
2294 if EFeqTdef is None:
2295 app.terminate("Error in tkDefects.find_EF: Could not reach convergence for EF", pause = True)
2296 else:
2297 try:
2298 EFeqTdef = float(EFdef)
2299 except:
2300 app.terminate("Error in tkDefects.find_EF: EFdef [{}] must be numeral".format(EFdef), pause = True)
2301
2302 mprint("", fp = outfp)
2303 mprint(" EF,eq({:8.3f} K)={:12.6g} eV".format(Tdef, EFeqTdef), fp = outfp)
2304 Z, ne, nh, Nds, NdsTdef, dGs, Qtot = defects.calculate_densities(T0, Tdef, None, EFeqTdef, ECmin, ECmax, EVmin, EVmax)
2305 for id in range(defects.ndefects):
2306 d = defects.defects[id]
2307 label = "{}_{}".format(d.atom, d.site)
2308 labelq = "{}^{}".format(label, d.charge)
2309 mprint(" {:>16} (N0={:5g} Ndoped={:5g}):".format(labelq, d.N0, d.Ndoped), end = '', fp = outfp)
2310 mprint(" Nds={:12.4g} cm-3 dGs={:12.4g} eV".format(Nds[id], dGs[id]), fp = outfp)
2311 mprint("", fp = outfp)
2312 mprint(" Total defect densities at {} K".format(Tdef), fp = outfp)
2313 for key in NdsTdef.keys():
2314 Nsite = NdsTdef[key] * defects.V * 1.0e-24
2315 mprint(" {:>8}: {:12.6g} cm-3 ({:8.2f} sites)".format(key, NdsTdef[key], Nsite), fp = outfp)
2316
2317# At T0, EF,eq(T0)
2318 EFmin_ini = EV + dEFmin
2319 EFmax_ini = EC + dEFmax
2320 mprint("", fp = outfp)
2321 if T0 <= Tdef:
2322 mprint("Calculate defect densities at T(electron) = {} K".format(T0))
2323 mprint(" Total defect densities frozen at {} K with EF = {:12.4f} eV".format(Tdef, EFeqTdef), fp = outfp)
2324 for key in NdsTdef.keys():
2325 Nsite = NdsTdef[key] * defects.V * 1.0e-24
2326 mprint(" {:>8}: {:12.6g} cm-3 ({:8.2f} sites)".format(key, NdsTdef[key], Nsite), fp = outfp)
2327 else:
2328 mprint("T(electron) is higher than T(defects). Defect densities are calculated at T(electron)")
2329 mprint("Calculate defect densities at T(electron) = {} K, T(defects) = {} K".format(T0, T0))
2330 mprint(" Calculate EF,eq: Bisecton method: eps = {}, nmaxiter = {}".format(eps_bisec, nmaxiter_bisection), fp = outfp)
2331 mprint(" Initial EF range: {:12.4f} - {:12.4f} eV".format(EFmin_ini, EFmax_ini))
2332 mprint(" Newton method : eps = {}, nmaxiter = {}, dump = {}".format(eps_newton, nmaxiter_newton, dump_newton), fp = outfp)
2333 EFeqT0, dQ = defects.find_EF(T0, Tdef, NdsTdef, ECmin, ECmax, EVmin, EVmax,
2334 callbackfunc = callbackfunc,
2335 eps_bisec = eps_bisec, nmaxiter_bisection = nmaxiter_bisection, initial = [EFmin_ini, EFmax_ini],
2336 eps_newton = eps_newton, nmaxiter_newton = nmaxiter_newton, dump_newton = dump_newton,
2337 h_newton = h_newton
2338 )
2339 if EFeqT0 is None:
2340 app.terminate("Error in tkDefects.find_EF: Could not reach convergence for EF", pause = True)
2341
2342#==============================================
2343# Start calculations
2344#==============================================
2345# At Tdef
2346 EFmin_ini = EV + dEFmin
2347 EFmax_ini = EC + dEFmax
2348 mprint("", fp = outfp)
2349 mprint("Calculate defect densities at T(electron) = T(defects) = {} K".format(Tdef))
2350 mprint(" Calculate EF,eq: Bisecton method: eps = {}, nmaxiter = {}".format(eps_bisec, nmaxiter_bisection), fp = outfp)
2351 mprint(" Initial EF range: {:12.4f} - {:12.4f} eV".format(EFmin_ini, EFmax_ini))
2352 mprint(" Newton method : eps = {}, nmaxiter = {}, dump = {}".format(eps_newton, nmaxiter_newton, dump_newton), fp = outfp)
2353 if EFdef == 'eq':
2354 EFeqTdef, dQ = defects.find_EF(Tdef, Tdef, None, ECmin, ECmax, EVmin, EVmax,
2355 callbackfunc = callbackfunc,
2356 eps_bisec = eps_bisec, nmaxiter_bisection = nmaxiter_bisection, initial = [EFmin_ini, EFmax_ini],
2357 eps_newton = eps_newton, nmaxiter_newton = nmaxiter_newton, dump_newton = dump_newton,
2358 h_newton = h_newton
2359 )
2360 if EFeqTdef is None:
2361 app.terminate("Error in tkDefects.find_EF: Could not reach convergence for EF", pause = True)
2362 else:
2363 try:
2364 EFeqTdef = float(EFdef)
2365 except:
2366 app.terminate("Error in tkDefects.find_EF: EFdef [{}] must be numeral".format(EFdef), pause = True)
2367
2368 mprint("", fp = outfp)
2369 mprint(" EF,eq({:8.3f} K)={:12.6g} eV".format(Tdef, EFeqTdef), fp = outfp)
2370 Z, ne, nh, Nds, NdsTdef, dGs, Qtot = defects.calculate_densities(T0, Tdef, None, EFeqTdef, ECmin, ECmax, EVmin, EVmax)
2371 for id in range(defects.ndefects):
2372 d = defects.defects[id]
2373 label = "{}_{}".format(d.atom, d.site)
2374 labelq = "{}^{}".format(label, d.charge)
2375 mprint(" {:>16} (N0={:5g} Ndoped={:5g}):".format(labelq, d.N0, d.Ndoped), end = '', fp = outfp)
2376 mprint(" Nds={:12.4g} cm-3 dGs={:12.4g} eV".format(Nds[id], dGs[id]), fp = outfp)
2377 mprint("", fp = outfp)
2378 mprint(" Total defect densities at {} K".format(Tdef), fp = outfp)
2379 for key in NdsTdef.keys():
2380 Nsite = NdsTdef[key] * defects.V * 1.0e-24
2381 mprint(" {:>8}: {:12.6g} cm-3 ({:8.2f} sites)".format(key, NdsTdef[key], Nsite), fp = outfp)
2382
2383# At T0, EF,eq(T0)
2384 EFmin_ini = EV + dEFmin
2385 EFmax_ini = EC + dEFmax
2386 mprint("", fp = outfp)
2387 mprint("Calculate defect densities at T(electron) = {} K, T(defects) = {} K".format(T0, Tdef))
2388 mprint(" Calculate EF,eq: Bisecton method: eps = {}, nmaxiter = {}".format(eps_bisec, nmaxiter_bisection), fp = outfp)
2389 mprint(" Initial EF range: {:12.4f} - {:12.4f} eV".format(EFmin_ini, EFmax_ini))
2390 mprint(" Newton method : eps = {}, nmaxiter = {}, dump = {}".format(eps_newton, nmaxiter_newton, dump_newton), fp = outfp)
2391 EFeqT0, dQ = defects.find_EF(T0, Tdef, NdsTdef, ECmin, ECmax, EVmin, EVmax,
2392 callbackfunc = callbackfunc,
2393 eps_bisec = eps_bisec, nmaxiter_bisection = nmaxiter_bisection, initial = [EFmin_ini, EFmax_ini],
2394 eps_newton = eps_newton, nmaxiter_newton = nmaxiter_newton, dump_newton = dump_newton,
2395 h_newton = h_newton
2396 )
2397 if EFeqT0 is None:
2398 app.terminate("Error in tkDefects.find_EF: Could not reach convergence for EF", pause = True)
2399
2400 mprint("", fp = outfp)
2401 mprint(" EF,eq({:8.3g} K)={:12.6g} eV".format(T0, EFeqT0), fp = outfp)
2402 Z, ne, nh, Nds, NdsTdef, dGs, Qtot = defects.calculate_densities(T0, Tdef, NdsTdef, EFeqT0, ECmin, ECmax, EVmin, EVmax)
2403 for id in range(defects.ndefects):
2404 d = defects.defects[id]
2405 label = f"{d.atom}_{d.site}"
2406 labelq = f"{label}^{d.charge}"
2407 mprint(f" {labelq:>16} (Nds(tot)={NdsTdef[labelq]:12.4g} cm-3):", end = '', fp = outfp)
2408# mprint(f" {labelq:>16} (Nds(tot)={NdsTdef[label]:12.6g} cm-3):", end = '', fp = outfp)
2409 mprint(f" Nds={Nds[id]:12.4g} cm-3 dGs={dGs[id]:12.4g} eV", fp = outfp)
2410
2411# At Tmin, EF,eq(Tmin)
2412 EFmin_ini = EV + dEFmin
2413 EFmax_ini = EC + dEFmax
2414 mprint("", fp = outfp)
2415 mprint("Calculate defect densities at T(min) = {} K, T(defects) = {} K".format(Tmin, Tdef))
2416 mprint(" Calculate EF,eq: Bisecton method: eps = {}, nmaxiter = {}".format(eps_bisec, nmaxiter_bisection), fp = outfp)
2417 mprint(" Initial EF range: {:12.4f} - {:12.4f} eV".format(EFmin_ini, EFmax_ini))
2418 mprint(" Newton method : eps = {}, nmaxiter = {}, dump = {}".format(eps_newton, nmaxiter_newton, dump_newton), fp = outfp)
2419 EFeqTmin, dQ = defects.find_EF(Tmin, Tdef, NdsTdef, ECmin, ECmax, EVmin, EVmax,
2420 callbackfunc = callbackfunc,
2421 eps_bisec = eps_bisec, nmaxiter_bisection = nmaxiter_bisection, initial = [EFmin_ini, EFmax_ini],
2422 eps_newton = eps_newton, nmaxiter_newton = nmaxiter_newton, dump_newton = dump_newton,
2423 h_newton = h_newton
2424 )
2425
2426 if EFeqTmin is None:
2427 app.terminate("Error in tkDefects.find_EF: Could not reach convergence for EF at {} K".format(EFeqTmin), pause = True)
2428 else:
2429 mprint(" EF,eq at {} K = {:12.6g} eV".format(Tmin, EFeqTmin), fp = outfp)
2430
2431# At various T, EF,eq(T)
2432 mprint("", fp = outfp)
2433 mprint("Calculate defect densities at various T = {} - {} K".format(Tmin, Tmax))
2434 mprint(" Note if T(electron) is higher than T(defects), defects are not frozen.")
2435 mprint(" If T is lower than T(defects) = {} K, ".format(Tdef), fp = outfp)
2436 mprint(" total defect densities are frozen at {} K with EF = {:.4f} eV as follows.".format(Tdef, EFeqTdef), fp = outfp)
2437 for key in NdsTdef.keys():
2438 Nsite = NdsTdef[key] * defects.V * 1.0e-24
2439 mprint(" {:>8}: {:12.6g} cm-3 ({:8.2f} sites)".format(key, NdsTdef[key], Nsite), fp = outfp)
2440
2441 mprint(" Calculate EF,eq: Bisecton method: eps = {}, nmaxiter = {}".format(eps_bisec, nmaxiter_bisection), fp = outfp)
2442 mprint(" Newton method : eps = {}, nmaxiter = {}, dump = {}".format(eps_newton, nmaxiter_newton, dump_newton), fp = outfp)
2443 EF = EFeqTmin
2444 xT = [Tmin + i * Tstep for i in range(nT)]
2445 xT1000 = []
2446 yEF = []
2447 ydQ = []
2448 yne = []
2449 ynh = []
2450 ynds = []
2451 for id in range(defects.ndefects):
2452 ynds.append([0.0]*nT)
2453 EFmin_ini = EV + dEFmin
2454 EFmax_ini = EC + dEFmax
2455 for iT in range(nT):
2456 T = xT[iT]
2457 xT1000.append(1000.0 / T)
2458
2459 print("T: {} K".format(T))
2460 EF, dQ = defects.find_EF(T, Tdef, NdsTdef, ECmin, ECmax, EVmin, EVmax,
2461 callbackfunc = callbackfunc,
2462 eps_bisec = eps_bisec, nmaxiter_bisection = nmaxiter_bisection, initial = [EFmin_ini, EFmax_ini],
2463 eps_newton = eps_newton, nmaxiter_newton = nmaxiter_newton, dump_newton = dump_newton,
2464 h_newton = h_newton
2465 )
2466 Z, ne, nh, Nds, NdsTdef, dGs, Qtot = defects.calculate_densities(T, Tdef, NdsTdef, EF, ECmin, ECmax, EVmin, EVmax)
2467 if ne == 0.0:
2468 ne = 1.0
2469 if nh == 0.0:
2470 nh = 1.0
2471 for id in range(defects.ndefects):
2472 ynds[id][iT] = Nds[id]
2473
2474 yEF.append(EF)
2475 yne.append(ne)
2476 ynh.append(nh)
2477 ydQ.append(Qtot)
2478
2479 EFmin_ini = EF - 0.2
2480 EFmax_ini = EF + 0.2
2481
2482 mprint("")
2483 mprint("T(defect frozen): {} K".format(Tdef))
2484 Twb = tkExcel(T_xlsx, 'w')
2485 mprint("{:8}: {:12} {:12} {:8} {:12} {:8}".format("T(K)", "EF(eV)", "ne(cm^-3)", "Ea(ne)(eV)", "nh(cm^-3)", "Ea(nh)(eV)"), end = '', fp = outfp)
2486 Twb.Print(["T(K)", "1000/T(K^-1)", "EF(eV)", "ne(cm^-3)", "Ea(ne)(eV)", "nh(cm^-3)", "Ea(nh)(eV)"], end = '')
2487 for id in range(defects.ndefects):
2488 d = defects.defects[id]
2489 mprint(" {:12}".format(d.name), end = '', fp = outfp)
2490 Twb.Print([d.name], end = '')
2491 mprint("", fp = outfp)
2492 Twb.Print(['dQ'])
2493 for iT in range(nT):
2494 T = xT[iT]
2495 EF = yEF[iT]
2496 ne = yne[iT]
2497 nh = ynh[iT]
2498 Qtot = ydQ[iT]
2499 if iT == 0:
2500 slopene = (log(yne[1]) - log(yne[0])) / (xT1000[1] - xT1000[0])
2501 slopenh = (log(ynh[1]) - log(ynh[0])) / (xT1000[1] - xT1000[0])
2502 elif iT == nT - 1:
2503 slopene = (log(yne[nT-1]) - log(yne[nT-2])) / (xT1000[nT-1] - xT1000[nT-2])
2504 slopenh = (log(ynh[nT-1]) - log(ynh[nT-2])) / (xT1000[nT-1] - xT1000[nT-2])
2505 else:
2506 slopene = (log(yne[iT+1]) - log(yne[iT-1])) / (xT1000[iT+1] - xT1000[iT-1])
2507 slopenh = (log(ynh[iT+1]) - log(ynh[iT-1])) / (xT1000[iT+1] - xT1000[iT-1])
2508 Eane = -slopene * kB / e * 1000.0
2509 Eanh = -slopenh * kB / e * 1000.0
2510
2511 mprint("{:8.4g}: {:12.6g} {:12.6g} {:8.3f} {:12.6g} {:8.3f}".format(T, EF, ne, Eane, nh, Eanh), end = '', fp = outfp)
2512 Twb.Print([T, 1000.0 / T, EF, ne, Eane, nh, Eanh], end = '')
2513 for id in range(defects.ndefects):
2514 mprint(" {:12.6g}".format(Nds[id]), end = '', fp = outfp)
2515 Twb.Print([Nds[id]], end = '')
2516 mprint(" {:12.6g}".format(Qtot), fp = outfp)
2517 Twb.Print([Qtot])
2518
2519
2520 mprint("", fp = outfp)
2521 mprint("*** Save N-EF data to [{}]".format(T_xlsx))
2522 Twb.Close()
2523
2524#=============================
2525# Plot graphs
2526#=============================
2527 fig = plt.figure(figsize = (12, 8))
2528
2529 axdos = fig.add_subplot(2, 2, 1)
2530 axEF = fig.add_subplot(2, 2, 2)
2531 axdH = axEF.twiny()
2532 axNT = fig.add_subplot(2, 2, 4)
2533 axNiT = fig.add_subplot(2, 2, 3)
2534
2535 axdos.set_title("{}, for Point {}".format(dH_path, defects.plabels[iPoint]))
2536
2537 axdos.plot(E_raw, dos_raw, label = 'DOS(EF={:.3} eV)'.format(EF0), color = 'cyan', linewidth =0.5)
2538 axdos.plot(E_norm, dos_raw, label = 'DOS(EV=0)', color = 'black', linewidth =1.0)
2539 yrange = [min(dos_raw), max(dos_raw)]
2540 axdos.plot([EV, EV], yrange, label = '$E_V$', color = 'blue', linestyle = 'dashed', linewidth = 0.5)
2541 axdos.plot([EC, EC], yrange, label = '$E_C$', color = 'blue', linestyle = 'dashed', linewidth = 0.5)
2542 EFeqmin = min(yEF)
2543 EFeqmax = max(yEF)
2544 axdos.plot([EFeqmin, EFeqmin], yrange, label = '$E_{F,eq}$(min)', color = 'red', linestyle = 'dashed', linewidth = 1.0)
2545 axdos.plot([EFeqmax, EFeqmax], yrange, label = '$E_{F,eq}$(max)', color = 'blue', linestyle = 'dashed', linewidth = 1.0)
2546# axdos.set_xlim([EFmin, EFmax])
2547 axdos.set_xlim([min([EFmin, view_Emin]), max([EFmax, view_Emax])])
2548 axdos.set_ylim([0.0, None])
2549 axdos.set_xlabel("$E$, $E - E_V$ (eV)")
2550 axdos.set_ylabel("DOS (states/cm$^3$)")
2551 _legend = axdos.legend()
2552 _legend.set_draggable(True)
2553
2554 axEF.plot(xT, yEF, label = '$E_F$ (eV)')
2555 axEF.plot([Tmin, Tmax], [EV, EV], label = '$E_V$', color = 'red', linestyle = 'dashed', linewidth = 1.0)
2556 axEF.plot([Tmin, Tmax], [EC, EC], label = '$E_C$', color = 'red', linestyle = 'dashed', linewidth = 1.0)
2557 axEF.set_xlabel("$T$ (K)")
2558 axEF.set_ylabel("$E_F - E_V$ (eV)")
2559# axEF.set_ylim([-1.0e19, 1.0e19])
2560 axEF.set_xlim([Tmin, Tmax])
2561# axEF.legend()
2562
2563 names = defects.get_names()
2564 for iname in range(len(names)):
2565 color = colors[iname % len(colors)]
2566
2567 name = names[iname]
2568 data = defects.get_groupdata(name, iPoint)
2569 axdH.plot([], [], label = "{}".format(name), linestyle = 'dashed', linewidth = 0.5, color = color)
2570
2571 minpts = minpts_list[iname]
2572 for ipt in range(len(minpts) - 1):
2573 axdH.plot([minpts[ipt][1], minpts[ipt+1][1]], [minpts[ipt][0], minpts[ipt+1][0]],
2574 linestyle = 'dashed', linewidth = 0.5, color = color,
2575 marker = 'o', markersize = 3.0, markeredgewidth = 1, markerfacecolor = 'w', markeredgecolor = color)
2576 axdH.set_xlabel("$\Delta$$H$ (eV)")
2577# axdH.set_xlim([view_dHmax, axdH.get_xlim()[0]])
2578 axdH.set_xlim([-0.2, axdH.get_xlim()[0]])
2579
2580 h1, l1 = axEF.get_legend_handles_labels()
2581 h2, l2 = axdH.get_legend_handles_labels()
2582 _legend = axEF.legend(h1 + h2, l1 + l2, bbox_to_anchor=(1.05, 1.0), loc='upper left', borderaxespad = 0, fontsize = legend_fontsize)
2583 _legend.set_draggable(True)
2584
2585 for i in [0, 1]:
2586 if i == 0:
2587 ax = axNiT
2588 xx = xT1000
2589 xxrange = [1000.0 / Tmax, 1000.0 / Tmin]
2590 ax.set_xlabel("1000/$T$ (K$^{-1}$)")
2591 else:
2592 ax = axNT
2593 xx = xT
2594 xxrange = [Tmin, Tmax]
2595 ax.set_xlabel("$T$ (K)")
2596
2597 ax.plot(xx, yne, label = '$N_e$', color = 'red', linestyle = '-', linewidth = 1.0)
2598 ax.plot(xx, ynh, label = '$N_h$', color = 'blue', linestyle = '-', linewidth = 1.0)
2599 for id in range(len(ynds)):
2600 d = defects.defects[id]
2601 ymax = max(ynds[id])
2602 if ymax < view_Nmin:
2603 continue
2604 ax.plot(xx, ynds[id], label = "{}({})".format(d.name, d.charge), linestyle = 'dashed', linewidth = 1.0)
2605 yrange = ax.get_ylim()
2606 yrange = [yrange[0], yrange[1] * 10.0]
2607 ax.plot(xxrange, [EV, EV], color = 'red', linestyle = 'dashed', linewidth = 0.5)
2608 ax.plot(xxrange, [EC, EC], color = 'red', linestyle = 'dashed', linewidth = 0.5)
2609 ax.set_ylabel("$N$ (cm$^{-3}$)")
2610 ax.set_yscale('log')
2611 ax.set_xlim(xxrange)
2612 ax.set_ylim([view_Nmin, 1.0e23])
2613# ax.legend()
2614 _legend = ax.legend(bbox_to_anchor=(1.05, 1.0), loc='upper left', borderaxespad = 0, fontsize = legend_fontsize)
2615 _legend.set_draggable(True)
2616
2617
2618# Rearange the graph axes so that they are not overlapped
2619 plt.tight_layout()
2620
2621 """
2622 print("")
2623 print("Close graph window to terminate")
2624 plt.show()
2625 """
2626
2627 plt.pause(0.1)
2628
2629 app.terminate("", usage = usage, pause = True)
2630
2631 if outfp:
2632 outfp.close()
2633
2634def exec_EF():
2635 """
2636 概要:
2637 フェルミ準位依存性の計算とプロットを実行します。
2638 詳細説明:
2639 欠陥形成エネルギーのExcelファイルとVASP計算結果ファイルを読み込み、
2640 様々なフェルミ準位における電子・正孔密度、欠陥密度、および全電荷の中性条件からの偏差を計算します。
2641 計算結果はExcelファイルに保存され、グラフとして可視化されます。
2642 """
2643 global mode, plot_mode, T0, EF0
2644 global dH_path, CAR_path, CAR_path_defect, DOSCAR_path, Vcell
2645 global E_raw, E_norm, dos_raw, nDOS, Enorm_min, Enorm_max, Enorm_step, Emin, Emax, Estep
2646 global EV, EC
2647 global EFmin, EFmax, nEF, EFstep
2648 global Einteg0
2649 global outfp # グローバル変数としてoutfpを宣言
2650
2651 kBTe = kB * T0 / e
2652 kBTedef = kB * Tdef / e
2653 if T0 < Tdef:
2654 dE = nrange * kBTedef
2655 else:
2656 dE = nrange * kBTe
2657
2658 vasp = tkVASP()
2659 CAR_path = vasp.getdir(CAR_path)
2660 INCAR_path = vasp.get_INCAR(CAR_path)
2661 POSCAR_path = vasp.get_POSCAR(CAR_path)
2662 CONTCAR_path = vasp.get_CONTCAR(CAR_path)
2663 OUTCAR_path = vasp.get_OUTCAR(CAR_path)
2664 EIGENVAL_path = vasp.get_VASPPath(CAR_path, 'EIGENVAL')
2665 DOSCAR_path = vasp.get_VASPPath(CAR_path, 'DOSCAR')
2666
2667 CAR_path_defect = vasp.getdir(CAR_path_defect)
2668 POSCAR_path_defect = vasp.get_POSCAR(CAR_path_defect)
2669
2670 print("")
2671 print("*** Read crystal structure from [{}]".format(POSCAR_path))
2672 cry = vasp.read_poscar(POSCAR_path)
2673# cry.PrintInf("cell")
2674 Vcell = cry.Volume()
2675 mprint(" volume: {:12.6f} A^-3".format(Vcell))
2676
2677 mprint("Crystal structure of defect model from [{}]".format(POSCAR_path_defect))
2678 cry_d = vasp.read_poscar(POSCAR_path_defect)
2679# a_d, b_d, c_d, alpha_d, beta_d, gamm_d = cry_d.LatticeParameters()
2680 Vcell_d = cry_d.Volume()
2681# cry.PrintInf("cell")
2682# mprint(" cell: {:12.8f} {:12.8f} {:12.8f} A {:10.6f} {:10.6f} {:10.6f}".format(a_d, b_d, c_d, alpha_d, beta_d, gamm_d), fp = outfp)
2683 mprint(" volume: {:12.6f} A^-3".format(Vcell_d))
2684
2685 print("")
2686 print("Read defect formation enthalpies from [{}]".format(dH_path))
2687 defects = tkDefects()
2688# defects.SetVolume(Vcell)
2689 defects.read_excel(dH_path, Vcell * int(Vcell_d / Vcell + 0.2))
2690 defects.Print()
2691
2692 outputfile = modify_path(dH_path, '-out-{}.txt'.format(defects.plabels[iPoint]))
2693 NEF_xlsx = modify_path(dH_path, '-N-EF-{}.xlsx'.format(defects.plabels[iPoint]))
2694 dHEF_all_xlsx = modify_path(dH_path, '-dH-EF-all-{}.xlsx'.format(defects.plabels[iPoint]))
2695 dHEF_min_xlsx = modify_path(dH_path, '-dH-EF-min-{}.xlsx'.format(defects.plabels[iPoint]))
2696 print(" Remove [{}]".format(outputfile))
2697 delete_file(outputfile)
2698 print(" Remove [{}]".format(NEF_xlsx))
2699 delete_file(NEF_xlsx)
2700 print(" Remove [{}]".format(dHEF_all_xlsx))
2701 delete_file(dHEF_all_xlsx)
2702 print(" Remove [{}]".format(dHEF_min_xlsx))
2703 delete_file(dHEF_min_xlsx)
2704
2705 print("")
2706 print("*** Open output file {}".format(outputfile))
2707 outfp = open(outputfile, 'w')
2708 if outfp is None:
2709 app.terminate("Error: Can not write to [{}]".format(outputfile), pause = True)
2710
2711 if plot_mode == 'all':
2712 plotall = True
2713 else:
2714 plotall = False
2715
2716 mprint("", fp = outfp)
2717 mprint("=======================================================", fp = outfp)
2718 mprint(" Calculate EF dependence of semiconductor properties", fp = outfp)
2719 mprint("=======================================================", fp = outfp)
2720 mprint("mode : ", mode, fp = outfp)
2721 mprint("plot mode: ", plot_mode, fp = outfp)
2722 mprint("plot all: ", plotall, fp = outfp)
2723
2724 mprint("", fp = outfp)
2725 mprint("Files:", fp = outfp)
2726 mprint(" dH input : ", dH_path, fp = outfp)
2727 mprint("", fp = outfp)
2728 mprint("Files:", fp = outfp)
2729 mprint(" dH input : ", dH_path, fp = outfp)
2730 mprint(" Output : ", outputfile, fp = outfp)
2731 mprint(" N-EF : ", NEF_xlsx, fp = outfp)
2732 mprint(" dH-EF(all): ", dHEF_all_xlsx, fp = outfp)
2733 mprint(" dH-EF(min): ", dHEF_min_xlsx, fp = outfp)
2734
2735 mprint("", fp = outfp)
2736 mprint("Defects red from [{}]".format(dH_path), fp = outfp)
2737 defects.Print(outfp = outfp)
2738 mprint("", fp = outfp)
2739 mprint("To be analyzed for Point {}".format(defects.plabels[iPoint]), fp = outfp)
2740 mprint("T(defects): {} K".format(Tdef), fp = outfp)
2741
2742 mprint("", fp = outfp)
2743 mprint("VASP files :", fp = outfp)
2744 mprint(" CAR dir : ", CAR_path, fp = outfp)
2745 mprint(" INCAR : ", INCAR_path, fp = outfp)
2746 mprint(" POSCAR : ", POSCAR_path, fp = outfp)
2747 mprint(" CONTCAR : ", CONTCAR_path, fp = outfp)
2748 mprint(" OUTCAR : ", OUTCAR_path, fp = outfp)
2749 mprint(" EIGENVAL : ", EIGENVAL_path, fp = outfp)
2750 mprint(" DOSCAR : ", DOSCAR_path, fp = outfp)
2751 mprint(" POSCAR(defect): ", POSCAR_path_defect, fp = outfp)
2752
2753 outcarinf, eigenvalinf, bandedgeinf, dosinf, EV, EC, Eg, EHOMO, ELUMO = \
2754 read_files(vasp, cry, cry_d, defects, POSCAR_path, OUTCAR_path, EIGENVAL_path, DOSCAR_path, fp = outfp)
2755
2756# EF plot range
2757 if EFmin is None: EFmin = EV + dEFmin
2758 if EFmax is None: EFmax = EC + dEFmax
2759 EFstep = (EFmax - EFmin) / (nEF - 1)
2760 mprint("", fp = outfp)
2761 mprint("EF dependence", fp = outfp)
2762 mprint(" EF range: {} - {}, {} eV step".format(EFmin, EFmax, EFstep), fp = outfp)
2763
2764 mprint("", fp = outfp)
2765 mprint("Integration configuration", fp = outfp)
2766 mprint(" Integration E range: {} - {} eV, or EF+-{}*kBT eV".format(Einteg0, Einteg1, nrange), fp = outfp)
2767
2768 ECmin = EC - Estep
2769 ECmax = EC + dE
2770 EVmin = EV - dE
2771 EVmax = EV + Estep
2772
2773#==============================================
2774# Print transition levels
2775#==============================================
2776 name, allpts_list, minpts_list = print_transition_level(defects)
2777# exit()
2778
2779#==============================================
2780# Start calculations
2781#==============================================
2782# At Tdef
2783# EFmin_ini = min([EV - dEFmin, EF - dEFmin])
2784# EFmax_ini = max([EC + dEFmax, EF + dEFmax])
2785 EFmin_ini = EV + dEFmin
2786 EFmax_ini = EC + dEFmax
2787 mprint("", fp = outfp)
2788 mprint("Calculate defect densities at T(electron) = T(defects) = {} K".format(Tdef))
2789 mprint(" Calculate EF,eq: Bisecton method: eps = {}, nmaxiter = {}".format(eps_bisec, nmaxiter_bisection), fp = outfp)
2790 mprint(" Initial EF range: {:12.4f} - {:12.4f} eV".format(EFmin_ini, EFmax_ini))
2791 mprint(" Newton method : eps = {}, nmaxiter = {}, dump = {}".format(eps_newton, nmaxiter_newton, dump_newton), fp = outfp)
2792
2793 if EFdef == 'eq':
2794 EFeqTdef, dQ = defects.find_EF(Tdef, Tdef, None, ECmin, ECmax, EVmin, EVmax,
2795 callbackfunc = callbackfunc,
2796 eps_bisec = eps_bisec, nmaxiter_bisection = nmaxiter_bisection, initial = [EFmin_ini, EFmax_ini],
2797 eps_newton = eps_newton, nmaxiter_newton = nmaxiter_newton, dump_newton = dump_newton,
2798 h_newton = h_newton
2799 )
2800 if EFeqTdef is None:
2801 app.terminate("Error in tkDefects.find_EF: Could not reach convergence for EF", pause = True)
2802 else:
2803 try:
2804 EFeqTdef = float(EFdef)
2805 except:
2806 app.terminate("Error in tkDefects.find_EF: EFdef [{}] must be numeral".format(EFdef), pause = True)
2807
2808 mprint("", fp = outfp)
2809 mprint(" EF,eq({:8.3f} K)={:12.6g} eV".format(Tdef, EFeqTdef), fp = outfp)
2810
2811 Z, ne, nh, Nds, NdsTdef, dGs, Qtot = defects.calculate_densities(T0, Tdef, None, EFeqTdef, ECmin, ECmax, EVmin, EVmax)
2812
2813 for id in range(defects.ndefects):
2814 d = defects.defects[id]
2815 label = "{}_{}".format(d.atom, d.site)
2816 labelq = "{}^{}".format(label, d.charge)
2817 mprint(" {:>16} (N0={:5g} Ndoped={:5g}):".format(labelq, d.N0, d.Ndoped), end = '', fp = outfp)
2818 mprint(" Nds={:12.4g} cm-3 dGs={:12.4g} eV".format(Nds[id], dGs[id]), fp = outfp)
2819 mprint("", fp = outfp)
2820
2821 mprint(" Total defect densities at {} K".format(Tdef), fp = outfp)
2822 for defect_name in NdsTdef.keys():
2823 Nsite = NdsTdef[defect_name] * defects.V * 1.0e-24
2824 mprint(f" {defect_name:>8}: {NdsTdef[defect_name]:12.6g} cm-3 ({Nsite:8.2f} sites)", fp = outfp)
2825
2826# At T0, EF,eq(T0)
2827 EFmin_ini = EV + dEFmin
2828 EFmax_ini = EC + dEFmax
2829 mprint("", fp = outfp)
2830 if T0 <= Tdef:
2831 mprint("Calculate electron distribution at T(electron) = {} K".format(T0))
2832 mprint(" Total defect densities are frozen at {} K with EF = {:12.4f} eV".format(Tdef, EFeqTdef), fp = outfp)
2833 for key in NdsTdef.keys():
2834 Nsite = NdsTdef[key] * defects.V * 1.0e-24
2835 mprint(" {:>8}: {:12.6g} cm-3 ({:8.2f} sites)".format(key, NdsTdef[key], Nsite), fp = outfp)
2836 else:
2837 mprint("T(electron) is higher than T(defects). Defect densities are calculated at T(electron)")
2838 mprint("Calculate electron distribution at T(electron) = {} K, T(defects) = {} K".format(T0, T0))
2839
2840 mprint(" Calculate EF,eq: Bisecton method: eps = {}, nmaxiter = {}".format(eps_bisec, nmaxiter_bisection), fp = outfp)
2841 mprint(" Initial EF range: {:12.4f} - {:12.4f} eV".format(EFmin_ini, EFmax_ini))
2842 mprint(" Newton method : eps = {}, nmaxiter = {}, dump = {}".format(eps_newton, nmaxiter_newton, dump_newton), fp = outfp)
2843
2844 EFeqT0, dQ = defects.find_EF(T0, Tdef, NdsTdef, ECmin, ECmax, EVmin, EVmax,
2845 callbackfunc = callbackfunc,
2846 eps_bisec = eps_bisec, nmaxiter_bisection = nmaxiter_bisection, initial = [EFmin_ini, EFmax_ini],
2847 eps_newton = eps_newton, nmaxiter_newton = nmaxiter_newton, dump_newton = dump_newton,
2848 h_newton = h_newton
2849 )
2850 if EFeqT0 is None:
2851 app.terminate("Error in tkDefects.find_EF: Could not reach convergence for EF", pause = True)
2852
2853 mprint("", fp = outfp)
2854 mprint(" EF,eq({:8.3g} K)={:12.6g} eV".format(T0, EFeqT0), fp = outfp)
2855 Z, ne, nh, Nds, NdsTdef, dGs, Qtot = defects.calculate_densities(T0, Tdef, NdsTdef, EFeqT0, ECmin, ECmax, EVmin, EVmax)
2856 for id in range(defects.ndefects):
2857 d = defects.defects[id]
2858 label = "{}_{}".format(d.atom, d.site)
2859 labelq = "{}^{}".format(label, d.charge)
2860 mprint(f" {labelq:>16} (Nds(tot)={NdsTdef[labelq]:12.6g} cm-3):", end = '', fp = outfp)
2861# mprint(f" {labelq:>16} (Nds(tot)={NdsTdef[label]:12.6g} cm-3):", end = '', fp = outfp)
2862 mprint(f" Nds={Nds[id]:12.6g} cm-3 dGs={dGs[id]:12.4g} eV", fp = outfp)
2863
2864# At T0, various EF
2865 outwb = tkExcel(NEF_xlsx, 'w')
2866
2867 xEF = [EFmin + i * EFstep for i in range(nEF)]
2868 mprint("", fp = outfp)
2869 mprint("Calculate defect and carrier densities at {} K at {} point as functions of EF:"
2870 .format(T0, defects.plabels[iPoint]), fp = outfp)
2871 if T0 <= Tdef:
2872 mprint(" T(electron) is lower than T(defects).", fp = outfp)
2873 mprint(" Total defect densities frozen at {} K with EF = {:12.4f} eV".format(Tdef, EFeqTdef), fp = outfp)
2874 for key in NdsTdef.keys():
2875 Nsite = NdsTdef[key] * defects.V * 1.0e-24
2876 mprint(" {:>8}: {:12.6g} cm-3 ({:8.2f} sites)".format(key, NdsTdef[key], Nsite), fp = outfp)
2877 else:
2878 mprint("T(electron) is higher than T(defects). Defects are not frozen")
2879 mprint("Calculate defect densities at T(electron) = {} K, T(defects) = {} K".format(T0, T0))
2880
2881 mprint("{:8}: {:12} {:12}".format("EF(eV)", "ne(cm^-3)", "nh(cm^-3)"), end = '', fp = outfp)
2882 outwb.Print(["EF(eV)", "ne(cm^-3)", "nh(cm^-3)"], end = '')
2883 for id in range(defects.ndefects):
2884 d = defects.defects[id]
2885 label = "{}({})".format(d.name, d.charge)
2886 mprint(" {:12}".format(label), end = '', fp = outfp)
2887 outwb.Print(["{}".format(label)], end = '')
2888 mprint(" {:12}".format('dQ'), fp = outfp)
2889 outwb.Print(['dQ'])
2890
2891 ydQ = []
2892 ylogdQ = []
2893 xEF_dQp = []
2894 xEF_dQm = []
2895 ylogdQp = []
2896 ylogdQm = []
2897 yne = []
2898 ynh = []
2899 ynds = []
2900 for id in range(defects.ndefects):
2901 ynds.append([0.0]*nEF)
2902
2903 for i in range(nEF):
2904 EF = xEF[i]
2905 Z, ne, nh, Nds, NdsTdef, dGs, Qtot = defects.calculate_densities(T0, Tdef, NdsTdef, EF, ECmin, ECmax, EVmin, EVmax)
2906 for id in range(defects.ndefects):
2907 ynds[id][i] = Nds[id]
2908
2909 ydQ.append(Qtot)
2910 yne.append(ne)
2911 ynh.append(nh)
2912
2913# Make an array for plotting log(|dQ|) - EF
2914 if Qtot > 0.0:
2915 logdQ = log(Qtot) / log10
2916 ylogdQ.append(logdQ)
2917 xEF_dQp.append(EF)
2918 ylogdQp.append(logdQ)
2919 elif Qtot < 0.0:
2920 logdQ = -log(-Qtot) / log10
2921 ylogdQ.append(logdQ)
2922 xEF_dQm.append(EF)
2923 ylogdQm.append(logdQ)
2924 else:
2925 ylogdQ.append(0.0)
2926 xEF_dQp.append(0.0)
2927 ylogdQp.append(0.0)
2928
2929 mprint(f"{EF:8.4g}: {ne:12.4g} {nh:12.4g}", end = '', fp = outfp)
2930 outwb.Print([EF, ne, nh], end = '')
2931 for id in range(defects.ndefects):
2932 mprint(" {:12.4g}".format(Nds[id]), end = '', fp = outfp)
2933 outwb.Print([Nds[id]], end = '')
2934 mprint(" {:12.4g}".format(Qtot), fp = outfp)
2935 outwb.Print([Qtot])
2936
2937 outwb.Close()
2938
2939# mprint("", fp = outfp)
2940# mprint("*** Open [{}] to save N-EF data".format(NEF_xlsx))
2941# for i in range(len(ylogdQ)):
2942# print(f"{xEF[i]:10.4g} {ydQ[i]:10.4g} {ylogdQ[i]:10.4g}")
2943
2944 mprint("", fp = outfp)
2945 mprint("*** Open [{}] to save dH-EF data (all)".format(dHEF_all_xlsx))
2946 mprint("*** Open [{}] to save dH-EF data (min)".format(dHEF_min_xlsx))
2947 allwb = tkExcel(dHEF_all_xlsx, 'w')
2948 minwb = tkExcel(dHEF_min_xlsx, 'w')
2949 for id in range(defects.ndefects):
2950 d = defects.defects[id]
2951 name = d.name
2952 q = d.charge
2953 allwb.Print([name, q])
2954 allwb.Print(["EF(eV)", "dH(eV)"])
2955 allwb.Print([EFmin, d.dH0s[iPoint] + q * EFmin])
2956 allwb.Print([EFmax, d.dH0s[iPoint] + q * EFmax])
2957 allwb.Close()
2958 minwb.Close()
2959
2960#=============================
2961# Plot graphs
2962#=============================
2963 fig = plt.figure(figsize = (12, 8))
2964 plot_event = tkPlotEvent(plt)
2965
2966 axdos = fig.add_subplot(2, 2, 1)
2967 axdQ = fig.add_subplot(2, 2, 3)
2968 axdH = fig.add_subplot(2, 2, 2)
2969 axdN = fig.add_subplot(2, 2, 4)
2970
2971# plot_event初期化
2972 root = get_window_from_plt(plt)
2973 plot_event = tkPlotEvent(plt)
2974 plot_event.prepare_annotation()
2975 plot_event.prepare_move_text(fig)
2976 plot_event.prepare_popup_menu(fig, parent = root)
2977 popup_menu = plot_event.popup_menu.menu
2978
2979# selector0 = RangeSelector('', ax1, color = 'green', print_level = 0)
2980# selector1 = RangeSelector('', ax2, color = 'blue', print_level = 0)
2981
2982 vars = tkParams() #cparams.copy()
2983 vars.caller = "EF"
2984 vars.plot_event = plot_event
2985# vars.selector0 = selector0
2986# vars.selector1 = selector1
2987
2988 if len(app.config.plugin_dir) >= 1 \
2989 and (app.config.plugin_dir[0] != "/" and app.config.plugin_dir[0] != "\\" and app.config.plugin_dir[2] != "\\"):
2990 plugin_dir = app.replace_path(None, template = ["{dirname}", app.config.plugin_dir])
2991 else:
2992 plugin_dir = app.config.plugin_dir
2993
2994 print()
2995 print(f"Read plugins from : {plugin_dir}")
2996 module_names, modules = app.load_modules(plugin_dir, "*.py", target = "popup_menu", sort = True, is_print = True)
2997 for m in modules:
2998 if hasattr(m, "add_popup_menu"):
2999 m.add_popup_menu(popup_menu, app = app, vars = vars, parent = root)
3000
3001 axdos.set_title("{}, for Point {}".format(dH_path, defects.plabels[iPoint]))
3002 axdos.plot(E_raw, dos_raw, label = 'DOS(EF={:.3} eV)'.format(EF0), picker = True, color = 'cyan', linewidth =0.3)
3003 axdos.plot(E_norm, dos_raw, label = 'DOS(EV=0)', picker = True, color = 'black', linewidth =1.0)
3004 yrange = [min(dos_raw), max(dos_raw)]
3005 axdos.plot([EV, EV], yrange, label = '$E_V$', color = 'blue', linestyle = 'dashed', linewidth = 0.5)
3006 axdos.plot([EC, EC], yrange, label = '$E_C$', color = 'blue', linestyle = 'dashed', linewidth = 0.5)
3007 axdos.plot([EFeqT0, EFeqT0], yrange, label = '$E_{F,eq}$(T0)', color = 'red', linestyle = 'dashed', linewidth = 1.0)
3008 axdos.plot([EFeqTdef, EFeqTdef], yrange, label = '$E_{F,eq}$(Tdef)', color = 'green', linestyle = 'dashed', linewidth = 1.0)
3009# axdos.set_xlim([EFmin, EFmax])
3010 xmin = min([EFmin, view_Emin])
3011 xmax = max([EFmax, view_Emax])
3012 axdos.set_xlim([xmin, xmax])
3013 ymin, ymax = minmax_xy(x = E_raw, y = dos_raw, xmin = xmin, xmax = xmax)
3014 axdos.set_ylim([0.0, ymax])
3015 axdos.set_xlabel("$E$, $E - E_V$ (eV)")
3016 axdos.set_ylabel("DOS (states/cm$^3$)")
3017 _legend = axdos.legend()
3018 _legend.set_draggable(True)
3019
3020# axdQ.plot(xEF, ylogdQ, label = 'log$_{10}$|$\Delta$$Q$|', picker = True, marker = 'o', markersize = 2.0)
3021 axdQ.plot(xEF_dQp, ylogdQp, label = 'log$_{10}$|$\Delta$$Q$| (dQ >= 0)', picker = True, marker = 'o', markersize = 2.0, markeredgecolor = 'blue', markerfacecolor = 'blue')
3022 axdQ.plot(xEF_dQm, ylogdQm, label = 'log$_{10}$|$\Delta$$Q$| (dQ < 0)', picker = True, marker = 'o', markersize = 2.0, markeredgecolor = 'red', markerfacecolor = 'red')
3023 axdQ.plot(axdQ.get_xlim(), [0.0, 0.0], color = 'red', linestyle = '-', linewidth = 1.0)
3024 yrange = axdQ.get_ylim()
3025 axdQ.plot([EV, EV], yrange, label = '$E_V$', color = 'blue', linestyle = 'dashed', linewidth = 0.5)
3026 axdQ.plot([EC, EC], yrange, label = '$E_C$', color = 'blue', linestyle = 'dashed', linewidth = 0.5)
3027 axdQ.plot([EFeqT0, EFeqT0], yrange, label = '$E_{F,eq}$(T0)', color = 'red', linestyle = 'dashed', linewidth = 1.0)
3028 axdQ.plot([EFeqTdef, EFeqTdef], yrange, label = '$E_{F,eq}$(Tdef)', color = 'green', linestyle = 'dashed', linewidth = 1.0)
3029 axdQ.set_xlabel("$E_F - E_V$ (eV)")
3030 axdQ.set_ylabel("log$_{10}$ |$\Delta$$Q$ / $e$|")
3031 axdQ.set_xlim([EFmin, EFmax])
3032# axdQ.set_ylim([-1.0e19, 1.0e19])
3033 _legend = axdQ.legend()
3034 _legend.set_draggable(True)
3035
3036 alpha = 0.8
3037 names = defects.get_names()
3038 ymin = 1.0e300
3039 ymax = -1.0e300
3040 for iname, name in enumerate(names):
3041 groupdata = defects.get_groupdata(name, iPoint)
3042 allpts, minpts = defects.find_all_transitions(name, groupdata, EFmin, EFmax)
3043 for pts in minpts:
3044 ymin = min([ymin, pts[0], pts[1]])
3045 ymax = max([ymax, pts[0], pts[1]])
3046
3047 yrange = [ymin, min(ymax, view_dHmax)]
3048 if dHmin is not None: yrange[0] = dHmin
3049 if dHmax is not None: yrange[1] = dHmax
3050 def plot_dH_EF(axdH):
3051 axdH.set_title('$E_{F,eq}$: ' + "{:8.3g} eV(Tdef={} K)".format(EFeqTdef, Tdef)
3052 + " {:8.3g} eV(T0={} K)".format(EFeqT0, T0))
3053
3054 for iname, name in enumerate(names):
3055 color = colors[iname % len(colors)]
3056 args = {'color': color, 'markersize': 3.0, 'markeredgewidth': 0, 'markerfacecolor': color }
3057
3058 groupdata = defects.get_groupdata(name, iPoint)
3059 ngroupdata = len(groupdata)
3060
3061 minwb.Print([name])
3062 minwb.Print(["EF(eV)", "dH(eV)", "q(-)", "q(+)"])
3063
3064 allpts, minpts = defects.find_all_transitions(name, groupdata, EFmin, EFmax)
3065
3066 min_x = []
3067 min_y = []
3068 for ipt in range(len(minpts)):
3069 min_x.append(minpts[ipt][0])
3070 min_y.append(minpts[ipt][1])
3071
3072 label = name
3073 largs = { 'label': label }
3074 line, = axdH.plot(min_x, min_y, linestyle = '-', marker = 'o', **args, **largs)
3075# axdH.plot(min_x, min_y, picker = True, linestyle = '-', marker = 'o', **args, **largs)
3076
3077 format = "{label}: line#{iline} data#{idata}:\n EF - EV={x_list:8.3g} eV dH={y_list:8.3g} eV"
3078 plot_event.annotation.add_line(label, axdH, axdH, min_x, min_y, line,
3079 inf_list = {"x_label": "EF - EV (eV)", "y_label": "dH (eV)", "x_list": min_x, "y_list": min_y,},
3080 annotation_format = format, inf_format = format)
3081
3082 plot_event.move_text.add_annotation(axdH, axdH, x_list = min_x, y_list = min_y,
3083 frac = None, ylim = yrange,
3084 text = name, fontsize = 10, ha = 'right', va = 'center', color = color, alpha = alpha, fc = "w", ec = "none")
3085
3086 for id in range(0, ngroupdata):
3087 d = groupdata[id]
3088 name = d["name"]
3089 dH0 = d["dH0"]
3090 q = d["charge"]
3091 if plotall:
3092 label = f"{name} (dashed line)"
3093 line, = axdH.plot([EFmin, EFmax], [dH0 + q * EFmin, dH0 + q * EFmax], linestyle = 'dashed', linewidth = 0.5, **args)
3094 plot_event.annotation.add_line(label, axdH, axdH, min_x, min_y, line,
3095 inf_list = {"x_label": "EF - EV (eV)", "y_label": "dH (eV)", "x_list": min_x, "y_list": min_y,},
3096 annotation_format = format, inf_format = format)
3097
3098 axdH.plot([EV, EV], yrange, color = 'blue', linestyle = 'dashed', linewidth = 0.5)
3099 axdH.plot([EC, EC], yrange, color = 'blue', linestyle = 'dashed', linewidth = 0.5)
3100 axdH.plot([EFeqT0, EFeqT0], yrange, label = '$E_{F,eq}$(T0)', color = 'red', linestyle = 'dashed', linewidth = 1.0)
3101 axdH.plot([EFeqTdef, EFeqTdef], yrange, label = '$E_{F,eq}$(Tdef)', color = 'green', linestyle = 'dashed', linewidth = 1.0)
3102 axdH.set_xlim([EFmin, EFmax])
3103 axdH.set_ylim(yrange)
3104 axdH.set_xlabel("$E_F - E_V$ (eV)")
3105 axdH.set_ylabel("$\Delta$$H$ (eV)")
3106 _legend = axdH.legend(bbox_to_anchor=(1.05, 1.0), loc='upper left', borderaxespad = 0, fontsize = legend_fontsize)
3107 _legend.set_draggable(True)
3108
3109 plot_dH_EF(axdH)
3110
3111
3112 format = "{label}: line#{iline} data#{idata}:\n EF - EV={x_list:8.3g} eV {x_label}={y_list:8.3g} 1/cm3"
3113 label = '$N_e$'
3114 line, = axdN.plot(xEF, yne, label = label, picker = True, color = 'red', linestyle = '-', linewidth = 1.0)
3115 plot_event.annotation.add_line(label, axdN, axdN, xEF, yne, line,
3116 inf_list = {"x_label": "EF - EV (eV)", "y_label": label, "x_list": xEF, "y_list": yne},
3117 annotation_format = format, inf_format = format)
3118 plot_event.move_text.add_annotation(axdN, axdN, x_list = xEF, y_list = yne,
3119 frac = None, xlim = [EFmin, EFmax], ylim = [view_Nmin, 1.0e23],
3120 text = label, fontsize = 10, ha = 'right', va = 'center', color = 'red', alpha = alpha, fc = "w", ec = "none")
3121 label = '$N_h$'
3122 line, axdN.plot(xEF, ynh, label = label, picker = True, color = 'blue', linestyle = '-', linewidth = 1.0)
3123 plot_event.annotation.add_line(label, axdN, axdN, xEF, ynh, line,
3124 inf_list = {"x_label": "EF - EV (eV)", "y_label": label, "x_list": xEF, "y_list": ynh},
3125 annotation_format = format, inf_format = format)
3126 plot_event.move_text.add_annotation(axdN, axdN, x_list = xEF, y_list = ynh,
3127 frac = None, xlim = [EFmin, EFmax], ylim = [view_Nmin, 1.0e23],
3128 text = label, fontsize = 10, ha = 'right', va = 'center', color = 'blue', alpha = alpha, fc = "w", ec = "none")
3129 ncolor = len(colors)
3130 for id in range(len(ynds)):
3131 d = defects.defects[id]
3132 ymax = max(ynds[id])
3133 if ymax < view_Nmin:
3134 continue
3135
3136 color = colors[id % ncolor]
3137 label = f"{d.name}({d.charge})"
3138 line, axdN.plot(xEF, ynds[id], label = label, picker = True, linestyle = 'dashed', linewidth = 1.0, color = color)
3139
3140 plot_event.annotation.add_line(label, axdN, axdN, xEF, ynds[id], line,
3141 inf_list = {"x_label": "EF - EV (eV)", "y_label": label, "x_list": xEF, "y_list": ynds[id]},
3142 annotation_format = format, inf_format = format)
3143
3144 plot_event.move_text.add_annotation(axdN, axdN, x_list = xEF, y_list = ynds[id],
3145 frac = None, xlim = [EFmin, EFmax], ylim = [view_Nmin, 1.0e23],
3146 text = label, fontsize = 10, ha = 'right', va = 'center', color = color, alpha = alpha, fc = "w", ec = "none")
3147
3148 yrange = axdN.get_ylim()
3149 yrange = [yrange[0], yrange[1] * 10.0]
3150 axdN.plot([EV, EV], yrange, color = 'blue', linestyle = 'dashed', linewidth = 0.5)
3151 axdN.plot([EC, EC], yrange, color = 'blue', linestyle = 'dashed', linewidth = 0.5)
3152 axdN.plot([EFeqT0, EFeqT0], yrange, label = '$E_{F,eq}$(T0)', color = 'red', linestyle = 'dashed', linewidth = 1.0)
3153 axdN.plot([EFeqTdef, EFeqTdef], yrange, label = '$E_{F,eq}$(Tdef)', color = 'green', linestyle = 'dashed', linewidth = 1.0)
3154 axdN.set_xlabel("$E_F - E_V$ (eV)")
3155 axdN.set_ylabel("$N$ (cm$^{-3}$)")
3156 axdN.set_yscale('log')
3157 axdN.set_xlim([EFmin, EFmax])
3158 axdN.set_ylim([view_Nmin, 1.0e23])
3159# axdN.legend()
3160 _legend = axdN.legend(bbox_to_anchor=(1.05, 1.0), loc='upper left', borderaxespad = 0, fontsize = legend_fontsize)
3161 _legend.set_draggable(True)
3162
3163# Rearange the graph axes so that they are not overlapped
3164 plt.tight_layout()
3165
3166 def rescale(ax: object, EFmin: float, EFmax: float, dHmin: float, dHmax: float):
3167 """
3168 概要:
3169 グラフの軸のスケールを再設定します。
3170 引数:
3171 :param ax: スケールを再設定するAxesオブジェクト。
3172 :type ax: object
3173 :param EFmin: EF軸の最小値 (eV)。
3174 :type EFmin: float
3175 :param EFmax: EF軸の最大値 (eV)。
3176 :type EFmax: float
3177 :param dHmin: dH軸の最小値 (eV)。
3178 :type dHmin: float
3179 :param dHmax: dH軸の最大値 (eV)。
3180 :type dHmax: float
3181 """
3182# ax.cla()
3183# plot_dH_EF(ax)
3184 ax.set_xlim([EFmin, EFmax])
3185 ax.set_ylim([dHmin, dHmax])
3186# plt.draw()
3187 plt.pause(1.0e-4)
3188
3189 vars.scale_callback = rescale
3190 vars.dH_axis = axdH
3191 EFlim = axdH.get_xlim()
3192 dHlim = axdH.get_ylim()
3193 vars.EF_min = EFlim[0]
3194 vars.EF_max = EFlim[1]
3195 vars.dH_min = dHlim[0]
3196 vars.dH_max = dHlim[1]
3197 plot_event.register_annotation_event(fig, activate = False, print_level = 0)
3198 plot_event.register_move_text_event(fig, activate = False)
3199 plot_event.register_popup_menu_event()
3200# plot_event.register_pick(fig) # callback = lambda event: plot_event.onclick(event))
3201
3202
3203 plt.pause(1.0e-4)
3204
3205 app.terminate("", pause = True) #usage = usage, pause = True)
3206
3207 if outfp:
3208 outfp.close()
3209
3210def main():
3211 """
3212 概要:
3213 スクリプトのメイン実行関数。
3214 詳細説明:
3215 アプリケーションの設定を読み込み、コマンドライン引数を解析してグローバル変数を更新します。
3216 ログファイルを初期化し、指定されたモードに応じてフェルミ準位依存性または
3217 温度依存性の計算とプロットを実行します。不正なモードが指定された場合はエラーで終了します。
3218 """
3219 app.config.plugin_dir = app.replace_path(None, template = ["{dirname}", "plugin/vasp_defect"])
3220 appconfig_path, config = app.read_app_config(print_level = 1)
3221
3222 updatevars()
3223
3224 vasp = tkVASP()
3225 base_path = vasp.getdir(CAR_path)
3226 logfile = app.replace_path(dH_path, template = ["{dirname}", "{filebody}-defect-out.txt"])
3227# logfile = os.path.join(base_path, 'vasp_defect-out.txt')
3228 print("")
3229 print(f"Open logfile [{logfile}]")
3230 app.redirect(targets = ["stdout", logfile], mode = 'w')
3231
3232 if mode == 'EF':
3233 exec_EF()
3234 elif mode == 'T':
3235 exec_T()
3236 else:
3237 app.terminate("Error: Invalid mode [{}]".format(mode), usage = usage, pause = True)
3238
3239
3240if __name__ == '__main__':
3241 main()