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

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()