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 DivertorLab
# Compact-reactor divertor design point
lab = DivertorLab(P_sol_MW=20.0, R_major=2.1, B_pol=1.0)
# Radiative detachment: raising the radiated-power fraction f_rad dissipates
# scrape-off-layer power volumetrically before it reaches the target plate,
# lowering the target heat flux predicted by the two-point model.
f_rad_sweep = np.linspace(0.0, 0.9, 19)
q_target = np.array(
[lab.solve_2point_transport(expansion_factor=10.0, f_rad=f)[1] for f in f_rad_sweep]
)
fig, ax = plt.subplots(figsize=(8, 5))
ax.plot(f_rad_sweep * 100, q_target, "b-o", markersize=4)
ax.set_xlabel("Radiated power fraction f_rad [%]")
ax.set_ylabel("Target-plate heat flux [MW/m$^2$]")
ax.set_title("Divertor radiative detachment (two-point model)")
ax.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
reduction = 100.0 * (1.0 - q_target[-1] / q_target[0])
print(f"Target heat flux at f_rad=0.0: {q_target[0]:.1f} MW/m^2")
print(f"Target heat flux at f_rad=0.9: {q_target[-1]:.1f} MW/m^2 ({reduction:.0f}% lower)")
# Plasma-facing material check at the design point
peak_W, status_W = lab.simulate_tungsten()
print(f"Tungsten target: peak T = {peak_W:.0f} C ({status_W})")
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 MultiGroupBlanket
class _MultiGroupTBR:
"""Three-group blanket TBR from Li-6 enrichment and blanket thickness."""
def compute_tbr(self, li6_enrichment: float, blanket_thickness_m: float) -> float:
blanket = MultiGroupBlanket(
thickness_cm=blanket_thickness_m * 100.0, li6_enrichment=li6_enrichment
)
return float(blanket.solve_transport()["tbr"])
neutronics = _MultiGroupTBR()
# 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 two-point transport solve
def bench_divertor_single():
lab.solve_2point_transport(expansion_factor=10.0, f_rad=0.5)
t_div = timeit.repeat(bench_divertor_single, number=100, repeat=5)
print("solve_2point_transport 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 f in np.linspace(0.0, 0.9, 20):
lab.solve_2point_transport(expansion_factor=10.0, f_rad=f)
t_sweep = timeit.repeat(bench_divertor_sweep, number=10, repeat=3)
print("\nDivertor 20-point detachment 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("\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("\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