d0fus 2.2.1__tar.gz → 2.3.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {d0fus-2.2.1 → d0fus-2.3.0}/D0FUS.py +30 -3
- {d0fus-2.2.1 → d0fus-2.3.0}/D0FUS_BIB/D0FUS_figures.py +330 -44
- {d0fus-2.2.1 → d0fus-2.3.0}/D0FUS_BIB/D0FUS_import.py +5 -1
- {d0fus-2.2.1 → d0fus-2.3.0}/D0FUS_BIB/D0FUS_parameterization.py +50 -6
- {d0fus-2.2.1 → d0fus-2.3.0}/D0FUS_BIB/D0FUS_physical_functions.py +239 -29
- {d0fus-2.2.1 → d0fus-2.3.0}/D0FUS_BIB/D0FUS_radial_build_functions.py +367 -111
- d0fus-2.3.0/D0FUS_EXE/D0FUS_genetic.py +2959 -0
- {d0fus-2.2.1 → d0fus-2.3.0}/D0FUS_EXE/D0FUS_run.py +168 -28
- {d0fus-2.2.1 → d0fus-2.3.0}/D0FUS_EXE/D0FUS_scan.py +15 -5
- {d0fus-2.2.1 → d0fus-2.3.0}/PKG-INFO +1 -1
- {d0fus-2.2.1 → d0fus-2.3.0}/d0fus.egg-info/PKG-INFO +1 -1
- {d0fus-2.2.1 → d0fus-2.3.0}/pyproject.toml +1 -1
- d0fus-2.2.1/D0FUS_EXE/D0FUS_genetic.py +0 -1335
- {d0fus-2.2.1 → d0fus-2.3.0}/D0FUS_BIB/D0FUS_cost_data.py +0 -0
- {d0fus-2.2.1 → d0fus-2.3.0}/D0FUS_BIB/D0FUS_cost_functions.py +0 -0
- {d0fus-2.2.1 → d0fus-2.3.0}/LICENSE +0 -0
- {d0fus-2.2.1 → d0fus-2.3.0}/README.md +0 -0
- {d0fus-2.2.1 → d0fus-2.3.0}/d0fus.egg-info/SOURCES.txt +0 -0
- {d0fus-2.2.1 → d0fus-2.3.0}/d0fus.egg-info/dependency_links.txt +0 -0
- {d0fus-2.2.1 → d0fus-2.3.0}/d0fus.egg-info/entry_points.txt +0 -0
- {d0fus-2.2.1 → d0fus-2.3.0}/d0fus.egg-info/requires.txt +0 -0
- {d0fus-2.2.1 → d0fus-2.3.0}/d0fus.egg-info/top_level.txt +0 -0
- {d0fus-2.2.1 → d0fus-2.3.0}/setup.cfg +0 -0
|
@@ -170,12 +170,39 @@ def detect_mode_from_input(input_file):
|
|
|
170
170
|
#%% Main functions
|
|
171
171
|
|
|
172
172
|
def print_banner():
|
|
173
|
-
"""Display D0FUS banner
|
|
174
|
-
|
|
173
|
+
"""Display the D0FUS startup banner.
|
|
174
|
+
|
|
175
|
+
Simple double-line framed banner with title, tagline, version, author,
|
|
176
|
+
license and repository URL. Can be silenced with the environment variable
|
|
177
|
+
``D0FUS_NO_BANNER`` (useful for batch scans, HPC jobs, CI).
|
|
178
|
+
"""
|
|
179
|
+
if os.environ.get("D0FUS_NO_BANNER", "").strip() not in ("", "0", "false", "False"):
|
|
180
|
+
return
|
|
181
|
+
|
|
182
|
+
# Resolve installed package version, fall back to a static string
|
|
183
|
+
try:
|
|
184
|
+
from importlib.metadata import version, PackageNotFoundError
|
|
185
|
+
try:
|
|
186
|
+
v = version("d0fus")
|
|
187
|
+
except PackageNotFoundError:
|
|
188
|
+
v = "dev"
|
|
189
|
+
except ImportError:
|
|
190
|
+
v = "dev"
|
|
191
|
+
|
|
192
|
+
# The version is the only dynamic line; the others are hardcoded for clarity.
|
|
193
|
+
# Inside width is 51 characters; the version is centered programmatically.
|
|
194
|
+
version_line = f"Version {v}".center(51)
|
|
195
|
+
|
|
196
|
+
banner = f"""
|
|
175
197
|
╔═══════════════════════════════════════════════════╗
|
|
176
198
|
║ ║
|
|
177
199
|
║ D0FUS ║
|
|
178
|
-
║
|
|
200
|
+
║ Design 0-dimensional for Fusion Systems ║
|
|
201
|
+
║ ║
|
|
202
|
+
╠═══════════════════════════════════════════════════╣
|
|
203
|
+
║{version_line}║
|
|
204
|
+
║ T. Auclair, CEA-IRFM | CeCILL-C ║
|
|
205
|
+
║ https://github.com/IRFM/D0FUS ║
|
|
179
206
|
║ ║
|
|
180
207
|
╚═══════════════════════════════════════════════════╝
|
|
181
208
|
"""
|
|
@@ -48,6 +48,8 @@ if __name__ != "__main__":
|
|
|
48
48
|
get_Lz,
|
|
49
49
|
f_He_fraction,
|
|
50
50
|
f_q_profile,
|
|
51
|
+
f_sigmav,
|
|
52
|
+
_two_point_core, M_F_DT,
|
|
51
53
|
)
|
|
52
54
|
from .D0FUS_radial_build_functions import (
|
|
53
55
|
J_non_Cu_NbTi, J_non_Cu_Nb3Sn, J_non_Cu_REBCO,
|
|
@@ -76,6 +78,8 @@ else:
|
|
|
76
78
|
get_Lz,
|
|
77
79
|
f_He_fraction,
|
|
78
80
|
f_q_profile,
|
|
81
|
+
f_sigmav,
|
|
82
|
+
_two_point_core, M_F_DT,
|
|
79
83
|
)
|
|
80
84
|
from D0FUS_BIB.D0FUS_radial_build_functions import (
|
|
81
85
|
J_non_Cu_NbTi, J_non_Cu_Nb3Sn, J_non_Cu_REBCO,
|
|
@@ -639,6 +643,96 @@ def plot_density_line_vol(
|
|
|
639
643
|
# 3. Nuclear / radiation physics
|
|
640
644
|
# =============================================================================
|
|
641
645
|
|
|
646
|
+
def plot_DT_reactivity(
|
|
647
|
+
T_min_keV: float = 1.0,
|
|
648
|
+
T_max_keV: float = 100.0,
|
|
649
|
+
n_points: int = 600,
|
|
650
|
+
T_op_min: float = 10.0,
|
|
651
|
+
T_op_max: float = 25.0,
|
|
652
|
+
save_dir: str | None = None,
|
|
653
|
+
) -> None:
|
|
654
|
+
"""
|
|
655
|
+
Plot the D-T Maxwellian reactivity ⟨σv⟩(T) used by D0FUS, together with
|
|
656
|
+
the pressure-limited fusion power density metric ⟨σv⟩/T².
|
|
657
|
+
|
|
658
|
+
Left panel:
|
|
659
|
+
⟨σv⟩(T) computed from the Bosch & Hale (1992) parameterisation,
|
|
660
|
+
with the power plant operating window [T_op_min, T_op_max] shaded
|
|
661
|
+
and the reactivity maximum marked.
|
|
662
|
+
|
|
663
|
+
Right panel:
|
|
664
|
+
Pressure-limited figure of merit ⟨σv⟩/T² (arbitrary units).
|
|
665
|
+
At fixed plasma pressure p = nT, the fuel ion density scales as
|
|
666
|
+
n ∝ 1/T, so the volumetric fusion power density p_fus ∝ n² ⟨σv⟩
|
|
667
|
+
is proportional to ⟨σv⟩/T². This identifies the optimal operating
|
|
668
|
+
temperature for a β-limited tokamak near 14 keV.
|
|
669
|
+
|
|
670
|
+
Parameters
|
|
671
|
+
----------
|
|
672
|
+
T_min_keV, T_max_keV : float Ion temperature scan range [keV].
|
|
673
|
+
n_points : int Number of temperature grid points.
|
|
674
|
+
T_op_min, T_op_max : float Power plant operating window bounds [keV].
|
|
675
|
+
save_dir : str or None
|
|
676
|
+
|
|
677
|
+
References
|
|
678
|
+
----------
|
|
679
|
+
Bosch & Hale, Nucl. Fusion 32, 611 (1992) — DT reactivity fit (Table IV).
|
|
680
|
+
Freidberg, Plasma Physics and Fusion Energy (2007) — pressure-limited optimum.
|
|
681
|
+
"""
|
|
682
|
+
# Temperature grid (linear, since the operating window lies in the rapid-rise zone)
|
|
683
|
+
T_arr = np.linspace(T_min_keV, T_max_keV, n_points)
|
|
684
|
+
sv_arr = f_sigmav(T_arr)
|
|
685
|
+
|
|
686
|
+
# Reactivity maximum
|
|
687
|
+
i_peak = int(np.argmax(sv_arr))
|
|
688
|
+
T_peak, sv_peak = T_arr[i_peak], sv_arr[i_peak]
|
|
689
|
+
|
|
690
|
+
# Pressure-limited metric ⟨σv⟩/T² (units arbitrary, normalised below)
|
|
691
|
+
metric = sv_arr / T_arr**2
|
|
692
|
+
i_opt = int(np.argmax(metric))
|
|
693
|
+
T_opt, m_opt = T_arr[i_opt], metric[i_opt]
|
|
694
|
+
|
|
695
|
+
# --- Figure ------------------------------------------------------------
|
|
696
|
+
fig, axes = plt.subplots(1, 2, figsize=(11, 4.5))
|
|
697
|
+
|
|
698
|
+
# Left panel: reactivity curve on log scale
|
|
699
|
+
ax = axes[0]
|
|
700
|
+
ax.semilogy(T_arr, sv_arr, color="tab:red", lw=2.0,
|
|
701
|
+
label="Bosch & Hale (1992)")
|
|
702
|
+
ax.axvspan(T_op_min, T_op_max, color="goldenrod", alpha=0.20,
|
|
703
|
+
label=f"Operating window\n{T_op_min:.0f}–{T_op_max:.0f} keV")
|
|
704
|
+
ax.axvline(T_peak, color="k", lw=1.0, ls="--",
|
|
705
|
+
label=f"peak: T = {T_peak:.0f} keV")
|
|
706
|
+
ax.set_xlabel(r"Ion temperature $T$ [keV]", fontsize=11)
|
|
707
|
+
ax.set_ylabel(r"$\langle\sigma v\rangle_{DT}$ [m$^3$ s$^{-1}$]", fontsize=11)
|
|
708
|
+
ax.set_title("D-T Maxwellian reactivity", fontsize=11)
|
|
709
|
+
ax.set_xlim(T_min_keV, T_max_keV)
|
|
710
|
+
ax.set_ylim(1e-25, 3e-21)
|
|
711
|
+
ax.grid(True, which="both", alpha=0.25)
|
|
712
|
+
ax.legend(fontsize=9, loc="lower right")
|
|
713
|
+
|
|
714
|
+
# Right panel: pressure-limited figure of merit
|
|
715
|
+
ax = axes[1]
|
|
716
|
+
ax.plot(T_arr, metric / m_opt, color="tab:blue", lw=2.0,
|
|
717
|
+
label=r"$\langle\sigma v\rangle / T^2$ (normalised)")
|
|
718
|
+
ax.axvspan(T_op_min, T_op_max, color="goldenrod", alpha=0.20,
|
|
719
|
+
label=f"Operating window\n{T_op_min:.0f}–{T_op_max:.0f} keV")
|
|
720
|
+
ax.axvline(T_opt, color="k", lw=1.0, ls="--",
|
|
721
|
+
label=f"optimum: T = {T_opt:.1f} keV")
|
|
722
|
+
ax.set_xlabel(r"Ion temperature $T$ [keV]", fontsize=11)
|
|
723
|
+
ax.set_ylabel(r"$\langle\sigma v\rangle / T^2$ [normalised]", fontsize=11)
|
|
724
|
+
ax.set_title(r"Pressure-limited fusion power figure of merit", fontsize=11)
|
|
725
|
+
ax.set_xlim(0, 50)
|
|
726
|
+
ax.set_ylim(0, 1.08)
|
|
727
|
+
ax.grid(True, alpha=0.25)
|
|
728
|
+
ax.legend(fontsize=9, loc="lower right")
|
|
729
|
+
|
|
730
|
+
plt.suptitle("Fusion reactivity and operating temperature window",
|
|
731
|
+
fontsize=12, fontweight="bold")
|
|
732
|
+
plt.tight_layout()
|
|
733
|
+
_save_or_show(fig, save_dir, "DT_reactivity")
|
|
734
|
+
|
|
735
|
+
|
|
642
736
|
def plot_Lz_cooling(
|
|
643
737
|
Te_min_keV: float = 0.05,
|
|
644
738
|
Te_max_keV: float = 100.0,
|
|
@@ -711,7 +805,8 @@ def plot_He_fraction(
|
|
|
711
805
|
nbar: float = 1.0,
|
|
712
806
|
Tbar: float = 8.9,
|
|
713
807
|
tauE: float = 3.7,
|
|
714
|
-
|
|
808
|
+
C_Alpha_ITER: float = 5.0,
|
|
809
|
+
C_Alpha_DEMO: float = 7.0,
|
|
715
810
|
nu_T: float = 1.0,
|
|
716
811
|
save_dir: str | None = None,
|
|
717
812
|
) -> None:
|
|
@@ -720,16 +815,25 @@ def plot_He_fraction(
|
|
|
720
815
|
C_α = τ_α / τ_E for ITER and EU-DEMO reference parameters, comparing
|
|
721
816
|
academic (no pedestal) and H-mode pedestal profile assumptions.
|
|
722
817
|
|
|
818
|
+
Two D0FUS default values for C_α are highlighted with vertical dotted
|
|
819
|
+
lines: C_α = 5 for ITER (consistent with Progress in the ITER Physics
|
|
820
|
+
Basis projections) and C_α = 7 for EU-DEMO 2017 (PROCESS reference run).
|
|
821
|
+
|
|
723
822
|
Parameters
|
|
724
823
|
----------
|
|
725
824
|
nbar, Tbar, tauE : float Reference plasma parameters [10²⁰ m⁻³, keV, s].
|
|
726
|
-
|
|
825
|
+
C_Alpha_ITER : float D0FUS default C_α for ITER (vertical line) [-].
|
|
826
|
+
C_Alpha_DEMO : float D0FUS default C_α for EU-DEMO 2017 (vertical line) [-].
|
|
727
827
|
nu_T : float Temperature peaking exponent [-].
|
|
728
828
|
save_dir : str or None
|
|
729
829
|
|
|
730
830
|
References
|
|
731
831
|
----------
|
|
732
832
|
ITER Physics Basis, Nucl. Fusion 39, §2.4 (1999).
|
|
833
|
+
Shimada et al., Progress in the ITER Physics Basis, Ch. 1,
|
|
834
|
+
Nucl. Fusion 47, S1 (2007).
|
|
835
|
+
Kovari et al., Fus. Eng. Des. 89, 3054 (2014) — PROCESS systems code.
|
|
836
|
+
Reiter, Wolf and Kever, Nucl. Fusion 30, 2141 (1990) — ignition bound on C_α.
|
|
733
837
|
"""
|
|
734
838
|
C_arr = np.linspace(2, 15, 150)
|
|
735
839
|
|
|
@@ -739,16 +843,22 @@ def plot_He_fraction(
|
|
|
739
843
|
for C in C_arr]
|
|
740
844
|
fa_DEMO = [f_He_fraction(1.2, 12.5, 4.6, C, nu_T) * 100 for C in C_arr]
|
|
741
845
|
|
|
742
|
-
fig, ax = plt.subplots(figsize=(
|
|
743
|
-
ax.plot(C_arr, fa_ITER, "b-", lw=
|
|
744
|
-
ax.plot(C_arr, fa_ITER_ped, "b--", lw=1.
|
|
745
|
-
ax.plot(C_arr, fa_DEMO, "r-", lw=
|
|
746
|
-
|
|
747
|
-
ax.
|
|
748
|
-
|
|
749
|
-
ax.
|
|
750
|
-
|
|
751
|
-
ax.
|
|
846
|
+
fig, ax = plt.subplots(figsize=(8.0, 5.2))
|
|
847
|
+
ax.plot(C_arr, fa_ITER, "b-", lw=2.0, label="ITER — academic (no pedestal)")
|
|
848
|
+
ax.plot(C_arr, fa_ITER_ped, "b--", lw=1.6, label="ITER — Refined H-mode pedestal")
|
|
849
|
+
ax.plot(C_arr, fa_DEMO, "r-", lw=2.0, label="EU-DEMO — academic")
|
|
850
|
+
# Two D0FUS default operating points: ITER and EU-DEMO 2017
|
|
851
|
+
ax.axvline(C_Alpha_ITER, color="tab:blue", lw=1.6, ls=":",
|
|
852
|
+
label=f"ITER default $C_\\alpha$ = {C_Alpha_ITER:.0f}")
|
|
853
|
+
ax.axvline(C_Alpha_DEMO, color="tab:red", lw=1.6, ls=":",
|
|
854
|
+
label=f"EU-DEMO 2017 default $C_\\alpha$ = {C_Alpha_DEMO:.0f}")
|
|
855
|
+
ax.axhspan(4, 6, color="grey", alpha=0.12, label="ITER target 4–6 %")
|
|
856
|
+
ax.set_xlabel(r"Removal efficiency $C_\alpha = \tau_\alpha / \tau_E$", fontsize=14)
|
|
857
|
+
ax.set_ylabel(r"Helium ash fraction $f_\alpha$ [%]", fontsize=14)
|
|
858
|
+
ax.set_title("He ash fraction — academic vs refined H-mode pedestal",
|
|
859
|
+
fontsize=13)
|
|
860
|
+
ax.legend(fontsize=11, loc="upper left")
|
|
861
|
+
ax.tick_params(axis="both", labelsize=12)
|
|
752
862
|
ax.set_xlim(2, 15)
|
|
753
863
|
ax.set_ylim(0, 25)
|
|
754
864
|
ax.grid(True, alpha=0.3)
|
|
@@ -761,12 +871,84 @@ def plot_He_fraction(
|
|
|
761
871
|
# 4. Superconductor / cable engineering
|
|
762
872
|
# =============================================================================
|
|
763
873
|
|
|
874
|
+
# -----------------------------------------------------------------------------
|
|
875
|
+
# NHMFL engineering current density reference data (T = 4.2 K)
|
|
876
|
+
# -----------------------------------------------------------------------------
|
|
877
|
+
# Source: National High Magnetic Field Laboratory (NHMFL / MagLab) "Engineering
|
|
878
|
+
# Current Density Plot", file Je_vs_B-041118a (updated 2021-01-04). Values are
|
|
879
|
+
# whole-strand / whole-tape engineering current density Je [A/mm²] vs applied
|
|
880
|
+
# field B [T]. Only the three series directly comparable to the D0FUS strand
|
|
881
|
+
# scalings (NbTi, Nb3Sn bronze, REBCO worst-orientation) are kept here.
|
|
882
|
+
#
|
|
883
|
+
# Per-series provenance:
|
|
884
|
+
# * REBCO B perp tape plane : SuperPower SP26, 50 µm substrate, 7.5%Zr,
|
|
885
|
+
# measured at NHMFL (Braccini, Jaroszynski, Xu) - DOI 10.1088/0953-2048/24/3/035001
|
|
886
|
+
# * Nb3Sn High Sn Bronze : Miyazaki et al., MT-18 (IEEE TASC 14:2, 2004)
|
|
887
|
+
# DOI 10.1109/TASC.2004.830344
|
|
888
|
+
# * NbTi LHC 4.2 K : Boutboul et al., MT-19 (IEEE TASC 16:2, 2006)
|
|
889
|
+
# DOI 10.1109/TASC.2006.870777
|
|
890
|
+
_NHMFL_JE_REFERENCE = {
|
|
891
|
+
"NbTi": {
|
|
892
|
+
"label": "NbTi LHC strand",
|
|
893
|
+
"B": np.array([0.61, 0.95, 1.34, 1.68, 2.23, 3.18, 4.15, 5.12, 6.10]),
|
|
894
|
+
"Je": np.array([5106.88, 4054.15, 3180.60, 2710.23, 2217.46,
|
|
895
|
+
1724.69, 1411.11, 1187.12, 918.34]),
|
|
896
|
+
},
|
|
897
|
+
"Nb3Sn": {
|
|
898
|
+
"label": "Nb₃Sn high-Sn bronze",
|
|
899
|
+
"B": np.array([18.00, 19.01, 20.01, 21.01, 22.02, 23.00, 24.00, 25.00]),
|
|
900
|
+
"Je": np.array([166.64, 137.81, 111.10, 84.38, 60.82, 40.08, 19.34, 8.09]),
|
|
901
|
+
},
|
|
902
|
+
"REBCO": {
|
|
903
|
+
"label": "REBCO tape, B⊥ (worst case)",
|
|
904
|
+
"B": np.array([1.0, 1.5, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0,
|
|
905
|
+
10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 18.0,
|
|
906
|
+
20.0, 22.0, 24.0, 25.0, 26.0, 28.0, 30.0, 31.0]),
|
|
907
|
+
"Je": np.array([3665.00, 2920.00, 2380.00, 1796.15, 1447.69, 1188.46,
|
|
908
|
+
1034.37, 956.92, 851.83, 780.84, 720.00, 679.44,
|
|
909
|
+
626.09, 593.24, 563.48, 535.38, 516.52, 469.56,
|
|
910
|
+
433.08, 406.96, 391.30, 367.69, 360.00, 344.35,
|
|
911
|
+
328.70, 322.31]),
|
|
912
|
+
},
|
|
913
|
+
}
|
|
914
|
+
|
|
915
|
+
|
|
916
|
+
def _overlay_nhmfl_reference(ax, color_map: dict, alpha: float = 0.30) -> None:
|
|
917
|
+
"""
|
|
918
|
+
Overlay the NHMFL engineering current density reference (4.2 K) on ``ax``
|
|
919
|
+
as transparent markers connected by a thin line, color-matched per material.
|
|
920
|
+
|
|
921
|
+
Parameters
|
|
922
|
+
----------
|
|
923
|
+
ax : matplotlib Axes Target axes (assumed log-y in A/mm²).
|
|
924
|
+
color_map : dict Mapping {material_key: hex_color} for NbTi,
|
|
925
|
+
Nb3Sn, REBCO; the same colours used for the
|
|
926
|
+
D0FUS curves so the visual pairing is direct.
|
|
927
|
+
alpha : float Transparency for both markers and connecting
|
|
928
|
+
line (default 0.30, "background" appearance).
|
|
929
|
+
"""
|
|
930
|
+
for key, ref in _NHMFL_JE_REFERENCE.items():
|
|
931
|
+
col = color_map.get(key, "#808080")
|
|
932
|
+
ax.plot(
|
|
933
|
+
ref["B"], ref["Je"],
|
|
934
|
+
linestyle="-", linewidth=1.0,
|
|
935
|
+
marker="o", markersize=4.5,
|
|
936
|
+
color=col, alpha=alpha,
|
|
937
|
+
zorder=1,
|
|
938
|
+
)
|
|
939
|
+
# Single neutral legend handle for the whole NHMFL reference set.
|
|
940
|
+
ax.plot([], [], linestyle="-", linewidth=1.0, marker="o", markersize=4.5,
|
|
941
|
+
color="#555555", alpha=alpha,
|
|
942
|
+
label="NHMFL strand/tape data @ 4.2 K (2011 vintage, ref.)")
|
|
943
|
+
|
|
944
|
+
|
|
764
945
|
def plot_Jc_scaling(
|
|
765
946
|
B_min: float = 0.5,
|
|
766
947
|
B_max: float = 45.0,
|
|
767
948
|
T_op: float = 4.2,
|
|
768
949
|
f_non_Cu_LTS: float = 0.50,
|
|
769
950
|
f_non_Cu_HTS: float = 0.60,
|
|
951
|
+
show_nhmfl_ref: bool = True,
|
|
770
952
|
save_dir: str | None = None,
|
|
771
953
|
) -> None:
|
|
772
954
|
"""
|
|
@@ -776,29 +958,47 @@ def plot_Jc_scaling(
|
|
|
776
958
|
* Nb₃Sn (LTS, ITER/EU-DEMO TF/CS)
|
|
777
959
|
* REBCO (HTS, ARC / SPARC)
|
|
778
960
|
|
|
961
|
+
When ``show_nhmfl_ref`` is True, the NHMFL/MagLab experimental engineering
|
|
962
|
+
current density data at 4.2 K is overlaid as a transparent background
|
|
963
|
+
reference (color-matched markers + thin line per material).
|
|
964
|
+
|
|
779
965
|
Parameters
|
|
780
966
|
----------
|
|
781
|
-
B_min, B_max
|
|
782
|
-
T_op
|
|
783
|
-
f_non_Cu_LTS
|
|
784
|
-
f_non_Cu_HTS
|
|
785
|
-
|
|
967
|
+
B_min, B_max : float Field scan range [T].
|
|
968
|
+
T_op : float Operating temperature [K].
|
|
969
|
+
f_non_Cu_LTS : float Non-copper fraction for LTS strands [-].
|
|
970
|
+
f_non_Cu_HTS : float Non-copper fraction for HTS tapes [-].
|
|
971
|
+
show_nhmfl_ref : bool Overlay NHMFL 4.2 K reference data (default True).
|
|
972
|
+
save_dir : str or None
|
|
786
973
|
|
|
787
974
|
References
|
|
788
975
|
----------
|
|
789
976
|
ITER TF strand specifications; Nijhuis (2008); Fleiter & Ballarino (2014).
|
|
977
|
+
NHMFL/MagLab Engineering Current Density Plot, updated 2021-01-04.
|
|
790
978
|
"""
|
|
791
979
|
B_vals = np.linspace(B_min, B_max, 300)
|
|
792
980
|
J_NbTi = J_non_Cu_NbTi(B_vals, T_op) * f_non_Cu_LTS / 1e6 # [A/mm²]
|
|
793
981
|
J_Nb3Sn = J_non_Cu_Nb3Sn(B_vals, T_op, Eps=-0.003) * f_non_Cu_LTS / 1e6
|
|
794
982
|
J_REBCO = J_non_Cu_REBCO(B_vals, T_op, Tet=0) * f_non_Cu_HTS / 1e6
|
|
795
983
|
|
|
984
|
+
# Colour palette shared between D0FUS curves and NHMFL reference overlay.
|
|
985
|
+
col_NbTi, col_Nb3Sn, col_REBCO = "#A06AB4", "#E06C75", "#D4B000"
|
|
986
|
+
|
|
796
987
|
fig, ax = plt.subplots(figsize=(7, 5))
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
988
|
+
|
|
989
|
+
# NHMFL background reference (drawn first → lower z-order).
|
|
990
|
+
if show_nhmfl_ref:
|
|
991
|
+
_overlay_nhmfl_reference(
|
|
992
|
+
ax,
|
|
993
|
+
color_map={"NbTi": col_NbTi, "Nb3Sn": col_Nb3Sn, "REBCO": col_REBCO},
|
|
994
|
+
alpha=0.30,
|
|
995
|
+
)
|
|
996
|
+
|
|
997
|
+
ax.plot(B_vals, J_NbTi, lw=2, color=col_NbTi, label="NbTi strand", zorder=3)
|
|
998
|
+
ax.plot(B_vals, J_Nb3Sn, lw=2, color=col_Nb3Sn, label="Nb₃Sn strand", zorder=3)
|
|
999
|
+
ax.plot(B_vals, J_REBCO, lw=2, color=col_REBCO, label="REBCO tape", zorder=3)
|
|
800
1000
|
ax.set_xlabel("Magnetic field B [T]", fontsize=12)
|
|
801
|
-
ax.set_ylabel("
|
|
1001
|
+
ax.set_ylabel("Strand/Tape current density [A/mm²]", fontsize=12)
|
|
802
1002
|
ax.set_title(f"Superconductor Jc scalings @ {T_op} K", fontsize=12)
|
|
803
1003
|
ax.legend(loc="upper right", fontsize=10)
|
|
804
1004
|
ax.grid(True, alpha=0.3)
|
|
@@ -2016,6 +2216,85 @@ def plot_radiation_profile(
|
|
|
2016
2216
|
_save_or_show(fig, save_dir, "run_radiation_profile")
|
|
2017
2217
|
|
|
2018
2218
|
|
|
2219
|
+
def plot_divertor_two_point(
|
|
2220
|
+
run: dict,
|
|
2221
|
+
n_pts: int = 300,
|
|
2222
|
+
save_dir: str | None = None,
|
|
2223
|
+
) -> None:
|
|
2224
|
+
"""
|
|
2225
|
+
Simple two-point-model view: target temperature versus SOL dissipation.
|
|
2226
|
+
|
|
2227
|
+
A single panel shows the target electron temperature T_et as a function of
|
|
2228
|
+
the SOL power-loss fraction f_cooling, at the converged upstream conditions
|
|
2229
|
+
(q_par_u, p_u read from the run). The dashed line is the detachment
|
|
2230
|
+
threshold T_et = 10 eV (Stangeby 2018). The flat plateau at high T_et is the
|
|
2231
|
+
attached sheath-limited branch where the two-point model no longer applies.
|
|
2232
|
+
The minimum dissipation required for target survival (Eq. 14) is given in
|
|
2233
|
+
the title and marked by the dotted vertical line.
|
|
2234
|
+
|
|
2235
|
+
Parameters
|
|
2236
|
+
----------
|
|
2237
|
+
run : dict D0FUS run output; reads the 'divertor' sub-dict produced
|
|
2238
|
+
by f_heat_two_point.
|
|
2239
|
+
n_pts : int Number of f_cooling samples.
|
|
2240
|
+
save_dir : str or None
|
|
2241
|
+
|
|
2242
|
+
References
|
|
2243
|
+
----------
|
|
2244
|
+
P.C. Stangeby, Plasma Phys. Control. Fusion 60 (2018) 044022.
|
|
2245
|
+
"""
|
|
2246
|
+
div = run.get("divertor", {}) or {}
|
|
2247
|
+
if not {"q_par_u", "T_eu", "n_sep"}.issubset(div):
|
|
2248
|
+
print(" [skip] no two-point-model divertor solution in run dict")
|
|
2249
|
+
return
|
|
2250
|
+
|
|
2251
|
+
q_par_u = div["q_par_u"] # [MW/m^2]
|
|
2252
|
+
T_eu = div["T_eu"] # [eV]
|
|
2253
|
+
n_sep = div["n_sep"] # [m^-3]
|
|
2254
|
+
f_c_op = div.get("f_cooling", 0.0)
|
|
2255
|
+
f_m_op = div.get("f_mom", 0.0)
|
|
2256
|
+
T_et_op = div.get("T_et", np.nan)
|
|
2257
|
+
f_pwr_req = div.get("f_pwr_loss_req", np.nan)
|
|
2258
|
+
R0 = run.get("R0", "?")
|
|
2259
|
+
|
|
2260
|
+
q_par_u_SI = q_par_u * 1e6
|
|
2261
|
+
p_u = 2.0 * n_sep * E_ELEM * T_eu
|
|
2262
|
+
|
|
2263
|
+
f_c = np.linspace(0.0, 0.995, n_pts)
|
|
2264
|
+
T_et = np.array([min(_two_point_core(q_par_u_SI, p_u, fc, f_m_op, 7.0, M_F_DT)[0],
|
|
2265
|
+
T_eu) for fc in f_c])
|
|
2266
|
+
|
|
2267
|
+
fig, ax = plt.subplots(figsize=(7, 4.5))
|
|
2268
|
+
ax.plot(f_c, T_et, color="#4477AA", lw=2.4)
|
|
2269
|
+
ax.axhline(10.0, color="#EE6677", lw=1.3, ls="--")
|
|
2270
|
+
ax.text(0.015, 11.0, "detachment (10 eV)", color="#EE6677",
|
|
2271
|
+
fontsize=9, va="bottom")
|
|
2272
|
+
if np.isfinite(f_pwr_req):
|
|
2273
|
+
ax.axvline(f_pwr_req, color="0.55", lw=1.0, ls=":")
|
|
2274
|
+
if np.isfinite(T_et_op):
|
|
2275
|
+
ax.plot([f_c_op], [T_et_op], "o", ms=9, color="k", zorder=5)
|
|
2276
|
+
near_left = f_c_op < 0.5
|
|
2277
|
+
near_top = T_et_op > 0.3 * T_eu
|
|
2278
|
+
ax.annotate(f"operating point\n$T_{{e,t}}$ = {T_et_op:.1f} eV",
|
|
2279
|
+
(f_c_op, T_et_op), textcoords="offset points",
|
|
2280
|
+
xytext=(14 if near_left else -12, -16 if near_top else 14),
|
|
2281
|
+
ha="left" if near_left else "right",
|
|
2282
|
+
va="top" if near_top else "bottom", fontsize=9)
|
|
2283
|
+
|
|
2284
|
+
ax.set_yscale("log")
|
|
2285
|
+
ax.set_xlim(0, 1)
|
|
2286
|
+
ax.set_xlabel(r"SOL power-loss fraction $f_{\rm cooling}$", fontsize=12)
|
|
2287
|
+
ax.set_ylabel(r"Target electron temperature $T_{e,t}$ [eV]", fontsize=12)
|
|
2288
|
+
ttl = (rf"Divertor two-point model: $R_0$={R0} m, "
|
|
2289
|
+
rf"$q_{{\parallel u}}$={q_par_u/1e3:.2f} GW/m$^2$")
|
|
2290
|
+
if np.isfinite(f_pwr_req):
|
|
2291
|
+
ttl += f", required dissipation = {f_pwr_req:.2f}"
|
|
2292
|
+
ax.set_title(ttl, fontsize=11)
|
|
2293
|
+
ax.grid(True, which="both", alpha=0.3)
|
|
2294
|
+
plt.tight_layout()
|
|
2295
|
+
_save_or_show(fig, save_dir, "run_divertor_two_point")
|
|
2296
|
+
|
|
2297
|
+
|
|
2019
2298
|
# ---------------------------------------------------------------------------
|
|
2020
2299
|
# A — Convenience wrapper
|
|
2021
2300
|
# ---------------------------------------------------------------------------
|
|
@@ -2663,10 +2942,10 @@ def plot_all(
|
|
|
2663
2942
|
|
|
2664
2943
|
── Plasma shaping [ 1– 7]
|
|
2665
2944
|
── Kinetic profiles [ 8–11]
|
|
2666
|
-
── Transport & current [12–
|
|
2667
|
-
── Radiation & impurities [
|
|
2668
|
-
── Superconductor eng. [
|
|
2669
|
-
── Coil sizing & mechanics [
|
|
2945
|
+
── Transport & current [12–13]
|
|
2946
|
+
── Radiation & impurities [14–16]
|
|
2947
|
+
── Superconductor eng. [17–19]
|
|
2948
|
+
── Coil sizing & mechanics [20–29]
|
|
2670
2949
|
· TF grading [21–23]
|
|
2671
2950
|
· CS / CIRCE / geometry [24–27]
|
|
2672
2951
|
· Benchmarks [28–29]
|
|
@@ -2732,6 +3011,9 @@ def plot_all(
|
|
|
2732
3011
|
_p(14, "Coronal cooling coefficient L_z(T)")
|
|
2733
3012
|
plot_Lz_cooling(save_dir=save_dir)
|
|
2734
3013
|
|
|
3014
|
+
_p(15, "D-T reactivity ⟨σv⟩(T)")
|
|
3015
|
+
plot_DT_reactivity(save_dir=save_dir)
|
|
3016
|
+
|
|
2735
3017
|
_p(16, "Helium ash fraction")
|
|
2736
3018
|
plot_He_fraction(save_dir=save_dir)
|
|
2737
3019
|
|
|
@@ -2797,23 +3079,24 @@ def plot_run(
|
|
|
2797
3079
|
save_dir: str | None = None,
|
|
2798
3080
|
) -> None:
|
|
2799
3081
|
"""
|
|
2800
|
-
Render the run-specific figure set (
|
|
3082
|
+
Render the run-specific figure set (11 figures).
|
|
2801
3083
|
|
|
2802
3084
|
This is the subset called after each D0FUS run. It contains only the
|
|
2803
3085
|
figures that depend on the current run configuration and results —
|
|
2804
3086
|
no validation curves, no benchmarks, no scaling-law surveys.
|
|
2805
3087
|
|
|
2806
3088
|
Figures produced:
|
|
2807
|
-
[ 1/
|
|
2808
|
-
[ 2/
|
|
2809
|
-
[ 3/
|
|
2810
|
-
[ 4/
|
|
2811
|
-
[ 5/
|
|
2812
|
-
[ 6/
|
|
2813
|
-
[ 7/
|
|
2814
|
-
[ 8/
|
|
2815
|
-
[ 9/
|
|
2816
|
-
[10/
|
|
3089
|
+
[ 1/11] Tokamak LCFS comparison (with D0FUS overlay)
|
|
3090
|
+
[ 2/11] Miller flux surfaces (run geometry)
|
|
3091
|
+
[ 3/11] Shaping profiles κ(ρ), δ(ρ)
|
|
3092
|
+
[ 4/11] Kinetic profiles n(ρ), T(ρ), p(ρ)
|
|
3093
|
+
[ 5/11] Safety factor q(ρ) and current decomposition
|
|
3094
|
+
[ 6/11] Radiation profiles
|
|
3095
|
+
[ 7/11] Divertor two-point model (detachment vs SOL dissipation)
|
|
3096
|
+
[ 8/11] TF coil side view
|
|
3097
|
+
[ 9/11] CICC TF conductor
|
|
3098
|
+
[10/11] CS cross-section
|
|
3099
|
+
[11/11] CICC CS conductor
|
|
2817
3100
|
|
|
2818
3101
|
Parameters
|
|
2819
3102
|
----------
|
|
@@ -2822,7 +3105,7 @@ def plot_run(
|
|
|
2822
3105
|
If provided, figures are saved as PNG files.
|
|
2823
3106
|
Pass ``None`` to display interactively.
|
|
2824
3107
|
"""
|
|
2825
|
-
N =
|
|
3108
|
+
N = 11
|
|
2826
3109
|
|
|
2827
3110
|
def _p(i, label):
|
|
2828
3111
|
print(f" [{i:2d}/{N}] {label}")
|
|
@@ -2850,17 +3133,20 @@ def plot_run(
|
|
|
2850
3133
|
_p(6, "Radiation profiles")
|
|
2851
3134
|
plot_radiation_profile(run, save_dir=save_dir)
|
|
2852
3135
|
|
|
3136
|
+
_p(7, "Divertor two-point model")
|
|
3137
|
+
plot_divertor_two_point(run, save_dir=save_dir)
|
|
3138
|
+
|
|
2853
3139
|
# ── Coils & conductors ────────────────────────────────────────────
|
|
2854
|
-
_p(
|
|
3140
|
+
_p(8, "TF coil side view")
|
|
2855
3141
|
plot_TF_side_view(run, save_dir=save_dir)
|
|
2856
3142
|
|
|
2857
|
-
_p(
|
|
3143
|
+
_p(9, "CICC TF conductor")
|
|
2858
3144
|
plot_CICC_cross_section(build_conductor_from_run(run, coil="TF"), save_dir=save_dir)
|
|
2859
3145
|
|
|
2860
|
-
_p(
|
|
3146
|
+
_p(10, "CS cross-section")
|
|
2861
3147
|
plot_CS_cross_section(run, save_dir=save_dir)
|
|
2862
3148
|
|
|
2863
|
-
_p(
|
|
3149
|
+
_p(11, "CICC CS conductor")
|
|
2864
3150
|
plot_CICC_cross_section(build_conductor_from_run(run, coil="CS"), save_dir=save_dir)
|
|
2865
3151
|
|
|
2866
3152
|
print("Done.")
|
|
@@ -3107,11 +3393,11 @@ def plot_TF_benchmark_table(cfg=None, save_dir=None) -> None:
|
|
|
3107
3393
|
"ARC": {"a": 1.10, "b": 0.89, "R0": 3.30, "σ": 1000e6, "T_op": 20.0,
|
|
3108
3394
|
"B_max": 23.0, "n_TF": 1, "sc": "REBCO", "config": "Plug",
|
|
3109
3395
|
"κ": 1.84, "I_cond": 50e3, "V_max": 10e3, "N_sub": 6,
|
|
3110
|
-
"tau_h": 20, "J_wost":
|
|
3396
|
+
"tau_h": 20, "J_wost": 200e6},
|
|
3111
3397
|
"SPARC": {"a": 0.57, "b": 0.18, "R0": 1.85, "σ": 1000e6, "T_op": 20.0,
|
|
3112
3398
|
"B_max": 20.0, "n_TF": 1, "sc": "REBCO", "config": "Bucking",
|
|
3113
|
-
"κ": 1.75, "I_cond": 40.5e3,"V_max": 10e3, "N_sub":
|
|
3114
|
-
"tau_h": 20, "J_wost":
|
|
3399
|
+
"κ": 1.75, "I_cond": 40.5e3,"V_max": 10e3, "N_sub": 6,
|
|
3400
|
+
"tau_h": 20, "J_wost": 200e6},
|
|
3115
3401
|
}
|
|
3116
3402
|
|
|
3117
3403
|
def _clean(val):
|
|
@@ -18,11 +18,13 @@ os.environ["KMP_DUPLICATE_LIB_OK"] = "TRUE"
|
|
|
18
18
|
|
|
19
19
|
import json
|
|
20
20
|
import math
|
|
21
|
+
import multiprocessing
|
|
21
22
|
import random
|
|
22
23
|
import re
|
|
23
24
|
import shutil
|
|
24
25
|
import sys
|
|
25
26
|
import time
|
|
27
|
+
import traceback
|
|
26
28
|
import warnings
|
|
27
29
|
import importlib
|
|
28
30
|
from datetime import datetime
|
|
@@ -34,7 +36,7 @@ import numpy as np
|
|
|
34
36
|
import pandas as pd
|
|
35
37
|
import sympy as sp
|
|
36
38
|
from typing import List, Tuple
|
|
37
|
-
from dataclasses import dataclass
|
|
39
|
+
from dataclasses import dataclass, replace, asdict
|
|
38
40
|
|
|
39
41
|
#%% Scipy - Optimization and Numerical Methods
|
|
40
42
|
|
|
@@ -66,11 +68,13 @@ import matplotlib.colors as mcolors
|
|
|
66
68
|
import matplotlib.lines as mlines
|
|
67
69
|
import matplotlib.patches as mpatches
|
|
68
70
|
import matplotlib.cm as cm
|
|
71
|
+
from matplotlib.animation import FuncAnimation, PillowWriter
|
|
69
72
|
from matplotlib.colors import Normalize
|
|
70
73
|
from matplotlib.gridspec import GridSpec
|
|
71
74
|
from matplotlib.patches import Circle, Rectangle, Patch
|
|
72
75
|
from matplotlib.ticker import MultipleLocator
|
|
73
76
|
from mpl_toolkits.axes_grid1 import make_axes_locatable
|
|
77
|
+
from mpl_toolkits.mplot3d import Axes3D # noqa: F401 (registers the '3d' projection)
|
|
74
78
|
from pandas.plotting import table
|
|
75
79
|
from tqdm import tqdm
|
|
76
80
|
from dataclasses import dataclass
|