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

H1s_HF_LDA.py をダウンロード

H1s_HF_LDA.py
H1s_HF_LDA.py
   1"""
   2概要:
   3H-like 1s軌道エネルギーレベル計算モジュール。
   4詳細説明:
   5このモジュールは、H-like 1s軌道のエネルギーレベルを計算するために、1s動径関数とスレーターのX-αポテンシャルを使用します。
   6ハートリー・フォック・スレーター(HFS)法の基本的な概念に基づき、電子間の相互作用を交換ポテンシャルで近似して扱います。
   7動径関数の指数係数kaや電子数Neなどを変分法で最適化し、全エネルギーを最小化する計算も可能です。
   8"""
   9import os
  10import sys
  11from math import exp, sqrt
  12import numpy as np
  13from math import log, exp
  14from numpy import arange
  15from scipy import integrate         # 数値積分関数 integrateを読み込む
  16from scipy.interpolate import interp1d
  17from scipy import optimize
  18from scipy.optimize import minimize
  19from matplotlib import pyplot as plt
  20
  21
  22# constants
  23pi   = 3.14159265358979323846
  24h    = 6.6260755e-34    # Js";
  25hbar = 1.05459e-34      # "Js";
  26c    = 2.99792458e8     # m/s";
  27e    = 1.60218e-19      # C";
  28kB   = 1.380658e-23     # JK<sup>-1</sup>";
  29me   = 9.1093897e-31    # kg";
  30e0          = 8.854418782e-12; # C<sup>2</sup>N<sup>-1</sup>m<sup>-2</sup>";
  31e2_4pie0    = 2.30711e-28      # Nm<sup>2</sup>";
  32a0          = 5.29177e-11 * 1.0e10      # A
  33HartreeToeV = 27.2116          # eV";
  34RyToeV      = HartreeToeV / 2.0
  35pi2 = pi + pi
  36pi4 = pi2 + pi2
  37
  38
  39# mode: 'd': debug mode, plot fundamental graphs
  40# 'g': plot graph
  41# 'k': sweep ka, 'n': sweep Ne
  42# 'v': add variational calculation, 'e': energy-based output
  43mode = 'k'
  44ELabel = 'E 1s'
  45
  46# Nuclear and orbital parameters
  47Z  = 1.0
  48n  = 1
  49l  = 0
  50m  = 0
  51ka = 1.0   # coefficient of the exponent in R1s
  52Ne = 1.0  
  53
  54# Number of electrons, Slater's alpha
  55alpha = 2.0 / 3.0
  56
  57# Radius range and integration (quad()) parameters
  58Rmin  =  0.0
  59Rmax  = 20.0
  60nR    = 2001
  61Rstep = None
  62Rmaxdata = None
  63nmaxdiv = 40
  64epsR = 1.0e-4
  65eps = 1.0e-8
  66
  67# Ne mesh to calculate 1st and 2nd derivative of Etot
  68hparab = 0.01
  69
  70# iteration parameters
  71#Nearray = [1.0, 0.8, 0.6]
  72#Nearray = [1.0, 0.8, 0.6, 0.5, 0.4, 0.3, 0.1, 0.01, 0.001]
  73Nearray = [1.0, 0.9, 0.8, 0.7, 0.6, 0.5, 0.4, 0.3, 0.2, 0.1, 0.07, 0.05, 0.04, 0.03, 0.02, 0.01, 1.0e-3, 1.0e-4, 1.0e-5, 1.0e-6]
  74kaarray = [0.5, 0.6, 0.65, 0.7, 0.8, 0.9, 0.95, 1.0, 1.05, 1.1, 1.2, 1.4]
  75
  76#=============================================
  77# scipy.optimize.minimizeで使うアルゴリズム
  78#=============================================
  79#nelder-mead Downhill simplex
  80#powell Modified Powell
  81#cg conjugate gradient (Polak-Ribiere method)
  82#bfgs BFGS法
  83#newton-cg Newton-CG
  84#trust-ncg 信頼領域 Newton-CG 法
  85#dogleg 信頼領域 dog-leg 法
  86#L-BFGS-B’ (see here)
  87#TNC’ (see here)
  88#COBYLA’ (see here)
  89#SLSQP’ (see here)
  90#trust-constr’(see here)
  91#dogleg’ (see here)
  92#trust-exact’ (see here)
  93#trust-krylov’ (see here)
  94method = "nelder-mead"
  95#method = 'cg'
  96#method = 'powell'
  97#method = 'bfgs'
  98
  99maxiter = 1000
 100tol    = 1.0e-3
 101h_diff = 1.0e-3
 102
 103def pfloat(str):
 104    """
 105    概要: 文字列を浮動小数点数に安全に変換します。
 106
 107    詳細説明:
 108    標準のfloat()関数と異なり、変換に失敗した場合でもエラーを発生させずにNoneを返します。
 109    これにより、不正な入力があってもプログラムが中断されるのを防ぎます。
 110
 111    :param str: 変換する文字列。
 112    :type str: str
 113    :returns: 変換された浮動小数点数、または変換に失敗した場合はNone。
 114    :rtype: float or None
 115    """
 116    try:
 117        return float(str)
 118    except:
 119        return None
 120
 121def pint(str):
 122    """
 123    概要: 文字列を整数に安全に変換します。
 124
 125    詳細説明:
 126    標準のint()関数と異なり、変換に失敗した場合でもエラーを発生させずにNoneを返します。
 127    これにより、不正な入力があってもプログラムが中断されるのを防ぎます。
 128
 129    :param str: 変換する文字列。
 130    :type str: str
 131    :returns: 変換された整数、または変換に失敗した場合はNone。
 132    :rtype: int or None
 133    """
 134    try:
 135        return int(str)
 136    except:
 137        return None
 138
 139def getarg(position, defval = None):
 140    """
 141    概要: コマンドライン引数を安全に取得します。
 142
 143    詳細説明:
 144    sys.argvリストから指定された位置の引数を取得します。
 145    指定された位置が存在しない場合、エラーを発生させずにデフォルト値を返します。
 146
 147    :param position: 取得する引数のsys.argv内でのインデックス。
 148    :type position: int
 149    :param defval: 引数が存在しない場合に返すデフォルト値。デフォルトはNone。
 150    :type defval: any, optional
 151    :returns: 指定された位置の引数、または引数が存在しない場合はdefval。
 152    :rtype: str or any
 153    """
 154    try:
 155        return sys.argv[position]
 156    except:
 157        return defval
 158
 159def getfloatarg(position, defval = None):
 160    """
 161    概要: コマンドライン引数を浮動小数点数として安全に取得します。
 162
 163    詳細説明:
 164    指定された位置のコマンドライン引数を取得し、pfloat()関数を使用して浮動小数点数に変換します。
 165    引数が存在しない場合や変換に失敗した場合、エラーを発生させずにデフォルト値を返します。
 166
 167    :param position: 取得する引数のsys.argv内でのインデックス。
 168    :type position: int
 169    :param defval: 引数が存在しない場合や変換に失敗した場合に返すデフォルト値。デフォルトはNone。
 170    :type defval: float or None, optional
 171    :returns: 変換された浮動小数点数、または失敗した場合はdefval。
 172    :rtype: float or None
 173    """
 174    return pfloat(getarg(position, defval))
 175
 176def getintarg(position, defval = None):
 177    """
 178    概要: コマンドライン引数を整数として安全に取得します。
 179
 180    詳細説明:
 181    指定された位置のコマンドライン引数を取得し、pint()関数を使用して整数に変換します。
 182    引数が存在しない場合や変換に失敗した場合、エラーを発生させずにデフォルト値を返します。
 183
 184    :param position: 取得する引数のsys.argv内でのインデックス。
 185    :type position: int
 186    :param defval: 引数が存在しない場合や変換に失敗した場合に返すデフォルト値。デフォルトはNone。
 187    :type defval: int or None, optional
 188    :returns: 変換された整数、または失敗した場合はdefval。
 189    :rtype: int or None
 190    """
 191    return pint(getarg(position, defval))
 192
 193def usage(ka = ka, Z = Z, n = n, l = l, m = m):
 194    """
 195    概要: プログラムの使用方法を表示します。
 196
 197    詳細説明:
 198    コマンドライン引数の構文と利用可能なモードオプションをユーザーに示します。
 199    デフォルト値のka, Z, n, l, mは現在のグローバル変数から取得されますが、
 200    直接引数として渡すことも可能です。
 201
 202    :param ka: 1s動径関数の指数部の係数。デフォルトはグローバル変数ka。
 203    :type ka: float, optional
 204    :param Z: 原子番号。デフォルトはグローバル変数Z。
 205    :type Z: float, optional
 206    :param n: 主量子数。デフォルトはグローバル変数n。
 207    :type n: int, optional
 208    :param l: 方位量子数。デフォルトはグローバル変数l。
 209    :type l: int, optional
 210    :param m: 磁気量子数。デフォルトはグローバル変数m。
 211    :type m: int, optional
 212    :returns: なし
 213    :rtype: None
 214    """
 215    print("")
 216    print("Usage: Variables in () are optional")
 217    print("  (i) python {} mode Z ka Ne".format(sys.argv[0]))
 218    print("      mode: combination of the following key characters")
 219    print("               d: debug mode, plot fundamental graphs")
 220    print("               v: add variational calculations")
 221    print("               e: output based on energy (default: based on E 1s eigen value)")
 222    print("               k: sweep ka")
 223    print("               n: sweep Ne")
 224    print("               g : plot graph")
 225    print("     ex: python {} nvg {} {} {}".format(sys.argv[0], Z, ka, Ne))
 226    print("     ex: python {} k {} {} {}".format(sys.argv[0], Z, ka, Ne))
 227
 228def terminate(message = None):
 229    """
 230    概要: メッセージを表示し、プログラムを終了します。
 231
 232    詳細説明:
 233    オプションで終了メッセージを表示し、usage()関数を呼び出して使用方法を提示した後、
 234    プログラムを終了します。
 235
 236    :param message: 終了時に表示するメッセージ。デフォルトはNone。
 237    :type message: str, optional
 238    :returns: なし (プログラムが終了するため)
 239    :rtype: None
 240    """
 241    if message is not None:
 242        print("")
 243        print(message)
 244
 245    usage()
 246    print("")
 247    exit()
 248
 249def updatevars():
 250    """
 251    概要: コマンドライン引数に基づいてグローバル変数を更新します。
 252
 253    詳細説明:
 254    sys.argvからプログラム起動時の引数を取得し、mode, ELabel, ka, Z, Ne
 255    などのグローバル変数を更新します。これにより、ユーザーはコマンドラインから計算パラメータを
 256    指定できます。ELabelはmodeに'e'が含まれるかどうかに応じて設定されます。
 257
 258    :returns: なし
 259    :rtype: None
 260    """
 261    global mode, ELabel
 262    global ka, Z, Ne, n, l, m, Ne, Rmax, Rstep
 263
 264    argv = sys.argv
 265    if len(argv) == 1:
 266        terminate()
 267
 268    mode = getarg( 1, mode)
 269    Z  = getfloatarg( 2, Z)
 270    ka = getfloatarg( 3, ka)
 271    Ne = getfloatarg( 4, Ne)
 272    
 273    if 'e' in mode:
 274        ELabel = 'Etot'
 275    else:
 276        ELabel = 'E 1s'
 277    
 278# Total charge inside r
 279yRr = []  # Raidal distributuion function
 280yQr = []  # Integrated charge inside r: integ_rm(4pi * rm * rm * rho(rm))_[rm = 0, r]
 281
 282def Rr(ka, Z, n, l, m, r):
 283    """
 284    概要: H-like 1s軌道の動径関数 R_nl(r) を計算します。
 285
 286    詳細説明:
 287    この関数は、水素様原子の1s軌道に対する動径関数を計算します。
 288    指数係数 ka と原子番号 Z に依存します。
 289    正規化条件は integ(4pi*r*r*Rr(r)*Rr(r))[r=0, inf] = 1 です。
 290    内部でグローバル変数 R1s0 を使用しますが、この変数はこのスクリプト内で定義されていません。
 291
 292    :param ka: 動径関数の指数部分の係数。
 293    :type ka: float
 294    :param Z: 原子番号。
 295    :type Z: float
 296    :param n: 主量子数 (1s軌道なので常に1)。
 297    :type n: int
 298    :param l: 方位量子数 (1s軌道なので常に0)。
 299    :type l: int
 300    :param m: 磁気量子数 (1s軌道なので常に0)。
 301    :type m: int
 302    :param r: 核からの距離。
 303    :type r: float
 304    :returns: 指定された距離におけるH-like 1s軌道の動径関数の値。
 305    :rtype: float
 306    """
 307    global R1s0
 308
 309    return R1s0 * pow(ka * Z, 1.5) * exp(-ka * Z * r)
 310
 311def rho(ka, Z, n, l, m, r):
 312    """
 313    概要: H-like 1s軌道の電子密度関数 ρ(r) を計算します。
 314
 315    詳細説明:
 316    動径関数 Rr(r) の二乗として電子密度を計算します。
 317    ρ(r) = Rr(r)^2 です。
 318
 319    :param ka: 動径関数の指数部分の係数。
 320    :type ka: float
 321    :param Z: 原子番号。
 322    :type Z: float
 323    :param n: 主量子数 (1s軌道なので常に1)。
 324    :type n: int
 325    :param l: 方位量子数 (1s軌道なので常に0)。
 326    :type l: int
 327    :param m: 磁気量子数 (1s軌道なので常に0)。
 328    :type m: int
 329    :param r: 核からの距離。
 330    :type r: float
 331    :returns: 指定された距離における電子密度 ρ(r) の値。
 332    :rtype: float
 333    """
 334    psi = Rr(ka, Z, n, l, m, r)
 335    return psi * psi
 336
 337def calQ(R):
 338    """
 339    概要: 核からの距離Rの内側に存在する総電荷 (Q(R)) を計算します。
 340
 341    詳細説明:
 342    0からRまでの範囲で電子密度 ρ(r) を積分することにより、
 343    距離Rの内側の電子が形成する電荷を求めます。
 344    この関数は build_Qr で構築された補間関数 qfunc を使用します。
 345    qfuncが未構築の場合や、指定されたRが有効範囲外の場合はエラー処理または0を返します。
 346
 347    :param R: 核からの距離。
 348    :type R: float
 349    :returns: 距離Rの内側に存在する総電荷。
 350    :rtype: float
 351    """
 352    global Rmin, Rmaxdata, r, Qr
 353
 354    if R < Rmin:
 355        print("Error in Q(r): Given r={} exceeds the R range [{}, {}]".format(R, Rmin, Rmax))
 356        exit()
 357    if R > Rmaxdata:
 358        return 0.0
 359
 360    return qfunc(R)
 361
 362def build_Qr(ka, Z, n, l, m):
 363    """
 364    概要: 距離r内側の総電荷分布 Q(r) の補間関数を構築します。
 365
 366    詳細説明:
 367    rの各点に対して Rr(r) と rho(r) を計算し、
 368    4pi * r*r * rho(r) を0から r まで積分することで Q(r) を求めます。
 369    求めた Q(r) のデータポイント yQr を使用して、
 370    scipy.interpolate.interp1d により三次スプライン補間関数 qfunc を構築し、
 371    calQ 関数で利用できるようにします。
 372
 373    :param ka: 動径関数の指数部分の係数。
 374    :type ka: float
 375    :param Z: 原子番号。
 376    :type Z: float
 377    :param n: 主量子数 (1s軌道なので常に1)。
 378    :type n: int
 379    :param l: 方位量子数 (1s軌道なので常に0)。
 380    :type l: int
 381    :param m: 磁気量子数 (1s軌道なので常に0)。
 382    :type m: int
 383    :returns: なし。グローバル変数yRr, yQr, qfuncを更新します。
 384    :rtype: None
 385    """
 386    global yRr, yQr, qfunc
 387
 388    yRr = []
 389    yQr = []
 390    for i in range(len(r)):
 391        yRr.append(Rr(ka, Z, n, l, m, r[i]))
 392        if r[i] == 0.0:
 393            Q   = 0.0
 394        else:
 395            Q, errQ = integrate.quad(lambda r: pi4 * r * r * rho(ka, Z, n, l, m, r), 0, r[i], limit = nmaxdiv, epsrel = eps)
 396        yQr.append(Ne * Q)
 397    qfunc = interp1d(r, yQr, kind = 'cubic')
 398
 399def calTanal(Z = 1.0, ka = 1.0):
 400    """
 401    概要: H様原子の動径関数における運動エネルギーの解析解を計算します。
 402
 403    詳細説明:
 404    この関数は、原子番号Zと指数係数kaを持つH様原子の1s軌道に対する運動エネルギーの
 405    解析的な近似値をハートリー単位で計算します。
 406    T = (ka*Z)^2 / 2 という関係に基づいています。
 407
 408    :param Z: 原子番号。デフォルトは1.0。
 409    :type Z: float, optional
 410    :param ka: 動径関数の指数部分の係数。デフォルトは1.0。
 411    :type ka: float, optional
 412    :returns: 運動エネルギーの解析解 (ハートリー単位)。
 413    :rtype: float
 414    """
 415# T = e^2 / 8pi / e0 / a0
 416    Tanal = Z * Z * ka * ka * e / 8.0 / pi / e0 / (a0*1.0e-10)
 417    return Tanal
 418
 419def calUanal(Z = 1.0, ka = 1.0):
 420    """
 421    概要: H様原子の動径関数における核電子相互作用ポテンシャルエネルギーの解析解を計算します。
 422
 423    詳細説明:
 424    この関数は、原子番号Zと指数係数kaを持つH様原子の1s軌道に対する
 425    核と電子の間のポテンシャルエネルギーの解析的な近似値をハートリー単位で計算します。
 426    U = -(ka*Z)^2 という関係に基づいています。
 427
 428    :param Z: 原子番号。デフォルトは1.0。
 429    :type Z: float, optional
 430    :param ka: 動径関数の指数部分の係数。デフォルトは1.0。
 431    :type ka: float, optional
 432    :returns: 核電子相互作用ポテンシャルエネルギーの解析解 (ハートリー単位)。
 433    :rtype: float
 434    """
 435# U = -e^2 / 4pi / e0 / a0
 436    Uanal = -Z * Z * ka * ka * e / 4.0 / pi / e0 / (a0*1.0e-10)
 437    return Uanal
 438
 439
 440def integrate_trapezoidal(func, E0, E1, h):
 441    """
 442    概要: 台形公式を用いて数値積分を行います。
 443
 444    詳細説明:
 445    指定された関数 func を E0 から E1 までの範囲で、
 446    ステップサイズ h を用いて台形公式で数値積分します。
 447    この関数は現在使用されていませんが、将来的な高速化のために残されています。
 448
 449    :param func: 積分する関数。引数を一つ取る関数を想定。
 450    :type func: callable
 451    :param E0: 積分範囲の下限。
 452    :type E0: float
 453    :param E1: 積分範囲の上限。
 454    :type E1: float
 455    :param h: 積分ステップサイズ。
 456    :type h: float
 457    :returns: [積分の結果, エラー値 (ここでは常に-1.0)]
 458    :rtype: list of float
 459    """
 460    n = int((E1 - E0) / h + 1.000001)
 461    h = (E1 - E0) / n
 462    y = [func(E0 + i * h) for i in range(n)]
 463
 464    S = 0.5 * (y[0] + y[n-1]) + sum(y[1:n-1])
 465    return [h * S, -1.0]
 466
 467def integrate3DR(func, rmin, rmax, limit = 15, epsrel = 1.0e-8):
 468    """
 469    概要: 3次元空間における動径方向の積分 (4πr^2 func(r) dr) を行います。
 470
 471    詳細説明:
 472    scipy.integrate.quad を使用して、球座標における体積要素 4pi * r*r を考慮した
 473    動径方向の積分を実行します。
 474
 475    :param func: 積分する動径関数。引数を一つ取る関数を想定。
 476    :type func: callable
 477    :param rmin: 積分範囲の下限。
 478    :type rmin: float
 479    :param rmax: 積分範囲の上限。
 480    :type rmax: float
 481    :param limit: quad関数の最大サブインターバル数。デフォルトは15。
 482    :type limit: int, optional
 483    :param epsrel: quad関数の相対許容誤差。デフォルトは1.0e-8。
 484    :type epsrel: float, optional
 485    :returns: [積分の結果, 積分の絶対誤差]
 486    :rtype: tuple of (float, float)
 487    """
 488    I, err = integrate.quad(lambda r: pi4 * r * r * func(r), rmin, rmax, limit, epsrel)
 489    return I, err
 490
 491def calUZ(r, Z):
 492    """
 493    概要: 核電荷Zによって生成される静電ポテンシャル U_Z(r) を計算します。
 494
 495    詳細説明:
 496    点電荷としての核が距離rに生成するクーロンポテンシャル -Z/r を計算します。
 497    rが非常に小さい場合 (1.0e-3より小さい場合) は、数値的な安定性のために
 498    -Z/1.0e-3を返します。
 499
 500    :param r: 核からの距離。
 501    :type r: float
 502    :param Z: 原子番号 (核電荷)。
 503    :type Z: float
 504    :returns: 核電荷Zによって生成される静電ポテンシャル。
 505    :rtype: float
 506    """
 507    if r < 1.0e-3:
 508        return -Z / 1.0e-3
 509    else:
 510        return -Z / r
 511
 512def calUrho(r, ka, Z, n, l, m):
 513    """
 514    概要: 電子密度 ρ(r) によって距離rに形成される静電ポテンシャル U_ee(r) を計算します。
 515
 516    詳細説明:
 517    電子密度 ρ(r) が自身によって生成するポテンシャルを計算します。
 518    これは以下の2つの部分から構成されます。
 519    1. 距離rの内側にある電荷 Q(r) によるポテンシャル (Q(r)/r)。
 520    2. 距離rの外側にある電荷が距離rに生成するポテンシャル (4pi * integ(rm * ρ(rm) drm, rから∞))。
 521
 522    :param r: 核からの距離。
 523    :type r: float
 524    :param ka: 動径関数の指数部分の係数。
 525    :type ka: float
 526    :param Z: 原子番号。
 527    :type Z: float
 528    :param n: 主量子数。
 529    :type n: int
 530    :param l: 方位量子数。
 531    :type l: int
 532    :param m: 磁気量子数。
 533    :type m: int
 534    :returns: 電子密度 ρ(r) によって距離rに形成される静電ポテンシャル。
 535    :rtype: float
 536    """
 537# Uerho(r) = integ(rho(m) / |r-rm|)_[rm=0, inf]                             [r, rm are vectors]
 538#          = Q(r) / r + integ[4pi * rm * rm * rho(rm) / rm]_[rm = r, inf])  [r is vector]
 539    if r == 0.0:
 540        Uee1 = 0.0
 541    else:
 542        Uee1 = calQ(r) / r
 543
 544    Rmaxint = Rmax
 545#    Rmaxint = min(-log(eps) / Z / ka, Rmax)
 546    Uee2, errUee2 = integrate.quad(lambda rm: pi4 * rm * rho(ka, Z, n, l, m, rm), r, Rmaxint, limit = nmaxdiv, epsrel = eps)
 547    return Uee1 + Uee2
 548#    return Uee1 + Ne * Uee2
 549
 550def calT(ka, Z, n, l, m, Ne, Rmin, Rmax, nmaxdiv, eps):
 551    """
 552    概要: 電子系の運動エネルギーを計算します。
 553
 554    詳細説明:
 555    シュレーディンガー方程式の運動エネルギー演算子 (-1/2)∇^2 に基づいて、
 556    H-like 1s軌道の電子の運動エネルギーを数値積分によって計算します。
 557    積分の範囲は Rmin から Rmaxint (実質的には∞) です。
 558    内部でグローバル変数 R1s0 を使用しますが、この変数はこのスクリプト内で定義されていません。
 559
 560    :param ka: 動径関数の指数部分の係数。
 561    :type ka: float
 562    :param Z: 原子番号。
 563    :type Z: float
 564    :param n: 主量子数。
 565    :type n: int
 566    :param l: 方位量子数。
 567    :type l: int
 568    :param m: 磁気量子数。
 569    :type m: int
 570    :param Ne: 電子数。
 571    :type Ne: float
 572    :param Rmin: 積分範囲の最小半径。
 573    :type Rmin: float
 574    :param Rmax: 積分範囲の最大半径(上限)。
 575    :type Rmax: float
 576    :param nmaxdiv: scipy.integrate.quad の最大サブインターバル数。
 577    :type nmaxdiv: int
 578    :param eps: scipy.integrate.quad の相対許容誤差。
 579    :type eps: float
 580    :returns: [運動エネルギー, 積分の絶対誤差]
 581    :rtype: tuple of (float, float)
 582    """
 583    Rmaxint = min(-log(eps) / Z / ka, Rmax)
 584    T, errT = integrate.quad(lambda r: r * (2.0 - Z * ka * r) * exp(-2.0 * Z * ka * r), 
 585                    Rmin, Rmaxint, limit = nmaxdiv, epsrel = eps)
 586    T *= 1.0 / 2.0 * pi4 * Z * ka * R1s0 * R1s0 * pow(ka * Z, 3.0)
 587    return T, errT
 588
 589def calUeZ(ka, Z, n, l, m, Ne, Rmin, Rmax, nmaxdiv, eps):
 590    """
 591    概要: 電子と原子核間の引力ポテンシャルエネルギー U_eZ を計算します。
 592
 593    詳細説明:
 594    電子密度 ρ(r) と核電荷 Z の間のクーロン引力によるポテンシャルエネルギーを
 595    数値積分によって計算します。積分は Rmin から Rmaxint (実質的には∞) です。
 596
 597    :param ka: 動径関数の指数部分の係数。
 598    :type ka: float
 599    :param Z: 原子番号。
 600    :type Z: float
 601    :param n: 主量子数。
 602    :type n: int
 603    :param l: 方位量子数。
 604    :type l: int
 605    :param m: 磁気量子数。
 606    :type m: int
 607    :param Ne: 電子数。
 608    :type Ne: float
 609    :param Rmin: 積分範囲の最小半径。
 610    :type Rmin: float
 611    :param Rmax: 積分範囲の最大半径(上限)。
 612    :type Rmax: float
 613    :param nmaxdiv: scipy.integrate.quad の最大サブインターバル数。
 614    :type nmaxdiv: int
 615    :param eps: scipy.integrate.quad の相対許容誤差。
 616    :type eps: float
 617    :returns: [電子と原子核間の引力ポテンシャルエネルギー, 積分の絶対誤差]
 618    :rtype: tuple of (float, float)
 619    """
 620    Rmaxint = min(-log(eps) / Z / ka, Rmax)
 621    UeZ, errUeZ = integrate.quad(lambda r: pi4 * r * rho(ka, Z, n, l, m, r), 
 622                        Rmin, Rmaxint, limit = nmaxdiv, epsrel = eps)
 623    UeZ *= -Z
 624    return UeZ, errUeZ
 625
 626def calUee(mode, ka, Z, n, l, m, Ne, Rmin, Rmax, nmaxdiv, eps):
 627    """
 628    概要: 電子間のクーロン反発ポテンシャルエネルギー U_ee を計算します。
 629
 630    詳細説明:
 631    電子密度 ρ(r) によって生成される静電ポテンシャル U_rho(r) と ρ(r) の積を
 632    全空間で積分することにより、電子間のクーロン反発エネルギーを計算します。
 633    mode に 'e' が含まれる場合、全エネルギー計算モードとして Ne*Ne 倍、
 634    それ以外の場合は Ne 倍します。
 635
 636    :param mode: 現在の実行モードを示す文字列('e'が含まれるかでスケールが変わる)。
 637    :type mode: str
 638    :param ka: 動径関数の指数部分の係数。
 639    :type ka: float
 640    :param Z: 原子番号。
 641    :type Z: float
 642    :param n: 主量子数。
 643    :type n: int
 644    :param l: 方位量子数。
 645    :type l: int
 646    :param m: 磁気量子数。
 647    :type m: int
 648    :param Ne: 電子数。
 649    :type Ne: float
 650    :param Rmin: 積分範囲の最小半径。
 651    :type Rmin: float
 652    :param Rmax: 積分範囲の最大半径(上限)。
 653    :type Rmax: float
 654    :param nmaxdiv: scipy.integrate.quad の最大サブインターバル数。
 655    :type nmaxdiv: int
 656    :param eps: scipy.integrate.quad の相対許容誤差。
 657    :type eps: float
 658    :returns: [電子間のクーロン反発ポテンシャルエネルギー, 積分の絶対誤差]
 659    :rtype: tuple of (float, float)
 660    """
 661    Rmaxint = min(-log(eps) / Z / ka, Rmax)
 662    Uee, errUee = integrate.quad(lambda r: pi4 * r * r * calUrho(r, ka, Z, n, l, m) * rho(ka, Z, n, l, m, r),
 663                         Rmin, Rmaxint, limit = nmaxdiv, epsrel = eps)
 664    if 'e' in mode:
 665        Uee *= Ne * Ne
 666    else:
 667        Uee *= Ne
 668
 669    return Uee, errUee
 670
 671def calLocalUXa(r, ka, Z, n, l, m, Ne):
 672    """
 673    概要: スレーターのX-α交換ポテンシャル (局所形式) を計算します。
 674
 675    詳細説明:
 676    距離rにおけるスレーターのX-α交換ポテンシャルの局所形式 -3.0 * alpha * (3/(4pi) * ρ(r))^(1/3)
 677    を計算します。
 678
 679    :param r: 核からの距離。
 680    :type r: float
 681    :param ka: 動径関数の指数部分の係数。
 682    :type ka: float
 683    :param Z: 原子番号。
 684    :type Z: float
 685    :param n: 主量子数。
 686    :type n: int
 687    :param l: 方位量子数。
 688    :type l: int
 689    :param m: 磁気量子数。
 690    :type m: int
 691    :param Ne: 電子数 (この関数では直接使用されないが引数として存在)。
 692    :type Ne: float
 693    :returns: 距離rにおけるX-α交換ポテンシャルの値。
 694    :rtype: float
 695    """
 696    return -3.0 * alpha *  pow(3.0/pi4 * rho(ka, Z, n, l, m, r), 1.0/3.0)
 697
 698def calUXa(mode, ka, Z, n, l, m, Ne, Rmin, Rmax, nmaxdiv, eps):
 699    """
 700    概要: X-α交換エネルギーを計算します。
 701
 702    詳細説明:
 703    スレーターのX-α交換ポテンシャル calLocalUXa(r) と電子密度 rho(r) の積を
 704    全空間で積分することにより、X-α交換エネルギーを計算します。
 705    mode に 'e' が含まれる場合、全エネルギー計算モードとして Ne^(4/3) 倍、
 706    それ以外の場合は Ne^(1/3) 倍します。
 707
 708    :param mode: 現在の実行モードを示す文字列('e'が含まれるかでスケールが変わる)。
 709    :type mode: str
 710    :param ka: 動径関数の指数部分の係数。
 711    :type ka: float
 712    :param Z: 原子番号。
 713    :type Z: float
 714    :param n: 主量子数。
 715    :type n: int
 716    :param l: 方位量子数。
 717    :type l: int
 718    :param m: 磁気量子数。
 719    :type m: int
 720    :param Ne: 電子数。
 721    :type Ne: float
 722    :param Rmin: 積分範囲の最小半径。
 723    :type Rmin: float
 724    :param Rmax: 積分範囲の最大半径(上限)。
 725    :type Rmax: float
 726    :param nmaxdiv: scipy.integrate.quad の最大サブインターバル数。
 727    :type nmaxdiv: int
 728    :param eps: scipy.integrate.quad の相対許容誤差。
 729    :type eps: float
 730    :returns: [X-α交換エネルギー, 積分の絶対誤差]
 731    :rtype: tuple of (float, float)
 732    """
 733    Rmaxint = min(-log(eps) / Z / ka, Rmax)
 734    UXa, errUXa = integrate.quad(lambda r: pi4 * r * r * calLocalUXa(r, ka, Z, n, l, m, Ne) * rho(ka, Z, n, l, m, r), 
 735                        Rmin, Rmaxint, limit = nmaxdiv, epsrel = eps)
 736    if 'e' in mode:
 737        UXa *= pow(Ne, 4.0/3.0)
 738    else:
 739        UXa *= pow(Ne, 1.0/3.0)
 740    
 741    return UXa, errUXa
 742
 743def calTotalEnergy(ka, Z, n, l, m, Ne, Rmin, Rmax, nmaxdiv, eps):
 744    """
 745    概要: H-like 1s軌道の全エネルギーを計算します。
 746
 747    詳細説明:
 748    運動エネルギー (T)、核電子引力エネルギー (UeZ)、電子間反発エネルギー (Uee)、
 749    およびX-α交換エネルギー (UXa) の各項を計算し、それらを合計して全エネルギーを求めます。
 750    build_Qr を呼び出して Q(r) 分布を事前に構築します。
 751
 752    :param ka: 動径関数の指数部分の係数。
 753    :type ka: float
 754    :param Z: 原子番号。
 755    :type Z: float
 756    :param n: 主量子数。
 757    :type n: int
 758    :param l: 方位量子数。
 759    :type l: int
 760    :param m: 磁気量子数。
 761    :type m: int
 762    :param Ne: 電子数。
 763    :type Ne: float
 764    :param Rmin: 積分範囲の最小半径。
 765    :type Rmin: float
 766    :param Rmax: 積分範囲の最大半径(上限)。
 767    :type Rmax: float
 768    :param nmaxdiv: scipy.integrate.quad の最大サブインターバル数。
 769    :type nmaxdiv: int
 770    :param eps: scipy.integrate.quad の相対許容誤差。
 771    :type eps: float
 772    :returns: [運動エネルギーT, 核電子引力エネルギーUeZ, 電子間反発エネルギーUee, X-α交換エネルギーUXa, 全エネルギーEtot]
 773    :rtype: tuple of (float, float, float, float, float)
 774    """
 775    build_Qr(ka, Z, n, l, m)
 776
 777#def calT(ka, Z, n, l, m, Ne, Rmin, Rmax, nmaxdiv, eps):
 778    T, errT = calT(ka, Z, n, l, m, Ne, Rmin, Rmax, nmaxdiv, eps)
 779    UeZ, errUeZ = calUeZ(ka, Z, n, l, m, Ne, Rmin, Rmax, nmaxdiv, eps)
 780    Uee, errUee = calUee(mode, ka, Z, n, l, m, Ne, Rmin, Rmax, nmaxdiv, eps)
 781    UXa, errUXa = calUXa(mode, ka, Z, n, l, m, Ne, Rmin, Rmax, nmaxdiv, eps)
 782    Etot = T + UeZ + Uee + UXa
 783
 784    return T, UeZ, Uee, UXa, Etot
 785
 786def calTotalEnergyOnly(kav, Z, n, l, m, Ne, Rmin, Rmax, nmaxdiv, eps):
 787    """
 788    概要: 変分計算のために、特定のka値におけるH-like 1s軌道の全エネルギーを計算します。
 789
 790    詳細説明:
 791    この関数は calTotalEnergy と同様に全エネルギーを計算しますが、
 792    最適化ルーチン (minimize) から呼び出されることを想定し、
 793    引数として与えられた kav を使用し、最終的な全エネルギー値のみを返します。
 794    また、電子間反発エネルギー (Uee) とX-α交換エネルギー (UXa) の計算モードは
 795    常に全エネルギー計算 ('e'モード) として扱われます。
 796
 797    :param kav: 変分パラメータとして使用される動径関数の指数部分の係数。
 798    :type kav: float
 799    :param Z: 原子番号。
 800    :type Z: float
 801    :param n: 主量子数。
 802    :type n: int
 803    :param l: 方位量子数。
 804    :type l: int
 805    :param m: 磁気量子数。
 806    :type m: int
 807    :param Ne: 電子数。
 808    :type Ne: float
 809    :param Rmin: 積分範囲の最小半径。
 810    :type Rmin: float
 811    :param Rmax: 積分範囲の最大半径(上限)。
 812    :type Rmax: float
 813    :param nmaxdiv: scipy.integrate.quad の最大サブインターバル数。
 814    :type nmaxdiv: int
 815    :param eps: scipy.integrate.quad の相対許容誤差。
 816    :type eps: float
 817    :returns: 計算された全エネルギー。
 818    :rtype: float
 819    """
 820    build_Qr(kav, Z, n, l, m)
 821
 822    T, errT = calT(kav, Z, n, l, m, Ne, Rmin, Rmax, nmaxdiv, eps)
 823    UeZ, errUeZ = calUeZ(kav, Z, n, l, m, Ne, Rmin, Rmax, nmaxdiv, eps)
 824    Uee, errUee = calUee('e', kav, Z, n, l, m, Ne, Rmin, Rmax, nmaxdiv, eps)
 825    UXa, errUXa = calUXa('e', kav, Z, n, l, m, Ne, Rmin, Rmax, nmaxdiv, eps)
 826    Etot = T + UeZ + Uee + UXa
 827
 828    return Etot
 829
 830def calOptimizedTotalEnergy(ka, Z, n, l, m, Ne, Rmin, Rmax, nmaxdiv, eps):
 831    """
 832    概要: 変分法を用いて全エネルギーを最小化するka値を探索し、そのときの全エネルギーと各エネルギー成分を計算します。
 833
 834    詳細説明:
 835    scipy.optimize.minimize 関数を使用して、動径関数の指数係数 ka を変分パラメータとして、
 836    calTotalEnergyOnly で計算される全エネルギーを最小化します。
 837    指定された最適化手法 (method) とパラメータ (tol, maxiter) に基づいて探索を行い、
 838    最小化された ka とそのときの各エネルギー成分、および全エネルギーを返します。
 839
 840    :param ka: 最適化の初期値となる動径関数の指数部分の係数。
 841    :type ka: float
 842    :param Z: 原子番号。
 843    :type Z: float
 844    :param n: 主量子数。
 845    :type n: int
 846    :param l: 方位量子数。
 847    :type l: int
 848    :param m: 磁気量子数。
 849    :type m: int
 850    :param Ne: 電子数。
 851    :type Ne: float
 852    :param Rmin: 積分範囲の最小半径。
 853    :type Rmin: float
 854    :param Rmax: 積分範囲の最大半径(上限)。
 855    :type Rmax: float
 856    :param nmaxdiv: scipy.integrate.quad の最大サブインターバル数。
 857    :type nmaxdiv: int
 858    :param eps: scipy.integrate.quad の相対許容誤差。
 859    :type eps: float
 860    :returns: [運動エネルギーT, 核電子引力エネルギーUeZ, 電子間反発エネルギーUee, X-α交換エネルギーUXa, 最小化された全エネルギーEtot, 最適化されたka値]
 861    :rtype: tuple of (float, float, float, float, float, float)
 862    """
 863    xa = [ka]
 864    ret = minimize(lambda xa: calTotalEnergyOnly(xa[0], Z, n, l, m, Ne, Rmin, Rmax, nmaxdiv, eps),
 865                xa, method = method, jac = diff1, tol = tol, 
 866                callback = lambda xa: callback(xa),
 867                options = {'maxiter':maxiter, "disp":True})
 868#       print("ret=", ret)
 869    if method == 'nelder-mead':
 870        simplex = ret['final_simplex']
 871        ai = simplex[0][0]    
 872        Emin = ret['fun']
 873    elif method == 'cg':
 874        ai = ret['x']
 875        Emin = ret['fun']
 876    elif method == 'powell':
 877        ai = ret['x']
 878        Emin = ret['fun']
 879    elif method == 'bfgs':
 880        ai = ret['x']
 881        ka = xa[0]
 882        Emin = calTotalEnergyOnly(xa[0], Z, n, l, m, Ne, Rmin, Rmax, nmaxdiv, eps)
 883
 884    ka = ai[0]
 885    T, UeZ, Uee, UXa, Etot = calTotalEnergy(ka, Z, n, l, m, Ne, Rmin, Rmax, nmaxdiv, eps)
 886
 887    return T, UeZ, Uee, UXa, Etot, ka
 888
 889
 890def diff1(ai):
 891    """
 892    概要: 1変数関数の勾配 (1次導関数) を中心差分法で近似します。
 893
 894    詳細説明:
 895    scipy.optimize.minimize の勾配情報が必要なメソッド (cgなど) のために、
 896    パラメータ ai (ここでは ka) の微小変化 h_diff を用いて、
 897    calTotalEnergyOnly 関数に対する1次導関数を数値的に計算します。
 898
 899    :param ai: 最適化される変数の現在の値 (ここでは ka の配列)。
 900    :type ai: numpy.ndarray
 901    :returns: 各変数に対する1次導関数の配列。
 902    :rtype: numpy.ndarray
 903    """
 904    global Z, n, l, m, Ne, Rmin, Rmax, nmaxdiv, eps
 905    
 906    n = len(ai)
 907    f0 = calTotalEnergyOnly(ai[0], Z, n, l, m, Ne, Rmin, Rmax, nmaxdiv, eps)
 908    df = np.empty(n)
 909    for i in range(n):
 910        aii = ai
 911        aii[i] = ai[i] + h_diff
 912        f1 = calTotalEnergyOnly(aii[0], Z, n, l, m, Ne, Rmin, Rmax, nmaxdiv, eps)
 913        df[i] = (f1 - f0) / h_diff
 914    return df
 915
 916iter = 0
 917def callback(xk):
 918    """
 919    概要: 最適化の各イテレーションで呼び出され、進行状況を表示します。
 920
 921    詳細説明:
 922    scipy.optimize.minimize 関数の callback オプションとして使用されます。
 923    各イテレーションで現在のパラメータ xk (ここでは ka) と
 924    それに対応する全エネルギー Etot を表示します。
 925
 926    :param xk: 現在のイテレーションでの最適化変数の値 (ここでは ka の配列)。
 927    :type xk: numpy.ndarray
 928    :returns: なし
 929    :rtype: None
 930    """
 931    global iter
 932    global Z, n, l, m, Ne, Rmin, Rmax, nmaxdiv, eps
 933
 934    ka = xk[0]
 935    Etot = calTotalEnergyOnly(ka, Z, n, l, m, Ne, Rmin, Rmax, nmaxdiv, eps)
 936
 937# パラメータと残差二乗和を表示
 938    print("callback {}: ka={:14.4g} Emin={} eV".format(iter, ka, Etot * HartreeToeV))
 939    iter += 1
 940
 941
 942def sweep_Ne(Eanal):
 943    """
 944    概要: 電子数Neを掃引し、非最適化および最適化された全エネルギーの変化を評価・プロットします。
 945
 946    詳細説明:
 947    Nearray に定義された様々な電子数Neに対して、固定された ka での全エネルギー、
 948    および変分法で ka を最適化した上での全エネルギーを計算します。
 949    また、Ne=0.5付近での放物線近似も行い、結果を表形式で出力し、グラフとしてプロットします。
 950    特に 'v' モードが有効な場合、ka の最適化も行います。
 951
 952    :param Eanal: 比較のための解析解エネルギー値。
 953    :type Eanal: float
 954    :returns: なし
 955    :rtype: None
 956    """
 957    global Rmin, Rstep, nR, Rmaxdata, r
 958    global ka
 959    global qfunc
 960
 961    print("")
 962    print("Not optimized")
 963
 964    print("  Parabolic expantion around Ne = 0.5")
 965    Tp, UeZp, Ueep, UXap, Etotp = calTotalEnergy(ka, Z, n, l, m, 0.5 + hparab, Rmin, Rmax, nmaxdiv, eps)
 966    T0, UeZ0, Uee0, UXa0, Etot0 = calTotalEnergy(ka, Z, n, l, m, 0.5         , Rmin, Rmax, nmaxdiv, eps)
 967    Tm, UeZm, Ueem, UXam, Etotm = calTotalEnergy(ka, Z, n, l, m, 0.5 - hparab, Rmin, Rmax, nmaxdiv, eps)
 968    Etotp *= HartreeToeV
 969    Etot0 *= HartreeToeV
 970    Etotm *= HartreeToeV
 971    print("  Ne={}: E={} eV".format(0.5 + hparab, Etotp))
 972    print("  Ne={}: E={} eV".format(0.5         , Etot0))
 973    print("  Ne={}: E={} eV".format(0.5 - hparab, Etotm))
 974    a2 = (Etotp - 2.0 * Etot0 + Etotm) / hparab / hparab
 975    a1 = (Etotp - Etotm) / 2.0 / hparab
 976    print("  E = {} + {} * Ne + {} * Ne^2".format(Etot0, a1, a2))
 977
 978    xNe = []
 979    yE1s = []
 980    yE1sparab = []
 981    print("")
 982    print("{:6s}\t{:6s}\t{:6s}\t{:10s}\t{:10s}\t{:10s}\t{:10s}\t{:10s}\t{:10s}"
 983            .format('ka', 'Z', 'Ne', 'T in eV', 'U(e-Z)', 'U(e-e)', 'U(Xa)', 
 984                    ELabel, ELabel + ' (parabolic)'))
 985    for Ne in Nearray:
 986        T, UeZ, Uee, UXa, Etot = calTotalEnergy(ka, Z, n, l, m, Ne, Rmin, Rmax, nmaxdiv, eps)
 987        Eparab = Etot0 + a1 * (Ne - 0.5) + a2 * pow(Ne - 0.5, 2)
 988        print("{:6.4f}\t{:6.4f}\t{:6.4f}\t{:10.6g}\t{:10.6g}\t{:10.6g}\t{:10.6g}\t{:10.6g}\t{:10.6g}"
 989            .format(ka, Z, Ne, 
 990                    T * HartreeToeV, UeZ * HartreeToeV, Uee * HartreeToeV, UXa * HartreeToeV, 
 991                    Etot * HartreeToeV, Eparab))
 992        xNe.append(Ne)
 993        yE1s.append(Etot * HartreeToeV)
 994        yE1sparab.append(Eparab)
 995
 996    if 'v' in mode:
 997        yE1sOpt = []
 998        yE1sOptparab = []
 999        ykaOpt  = []
1000        print("")
1001        print("Optimized for ka by variational principle")
1002
1003        print("  Parabolic expantion around Ne = 0.5")
1004        Tp, UeZp, Ueep, UXap, Etotp, kap = calOptimizedTotalEnergy(ka, Z, n, l, m, 0.5 + hparab, Rmin, Rmax, nmaxdiv, eps)
1005        T0, UeZ0, Uee0, UXa0, Etot0, ka0 = calOptimizedTotalEnergy(ka, Z, n, l, m, 0.5         , Rmin, Rmax, nmaxdiv, eps)
1006        Tm, UeZm, Ueem, UXam, Etotm, kam = calOptimizedTotalEnergy(ka, Z, n, l, m, 0.5 - hparab, Rmin, Rmax, nmaxdiv, eps)
1007        Etotp *= HartreeToeV    
1008        Etot0 *= HartreeToeV
1009        Etotm *= HartreeToeV
1010        print("  Ne={}: E={} eV".format(0.5 + hparab, Etotp))
1011        print("  Ne={}: E={} eV".format(0.5         , Etot0))
1012        print("  Ne={}: E={} eV".format(0.5 - hparab, Etotm))
1013        a2 = (Etotp - 2.0 * Etot0 + Etotm) / hparab / hparab
1014        a1 = (Etotp - Etotm) / 2.0 / hparab
1015        print("  E = {} + {} * Ne + {} * Ne^2".format(Etot0, a1, a2))
1016
1017        print("{:6s}\t{:6s}\t{:6s}\t{:10s}\t{:10s}\t{:10s}\t{:10s}\t{:10s}\t{:10s}"
1018                .format('ka', 'Z', 'Ne', 'T in eV', 'U(e-Z)', 'U(e-e)', 'U(Xa)', ELabel, ELabel + ' (parabolic)'))
1019        for Ne in Nearray:
1020            T, UeZ, Uee, UXa, Etot, ka = calOptimizedTotalEnergy(ka, Z, n, l, m, Ne, Rmin, Rmax, nmaxdiv, eps)
1021            Eparab = Etot0 + a1 * (Ne - 0.5) + a2 * pow(Ne - 0.5, 2)
1022            print("{:6.4f}\t{:6.4f}\t{:6.4g}\t{:10.6g}\t{:10.6g}\t{:10.6g}\t{:10.6g}\t{:10.6g}t{:10.6g}"
1023                .format(ka, Z, Ne, 
1024                       T * HartreeToeV, UeZ * HartreeToeV, Uee * HartreeToeV, UXa * HartreeToeV, 
1025                       Etot * HartreeToeV, Eparab))
1026
1027            yE1sOpt.append(Etot * HartreeToeV)
1028            yE1sOptparab.append(Eparab)
1029            ykaOpt.append(ka)
1030
1031#=============================
1032# Plot graphs
1033#=============================
1034    if 'g' not in mode:
1035        terminate()
1036
1037    fig = plt.figure(figsize = (12, 8))
1038
1039    ax1 = fig.add_subplot(1, 2, 1)
1040    ax2 = fig.add_subplot(1, 2, 2)
1041#    ax3 = fig.add_subplot(2, 3, 3)
1042#    ax4 = fig.add_subplot(2, 3, 4)
1043#    ax5 = fig.add_subplot(2, 3, 5)
1044    ax1.plot(xNe, yE1s,      label = ELabel + ' (non-optimized)', color = 'black', linewidth = 1.5, marker = 'o', markersize = 1.0)
1045    ax1.plot(xNe, yE1sparab, label = ELabel + ' (non-opt,parabolic)', color = 'black', linestyle = 'dashed', linewidth = 0.5)
1046    if 'v' in mode:
1047        ax1.plot(xNe, yE1sOpt,      label = ELabel + ' (optimized)',     color = 'red',   linewidth = 1.5, marker = 'o', markersize = 1.0)
1048        ax1.plot(xNe, yE1sOptparab, label = ELabel + ' (opt,parabolic)', color = 'red',   linewidth = 0.5)
1049    ax1.plot([min(xNe), max(xNe)], [Eanal, Eanal], color = 'red', linestyle = 'dashed')
1050    ax1.plot([0.5, 0.5], [min(yE1s), max(yE1s)], color = 'red', linestyle = 'dashed')
1051    ax1.set_xlabel("Ne")
1052    ax1.set_ylabel(ELabel + " (eV)")
1053    ax1.legend()
1054    if 'v' in mode:
1055        ax2.plot(xNe, ykaOpt, label = 'ka (optimized)', color = 'black', linewidth = 0.5, marker = 'o', markersize = 1.0)
1056    ax2.plot([min(xNe), max(xNe)], [1.0, 1.0], color = 'red', linestyle = 'dashed')
1057    if 'v' in mode:
1058        ax2.plot([0.5, 0.5], [min(ykaOpt), max(ykaOpt)], color = 'red', linestyle = 'dashed')
1059    ax2.set_xlabel("Ne")
1060    ax2.set_ylabel("ka (optimized)")
1061    ax2.legend()
1062
1063    plt.tight_layout()
1064    plt.pause(0.1)
1065
1066    print("Press ENTER to exit>>", end = '')
1067    input()
1068
1069
1070def sweep_ka(Eanal):
1071    """
1072    概要: 動径関数の指数係数kaを掃引し、全エネルギーの変化を評価・プロットします。
1073
1074    詳細説明:
1075    kaarray に定義された様々な ka の値に対して、
1076    H-like 1s軌道の全エネルギーとその各成分を計算します。
1077    結果を表形式で出力し、'g' モードが有効な場合はグラフとしてプロットします。
1078
1079    :param Eanal: 比較のための解析解エネルギー値。
1080    :type Eanal: float
1081    :returns: なし
1082    :rtype: None
1083    """
1084    global Rmin, Rstep, nR, Rmaxdata, r
1085    global qfunc
1086    global nmaxdiv, eps
1087
1088    xka = []
1089    yE1s = []
1090    print("")
1091    print("{:6s}\t{:6s}\t{:6s}\t{:10s}\t{:10s}\t{:10s}\t{:10s}\t{:10s}"
1092            .format('ka', 'Z', 'Ne', 'T in eV', 'U(e-Z)', 'U(e-e)', 'U(Xa)', ELabel))
1093    for kav in kaarray:
1094        T, UeZ, Uee, UXa, Etot = calTotalEnergy(kav, Z, n, l, m, Ne, Rmin, Rmax, nmaxdiv, eps)
1095        print("{:6.4f}\t{:6.4f}\t{:6.4f}\t{:10.6g}\t{:10.6g}\t{:10.6g}\t{:10.6g}\t{:10.6g}"
1096            .format(kav, Z, Ne, 
1097                    T * HartreeToeV, UeZ * HartreeToeV, Uee * HartreeToeV, UXa * HartreeToeV, Etot * HartreeToeV))
1098        xka.append(kav)
1099        yE1s.append(Etot * HartreeToeV)
1100
1101#=============================
1102# Plot graphs
1103#=============================
1104    if 'g' not in mode:
1105        terminate()
1106
1107    fig = plt.figure(figsize = (12, 8))
1108
1109    ax1 = fig.add_subplot(2, 3, 1)
1110    ax2 = fig.add_subplot(2, 3, 2)
1111    ax3 = fig.add_subplot(2, 3, 3)
1112    ax4 = fig.add_subplot(2, 3, 4)
1113    ax5 = fig.add_subplot(2, 3, 5)
1114    ax1.plot(xka, yE1s, label = ELabel, color = 'black', marker = 'o')
1115    ax1.plot([min(xka), max(xka)], [Eanal, Eanal], color = 'red', linestyle = 'dashed')
1116    ax1.set_xlabel("ka")
1117    ax1.set_ylabel(ELabel + " (eV)")
1118    ax1.legend()
1119
1120    plt.tight_layout()
1121    plt.pause(0.1)
1122
1123    print("Press ENTER to exit>>", end = '')
1124    input()
1125
1126def debug(Eanal):
1127    """
1128    概要: デバッグモードで、H-like 1s軌道の計算結果と関連するグラフを表示します。
1129
1130    詳細説明:
1131    現在のパラメータ (ka, Z, Ne など) における運動エネルギー、核電子引力エネルギー、
1132    電子間反発エネルギー、X-α交換エネルギー、および全エネルギーを計算し、表形式で出力します。
1133    'v' モードが有効な場合は、ka を最適化した結果も表示します。
1134    また、'g' モードが有効な場合は、動径関数 Rr(r)、電荷分布 Q(r)、および各種ポテンシャル
1135    (U(Z), U(Z-rho), U(rho), U(Xa)) のグラフをプロットします。
1136
1137    :param Eanal: 比較のための解析解エネルギー値。
1138    :type Eanal: float
1139    :returns: なし
1140    :rtype: None
1141    """
1142    global ka, Z, n, l, m, Ne
1143    global Rmin, Rstep, nR, Rmaxdata, r
1144
1145    ka0, Z0 = ka, Z
1146    
1147    print("")
1148    print("{:6s}\t{:6s}\t{:6s}\t{:10s}\t{:10s}\t{:10s}\t{:10s}\t{:10s}"
1149            .format('ka', 'Z', 'Ne', 'T in eV', 'U(e-Z)', 'U(e-e)', 'U(Xa)', ELabel))
1150    T, UeZ, Uee, UXa, Etot = calTotalEnergy(ka, Z, n, l, m, Ne, Rmin, Rmax, nmaxdiv, eps)
1151    print("{:6.4f}\t{:6.4f}\t{:6.4f}\t{:10.6g}\t{:10.6g}\t{:10.6g}\t{:10.6g}\t{:10.6g}"
1152            .format(ka, Z, Ne, 
1153                    T * HartreeToeV, UeZ * HartreeToeV, Uee * HartreeToeV, UXa * HartreeToeV, Etot * HartreeToeV))
1154
1155    if 'v' in mode:
1156        print("")
1157        print("Optimized for ka by variational principle")
1158
1159        print("{:6s}\t{:6s}\t{:6s}\t{:10s}\t{:10s}\t{:10s}\t{:10s}\t{:10s}"
1160                .format('ka', 'Z', 'Ne', 'T in eV', 'U(e-Z)', 'U(e-e)', 'U(Xa)', ELabel))
1161        T, UeZ, Uee, UXa, Etot, ka = calOptimizedTotalEnergy(ka, Z, n, l, m, Ne, Rmin, Rmax, nmaxdiv, eps)
1162        print("{:6.4f}\t{:6.4f}\t{:6.4f}\t{:10.6g}\t{:10.6g}\t{:10.6g}\t{:10.6g}\t{:10.6g}"
1163            .format(ka, Z, Ne, 
1164                   T * HartreeToeV, UeZ * HartreeToeV, Uee * HartreeToeV, UXa * HartreeToeV, Etot * HartreeToeV))
1165
1166#=============================
1167# Plot graphs
1168#=============================
1169    if 'g' not in mode:
1170        terminate()
1171
1172    build_Qr(ka0, Z, n, l, m)
1173
1174    ypotZ    = []  # U(Z) = -Z/r    
1175    ypotZrho = []  # U(e-Z) for Z = 1
1176    ypotXa   = []  # Xa potential
1177    ypotrho  = []  # Electrostatic potential by rho(r)
1178    for i in range(len(r)):
1179        if r[i] == 0.0:
1180            phi = 0.0
1181        else:
1182            phi, errpot = integrate.quad(lambda r: pi4 * r * rho(ka, Z, n, l, m, r), 
1183                                    Rmin, r[i], limit = nmaxdiv, epsrel = eps)
1184            potZ = -Z / r[i]
1185        ypotZ.append(calUZ(r[i], Z))
1186        ypotZrho.append(-Z * Ne * phi)
1187        ypotrho.append(Ne * calUrho(r[i], ka, Z, n, l, m))
1188        ypotXa.append(calLocalUXa(r[i], ka, Z, n, l, m, 1.0))
1189
1190    fig = plt.figure(figsize = (12, 8))
1191
1192    ax1 = fig.add_subplot(2, 3, 1)
1193    ax2 = fig.add_subplot(2, 3, 2)
1194    ax3 = fig.add_subplot(2, 3, 3)
1195    ax1.plot(r, yRr, label = 'Rr(r)', color = 'black')
1196    ax1.set_xlim([Rmin, Rmax])
1197    ax1.set_ylim([0, max(yRr)*1.1])
1198    ax1.set_xlabel("r (bohr)")
1199    ax1.set_ylabel("Rr(r)")
1200    ax1.legend()
1201    ax2.plot(r, yQr, label = 'Q(r)', color = 'black')
1202    ax2.set_xlabel("r (bohr)")
1203    ax2.set_ylabel("Q(r)")
1204    ax2.legend()
1205    ax3.plot(r, ypotZ,    label = 'U(Z)', color = 'black')
1206    ax3.plot(r, ypotZrho, label = 'U(Z-rho)', color = 'red')
1207    ax3.plot(r, -np.array(ypotrho),  label = '-U(rho)',   color = 'blue')
1208    ax3.plot(r, ypotXa,   label = 'U(Xa)',    color = 'green')
1209    ax3.set_ylim([min(ypotXa) * 5.0, 0.0])
1210    ax3.set_xlabel("r (bohr)")
1211    ax3.set_ylabel("Potential / Energy (Hartree)")
1212    ax3.legend()
1213
1214    plt.tight_layout()
1215    plt.pause(0.1)
1216
1217    print("Press ENTER to exit>>", end = '')
1218    input()
1219
1220
1221def main():
1222    """
1223    概要: プログラムのメインエントリポイントです。
1224
1225    詳細説明:
1226    コマンドライン引数を解析し、グローバル変数を更新します。
1227    水素様原子の解析解エネルギーを計算した後、
1228    指定されたモードに基づいて異なる計算(デバッグ、ka掃引、Ne掃引)を実行します。
1229    最後に、動径関数の正規化チェックも行います。
1230
1231    :returns: なし
1232    :rtype: None
1233    """
1234    global mode
1235    global ka, Z, n, l, m, Ne
1236    global Rmin, Rstep, nR, Rmaxdata, r
1237    global qfunc
1238
1239    updatevars()
1240
1241    Rstep = (Rmax - Rmin) / (nR - 1)
1242    r = [Rmin + i * Rstep for i in range(nR+100)]
1243    Rmaxdata = max(r)
1244
1245    print("")
1246    print("mode: ", mode)
1247    print("")
1248    print("Orbital: ka={} Z={} n={} l={} m={}".format(ka, Z, n, l, m))
1249    print("Ne: ", Ne)
1250    print("Integration: Rmax=", Rmax)
1251    print("   Rmax: epsR={}  Rmaxinteg={}".format(epsR, -log(epsR) / Z / ka))
1252
1253    print("")
1254    print("Analytical solution")
1255
1256    Tanal = calTanal(Z, ka)
1257    Uanal = calUanal(Z, ka)
1258    Eanal = Tanal + Uanal
1259    print("T(analytical) = {} eV".format(Tanal))
1260    print("U(analytical) = {} eV".format(Uanal))
1261    print("  Etotl(analytical) = {} eV".format(Eanal))
1262
1263    print("")
1264    print("Numerical integration")
1265    Rr2tot, err = integrate.quad(lambda r: r * r * rho(ka, Z, n, l, m, r), Rmin, Rmax, limit = nmaxdiv, epsrel = eps)
1266    Rr2tot *= pi4
1267    print("R(r) normalization check: 2pi * integ(r*r * Rr*2)dr = ", Rr2tot)
1268
1269    if 'd' in mode:
1270        debug(Eanal)
1271    if 'k' in mode:
1272        sweep_ka(Eanal)
1273    if 'n' in mode:
1274        sweep_Ne(Eanal)
1275
1276    terminate()
1277
1278
1279if __name__ == '__main__':
1280    usage()
1281    main()