🎯 学習目標
- Gibbsの相律 \(F = C - P + 2\) を理解し適用できる
- Clausius-Clapeyron式を導出し蒸気圧曲線を計算できる
- van der Waals状態方程式とMaxwell構成を理解する
- 二元系相図(共晶型、包晶型、固溶体型)を読める
- Lever rule(てこの規則)で相分率を計算できる
- 三元系相図の基礎を理解する
- 材料科学における相図の重要性を説明できる
📖 相平衡の基礎
相平衡条件
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
\]
- \(F\): 自由度(独立に変化させられる変数の数)
- \(C\): 成分数(独立な化学種の数)
- \(P\): 相の数
- \(2\): 温度と圧力
例:
- 水の三重点: \(C = 1\), \(P = 3\) (固体・液体・気体) → \(F = 0\) (自由度なし、一点で決まる)
- 水の沸点曲線: \(C = 1\), \(P = 2\) (液体・気体) → \(F = 1\) (温度を決めると圧力が決まる)
- 二元共晶点: \(C = 2\), \(P = 3\) → \(F = 1\) (圧力固定なら温度が決まる)
💻 例題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₂は分子間力が弱く、蒸発エンタルピーが小さい")
📚 まとめ
- Gibbsの相律 \(F = C - P + 2\) は系の自由度を決定する
- Clausius-Clapeyron式により相平衡曲線と相転移エンタルピーが関係づけられる
- van der Waals状態方程式のMaxwell構成で気液共存を記述できる
- 二元系相図には共晶型・包晶型・全率固溶体型などがある
- Lever ruleで2相共存領域の相分率を計算できる
- 共晶反応 (L → α + β) と包晶反応 (L + α → β) は重要な相転移
- 三元系相図は三角座標で表され、材料設計に重要
- 相図は材料のプロセス設計・組織制御に不可欠なツール
💡 演習問題
- [Easy] Gibbsの相律を用いて、CO₂の三重点(固体・液体・気体共存)での自由度を求めよ。
- [Easy] Lever ruleを用いて、Pb-Sn系でSn 30%の合金が200℃でα相(Sn 15%)とβ相(Sn 90%)に分離するときの各相の分率を計算せよ。
- [Medium] 水の蒸発エンタルピーが40.66 kJ/molのとき、90℃での蒸気圧を計算せよ。ただし100℃での蒸気圧は1 atmとする。
- [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}\) を導出せよ。
- [Hard] 二元共晶系で共晶組成が40%、共晶温度が300℃、純A・純Bの融点がそれぞれ500℃、450℃とする。液相線を放物線近似で求め、組成30%の合金の液相線温度を計算せよ。