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

Seebeck_ZT.py をダウンロード

Seebeck_ZT.py
Seebeck_ZT.py
  1"""
  2熱電輸送特性計算モジュール
  3
  4概要:
  5ゼーベック係数、ローレンツ因子、電気伝導度、移動度、電子熱伝導度、パワーファクター、ZT因子を計算し、
  6キャリア濃度依存性をプロット・Excelファイルに保存します。
  7
  8詳細説明:
  9このモジュールは、3次元放物線バンドモデルと緩和時間近似(tau(E) ∝ E^(r-1/2))に基づき、
 10フェルミ・ディラック積分を用いた厳密な輸送積分によって熱電輸送特性を評価します。
 11コマンドライン引数を通じて、温度、有効質量、参照移動度、格子熱伝導率、キャリア種別、散乱因子rのリストなどを設定できます。
 12
 13関連リンク:
 14Seebeck_ZT_usage
 15"""
 16import numpy as np
 17import matplotlib.pyplot as plt
 18from scipy.integrate import quad
 19from scipy.special import expit, gamma
 20from functools import lru_cache
 21import argparse
 22from openpyxl import Workbook
 23
 24# =========================
 25# constants (SI)
 26# =========================
 27kB = 1.380649e-23          # J/K
 28e  = 1.602176634e-19       # C
 29h  = 6.62607015e-34        # J s
 30hbar = h / (2.0 * np.pi)
 31m0 = 9.1093837015e-31      # kg
 32
 33r"""
 34散乱因子 r は tau(E) ∝ E^(r-1/2) で定義
 35"""
 36
 37figsize = (10, 10)
 38
 39# ============================================================
 40# Fermi–Dirac integral (NO 1/Gamma(j+1))
 41#   Fj(j, xi) = ∫_0^∞ x^j / (1+exp(x-xi)) dx
 42# ============================================================
 43@lru_cache(maxsize=40000)
 44def Fj(j, xi):
 45    """
 46    概要:
 47        フェルミ・ディラック積分 Fj(j, xi) = ∫_0^∞ x^j / (1+exp(x-xi)) dx を計算します。
 48
 49    詳細説明:
 50        SciPyのquad関数を使用して数値積分を実行します。結果はlru_cacheでキャッシュされ、
 51        同じ引数での再計算を回避し、パフォーマンスを向上させます。
 52        この関数は、ガンマ関数による正規化(1/Gamma(j+1))は含まれていません。
 53
 54    引数:
 55        :param j: 積分の次数。
 56        :type j: float
 57        :param xi: 還元フェルミ準位 (reduced Fermi level)。
 58        :type xi: float
 59    戻り値:
 60        :returns: フェルミ・ディラック積分 Fj(j, xi) の値。
 61        :rtype: float
 62    """
 63    j = float(j)
 64    xi = float(np.round(xi, 12))
 65    integrand = lambda x: (x**j) * expit(xi - x)
 66    val, _ = quad(integrand, 0.0, np.inf, epsabs=1e-10, epsrel=1e-10, limit=400)
 67    return val
 68
 69
 70def Nc_3D(T, m_eff=1.0):
 71    """
 72    概要:
 73        3次元放物線バンドにおける有効状態密度 Nc を計算します。
 74
 75    詳細説明:
 76        以下の公式を用いて有効状態密度を計算します。
 77        Nc = 2 * (2π m* kB T / h^2)^(3/2)
 78        ここで、m* は有効質量、kB はボルツマン定数、T は温度、h はプランク定数です。
 79
 80    引数:
 81        :param T: 温度 [K]。
 82        :type T: float
 83        :param m_eff: 有効質量の比率 (m*/m0)。m0 は自由電子質量です。デフォルトは1.0。
 84        :type m_eff: float, optional
 85    戻り値:
 86        :returns: 有効状態密度 Nc [m^-3]。
 87        :rtype: float
 88    """
 89    mstar = m_eff * m0
 90    return 2.0 * (2.0 * np.pi * mstar * kB * T / (h**2))**1.5
 91
 92
 93def electron_density_from_xi(xi, T, m_eff=1.0):
 94    """
 95    概要:
 96        還元フェルミ準位 xi から電子濃度 n を計算します。
 97
 98    詳細説明:
 99        電子濃度は以下の式で計算されます。
100        n [m^-3] = Nc * F^{std}_{1/2}(xi)
101        ここで、Nc は有効状態密度、F^{std}_{1/2}(xi) = Fj(1/2, xi) / Gamma(3/2) は
102        標準化されたフェルミ・ディラック積分です。
103
104    引数:
105        :param xi: 還元フェルミ準位。
106        :type xi: float
107        :param T: 温度 [K]。
108        :type T: float
109        :param m_eff: 有効質量の比率 (m*/m0)。デフォルトは1.0。
110        :type m_eff: float, optional
111    戻り値:
112        :returns: 電子濃度 n [m^-3]。
113        :rtype: float
114    """
115    Nc = Nc_3D(T, m_eff=m_eff)
116    F12_std = Fj(0.5, xi) / gamma(1.5)
117    return Nc * F12_std
118
119
120# ============================================================
121# Transport integrals for tau(E) ∝ E^(r-1/2)
122#   I0 = (r+1) F_r
123#   I1 = (r+2) F_{r+1} - xi (r+1) F_r
124#   I2 = (r+3) F_{r+2} - 2 xi (r+2) F_{r+1} + xi^2 (r+1) F_r
125# ============================================================
126def transport_I0(xi, r):
127    """
128    概要:
129        輸送積分 I0 を計算します。
130
131    詳細説明:
132        tau(E) ∝ E^(r-1/2) の緩和時間に対する輸送積分の一つである I0 を計算します。
133        I0 = (r + 1) * F_r(xi) の式で定義されます。
134
135    引数:
136        :param xi: 還元フェルミ準位。
137        :type xi: float
138        :param r: 散乱因子。緩和時間のエネルギー依存性 E^(r-1/2) を定義します。
139        :type r: float
140    戻り値:
141        :returns: 輸送積分 I0 の値。
142        :rtype: float
143    """
144    return (r + 1.0) * Fj(r, xi)
145
146
147def transport_I1(xi, r):
148    """
149    概要:
150        輸送積分 I1 を計算します。
151
152    詳細説明:
153        tau(E) ∝ E^(r-1/2) の緩和時間に対する輸送積分の一つである I1 を計算します。
154        I1 = (r + 2) * F_{r+1}(xi) - xi * (r + 1) * F_r(xi) の式で定義されます。
155
156    引数:
157        :param xi: 還元フェルミ準位。
158        :type xi: float
159        :param r: 散乱因子。緩和時間のエネルギー依存性 E^(r-1/2) を定義します。
160        :type r: float
161    戻り値:
162        :returns: 輸送積分 I1 の値。
163        :rtype: float
164    """
165    return (r + 2.0) * Fj(r + 1.0, xi) - xi * (r + 1.0) * Fj(r, xi)
166
167
168def transport_I2(xi, r):
169    """
170    概要:
171        輸送積分 I2 を計算します。
172
173    詳細説明:
174        tau(E) ∝ E^(r-1/2) の緩和時間に対する輸送積分の一つである I2 を計算します。
175        I2 = (r + 3) * F_{r+2}(xi) - 2 * xi * (r + 2) * F_{r+1}(xi) + xi^2 * (r + 1) * F_r(xi)
176        の式で定義されます。
177
178    引数:
179        :param xi: 還元フェルミ準位。
180        :type xi: float
181        :param r: 散乱因子。緩和時間のエネルギー依存性 E^(r-1/2) を定義します。
182        :type r: float
183    戻り値:
184        :returns: 輸送積分 I2 の値。
185        :rtype: float
186    """
187    return (
188        (r + 3.0) * Fj(r + 2.0, xi)
189        - 2.0 * xi * (r + 2.0) * Fj(r + 1.0, xi)
190        + (xi**2) * (r + 1.0) * Fj(r, xi)
191    )
192
193
194def transport_prefactor_sigma(T, m_eff=1.0):
195    """
196    概要:
197        電気伝導度 sigma の計算における前因子 A_sigma を計算します。
198
199    詳細説明:
200        電気伝導度は sigma = A_sigma * tau_pref * (kB T)^(r+1) * I0 の形で表されます。
201        この関数は、tau(E) = tau_pref * E^(r-1/2) と定義される緩和時間において、
202        以下の物理定数を含む前因子 A_sigma を計算します。
203        A_sigma [SI] = e^2 * 2^(3/2) * sqrt(m*) / (3 π^2 ħ^3)
204        ここで、e は素電荷、m* は有効質量、ħ は換算プランク定数です。
205
206    引数:
207        :param T: 温度 [K]。
208        :type T: float
209        :param m_eff: 有効質量の比率 (m*/m0)。m0 は自由電子質量です。デフォルトは1.0。
210        :type m_eff: float, optional
211    戻り値:
212        :returns: 電気伝導度の前因子 A_sigma [SI単位]。
213        :rtype: float
214    """
215    mstar = m_eff * m0
216    return e**2 * (2.0**1.5) * np.sqrt(mstar) / (3.0 * np.pi**2 * hbar**3)
217
218
219def sigma_from_xi_tau_pref(xi, r, T, tau_pref, m_eff=1.0):
220    """
221    概要:
222        還元フェルミ準位 xi、散乱因子 r、温度 T、緩和時間の前因子 tau_pref から
223        電気伝導度 sigma を計算します。
224
225    詳細説明:
226        電気伝導度は以下の式で計算されます。
227        sigma = A_sigma * tau_pref * (kB T)^(r + 1.0) * I0
228        ここで、A_sigma は transport_prefactor_sigma で計算される前因子、
229        I0 は transport_I0 で計算される輸送積分です。
230
231    引数:
232        :param xi: 還元フェルミ準位。
233        :type xi: float
234        :param r: 散乱因子。
235        :type r: float
236        :param T: 温度 [K]。
237        :type T: float
238        :param tau_pref: 緩和時間の前因子 tau_pref [s / J^(r-1/2)]。
239        :type tau_pref: float
240        :param m_eff: 有効質量の比率 (m*/m0)。m0 は自由電子質量です。デフォルトは1.0。
241        :type m_eff: float, optional
242    戻り値:
243        :returns: 電気伝導度 sigma [S/m]。
244        :rtype: float
245    """
246    A = transport_prefactor_sigma(T, m_eff=m_eff)
247    I0 = transport_I0(xi, r)
248    return A * tau_pref * (kB * T)**(r + 1.0) * I0
249
250
251def seebeck_from_xi_transport(xi, r, carrier="electron"):
252    """
253    概要:
254        還元フェルミ準位 xi と散乱因子 r からゼーベック係数 S を計算します。
255
256    詳細説明:
257        ゼーベック係数は厳密な輸送積分を用いて以下の式で計算されます。
258        S = (kB/q) * I1/I0
259        q はキャリアの種類によって異なり、電子の場合は -e、正孔の場合は +e となります。
260
261    引数:
262        :param xi: 還元フェルミ準位。
263        :type xi: float
264        :param r: 散乱因子。tau(E) ∝ E^(r-1/2) で定義されます。
265        :type r: float
266        :param carrier: キャリアの種類。"electron"または"hole"を指定します。デフォルトは"electron"。
267        :type carrier: str, optional
268    戻り値:
269        :returns: ゼーベック係数 S [V/K]。
270        :rtype: float
271    例外:
272        :raises ValueError: carrier が "electron" または "hole" 以外の場合。
273    """
274    if carrier.lower().startswith("e"):
275        q = -e
276    elif carrier.lower().startswith("h"):
277        q = +e
278    else:
279        raise ValueError("carrier must be 'electron' or 'hole'")
280
281    I0 = transport_I0(xi, r)
282    I1 = transport_I1(xi, r)
283    return (kB / q) * (I1 / I0)
284
285
286def lorenz_from_xi_transport(xi, r):
287    """
288    概要:
289        還元フェルミ準位 xi と散乱因子 r からローレンツ因子 L を計算します。
290
291    詳細説明:
292        ローレンツ因子は厳密な輸送積分を用いて以下の式で計算されます。
293        L = (kB/e)^2 * ( I2/I0 - (I1/I0)^2 )
294
295    引数:
296        :param xi: 還元フェルミ準位。
297        :type xi: float
298        :param r: 散乱因子。
299        :type r: float
300    戻り値:
301        :returns: ローレンツ因子 L [V^2/K^2 (または W ohm / K^2)]。
302        :rtype: float
303    """
304    I0 = transport_I0(xi, r)
305    I1 = transport_I1(xi, r)
306    I2 = transport_I2(xi, r)
307    return (kB / e)**2 * (I2 / I0 - (I1 / I0)**2)
308
309
310def mobility_from_sigma_n(sigma, n):
311    """
312    概要:
313        電気伝導度 sigma と電子濃度 n から移動度 mu を計算します。
314
315    詳細説明:
316        電気伝導度 sigma、電子濃度 n、素電荷 e、移動度 mu の間に成り立つ関係式
317        sigma = n * e * mu を用いて移動度を計算します。
318        したがって、mu = sigma / (n * e) となります。
319
320    引数:
321        :param sigma: 電気伝導度 [S/m]。
322        :type sigma: float
323        :param n: 電子濃度 [m^-3]。
324        :type n: float
325    戻り値:
326        :returns: 移動度 mu [m^2/V/s]。
327        :rtype: float
328    """
329    return sigma / (n * e)
330
331
332def tau_pref_from_mu_ref(mu_ref_cm2_Vs, xi_ref, r, T, m_eff=1.0):
333    """
334    概要:
335        参照移動度 mu_ref と参照還元フェルミ準位 xi_ref に基づいて、
336        緩和時間の前因子 tau_pref を決定します。
337
338    詳細説明:
339        緩和時間 tau(E) が tau(E) = tau_pref * E^(r-1/2) で定義されるとき、
340        指定された xi_ref と T における移動度が mu_ref となるように
341        tau_pref を逆算します。
342        参照移動度 mu_ref_cm2_Vs は cm^2/V/s 単位で与えられますが、計算ではSI単位に変換されます。
343
344    引数:
345        :param mu_ref_cm2_Vs: 参照移動度 [cm^2/V/s]。
346        :type mu_ref_cm2_Vs: float
347        :param xi_ref: 参照還元フェルミ準位。この xi で参照移動度が再現されます。
348        :type xi_ref: float
349        :param r: 散乱因子。
350        :type r: float
351        :param T: 温度 [K]。
352        :type T: float
353        :param m_eff: 有効質量の比率 (m*/m0)。m0 は自由電子質量です。デフォルトは1.0。
354        :type m_eff: float, optional
355    戻り値:
356        :returns: 緩和時間の前因子 tau_pref [s / J^(r-1/2)]。
357        :rtype: float
358    """
359    mu_ref_SI = mu_ref_cm2_Vs * 1e-4  # cm^2/V/s -> m^2/V/s
360    n_ref = electron_density_from_xi(xi_ref, T=T, m_eff=m_eff)
361    sigma_ref = n_ref * e * mu_ref_SI
362
363    A = transport_prefactor_sigma(T, m_eff=m_eff)
364    I0_ref = transport_I0(xi_ref, r)
365
366    tau_pref = sigma_ref / (A * (kB * T)**(r + 1.0) * I0_ref)
367    return tau_pref
368
369
370def tau_at_energy(E_J, tau_pref, r):
371    """
372    概要:
373        特定のエネルギー E_J における緩和時間 tau(E_J) を計算します。
374
375    詳細説明:
376        緩和時間は以下の式で定義されます。
377        tau(E) = tau_pref * E^(r-1/2)
378        E_J が0以下の場合は np.nan を返します。
379
380    引数:
381        :param E_J: エネルギー [J]。
382        :type E_J: float
383        :param tau_pref: 緩和時間の前因子 [s / J^(r-1/2)]。
384        :type tau_pref: float
385        :param r: 散乱因子。
386        :type r: float
387    戻り値:
388        :returns: エネルギー E_J における緩和時間 tau(E_J) [s]。
389        :rtype: float
390    """
391    if E_J <= 0.0:
392        return np.nan
393    return tau_pref * (E_J**(r - 0.5))
394
395
396def equivalent_l0_from_tau_pref(tau_pref, r, m_eff=1.0):
397    """
398    概要:
399        緩和時間の前因子 tau_pref から等価な平均自由行程 l0 を計算します。
400
401    詳細説明:
402        一部のスライドでの慣例的な定義
403        tau(E) = sqrt(m*/2) * [ l0(T) * e_chg^(-r) ] * E^(r-1/2)
404        から、l0(T) を逆算します。
405        したがって、l0(T) = tau_pref * sqrt(2/m*) * e_chg^r となります。
406
407    引数:
408        :param tau_pref: 緩和時間の前因子 [s / J^(r-1/2)]。
409        :type tau_pref: float
410        :param r: 散乱因子。
411        :type r: float
412        :param m_eff: 有効質量の比率 (m*/m0)。m0 は自由電子質量です。デフォルトは1.0。
413        :type m_eff: float, optional
414    戻り値:
415        :returns: 等価な平均自由行程 l0 [m]。
416        :rtype: float
417    """
418    mstar = m_eff * m0
419    return tau_pref * np.sqrt(2.0 / mstar) * (e**r)
420
421
422def save_results_to_excel(outfile, meta_rows, data_rows):
423    """
424    概要:
425        計算結果とメタデータをExcelファイルに保存します。
426
427    詳細説明:
428        指定された出力ファイル名で新しいExcelワークブックを作成し、
429        "metadata"と"transport_vs_Ne"の2つのシートにデータを書き込みます。
430        "metadata"シートには計算条件などのメタデータが保存され、
431        "transport_vs_Ne"シートにはキャリア濃度に対する輸送特性の計算結果が保存されます。
432
433    引数:
434        :param outfile: 出力するExcelファイル名。例: "results.xlsx"。
435        :type outfile: str
436        :param meta_rows: メタデータを含む行のリスト。各要素はExcelの1行に対応するリストです。
437        :type meta_rows: list
438        :param data_rows: 計算結果データを含む行のリスト。各要素はExcelの1行に対応するリストです。
439        :type data_rows: list
440    戻り値:
441        :returns: なし
442        :rtype: None
443    """
444    wb = Workbook()
445    ws_meta = wb.active
446    ws_meta.title = "metadata"
447
448    for row in meta_rows:
449        ws_meta.append(row)
450
451    ws = wb.create_sheet("transport_vs_Ne")
452    ws.append([
453        "r",
454        "xi",
455        "tau_pref_s_per_J^(r-1/2)",
456        "tau_at_kBT_s",
457        "l0_equiv_m",
458        "Ne_m^-3",
459        "Ne_cm^-3",
460        "S_V_per_K",
461        "S_uV_per_K",
462        "L_Wohm_per_K2",
463        "sigma_S_per_m",
464        "mu_m2_per_Vs",
465        "mu_cm2_per_Vs",
466        "kappa_e_W_per_mK",
467        "PF_W_per_mK2",
468        "PF_mW_per_mK2",
469        "ZT",
470    ])
471    for row in data_rows:
472        ws.append(row)
473
474    wb.save(outfile)
475
476
477def main():
478    """
479    概要:
480        スクリプトの主要な実行フローを定義します。コマンドライン引数を解析し、
481        熱電輸送特性を計算・プロット・保存します。
482
483    詳細説明:
484        argparse モジュールを使用してコマンドライン引数(温度、有効質量、参照移動度、
485        格子熱伝導率、キャリア種別、還元フェルミ準位の範囲と点数、散乱因子rのリスト、
486        出力ファイル名など)を解析します。
487        次に、指定されたパラメータに基づいて、各散乱因子 r に対して輸送特性(電子濃度、
488        ゼーベック係数、ローレンツ因子、電気伝導度、移動度、電子熱伝導度、パワーファクター、ZT因子)を計算します。
489        計算されたデータは save_results_to_excel 関数を使用してExcelファイルに保存されます。
490        最後に、計算結果は matplotlib を用いてキャリア濃度に対する各特性のグラフとしてプロットされ、表示されます。
491
492    戻り値:
493        :returns: なし
494        :rtype: None
495    """
496    parser = argparse.ArgumentParser(
497        description=(
498            "S, L, sigma, mu, kappa_e, PF, ZT vs Ne using exact transport integrals "
499            "for tau(E) ∝ E^(r-1/2) in a 3D parabolic band."
500        )
501    )
502    parser.add_argument("--T", type=float, default=300.0, help="Temperature [K]")
503    parser.add_argument("--m_eff", type=float, default=1.0, help="Effective mass m*/m0")
504    parser.add_argument("--mu_ref", type=float, default=10.0,
505                        help="Reference mobility at xi_ref [cm^2/V/s]")
506    parser.add_argument("--xi_ref", type=float, default=0.0,
507                        help="Reference reduced Fermi level xi where mu_ref is reproduced")
508    parser.add_argument("--klatt", type=float, default=5.0,
509                        help="Lattice thermal conductivity [W/m/K] (default: 5)")
510    parser.add_argument("--carrier", type=str, default="electron",
511                        choices=["electron", "hole"], help="Carrier type")
512    parser.add_argument("--xi_min", type=float, default=-5.0, help="Minimum xi")
513    parser.add_argument("--xi_max", type=float, default=40.0, help="Maximum xi")
514    parser.add_argument("--nxi", type=int, default=181, help="Number of xi points")
515    parser.add_argument("--r_list", type=str, default="2.0,1.5,1.0,0.5,0.0,-0.5",
516                        help="Comma-separated r list")
517    parser.add_argument("--outfile", type=str, default="transport_vs_Ne.xlsx",
518                        help="Excel output filename")
519    args = parser.parse_args()
520
521    T = args.T
522    m_eff = args.m_eff
523    mu_ref_cm2_Vs = args.mu_ref
524    xi_ref = args.xi_ref
525    klatt = args.klatt
526    carrier = args.carrier
527    r_list = [float(x.strip()) for x in args.r_list.split(",")]
528    xi_range = np.linspace(args.xi_min, args.xi_max, args.nxi)
529    outfile = args.outfile
530
531    print("Convention: tau(E) ∝ E^(r-1/2)")
532    print(f"T         = {T:g} K")
533    print(f"m_eff     = {m_eff:g} m0")
534    print(f"mu_ref    = {mu_ref_cm2_Vs:g} cm^2/V/s")
535    print(f"xi_ref    = {xi_ref:g}")
536    print(f"klatt     = {klatt:g} W/m/K")
537    print(f"carrier   = {carrier}")
538    print(f"outfile   = {outfile}")
539    print()
540
541    all_results = []
542
543    for r in r_list:
544        tau_pref = tau_pref_from_mu_ref(mu_ref_cm2_Vs, xi_ref, r, T, m_eff=m_eff)
545        tau_kBT = tau_at_energy(kB * T, tau_pref, r)
546        l0_equiv = equivalent_l0_from_tau_pref(tau_pref, r, m_eff=m_eff)
547
548        print(f"r = {r:g}")
549        print(f"  tau_pref        = {tau_pref:.6e}  [s / J^({r:g}-1/2)]")
550        print(f"  tau(E=kBT)      = {tau_kBT:.6e}  [s]")
551        print(f"  equivalent l0   = {l0_equiv:.6e}  [m]  (slide convention)")
552
553        n_list = []
554        S_list = []
555        L_list = []
556        sig_list = []
557        mu_list = []
558        ke_list = []
559        PF_list = []
560        ZT_list = []
561
562        for xi in xi_range:
563            n = electron_density_from_xi(xi, T=T, m_eff=m_eff)                # [m^-3]
564            sigma = sigma_from_xi_tau_pref(xi, r, T, tau_pref, m_eff=m_eff)   # [S/m]
565            mu_SI = mobility_from_sigma_n(sigma, n)                            # [m^2/V/s]
566            mu_cm2_Vs = mu_SI * 1e4
567
568            S = seebeck_from_xi_transport(xi, r, carrier=carrier)              # [V/K]
569            L = lorenz_from_xi_transport(xi, r)                                # [V^2/K^2]
570            kappa_e = L * sigma * T                                            # [W/m/K]
571            PF = S**2 * sigma                                                   # [W/m/K^2]
572            ZT = PF * T / (kappa_e + klatt)
573
574            n_list.append(n)
575            S_list.append(S)
576            L_list.append(L)
577            sig_list.append(sigma)
578            mu_list.append(mu_cm2_Vs)
579            ke_list.append(kappa_e)
580            PF_list.append(PF)
581            ZT_list.append(ZT)
582
583            all_results.append([
584                r,
585                xi,
586                tau_pref,
587                tau_kBT,
588                l0_equiv,
589                n,
590                n / 1e6,
591                S,
592                S * 1e6,
593                L,
594                sigma,
595                mu_SI,
596                mu_cm2_Vs,
597                kappa_e,
598                PF,
599                PF * 1e3,
600                ZT,
601            ])
602
603        # 配列化して保持
604        # この行は、ループ内でリストに追加された要素を再度リストに代入するもので、
605        # 結果的にはall_resultsのリスト構造に影響を与えません。
606        # 論理的な意味は薄いですが、既存コードの変更禁止ルールに従い保持します。
607        all_results[-len(xi_range):] = all_results[-len(xi_range):]
608
609    # ---------- save Excel before plotting ----------
610    meta_rows = [
611        ["Convention", "tau(E) ∝ E^(r-1/2)"],
612        ["T_K", T],
613        ["m_eff_over_m0", m_eff],
614        ["mu_ref_cm2_per_Vs", mu_ref_cm2_Vs],
615        ["xi_ref", xi_ref],
616        ["klatt_W_per_mK", klatt],
617        ["carrier", carrier],
618        ["r_list", ",".join(str(r) for r in r_list)],
619        ["xi_min", args.xi_min],
620        ["xi_max", args.xi_max],
621        ["nxi", args.nxi],
622    ]
623    save_results_to_excel(outfile, meta_rows, all_results)
624    print(f"\nSaved calculation results to [{outfile}]\n")
625
626    # ---------- plot ----------
627    fig, axes = plt.subplots(4, 2, figsize=figsize)
628    axS   = axes[0, 0]
629    axL   = axes[0, 1]
630    axSig = axes[1, 0]
631    axMu  = axes[1, 1]
632    axKe  = axes[2, 0]
633    axPF  = axes[2, 1]
634    axZT  = axes[3, 0]
635    axBlank = axes[3, 1]
636
637    for r in r_list:
638        tau_pref = tau_pref_from_mu_ref(mu_ref_cm2_Vs, xi_ref, r, T, m_eff=m_eff)
639
640        n_list = []
641        S_list = []
642        L_list = []
643        sig_list = []
644        mu_list = []
645        ke_list = []
646        PF_list = []
647        ZT_list = []
648
649        for xi in xi_range:
650            n = electron_density_from_xi(xi, T=T, m_eff=m_eff)
651            sigma = sigma_from_xi_tau_pref(xi, r, T, tau_pref, m_eff=m_eff)
652            mu_SI = mobility_from_sigma_n(sigma, n)
653            S = seebeck_from_xi_transport(xi, r, carrier=carrier)
654            L = lorenz_from_xi_transport(xi, r)
655            kappa_e = L * sigma * T
656            PF = S**2 * sigma
657            ZT = PF * T / (kappa_e + klatt)
658
659            n_list.append(n)
660            S_list.append(S)
661            L_list.append(L)
662            sig_list.append(sigma)
663            mu_list.append(mu_SI * 1e4)
664            ke_list.append(kappa_e)
665            PF_list.append(PF)
666            ZT_list.append(ZT)
667
668        n_arr_cm3 = np.array(n_list) / 1e6
669        S_arr_uVK = np.array(S_list) * 1e6
670        L_arr     = np.array(L_list)
671        sig_arr   = np.array(sig_list)
672        mu_arr    = np.array(mu_list)
673        ke_arr    = np.array(ke_list)
674        PF_arr    = np.array(PF_list) * 1e3
675        ZT_arr    = np.array(ZT_list)
676
677        label = f"r = {r:g}"
678
679        axS.plot(n_arr_cm3, S_arr_uVK, label=label)
680        axL.plot(n_arr_cm3, L_arr, label=label)
681        axSig.plot(n_arr_cm3, sig_arr, label=label)
682        axMu.plot(n_arr_cm3, mu_arr, label=label)
683        axKe.plot(n_arr_cm3, ke_arr, label=label)
684        axPF.plot(n_arr_cm3, PF_arr, label=label)
685        axZT.plot(n_arr_cm3, ZT_arr, label=label)
686
687    for ax in [axS, axL, axSig, axMu, axKe, axPF, axZT]:
688        ax.set_xscale("log")
689        ax.grid(True, which="both", alpha=0.25)
690        ax.set_xlabel(r"$N_e$ (cm$^{-3}$)")
691
692    axS.axhline(0.0, linewidth=1.0, alpha=0.4)
693    axS.set_ylabel(r"$S$ ($\mu$V/K)")
694    axS.set_title("Seebeck coefficient")
695
696    axL.set_ylabel(r"$L$ (V$^2$/K$^2$)")
697    axL.set_title("Lorenz factor")
698
699    axSig.set_ylabel(r"$\sigma$ (S/m)")
700    axSig.set_title("Electrical conductivity")
701
702    axMu.set_ylabel(r"$\mu$ (cm$^2$/V/s)")
703    axMu.set_title(r"Mobility  ($\tau(E)\propto E^{r-1/2}$)")
704
705    axKe.set_ylabel(r"$\kappa_e$ (W/m/K)")
706    axKe.set_title("Electronic thermal conductivity")
707
708    axPF.set_ylabel(r"$PF=S^2\sigma$ (mW/m/K$^2$)")
709    axPF.set_title("Power factor")
710
711    axZT.set_ylabel(r"$ZT$")
712    axZT.set_title(rf"$ZT$  ($\kappa_{{latt}}$={klatt:g} W/m/K)")
713
714    axBlank.axis("off")
715
716    handles, labels = axS.get_legend_handles_labels()
717    fig.legend(handles, labels, loc="upper center", ncol=min(len(r_list), 6))
718    fig.suptitle(
719        f"Transport vs Ne  (T={T:g} K, m*/m0={m_eff:g}, "
720        f"mu_ref={mu_ref_cm2_Vs:g} cm$^2$/V/s at xi_ref={xi_ref:g}, "
721        r"$\tau(E)\propto E^{r-1/2}$)",
722        y=0.995
723    )
724    plt.tight_layout(rect=[0, 0, 1, 0.96])
725    plt.show()
726
727
728if __name__ == "__main__":
729    main()