🌐 EN | 🇯🇵 JP | Last sync: 2026-07-05

Chapter 5: Applications to Materials Science

🎯 Learning Objectives

📖 Solid Solutions and Intermetallic Compounds

Solid Solution

A single-phase solid in which two or more elements are randomly mixed within a crystal lattice

Substitutional solid solution: Solute atoms replace solvent atoms at lattice sites

  • Hume-Rothery rules: atomic radius difference <15%, identical crystal structure, similar electronegativity
  • Examples: Cu-Ni (complete solid solubility), Fe-Cr (stainless steel)

Interstitial solid solution: Solute atoms occupy interstitial sites of the lattice

  • Solute atoms are small (C, N, H, B)
  • Examples: Fe-C (steel), Ti-O

Intermetallic Compound

An ordered structure formed at a specific stoichiometric ratio

  • Examples: Fe₃C (cementite), Ni₃Al, TiAl
  • Characteristics: high strength, high brittleness, high melting point

💻 Code Example 5.1: Analyzing the Fe-C Phase Diagram

Python Implementation: Drawing and Reading the Fe-C Phase Diagram
import numpy as np import matplotlib.pyplot as plt def fe_c_phase_diagram(): """Simplified model of the Fe-C phase diagram""" # Composition (C wt%) # Liquidus x_liquidus = np.array([0, 0.5, 2.11, 4.3, 6.69]) T_liquidus = np.array([1538, 1500, 1148, 1148, 1130]) # γ phase (austenite) solidus x_gamma_solidus = np.array([0, 0.5, 2.11]) T_gamma_solidus = np.array([1495, 1450, 1148]) # α phase (ferrite) boundary x_alpha = np.array([0, 0.022, 0.022, 0, 0]) T_alpha = np.array([912, 727, 1495, 1538, 912]) # γ phase boundary x_gamma_boundary = np.array([0.022, 0.77, 2.11, 2.11, 0.77, 0.022]) T_gamma_boundary = np.array([727, 727, 1148, 1495, 1495, 1495]) # Cementite boundary x_cementite = np.array([6.69, 6.69]) T_cementite = np.array([727, 1130]) # Eutectic and eutectoid points eutectic_point = (4.3, 1148) # L → γ + Fe₃C eutectoid_point = (0.77, 727) # γ → α + Fe₃C return { 'liquidus': (x_liquidus, T_liquidus), 'gamma_solidus': (x_gamma_solidus, T_gamma_solidus), 'alpha': (x_alpha, T_alpha), 'gamma_boundary': (x_gamma_boundary, T_gamma_boundary), 'cementite': (x_cementite, T_cementite), 'eutectic': eutectic_point, 'eutectoid': eutectoid_point } # Draw the phase diagram phase_data = fe_c_phase_diagram() fig, ax = plt.subplots(figsize=(12, 8)) # Liquidus x_liq, T_liq = phase_data['liquidus'] ax.plot(x_liq, T_liq, 'r-', linewidth=2.5, label='Liquidus') # γ phase solidus x_g_sol, T_g_sol = phase_data['gamma_solidus'] ax.plot(x_g_sol, T_g_sol, 'b-', linewidth=2.5) # α phase boundary x_a, T_a = phase_data['alpha'] ax.plot(x_a, T_a, 'g-', linewidth=2.5, label='α phase (ferrite)') # γ phase boundary x_g, T_g = phase_data['gamma_boundary'] ax.plot(x_g, T_g, 'b-', linewidth=2.5, label='γ phase (austenite)') # Cementite boundary x_cem, T_cem = phase_data['cementite'] ax.plot([6.69], [1130], 'ko', markersize=8) ax.axvline(6.69, color='purple', linestyle='--', linewidth=1.5, label='Fe₃C (cementite)') # Key points eutectic = phase_data['eutectic'] eutectoid = phase_data['eutectoid'] ax.plot(eutectic[0], eutectic[1], 'ro', markersize=12, label=f'Eutectic point (C {eutectic[0]:.1f}%, {eutectic[1]}°C)') ax.plot(eutectoid[0], eutectoid[1], 'mo', markersize=12, label=f'Eutectoid point (C {eutectoid[0]:.2f}%, {eutectoid[1]}°C)') # Region labels ax.text(0.2, 1000, 'δ-Fe', fontsize=12, ha='center') ax.text(0.2, 1300, 'L (liquid)', fontsize=13, ha='center', fontweight='bold') ax.text(0.5, 1000, 'γ\n(austenite)', fontsize=12, ha='center') ax.text(0.01, 850, 'α\n(ferrite)', fontsize=12, ha='center') ax.text(0.4, 650, 'α + Fe₃C\n(pearlite)', fontsize=11, ha='center') ax.text(3.0, 1000, 'L + γ', fontsize=11, ha='center') ax.text(5.0, 1000, 'L + Fe₃C', fontsize=11, ha='center') # Vertical lines marking important carbon concentrations ax.axvline(0.022, color='gray', linestyle=':', alpha=0.5) ax.text(0.022, 500, '0.022%\n(max. solubility in α)', fontsize=9, ha='center') ax.axvline(0.77, color='gray', linestyle=':', alpha=0.5) ax.text(0.77, 500, '0.77%\n(eutectoid point)', fontsize=9, ha='center') ax.axvline(2.11, color='gray', linestyle=':', alpha=0.5) ax.text(2.11, 500, '2.11%\n(max. solubility in γ)', fontsize=9, ha='center') ax.set_xlabel('Carbon content (wt%)', fontsize=13) ax.set_ylabel('Temperature (°C)', fontsize=13) ax.set_title('Fe-C Phase Diagram (Iron-Carbon System)', fontsize=15, fontweight='bold') ax.set_xlim([0, 7]) ax.set_ylim([400, 1600]) ax.legend(loc='upper right', fontsize=10) ax.grid(True, alpha=0.3) plt.tight_layout() plt.savefig('materials_fe_c_phase_diagram.png', dpi=300, bbox_inches='tight') plt.show() # Classification of Fe-C alloys print("=== Classification of Fe-C Alloys ===\n") print(f"{'Type':<25} {'Carbon content (wt%)':<22} {'Main microstructure':<35}") print("-" * 65) print(f"{'Pure iron':<25} {'< 0.008':<22} {'α-Fe (ferrite)':<35}") print(f"{'Steel':<25} {'0.008 - 2.11':<22} {'Ferrite + cementite':<35}") print(f"{' Hypoeutectoid steel':<25} {'< 0.77':<22} {'Mostly ferrite + pearlite':<35}") print(f"{' Eutectoid steel':<25} {'= 0.77':<22} {'Pearlite (lamellar structure)':<35}") print(f"{' Hypereutectoid steel':<25} {'0.77 - 2.11':<22} {'Cementite + pearlite':<35}") print(f"{'Cast iron':<25} {'2.11 - 6.69':<22} {'γ + graphite/Fe₃C':<35}") print() print("Key phase-transformation temperatures:") print(f" A₁: 727°C (eutectoid temperature, γ → α + Fe₃C)") print(f" A₃: 912°C (start of α → γ transformation)") print(f" Melting point: 1538°C (pure iron)")

💻 Code Example 5.2: Calculating the α-γ Transformation Temperature

α-γ Transformation (Ferrite-Austenite Transformation)

Allotropic transformation of iron:

\[ \text{α-Fe (BCC)} \xrightarrow{912°C} \text{γ-Fe (FCC)} \]

Phase equilibrium condition: equal chemical potentials

\[ \mu^\alpha(T, P) = \mu^\gamma(T, P) \]

Gibbs free energy difference:

\[ \Delta G^{\gamma \to \alpha} = \Delta H - T \Delta S \]

Python Implementation: Thermodynamic Calculation of the α-γ Transformation
import numpy as np import matplotlib.pyplot as plt from scipy.optimize import fsolve # Parameters for the α-γ transformation (pure iron) def gibbs_alpha_gamma(T): """Gibbs free energy difference between the α and γ phases Args: T: temperature (K) Returns: ΔG^(γ→α): Gibbs free energy difference (J/mol) """ # Model based on experimental values # ΔH ≈ -900 J/mol, ΔS ≈ -1.0 J/(mol·K) (simplified) T_eq = 1185 # 912°C = 1185 K (α-γ transformation point) DH = -900 # J/mol (transformation enthalpy) DS = DH / T_eq # J/(mol·K) DG = DH - T * DS return DG # Temperature range T_range = np.linspace(800, 1400, 100) # K DG_values = [gibbs_alpha_gamma(T) for T in T_range] # Compute the transformation point T_transformation = fsolve(gibbs_alpha_gamma, 1185)[0] # Visualization fig, axes = plt.subplots(1, 2, figsize=(14, 6)) # Gibbs free energy difference ax1 = axes[0] ax1.plot(T_range - 273.15, DG_values, 'b-', linewidth=2.5) ax1.axhline(0, color='r', linestyle='--', linewidth=2, label='ΔG = 0 (equilibrium)') ax1.axvline(T_transformation - 273.15, color='g', linestyle='--', linewidth=2, label=f'Transformation point {T_transformation - 273.15:.0f}°C') # Regions of phase stability ax1.fill_between(T_range - 273.15, -2000, 0, where=(T_range < T_transformation), color='lightblue', alpha=0.3, label='α phase stable') ax1.fill_between(T_range - 273.15, 0, 2000, where=(T_range > T_transformation), color='lightcoral', alpha=0.3, label='γ phase stable') ax1.set_xlabel('Temperature (°C)') ax1.set_ylabel('ΔG^(γ→α) (J/mol)') ax1.set_title('Gibbs Free Energy of the α-γ Transformation') ax1.legend() ax1.grid(True, alpha=0.3) # Change in transformation temperature with carbon content # Simplified model: T_γ→α = 912°C - k·[C%] C_range = np.linspace(0, 1.5, 50) # C wt% k = 200 # °C/wt% (empirical rule) T_gamma_alpha = 912 - k * C_range ax2 = axes[1] ax2.plot(C_range, T_gamma_alpha, 'r-', linewidth=2.5, label='A₃ line (α-γ transformation start)') ax2.axhline(727, color='purple', linestyle='--', linewidth=2, label='A₁ line (eutectoid temperature)') # Important composition ax2.axvline(0.77, color='gray', linestyle=':', alpha=0.5) ax2.text(0.77, 650, 'Eutectoid composition\n0.77%', fontsize=10, ha='center') # Region labels ax2.text(0.3, 850, 'γ phase\n(austenite)', fontsize=12, ha='center', bbox=dict(boxstyle='round', facecolor='lightcoral', alpha=0.5)) ax2.text(0.3, 750, 'α + γ', fontsize=11, ha='center') ax2.text(0.3, 650, 'α + Fe₃C\n(ferrite + cementite)', fontsize=11, ha='center', bbox=dict(boxstyle='round', facecolor='lightblue', alpha=0.5)) ax2.set_xlabel('Carbon content (wt%)') ax2.set_ylabel('Temperature (°C)') ax2.set_title('Carbon Content and α-γ Transformation Temperature') ax2.set_xlim([0, 1.5]) ax2.set_ylim([600, 950]) ax2.legend() ax2.grid(True, alpha=0.3) plt.tight_layout() plt.savefig('materials_alpha_gamma_transformation.png', dpi=300, bbox_inches='tight') plt.show() # Results print("=== Thermodynamics of the α-γ Transformation ===\n") print(f"Transformation point (calculated): {T_transformation - 273.15:.2f}°C") print(f"Experimental value: 912°C") print() print("Depression of the transformation point by carbon:") print(f"{'C content (wt%)':<18} {'Transformation temperature (°C)':<32}") print("-" * 35) for C in [0, 0.2, 0.4, 0.6, 0.77]: T = 912 - k * C print(f"{C:<18.2f} {T:<32.0f}") print("\nPhysical significance:") print(" - The α phase (BCC) has low carbon solubility (0.022% at 727°C)") print(" - The γ phase (FCC) has high carbon solubility (2.11% at 1148°C)") print(" - Adding carbon stabilizes the γ phase → lower transformation temperature")

💻 Code Example 5.3: Thermodynamics of the Martensitic Transformation

Martensitic Transformation

Diffusionless transformation: a supersaturated solid solution formed from the γ phase (FCC) by rapid quenching

Characteristics:

  • No diffusion (cooperative shear deformation of atoms)
  • Metastable phase (non-equilibrium)
  • High hardness, high brittleness
  • Crystal structure: BCT (body-centered tetragonal)

Ms point (Martensite start): temperature at which the martensitic transformation begins

\[ M_s = 539 - 423[\text{C}] - 30.4[\text{Mn}] - 12.1[\text{Cr}] - 17.7[\text{Ni}] \quad (°C) \]

Python Implementation: Ms Point Calculation and Cooling Curves
import numpy as np import matplotlib.pyplot as plt def calculate_Ms(C, Mn=0, Cr=0, Ni=0): """Calculate the martensite start temperature (Ms point) Empirical rule based on the Andrews equation Args: C: carbon content (wt%) Mn: manganese content (wt%) Cr: chromium content (wt%) Ni: nickel content (wt%) Returns: Ms: martensite start temperature (°C) """ Ms = 539 - 423*C - 30.4*Mn - 12.1*Cr - 17.7*Ni return Ms # Variation of the Ms point with carbon content C_range = np.linspace(0.1, 1.2, 50) Ms_values = [calculate_Ms(C) for C in C_range] # Effect of alloying elements Mn_range = [0, 1.0, 2.0] Ni_range = [0, 2.0, 4.0] fig, axes = plt.subplots(1, 2, figsize=(14, 6)) # Effect of carbon content ax1 = axes[0] ax1.plot(C_range, Ms_values, 'b-', linewidth=2.5, label='Plain carbon steel') # Effect of Mn addition for Mn in Mn_range[1:]: Ms_Mn = [calculate_Ms(C, Mn=Mn) for C in C_range] ax1.plot(C_range, Ms_Mn, '--', linewidth=2, label=f'+ {Mn:.1f}% Mn') ax1.axhline(0, color='red', linestyle=':', linewidth=1.5, label='Room temperature') ax1.set_xlabel('Carbon content (wt%)') ax1.set_ylabel('Ms point (°C)') ax1.set_title('Martensite Start Temperature (Ms Point)') ax1.legend() ax1.grid(True, alpha=0.3) ax1.set_ylim([-100, 550]) # Cooling curves and martensite fraction ax2 = axes[1] # Koistinen-Marburger equation: f_M = 1 - exp(-α(Ms - T)) def martensite_fraction(T, Ms, alpha=0.011): """Martensite fraction Args: T: temperature (°C) Ms: Ms point (°C) alpha: constant (typically 0.011) Returns: f_M: martensite fraction """ if T >= Ms: return 0 else: f_M = 1 - np.exp(-alpha * (Ms - T)) return f_M # Martensite fraction for different carbon contents T_range = np.linspace(-100, 800, 200) C_values = [0.3, 0.6, 0.9] colors = ['blue', 'green', 'red'] for C, color in zip(C_values, colors): Ms = calculate_Ms(C) f_M = [martensite_fraction(T, Ms) for T in T_range] ax2.plot(T_range, f_M, color=color, linewidth=2.5, label=f'C {C:.1f}% (Ms={Ms:.0f}°C)') ax2.axvline(25, color='purple', linestyle='--', linewidth=1.5, label='Room temperature') ax2.set_xlabel('Temperature (°C)') ax2.set_ylabel('Martensite fraction') ax2.set_title('Temperature Dependence of the Martensite Fraction') ax2.legend() ax2.grid(True, alpha=0.3) ax2.set_xlim([-100, 800]) ax2.set_ylim([0, 1]) plt.tight_layout() plt.savefig('materials_martensite_transformation.png', dpi=300, bbox_inches='tight') plt.show() # Example calculation print("=== Ms Points of the Martensitic Transformation ===\n") steels = [ ("Low-carbon steel", 0.2, 0, 0, 0), ("Medium-carbon steel", 0.6, 0, 0, 0), ("High-carbon steel", 1.0, 0, 0, 0), ("Mn steel", 0.6, 2.0, 0, 0), ("Cr-Ni steel", 0.6, 1.0, 12.0, 8.0), ] print(f"{'Steel type':<20} {'C%':<6} {'Mn%':<6} {'Cr%':<6} {'Ni%':<6} {'Ms point (°C)':<14}") print("-" * 60) for name, C, Mn, Cr, Ni in steels: Ms = calculate_Ms(C, Mn, Cr, Ni) print(f"{name:<20} {C:<6.1f} {Mn:<6.1f} {Cr:<6.1f} {Ni:<6.1f} {Ms:<14.0f}") print("\nMartensite fraction at room temperature (25°C):") for name, C, Mn, Cr, Ni in steels: Ms = calculate_Ms(C, Mn, Cr, Ni) f_M = martensite_fraction(25, Ms) print(f" {name}: {f_M*100:.1f}%") print("\nKey insights:") print(" - Carbon ↑ → Ms point ↓ (martensite forms less readily)") print(" - Alloying elements (Mn, Cr, Ni) also lower the Ms point") print(" - When Ms < room temperature, retained austenite remains")

💻 Code Example 5.4: Drawing a TTT Diagram (Time-Temperature-Transformation)

What Is a TTT Diagram?

Isothermal transformation diagram: shows transformation start and finish times during isothermal holding

Main transformations:

  • Pearlite transformation: diffusional, lamellar structure (α + Fe₃C)
  • Bainite transformation: intermediate, acicular structure
  • Martensitic transformation: diffusionless, formed by rapid quenching

Uses: heat-treatment process design, microstructure prediction

Python Implementation: Generating Simulated TTT Diagram Data
import numpy as np import matplotlib.pyplot as plt def generate_TTT_diagram(C_content=0.8): """Generate simulated TTT diagram data Args: C_content: carbon content (wt%) Returns: TTT diagram data """ # Temperature range T_range = np.linspace(200, 700, 100) # Pearlite transformation (C-curve) # Nose point (shortest transformation time): ~550°C, ~1 second T_pearlite_nose = 550 t_pearlite_nose = 1.0 # seconds def pearlite_curve(T, factor): """Pearlite transformation curve (based on the Johnson-Mehl-Avrami equation)""" if T < 500 or T > 727: return 1e10 # No transformation else: # Shape of the C-curve t = t_pearlite_nose * factor * np.exp(abs(T - T_pearlite_nose) / 50) return t t_pearlite_start = [pearlite_curve(T, 1) for T in T_range] t_pearlite_finish = [pearlite_curve(T, 10) for T in T_range] # Bainite transformation T_bainite_nose = 350 t_bainite_nose = 10.0 def bainite_curve(T, factor): if T < 250 or T > 500: return 1e10 else: t = t_bainite_nose * factor * np.exp(abs(T - T_bainite_nose) / 30) return t t_bainite_start = [bainite_curve(T, 1) for T in T_range] t_bainite_finish = [bainite_curve(T, 10) for T in T_range] # Martensite (instantaneous) Ms = calculate_Ms(C_content) Mf = Ms - 215 # Mf point (martensite finish) return { 'T_range': T_range, 'pearlite_start': t_pearlite_start, 'pearlite_finish': t_pearlite_finish, 'bainite_start': t_bainite_start, 'bainite_finish': t_bainite_finish, 'Ms': Ms, 'Mf': Mf } # Draw the TTT diagram C = 0.8 # Eutectoid steel ttt_data = generate_TTT_diagram(C) fig, ax = plt.subplots(figsize=(12, 8)) T = ttt_data['T_range'] t_p_s = np.array(ttt_data['pearlite_start']) t_p_f = np.array(ttt_data['pearlite_finish']) t_b_s = np.array(ttt_data['bainite_start']) t_b_f = np.array(ttt_data['bainite_finish']) # Mask times to the valid range valid_p_s = t_p_s < 1e6 valid_p_f = t_p_f < 1e6 valid_b_s = t_b_s < 1e6 valid_b_f = t_b_f < 1e6 # Pearlite ax.semilogx(t_p_s[valid_p_s], T[valid_p_s], 'b-', linewidth=2.5, label='Pearlite transformation start') ax.semilogx(t_p_f[valid_p_f], T[valid_p_f], 'b--', linewidth=2.5, label='Pearlite transformation finish') ax.fill_betweenx(T[valid_p_s], t_p_s[valid_p_s], t_p_f[valid_p_f], where=valid_p_s & valid_p_f, color='lightblue', alpha=0.3) # Bainite ax.semilogx(t_b_s[valid_b_s], T[valid_b_s], 'g-', linewidth=2.5, label='Bainite transformation start') ax.semilogx(t_b_f[valid_b_f], T[valid_b_f], 'g--', linewidth=2.5, label='Bainite transformation finish') ax.fill_betweenx(T[valid_b_s], t_b_s[valid_b_s], t_b_f[valid_b_f], where=valid_b_s & valid_b_f, color='lightgreen', alpha=0.3) # Martensite Ms = ttt_data['Ms'] Mf = ttt_data['Mf'] ax.axhline(Ms, color='red', linestyle='-', linewidth=2.5, label=f'Ms = {Ms:.0f}°C') ax.axhline(Mf, color='red', linestyle='--', linewidth=2, label=f'Mf = {Mf:.0f}°C') ax.fill_between([1e-2, 1e6], Mf, Ms, color='lightcoral', alpha=0.3) # Example cooling curves # Slow cooling: pearlite formation t_cool_slow = np.logspace(-1, 3, 50) T_cool_slow = 900 - 100 * np.log10(t_cool_slow + 1) ax.plot(t_cool_slow, T_cool_slow, 'purple', linewidth=2, linestyle=':', label='Slow cooling (furnace cooling) → pearlite') # Rapid cooling: martensite formation t_cool_fast = np.logspace(-2, 0, 30) T_cool_fast = 900 - 800 * t_cool_fast ax.plot(t_cool_fast, T_cool_fast, 'orange', linewidth=2, linestyle=':', label='Rapid cooling (water quenching) → martensite') # Labels ax.text(10, 600, 'Pearlite', fontsize=14, fontweight='bold', bbox=dict(boxstyle='round', facecolor='lightblue', alpha=0.7)) ax.text(100, 350, 'Bainite', fontsize=14, fontweight='bold', bbox=dict(boxstyle='round', facecolor='lightgreen', alpha=0.7)) ax.text(0.5, Ms-50, 'Martensite', fontsize=14, fontweight='bold', bbox=dict(boxstyle='round', facecolor='lightcoral', alpha=0.7)) ax.set_xlabel('Time (s)', fontsize=13) ax.set_ylabel('Temperature (°C)', fontsize=13) ax.set_title(f'TTT Diagram (Time-Temperature-Transformation)\nC {C}% steel', fontsize=15, fontweight='bold') ax.set_xlim([1e-2, 1e6]) ax.set_ylim([0, 800]) ax.legend(loc='upper right', fontsize=10) ax.grid(True, alpha=0.3, which='both') plt.tight_layout() plt.savefig('materials_TTT_diagram.png', dpi=300, bbox_inches='tight') plt.show() print("=== How to Read a TTT Diagram ===\n") print("1. Vertical axis: temperature (cooling from the austenitizing temperature)") print("2. Horizontal axis: time (logarithmic scale)") print("3. C-curves: transformation start and finish times") print() print("Determining the microstructure:") print(" - Slow cooling (crosses the curves) → pearlite") print(" - Intermediate cooling → bainite") print(" - Rapid cooling (avoids the curves, below Ms) → martensite") print() print("Practical examples:") print(" - Quenching: rapid cooling to form martensite (high hardness)") print(" - Annealing: slow cooling to form pearlite (softening)") print(" - Tempering: reheating martensite to improve toughness")

💻 Code Example 5.5: Calculating a CCT Diagram (Continuous Cooling Transformation)

Python Implementation: CCT Diagram and Cooling Rates
import numpy as np import matplotlib.pyplot as plt def generate_CCT_diagram(C_content=0.4): """Simulated CCT diagram data (continuous cooling transformation diagram) Args: C_content: carbon content (wt%) """ # Curves are shifted to the right compared with the TTT diagram T_range = np.linspace(200, 750, 100) # Ferrite + pearlite transformation T_fp_nose = 600 t_fp_nose = 10.0 def fp_curve(T, factor): if T < 500 or T > 750: return 1e10 else: t = t_fp_nose * factor * np.exp(abs(T - T_fp_nose) / 60) return t t_fp_start = [fp_curve(T, 1) for T in T_range] t_fp_finish = [fp_curve(T, 50) for T in T_range] # Bainite T_b_nose = 400 t_b_nose = 100.0 def b_curve(T, factor): if T < 300 or T > 550: return 1e10 else: t = t_b_nose * factor * np.exp(abs(T - T_b_nose) / 40) return t t_b_start = [b_curve(T, 1) for T in T_range] t_b_finish = [b_curve(T, 50) for T in T_range] # Martensite Ms = calculate_Ms(C_content) Mf = Ms - 215 return { 'T_range': T_range, 'fp_start': t_fp_start, 'fp_finish': t_fp_finish, 'b_start': t_b_start, 'b_finish': t_b_finish, 'Ms': Ms, 'Mf': Mf } # Generate cooling curves def cooling_curve(cooling_rate, T_start=900, T_final=25): """Cooling curve Args: cooling_rate: cooling rate (°C/s) T_start: initial temperature (°C) T_final: final temperature (°C) Returns: t, T: time, temperature """ t_total = (T_start - T_final) / cooling_rate t = np.linspace(0, t_total, 200) T = T_start - cooling_rate * t T = np.maximum(T, T_final) return t, T # Draw the CCT diagram C = 0.4 # Medium-carbon steel cct_data = generate_CCT_diagram(C) fig, ax = plt.subplots(figsize=(12, 8)) T = cct_data['T_range'] t_fp_s = np.array(cct_data['fp_start']) t_fp_f = np.array(cct_data['fp_finish']) t_b_s = np.array(cct_data['b_start']) t_b_f = np.array(cct_data['b_finish']) # Valid range valid_fp_s = t_fp_s < 1e6 valid_fp_f = t_fp_f < 1e6 valid_b_s = t_b_s < 1e6 valid_b_f = t_b_f < 1e6 # Ferrite + pearlite ax.semilogx(t_fp_s[valid_fp_s], T[valid_fp_s], 'b-', linewidth=2.5, label='F+P start') ax.semilogx(t_fp_f[valid_fp_f], T[valid_fp_f], 'b--', linewidth=2.5, label='F+P finish') ax.fill_betweenx(T, t_fp_s, t_fp_f, where=valid_fp_s & valid_fp_f, color='lightblue', alpha=0.3) # Bainite ax.semilogx(t_b_s[valid_b_s], T[valid_b_s], 'g-', linewidth=2.5, label='B start') ax.semilogx(t_b_f[valid_b_f], T[valid_b_f], 'g--', linewidth=2.5, label='B finish') ax.fill_betweenx(T, t_b_s, t_b_f, where=valid_b_s & valid_b_f, color='lightgreen', alpha=0.3) # Martensite Ms = cct_data['Ms'] Mf = cct_data['Mf'] ax.axhline(Ms, color='red', linestyle='-', linewidth=2.5, label=f'Ms = {Ms:.0f}°C') ax.axhline(Mf, color='red', linestyle='--', linewidth=2, label=f'Mf = {Mf:.0f}°C') # Various cooling rates cooling_rates = [0.5, 5, 50, 500] # °C/s colors = ['purple', 'orange', 'brown', 'magenta'] microstructures = ['F+P', 'F+P+B', 'B+M', 'M'] for i, (cr, color, micro) in enumerate(zip(cooling_rates, colors, microstructures)): t_cool, T_cool = cooling_curve(cr) ax.plot(t_cool, T_cool, color=color, linewidth=2, linestyle=':', label=f'{cr} °C/s → {micro}') # Show the critical cooling rate ax.axvline(1, color='gray', linestyle='--', alpha=0.5) ax.text(1, 100, 'Critical cooling rate\n(martensite forms at\nrates above this)', fontsize=10, ha='center', bbox=dict(boxstyle='round', facecolor='yellow', alpha=0.6)) ax.set_xlabel('Time (s)', fontsize=13) ax.set_ylabel('Temperature (°C)', fontsize=13) ax.set_title(f'CCT Diagram (Continuous Cooling Transformation)\nC {C}% steel', fontsize=15, fontweight='bold') ax.set_xlim([1e-1, 1e4]) ax.set_ylim([0, 900]) ax.legend(loc='upper right', fontsize=9) ax.grid(True, alpha=0.3, which='both') plt.tight_layout() plt.savefig('materials_CCT_diagram.png', dpi=300, bbox_inches='tight') plt.show() # Relationship between cooling rate and microstructure print("=== CCT Diagram and Cooling Rates ===\n") print(f"{'Cooling rate (°C/s)':<22} {'Cooling time (900→25°C)':<27} {'Microstructure':<35}") print("-" * 75) for cr in cooling_rates: t_total = (900 - 25) / cr # Microstructure determination (simplified) if cr < 1: micro = "Ferrite + pearlite" elif cr < 10: micro = "Ferrite + pearlite + bainite" elif cr < 100: micro = "Bainite + martensite" else: micro = "Martensite" print(f"{cr:<22.1f} {t_total:<27.1f} {micro:<35}") print("\nCooling methods and cooling rates:") print(" - Furnace cooling: ~0.1-1 °C/s") print(" - Air cooling: ~1-10 °C/s") print(" - Oil quenching: ~10-100 °C/s") print(" - Water quenching: ~100-1000 °C/s") print() print("TTT diagram vs. CCT diagram:") print(" - TTT: isothermal transformation (laboratory conditions)") print(" - CCT: continuous cooling transformation (practical conditions)") print(" - CCT curves lie to the right of TTT curves (longer times)")

💻 Code Example 5.6: Calculating the Mixing Enthalpy of an Alloy

Python Implementation: Mixing Enthalpy of a Binary System
import numpy as np import matplotlib.pyplot as plt def mixing_enthalpy(x_B, H_AB, omega=0): """Mixing enthalpy of a binary alloy A-B Regular solution model: ΔH_mix = Ω x_A x_B Args: x_B: atomic fraction of B H_AB: interaction parameter (A-B bond energy) omega: interaction parameter Ω = z(ε_AB - (ε_AA + ε_BB)/2) Returns: ΔH_mix: mixing enthalpy (J/mol) """ x_A = 1 - x_B if omega == 0: omega = H_AB DH_mix = omega * x_A * x_B return DH_mix def mixing_entropy(x_B, R=8.314): """Configurational entropy of an ideal solution Args: x_B: atomic fraction of B R: gas constant (J/(mol·K)) Returns: ΔS_mix: mixing entropy (J/(mol·K)) """ x_A = 1 - x_B if x_A == 0 or x_B == 0: return 0 else: DS_mix = -R * (x_A * np.log(x_A) + x_B * np.log(x_B)) return DS_mix def mixing_gibbs(x_B, T, omega, R=8.314): """Gibbs free energy of mixing ΔG_mix = ΔH_mix - T ΔS_mix Args: x_B: atomic fraction of B T: temperature (K) omega: interaction parameter (J/mol) R: gas constant Returns: ΔG_mix: Gibbs free energy of mixing (J/mol) """ DH = mixing_enthalpy(x_B, 0, omega) DS = mixing_entropy(x_B, R) DG = DH - T * DS return DG # Composition range x_B_range = np.linspace(0.001, 0.999, 200) # Interaction parameters (different systems) omega_values = { 'Ideal solution (Ω=0)': 0, 'Weak attraction (Ω=-5kJ/mol)': -5000, 'Strong repulsion (Ω=+20kJ/mol)': 20000, } fig, axes = plt.subplots(2, 2, figsize=(14, 10)) # Mixing enthalpy ax1 = axes[0, 0] for name, omega in omega_values.items(): DH_values = [mixing_enthalpy(x, 0, omega) for x in x_B_range] ax1.plot(x_B_range, np.array(DH_values) / 1000, linewidth=2.5, label=name) ax1.set_xlabel('Composition x_B') ax1.set_ylabel('ΔH_mix (kJ/mol)') ax1.set_title('Mixing Enthalpy') ax1.legend() ax1.grid(True, alpha=0.3) ax1.axhline(0, color='black', linestyle='--', linewidth=1) # Mixing entropy ax2 = axes[0, 1] DS_values = [mixing_entropy(x) for x in x_B_range] ax2.plot(x_B_range, DS_values, 'g-', linewidth=2.5) ax2.set_xlabel('Composition x_B') ax2.set_ylabel('ΔS_mix (J/(mol·K))') ax2.set_title('Mixing Entropy (Ideal Solution)') ax2.grid(True, alpha=0.3) # Gibbs free energy of mixing (temperature dependence) ax3 = axes[1, 0] T_values = [300, 600, 1000, 1500] omega_example = 20000 # Repulsive system for T in T_values: DG_values = [mixing_gibbs(x, T, omega_example) for x in x_B_range] ax3.plot(x_B_range, np.array(DG_values) / 1000, linewidth=2.5, label=f'T = {T} K') ax3.set_xlabel('Composition x_B') ax3.set_ylabel('ΔG_mix (kJ/mol)') ax3.set_title(f'Gibbs Free Energy of Mixing (Ω = {omega_example/1000:.0f} kJ/mol)') ax3.legend() ax3.grid(True, alpha=0.3) ax3.axhline(0, color='black', linestyle='--', linewidth=1) # Determining phase separation (spinodal where ∂²ΔG/∂x² < 0) ax4 = axes[1, 1] T = 600 omega_spinodal = 20000 DG_values = [mixing_gibbs(x, T, omega_spinodal) for x in x_B_range] ax4.plot(x_B_range, np.array(DG_values) / 1000, 'b-', linewidth=2.5, label='ΔG_mix') # Tangent line (find the phase-separation compositions by the common tangent method - simplified) # Shown here only visually ax4.axhline(0, color='gray', linestyle='--', linewidth=1, alpha=0.5) ax4.text(0.5, 3, f'T = {T} K\nTendency toward phase separation', fontsize=11, ha='center', bbox=dict(boxstyle='round', facecolor='yellow', alpha=0.6)) ax4.set_xlabel('Composition x_B') ax4.set_ylabel('ΔG_mix (kJ/mol)') ax4.set_title('Phase Separation and Common Tangent') ax4.legend() ax4.grid(True, alpha=0.3) plt.tight_layout() plt.savefig('materials_mixing_enthalpy.png', dpi=300, bbox_inches='tight') plt.show() # Analysis print("=== Mixing Enthalpy and Phase Stability ===\n") print("Meaning of the interaction parameter Ω:") print(" Ω < 0: A-B bonds are stronger than A-A, B-B → attraction, tendency to form a solid solution") print(" Ω = 0: ideal solution (completely random)") print(" Ω > 0: A-B bonds are weak → repulsion, tendency toward phase separation") print() print("Competition in ΔG_mix = ΔH_mix - T ΔS_mix:") print(" - Low temperature: ΔH_mix dominates → phase separation if Ω>0") print(" - High temperature: -TΔS_mix dominates → mixing driven by the entropy term") print() # Critical temperature calculation (simplified) omega_critical = 20000 R = 8.314 T_critical = omega_critical / (2 * R) # Critical temperature of a regular solution print(f"Critical temperature (regular solution model):") print(f" T_c = Ω / (2R) = {omega_critical/1000:.0f} / (2 × 8.314)") print(f" T_c ≈ {T_critical:.0f} K = {T_critical - 273:.0f} °C") print(f" T > T_c: complete solid solubility") print(f" T < T_c: phase separation (miscibility gap)")

💻 Code Example 5.7: Configurational Entropy of High-Entropy Alloys (HEAs)

Python Implementation: Configurational Entropy of Multicomponent Systems
import numpy as np import matplotlib.pyplot as plt from itertools import combinations def configurational_entropy_multicomponent(composition, R=8.314): """Configurational entropy of a multicomponent system ΔS_conf = -R Σ x_i ln(x_i) Args: composition: atomic fractions of each element (list) R: gas constant (J/(mol·K)) Returns: ΔS_conf: configurational entropy (J/(mol·K)) """ x = np.array(composition) x = x[x > 0] # Exclude zeros if np.sum(x) != 1.0: x = x / np.sum(x) # Normalize DS_conf = -R * np.sum(x * np.log(x)) return DS_conf # Representative high-entropy alloys (HEAs) HEAs = { 'CoCrFeMnNi (Cantor alloy)': [0.2, 0.2, 0.2, 0.2, 0.2], # Equiatomic quinary system 'AlCoCrFeNi': [0.2, 0.2, 0.2, 0.2, 0.2], 'CoCrFeNi': [0.25, 0.25, 0.25, 0.25], # Quaternary system 'TiZrHfNbTa': [0.2, 0.2, 0.2, 0.2, 0.2], # Refractory HEA } # Traditional alloys (for comparison) traditional_alloys = { 'Stainless steel (SUS304)': [0.70, 0.18, 0.08, 0.04], # Fe, Cr, Ni, Mn (simplified) 'Al alloy (7075)': [0.90, 0.05, 0.03, 0.02], # Al, Zn, Mg, Cu (simplified) 'Brass': [0.70, 0.30], # Cu, Zn } print("=== Configurational Entropy of High-Entropy Alloys (HEAs) ===\n") print(f"{'Alloy':<30} {'Elements':<10} {'ΔS_conf (J/(mol·K))':<25}") print("-" * 65) # HEA calculations for name, comp in HEAs.items(): n_elements = len(comp) DS = configurational_entropy_multicomponent(comp) print(f"{name:<30} {n_elements:<10} {DS:<25.4f}") print("\nTraditional alloys (comparison):") for name, comp in traditional_alloys.items(): n_elements = len(comp) DS = configurational_entropy_multicomponent(comp) print(f"{name:<30} {n_elements:<10} {DS:<25.4f}") # Theoretical maximum values R = 8.314 print("\nTheoretical maximum values:") for n in [2, 3, 4, 5, 6]: DS_max = R * np.log(n) print(f" {n}-component (equiatomic): ΔS_conf = R ln({n}) = {DS_max:.4f} J/(mol·K)") # Visualization fig, axes = plt.subplots(1, 2, figsize=(14, 6)) # Number of elements vs. maximum entropy ax1 = axes[0] n_range = np.arange(2, 11) DS_max_range = R * np.log(n_range) ax1.plot(n_range, DS_max_range, 'bo-', markersize=8, linewidth=2.5, label='Theoretical maximum (equiatomic)') # Plot HEAs and traditional alloys all_alloys = {**HEAs, **traditional_alloys} for name, comp in all_alloys.items(): n = len(comp) DS = configurational_entropy_multicomponent(comp) marker = 's' if name in HEAs else '^' color = 'red' if name in HEAs else 'green' ax1.plot(n, DS, marker, markersize=10, color=color) ax1.plot([], [], 'rs', markersize=10, label='HEA') ax1.plot([], [], 'g^', markersize=10, label='Traditional alloys') ax1.set_xlabel('Number of elements') ax1.set_ylabel('ΔS_conf (J/(mol·K))') ax1.set_title('Configurational Entropy vs. Number of Elements') ax1.legend() ax1.grid(True, alpha=0.3) ax1.set_xticks(n_range) # Effect of composition (varying the content of one element in a quinary system) ax2 = axes[1] x_vary = np.linspace(0.05, 0.85, 50) DS_vary = [] for x1 in x_vary: # Divide the remainder equally among the other four elements x_rest = (1 - x1) / 4 comp = [x1, x_rest, x_rest, x_rest, x_rest] DS = configurational_entropy_multicomponent(comp) DS_vary.append(DS) ax2.plot(x_vary, DS_vary, 'b-', linewidth=2.5) ax2.axvline(0.2, color='red', linestyle='--', linewidth=2, label='Equiatomic (x=0.2)') ax2.axhline(R * np.log(5), color='red', linestyle='--', linewidth=1.5, alpha=0.5) ax2.set_xlabel('Atomic fraction of the first element x₁') ax2.set_ylabel('ΔS_conf (J/(mol·K))') ax2.set_title('Composition Dependence of Configurational Entropy (Quinary System)') ax2.legend() ax2.grid(True, alpha=0.3) plt.tight_layout() plt.savefig('materials_HEA_entropy.png', dpi=300, bbox_inches='tight') plt.show() # Definition of HEAs print("\n=== Definition of High-Entropy Alloys (HEAs) ===") print("Multicomponent alloys satisfying ΔS_conf ≥ 1.5R") print(f" 1.5R = 1.5 × 8.314 = {1.5*R:.4f} J/(mol·K)") print() print("Characteristics of HEAs:") print(" 1. High configurational entropy → stabilizes the solid solution") print(" 2. Multicomponent (usually 5 or more elements)") print(" 3. Lattice distortion effect → high strength") print(" 4. Cocktail effect (synergistic effects)") print() print("Application examples:") print(" - CoCrFeMnNi: excellent toughness at cryogenic temperatures") print(" - TiZrHfNbTa: ultra-high-temperature resistance (refractory HEA)") print(" - AlCoCrFeNi: high-temperature strength and oxidation resistance")

📚 Summary

💡 Practice Problems

  1. [Easy] In the Fe-C system, what microstructure is obtained when a steel with 0.4% carbon is slowly cooled from 800°C? Explain using the phase diagram.
  2. [Easy] Using the Andrews equation, calculate the Ms point of a steel with C 0.8% and Mn 1.0%.
  3. [Medium] Using the Koistinen-Marburger equation \(f_M = 1 - \exp(-0.011(M_s - T))\), find the martensite fraction when a steel with Ms=350°C is cooled to 25°C.
  4. [Medium] Calculate the critical temperature of a binary system with interaction parameter Ω=20 kJ/mol in the regular solution model, given that \(T_c = \Omega / (2R)\).
  5. [Hard] Calculate the configurational entropy of an equiatomic quinary alloy (20% each) and of an alloy with 80% principal element plus 5% each of four other elements, and determine which satisfies the definition of a high-entropy alloy (ΔS ≥ 1.5R).

Disclaimer