第3章: 相平衡と相図

🎯 学習目標

📖 相平衡の基礎

相平衡条件

2つの相 α, β が平衡にあるための条件:

  • 熱平衡: \(T^\alpha = T^\beta\)
  • 力学平衡: \(P^\alpha = P^\beta\)
  • 化学平衡: \(\mu_i^\alpha = \mu_i^\beta\) (各成分 i について)

ここで \(\mu_i\) は成分 i の化学ポテンシャルです。

Gibbsの相律

Gibbs phase ruleは、系の自由度を決定します:

\[ F = C - P + 2 \]

💻 例題3.1: Clausius-Clapeyron式による蒸気圧曲線

Clausius-Clapeyron式

相平衡曲線上での圧力と温度の関係:

\[ \frac{dP}{dT} = \frac{L}{T \Delta V} \]

ここで \(L\) は相転移の潜熱、\(\Delta V\) は相転移に伴う体積変化です。

液体-気体平衡の場合(\(V_{\text{gas}} \gg V_{\text{liquid}}\) かつ理想気体近似):

\[ \frac{d \ln P}{dT} = \frac{L_{\text{vap}}}{RT^2} \]

積分すると:

\[ \ln P = -\frac{L_{\text{vap}}}{RT} + C \]

Python実装: 蒸気圧曲線の計算
import numpy as np import matplotlib.pyplot as plt from scipy.integrate import odeint from scipy.optimize import fsolve # Clausius-Clapeyron式の積分形 def vapor_pressure_clausius_clapeyron(T, L_vap, R, P0, T0): """Clausius-Clapeyron式による蒸気圧""" return P0 * np.exp(-L_vap / R * (1/T - 1/T0)) # 水の蒸気圧データ R = 8.314 # J/(mol·K) L_vap_water = 40660 # J/mol(100℃での蒸発熱) T0_water = 373.15 # K (100℃) P0_water = 101325 # Pa (1 atm) # 温度範囲 T_range = np.linspace(273.15, 473.15, 200) # 0-200℃ # 蒸気圧曲線 P_vapor_water = vapor_pressure_clausius_clapeyron(T_range, L_vap_water, R, P0_water, T0_water) # 他の物質(エタノール) L_vap_ethanol = 38560 # J/mol T0_ethanol = 351.5 # K (78.3℃) P0_ethanol = 101325 # Pa P_vapor_ethanol = vapor_pressure_clausius_clapeyron(T_range, L_vap_ethanol, R, P0_ethanol, T0_ethanol) # 可視化 fig, axes = plt.subplots(1, 2, figsize=(14, 6)) # 蒸気圧曲線(線形スケール) ax1 = axes[0] ax1.plot(T_range - 273.15, P_vapor_water / 1e5, 'b-', linewidth=2, label='H₂O') ax1.plot(T_range - 273.15, P_vapor_ethanol / 1e5, 'r-', linewidth=2, label='C₂H₅OH') ax1.axhline(1.0, color='gray', linestyle='--', alpha=0.5, label='1 atm') ax1.set_xlabel('Temperature (°C)') ax1.set_ylabel('Vapor pressure (bar)') ax1.set_title('蒸気圧曲線(Clausius-Clapeyron)') ax1.legend() ax1.grid(True, alpha=0.3) # 蒸気圧曲線(対数スケール) ax2 = axes[1] ax2.semilogy(T_range - 273.15, P_vapor_water / 1e5, 'b-', linewidth=2, label='H₂O') ax2.semilogy(T_range - 273.15, P_vapor_ethanol / 1e5, 'r-', linewidth=2, label='C₂H₅OH') ax2.axhline(1.0, color='gray', linestyle='--', alpha=0.5, label='1 atm') ax2.set_xlabel('Temperature (°C)') ax2.set_ylabel('Vapor pressure (bar, log scale)') ax2.set_title('蒸気圧曲線(対数プロット)') ax2.legend() ax2.grid(True, alpha=0.3, which='both') plt.tight_layout() plt.savefig('phase_vapor_pressure_clausius_clapeyron.png', dpi=300, bbox_inches='tight') plt.show() # 沸点の計算 def boiling_point(P_target, L_vap, R, P0, T0): """指定圧力での沸点を計算""" # ln(P) = -L/(RT) + C を T について解く T = 1 / (1/T0 - R/L_vap * np.log(P_target / P0)) return T # 様々な圧力での沸点 pressures = [0.5e5, 1.0e5, 2.0e5, 5.0e5] # Pa print("=== Clausius-Clapeyron式による沸点予測 ===\n") print(f"{'圧力 (bar)':<15} {'水の沸点 (°C)':<20} {'エタノールの沸点 (°C)':<25}") print("-" * 60) for P in pressures: T_bp_water = boiling_point(P, L_vap_water, R, P0_water, T0_water) T_bp_ethanol = boiling_point(P, L_vap_ethanol, R, P0_ethanol, T0_ethanol) print(f"{P/1e5:<15.1f} {T_bp_water - 273.15:<20.2f} {T_bp_ethanol - 273.15:<25.2f}") # Antoine式との比較 print("\n=== 実験値との比較(水、1 atm)===") print(f"Clausius-Clapeyron: {boiling_point(101325, L_vap_water, R, P0_water, T0_water) - 273.15:.2f} °C") print(f"実験値: 100.00 °C") print("\nClausius-Clapeyron式は温度範囲が狭い場合に高精度")

💻 例題3.2: van der Waals等温線とMaxwell構成

van der Waals状態方程式

\[ \left(P + \frac{a}{V^2}\right)(V - b) = RT \]

臨界点以下の温度では、等温線が非物理的な領域(\(\frac{\partial P}{\partial V} > 0\))を持ちます。

Maxwell構成: 気液共存領域で等面積則により圧力を決定

\[ \int_{V_L}^{V_G} P_{\text{vdW}}(V) dV = P_{\text{eq}}(V_G - V_L) \]

Python実装: van der Waals等温線とMaxwell構成
import numpy as np import matplotlib.pyplot as plt from scipy.optimize import fsolve, minimize # van der Waals圧力 def P_vdw(V, T, a, b, R): """van der Waals状態方程式""" return R * T / (V - b) - a / V**2 # CO₂のvan der Waalsパラメータ R = 8.314e-6 # MPa·m³/(mol·K) a = 0.3658 # MPa·m⁶/mol² b = 4.267e-5 # m³/mol # 臨界点 T_c = 8 * a / (27 * R * b) P_c = a / (27 * b**2) V_c = 3 * b print(f"=== CO₂の臨界点 ===") print(f"T_c = {T_c:.2f} K = {T_c - 273.15:.2f} °C") print(f"P_c = {P_c:.4f} MPa") print(f"V_c = {V_c * 1e6:.2f} cm³/mol\n") # Maxwell構成の実装 def maxwell_construction(T, a, b, R): """Maxwell構成により平衡圧力を計算""" # van der Waals等温線の極値を求める def dP_dV(V): return -R * T / (V - b)**2 + 2 * a / V**3 # スピノーダル点(dP/dV = 0) V_range = np.linspace(b * 1.1, 10 * b, 1000) candidates = [] for i in range(len(V_range) - 1): V1, V2 = V_range[i], V_range[i+1] if dP_dV(V1) * dP_dV(V2) < 0: V_spin = fsolve(dP_dV, (V1 + V2) / 2)[0] candidates.append(V_spin) if len(candidates) < 2: return None, None, None V_spin_min, V_spin_max = sorted(candidates)[:2] # Maxwell等面積則 def area_difference(P_eq): # P_eq より下の面積 - 上の面積 def integrand_lower(V): return P_vdw(V, T, a, b, R) - P_eq from scipy.integrate import quad area_lower, _ = quad(integrand_lower, V_spin_min, V_spin_max) return area_lower # P_eqを探索 P_min = P_vdw(V_spin_max, T, a, b, R) P_max = P_vdw(V_spin_min, T, a, b, R) P_eq = fsolve(area_difference, (P_min + P_max) / 2)[0] # 気液の体積を求める def find_volumes(P_eq): V_liquid = fsolve(lambda V: P_vdw(V, T, a, b, R) - P_eq, V_spin_min)[0] V_gas = fsolve(lambda V: P_vdw(V, T, a, b, R) - P_eq, V_spin_max)[0] return V_liquid, V_gas V_L, V_G = find_volumes(P_eq) return P_eq, V_L, V_G # 複数温度での等温線 temperatures = [280, 300, T_c, 320, 350] # K colors = ['blue', 'green', 'red', 'orange', 'purple'] fig, ax = plt.subplots(figsize=(10, 8)) V_plot = np.linspace(b * 1.05, 10 * b, 1000) for T, color in zip(temperatures, colors): P_plot = [P_vdw(V, T, a, b, R) for V in V_plot] if T < T_c: label = f'T = {T:.0f} K (< T_c)' ax.plot(V_plot * 1e6, P_plot, color=color, linestyle='--', linewidth=1.5, alpha=0.5) # Maxwell構成 P_eq, V_L, V_G = maxwell_construction(T, a, b, R) if P_eq is not None: ax.plot([V_L * 1e6, V_G * 1e6], [P_eq, P_eq], color=color, linewidth=2.5, label=label) elif T == T_c: label = f'T = {T:.0f} K (= T_c)' ax.plot(V_plot * 1e6, P_plot, color=color, linewidth=2.5, label=label) else: label = f'T = {T:.0f} K (> T_c)' ax.plot(V_plot * 1e6, P_plot, color=color, linewidth=2, label=label) # 臨界点をマーク ax.plot(V_c * 1e6, P_c, 'ko', markersize=10, label='臨界点') ax.set_xlabel('Molar volume (cm³/mol)') ax.set_ylabel('Pressure (MPa)') ax.set_title('van der Waals等温線とMaxwell構成(CO₂)') ax.set_xlim([0, 500]) ax.set_ylim([0, 15]) ax.legend() ax.grid(True, alpha=0.3) plt.tight_layout() plt.savefig('phase_vdw_maxwell_construction.png', dpi=300, bbox_inches='tight') plt.show() # Maxwell構成の数値結果 print("=== Maxwell構成による気液平衡 ===\n") print(f"{'温度 (K)':<12} {'平衡圧力 (MPa)':<18} {'液体V (cm³/mol)':<20} {'気体V (cm³/mol)':<20}") print("-" * 70) for T in [280, 290, 300]: P_eq, V_L, V_G = maxwell_construction(T, a, b, R) if P_eq is not None: print(f"{T:<12.0f} {P_eq:<18.4f} {V_L*1e6:<20.2f} {V_G*1e6:<20.2f}")

💻 例題3.3: 二元系相図の描画(共晶型)

二元系相図の分類

  • 共晶型(Eutectic): 最低融点を持つ共晶点が存在
  • 包晶型(Peritectic): 液相から2つの固相が生成
  • 全率固溶体型: 全組成範囲で固溶体を形成
  • 偏晶型(Monotectic): 液相分離が起こる
Python実装: 共晶型二元系相図
import numpy as np import matplotlib.pyplot as plt # Pb-Sn系の簡略モデル(共晶型) # 実験データに基づく簡略化した相図 def pb_sn_phase_diagram(): """Pb-Sn共晶系の相図データ""" # 組成(Sn原子分率) x_Sn = np.array([0.0, 0.1, 0.183, 0.183, 0.5, 0.619, 0.619, 0.8, 1.0]) # 液相線(Liquidus) T_liquidus = np.array([327, 300, 183, 183, 220, 183, 183, 210, 232]) # °C # 固相線(Solidus)- α相(Pb-rich) x_alpha = np.array([0.0, 0.05, 0.183]) T_alpha = np.array([327, 250, 183]) # 固相線 - β相(Sn-rich) x_beta = np.array([0.619, 0.8, 1.0]) T_beta = np.array([183, 210, 232]) # 共晶温度 T_eutectic = 183 # °C x_eutectic = 0.619 # Sn 61.9% return { 'liquidus': (x_Sn, T_liquidus), 'alpha_solidus': (x_alpha, T_alpha), 'beta_solidus': (x_beta, T_beta), 'eutectic_T': T_eutectic, 'eutectic_x': x_eutectic } # 相図の描画 phase_data = pb_sn_phase_diagram() fig, ax = plt.subplots(figsize=(10, 8)) # 液相線 x_liq, T_liq = phase_data['liquidus'] ax.plot(x_liq * 100, T_liq, 'r-', linewidth=2.5, label='液相線(Liquidus)') # α相固相線 x_alpha, T_alpha = phase_data['alpha_solidus'] ax.plot(x_alpha * 100, T_alpha, 'b-', linewidth=2.5, label='α相固相線(Pb-rich)') # β相固相線 x_beta, T_beta = phase_data['beta_solidus'] ax.plot(x_beta * 100, T_beta, 'g-', linewidth=2.5, label='β相固相線(Sn-rich)') # 共晶線 T_e = phase_data['eutectic_T'] ax.axhline(T_e, color='orange', linestyle='--', linewidth=2, label=f'共晶温度({T_e}°C)') # 共晶点 x_e = phase_data['eutectic_x'] ax.plot(x_e * 100, T_e, 'ko', markersize=12, label=f'共晶点(Sn {x_e*100:.1f}%)') # 領域のラベル ax.text(10, 280, 'L(液相)', fontsize=14, ha='center', bbox=dict(boxstyle='round', facecolor='white', alpha=0.8)) ax.text(10, 210, 'L + α', fontsize=12, ha='center') ax.text(70, 210, 'L + β', fontsize=12, ha='center') ax.text(10, 150, 'α(Pb-rich固溶体)', fontsize=12, ha='center') ax.text(70, 150, 'β(Sn-rich固溶体)', fontsize=12, ha='center') ax.text(30, 170, 'α + β(共晶組織)', fontsize=11, ha='center') ax.set_xlabel('Sn濃度 (at%)', fontsize=12) ax.set_ylabel('温度 (°C)', fontsize=12) ax.set_title('Pb-Sn二元系相図(共晶型)', fontsize=14, fontweight='bold') ax.set_xlim([0, 100]) ax.set_ylim([100, 350]) ax.legend(loc='upper right', fontsize=10) ax.grid(True, alpha=0.3) plt.tight_layout() plt.savefig('phase_diagram_pb_sn_eutectic.png', dpi=300, bbox_inches='tight') plt.show() # 相図情報の出力 print("=== Pb-Sn共晶系相図 ===\n") print(f"共晶点: Sn {x_e*100:.1f}%, 温度 {T_e}°C") print(f"Pb融点: 327°C") print(f"Sn融点: 232°C") print(f"最低融点: {T_e}°C(共晶温度)") print("\n用途: ハンダ(共晶組成に近い Sn-37Pb が伝統的)")

💻 例題3.4: Lever rule(てこの規則)の計算

Lever rule(てこの規則)

2相共存領域で各相の量を求める規則:

組成 \(x_0\) の合金が温度 \(T\) で α相(組成 \(x_\alpha\))と β相(組成 \(x_\beta\))に分離するとき:

\[ \frac{f_\alpha}{f_\beta} = \frac{x_\beta - x_0}{x_0 - x_\alpha} \]

相分率:

\[ f_\alpha = \frac{x_\beta - x_0}{x_\beta - x_\alpha}, \quad f_\beta = \frac{x_0 - x_\alpha}{x_\beta - x_\alpha} \]

Python実装: Lever ruleの計算と可視化
import numpy as np import matplotlib.pyplot as plt def lever_rule(x0, x_alpha, x_beta): """Lever ruleにより相分率を計算 Args: x0: 全体組成 x_alpha: α相組成 x_beta: β相組成 Returns: f_alpha, f_beta: 各相の分率 """ f_beta = (x0 - x_alpha) / (x_beta - x_alpha) f_alpha = 1 - f_beta return f_alpha, f_beta # Pb-Sn系での具体例 # 全体組成: Sn 40%、温度 200°C での α + β 領域 x0 = 0.40 # Sn 40% T = 200 # °C # この温度での各相の組成(相図から読み取り) x_alpha = 0.15 # α相(Pb-rich)のSn濃度 x_beta = 0.90 # β相(Sn-rich)のSn濃度 # Lever rule計算 f_alpha, f_beta = lever_rule(x0, x_alpha, x_beta) print("=== Lever rule(てこの規則)の適用 ===\n") print(f"系: Pb-Sn合金、全体組成 Sn {x0*100:.0f}%、温度 {T}°C") print(f"α相組成: Sn {x_alpha*100:.0f}% (Pb-rich)") print(f"β相組成: Sn {x_beta*100:.0f}% (Sn-rich)") print(f"\n相分率:") print(f" α相: {f_alpha*100:.2f}%") print(f" β相: {f_beta*100:.2f}%") print(f"\n検証: f_α + f_β = {f_alpha + f_beta:.4f} (should be 1.000)") # 可視化 fig, axes = plt.subplots(1, 2, figsize=(14, 6)) # てこの図解 ax1 = axes[0] ax1.plot([x_alpha, x_beta], [1, 1], 'ko-', linewidth=2, markersize=10) ax1.plot(x0, 1, 'r^', markersize=15, label=f'全体組成 (Sn {x0*100:.0f}%)') # てこの腕 ax1.plot([x_alpha, x0], [0.95, 0.95], 'b-', linewidth=3, label=f'β側の腕 (L_β)') ax1.plot([x0, x_beta], [0.95, 0.95], 'g-', linewidth=3, label=f'α側の腕 (L_α)') ax1.text(x_alpha, 1.05, f'α相\n(Sn {x_alpha*100:.0f}%)', ha='center', fontsize=11) ax1.text(x_beta, 1.05, f'β相\n(Sn {x_beta*100:.0f}%)', ha='center', fontsize=11) ax1.text(x0, 0.85, f'f_α/f_β = L_β/L_α', ha='center', fontsize=12, color='red') ax1.set_xlim([0, 1]) ax1.set_ylim([0.7, 1.2]) ax1.set_xlabel('Sn濃度(原子分率)', fontsize=12) ax1.set_title('てこの規則の図解', fontsize=13, fontweight='bold') ax1.legend(loc='lower center', fontsize=10) ax1.axis('off') # 組成変化に対する相分率 ax2 = axes[1] x0_range = np.linspace(x_alpha, x_beta, 100) f_alpha_range = [] f_beta_range = [] for x in x0_range: f_a, f_b = lever_rule(x, x_alpha, x_beta) f_alpha_range.append(f_a) f_beta_range.append(f_b) ax2.plot(x0_range * 100, np.array(f_alpha_range) * 100, 'b-', linewidth=2.5, label='α相分率') ax2.plot(x0_range * 100, np.array(f_beta_range) * 100, 'g-', linewidth=2.5, label='β相分率') ax2.axvline(x0 * 100, color='r', linestyle='--', linewidth=1.5, label=f'例: Sn {x0*100:.0f}%') ax2.set_xlabel('全体組成 Sn (at%)', fontsize=12) ax2.set_ylabel('相分率 (%)', fontsize=12) ax2.set_title(f'相分率の組成依存性(T = {T}°C)', fontsize=13, fontweight='bold') ax2.legend(fontsize=10) ax2.grid(True, alpha=0.3) plt.tight_layout() plt.savefig('phase_lever_rule.png', dpi=300, bbox_inches='tight') plt.show() # 質量保存の確認 print("\n=== 質量保存の確認 ===") x_average = f_alpha * x_alpha + f_beta * x_beta print(f"f_α · x_α + f_β · x_β = {f_alpha:.4f} × {x_alpha:.2f} + {f_beta:.4f} × {x_beta:.2f}") print(f" = {x_average:.4f}") print(f"全体組成 x₀ = {x0:.4f}") print(f"誤差 = {abs(x_average - x0):.6f}")

💻 例題3.5: 共晶点・包晶点の解析

Python実装: 共晶・包晶反応の可視化
import numpy as np import matplotlib.pyplot as plt def plot_eutectic_peritectic(): """共晶反応と包晶反応の比較""" fig, axes = plt.subplots(1, 2, figsize=(14, 6)) # 共晶型(Eutectic) ax1 = axes[0] # 液相線 x_L_left = np.linspace(0, 0.4, 50) T_L_left = 400 - 200 * x_L_left x_L_right = np.linspace(0.4, 1.0, 50) T_L_right = 320 + 80 * (x_L_right - 0.4) ax1.plot(x_L_left * 100, T_L_left, 'r-', linewidth=2.5) ax1.plot(x_L_right * 100, T_L_right, 'r-', linewidth=2.5, label='液相線') # 固相線 ax1.plot([0, 20], [400, 320], 'b-', linewidth=2.5, label='α相固相線') ax1.plot([70, 100], [320, 400], 'g-', linewidth=2.5, label='β相固相線') # 共晶線 ax1.plot([0, 100], [320, 320], 'orange', linestyle='--', linewidth=2) # 共晶点 ax1.plot(40, 320, 'ko', markersize=12) ax1.text(40, 310, '共晶点 E', ha='center', fontsize=12, fontweight='bold') # 反応式 ax1.text(50, 380, 'L → α + β', fontsize=14, ha='center', bbox=dict(boxstyle='round', facecolor='yellow', alpha=0.7)) # 領域ラベル ax1.text(20, 370, 'L', fontsize=14, ha='center') ax1.text(10, 340, 'L+α', fontsize=11, ha='center') ax1.text(60, 340, 'L+β', fontsize=11, ha='center') ax1.text(30, 290, 'α+β', fontsize=12, ha='center') ax1.set_xlabel('組成 B (at%)', fontsize=12) ax1.set_ylabel('温度 (°C)', fontsize=12) ax1.set_title('共晶型(Eutectic)', fontsize=14, fontweight='bold') ax1.set_xlim([0, 100]) ax1.set_ylim([280, 420]) ax1.legend(loc='upper right', fontsize=10) ax1.grid(True, alpha=0.3) # 包晶型(Peritectic) ax2 = axes[1] # 液相線 x_L = np.linspace(0, 1.0, 100) T_L = 500 - 150 * x_L ax2.plot(x_L * 100, T_L, 'r-', linewidth=2.5, label='液相線') # α相固相線 ax2.plot([0, 30], [500, 420], 'b-', linewidth=2.5, label='α相固相線') # β相固相線 ax2.plot([50, 100], [420, 350], 'g-', linewidth=2.5, label='β相固相線') # 包晶線 ax2.plot([0, 100], [420, 420], 'purple', linestyle='--', linewidth=2) # 包晶点 ax2.plot(40, 420, 'ko', markersize=12) ax2.text(40, 410, '包晶点 P', ha='center', fontsize=12, fontweight='bold') # 反応式 ax2.text(50, 470, 'L + α → β', fontsize=14, ha='center', bbox=dict(boxstyle='round', facecolor='lightblue', alpha=0.7)) # 領域ラベル ax2.text(20, 460, 'L', fontsize=14, ha='center') ax2.text(15, 440, 'L+α', fontsize=11, ha='center') ax2.text(60, 440, 'L+β', fontsize=11, ha='center') ax2.text(15, 390, 'α', fontsize=12, ha='center') ax2.text(70, 390, 'β', fontsize=12, ha='center') ax2.text(35, 390, 'α+β', fontsize=11, ha='center') ax2.set_xlabel('組成 B (at%)', fontsize=12) ax2.set_ylabel('温度 (°C)', fontsize=12) ax2.set_title('包晶型(Peritectic)', fontsize=14, fontweight='bold') ax2.set_xlim([0, 100]) ax2.set_ylim([340, 520]) ax2.legend(loc='upper right', fontsize=10) ax2.grid(True, alpha=0.3) plt.tight_layout() plt.savefig('phase_eutectic_peritectic_comparison.png', dpi=300, bbox_inches='tight') plt.show() plot_eutectic_peritectic() # 反応の解説 print("=== 共晶反応と包晶反応の比較 ===\n") print("【共晶反応(Eutectic)】") print(" 反応: L → α + β") print(" - 液相が冷却されて2つの固相に分離") print(" - 最低融点を持つ(融点降下)") print(" - 例: Pb-Sn (183°C), Al-Si (577°C)") print(" - 用途: ハンダ、鋳造合金") print() print("【包晶反応(Peritectic)】") print(" 反応: L + α → β") print(" - 液相と固相αが反応して固相βを生成") print(" - 非平衡凝固では α が残留しやすい") print(" - 例: Fe-C (1493°C), Pt-Ag") print(" - 課題: 均質化が困難、偏析が起こりやすい") print() print("【Gibbsの相律の確認】") print(" 共晶点/包晶点: C = 2, P = 3 → F = 2 - 3 + 2 = 1") print(" 圧力固定なら F = 0 → 温度・組成ともに固定(不変系)")

💻 例題3.6: 三元系相図の可視化

Python実装: 三元系相図(等温断面)
import numpy as np import matplotlib.pyplot as plt from matplotlib.patches import Polygon from matplotlib.collections import PatchCollection def ternary_to_cartesian(a, b, c): """三元系組成座標をデカルト座標に変換 Args: a, b, c: 各成分の原子分率(a + b + c = 1) Returns: x, y: デカルト座標 """ x = 0.5 * (2 * b + c) / (a + b + c) y = (np.sqrt(3) / 2) * c / (a + b + c) return x, y def plot_ternary_diagram(): """三元系相図の基本テンプレート""" fig, ax = plt.subplots(figsize=(10, 9)) # 三角形の頂点 vertices = np.array([ [0, 0], # A (左下) [1, 0], # B (右下) [0.5, np.sqrt(3)/2] # C (上) ]) # 三角形の枠 triangle = Polygon(vertices, fill=False, edgecolor='black', linewidth=2) ax.add_patch(triangle) # グリッド線(10%刻み) for i in range(1, 10): frac = i / 10 # A-B軸に平行(Cの割合が一定) x1, y1 = ternary_to_cartesian(1-frac, 0, frac) x2, y2 = ternary_to_cartesian(0, 1-frac, frac) ax.plot([x1, x2], [y1, y2], 'gray', linewidth=0.5, alpha=0.5) # B-C軸に平行(Aの割合が一定) x1, y1 = ternary_to_cartesian(frac, 1-frac, 0) x2, y2 = ternary_to_cartesian(frac, 0, 1-frac) ax.plot([x1, x2], [y1, y2], 'gray', linewidth=0.5, alpha=0.5) # C-A軸に平行(Bの割合が一定) x1, y1 = ternary_to_cartesian(1-frac, frac, 0) x2, y2 = ternary_to_cartesian(0, frac, 1-frac) ax.plot([x1, x2], [y1, y2], 'gray', linewidth=0.5, alpha=0.5) # 頂点ラベル ax.text(0, -0.05, 'A', fontsize=16, ha='center', fontweight='bold') ax.text(1, -0.05, 'B', fontsize=16, ha='center', fontweight='bold') ax.text(0.5, np.sqrt(3)/2 + 0.05, 'C', fontsize=16, ha='center', fontweight='bold') # 軸ラベル for i in [0.2, 0.4, 0.6, 0.8]: # A軸 x, y = ternary_to_cartesian(1-i, i, 0) ax.text(x, y - 0.03, f'{int(i*100)}', fontsize=9, ha='center', color='blue') # B軸 x, y = ternary_to_cartesian(i, 0, 1-i) ax.text(x + 0.03, y, f'{int(i*100)}', fontsize=9, ha='left', color='green') # C軸 x, y = ternary_to_cartesian(0, 1-i, i) ax.text(x - 0.03, y, f'{int(i*100)}', fontsize=9, ha='right', color='red') # 例: 単相領域と2相領域を描く(模擬データ) # 単相領域α(A-rich) alpha_region = np.array([ ternary_to_cartesian(1.0, 0.0, 0.0), ternary_to_cartesian(0.8, 0.2, 0.0), ternary_to_cartesian(0.8, 0.0, 0.2), ]) alpha_patch = Polygon(alpha_region, facecolor='lightblue', edgecolor='blue', linewidth=1.5, alpha=0.5, label='α相') ax.add_patch(alpha_patch) ax.text(0.85, 0.05, 'α', fontsize=14, ha='center', fontweight='bold') # 単相領域β(B-rich) beta_region = np.array([ ternary_to_cartesian(0.0, 1.0, 0.0), ternary_to_cartesian(0.2, 0.8, 0.0), ternary_to_cartesian(0.0, 0.8, 0.2), ]) beta_patch = Polygon(beta_region, facecolor='lightgreen', edgecolor='green', linewidth=1.5, alpha=0.5, label='β相') ax.add_patch(beta_patch) ax.text(0.15, 0.05, 'β', fontsize=14, ha='center', fontweight='bold') # 単相領域γ(C-rich) gamma_region = np.array([ ternary_to_cartesian(0.0, 0.0, 1.0), ternary_to_cartesian(0.2, 0.0, 0.8), ternary_to_cartesian(0.0, 0.2, 0.8), ]) gamma_patch = Polygon(gamma_region, facecolor='lightcoral', edgecolor='red', linewidth=1.5, alpha=0.5, label='γ相') ax.add_patch(gamma_patch) ax.text(0.5, 0.75, 'γ', fontsize=14, ha='center', fontweight='bold') # 2相領域 ax.text(0.5, 0.4, 'α+β+γ\n(3相領域)', fontsize=12, ha='center', bbox=dict(boxstyle='round', facecolor='yellow', alpha=0.6)) ax.set_xlim([-0.1, 1.1]) ax.set_ylim([-0.15, 1.0]) ax.set_aspect('equal') ax.axis('off') ax.set_title('三元系相図(A-B-C系、等温断面)', fontsize=15, fontweight='bold', pad=20) ax.legend(loc='upper right', fontsize=11) plt.tight_layout() plt.savefig('phase_ternary_diagram.png', dpi=300, bbox_inches='tight') plt.show() plot_ternary_diagram() # 三元系での組成計算例 print("=== 三元系組成の例 ===\n") # 例: Al-Cu-Mg系合金 compositions = [ ("Al 90% - Cu 5% - Mg 5%", 0.90, 0.05, 0.05), ("Al 70% - Cu 20% - Mg 10%", 0.70, 0.20, 0.10), ("Al 50% - Cu 30% - Mg 20%", 0.50, 0.30, 0.20), ] print(f"{'組成':<30} {'Al':<8} {'Cu':<8} {'Mg':<8} {'合計':<8}") print("-" * 65) for name, a, b, c in compositions: total = a + b + c print(f"{name:<30} {a:<8.2f} {b:<8.2f} {c:<8.2f} {total:<8.2f}") print("\n三元系相図の読み方:") print(" 1. 3つの頂点が純元素(A, B, C)") print(" 2. 各辺が二元系") print(" 3. 内部が三元系組成") print(" 4. Gibbsの相律: C = 3, T・P固定 → F = 3 - P") print(" - 1相領域: F = 2(組成2変数を自由に選べる)") print(" - 2相領域: F = 1(1変数のみ自由)") print(" - 3相領域: F = 0(組成固定、タイライン)")

💻 例題3.7: 相転移エンタルピーの計算

Python実装: 相転移エンタルピーとClapeyron式
import numpy as np import matplotlib.pyplot as plt from scipy.optimize import curve_fit # 実験データ(模擬): 水の蒸気圧 T_exp = np.array([273.15, 283.15, 293.15, 303.15, 313.15, 323.15, 333.15, 343.15, 353.15, 363.15, 373.15]) # K P_exp = np.array([611, 1228, 2339, 4246, 7384, 12350, 19940, 31190, 47410, 70140, 101325]) # Pa # Clausius-Clapeyron式のフィッティング # ln(P) = -L_vap/(R·T) + C def clausius_clapeyron_fit(T, L_vap, C): """ln(P) = -L_vap/(R·T) + C""" R = 8.314 return -L_vap / (R * T) + C # フィッティング params, cov = curve_fit(clausius_clapeyron_fit, T_exp, np.log(P_exp)) L_vap_fit, C_fit = params L_vap_err = np.sqrt(cov[0, 0]) print("=== Clausius-Clapeyron式のフィッティング ===\n") print(f"蒸発エンタルピー L_vap = {L_vap_fit:.0f} ± {L_vap_err:.0f} J/mol") print(f"文献値(100°C): 40660 J/mol") print(f"相対誤差: {abs(L_vap_fit - 40660) / 40660 * 100:.2f}%\n") # フィット結果の可視化 T_fit = np.linspace(270, 380, 100) P_fit = np.exp(clausius_clapeyron_fit(T_fit, L_vap_fit, C_fit)) fig, axes = plt.subplots(1, 2, figsize=(14, 6)) # 線形プロット ax1 = axes[0] ax1.plot(T_exp - 273.15, P_exp / 1e3, 'bo', markersize=8, label='実験データ') ax1.plot(T_fit - 273.15, P_fit / 1e3, 'r-', linewidth=2, label='フィット') ax1.set_xlabel('Temperature (°C)') ax1.set_ylabel('Vapor pressure (kPa)') ax1.set_title('水の蒸気圧曲線') ax1.legend() ax1.grid(True, alpha=0.3) # Clausius-Clapeyronプロット ax2 = axes[1] ax2.plot(1000 / T_exp, np.log(P_exp), 'bo', markersize=8, label='実験データ') ax2.plot(1000 / T_fit, np.log(P_fit), 'r-', linewidth=2, label='フィット') ax2.set_xlabel('1000/T (K⁻¹)') ax2.set_ylabel('ln(P)') ax2.set_title('Clausius-Clapeyronプロット') ax2.legend() ax2.grid(True, alpha=0.3) plt.tight_layout() plt.savefig('phase_transition_enthalpy_fit.png', dpi=300, bbox_inches='tight') plt.show() # 他の物質との比較 substances = { 'H₂O': 40660, 'C₂H₅OH (エタノール)': 38560, 'CH₃OH (メタノール)': 35270, 'C₆H₆ (ベンゼン)': 30720, 'N₂': 5577, 'O₂': 6820, } print("=== 蒸発エンタルピーの比較 ===\n") print(f"{'物質':<20} {'L_vap (J/mol)':<18} {'L_vap (kJ/mol)':<18}") print("-" * 60) for substance, L in substances.items(): print(f"{substance:<20} {L:<18.0f} {L/1000:<18.2f}") print("\n水素結合の影響:") print(" - 水は強い水素結合のため蒸発エンタルピーが大きい") print(" - エタノール・メタノールも水素結合を持つ") print(" - N₂, O₂は分子間力が弱く、蒸発エンタルピーが小さい")

📚 まとめ

💡 演習問題

  1. [Easy] Gibbsの相律を用いて、CO₂の三重点(固体・液体・気体共存)での自由度を求めよ。
  2. [Easy] Lever ruleを用いて、Pb-Sn系でSn 30%の合金が200℃でα相(Sn 15%)とβ相(Sn 90%)に分離するときの各相の分率を計算せよ。
  3. [Medium] 水の蒸発エンタルピーが40.66 kJ/molのとき、90℃での蒸気圧を計算せよ。ただし100℃での蒸気圧は1 atmとする。
  4. [Medium] van der Waals気体の臨界点で \(\left(\frac{\partial P}{\partial V}\right)_T = \left(\frac{\partial^2 P}{\partial V^2}\right)_T = 0\) となることを示し、これより \(T_c = \frac{8a}{27Rb}\) を導出せよ。
  5. [Hard] 二元共晶系で共晶組成が40%、共晶温度が300℃、純A・純Bの融点がそれぞれ500℃、450℃とする。液相線を放物線近似で求め、組成30%の合金の液相線温度を計算せよ。