04 — Divertor Heat Flux & Blanket Neutronics¶
This tutorial demonstrates two nuclear engineering modules:
- Divertor Thermal Simulation — heat-flux spreading on the divertor target
- Blanket Neutronics — tritium breeding ratio (TBR) calculation
Both are critical for DEMO-class reactor feasibility.
License: © 1998–2026 Miroslav Šotek. GNU AGPL v3.
In [ ]:
import numpy as np
import matplotlib.pyplot as plt
Part A: Divertor Thermal Simulation¶
The divertor receives the exhaust heat from the plasma scrape-off layer. ITER's divertor must handle ~10 MW/m² steady-state, with transients up to 20 MW/m².
In [ ]:
from scpn_fusion.core.divertor_thermal_sim import DivertorThermalSim
# ITER-like divertor parameters
sim = DivertorThermalSim(
target_length_m=1.2,
target_width_m=0.3,
n_points=100,
coolant_temp_K=423.0, # 150°C water
)
# Sweep over power levels
powers = np.linspace(5.0, 25.0, 20) # MW/m²
peak_temps = []
for q in powers:
result = sim.compute_steady_state(heat_flux_mw_m2=q)
peak_temps.append(result['peak_temp_K'])
fig, ax = plt.subplots(figsize=(8, 5))
ax.plot(powers, np.array(peak_temps) - 273.15, 'b-o', markersize=4)
ax.axhline(y=1800, color='r', linestyle='--', label='Tungsten recrystallization (1800°C)')
ax.axhline(y=3422, color='darkred', linestyle='--', label='Tungsten melting (3422°C)')
ax.set_xlabel('Heat Flux [MW/m²]')
ax.set_ylabel('Peak Surface Temperature [°C]')
ax.set_title('Divertor Target Temperature vs Heat Flux')
ax.legend()
ax.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
print(f"At 10 MW/m²: {peak_temps[5]-273.15:.0f}°C")
print(f"At 20 MW/m²: {peak_temps[15]-273.15:.0f}°C")
Part B: Blanket Neutronics (Tritium Breeding)¶
A D-T fusion reactor must breed its own tritium. The Tritium Breeding Ratio (TBR) must exceed 1.0 for fuel self-sufficiency. ITER targets TBR ≈ 1.10.
In [ ]:
from scpn_fusion.nuclear.blanket_neutronics import BlanketNeutronics
neutronics = BlanketNeutronics()
# Compare blanket concepts
concepts = {
'HCLL (Li-Pb)': {'li6_enrichment': 0.30, 'blanket_thickness_m': 0.8},
'HCPB (Li-ceramic)': {'li6_enrichment': 0.60, 'blanket_thickness_m': 0.7},
'WCLL (Water-cooled)': {'li6_enrichment': 0.40, 'blanket_thickness_m': 0.75},
}
results = {}
for name, params in concepts.items():
tbr = neutronics.compute_tbr(**params)
results[name] = tbr
print(f"{name:25s}: TBR = {tbr:.3f} {'✓' if tbr > 1.0 else '✗'}")
# Plot
fig, ax = plt.subplots(figsize=(8, 4))
bars = ax.barh(list(results.keys()), list(results.values()), color=['#2196F3', '#4CAF50', '#FF9800'])
ax.axvline(x=1.0, color='r', linestyle='--', linewidth=2, label='TBR = 1.0 (self-sufficiency)')
ax.set_xlabel('Tritium Breeding Ratio')
ax.set_title('Blanket Concept TBR Comparison')
ax.legend()
plt.tight_layout()
plt.show()
Benchmark¶
In [ ]:
%%timeit -n1 -r3
for _name, _params in concepts.items():
neutronics.compute_tbr(**_params)
Part C: Li-6 Enrichment Sensitivity¶
TBR depends strongly on Li-6 enrichment. Natural lithium is only 7.5% Li-6.
In [ ]:
enrichments = np.linspace(0.075, 0.90, 50)
tbr_values = [neutronics.compute_tbr(li6_enrichment=e, blanket_thickness_m=0.8) for e in enrichments]
fig, ax = plt.subplots(figsize=(8, 5))
ax.plot(enrichments * 100, tbr_values, 'g-', linewidth=2)
ax.axhline(y=1.0, color='r', linestyle='--', label='TBR = 1.0')
ax.axhline(y=1.1, color='orange', linestyle='--', alpha=0.7, label='TBR = 1.1 (target)')
ax.axvline(x=7.5, color='gray', linestyle=':', label='Natural Li-6 (7.5%)')
ax.set_xlabel('Li-6 Enrichment [%]')
ax.set_ylabel('Tritium Breeding Ratio')
ax.set_title('TBR vs Li-6 Enrichment (0.8m blanket)')
ax.legend()
ax.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
Performance Benchmarks¶
Timing the key computations in this notebook:
- Single divertor steady-state solve (
compute_steady_state) - Heat flux sweep (20-point parameter scan)
- Single TBR calculation (
compute_tbr) - Li-6 enrichment sweep (50-point parameter scan)
In [ ]:
import timeit
# 1. Single divertor steady-state solve
def bench_divertor_single():
sim.compute_steady_state(heat_flux_mw_m2=10.0)
t_div = timeit.repeat(bench_divertor_single, number=100, repeat=5)
print(f"compute_steady_state single call (100 calls):")
print(f" Mean: {np.mean(t_div)*1000:.1f} ms +/- {np.std(t_div)*1000:.1f} ms")
print(f" Per call: {np.mean(t_div)/100*1000:.2f} ms")
# 2. Heat flux sweep (20-point parameter scan)
def bench_divertor_sweep():
for q in np.linspace(5.0, 25.0, 20):
sim.compute_steady_state(heat_flux_mw_m2=q)
t_sweep = timeit.repeat(bench_divertor_sweep, number=10, repeat=3)
print(f"\nDivertor 20-point heat flux sweep (10 runs):")
print(f" Mean: {np.mean(t_sweep)*1000:.1f} ms +/- {np.std(t_sweep)*1000:.1f} ms")
print(f" Per run: {np.mean(t_sweep)/10*1000:.2f} ms")
# 3. Single TBR calculation
def bench_tbr_single():
neutronics.compute_tbr(li6_enrichment=0.30, blanket_thickness_m=0.8)
t_tbr = timeit.repeat(bench_tbr_single, number=1000, repeat=5)
print(f"\ncompute_tbr single call (1000 calls):")
print(f" Mean: {np.mean(t_tbr)*1000:.1f} ms +/- {np.std(t_tbr)*1000:.1f} ms")
print(f" Per call: {np.mean(t_tbr)/1000*1e6:.2f} us")
# 4. Li-6 enrichment sweep (50-point parameter scan)
def bench_tbr_sweep():
for e in np.linspace(0.075, 0.90, 50):
neutronics.compute_tbr(li6_enrichment=e, blanket_thickness_m=0.8)
t_tbr_sweep = timeit.repeat(bench_tbr_sweep, number=10, repeat=3)
print(f"\nTBR 50-point enrichment sweep (10 runs):")
print(f" Mean: {np.mean(t_tbr_sweep)*1000:.1f} ms +/- {np.std(t_tbr_sweep)*1000:.1f} ms")
print(f" Per run: {np.mean(t_tbr_sweep)/10*1000:.2f} ms")
Summary¶
- The divertor model computes 1D/2D temperature profiles under steady-state and transient heat loads
- The neutronics model estimates TBR from Li-6 enrichment, blanket geometry, and neutron multiplication
- Both modules feed into the compact reactor optimizer (see notebook 01) for integrated design
- With Rust acceleration, these calculations can be embedded in real-time digital twin scenarios