Heat treatment processes are core technologies for controlling mechanical properties (strength, hardness, toughness) of materials. By controlling phase transformation, diffusion, and precipitation through temperature history of heating, holding, and cooling, target properties are achieved. This chapter covers learning the principles of annealing, quenching and tempering, and age hardening through Python simulations, and mastering practical heat treatment design using TTT/CCT diagrams.
Learning Objectives
By completing this chapter, you will be able to:
- Understand the types of annealing (full annealing, stress relief annealing, recrystallization) and their effects
- Explain the relationship between quenching cooling rate and microstructure (martensite, bainite)
- Conduct hardenability evaluation using the Jominy test
- Understand hardness and toughness balance design through tempering
- Understand precipitation strengthening mechanism in age hardening
- Design heat treatments using TTT (Time-Temperature-Transformation) and CCT diagrams
- Simulate diffusion equations, grain growth, and precipitation strengthening in Python
2.1 Annealing Process
2.1.1 Types and Purposes of Annealing
Annealing involves heating material to high temperature, holding, and slow cooling to achieve the following effects:
| Annealing Type | Purpose | Temperature Range | Microstructural Change |
|---|---|---|---|
| Full Annealing | Softening, improved workability | Acƒ + 30-50°C | Ferrite + Pearlite |
| Stress Relief Annealing | Residual stress removal | 500-650°C | No microstructural change |
| Recrystallization Annealing | Work hardening removal, grain refinement | 500-700°C | New grain formation |
| Spheroidizing Annealing | Carbide spheroidization, improved machinability | Near Ac�, long duration | Spheroidal cementite |
2.1.2 Diffusion-Controlled Process and Holding Time
In annealing, atomic diffusion is the key to microstructural homogenization. According to Fick's Second Law, diffusion distance is proportional to the square root of time:
$$ x = \sqrt{D t} $$- $x$: Diffusion distance (m)
- $D$: Diffusion coefficient (m²/s)
- $t$: Time (s)
Arrhenius equation (temperature dependence):
$$ D = D_0 \exp\left(-\frac{Q}{RT}\right) $$- $D_0$: Frequency factor (m²/s)
- $Q$: Activation energy (J/mol)
- $R$: Gas constant (8.314 J/mol·K)
- $T$: Absolute temperature (K)
Code Example 2-1: Calculating Diffusion Distance and Holding Time
# Requirements:
# - Python 3.9+
# - matplotlib>=3.7.0
# - numpy>=1.24.0, <2.0.0
import numpy as np
import matplotlib.pyplot as plt
def arrhenius_diffusion_coefficient(T_celsius, Q, D0):
"""
Diffusion coefficient calculation using Arrhenius equation
Parameters
----------
T_celsius : float
Temperature (°C)
Q : float
Activation energy (J/mol)
D0 : float
Frequency factor (m²/s)
Returns
-------
D : float
Diffusion coefficient (m²/s)
"""
R = 8.314 # J/mol·K
T_kelvin = T_celsius + 273.15
D = D0 * np.exp(-Q / (R * T_kelvin))
return D
def diffusion_distance(D, time):
"""
Diffusion distance calculation
Parameters
----------
D : float
Diffusion coefficient (m²/s)
time : float
Time (seconds)
Returns
-------
x : float
Diffusion distance (m)
"""
x = np.sqrt(D * time)
return x
# Carbon diffusion in iron (typical values)
Q_C_in_Fe = 142000 # J/mol
D0_C_in_Fe = 2.0e-5 # m²/s
# Simulation over temperature range
temperatures = np.array([700, 800, 900, 1000]) # °C
time_hours = np.linspace(0, 10, 100) # Time (hours)
time_seconds = time_hours * 3600
plt.figure(figsize=(12, 6))
for T in temperatures:
D = arrhenius_diffusion_coefficient(T, Q_C_in_Fe, D0_C_in_Fe)
distances = [diffusion_distance(D, t) * 1e6 for t in time_seconds] # ¼m
plt.plot(time_hours, distances, linewidth=2, label=f'{T}°C')
# Estimate holding time (reaching diffusion distance of 100 ¼m)
target_distance = 100e-6 # m
required_time = (target_distance ** 2) / D / 3600 # hours
print(f"Temperature: {T}°C")
print(f" Diffusion Coefficient: {D:.2e} m²/s")
print(f" Time to diffuse 100 ¼m: {required_time:.2f} hours\n")
plt.xlabel('Time (hours)')
plt.ylabel('Diffusion Distance (¼m)')
plt.title('Carbon Diffusion in Iron (Annealing Process)')
plt.legend()
plt.grid(True, alpha=0.3)
plt.savefig('diffusion_annealing.png', dpi=150, bbox_inches='tight')
plt.show()
# Results:
# 700°C to diffuse 100 ¼m ’ approximately 10 hours needed
# 1000°C to diffuse 100 ¼m ’ approximately 0.5 hours (30 minutes)
# ’ Higher temperature enables homogenization in shorter time
2.1.3 Recrystallization and Grain Growth
During annealing after cold working, recrystallization (nucleation and growth of new grains) and grain growth (grain boundary migration) occur.
Beck-Spaepen grain growth law:
$$ d^n - d_0^n = K t $$- $d$: Average grain size (at time $t$)
- $d_0$: Initial grain size
- $n$: Growth exponent (typically 2-3)
- $K$: Temperature-dependent constant
Code Example 2-2: Grain Growth Simulation
# Requirements:
# - Python 3.9+
# - matplotlib>=3.7.0
# - numpy>=1.24.0, <2.0.0
import numpy as np
import matplotlib.pyplot as plt
def grain_growth(d0, K, time, n=2):
"""
Beck-Spaepen grain growth model
Parameters
----------
d0 : float
Initial grain size (¼m)
K : float
Growth constant (¼m^n / s)
time : ndarray
Time (seconds)
n : int
Growth exponent (default 2)
Returns
-------
d : ndarray
Grain size history (¼m)
"""
d = (d0 ** n + K * time) ** (1 / n)
return d
# Parameter settings
d0 = 10 # ¼m (initial grain size)
K = 0.5 # ¼m²/s (growth constant at 800°C)
time_hours = np.linspace(0, 10, 100)
time_seconds = time_hours * 3600
# Grain growth calculation
grain_size = grain_growth(d0, K, time_seconds, n=2)
# Plot
plt.figure(figsize=(10, 6))
plt.plot(time_hours, grain_size, 'b-', linewidth=2)
plt.xlabel('Annealing Time (hours)')
plt.ylabel('Average Grain Size (¼m)')
plt.title('Grain Growth during Annealing (800°C)')
plt.grid(True, alpha=0.3)
plt.axhline(y=50, color='r', linestyle='--', label='Target: 50 ¼m')
plt.legend()
plt.savefig('grain_growth.png', dpi=150, bbox_inches='tight')
plt.show()
# Calculate time to reach target grain size (50 ¼m)
target_size = 50 # ¼m
required_time = ((target_size ** 2) - (d0 ** 2)) / K
required_hours = required_time / 3600
print(f"Grain Growth Analysis:")
print(f" Initial Grain Size: {d0} ¼m")
print(f" Target Grain Size: {target_size} ¼m")
print(f" Required Annealing Time: {required_hours:.2f} hours")
print(f" Final Grain Size after 10h: {grain_size[-1]:.1f} ¼m")
2.2 Quenching and Tempering
2.2.1 Quenching and Martensitic Transformation
Quenching involves rapid cooling from austenite (³ phase) to produce martensite (supersaturated solid solution). Martensite has high hardness but high brittleness, so it is adjusted through tempering.
| Cooling Rate | Quenching Medium | Resulting Microstructure | Hardness (HV) |
|---|---|---|---|
| >200 °C/s | Water, brine | Martensite | 600-800 |
| 50-200 °C/s | Oil | Martensite + Bainite | 500-700 |
| 10-50 °C/s | Air cooling | Bainite | 400-600 |
| <10 °C/s | Furnace cooling | Pearlite | 200-400 |
Newton's law of cooling (simplified model):
$$ \frac{dT}{dt} = -h (T - T_{\text{medium}}) $$- $h$: Cooling coefficient (1/s)
- $T_{\text{medium}}$: Quenching medium temperature
Code Example 2-3: Quenching Cooling Curve Simulation
# Requirements:
# - Python 3.9+
# - matplotlib>=3.7.0
# - numpy>=1.24.0, <2.0.0
import numpy as np
import matplotlib.pyplot as plt
def quenching_cooling_curve(T0, T_medium, h, time):
"""
Cooling curve using Newton's law of cooling
Parameters
----------
T0 : float
Initial temperature (°C)
T_medium : float
Quenching medium temperature (°C)
h : float
Cooling coefficient (1/s)
time : ndarray
Time (seconds)
Returns
-------
T : ndarray
Temperature history (°C)
"""
T = T_medium + (T0 - T_medium) * np.exp(-h * time)
return T
# Cooling condition settings
T_austenitize = 850 # °C (austenitizing temperature)
time = np.linspace(0, 60, 500) # seconds
quenching_conditions = [
{'name': 'Water Quench', 'T_med': 25, 'h': 0.3, 'color': 'blue'},
{'name': 'Oil Quench', 'T_med': 60, 'h': 0.1, 'color': 'orange'},
{'name': 'Air Cool', 'T_med': 25, 'h': 0.02, 'color': 'green'},
{'name': 'Furnace Cool', 'T_med': 25, 'h': 0.005, 'color': 'red'}
]
plt.figure(figsize=(12, 8))
# Martensite start temperature (Ms) and bainite range
Ms_temp = 350 # °C
Bs_temp = 550 # °C
plt.axhline(y=Ms_temp, color='purple', linestyle='--', linewidth=2,
label=f'Ms (Martensite Start): {Ms_temp}°C')
plt.axhspan(Bs_temp, Ms_temp, alpha=0.2, color='cyan',
label='Bainite Range')
for cond in quenching_conditions:
T_curve = quenching_cooling_curve(
T_austenitize, cond['T_med'], cond['h'], time
)
plt.plot(time, T_curve, linewidth=2.5,
color=cond['color'], label=cond['name'])
# Cooling rate calculation (average from 800°C to 500°C)
idx_800 = np.argmin(np.abs(T_curve - 800))
idx_500 = np.argmin(np.abs(T_curve - 500))
cooling_rate = (T_curve[idx_800] - T_curve[idx_500]) / (time[idx_500] - time[idx_800])
print(f"{cond['name']}:")
print(f" Cooling Rate (800’500°C): {cooling_rate:.1f} °C/s")
print(f" Time to Ms ({Ms_temp}°C): {time[np.argmin(np.abs(T_curve - Ms_temp))]:.1f} s\n")
plt.xlabel('Time (s)')
plt.ylabel('Temperature (°C)')
plt.title('Quenching Cooling Curves and Microstructure Formation')
plt.legend(loc='upper right')
plt.grid(True, alpha=0.3)
plt.xlim(0, 60)
plt.ylim(0, 900)
plt.savefig('quenching_curves.png', dpi=150, bbox_inches='tight')
plt.show()
2.2.2 Jominy Hardenability Test
The Jominy test is a standard test method for evaluating hardenability of steel. One end is water-cooled, and hardness distribution is measured due to the cooling rate gradient.
850°C, 30 min] --> B[Hold specimen
Water cool one end only] B --> C[Cooling rate gradient
End: Fast
Interior: Slow] C --> D[Hardness measurement
Distance from end vs HRC] D --> E[Hardenability evaluation
Gradual hardness drop = High] style A fill:#f093fb,stroke:#f5576c,stroke-width:2px,color:#fff style E fill:#f093fb,stroke:#f5576c,stroke-width:2px,color:#fff
Code Example 2-4: Jominy Hardenability Curve Simulation
# Requirements:
# - Python 3.9+
# - matplotlib>=3.7.0
# - numpy>=1.24.0, <2.0.0
import numpy as np
import matplotlib.pyplot as plt
def jominy_hardness_profile(distance_mm, HRC_max, DI, steel_type='Low Alloy'):
"""
Jominy hardenability curve model
Parameters
----------
distance_mm : ndarray
Distance from quenched end (mm)
HRC_max : float
Maximum hardness (HRC)
DI : float
Ideal Diameter (hardenability index)
steel_type : str
Steel grade
Returns
-------
hardness : ndarray
Hardness distribution (HRC)
"""
# Grossmann hardenability model (simplified)
# HRC = HRC_max * exp(-k * distance)
k = 0.1 / DI # Decay constant
hardness = HRC_max * np.exp(-k * distance_mm)
# Minimum hardness (ferrite + pearlite microstructure)
HRC_min = 20
hardness = np.maximum(hardness, HRC_min)
return hardness
# Comparison of 3 steel types
distance = np.linspace(0, 50, 100) # mm
steels = [
{'name': 'S45C (Plain Carbon)', 'HRC_max': 62, 'DI': 10},
{'name': 'SCM440 (Cr-Mo)', 'HRC_max': 60, 'DI': 30},
{'name': 'SNC815 (Ni-Cr)', 'HRC_max': 58, 'DI': 50}
]
plt.figure(figsize=(12, 7))
for steel in steels:
hardness = jominy_hardness_profile(distance, steel['HRC_max'], steel['DI'])
plt.plot(distance, hardness, linewidth=2.5, label=steel['name'])
# Hardenability evaluation (distance where hardness drops to HRC 50)
idx_hrc50 = np.argmin(np.abs(hardness - 50))
distance_hrc50 = distance[idx_hrc50]
print(f"{steel['name']}:")
print(f" Maximum Hardness: {steel['HRC_max']} HRC")
print(f" Ideal Diameter (DI): {steel['DI']} mm")
print(f" Distance to HRC 50: {distance_hrc50:.1f} mm\n")
plt.axhline(y=50, color='k', linestyle='--', alpha=0.5, label='Target: HRC 50')
plt.xlabel('Distance from Quenched End (mm)')
plt.ylabel('Hardness (HRC)')
plt.title('Jominy Hardenability Test: Steel Comparison')
plt.legend()
plt.grid(True, alpha=0.3)
plt.savefig('jominy_hardenability.png', dpi=150, bbox_inches='tight')
plt.show()
# Result interpretation:
# - Plain Carbon (S45C): Low hardenability, only surface hardening
# - Cr-Mo (SCM440): Moderate hardenability
# - Ni-Cr (SNC815): High hardenability, hardening to deep sections
2.2.3 Tempering and Hardness Control
Tempering involves reheating quenched martensite at appropriate temperature to adjust the balance between hardness and toughness.
Hollomon-Jaffe equation (tempering parameter):
$$ P = T (C + \log_{10} t) \times 10^{-3} $$- $P$: Tempering parameter (dimensionless)
- $T$: Tempering temperature (K)
- $t$: Tempering time (hours)
- $C$: Constant (typically 20)
Code Example 2-5: Tempering Hardness Prediction
# Requirements:
# - Python 3.9+
# - matplotlib>=3.7.0
# - numpy>=1.24.0, <2.0.0
import numpy as np
import matplotlib.pyplot as plt
def hollomon_jaffe_parameter(temp_celsius, time_hours, C=20):
"""
Hollomon-Jaffe tempering parameter
Parameters
----------
temp_celsius : float
Tempering temperature (°C)
time_hours : float
Tempering time (hours)
C : float
Constant (default 20)
Returns
-------
P : float
Tempering parameter
"""
T_kelvin = temp_celsius + 273.15
P = T_kelvin * (C + np.log10(time_hours)) * 1e-3
return P
def tempered_hardness(P, HRC_initial=65):
"""
Hardness prediction from tempering parameter
Parameters
----------
P : float
Tempering parameter
HRC_initial : float
Initial hardness after quenching (HRC)
Returns
-------
HRC : float
Hardness after tempering (HRC)
"""
# Empirical equation (varies by steel grade)
HRC = HRC_initial - 0.15 * (P - 10) ** 1.5
# Minimum hardness constraint
HRC = np.maximum(HRC, 20)
return HRC
# Temperature and time effect evaluation
temperatures = [200, 300, 400, 500, 600] # °C
time_hours = np.logspace(-1, 2, 100) # 0.1 to 100 hours
plt.figure(figsize=(12, 8))
for temp in temperatures:
P_values = [hollomon_jaffe_parameter(temp, t) for t in time_hours]
hardness = [tempered_hardness(P) for P in P_values]
plt.plot(time_hours, hardness, linewidth=2.5, label=f'{temp}°C')
# Time to reach target hardness (HRC 45)
idx_target = np.argmin(np.abs(np.array(hardness) - 45))
time_target = time_hours[idx_target]
print(f"Tempering at {temp}°C:")
print(f" Time to reach HRC 45: {time_target:.2f} hours\n")
plt.axhline(y=45, color='k', linestyle='--', alpha=0.5, label='Target: HRC 45')
plt.xscale('log')
plt.xlabel('Tempering Time (hours)')
plt.ylabel('Hardness (HRC)')
plt.title('Tempering: Hardness vs Time and Temperature')
plt.legend()
plt.grid(True, alpha=0.3, which='both')
plt.savefig('tempering_hardness.png', dpi=150, bbox_inches='tight')
plt.show()
# Iso-hardness contour plot (2D map)
fig, ax = plt.subplots(figsize=(12, 8))
temp_range = np.linspace(150, 650, 50)
time_range = np.logspace(-1, 2, 50)
T_mesh, Time_mesh = np.meshgrid(temp_range, time_range)
Hardness_mesh = np.zeros_like(T_mesh)
for i in range(T_mesh.shape[0]):
for j in range(T_mesh.shape[1]):
P = hollomon_jaffe_parameter(T_mesh[i, j], Time_mesh[i, j])
Hardness_mesh[i, j] = tempered_hardness(P)
contour = ax.contourf(T_mesh, Time_mesh, Hardness_mesh, levels=15, cmap='viridis')
contour_lines = ax.contour(T_mesh, Time_mesh, Hardness_mesh, levels=[40, 45, 50, 55, 60],
colors='white', linewidths=2)
ax.clabel(contour_lines, inline=True, fontsize=10, fmt='%d HRC')
ax.set_xlabel('Tempering Temperature (°C)')
ax.set_ylabel('Tempering Time (hours)')
ax.set_yscale('log')
ax.set_title('Tempering Map: Hardness Contours')
plt.colorbar(contour, label='Hardness (HRC)')
plt.savefig('tempering_map.png', dpi=150, bbox_inches='tight')
plt.show()
2.3 Age Hardening
2.3.1 Mechanism of Age Hardening
Age hardening (Precipitation Hardening) involves generating fine precipitates from supersaturated solid solution to inhibit dislocation movement and improve strength.
Age hardening process (Al alloy example):
- Solution Treatment: Dissolve alloying elements at high temperature (500-550°C)
- Quenching: Rapid cooling to room temperature, retaining supersaturated solid solution (SSSS)
- Aging Treatment: Precipitate formation at intermediate temperature (100-200°C)
- GP zone (Guinier-Preston zone) formation
- Intermediate phase (¸'', ¸') precipitation
- Stable phase (¸, Mg‚Si) coarsening
Solid Solution
SSSS] --> B[GP zone formation
Few nm] B --> C[Intermediate phase ¸''
Metastable] C --> D[¸' phase
Tens of nm] D --> E[Stable phase ¸
Coarsening] B -.-> F[Peak strength
Optimal precipitation] D -.-> G[Over-aging
Strength decrease] style A fill:#fce7f3,stroke:#f093fb,stroke-width:2px style F fill:#f093fb,stroke:#f5576c,stroke-width:2px,color:#fff style G fill:#fee2e2,stroke:#ef4444,stroke-width:2px
Orowan mechanism (precipitation strengthening):
$$ \Delta \sigma = \frac{M G b}{\lambda} $$- $\Delta \sigma$: Strength increase (MPa)
- $M$: Taylor factor (~3)
- $G$: Shear modulus (GPa)
- $b$: Burgers vector (nm)
- $\lambda$: Precipitate spacing (nm)
Code Example 2-6: Age Hardening Curve Simulation
# Requirements:
# - Python 3.9+
# - matplotlib>=3.7.0
# - numpy>=1.24.0, <2.0.0
import numpy as np
import matplotlib.pyplot as plt
def age_hardening_strength(time_hours, temp_celsius, k=0.5, n=0.3):
"""
Age hardening strength change model (based on JMA equation)
Parameters
----------
time_hours : ndarray
Aging time (hours)
temp_celsius : float
Aging temperature (°C)
k : float
Rate constant
n : float
Avrami exponent
Returns
-------
strength : ndarray
Tensile strength (MPa)
"""
# Temperature dependence (Arrhenius type)
k_eff = k * np.exp(-(5000 / (temp_celsius + 273.15)))
# JMA equation (phase transformation fraction)
f = 1 - np.exp(-(k_eff * time_hours) ** n)
# Strength model
sigma_0 = 100 # Solid solution strength (MPa)
delta_sigma_max = 300 # Maximum precipitation strengthening (MPa)
# After peak aging, decrease due to over-aging
peak_fraction = 0.6
strength = sigma_0 + delta_sigma_max * f * np.exp(-2 * (f - peak_fraction) ** 2)
return strength
# Temperature dependence evaluation
temperatures = [150, 180, 200, 220]
time_hours = np.logspace(-1, 3, 200) # 0.1 to 1000 hours
plt.figure(figsize=(12, 8))
for temp in temperatures:
strength = age_hardening_strength(time_hours, temp)
plt.plot(time_hours, strength, linewidth=2.5, label=f'{temp}°C')
# Peak strength and time to reach
peak_strength = np.max(strength)
peak_time = time_hours[np.argmax(strength)]
print(f"Age Hardening at {temp}°C:")
print(f" Peak Strength: {peak_strength:.1f} MPa")
print(f" Time to Peak: {peak_time:.2f} hours\n")
plt.xscale('log')
plt.xlabel('Aging Time (hours)')
plt.ylabel('Tensile Strength (MPa)')
plt.title('Age Hardening: Strength vs Time and Temperature')
plt.legend()
plt.grid(True, alpha=0.3, which='both')
plt.savefig('age_hardening_curve.png', dpi=150, bbox_inches='tight')
plt.show()
# Iso-strength contours (2D map)
fig, ax = plt.subplots(figsize=(12, 8))
temp_range = np.linspace(130, 250, 50)
time_range = np.logspace(-1, 3, 50)
T_mesh, Time_mesh = np.meshgrid(temp_range, time_range)
Strength_mesh = np.zeros_like(T_mesh)
for i in range(T_mesh.shape[0]):
for j in range(T_mesh.shape[1]):
strength_val = age_hardening_strength(np.array([Time_mesh[i, j]]), T_mesh[i, j])
Strength_mesh[i, j] = strength_val[0]
contour = ax.contourf(T_mesh, Time_mesh, Strength_mesh, levels=15, cmap='plasma')
contour_lines = ax.contour(T_mesh, Time_mesh, Strength_mesh,
levels=[250, 300, 350, 380], colors='white', linewidths=2)
ax.clabel(contour_lines, inline=True, fontsize=10, fmt='%d MPa')
ax.set_xlabel('Aging Temperature (°C)')
ax.set_ylabel('Aging Time (hours)')
ax.set_yscale('log')
ax.set_title('Age Hardening Map: Strength Contours')
plt.colorbar(contour, label='Tensile Strength (MPa)')
plt.savefig('age_hardening_map.png', dpi=150, bbox_inches='tight')
plt.show()
2.4 Utilization of TTT and CCT Diagrams
2.4.1 TTT Diagram (Time-Temperature-Transformation)
The TTT diagram (isothermal transformation diagram) represents microstructural transformation during constant temperature holding. It is essential for steel heat treatment design.
Code Example 2-7: TTT/CCT Diagram Generation and Heat Treatment Simulation
# Requirements:
# - Python 3.9+
# - matplotlib>=3.7.0
# - numpy>=1.24.0, <2.0.0
import numpy as np
import matplotlib.pyplot as plt
def generate_TTT_diagram():
"""
Generate simplified TTT diagram (eutectoid steel 0.8%C)
"""
# Pearlite transformation start/finish curves (C curves)
time = np.logspace(-1, 5, 200) # seconds
# Pearlite nose (fastest transformation at approximately 550°C)
T_pearlite_start = 700 - 150 * np.exp(-((np.log10(time) - 1.5) / 1.5) ** 2)
T_pearlite_finish = T_pearlite_start - 50
# Bainite transformation (350-550°C)
T_bainite_start = 500 - 150 * np.exp(-((np.log10(time) - 2.5) / 1.2) ** 2)
T_bainite_finish = T_bainite_start - 50
# Martensite start temperature (time-independent)
Ms_temp = 250
return time, T_pearlite_start, T_pearlite_finish, T_bainite_start, T_bainite_finish, Ms_temp
def simulate_cooling_path(cooling_rate, T_initial=850, time_max=1000):
"""
Simulate cooling path
Parameters
----------
cooling_rate : float
Cooling rate (°C/s)
T_initial : float
Initial temperature (°C)
time_max : float
Maximum time (seconds)
Returns
-------
time, temperature : ndarray
Cooling path
"""
time = np.linspace(0, time_max, 500)
temperature = T_initial - cooling_rate * time
temperature = np.maximum(temperature, 25) # Room temperature lower limit
return time, temperature
# TTT diagram generation
time_ttt, T_p_start, T_p_finish, T_b_start, T_b_finish, Ms = generate_TTT_diagram()
# Plot
fig, ax = plt.subplots(figsize=(14, 10))
# TTT curves
ax.plot(time_ttt, T_p_start, 'r-', linewidth=2.5, label='Pearlite Start')
ax.plot(time_ttt, T_p_finish, 'r--', linewidth=2.5, label='Pearlite Finish')
ax.plot(time_ttt, T_b_start, 'b-', linewidth=2.5, label='Bainite Start')
ax.plot(time_ttt, T_b_finish, 'b--', linewidth=2.5, label='Bainite Finish')
ax.axhline(y=Ms, color='purple', linestyle='-', linewidth=2.5, label=f'Ms (Martensite): {Ms}°C')
# Region shading
ax.fill_between(time_ttt, T_p_start, T_p_finish, alpha=0.3, color='red', label='Pearlite Region')
ax.fill_between(time_ttt, T_b_start, T_b_finish, alpha=0.3, color='blue', label='Bainite Region')
ax.fill_between(time_ttt, 0, Ms, alpha=0.2, color='purple', label='Martensite Region')
# Overlay cooling paths
cooling_cases = [
{'rate': 500, 'name': 'Water Quench (500°C/s)', 'color': 'green', 'linestyle': '-'},
{'rate': 50, 'name': 'Oil Quench (50°C/s)', 'color': 'orange', 'linestyle': '-'},
{'rate': 5, 'name': 'Air Cool (5°C/s)', 'color': 'brown', 'linestyle': '-'},
]
for case in cooling_cases:
time_cool, temp_cool = simulate_cooling_path(case['rate'])
ax.plot(time_cool, temp_cool, color=case['color'],
linestyle=case['linestyle'], linewidth=3, label=case['name'])
ax.set_xscale('log')
ax.set_xlabel('Time (s)')
ax.set_ylabel('Temperature (°C)')
ax.set_title('TTT Diagram with Cooling Paths (Eutectoid Steel 0.8% C)')
ax.set_xlim(0.1, 1e4)
ax.set_ylim(0, 900)
ax.legend(loc='upper right', fontsize=9)
ax.grid(True, alpha=0.3, which='both')
plt.savefig('ttt_diagram_with_cooling.png', dpi=150, bbox_inches='tight')
plt.show()
# Microstructure prediction
print("Microstructure Prediction:")
for case in cooling_cases:
time_cool, temp_cool = simulate_cooling_path(case['rate'])
# Pearlite nose passage determination
pearlite_time_min = time_ttt[np.argmin(T_p_start)]
pearlite_temp_min = np.min(T_p_start)
if case['rate'] > 100:
microstructure = "100% Martensite (avoided pearlite nose)"
elif case['rate'] > 20:
microstructure = "Martensite + Bainite (partial transformation)"
else:
microstructure = "Pearlite + Ferrite (slow cooling)"
print(f"{case['name']}: {microstructure}")
Exercises
Exercise 2-1: Annealing Holding Time Design (Easy)
For full annealing of carbon steel (0.5% C), you want to homogenize a 20 mm thick plate. The diffusion distance must reach at least 10 mm. Calculate the required holding time when annealing at 800°C. (Carbon diffusion coefficient: $D = 2.0 \times 10^{-5} \exp(-142000 / (8.314 \times T))$ m²/s, $T$ in K)
Solution Example
# Given conditions
target_distance = 10e-3 # m (10 mm)
T_celsius = 800
T_kelvin = T_celsius + 273.15
# Diffusion coefficient calculation
Q = 142000 # J/mol
D0 = 2.0e-5 # m²/s
R = 8.314 # J/mol·K
D = D0 * np.exp(-Q / (R * T_kelvin))
# Holding time calculation (x² = D * t)
t_required = (target_distance ** 2) / D
t_hours = t_required / 3600
print(f"Complete Annealing Time Calculation:")
print(f" Temperature: {T_celsius} °C")
print(f" Target Diffusion Distance: {target_distance*1000} mm")
print(f" Diffusion Coefficient: {D:.2e} m²/s")
print(f" Required Holding Time: {t_hours:.2f} hours ({t_required/60:.1f} min)")
# Result: Approximately 8 hours needed
# In practice, with safety margin, 10-12 hours holding is recommended
Exercise 2-2: Microstructure Prediction After Quenching (Easy)
After austenitizing eutectoid steel (0.8% C) at 850°C, predict the microstructure when quenched at the following cooling rates: (a) 200°C/s, (b) 30°C/s, (c) 3°C/s. Refer to the TTT diagram and answer the resulting microstructure and expected hardness range.
Solution Example
TTT Diagram Analysis:
- (a) 200°C/s (water quench): Avoids pearlite nose (approximately 550°C, 1 second) ’ 100% Martensite, hardness HRC 64-66
- (b) 30°C/s (oil quench): Passes through pearlite nose but short duration ’ Martensite + Upper Bainite, hardness HRC 55-60
- (c) 3°C/s (air cooling): Long residence in pearlite transformation region ’ Fine Pearlite, hardness HRC 35-40
Experimental Verification: Ideally, measure with Jominy test and compare with predictions
Exercise 2-3: Tempering Condition Design (Medium)
You want to temper quenched tool steel (HRC 65) to target hardness HRC 55. Calculate the required time when tempering at 400°C and 500°C. (Use Hollomon-Jaffe parameter)
Solution Example
# Inverse calculation: Calculate P from target hardness, then calculate time
def inverse_tempered_hardness(HRC_target, HRC_initial=65):
"""Calculate Hollomon-Jaffe parameter from target hardness"""
# HRC = HRC_initial - 0.15 * (P - 10)^1.5
# ’ P = 10 + ((HRC_initial - HRC) / 0.15)^(2/3)
P_target = 10 + ((HRC_initial - HRC_target) / 0.15) ** (2/3)
return P_target
HRC_target = 55
P_target = inverse_tempered_hardness(HRC_target)
print(f"Target Hollomon-Jaffe Parameter: P = {P_target:.2f}\n")
# Calculate time for two temperatures
temperatures = [400, 500] # °C
for temp in temperatures:
T_kelvin = temp + 273.15
C = 20
# P = T * (C + log10(t)) * 1e-3
# ’ log10(t) = (P / (T * 1e-3)) - C
log10_t = (P_target / (T_kelvin * 1e-3)) - C
t_hours = 10 ** log10_t
print(f"Tempering at {temp}°C:")
print(f" Required Time: {t_hours:.2f} hours")
print(f" Practical Recommendation: {t_hours*1.2:.2f} hours (with safety margin)\n")
# Results:
# 400°C ’ Approximately 10 hours
# 500°C ’ Approximately 2 hours
# ’ Higher temperature reaches target hardness in shorter time
Exercise 2-4: Age Hardening Optimal Condition Determination (Medium)
For age hardening of Al-Cu alloy (Al-4%Cu), the target tensile strength is 400 MPa. If aging treatment can be performed at 180°C and 200°C, compare the optimal aging time and productivity (throughput) for each.
Solution Example
# Age hardening simulation (time to reach strength 400 MPa)
target_strength = 400 # MPa
temperatures = [180, 200]
time_hours_range = np.logspace(-1, 3, 500)
for temp in temperatures:
strength_curve = age_hardening_strength(time_hours_range, temp)
# Time to reach target strength
idx_target = np.argmin(np.abs(strength_curve - target_strength))
time_target = time_hours_range[idx_target]
strength_achieved = strength_curve[idx_target]
# Peak strength
peak_strength = np.max(strength_curve)
peak_time = time_hours_range[np.argmax(strength_curve)]
print(f"Aging at {temp}°C:")
print(f" Time to reach {target_strength} MPa: {time_target:.2f} hours")
print(f" Achieved Strength: {strength_achieved:.1f} MPa")
print(f" Peak Strength: {peak_strength:.1f} MPa (at {peak_time:.2f} hours)")
# Productivity evaluation (batch processing)
batch_capacity = 100 # parts/batch
throughput = batch_capacity / time_target # parts/hour
print(f" Production Throughput: {throughput:.2f} parts/hour")
print(f" Daily Production (24h): {throughput*24:.0f} parts\n")
# Results:
# 180°C ’ Longer time but higher peak strength (stable)
# 200°C ’ Shorter time, higher productivity, but over-aging risk
# ’ Choose 200°C for productivity priority, 180°C for quality stability priority
Exercise 2-5: Hardenability Evaluation from Jominy Test Data (Medium)
The following hardness data were obtained from Jominy test: End surface (0 mm): HRC 62, 5 mm: HRC 58, 10 mm: HRC 52, 15 mm: HRC 45. From this data, predict the center hardness when quenching a 30 mm diameter round bar.
Solution Example
# Requirements:
# - Python 3.9+
# - numpy>=1.24.0, <2.0.0
"""
Example: The following hardness data were obtained from Jominy test:
Purpose: Demonstrate data visualization techniques
Target: Intermediate
Execution time: 2-5 seconds
Dependencies: None
"""
import numpy as np
from scipy.interpolate import interp1d
# Jominy test data
jominy_distance = np.array([0, 5, 10, 15]) # mm
jominy_hardness = np.array([62, 58, 52, 45]) # HRC
# Create interpolation function
interp_func = interp1d(jominy_distance, jominy_hardness,
kind='cubic', fill_value='extrapolate')
# Grossmann conversion table (simplified): Round bar diameter ’ Jominy equivalent distance
# Center of 30 mm diameter is equivalent to approximately 12-13 mm of Jominy test in terms of cooling rate
# (Actual conversion tables or H-band method should be used, but simplified calculation here)
diameter = 30 # mm
equivalent_jominy_distance = 0.4 * diameter # Simplified conversion (center)
predicted_hardness = interp_func(equivalent_jominy_distance)
print(f"Jominy Hardenability Analysis:")
print(f" Bar Diameter: {diameter} mm")
print(f" Equivalent Jominy Distance (center): {equivalent_jominy_distance:.1f} mm")
print(f" Predicted Center Hardness: {predicted_hardness:.1f} HRC")
# Surface hardness (Jominy 0 mm equivalent)
surface_hardness = interp_func(0)
print(f" Predicted Surface Hardness: {surface_hardness:.1f} HRC")
# Case depth (depth with HRC e 50)
hardness_threshold = 50
try:
depth_threshold = np.interp(hardness_threshold, jominy_hardness[::-1], jominy_distance[::-1])
# Case depth in round bar (simplified conversion)
effective_depth = depth_threshold / 0.4
print(f"\nCase Depth (HRC e 50): {effective_depth:.1f} mm from surface")
except:
print(f"\nCase Depth: Exceeds measurement range")
# Plot
plt.figure(figsize=(10, 6))
jominy_fine = np.linspace(0, 20, 100)
hardness_fine = interp_func(jominy_fine)
plt.plot(jominy_fine, hardness_fine, 'b-', linewidth=2, label='Interpolated Curve')
plt.scatter(jominy_distance, jominy_hardness, color='red', s=100,
zorder=5, label='Measured Data')
plt.axvline(x=equivalent_jominy_distance, color='green', linestyle='--',
linewidth=2, label=f'Equivalent Position (30mm bar center)')
plt.axhline(y=predicted_hardness, color='green', linestyle='--', alpha=0.5)
plt.xlabel('Distance from Quenched End (mm)')
plt.ylabel('Hardness (HRC)')
plt.title('Jominy Hardenability and Center Hardness Prediction')
plt.legend()
plt.grid(True, alpha=0.3)
plt.savefig('jominy_prediction.png', dpi=150, bbox_inches='tight')
plt.show()
Exercise 2-6: Heat Treatment Process Design Using TTT Diagram (Hard)
For eutectoid steel (0.8% C), you want to obtain a mixed microstructure of 50% martensite + 50% bainite. Design optimal cooling rate and isothermal holding conditions referring to TTT diagram. Target hardness is HRC 55-58.
Solution Example
# Strategy: Austempering
# 1. Austenitize at 850°C
# 2. Rapid quench to bainite region (e.g., 300°C) below Ms temperature (250°C)
# 3. Bainite transformation by isothermal holding (approximately 50%)
# 4. Cool to room temperature ’ Retained austenite transforms to martensite
T_austenitize = 850 # °C
T_austempering = 300 # °C (lower bainite region)
Ms = 250 # °C
# Cooling rate calculation (reach bainite region before entering below Ms)
# Must avoid pearlite nose (550°C, 1 second)
required_cooling_rate = (T_austenitize - T_austempering) / 5 # Within 5 seconds
print(f"Austempering Process Design:")
print(f" Step 1: Austenitizing at {T_austenitize}°C")
print(f" Step 2: Rapid quench to {T_austempering}°C")
print(f" Required Cooling Rate: >{required_cooling_rate:.1f} °C/s")
print(f" (Use oil or salt bath quench)")
# Bainite transformation time (read from TTT diagram)
# Transformation start at 300°C: approximately 10 seconds, 50% transformation: approximately 60 seconds
t_bainite_start = 10 # seconds
t_bainite_50pct = 60 # seconds
print(f"\n Step 3: Isothermal hold at {T_austempering}°C")
print(f" Bainite transformation starts: {t_bainite_start} s")
print(f" Hold time for 50% bainite: {t_bainite_50pct} s")
# Retained austenite ’ Martensite transformation
print(f"\n Step 4: Cool to room temperature")
print(f" Remaining austenite (50%) transforms to martensite below {Ms}°C")
# Microstructure and hardness prediction
print(f"\nExpected Microstructure:")
print(f" 50% Lower Bainite (HRC 50-55)")
print(f" 50% Martensite (HRC 60-65)")
print(f" Composite Hardness: HRC 55-58 ")
# Recommendation for experimental verification
print(f"\nExperimental Verification:")
print(f" 1. Conduct dilatometry to confirm transformation kinetics")
print(f" 2. Metallographic examination (SEM, optical microscopy)")
print(f" 3. Hardness testing (Rockwell C scale)")
print(f" 4. Impact toughness testing (Charpy) for quality assurance")
Exercise 2-7: Multi-Step Aging Treatment Optimization (Hard)
For Al-Mg-Si alloy, perform two-step aging treatment. Determine optimal conditions for Step 1 (low temperature, GP zone formation) and Step 2 (high temperature, intermediate phase precipitation) considering trade-off between tensile strength and cost (energy, time).
Solution Example
# Requirements:
# - Python 3.9+
# - matplotlib>=3.7.0
# - numpy>=1.24.0, <2.0.0
import numpy as np
import matplotlib.pyplot as plt
from scipy.optimize import differential_evolution
def two_step_aging_strength(t1_hours, T1_celsius, t2_hours, T2_celsius):
"""
Two-step aging treatment strength prediction model
Parameters
----------
t1_hours, T1_celsius : float
Step 1 aging (time, temperature)
t2_hours, T2_celsius : float
Step 2 aging (time, temperature)
Returns
-------
strength : float
Tensile strength (MPa)
"""
# Step 1: GP zone formation (low temperature, long time)
strength_step1 = 200 + 100 * (1 - np.exp(-0.5 * t1_hours)) * np.exp(-T1_celsius / 200)
# Step 2: Intermediate phase precipitation (high temperature, short time)
strength_step2 = 150 * (1 - np.exp(-0.2 * t2_hours)) * (1 - np.exp(-(T2_celsius - 150) / 50))
# Composite strength
total_strength = strength_step1 + strength_step2
return total_strength
def calculate_cost(t1_hours, T1_celsius, t2_hours, T2_celsius):
"""
Process cost calculation
Returns
-------
cost : float
Normalized cost (time + energy)
"""
# Time cost (productivity)
time_cost = t1_hours + t2_hours
# Energy cost (temperature × time)
energy_cost = (T1_celsius * t1_hours + T2_celsius * t2_hours) / 1000
# Total cost (weighted)
total_cost = time_cost + 0.5 * energy_cost
return total_cost
def objective_function(params):
"""
Multi-objective optimization objective function
Maximize strength, minimize cost
Returns
-------
-performance : float
Negative performance index (minimization problem)
"""
t1, T1, t2, T2 = params
strength = two_step_aging_strength(t1, T1, t2, T2)
cost = calculate_cost(t1, T1, t2, T2)
# Performance index: strength / cost (higher is better)
performance = strength / cost
return -performance # Convert to minimization problem
# Optimization execution
bounds = [
(1, 24), # t1: 1-24 hours
(100, 150), # T1: 100-150°C
(1, 12), # t2: 1-12 hours
(170, 220) # T2: 170-220°C
]
result = differential_evolution(
objective_function,
bounds,
maxiter=100,
seed=42,
disp=True
)
t1_opt, T1_opt, t2_opt, T2_opt = result.x
strength_opt = two_step_aging_strength(t1_opt, T1_opt, t2_opt, T2_opt)
cost_opt = calculate_cost(t1_opt, T1_opt, t2_opt, T2_opt)
print(f"Two-Step Aging Optimization Results:")
print(f"\nStep 1 (GP Zone Formation):")
print(f" Temperature: {T1_opt:.1f} °C")
print(f" Time: {t1_opt:.2f} hours")
print(f"\nStep 2 (Intermediate Phase Precipitation):")
print(f" Temperature: {T2_opt:.1f} °C")
print(f" Time: {t2_opt:.2f} hours")
print(f"\nPerformance:")
print(f" Tensile Strength: {strength_opt:.1f} MPa")
print(f" Total Process Cost: {cost_opt:.2f} (normalized)")
print(f" Performance Index: {strength_opt/cost_opt:.2f} MPa/cost")
# Comparison: Single-step aging
T_single = 180
t_single = 10
strength_single = two_step_aging_strength(0, 100, t_single, T_single)
cost_single = calculate_cost(0, 100, t_single, T_single)
print(f"\nComparison with Single-Step Aging ({T_single}°C, {t_single}h):")
print(f" Strength: {strength_single:.1f} MPa")
print(f" Cost: {cost_single:.2f}")
print(f" Performance Index: {strength_single/cost_single:.2f} MPa/cost")
print(f"\nImprovement:")
print(f" Strength: +{strength_opt - strength_single:.1f} MPa ({(strength_opt/strength_single-1)*100:.1f}%)")
print(f" Performance Index: +{(strength_opt/cost_opt) - (strength_single/cost_single):.2f} ({((strength_opt/cost_opt)/(strength_single/cost_single)-1)*100:.1f}%)")
Exercise 2-8: Statistical Quality Control of Heat Treatment Process (Hard)
In factory tempering process, there are variations of temperature ±5°C and time ±10%. Calculate the probability of achieving target hardness HRC 50±2 and evaluate process capability indices (Cp, Cpk). Propose improvement measures to keep defect rate below 1%.
Solution Example
# Requirements:
# - Python 3.9+
# - matplotlib>=3.7.0
# - numpy>=1.24.0, <2.0.0
"""
Example: In factory tempering process, there are variations of temper
Purpose: Demonstrate data visualization techniques
Target: Intermediate
Execution time: 5-15 seconds
Dependencies: None
"""
import numpy as np
import matplotlib.pyplot as plt
from scipy.stats import norm
# Process conditions (nominal values)
T_nominal = 450 # °C
t_nominal = 2.0 # hours
HRC_target = 50
# Process variation
T_std = 5 / 3 # °C (±5°C as 3Ã)
t_std = t_nominal * 0.1 / 3 # hours (±10% as 3Ã)
# Monte Carlo simulation
n_samples = 10000
np.random.seed(42)
T_samples = np.random.normal(T_nominal, T_std, n_samples)
t_samples = np.random.normal(t_nominal, t_std, n_samples)
# Hardness calculation (each sample)
hardness_samples = np.array([
tempered_hardness(hollomon_jaffe_parameter(T, t))
for T, t in zip(T_samples, t_samples)
])
# Statistical analysis
HRC_mean = np.mean(hardness_samples)
HRC_std = np.std(hardness_samples)
print(f"Process Capability Analysis:")
print(f" Target Hardness: {HRC_target} ± 2 HRC")
print(f" Actual Mean: {HRC_mean:.2f} HRC")
print(f" Actual Std Dev: {HRC_std:.2f} HRC")
# Process capability indices
USL = HRC_target + 2 # Upper Specification Limit
LSL = HRC_target - 2 # Lower Specification Limit
Cp = (USL - LSL) / (6 * HRC_std)
Cpk = min((USL - HRC_mean) / (3 * HRC_std),
(HRC_mean - LSL) / (3 * HRC_std))
print(f"\nProcess Capability Indices:")
print(f" Cp = {Cp:.3f}")
print(f" Cpk = {Cpk:.3f}")
if Cp >= 1.33:
print(f" ’ Cp assessment: CAPABLE (e1.33)")
else:
print(f" ’ Cp assessment: MARGINAL (<1.33)")
if Cpk >= 1.33:
print(f" ’ Cpk assessment: CAPABLE and CENTERED (e1.33)")
else:
print(f" ’ Cpk assessment: NEEDS IMPROVEMENT (<1.33)")
# Defect rate calculation
out_of_spec = np.sum((hardness_samples < LSL) | (hardness_samples > USL))
defect_rate = out_of_spec / n_samples * 100
print(f"\nDefect Rate:")
print(f" Out of Specification: {out_of_spec} / {n_samples}")
print(f" Defect Rate: {defect_rate:.2f}%")
if defect_rate < 1.0:
print(f" ’ Target (<1%) ACHIEVED ")
else:
print(f" ’ Target (<1%) NOT MET ")
# Histogram and process distribution
plt.figure(figsize=(12, 6))
plt.hist(hardness_samples, bins=50, density=True, alpha=0.7,
color='blue', edgecolor='black', label='Actual Distribution')
# Normal distribution fit
x_fit = np.linspace(HRC_mean - 4*HRC_std, HRC_mean + 4*HRC_std, 200)
y_fit = norm.pdf(x_fit, HRC_mean, HRC_std)
plt.plot(x_fit, y_fit, 'r-', linewidth=2, label='Normal Fit')
# Specification limits
plt.axvline(x=LSL, color='green', linestyle='--', linewidth=2, label='LSL/USL')
plt.axvline(x=USL, color='green', linestyle='--', linewidth=2)
plt.axvline(x=HRC_target, color='black', linestyle='-', linewidth=2, label='Target')
# Shade out-of-spec regions
x_below = x_fit[x_fit < LSL]
y_below = norm.pdf(x_below, HRC_mean, HRC_std)
plt.fill_between(x_below, 0, y_below, color='red', alpha=0.3, label='Out of Spec')
x_above = x_fit[x_fit > USL]
y_above = norm.pdf(x_above, HRC_mean, HRC_std)
plt.fill_between(x_above, 0, y_above, color='red', alpha=0.3)
plt.xlabel('Hardness (HRC)')
plt.ylabel('Probability Density')
plt.title(f'Tempering Process Capability (Cp={Cp:.2f}, Cpk={Cpk:.2f})')
plt.legend()
plt.grid(True, alpha=0.3)
plt.savefig('process_capability.png', dpi=150, bbox_inches='tight')
plt.show()
# Improvement recommendations
print(f"\nImprovement Recommendations:")
if Cpk < 1.33:
print(f" Priority 1: Reduce process variation")
print(f" - Improve temperature control (±3°C target)")
print(f" - Calibrate furnace thermocouples")
print(f" - Implement SPC charts for real-time monitoring")
if abs(HRC_mean - HRC_target) > 0.5:
print(f" Priority 2: Center the process")
print(f" - Adjust nominal temperature to {T_nominal + (HRC_target - HRC_mean)*2:.1f}°C")
print(f" Priority 3: Implement closed-loop control")
print(f" - Measure hardness in-line (non-destructive testing)")
print(f" - Adaptive control based on feedback")
# Simulation: Predicted improvement
T_std_improved = 3 / 3 # ±3°C
t_std_improved = t_nominal * 0.05 / 3 # ±5%
T_samples_improved = np.random.normal(T_nominal, T_std_improved, n_samples)
t_samples_improved = np.random.normal(t_nominal, t_std_improved, n_samples)
hardness_improved = np.array([
tempered_hardness(hollomon_jaffe_parameter(T, t))
for T, t in zip(T_samples_improved, t_samples_improved)
])
HRC_std_improved = np.std(hardness_improved)
Cp_improved = (USL - LSL) / (6 * HRC_std_improved)
defect_rate_improved = np.sum((hardness_improved < LSL) | (hardness_improved > USL)) / n_samples * 100
print(f"\nProjected Improvement (±3°C, ±5% time):")
print(f" New Cp: {Cp_improved:.3f} (from {Cp:.3f})")
print(f" New Defect Rate: {defect_rate_improved:.2f}% (from {defect_rate:.2f}%)")
print(f" ’ Improvement: {defect_rate - defect_rate_improved:.2f}% reduction")
Exercise 2-9: Comprehensive Heat Treatment Simulation Project (Hard)
Design the entire manufacturing process for automotive gear (SCM440) (carburizing quenching ’ tempering ’ surface hardness HRC 58-62, core hardness HRC 35-40). Consider balance between process time, energy cost, and material properties, and determine optimal process using Python.
Solution Example
# Comprehensive process design framework
print("Comprehensive Heat Treatment Process Design for Automotive Gear (SCM440)")
print("="*70)
# Step 1: Carburizing
print("\nStep 1: Carburizing Process")
T_carburizing = 930 # °C
t_carburizing = 8 # hours (3mm carburized layer thickness)
case_depth = 3 # mm
print(f" Temperature: {T_carburizing}°C")
print(f" Time: {t_carburizing} hours")
print(f" Target Case Depth: {case_depth} mm")
print(f" Carbon Potential: 1.0-1.2%")
# Step 2: Diffusion Hold
print("\nStep 2: Diffusion Hold")
T_diffusion = 850 # °C
t_diffusion = 2 # hours
print(f" Temperature: {T_diffusion}°C")
print(f" Time: {t_diffusion} hours")
print(f" Purpose: Uniform carbon distribution")
# Step 3: Quenching
print("\nStep 3: Quenching")
T_quench_start = 850
T_quench_medium = 60 # Oil temperature
cooling_rate = 50 # °C/s
print(f" Quench Medium: Oil at {T_quench_medium}°C")
print(f" Cooling Rate: ~{cooling_rate}°C/s")
print(f" Expected Surface Microstructure: Martensite (HRC 60-64)")
print(f" Expected Core Microstructure: Martensite + Bainite (HRC 45-50)")
# Step 4: Tempering
print("\nStep 4: Tempering")
T_tempering = 180 # °C (low-temperature tempering)
t_tempering = 2 # hours
# Hardness prediction (simplified)
P_surface = hollomon_jaffe_parameter(T_tempering, t_tempering)
HRC_surface_tempered = tempered_hardness(P_surface, HRC_initial=62)
print(f" Temperature: {T_tempering}°C")
print(f" Time: {t_tempering} hours")
print(f" Expected Surface Hardness: HRC {HRC_surface_tempered:.0f}")
print(f" Expected Core Hardness: HRC 35-40 (minimal change)")
# Process evaluation
print("\n" + "="*70)
print("Process Performance Evaluation:")
# Total process time
total_time = t_carburizing + t_diffusion + 0.5 + t_tempering # Quenching 0.5h
print(f" Total Process Time: {total_time:.1f} hours")
# Energy cost (simplified calculation)
# Furnace volume 1m³, heater efficiency 70%, electricity rate 0.15 USD/kWh
furnace_volume = 1.0 # m³
heat_capacity = 1200 # kJ/m³/°C
efficiency = 0.7
energy_carb = furnace_volume * heat_capacity * T_carburizing * t_carburizing / efficiency
energy_diff = furnace_volume * heat_capacity * T_diffusion * t_diffusion / efficiency
energy_temp = furnace_volume * heat_capacity * T_tempering * t_tempering / efficiency
total_energy = (energy_carb + energy_diff + energy_temp) / 3600 # kWh
energy_cost = total_energy * 0.15 # USD/batch
print(f" Total Energy Consumption: {total_energy:.1f} kWh/batch")
print(f" Energy Cost: ${energy_cost:.2f}/batch (100 gears)")
print(f" Per-Gear Cost: ${energy_cost/100:.4f}")
# Material properties evaluation
print(f"\nTarget Material Properties:")
print(f" Surface Hardness: HRC 58-62 (Target: {HRC_surface_tempered:.0f} )")
print(f" Core Hardness: HRC 35-40 ")
print(f" Case Depth: {case_depth} mm ")
print(f" Core Toughness: Maintained by lower hardness")
# Optimization opportunities
print(f"\nOptimization Opportunities:")
print(f" 1. Use vacuum carburizing ’ Reduce time by 30%")
print(f" 2. Implement press quenching ’ Reduce distortion")
print(f" 3. Online hardness monitoring ’ Quality assurance")
print(f" 4. Energy recovery system ’ Reduce energy cost by 20%")
# Conclusion
print(f"\n" + "="*70)
print("Conclusion:")
print(f" This process meets all specifications for automotive gears.")
print(f" Total cycle time: {total_time:.1f} hours (competitive)")
print(f" Energy efficiency: Moderate (improvement possible)")
print(f" Quality assurance: Jominy testing + statistical process control recommended")
Learning Achievement Checklist
Basic Understanding Level
- Can explain the four types of annealing (full annealing, stress relief, recrystallization, spheroidizing)
- Understand Fick's diffusion equation and Arrhenius equation
- Can explain the relationship between quenching cooling rate and microstructure (martensite, bainite, pearlite)
- Understand the hardness-toughness trade-off in tempering
- Can explain the three stages of age hardening (solution treatment ’ quenching ’ aging)
Practical Skills Level
- Can calculate diffusion distance and holding time to design annealing profiles
- Can evaluate hardenability from Jominy test data
- Can determine tempering conditions using Hollomon-Jaffe parameter
- Can read TTT/CCT diagrams and predict microstructure from cooling rate
- Can determine optimal aging time from age hardening curves
Application Level
- Can design complex heat treatment processes (austempering, etc.) using TTT diagrams
- Can optimize two-step aging treatment (strength-cost trade-off)
- Can statistically evaluate heat treatment quality using process capability indices (Cp, Cpk)
- Can design and optimize entire composite processes such as carburizing quenching
- Can quantitatively evaluate the impact of process variation using Monte Carlo simulation
- Can comprehensively evaluate energy cost and productivity of heat treatment
References
- Porter, D.A., Easterling, K.E., Sherif, M.Y. (2009). Phase Transformations in Metals and Alloys (3rd ed.). CRC Press, pp. 234-278, 345-389.
- Krauss, G. (2015). Steels: Processing, Structure, and Performance (2nd ed.). ASM International, pp. 145-189, 267-312.
- Honeycombe, R.W.K., Bhadeshia, H.K.D.H. (2017). Steels: Microstructure and Properties (4th ed.). Butterworth-Heinemann, pp. 89-124, 201-245.
- ASM International. (1991). ASM Handbook Volume 4: Heat Treating. ASM International, pp. 456-489, 567-602.
- Callister, W.D., Rethwisch, D.G. (2020). Materials Science and Engineering: An Introduction (10th ed.). Wiley, pp. 345-378, 412-456.
- Totten, G.E. (Ed.). (2006). Steel Heat Treatment: Metallurgy and Technologies (2nd ed.). CRC Press, pp. 123-167, 289-334.
- Polmear, I.J., StJohn, D., Nie, J.F., Qian, M. (2017). Light Alloys: Metallurgy of the Light Metals (5th ed.). Butterworth-Heinemann, pp. 178-223, 267-301.
- Brooks, C.R. (1996). Principles of the Heat Treatment of Plain Carbon and Low Alloy Steels. ASM International, pp. 56-89, 134-178.