From b821ee99c0387735881cadec6ed0f0af3f12b49e Mon Sep 17 00:00:00 2001 From: mn3981 Date: Fri, 7 Aug 2026 14:48:16 +0100 Subject: [PATCH 01/29] Rename `drsep` to `dr_plasma_outboard_midplane_separatrix_separation` for clarity and update references --- process/data_structure/physics_variables.py | 3 ++- process/models/physics/physics.py | 16 ++++++++-------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/process/data_structure/physics_variables.py b/process/data_structure/physics_variables.py index 910ce574c1..9ff1054c7a 100644 --- a/process/data_structure/physics_variables.py +++ b/process/data_structure/physics_variables.py @@ -435,7 +435,8 @@ class PhysicsData: len_sol_outboard_power_decay: float = 0.0 """Outboard scrape off layer power decay length (λ_q) [m]""" - drsep: float = 0.0 + dr_plasma_outboard_midplane_separatrix_separation: float = 0.0 + """Radial distance between the first and second plasma separatrixes at the outer midplane (δR_sep) [m]""" fio: float = 0.0 diff --git a/process/models/physics/physics.py b/process/models/physics/physics.py index 95b6d126a8..c71a9c4177 100644 --- a/process/models/physics/physics.py +++ b/process/models/physics/physics.py @@ -1002,15 +1002,15 @@ def run(self): # Parameters taken from double null machine # D. Brunner et al - # Issue #1559 Infinities in physics_module.drsep when running single null in a + # Issue #1559 Infinities in physics_module.dr_plasma_outboard_midplane_separatrix_separation when running single null in a # double null machine # C W Ashe if self.data.physics.f_p_div_lower < 4.5e-5: - self.data.physics.drsep = 1.5e-2 + self.data.physics.dr_plasma_outboard_midplane_separatrix_separation = 1.5e-2 elif self.data.physics.f_p_div_lower > (1.0e0 - 4.5e-5): - self.data.physics.drsep = -1.5e-2 + self.data.physics.dr_plasma_outboard_midplane_separatrix_separation = -1.5e-2 else: - self.data.physics.drsep = ( + self.data.physics.dr_plasma_outboard_midplane_separatrix_separation = ( -2.0e0 * 1.5e-3 * math.atanh(2.0e0 * (self.data.physics.f_p_div_lower - 0.5e0)) @@ -1029,7 +1029,7 @@ def run(self): + np.exp( -( ( - self.data.physics.drsep + self.data.physics.dr_plasma_outboard_midplane_separatrix_separation / self.data.physics.len_sol_outboard_power_decay ) ** 2 @@ -2226,9 +2226,9 @@ def outplas(self): if self.data.divertor.n_divertors == 2: po.ovarre( self.outfile, - "Midplane separation of the two magnetic closed flux surfaces (m)", - "(drsep)", - self.data.physics.drsep, + "Radial distance between the first and second plasma separatrixes at the outer midplane (δR_sep) [m]", + "(dr_plasma_outboard_midplane_separatrix_separation)", + self.data.physics.dr_plasma_outboard_midplane_separatrix_separation, "OP ", ) From 5de613811f4aa4564cbd90f7f2540b2730ddfd3c Mon Sep 17 00:00:00 2001 From: mn3981 Date: Fri, 7 Aug 2026 16:19:59 +0100 Subject: [PATCH 02/29] Rename `fli` to `f_p_div_lower_inboard_separatrix` for clarity and update references in physics calculations --- process/data_structure/physics_variables.py | 3 ++- process/models/physics/physics.py | 16 +++++++++++----- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/process/data_structure/physics_variables.py b/process/data_structure/physics_variables.py index 9ff1054c7a..bdb62e2f24 100644 --- a/process/data_structure/physics_variables.py +++ b/process/data_structure/physics_variables.py @@ -440,7 +440,8 @@ class PhysicsData: fio: float = 0.0 - fli: float = 0.0 + f_p_div_lower_inboard_separatrix: float = 0.0 + """Fraction of plasma separatrix power to the lower inboard divertor target""" flo: float = 0.0 diff --git a/process/models/physics/physics.py b/process/models/physics/physics.py index c71a9c4177..13bb3e8e1c 100644 --- a/process/models/physics/physics.py +++ b/process/models/physics/physics.py @@ -1042,7 +1042,7 @@ def run(self): # Double Null configuration # Find all the power fractions accross the targets # Taken from D3-D conventional divertor design - self.data.physics.fli = ( + self.data.physics.f_p_div_lower_inboard_separatrix = ( self.data.physics.f_p_div_lower * self.data.physics.fio ) self.data.physics.flo = self.data.physics.f_p_div_lower * ( @@ -1055,16 +1055,22 @@ def run(self): 1.0e0 - self.data.physics.fio ) # power into each target - self.data.physics.plimw = self.data.physics.fli * self.data.physics.ptarmw + self.data.physics.plimw = ( + self.data.physics.f_p_div_lower_inboard_separatrix + * self.data.physics.ptarmw + ) self.data.physics.plomw = self.data.physics.flo * self.data.physics.ptarmw self.data.physics.puimw = self.data.physics.fui * self.data.physics.ptarmw self.data.physics.puomw = self.data.physics.fuo * self.data.physics.ptarmw else: # Single null configuration - self.data.physics.fli = self.data.physics.fio + self.data.physics.f_p_div_lower_inboard_separatrix = self.data.physics.fio self.data.physics.flo = 1.0e0 - self.data.physics.fio # power into each target - self.data.physics.plimw = self.data.physics.fli * self.data.physics.ptarmw + self.data.physics.plimw = ( + self.data.physics.f_p_div_lower_inboard_separatrix + * self.data.physics.ptarmw + ) self.data.physics.plomw = self.data.physics.flo * self.data.physics.ptarmw # Calculate some derived quantities that may not have been defined earlier @@ -2243,7 +2249,7 @@ def outplas(self): self.outfile, "Fraction of power incident on the lower inner target", "(fLI)", - self.data.physics.fli, + self.data.physics.f_p_div_lower_inboard_separatrix, "OP ", ) po.ovarre( From 61781aeff9d17a30fb42d2a953de6997258048a2 Mon Sep 17 00:00:00 2001 From: mn3981 Date: Fri, 7 Aug 2026 16:20:35 +0100 Subject: [PATCH 03/29] Rename `flo` to `f_p_div_lower_outboard_separatrix` for clarity and update references in physics calculations --- process/data_structure/physics_variables.py | 3 ++- process/models/physics/physics.py | 20 ++++++++++++++------ 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/process/data_structure/physics_variables.py b/process/data_structure/physics_variables.py index bdb62e2f24..87abb86358 100644 --- a/process/data_structure/physics_variables.py +++ b/process/data_structure/physics_variables.py @@ -443,7 +443,8 @@ class PhysicsData: f_p_div_lower_inboard_separatrix: float = 0.0 """Fraction of plasma separatrix power to the lower inboard divertor target""" - flo: float = 0.0 + f_p_div_lower_outboard_separatrix: float = 0.0 + """Fraction of plasma separatrix power to the lower outboard divertor target""" fui: float = 0.0 diff --git a/process/models/physics/physics.py b/process/models/physics/physics.py index 13bb3e8e1c..25b354c0c3 100644 --- a/process/models/physics/physics.py +++ b/process/models/physics/physics.py @@ -1045,8 +1045,8 @@ def run(self): self.data.physics.f_p_div_lower_inboard_separatrix = ( self.data.physics.f_p_div_lower * self.data.physics.fio ) - self.data.physics.flo = self.data.physics.f_p_div_lower * ( - 1.0e0 - self.data.physics.fio + self.data.physics.f_p_div_lower_outboard_separatrix = ( + self.data.physics.f_p_div_lower * (1.0e0 - self.data.physics.fio) ) self.data.physics.fui = ( 1.0e0 - self.data.physics.f_p_div_lower @@ -1059,19 +1059,27 @@ def run(self): self.data.physics.f_p_div_lower_inboard_separatrix * self.data.physics.ptarmw ) - self.data.physics.plomw = self.data.physics.flo * self.data.physics.ptarmw + self.data.physics.plomw = ( + self.data.physics.f_p_div_lower_outboard_separatrix + * self.data.physics.ptarmw + ) self.data.physics.puimw = self.data.physics.fui * self.data.physics.ptarmw self.data.physics.puomw = self.data.physics.fuo * self.data.physics.ptarmw else: # Single null configuration self.data.physics.f_p_div_lower_inboard_separatrix = self.data.physics.fio - self.data.physics.flo = 1.0e0 - self.data.physics.fio + self.data.physics.f_p_div_lower_outboard_separatrix = ( + 1.0e0 - self.data.physics.fio + ) # power into each target self.data.physics.plimw = ( self.data.physics.f_p_div_lower_inboard_separatrix * self.data.physics.ptarmw ) - self.data.physics.plomw = self.data.physics.flo * self.data.physics.ptarmw + self.data.physics.plomw = ( + self.data.physics.f_p_div_lower_outboard_separatrix + * self.data.physics.ptarmw + ) # Calculate some derived quantities that may not have been defined earlier self.data.physics.p_plasma_heating_total_mw = ( @@ -2256,7 +2264,7 @@ def outplas(self): self.outfile, "Fraction of power incident on the lower outer target", "(fLO)", - self.data.physics.flo, + self.data.physics.f_p_div_lower_outboard_separatrix, "OP ", ) if self.data.divertor.n_divertors == 2: From 5dece072ad444cebef096dbee429130b96e57593 Mon Sep 17 00:00:00 2001 From: mn3981 Date: Fri, 7 Aug 2026 17:24:28 +0100 Subject: [PATCH 04/29] Rename `fui` to `f_p_div_upper_inboard_separatrix` for clarity and update references in plasma calculations; add Brunner's method for divertor power splits calculation --- process/data_structure/physics_variables.py | 3 +- process/models/physics/exhaust.py | 97 +++++++++++++++++++++ process/models/physics/physics.py | 14 ++- 3 files changed, 110 insertions(+), 4 deletions(-) diff --git a/process/data_structure/physics_variables.py b/process/data_structure/physics_variables.py index 87abb86358..dd025ef4d4 100644 --- a/process/data_structure/physics_variables.py +++ b/process/data_structure/physics_variables.py @@ -446,7 +446,8 @@ class PhysicsData: f_p_div_lower_outboard_separatrix: float = 0.0 """Fraction of plasma separatrix power to the lower outboard divertor target""" - fui: float = 0.0 + f_p_div_upper_inboard_separatrix: float = 0.0 + """Fraction of plasma separatrix power to the upper inboard divertor target""" fuo: float = 0.0 diff --git a/process/models/physics/exhaust.py b/process/models/physics/exhaust.py index c61f9c66aa..b44bafbca1 100644 --- a/process/models/physics/exhaust.py +++ b/process/models/physics/exhaust.py @@ -2,6 +2,8 @@ import logging +import numpy as np + from process.core import constants from process.core import process_output as po from process.core.model import Model @@ -218,3 +220,98 @@ def calculate_radiation_fraction( return 0.0 return p_plasma_rad_mw / p_plasma_heating_mw + + @staticmethod + def calculate_brunner_divertor_power_splits( + dr_plasma_outboard_midplane_separatrix_separation: float, + len_plasma_sol_power_decay: float, + ): + """ + Calculate the power splits to the divertor targets using Brunner's method. + + Returns + ------- + dict + Dictionary containing the power splits to the divertor targets. + + References + ---------- + [1] D. Brunner, A. Q. Kuang, B. LaBombard, and J. L. Terry, “The dependence of + divertor power sharing on magnetic flux balance in near double-null + configurations on Alcator C-Mod,” Nuclear Fusion, vol. 58, no. 7, p. 076010, + May 2018, doi: 10.1088/1741-4326/aac006. + """ + # Fraction of the power to the inner divertors at δR_sep = 0 and δR_sep → ∞ + F_P_INNER_SEP_0 = 0.16e0 + F_P_INNER_SEP_INFINITY = 0.41e0 + + # Fractions of total outboard power going to each target + + f_p_outboard_lower = 1 / ( + 1 + + np.exp( + -dr_plasma_outboard_midplane_separatrix_separation + / len_plasma_sol_power_decay + ) + ) + + f_p_outboard_upper = 1 / ( + 1 + + np.exp( + dr_plasma_outboard_midplane_separatrix_separation + / len_plasma_sol_power_decay + ) + ) + + # Fractions of the total inboard power going to each target + + f_p_inboard_lower = 1 / ( + 1 + + np.exp( + dr_plasma_outboard_midplane_separatrix_separation + / (len_plasma_sol_power_decay * 0.5) + ) + ) + + f_p_total_inboard = F_P_INNER_SEP_0 + ( + F_P_INNER_SEP_0 - F_P_INNER_SEP_INFINITY + ) * ( + 1.0e0 + - ( + 2.0e0 + / ( + 1.0e0 + + np.exp( + -( + ( + dr_plasma_outboard_midplane_separatrix_separation + / len_plasma_sol_power_decay + ) + ** 2 + ) + ) + ) + ) + ) + + f_p_total_outboard = 1.0e0 - f_p_total_inboard + + f_p_inboard_upper = f_p_total_inboard * (1.0e0 - f_p_inboard_lower) + f_p_outboard_upper = f_p_total_outboard * (1.0e0 - f_p_outboard_lower) + f_p_inboard_lower = f_p_total_inboard * f_p_inboard_lower + f_p_outboard_lower = f_p_total_outboard * f_p_outboard_lower + + print( + f"f_p_inboard_lower={f_p_inboard_lower}, f_p_inboard_upper={f_p_inboard_upper}, " + f"f_p_outboard_lower={f_p_outboard_lower}, f_p_outboard_upper={f_p_outboard_upper}" + ) + print( + f"sum={np.sum([f_p_inboard_lower, f_p_inboard_upper, f_p_outboard_lower, f_p_outboard_upper])}" + ) + + return ( + f_p_inboard_lower, + f_p_inboard_upper, + f_p_outboard_lower, + f_p_outboard_upper, + ) diff --git a/process/models/physics/physics.py b/process/models/physics/physics.py index 25b354c0c3..0272a26984 100644 --- a/process/models/physics/physics.py +++ b/process/models/physics/physics.py @@ -993,6 +993,11 @@ def run(self): # ============================================================ + _, _, _, _ = self.exhaust.calculate_brunner_divertor_power_splits( + self.data.physics.dr_plasma_outboard_midplane_separatrix_separation, + len_plasma_sol_power_decay=0.001, + ) + # Calculate the target imbalances # find the total power into the targets self.data.physics.ptarmw = self.data.physics.p_plasma_separatrix_mw * ( @@ -1048,7 +1053,7 @@ def run(self): self.data.physics.f_p_div_lower_outboard_separatrix = ( self.data.physics.f_p_div_lower * (1.0e0 - self.data.physics.fio) ) - self.data.physics.fui = ( + self.data.physics.f_p_div_upper_inboard_separatrix = ( 1.0e0 - self.data.physics.f_p_div_lower ) * self.data.physics.fio self.data.physics.fuo = (1.0e0 - self.data.physics.f_p_div_lower) * ( @@ -1063,7 +1068,10 @@ def run(self): self.data.physics.f_p_div_lower_outboard_separatrix * self.data.physics.ptarmw ) - self.data.physics.puimw = self.data.physics.fui * self.data.physics.ptarmw + self.data.physics.puimw = ( + self.data.physics.f_p_div_upper_inboard_separatrix + * self.data.physics.ptarmw + ) self.data.physics.puomw = self.data.physics.fuo * self.data.physics.ptarmw else: # Single null configuration @@ -2272,7 +2280,7 @@ def outplas(self): self.outfile, "Fraction of power incident on the upper inner target", "(fUI)", - self.data.physics.fui, + self.data.physics.f_p_div_upper_inboard_separatrix, "OP ", ) po.ovarre( From 99b32be9c5ca3c8b19db05d13ee2ba66a926e355 Mon Sep 17 00:00:00 2001 From: mn3981 Date: Fri, 7 Aug 2026 17:58:48 +0100 Subject: [PATCH 05/29] Add Brunner divertor power split comparison plot and update PlasmaExhaust calculations --- process/core/io/plot/summary.py | 63 +++++++++++++++++++++++++++++++ process/models/physics/exhaust.py | 8 ---- 2 files changed, 63 insertions(+), 8 deletions(-) diff --git a/process/core/io/plot/summary.py b/process/core/io/plot/summary.py index 5c53ac2933..7c880d71d6 100644 --- a/process/core/io/plot/summary.py +++ b/process/core/io/plot/summary.py @@ -64,6 +64,7 @@ ElectronCyclotron, ) from process.models.physics.density_limit import DensityLimitModel +from process.models.physics.exhaust import PlasmaExhaust from process.models.physics.impurity_radiation import read_impurity_file from process.models.physics.l_h_transition import PlasmaConfinementTransitionModel from process.models.physics.physics import ( @@ -9087,6 +9088,64 @@ def plot_sol_power_decay_length_comparison(axis: plt.Axes, mfile: MFile, scan: i axis.set_facecolor("#f0f0f0") +def plot_brunner_divertor_power_plit_comparison(axis: plt.Axes, mfile: MFile, scan: int): + """Plot Brunner divertor power split fractions as a stack plot over dr_sep.""" + # Use the case decay length when available; fall back to 1 mm if absent. + try: + len_plasma_sol_power_decay = mfile.get( + "len_plasma_sol_eich13_power_decay", scan=scan + ) + except KeyError: + len_plasma_sol_power_decay = 1.0e-3 + + dr_sep_values = np.linspace(-5*len_plasma_sol_power_decay, 5*len_plasma_sol_power_decay, 200) + f_p_inboard_lower = np.zeros_like(dr_sep_values) + f_p_inboard_upper = np.zeros_like(dr_sep_values) + f_p_outboard_lower = np.zeros_like(dr_sep_values) + f_p_outboard_upper = np.zeros_like(dr_sep_values) + + for idx, dr_sep in enumerate(dr_sep_values): + ( + f_p_inboard_lower[idx], + f_p_inboard_upper[idx], + f_p_outboard_lower[idx], + f_p_outboard_upper[idx], + ) = PlasmaExhaust().calculate_brunner_divertor_power_splits( + dr_plasma_outboard_midplane_separatrix_separation=dr_sep, + len_plasma_sol_power_decay=len_plasma_sol_power_decay, + ) + + axis.stackplot( + dr_sep_values, + f_p_inboard_lower, + f_p_inboard_upper, + f_p_outboard_lower, + f_p_outboard_upper, + labels=[ + "$f_{P,\\mathrm{in,lower}}$", + "$f_{P,\\mathrm{in,upper}}$", + "$f_{P,\\mathrm{out,lower}}$", + "$f_{P,\\mathrm{out,upper}}$", + ], + alpha=0.9, + ) + + axis.axvline( + mfile.get("dr_plasma_outboard_midplane_separatrix_separation", scan=scan), + color="k", + linestyle="--", + linewidth=1.0, + alpha=0.5, + ) + axis.set_ylim([0.0, 1.0]) + axis.set_xlim([-5*len_plasma_sol_power_decay, 5*len_plasma_sol_power_decay]) + axis.grid(True, which="both", linestyle="--", linewidth=0.5, alpha=0.35) + axis.set_title("Brunner Divertor Power Split Fractions") + axis.set_xlabel("$\\Delta r_{\\mathrm{sep}}$ [m]") + axis.set_ylabel("Power split fraction, $f_P$") + axis.legend(loc="upper left", fontsize=8) + + def plot_h_threshold_comparison(axis: plt.Axes, mfile: MFile, scan: int, u_seed=None): """Function to plot a scatter box plot of L-H threshold power comparisons. @@ -16445,6 +16504,10 @@ def _add_page(name: str | None = None): _add_page("plasma_compare_3").add_subplot(221), m_file, scan ) + plot_brunner_divertor_power_plit_comparison( + _add_page("plasma_exhaust").add_subplot(121), m_file, scan + ) + plot_debye_length_profile( _add_page("microscopic_quantities").add_subplot(232), m_file, scan ) diff --git a/process/models/physics/exhaust.py b/process/models/physics/exhaust.py index b44bafbca1..99ce4c0df1 100644 --- a/process/models/physics/exhaust.py +++ b/process/models/physics/exhaust.py @@ -301,14 +301,6 @@ def calculate_brunner_divertor_power_splits( f_p_inboard_lower = f_p_total_inboard * f_p_inboard_lower f_p_outboard_lower = f_p_total_outboard * f_p_outboard_lower - print( - f"f_p_inboard_lower={f_p_inboard_lower}, f_p_inboard_upper={f_p_inboard_upper}, " - f"f_p_outboard_lower={f_p_outboard_lower}, f_p_outboard_upper={f_p_outboard_upper}" - ) - print( - f"sum={np.sum([f_p_inboard_lower, f_p_inboard_upper, f_p_outboard_lower, f_p_outboard_upper])}" - ) - return ( f_p_inboard_lower, f_p_inboard_upper, From be5d5d33e798fe51e7c0942c2bf63b76b8f340fb Mon Sep 17 00:00:00 2001 From: mn3981 Date: Tue, 11 Aug 2026 10:46:54 +0100 Subject: [PATCH 06/29] Refactor Brunner divertor power splits calculation: introduce DivertorSeparatrixPowerSplits dataclass and update related methods --- process/core/io/plot/summary.py | 20 ++++----- process/models/physics/exhaust.py | 67 +++++++++++++++++++++++-------- process/models/physics/physics.py | 12 +++--- 3 files changed, 68 insertions(+), 31 deletions(-) diff --git a/process/core/io/plot/summary.py b/process/core/io/plot/summary.py index 7c880d71d6..f7e0b31644 100644 --- a/process/core/io/plot/summary.py +++ b/process/core/io/plot/summary.py @@ -9098,22 +9098,24 @@ def plot_brunner_divertor_power_plit_comparison(axis: plt.Axes, mfile: MFile, sc except KeyError: len_plasma_sol_power_decay = 1.0e-3 - dr_sep_values = np.linspace(-5*len_plasma_sol_power_decay, 5*len_plasma_sol_power_decay, 200) + dr_sep_values = np.linspace( + -5 * len_plasma_sol_power_decay, 5 * len_plasma_sol_power_decay, 200 + ) f_p_inboard_lower = np.zeros_like(dr_sep_values) f_p_inboard_upper = np.zeros_like(dr_sep_values) f_p_outboard_lower = np.zeros_like(dr_sep_values) f_p_outboard_upper = np.zeros_like(dr_sep_values) for idx, dr_sep in enumerate(dr_sep_values): - ( - f_p_inboard_lower[idx], - f_p_inboard_upper[idx], - f_p_outboard_lower[idx], - f_p_outboard_upper[idx], - ) = PlasmaExhaust().calculate_brunner_divertor_power_splits( + div_power_splits = PlasmaExhaust().calculate_brunner_divertor_power_splits( dr_plasma_outboard_midplane_separatrix_separation=dr_sep, - len_plasma_sol_power_decay=len_plasma_sol_power_decay, + len_plasma_sol_outboard_power_decay=len_plasma_sol_power_decay, + f_len_sol_power_decay_inboard=0.7, ) + f_p_inboard_lower[idx] = div_power_splits.f_p_div_inboard_lower_separatrix + f_p_inboard_upper[idx] = div_power_splits.f_p_div_inboard_upper_separatrix + f_p_outboard_lower[idx] = div_power_splits.f_p_div_outboard_lower_separatrix + f_p_outboard_upper[idx] = div_power_splits.f_p_div_outboard_upper_separatrix axis.stackplot( dr_sep_values, @@ -9138,7 +9140,7 @@ def plot_brunner_divertor_power_plit_comparison(axis: plt.Axes, mfile: MFile, sc alpha=0.5, ) axis.set_ylim([0.0, 1.0]) - axis.set_xlim([-5*len_plasma_sol_power_decay, 5*len_plasma_sol_power_decay]) + axis.set_xlim([-5 * len_plasma_sol_power_decay, 5 * len_plasma_sol_power_decay]) axis.grid(True, which="both", linestyle="--", linewidth=0.5, alpha=0.35) axis.set_title("Brunner Divertor Power Split Fractions") axis.set_xlabel("$\\Delta r_{\\mathrm{sep}}$ [m]") diff --git a/process/models/physics/exhaust.py b/process/models/physics/exhaust.py index 99ce4c0df1..18a18644b9 100644 --- a/process/models/physics/exhaust.py +++ b/process/models/physics/exhaust.py @@ -1,6 +1,7 @@ """Module for plasma exhaust calculations and analysis.""" import logging +from dataclasses import dataclass import numpy as np @@ -11,6 +12,23 @@ logger = logging.getLogger(__name__) +@dataclass +class DivertorSeparatrixPowerSplits: + """Dataclass to hold the power splits to the divertor targets.""" + + f_p_div_inboard_lower_separatrix: float = 0.0 + """Fraction of total separatrix power to inboard lower divertor target""" + + f_p_div_inboard_upper_separatrix: float = 0.0 + """Fraction of total separatrix power to inboard upper divertor target""" + + f_p_div_outboard_lower_separatrix: float = 0.0 + """Fraction of total separatrix power to outboard lower divertor target""" + + f_p_div_outboard_upper_separatrix: float = 0.0 + """Fraction of total separatrix power to outboard upper divertor target""" + + class PlasmaExhaust(Model): """Class to hold plasma exhaust calculations for plasma processing.""" @@ -224,15 +242,26 @@ def calculate_radiation_fraction( @staticmethod def calculate_brunner_divertor_power_splits( dr_plasma_outboard_midplane_separatrix_separation: float, - len_plasma_sol_power_decay: float, - ): + len_plasma_sol_outboard_power_decay: float, + f_len_sol_power_decay_inboard: float, + ) -> DivertorSeparatrixPowerSplits: """ Calculate the power splits to the divertor targets using Brunner's method. + Parameters + ---------- + dr_plasma_outboard_midplane_separatrix_separation : float + Radial separation of the plasma outboard midplane separatrix (δR_sep) [m]. + len_plasma_sol_outboard_power_decay : float + Power decay length in the scrape-off layer (λ_q) [m]. + f_len_sol_power_decay_inboard : float + Fraction of the scrape-off layer power decay length for the inboard side [-]. + Returns ------- - dict - Dictionary containing the power splits to the divertor targets. + DivertorSeparatrixPowerSplits + Dataclass containing the fractions of total separatrix power to each + divertor target. References ---------- @@ -240,36 +269,40 @@ def calculate_brunner_divertor_power_splits( divertor power sharing on magnetic flux balance in near double-null configurations on Alcator C-Mod,” Nuclear Fusion, vol. 58, no. 7, p. 076010, May 2018, doi: 10.1088/1741-4326/aac006. + + [2] T. W. Petrie et al., “The effect of divertor magnetic balance on H-mode + performance in DIII-D,” Journal of Nuclear Materials, vol. 290-293, pp. 935-939, + Mar. 2001, doi: 10.1016/s0022-3115(00)00492-x. """ # Fraction of the power to the inner divertors at δR_sep = 0 and δR_sep → ∞ F_P_INNER_SEP_0 = 0.16e0 F_P_INNER_SEP_INFINITY = 0.41e0 # Fractions of total outboard power going to each target - + # Outboard lower divertor f_p_outboard_lower = 1 / ( 1 + np.exp( - -dr_plasma_outboard_midplane_separatrix_separation - / len_plasma_sol_power_decay + dr_plasma_outboard_midplane_separatrix_separation + / len_plasma_sol_outboard_power_decay ) ) + # Outboard upper divertor f_p_outboard_upper = 1 / ( 1 + np.exp( - dr_plasma_outboard_midplane_separatrix_separation - / len_plasma_sol_power_decay + -dr_plasma_outboard_midplane_separatrix_separation + / len_plasma_sol_outboard_power_decay ) ) # Fractions of the total inboard power going to each target - f_p_inboard_lower = 1 / ( 1 + np.exp( dr_plasma_outboard_midplane_separatrix_separation - / (len_plasma_sol_power_decay * 0.5) + / (len_plasma_sol_outboard_power_decay * f_len_sol_power_decay_inboard) ) ) @@ -285,7 +318,7 @@ def calculate_brunner_divertor_power_splits( -( ( dr_plasma_outboard_midplane_separatrix_separation - / len_plasma_sol_power_decay + / len_plasma_sol_outboard_power_decay ) ** 2 ) @@ -301,9 +334,9 @@ def calculate_brunner_divertor_power_splits( f_p_inboard_lower = f_p_total_inboard * f_p_inboard_lower f_p_outboard_lower = f_p_total_outboard * f_p_outboard_lower - return ( - f_p_inboard_lower, - f_p_inboard_upper, - f_p_outboard_lower, - f_p_outboard_upper, + return DivertorSeparatrixPowerSplits( + f_p_div_inboard_lower_separatrix=f_p_inboard_lower, + f_p_div_inboard_upper_separatrix=f_p_inboard_upper, + f_p_div_outboard_lower_separatrix=f_p_outboard_lower, + f_p_div_outboard_upper_separatrix=f_p_outboard_upper, ) diff --git a/process/models/physics/physics.py b/process/models/physics/physics.py index 0272a26984..ca9ada32d4 100644 --- a/process/models/physics/physics.py +++ b/process/models/physics/physics.py @@ -993,9 +993,10 @@ def run(self): # ============================================================ - _, _, _, _ = self.exhaust.calculate_brunner_divertor_power_splits( - self.data.physics.dr_plasma_outboard_midplane_separatrix_separation, - len_plasma_sol_power_decay=0.001, + div_power_plits = self.exhaust.calculate_brunner_divertor_power_splits( + dr_plasma_outboard_midplane_separatrix_separation=self.data.physics.dr_plasma_outboard_midplane_separatrix_separation, + len_plasma_sol_outboard_power_decay=0.001, + f_len_sol_power_decay_inboard=0.7, ) # Calculate the target imbalances @@ -1007,8 +1008,9 @@ def run(self): # Parameters taken from double null machine # D. Brunner et al - # Issue #1559 Infinities in physics_module.dr_plasma_outboard_midplane_separatrix_separation when running single null in a - # double null machine + # Issue #1559 Infinities in + # physics_module.dr_plasma_outboard_midplane_separatrix_separation when running + # single null in a double null machine # C W Ashe if self.data.physics.f_p_div_lower < 4.5e-5: self.data.physics.dr_plasma_outboard_midplane_separatrix_separation = 1.5e-2 From bf4ea727e349e6b44b70ccabe028f8c9cd85435d Mon Sep 17 00:00:00 2001 From: mn3981 Date: Tue, 11 Aug 2026 14:47:19 +0100 Subject: [PATCH 07/29] Rename 'fuo' to 'f_p_div_upper_outboard_separatrix' and update calculations in Physics class --- process/data_structure/physics_variables.py | 3 ++- process/models/physics/physics.py | 13 ++++++++----- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/process/data_structure/physics_variables.py b/process/data_structure/physics_variables.py index dd025ef4d4..399d8aeed7 100644 --- a/process/data_structure/physics_variables.py +++ b/process/data_structure/physics_variables.py @@ -449,7 +449,8 @@ class PhysicsData: f_p_div_upper_inboard_separatrix: float = 0.0 """Fraction of plasma separatrix power to the upper inboard divertor target""" - fuo: float = 0.0 + f_p_div_upper_outboard_separatrix: float = 0.0 + """Fraction of plasma separatrix power to the upper outboard divertor target""" plimw: float = 0.0 diff --git a/process/models/physics/physics.py b/process/models/physics/physics.py index ca9ada32d4..96968de56f 100644 --- a/process/models/physics/physics.py +++ b/process/models/physics/physics.py @@ -1058,9 +1058,9 @@ def run(self): self.data.physics.f_p_div_upper_inboard_separatrix = ( 1.0e0 - self.data.physics.f_p_div_lower ) * self.data.physics.fio - self.data.physics.fuo = (1.0e0 - self.data.physics.f_p_div_lower) * ( - 1.0e0 - self.data.physics.fio - ) + self.data.physics.f_p_div_upper_outboard_separatrix = ( + 1.0e0 - self.data.physics.f_p_div_lower + ) * (1.0e0 - self.data.physics.fio) # power into each target self.data.physics.plimw = ( self.data.physics.f_p_div_lower_inboard_separatrix @@ -1074,7 +1074,10 @@ def run(self): self.data.physics.f_p_div_upper_inboard_separatrix * self.data.physics.ptarmw ) - self.data.physics.puomw = self.data.physics.fuo * self.data.physics.ptarmw + self.data.physics.puomw = ( + self.data.physics.f_p_div_upper_outboard_separatrix + * self.data.physics.ptarmw + ) else: # Single null configuration self.data.physics.f_p_div_lower_inboard_separatrix = self.data.physics.fio @@ -2289,7 +2292,7 @@ def outplas(self): self.outfile, "Fraction of power incident on the upper outer target", "(fUO)", - self.data.physics.fuo, + self.data.physics.f_p_div_upper_outboard_separatrix, "OP ", ) From 33a9c5edb2804578cac57591d5d7bc4fea689d91 Mon Sep 17 00:00:00 2001 From: mn3981 Date: Tue, 11 Aug 2026 14:49:53 +0100 Subject: [PATCH 08/29] Rename 'plimw' to 'p_div_lower_inboard_separatrix_mw' and update related calculations in Physics class --- process/data_structure/physics_variables.py | 3 ++- process/models/physics/physics.py | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/process/data_structure/physics_variables.py b/process/data_structure/physics_variables.py index 399d8aeed7..5ae066af33 100644 --- a/process/data_structure/physics_variables.py +++ b/process/data_structure/physics_variables.py @@ -452,7 +452,8 @@ class PhysicsData: f_p_div_upper_outboard_separatrix: float = 0.0 """Fraction of plasma separatrix power to the upper outboard divertor target""" - plimw: float = 0.0 + p_div_lower_inboard_separatrix_mw: float = 0.0 + """Plasma separatrix power directed to the lower inboard divertor target [MW]""" plomw: float = 0.0 diff --git a/process/models/physics/physics.py b/process/models/physics/physics.py index 96968de56f..c7c12e2fb0 100644 --- a/process/models/physics/physics.py +++ b/process/models/physics/physics.py @@ -1062,7 +1062,7 @@ def run(self): 1.0e0 - self.data.physics.f_p_div_lower ) * (1.0e0 - self.data.physics.fio) # power into each target - self.data.physics.plimw = ( + self.data.physics.p_div_lower_inboard_separatrix_mw = ( self.data.physics.f_p_div_lower_inboard_separatrix * self.data.physics.ptarmw ) @@ -1085,7 +1085,7 @@ def run(self): 1.0e0 - self.data.physics.fio ) # power into each target - self.data.physics.plimw = ( + self.data.physics.p_div_lower_inboard_separatrix_mw = ( self.data.physics.f_p_div_lower_inboard_separatrix * self.data.physics.ptarmw ) @@ -2300,7 +2300,7 @@ def outplas(self): self.outfile, "Power incident on the lower inner target (MW)", "(pLImw)", - self.data.physics.plimw, + self.data.physics.p_div_lower_inboard_separatrix_mw, "OP ", ) po.ovarre( From 751567fdeaa2530e38d69c09319956fa54b7990d Mon Sep 17 00:00:00 2001 From: mn3981 Date: Tue, 11 Aug 2026 14:53:52 +0100 Subject: [PATCH 09/29] Rename 'plomw' to 'p_div_lower_outboard_separatrix_mw' and update related calculations in Physics class --- process/data_structure/physics_variables.py | 3 ++- process/models/physics/physics.py | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/process/data_structure/physics_variables.py b/process/data_structure/physics_variables.py index 5ae066af33..0a0afb9307 100644 --- a/process/data_structure/physics_variables.py +++ b/process/data_structure/physics_variables.py @@ -455,7 +455,8 @@ class PhysicsData: p_div_lower_inboard_separatrix_mw: float = 0.0 """Plasma separatrix power directed to the lower inboard divertor target [MW]""" - plomw: float = 0.0 + p_div_lower_outboard_separatrix_mw: float = 0.0 + """Plasma separatrix power directed to the lower outboard divertor target [MW]""" puimw: float = 0.0 diff --git a/process/models/physics/physics.py b/process/models/physics/physics.py index c7c12e2fb0..dc38597ca2 100644 --- a/process/models/physics/physics.py +++ b/process/models/physics/physics.py @@ -1066,7 +1066,7 @@ def run(self): self.data.physics.f_p_div_lower_inboard_separatrix * self.data.physics.ptarmw ) - self.data.physics.plomw = ( + self.data.physics.p_div_lower_outboard_separatrix_mw = ( self.data.physics.f_p_div_lower_outboard_separatrix * self.data.physics.ptarmw ) @@ -1089,7 +1089,7 @@ def run(self): self.data.physics.f_p_div_lower_inboard_separatrix * self.data.physics.ptarmw ) - self.data.physics.plomw = ( + self.data.physics.p_div_lower_outboard_separatrix_mw = ( self.data.physics.f_p_div_lower_outboard_separatrix * self.data.physics.ptarmw ) @@ -2307,7 +2307,7 @@ def outplas(self): self.outfile, "Power incident on the lower outer target (MW)", "(pLOmw)", - self.data.physics.plomw, + self.data.physics.p_div_lower_outboard_separatrix_mw, "OP ", ) if self.data.divertor.n_divertors == 2: From 0a8c94585daed815fbecc6c0880b3caff1f38a4a Mon Sep 17 00:00:00 2001 From: mn3981 Date: Tue, 11 Aug 2026 14:54:32 +0100 Subject: [PATCH 10/29] Rename 'puimw' to 'p_div_upper_inboard_separatrix_mw' and update related calculations in Physics class --- process/data_structure/physics_variables.py | 3 ++- process/models/physics/physics.py | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/process/data_structure/physics_variables.py b/process/data_structure/physics_variables.py index 0a0afb9307..90d7e38264 100644 --- a/process/data_structure/physics_variables.py +++ b/process/data_structure/physics_variables.py @@ -458,7 +458,8 @@ class PhysicsData: p_div_lower_outboard_separatrix_mw: float = 0.0 """Plasma separatrix power directed to the lower outboard divertor target [MW]""" - puimw: float = 0.0 + p_div_upper_inboard_separatrix_mw: float = 0.0 + """Plasma separatrix power directed to the upper inboard divertor target [MW]""" puomw: float = 0.0 diff --git a/process/models/physics/physics.py b/process/models/physics/physics.py index dc38597ca2..ed7cad7b0d 100644 --- a/process/models/physics/physics.py +++ b/process/models/physics/physics.py @@ -1070,7 +1070,7 @@ def run(self): self.data.physics.f_p_div_lower_outboard_separatrix * self.data.physics.ptarmw ) - self.data.physics.puimw = ( + self.data.physics.p_div_upper_inboard_separatrix_mw = ( self.data.physics.f_p_div_upper_inboard_separatrix * self.data.physics.ptarmw ) @@ -2315,7 +2315,7 @@ def outplas(self): self.outfile, "Power incident on the upper innner target (MW)", "(pUImw)", - self.data.physics.puimw, + self.data.physics.p_div_upper_inboard_separatrix_mw, "OP ", ) po.ovarre( From 431e5141ecc48bdae8d10ed61b48879b64d8781b Mon Sep 17 00:00:00 2001 From: mn3981 Date: Tue, 11 Aug 2026 14:55:30 +0100 Subject: [PATCH 11/29] Rename 'puomw' to 'p_div_upper_outboard_separatrix_mw' and update related calculations in Physics class --- process/data_structure/physics_variables.py | 3 ++- process/models/physics/physics.py | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/process/data_structure/physics_variables.py b/process/data_structure/physics_variables.py index 90d7e38264..473c02d072 100644 --- a/process/data_structure/physics_variables.py +++ b/process/data_structure/physics_variables.py @@ -461,7 +461,8 @@ class PhysicsData: p_div_upper_inboard_separatrix_mw: float = 0.0 """Plasma separatrix power directed to the upper inboard divertor target [MW]""" - puomw: float = 0.0 + p_div_upper_outboard_separatrix_mw: float = 0.0 + """Plasma separatrix power directed to the upper outboard divertor target [MW]""" rho_star: float = 0.0 diff --git a/process/models/physics/physics.py b/process/models/physics/physics.py index ed7cad7b0d..f1fff39a20 100644 --- a/process/models/physics/physics.py +++ b/process/models/physics/physics.py @@ -1074,7 +1074,7 @@ def run(self): self.data.physics.f_p_div_upper_inboard_separatrix * self.data.physics.ptarmw ) - self.data.physics.puomw = ( + self.data.physics.p_div_upper_outboard_separatrix_mw = ( self.data.physics.f_p_div_upper_outboard_separatrix * self.data.physics.ptarmw ) @@ -2322,7 +2322,7 @@ def outplas(self): self.outfile, "Power incident on the upper outer target (MW)", "(pUOmw)", - self.data.physics.puomw, + self.data.physics.p_div_upper_outboard_separatrix_mw, "OP ", ) From a1fd85585d20a865bb4593af2dd8097b50c638b3 Mon Sep 17 00:00:00 2001 From: mn3981 Date: Tue, 11 Aug 2026 15:03:40 +0100 Subject: [PATCH 12/29] Refactor output methods for Brunner divertor power splits and update output formatting in PlasmaExhaust class --- process/models/physics/exhaust.py | 119 ++++++++++++++++++++++++++++-- process/models/physics/physics.py | 106 -------------------------- 2 files changed, 114 insertions(+), 111 deletions(-) diff --git a/process/models/physics/exhaust.py b/process/models/physics/exhaust.py index 18a18644b9..f73f6ad1d7 100644 --- a/process/models/physics/exhaust.py +++ b/process/models/physics/exhaust.py @@ -71,7 +71,7 @@ def output(self): # Double null divertor configuration po.ovarre( self.outfile, - "Plasma separatrix power over major radius (Pₛₑₚ / R₀) (MW/m) " + "Plasma separatrix power over major radius (Pₛₑₚ / R₀) [MW/m] " "(On peak divertor)", "(p_plasma_separatrix_rmajor_mw)", self.data.physics.p_plasma_separatrix_rmajor_mw, @@ -80,7 +80,7 @@ def output(self): po.ovarre( self.outfile, "EU-DEMO divertor protection re-attachment metric (PₛₑₚBₜ / q₉₅AR₀) " - "(MWT/m) (On peak divertor)", + "[MWT/m] (On peak divertor)", "(p_div_bt_q_aspect_rmajor_mw)", self.data.physics.p_div_bt_q_aspect_rmajor_mw, "OP ", @@ -89,7 +89,7 @@ def output(self): # Single null divertor configuration po.ovarre( self.outfile, - "Plasma separatrix power over major radius (Pₛₑₚ / R₀) (MW/m)", + "Plasma separatrix power over major radius (Pₛₑₚ / R₀) [MW/m]", "(p_plasma_separatrix_rmajor_mw)", self.data.physics.p_plasma_separatrix_rmajor_mw, "OP ", @@ -97,13 +97,14 @@ def output(self): po.ovarre( self.outfile, "EU-DEMO divertor protection re-attachment metric (PₛₑₚBₜ / q₉₅AR₀) " - "(MWT/m)", + "[MWT/m]", "(p_div_bt_q_aspect_rmajor_mw)", self.data.physics.p_div_bt_q_aspect_rmajor_mw, "OP ", ) - po.oblnkl(self.outfile) + po.ocmmnt(self.outfile, "----------------------------") + self.output_brunner_divertor_power_splits() @staticmethod def calculate_separatrix_power( @@ -340,3 +341,111 @@ def calculate_brunner_divertor_power_splits( f_p_div_outboard_lower_separatrix=f_p_outboard_lower, f_p_div_outboard_upper_separatrix=f_p_outboard_upper, ) + + def output_brunner_divertor_power_splits(self): + """Output the Brunner divertor power splits to the output file.""" + if self.data.stellarator.istell == 0: + po.osubhd(self.outfile, "Brunner Divertor Power Splits:") + po.ovarre( + self.outfile, + "Power incident on the divertor targets (MW)", + "(ptarmw)", + self.data.physics.ptarmw, + "OP ", + ) + po.ovarre( + self.outfile, + "Divertor poloidal angle subtended by plasma (degrees)", + "(deg_div_poloidal_plasma)", + self.data.divertor.deg_div_poloidal_plasma, + "OP ", + ) + po.ovarre( + self.outfile, + "Fraction of power to the lower divertor", + "(f_p_div_lower)", + self.data.physics.f_p_div_lower, + "IP ", + ) + po.ovarre( + self.outfile, + "Outboard side heat flux decay length (m)", + "(len_sol_outboard_power_decay)", + self.data.physics.len_sol_outboard_power_decay, + "OP ", + ) + if self.data.divertor.n_divertors == 2: + po.ovarre( + self.outfile, + "Radial distance between the first and second plasma separatrixes at the outer midplane (δR_sep) [m]", + "(dr_plasma_outboard_midplane_separatrix_separation)", + self.data.physics.dr_plasma_outboard_midplane_separatrix_separation, + "OP ", + ) + + po.ovarre( + self.outfile, + "Fraction of power on the inner targets", + "(fio)", + self.data.physics.fio, + "OP ", + ) + po.ovarre( + self.outfile, + "Fraction of power incident on the lower inner target", + "(fLI)", + self.data.physics.f_p_div_lower_inboard_separatrix, + "OP ", + ) + po.ovarre( + self.outfile, + "Fraction of power incident on the lower outer target", + "(fLO)", + self.data.physics.f_p_div_lower_outboard_separatrix, + "OP ", + ) + if self.data.divertor.n_divertors == 2: + po.ovarre( + self.outfile, + "Fraction of power incident on the upper inner target", + "(fUI)", + self.data.physics.f_p_div_upper_inboard_separatrix, + "OP ", + ) + po.ovarre( + self.outfile, + "Fraction of power incident on the upper outer target", + "(fUO)", + self.data.physics.f_p_div_upper_outboard_separatrix, + "OP ", + ) + + po.ovarre( + self.outfile, + "Power incident on the lower inner target (MW)", + "(pLImw)", + self.data.physics.p_div_lower_inboard_separatrix_mw, + "OP ", + ) + po.ovarre( + self.outfile, + "Power incident on the lower outer target (MW)", + "(pLOmw)", + self.data.physics.p_div_lower_outboard_separatrix_mw, + "OP ", + ) + if self.data.divertor.n_divertors == 2: + po.ovarre( + self.outfile, + "Power incident on the upper innner target (MW)", + "(pUImw)", + self.data.physics.p_div_upper_inboard_separatrix_mw, + "OP ", + ) + po.ovarre( + self.outfile, + "Power incident on the upper outer target (MW)", + "(pUOmw)", + self.data.physics.p_div_upper_outboard_separatrix_mw, + "OP ", + ) \ No newline at end of file diff --git a/process/models/physics/physics.py b/process/models/physics/physics.py index f1fff39a20..24a0dcf812 100644 --- a/process/models/physics/physics.py +++ b/process/models/physics/physics.py @@ -2220,112 +2220,6 @@ def outplas(self): "OP ", ) - if self.data.stellarator.istell == 0: - po.oblnkl(self.outfile) - po.ovarre( - self.outfile, - "Power incident on the divertor targets (MW)", - "(ptarmw)", - self.data.physics.ptarmw, - "OP ", - ) - po.ovarre( - self.outfile, - "Divertor poloidal angle subtended by plasma (degrees)", - "(deg_div_poloidal_plasma)", - self.data.divertor.deg_div_poloidal_plasma, - "OP ", - ) - po.ovarre( - self.outfile, - "Fraction of power to the lower divertor", - "(f_p_div_lower)", - self.data.physics.f_p_div_lower, - "IP ", - ) - po.ovarre( - self.outfile, - "Outboard side heat flux decay length (m)", - "(len_sol_outboard_power_decay)", - self.data.physics.len_sol_outboard_power_decay, - "OP ", - ) - if self.data.divertor.n_divertors == 2: - po.ovarre( - self.outfile, - "Radial distance between the first and second plasma separatrixes at the outer midplane (δR_sep) [m]", - "(dr_plasma_outboard_midplane_separatrix_separation)", - self.data.physics.dr_plasma_outboard_midplane_separatrix_separation, - "OP ", - ) - - po.ovarre( - self.outfile, - "Fraction of power on the inner targets", - "(fio)", - self.data.physics.fio, - "OP ", - ) - po.ovarre( - self.outfile, - "Fraction of power incident on the lower inner target", - "(fLI)", - self.data.physics.f_p_div_lower_inboard_separatrix, - "OP ", - ) - po.ovarre( - self.outfile, - "Fraction of power incident on the lower outer target", - "(fLO)", - self.data.physics.f_p_div_lower_outboard_separatrix, - "OP ", - ) - if self.data.divertor.n_divertors == 2: - po.ovarre( - self.outfile, - "Fraction of power incident on the upper inner target", - "(fUI)", - self.data.physics.f_p_div_upper_inboard_separatrix, - "OP ", - ) - po.ovarre( - self.outfile, - "Fraction of power incident on the upper outer target", - "(fUO)", - self.data.physics.f_p_div_upper_outboard_separatrix, - "OP ", - ) - - po.ovarre( - self.outfile, - "Power incident on the lower inner target (MW)", - "(pLImw)", - self.data.physics.p_div_lower_inboard_separatrix_mw, - "OP ", - ) - po.ovarre( - self.outfile, - "Power incident on the lower outer target (MW)", - "(pLOmw)", - self.data.physics.p_div_lower_outboard_separatrix_mw, - "OP ", - ) - if self.data.divertor.n_divertors == 2: - po.ovarre( - self.outfile, - "Power incident on the upper innner target (MW)", - "(pUImw)", - self.data.physics.p_div_upper_inboard_separatrix_mw, - "OP ", - ) - po.ovarre( - self.outfile, - "Power incident on the upper outer target (MW)", - "(pUOmw)", - self.data.physics.p_div_upper_outboard_separatrix_mw, - "OP ", - ) - po.oblnkl(self.outfile) po.ovarre( self.outfile, From 927556014da4900faab16402a2ed1b1dadeede31 Mon Sep 17 00:00:00 2001 From: mn3981 Date: Tue, 11 Aug 2026 15:11:04 +0100 Subject: [PATCH 13/29] Rename 'fio' to 'f_p_div_inboard_separatrix' and update related calculations in Physics class --- process/data_structure/physics_variables.py | 4 +- process/models/physics/exhaust.py | 6 +-- process/models/physics/physics.py | 47 ++++++++++++--------- 3 files changed, 32 insertions(+), 25 deletions(-) diff --git a/process/data_structure/physics_variables.py b/process/data_structure/physics_variables.py index 473c02d072..a1221b391d 100644 --- a/process/data_structure/physics_variables.py +++ b/process/data_structure/physics_variables.py @@ -438,7 +438,9 @@ class PhysicsData: dr_plasma_outboard_midplane_separatrix_separation: float = 0.0 """Radial distance between the first and second plasma separatrixes at the outer midplane (δR_sep) [m]""" - fio: float = 0.0 + f_p_div_inboard_separatrix: float = 0.0 + """Fraction of the total plasma separatrix power going to the inboard divertor + target(s)""" f_p_div_lower_inboard_separatrix: float = 0.0 """Fraction of plasma separatrix power to the lower inboard divertor target""" diff --git a/process/models/physics/exhaust.py b/process/models/physics/exhaust.py index f73f6ad1d7..5f788484bd 100644 --- a/process/models/physics/exhaust.py +++ b/process/models/physics/exhaust.py @@ -386,8 +386,8 @@ def output_brunner_divertor_power_splits(self): po.ovarre( self.outfile, "Fraction of power on the inner targets", - "(fio)", - self.data.physics.fio, + "(f_p_div_inboard_separatrix)", + self.data.physics.f_p_div_inboard_separatrix, "OP ", ) po.ovarre( @@ -448,4 +448,4 @@ def output_brunner_divertor_power_splits(self): "(pUOmw)", self.data.physics.p_div_upper_outboard_separatrix_mw, "OP ", - ) \ No newline at end of file + ) diff --git a/process/models/physics/physics.py b/process/models/physics/physics.py index 24a0dcf812..b84156a6a0 100644 --- a/process/models/physics/physics.py +++ b/process/models/physics/physics.py @@ -993,6 +993,21 @@ def run(self): # ============================================================ + # Issue #1559 Infinities in + # physics_module.dr_plasma_outboard_midplane_separatrix_separation when running + # single null in a double null machine + # C W Ashe + if self.data.physics.f_p_div_lower < 4.5e-5: + self.data.physics.dr_plasma_outboard_midplane_separatrix_separation = 1.5e-2 + elif self.data.physics.f_p_div_lower > (1.0e0 - 4.5e-5): + self.data.physics.dr_plasma_outboard_midplane_separatrix_separation = -1.5e-2 + else: + self.data.physics.dr_plasma_outboard_midplane_separatrix_separation = ( + -2.0e0 + * 1.5e-3 + * math.atanh(2.0e0 * (self.data.physics.f_p_div_lower - 0.5e0)) + ) + div_power_plits = self.exhaust.calculate_brunner_divertor_power_splits( dr_plasma_outboard_midplane_separatrix_separation=self.data.physics.dr_plasma_outboard_midplane_separatrix_separation, len_plasma_sol_outboard_power_decay=0.001, @@ -1008,26 +1023,12 @@ def run(self): # Parameters taken from double null machine # D. Brunner et al - # Issue #1559 Infinities in - # physics_module.dr_plasma_outboard_midplane_separatrix_separation when running - # single null in a double null machine - # C W Ashe - if self.data.physics.f_p_div_lower < 4.5e-5: - self.data.physics.dr_plasma_outboard_midplane_separatrix_separation = 1.5e-2 - elif self.data.physics.f_p_div_lower > (1.0e0 - 4.5e-5): - self.data.physics.dr_plasma_outboard_midplane_separatrix_separation = -1.5e-2 - else: - self.data.physics.dr_plasma_outboard_midplane_separatrix_separation = ( - -2.0e0 - * 1.5e-3 - * math.atanh(2.0e0 * (self.data.physics.f_p_div_lower - 0.5e0)) - ) # Model Taken from D3-D paper for conventional divertor # Journal of Nuclear Materials # Volumes 290-293, March 2001, Pages 935-939 # Find the innner and outer lower target imbalance - self.data.physics.fio = 0.16e0 + (0.16e0 - 0.41e0) * ( + self.data.physics.f_p_div_inboard_separatrix = 0.16e0 + (0.16e0 - 0.41e0) * ( 1.0e0 - ( 2.0e0 @@ -1050,17 +1051,19 @@ def run(self): # Find all the power fractions accross the targets # Taken from D3-D conventional divertor design self.data.physics.f_p_div_lower_inboard_separatrix = ( - self.data.physics.f_p_div_lower * self.data.physics.fio + self.data.physics.f_p_div_lower + * self.data.physics.f_p_div_inboard_separatrix ) self.data.physics.f_p_div_lower_outboard_separatrix = ( - self.data.physics.f_p_div_lower * (1.0e0 - self.data.physics.fio) + self.data.physics.f_p_div_lower + * (1.0e0 - self.data.physics.f_p_div_inboard_separatrix) ) self.data.physics.f_p_div_upper_inboard_separatrix = ( 1.0e0 - self.data.physics.f_p_div_lower - ) * self.data.physics.fio + ) * self.data.physics.f_p_div_inboard_separatrix self.data.physics.f_p_div_upper_outboard_separatrix = ( 1.0e0 - self.data.physics.f_p_div_lower - ) * (1.0e0 - self.data.physics.fio) + ) * (1.0e0 - self.data.physics.f_p_div_inboard_separatrix) # power into each target self.data.physics.p_div_lower_inboard_separatrix_mw = ( self.data.physics.f_p_div_lower_inboard_separatrix @@ -1080,9 +1083,11 @@ def run(self): ) else: # Single null configuration - self.data.physics.f_p_div_lower_inboard_separatrix = self.data.physics.fio + self.data.physics.f_p_div_lower_inboard_separatrix = ( + self.data.physics.f_p_div_inboard_separatrix + ) self.data.physics.f_p_div_lower_outboard_separatrix = ( - 1.0e0 - self.data.physics.fio + 1.0e0 - self.data.physics.f_p_div_inboard_separatrix ) # power into each target self.data.physics.p_div_lower_inboard_separatrix_mw = ( From 0c76cdb6392e62d8f062cf9efd8f8f42dbb8294c Mon Sep 17 00:00:00 2001 From: mn3981 Date: Tue, 11 Aug 2026 15:12:41 +0100 Subject: [PATCH 14/29] Add outboard divertor power fraction variable to PhysicsData class --- process/data_structure/physics_variables.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/process/data_structure/physics_variables.py b/process/data_structure/physics_variables.py index a1221b391d..32141d078d 100644 --- a/process/data_structure/physics_variables.py +++ b/process/data_structure/physics_variables.py @@ -441,6 +441,10 @@ class PhysicsData: f_p_div_inboard_separatrix: float = 0.0 """Fraction of the total plasma separatrix power going to the inboard divertor target(s)""" + + f_p_div_outboard_separatrix: float = 0.0 + """Fraction of the total plasma separatrix power going to the outboard divertor + target(s)""" f_p_div_lower_inboard_separatrix: float = 0.0 """Fraction of plasma separatrix power to the lower inboard divertor target""" From f74cc2df2e636d9390e7bcd3ea0b104133d44928 Mon Sep 17 00:00:00 2001 From: mn3981 Date: Tue, 11 Aug 2026 15:13:39 +0100 Subject: [PATCH 15/29] Add inboard and outboard divertor power fraction attributes to DivertorSeparatrixPowerSplits --- process/models/physics/exhaust.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/process/models/physics/exhaust.py b/process/models/physics/exhaust.py index 5f788484bd..969dc5e22c 100644 --- a/process/models/physics/exhaust.py +++ b/process/models/physics/exhaust.py @@ -16,6 +16,12 @@ class DivertorSeparatrixPowerSplits: """Dataclass to hold the power splits to the divertor targets.""" + f_p_div_inboard_separatrix: float = 0.0 + """Fraction of total separatrix power to inboard divertor targets""" + + f_p_div_outboard_separatrix: float = 0.0 + """Fraction of total separatrix power to outboard divertor targets""" + f_p_div_inboard_lower_separatrix: float = 0.0 """Fraction of total separatrix power to inboard lower divertor target""" @@ -336,6 +342,8 @@ def calculate_brunner_divertor_power_splits( f_p_outboard_lower = f_p_total_outboard * f_p_outboard_lower return DivertorSeparatrixPowerSplits( + f_p_div_inboard_separatrix=f_p_total_inboard, + f_p_div_outboard_separatrix=f_p_total_outboard, f_p_div_inboard_lower_separatrix=f_p_inboard_lower, f_p_div_inboard_upper_separatrix=f_p_inboard_upper, f_p_div_outboard_lower_separatrix=f_p_outboard_lower, From a0e30bad4b03d0808a78e7630aeeef7e54e7d6cb Mon Sep 17 00:00:00 2001 From: mn3981 Date: Tue, 11 Aug 2026 15:26:51 +0100 Subject: [PATCH 16/29] Update divertor power fraction attributes and calculations in PhysicsData and exhaust modules --- process/data_structure/physics_variables.py | 2 +- process/models/physics/exhaust.py | 4 +- process/models/physics/physics.py | 41 ++++++++++++++++++++- 3 files changed, 43 insertions(+), 4 deletions(-) diff --git a/process/data_structure/physics_variables.py b/process/data_structure/physics_variables.py index 32141d078d..68f451d8e3 100644 --- a/process/data_structure/physics_variables.py +++ b/process/data_structure/physics_variables.py @@ -441,7 +441,7 @@ class PhysicsData: f_p_div_inboard_separatrix: float = 0.0 """Fraction of the total plasma separatrix power going to the inboard divertor target(s)""" - + f_p_div_outboard_separatrix: float = 0.0 """Fraction of the total plasma separatrix power going to the outboard divertor target(s)""" diff --git a/process/models/physics/exhaust.py b/process/models/physics/exhaust.py index 969dc5e22c..7628d0df0d 100644 --- a/process/models/physics/exhaust.py +++ b/process/models/physics/exhaust.py @@ -18,10 +18,10 @@ class DivertorSeparatrixPowerSplits: f_p_div_inboard_separatrix: float = 0.0 """Fraction of total separatrix power to inboard divertor targets""" - + f_p_div_outboard_separatrix: float = 0.0 """Fraction of total separatrix power to outboard divertor targets""" - + f_p_div_inboard_lower_separatrix: float = 0.0 """Fraction of total separatrix power to inboard lower divertor target""" diff --git a/process/models/physics/physics.py b/process/models/physics/physics.py index b84156a6a0..f67d76ae27 100644 --- a/process/models/physics/physics.py +++ b/process/models/physics/physics.py @@ -1010,10 +1010,49 @@ def run(self): div_power_plits = self.exhaust.calculate_brunner_divertor_power_splits( dr_plasma_outboard_midplane_separatrix_separation=self.data.physics.dr_plasma_outboard_midplane_separatrix_separation, - len_plasma_sol_outboard_power_decay=0.001, + len_plasma_sol_outboard_power_decay=self.data.physics.len_sol_outboard_power_decay, f_len_sol_power_decay_inboard=0.7, ) + self.data.physics.f_p_div_inboard_separatrix = ( + div_power_plits.f_p_div_inboard_separatrix + ) + self.data.physics.f_p_div_outboard_separatrix = ( + div_power_plits.f_p_div_outboard_separatrix + ) + self.data.physics.f_p_div_lower_inboard_separatrix = ( + div_power_plits.f_p_div_inboard_lower_separatrix + ) + self.data.physics.f_p_div_lower_outboard_separatrix = ( + div_power_plits.f_p_div_outboard_lower_separatrix + ) + self.data.physics.f_p_div_upper_inboard_separatrix = ( + div_power_plits.f_p_div_inboard_upper_separatrix + ) + self.data.physics.f_p_div_upper_outboard_separatrix = ( + div_power_plits.f_p_div_outboard_upper_separatrix + ) + + self.data.physics.p_div_lower_inboard_separatrix_mw = ( + self.data.physics.f_p_div_lower_inboard_separatrix + * self.data.physics.p_plasma_separatrix_mw + ) + + self.data.physics.p_div_lower_outboard_separatrix_mw = ( + self.data.physics.f_p_div_lower_outboard_separatrix + * self.data.physics.p_plasma_separatrix_mw + ) + + self.data.physics.p_div_upper_inboard_separatrix_mw = ( + self.data.physics.f_p_div_upper_inboard_separatrix + * self.data.physics.p_plasma_separatrix_mw + ) + + self.data.physics.p_div_upper_outboard_separatrix_mw = ( + self.data.physics.f_p_div_upper_outboard_separatrix + * self.data.physics.p_plasma_separatrix_mw + ) + # Calculate the target imbalances # find the total power into the targets self.data.physics.ptarmw = self.data.physics.p_plasma_separatrix_mw * ( From ff72cccfa7159c72cc13eb96993ee38d08b28cdc Mon Sep 17 00:00:00 2001 From: mn3981 Date: Tue, 11 Aug 2026 15:29:07 +0100 Subject: [PATCH 17/29] Remove outdated calculations for target imbalances and power fractions in Physics class --- process/data_structure/physics_variables.py | 2 - process/models/physics/exhaust.py | 16 +--- process/models/physics/physics.py | 84 --------------------- 3 files changed, 2 insertions(+), 100 deletions(-) diff --git a/process/data_structure/physics_variables.py b/process/data_structure/physics_variables.py index 68f451d8e3..a7ef59497b 100644 --- a/process/data_structure/physics_variables.py +++ b/process/data_structure/physics_variables.py @@ -430,8 +430,6 @@ class PhysicsData: t_energy_confinement_beta: float = 0.0 """[s]""" - ptarmw: float = 0.0 - len_sol_outboard_power_decay: float = 0.0 """Outboard scrape off layer power decay length (λ_q) [m]""" diff --git a/process/models/physics/exhaust.py b/process/models/physics/exhaust.py index 7628d0df0d..bb5872b387 100644 --- a/process/models/physics/exhaust.py +++ b/process/models/physics/exhaust.py @@ -354,20 +354,8 @@ def output_brunner_divertor_power_splits(self): """Output the Brunner divertor power splits to the output file.""" if self.data.stellarator.istell == 0: po.osubhd(self.outfile, "Brunner Divertor Power Splits:") - po.ovarre( - self.outfile, - "Power incident on the divertor targets (MW)", - "(ptarmw)", - self.data.physics.ptarmw, - "OP ", - ) - po.ovarre( - self.outfile, - "Divertor poloidal angle subtended by plasma (degrees)", - "(deg_div_poloidal_plasma)", - self.data.divertor.deg_div_poloidal_plasma, - "OP ", - ) + + po.ovarre( self.outfile, "Fraction of power to the lower divertor", diff --git a/process/models/physics/physics.py b/process/models/physics/physics.py index f67d76ae27..e665808cf3 100644 --- a/process/models/physics/physics.py +++ b/process/models/physics/physics.py @@ -1053,90 +1053,6 @@ def run(self): * self.data.physics.p_plasma_separatrix_mw ) - # Calculate the target imbalances - # find the total power into the targets - self.data.physics.ptarmw = self.data.physics.p_plasma_separatrix_mw * ( - 1.0e0 - self.data.physics.rad_fraction_sol - ) - # use self.data.physics.f_p_div_lower to find deltarsep - # Parameters taken from double null machine - # D. Brunner et al - - # Model Taken from D3-D paper for conventional divertor - # Journal of Nuclear Materials - # Volumes 290-293, March 2001, Pages 935-939 - # Find the innner and outer lower target imbalance - - self.data.physics.f_p_div_inboard_separatrix = 0.16e0 + (0.16e0 - 0.41e0) * ( - 1.0e0 - - ( - 2.0e0 - / ( - 1.0e0 - + np.exp( - -( - ( - self.data.physics.dr_plasma_outboard_midplane_separatrix_separation - / self.data.physics.len_sol_outboard_power_decay - ) - ** 2 - ) - ) - ) - ) - ) - if self.data.divertor.n_divertors == 2: - # Double Null configuration - # Find all the power fractions accross the targets - # Taken from D3-D conventional divertor design - self.data.physics.f_p_div_lower_inboard_separatrix = ( - self.data.physics.f_p_div_lower - * self.data.physics.f_p_div_inboard_separatrix - ) - self.data.physics.f_p_div_lower_outboard_separatrix = ( - self.data.physics.f_p_div_lower - * (1.0e0 - self.data.physics.f_p_div_inboard_separatrix) - ) - self.data.physics.f_p_div_upper_inboard_separatrix = ( - 1.0e0 - self.data.physics.f_p_div_lower - ) * self.data.physics.f_p_div_inboard_separatrix - self.data.physics.f_p_div_upper_outboard_separatrix = ( - 1.0e0 - self.data.physics.f_p_div_lower - ) * (1.0e0 - self.data.physics.f_p_div_inboard_separatrix) - # power into each target - self.data.physics.p_div_lower_inboard_separatrix_mw = ( - self.data.physics.f_p_div_lower_inboard_separatrix - * self.data.physics.ptarmw - ) - self.data.physics.p_div_lower_outboard_separatrix_mw = ( - self.data.physics.f_p_div_lower_outboard_separatrix - * self.data.physics.ptarmw - ) - self.data.physics.p_div_upper_inboard_separatrix_mw = ( - self.data.physics.f_p_div_upper_inboard_separatrix - * self.data.physics.ptarmw - ) - self.data.physics.p_div_upper_outboard_separatrix_mw = ( - self.data.physics.f_p_div_upper_outboard_separatrix - * self.data.physics.ptarmw - ) - else: - # Single null configuration - self.data.physics.f_p_div_lower_inboard_separatrix = ( - self.data.physics.f_p_div_inboard_separatrix - ) - self.data.physics.f_p_div_lower_outboard_separatrix = ( - 1.0e0 - self.data.physics.f_p_div_inboard_separatrix - ) - # power into each target - self.data.physics.p_div_lower_inboard_separatrix_mw = ( - self.data.physics.f_p_div_lower_inboard_separatrix - * self.data.physics.ptarmw - ) - self.data.physics.p_div_lower_outboard_separatrix_mw = ( - self.data.physics.f_p_div_lower_outboard_separatrix - * self.data.physics.ptarmw - ) # Calculate some derived quantities that may not have been defined earlier self.data.physics.p_plasma_heating_total_mw = ( From ed289e0ce00055e1b30295ce8ce11eedf4cd7ba7 Mon Sep 17 00:00:00 2001 From: mn3981 Date: Tue, 11 Aug 2026 15:33:05 +0100 Subject: [PATCH 18/29] Rename 'f_p_div_lower' to 'f_p_div_lower_separatrix' across multiple modules and update related calculations --- process/core/init.py | 2 +- process/core/input.py | 2 +- process/core/io/obsolete_vars.py | 2 +- process/core/scan.py | 2 +- process/data_structure/physics_variables.py | 2 +- process/models/divertor.py | 10 +++++----- process/models/physics/exhaust.py | 7 +++---- process/models/physics/physics.py | 15 ++++++++------- .../input_files/spherical_tokamak_eval.IN.DAT | 2 +- tests/regression/input_files/st_regression.IN.DAT | 2 +- tests/unit/models/test_divertor.py | 4 ++-- 11 files changed, 25 insertions(+), 25 deletions(-) diff --git a/process/core/init.py b/process/core/init.py index 7704fd05e8..a1be430db5 100644 --- a/process/core/init.py +++ b/process/core/init.py @@ -718,7 +718,7 @@ def check_process(inputs, data): # noqa: ARG001 # Check if a single null divertor is used in double null machine if i_single_null == DivertorNumberModels.DOUBLE_NULL and ( - data.physics.f_p_div_lower in {1.0, 0.0} + data.physics.f_p_div_lower_separatrix in {1.0, 0.0} ): logger.error( "Operating with a single null in a double null machine", stacklevel=2 diff --git a/process/core/input.py b/process/core/input.py index bf30712004..cfb2ccc553 100644 --- a/process/core/input.py +++ b/process/core/input.py @@ -186,7 +186,7 @@ def bounds(self) -> tuple[NumberType | None, NumberType | None]: "beta_norm_max": InputVariable("physics", float, range=(0.0, 20.0)), "beta_poloidal_eps_max": InputVariable("physics", float, range=(0.01, 10.0)), "f_p_alpha_plasma_deposited": InputVariable("physics", float, range=(0.0, 1.0)), - "f_p_div_lower": InputVariable("physics", float, range=(0.0, 1.0)), + "f_p_div_lower_separatrix": InputVariable("physics", float, range=(0.0, 1.0)), "f_plasma_fuel_deuterium": InputVariable("physics", float, range=(0.0, 1.0)), "ffwal": InputVariable("physics", float, range=(0.0, 10.0)), "f_nd_plasma_pedestal_greenwald": InputVariable("physics", float, range=(0.1, 1.5)), diff --git a/process/core/io/obsolete_vars.py b/process/core/io/obsolete_vars.py index 7227a4b773..a292963b56 100644 --- a/process/core/io/obsolete_vars.py +++ b/process/core/io/obsolete_vars.py @@ -301,7 +301,7 @@ "irfcd": "i_hcd_calculations", "wave_mode": "i_ecrh_wave_mode", "pheatfix": "p_hcd_secondary_extra_heat_mw", - "ftar": "f_p_div_lower", + "ftar": "f_p_div_lower_separatrix", "beta_div": "deg_div_field_plate", "divclfr": "f_vol_div_coolant", "divdens": "den_div_structure", diff --git a/process/core/scan.py b/process/core/scan.py index d62a84a769..f7fa304e8e 100644 --- a/process/core/scan.py +++ b/process/core/scan.py @@ -191,7 +191,7 @@ def get_val(self, mfile, scan): n_tf_wp_pancakes = (48, Area.T) n_tf_wp_layers = (49, Area.T) f_nd_impurity_electrons__13 = (50, Area.IR) - f_p_div_lower = (51, Area.P) + f_p_div_lower_separatrix = (51, Area.P) rad_fraction_sol = (52, Area.P) boundu__157 = (53, Area.NUM) b_crit_upper_nbti = (54, Area.T) diff --git a/process/data_structure/physics_variables.py b/process/data_structure/physics_variables.py index a7ef59497b..9cde8d19b8 100644 --- a/process/data_structure/physics_variables.py +++ b/process/data_structure/physics_variables.py @@ -750,7 +750,7 @@ class PhysicsData: f_plasma_fuel_deuterium: float = 0.5 """Plasma deuterium fuel fraction""" - f_p_div_lower: float = 1.0 + f_p_div_lower_separatrix: float = 1.0 """fraction of power to the lower divertor in double null configuration (`i_single_null = 0` only) (default assumes SN) """ diff --git a/process/models/divertor.py b/process/models/divertor.py index a9563e4383..2723bd8c20 100644 --- a/process/models/divertor.py +++ b/process/models/divertor.py @@ -98,7 +98,7 @@ def run(self, output: bool = False): self.data.physics.nd_plasma_separatrix_electron, self.data.divertor.deg_div_field_plate, self.data.physics.rad_fraction_sol, - self.data.physics.f_p_div_lower, + self.data.physics.f_p_div_lower_separatrix, output=output, ) return @@ -281,7 +281,7 @@ def divwade( nd_plasma_separatrix_electron: float, deg_div_field_plate: float, rad_fraction_sol: float, - f_p_div_lower: float, + f_p_div_lower_separatrix: float, output: bool, ) -> float: """Divertor heat load model (Wade 2020) @@ -316,7 +316,7 @@ def divwade( field line angle wrt divertor target plate (degrees) rad_fraction_sol : float SOL radiation fraction - f_p_div_lower : float + f_p_div_lower_separatrix : float fraction of power to the lower divertor in double null configuration Returns @@ -375,8 +375,8 @@ def divwade( # For double null, calculate heat loads to upper and lower divertors # and use the highest if self.data.divertor.n_divertors == 2: - hldiv_lower = f_p_div_lower * hldiv_base - hldiv_upper = (1.0 - f_p_div_lower) * hldiv_base + hldiv_lower = f_p_div_lower_separatrix * hldiv_base + hldiv_upper = (1.0 - f_p_div_lower_separatrix) * hldiv_base self.data.divertor.pflux_div_heat_load_mw = max(hldiv_lower, hldiv_upper) else: self.data.divertor.pflux_div_heat_load_mw = hldiv_base diff --git a/process/models/physics/exhaust.py b/process/models/physics/exhaust.py index bb5872b387..5f3adcbebb 100644 --- a/process/models/physics/exhaust.py +++ b/process/models/physics/exhaust.py @@ -354,13 +354,12 @@ def output_brunner_divertor_power_splits(self): """Output the Brunner divertor power splits to the output file.""" if self.data.stellarator.istell == 0: po.osubhd(self.outfile, "Brunner Divertor Power Splits:") - - + po.ovarre( self.outfile, "Fraction of power to the lower divertor", - "(f_p_div_lower)", - self.data.physics.f_p_div_lower, + "(f_p_div_lower_separatrix)", + self.data.physics.f_p_div_lower_separatrix, "IP ", ) po.ovarre( diff --git a/process/models/physics/physics.py b/process/models/physics/physics.py index e665808cf3..dcb3b3341a 100644 --- a/process/models/physics/physics.py +++ b/process/models/physics/physics.py @@ -845,15 +845,15 @@ def run(self): ) # if double null configuration share the power - # over the upper and lower divertor, where self.data.physics.f_p_div_lower gives + # over the upper and lower divertor, where self.data.physics.f_p_div_lower_separatrix gives # the factor of power conducted to the lower divertor if self.data.divertor.n_divertors == 2: self.data.physics.p_div_lower_separatrix_mw = ( - self.data.physics.f_p_div_lower + self.data.physics.f_p_div_lower_separatrix * self.data.physics.p_plasma_separatrix_mw ) self.data.physics.p_div_upper_separatrix_mw = ( - 1.0e0 - self.data.physics.f_p_div_lower + 1.0e0 - self.data.physics.f_p_div_lower_separatrix ) * self.data.physics.p_plasma_separatrix_mw self.data.physics.p_div_separatrix_max_mw = max( self.data.physics.p_div_lower_separatrix_mw, @@ -997,15 +997,17 @@ def run(self): # physics_module.dr_plasma_outboard_midplane_separatrix_separation when running # single null in a double null machine # C W Ashe - if self.data.physics.f_p_div_lower < 4.5e-5: + if self.data.physics.f_p_div_lower_separatrix < 4.5e-5: self.data.physics.dr_plasma_outboard_midplane_separatrix_separation = 1.5e-2 - elif self.data.physics.f_p_div_lower > (1.0e0 - 4.5e-5): + elif self.data.physics.f_p_div_lower_separatrix > (1.0e0 - 4.5e-5): self.data.physics.dr_plasma_outboard_midplane_separatrix_separation = -1.5e-2 else: self.data.physics.dr_plasma_outboard_midplane_separatrix_separation = ( -2.0e0 * 1.5e-3 - * math.atanh(2.0e0 * (self.data.physics.f_p_div_lower - 0.5e0)) + * math.atanh( + 2.0e0 * (self.data.physics.f_p_div_lower_separatrix - 0.5e0) + ) ) div_power_plits = self.exhaust.calculate_brunner_divertor_power_splits( @@ -1053,7 +1055,6 @@ def run(self): * self.data.physics.p_plasma_separatrix_mw ) - # Calculate some derived quantities that may not have been defined earlier self.data.physics.p_plasma_heating_total_mw = ( self.calculate_total_plasma_heating_power( diff --git a/tests/regression/input_files/spherical_tokamak_eval.IN.DAT b/tests/regression/input_files/spherical_tokamak_eval.IN.DAT index 93f3541265..b3e92c2df9 100644 --- a/tests/regression/input_files/spherical_tokamak_eval.IN.DAT +++ b/tests/regression/input_files/spherical_tokamak_eval.IN.DAT @@ -263,7 +263,7 @@ b_plasma_toroidal_on_axis = 3.0 * toroidal field on axis (T) (`iteration v nd_plasma_electrons_vol_avg = 9.69888313737236e+19 * electron density (/m3) (`iteration variable 6`) beta_norm_max = 5.0 * Troyon-like coefficient for beta scaling i_beta_norm_max = 0 * Normalised beta max selection (0 = user input) -f_p_div_lower = 0.5 * fraction of power to the lower divertor in double null configuration +f_p_div_lower_separatrix = 0.5 * fraction of power to the lower divertor in double null configuration f_nd_plasma_pedestal_greenwald = 0.1 * fraction of Greenwald density to set as pedestal-top density; If `<0`; pedestal-top f_nd_plasma_separatrix_greenwald = 0.1 * fraction of Greenwald density to set as separatrix density; If `<0`; separatrix hfact = 1.2 * H factor on energy confinement times; radiation corrected (`iteration variable 10`); diff --git a/tests/regression/input_files/st_regression.IN.DAT b/tests/regression/input_files/st_regression.IN.DAT index a34de1f2e9..2a346db5ac 100644 --- a/tests/regression/input_files/st_regression.IN.DAT +++ b/tests/regression/input_files/st_regression.IN.DAT @@ -631,7 +631,7 @@ f_nd_impurity_electrons(14) = 5e-05 *Tungsten *-------------------------------- *Divertor* ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------* *‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾* -f_p_div_lower = 0.5 +f_p_div_lower_separatrix = 0.5 * DESCRIPTION: Fraction of power to the lower divertor * JUSTIFICATION: All power to lower divertor as Single-Null diff --git a/tests/unit/models/test_divertor.py b/tests/unit/models/test_divertor.py index 803df7cfc7..1ca1bf2ac3 100644 --- a/tests/unit/models/test_divertor.py +++ b/tests/unit/models/test_divertor.py @@ -86,7 +86,7 @@ def test_divwade(monkeypatch, divertor): nd_plasma_separatrix_electron = 1.0e19 deg_div_field_plate = 5.0 rad_fraction_sol = 8.0e-1 - f_p_div_lower = 1.0 + f_p_div_lower_separatrix = 1.0 expected_pflux_div_heat_load_mw = 0.58898578 @@ -101,7 +101,7 @@ def test_divwade(monkeypatch, divertor): nd_plasma_separatrix_electron, deg_div_field_plate, rad_fraction_sol, - f_p_div_lower, + f_p_div_lower_separatrix, False, ) From 509c78b6bb380594f40efca4951b43c70ec7bf8a Mon Sep 17 00:00:00 2001 From: mn3981 Date: Tue, 11 Aug 2026 15:45:44 +0100 Subject: [PATCH 19/29] Enhance PlasmaExhaust output with detailed separatrix power fractions and distances for double null configuration --- process/models/build.py | 7 +++ process/models/physics/exhaust.py | 101 ++++++++++++++++++------------ 2 files changed, 68 insertions(+), 40 deletions(-) diff --git a/process/models/build.py b/process/models/build.py index d0f86fa049..de7167d7c2 100644 --- a/process/models/build.py +++ b/process/models/build.py @@ -1471,6 +1471,13 @@ def divgeom(self, output: bool): self.outfile, "ERROR: null value not supported, check i_single_null value.", ) + po.ovarre( + self.outfile, + "Divertor poloidal angle subtended by plasma (degrees)", + "(deg_div_poloidal_plasma)", + self.data.divertor.deg_div_poloidal_plasma, + "OP ", + ) return divht @staticmethod diff --git a/process/models/physics/exhaust.py b/process/models/physics/exhaust.py index 5f3adcbebb..de89d30567 100644 --- a/process/models/physics/exhaust.py +++ b/process/models/physics/exhaust.py @@ -8,6 +8,7 @@ from process.core import constants from process.core import process_output as po from process.core.model import Model +from process.data_structure.physics_variables import DivertorNumberModels logger = logging.getLogger(__name__) @@ -357,11 +358,20 @@ def output_brunner_divertor_power_splits(self): po.ovarre( self.outfile, - "Fraction of power to the lower divertor", + "Requested fraction of power to the lower divertor in double null configuration", "(f_p_div_lower_separatrix)", self.data.physics.f_p_div_lower_separatrix, "IP ", ) + po.ovarre( + self.outfile, + "Required distance between the first and second plasma separatrixes at the outer midplane (δR_sep) [m]", + "(dr_plasma_outboard_midplane_separatrix_separation)", + self.data.physics.dr_plasma_outboard_midplane_separatrix_separation, + "OP ", + ) + po.oblnkl(self.outfile) + po.ovarre( self.outfile, "Outboard side heat flux decay length (m)", @@ -369,78 +379,89 @@ def output_brunner_divertor_power_splits(self): self.data.physics.len_sol_outboard_power_decay, "OP ", ) - if self.data.divertor.n_divertors == 2: - po.ovarre( - self.outfile, - "Radial distance between the first and second plasma separatrixes at the outer midplane (δR_sep) [m]", - "(dr_plasma_outboard_midplane_separatrix_separation)", - self.data.physics.dr_plasma_outboard_midplane_separatrix_separation, - "OP ", - ) + po.oblnkl(self.outfile) po.ovarre( self.outfile, - "Fraction of power on the inner targets", + "Fraction of separatrix power on the inner target(s)", "(f_p_div_inboard_separatrix)", self.data.physics.f_p_div_inboard_separatrix, "OP ", ) po.ovarre( self.outfile, - "Fraction of power incident on the lower inner target", - "(fLI)", + "Fraction of separatrix power on the outer target(s)", + "(f_p_div_outboard_separatrix)", + self.data.physics.f_p_div_outboard_separatrix, + "OP ", + ) + + po.oblnkl(self.outfile) + + po.ovarre( + self.outfile, + "Fraction of separatrix power on the inner lower target", + "(f_p_div_lower_inboard_separatrix)", self.data.physics.f_p_div_lower_inboard_separatrix, "OP ", ) po.ovarre( self.outfile, - "Fraction of power incident on the lower outer target", - "(fLO)", - self.data.physics.f_p_div_lower_outboard_separatrix, + "Separatrix power on the inner lower target", + "(p_div_lower_inboard_separatrix_mw)", + self.data.physics.p_div_lower_inboard_separatrix_mw, "OP ", ) - if self.data.divertor.n_divertors == 2: - po.ovarre( - self.outfile, - "Fraction of power incident on the upper inner target", - "(fUI)", - self.data.physics.f_p_div_upper_inboard_separatrix, - "OP ", - ) - po.ovarre( - self.outfile, - "Fraction of power incident on the upper outer target", - "(fUO)", - self.data.physics.f_p_div_upper_outboard_separatrix, - "OP ", - ) + po.oblnkl(self.outfile) po.ovarre( self.outfile, - "Power incident on the lower inner target (MW)", - "(pLImw)", - self.data.physics.p_div_lower_inboard_separatrix_mw, + "Fraction of separatrix power on the outer lower target", + "(f_p_div_lower_outboard_separatrix)", + self.data.physics.f_p_div_lower_outboard_separatrix, "OP ", ) po.ovarre( self.outfile, - "Power incident on the lower outer target (MW)", - "(pLOmw)", + "Separatrix power on the outer lower target", + "(p_div_lower_outboard_separatrix_mw)", self.data.physics.p_div_lower_outboard_separatrix_mw, "OP ", ) - if self.data.divertor.n_divertors == 2: + + if ( + DivertorNumberModels(self.data.physics.i_single_null) + == DivertorNumberModels.DOUBLE_NULL + ): + po.oblnkl(self.outfile) + po.ocmmnt(self.outfile, "----------------------------") + po.oblnkl(self.outfile) + po.ovarre( + self.outfile, + "Fraction of separatrix power on the inner upper target", + "(f_p_div_upper_inboard_separatrix)", + self.data.physics.f_p_div_upper_inboard_separatrix, + "OP ", + ) po.ovarre( self.outfile, - "Power incident on the upper innner target (MW)", - "(pUImw)", + "Separatrix power on the inner upper target", + "(p_div_upper_inboard_separatrix_mw)", self.data.physics.p_div_upper_inboard_separatrix_mw, "OP ", ) + po.oblnkl(self.outfile) + po.ovarre( + self.outfile, + "Fraction of separatrix power on the outer upper target", + "(f_p_div_upper_outboard_separatrix)", + self.data.physics.f_p_div_upper_outboard_separatrix, + "OP ", + ) po.ovarre( self.outfile, - "Power incident on the upper outer target (MW)", - "(pUOmw)", + "Separatrix power on the outer upper target", + "(p_div_upper_outboard_separatrix_mw)", self.data.physics.p_div_upper_outboard_separatrix_mw, "OP ", ) From 0284e1efbb4f66674f1f5e35090ebce46252ea47 Mon Sep 17 00:00:00 2001 From: mn3981 Date: Wed, 12 Aug 2026 11:33:31 +0100 Subject: [PATCH 20/29] Update obsolete variable mapping and enhance Brunner divertor power split plotting functions --- process/core/io/obsolete_vars.py | 1 + process/core/io/plot/summary.py | 215 +++++++++++++++++++++++++++++-- 2 files changed, 207 insertions(+), 9 deletions(-) diff --git a/process/core/io/obsolete_vars.py b/process/core/io/obsolete_vars.py index a292963b56..a9734f8f13 100644 --- a/process/core/io/obsolete_vars.py +++ b/process/core/io/obsolete_vars.py @@ -478,6 +478,7 @@ "minmax": "i_figure_merit", "neqns": "n_equality_constraints", "nineqns": "n_inequality_constraints", + "f_p_div_lower": "f_p_div_lower_separatrix", } OBS_VARS_HELP = { diff --git a/process/core/io/plot/summary.py b/process/core/io/plot/summary.py index f7e0b31644..e61ca772e0 100644 --- a/process/core/io/plot/summary.py +++ b/process/core/io/plot/summary.py @@ -23,7 +23,10 @@ from process.data_structure.impurity_radiation_variables import N_IMPURITIES from process.data_structure.numerics import FiguresOfMerit, PROCESSRunMode from process.data_structure.pfcoil_variables import NFIXMX -from process.data_structure.physics_variables import ConfinementTimeModel +from process.data_structure.physics_variables import ( + ConfinementTimeModel, + DivertorNumberModels, +) from process.data_structure.superconducting_tf_coil_variables import TFWPIntegerTurnType from process.models.build import Build from process.models.engineering.materials import ( @@ -9088,15 +9091,14 @@ def plot_sol_power_decay_length_comparison(axis: plt.Axes, mfile: MFile, scan: i axis.set_facecolor("#f0f0f0") -def plot_brunner_divertor_power_plit_comparison(axis: plt.Axes, mfile: MFile, scan: int): +def plot_brunner_divertor_power_split_comparison_stackplot( + axis: plt.Axes, mfile: MFile, scan: int +): """Plot Brunner divertor power split fractions as a stack plot over dr_sep.""" # Use the case decay length when available; fall back to 1 mm if absent. - try: - len_plasma_sol_power_decay = mfile.get( - "len_plasma_sol_eich13_power_decay", scan=scan - ) - except KeyError: - len_plasma_sol_power_decay = 1.0e-3 + + len_plasma_sol_power_decay = mfile.get("len_sol_outboard_power_decay", scan=scan) + colors = plt.cm.plasma(np.linspace(0.15, 0.85, 4)) dr_sep_values = np.linspace( -5 * len_plasma_sol_power_decay, 5 * len_plasma_sol_power_decay, 200 @@ -9129,6 +9131,7 @@ def plot_brunner_divertor_power_plit_comparison(axis: plt.Axes, mfile: MFile, sc "$f_{P,\\mathrm{out,lower}}$", "$f_{P,\\mathrm{out,upper}}$", ], + colors=colors, alpha=0.9, ) @@ -9148,6 +9151,196 @@ def plot_brunner_divertor_power_plit_comparison(axis: plt.Axes, mfile: MFile, sc axis.legend(loc="upper left", fontsize=8) +def plot_separatrix_power_split(axis: plt.Axes, mfile: MFile, scan: int, colour_scheme): + """Plot separatrix power split fractions as a bar chart.""" + plot_plasma(axis=axis, mfile=mfile, scan=scan, colour_scheme=colour_scheme) + rmajor, rminor, kappa, dr_sep = mfile.get_variables( + "rmajor", + "rminor", + "kappa", + "dr_plasma_outboard_midplane_separatrix_separation", + scan=scan, + ) + + plasma_scale = max(rminor, abs(kappa * rminor), 1e-6) + scale_factor = min(max(plasma_scale / 2.0, 0.7), 1.0) + text_fontsize = 9 * scale_factor + + is_double_null = ( + DivertorNumberModels(mfile.get("i_single_null", scan=scan)) + == DivertorNumberModels.DOUBLE_NULL + ) + p_sep = mfile.get("p_plasma_separatrix_mw", scan=scan) + f_outboard = mfile.get("f_p_div_outboard_separatrix", scan=scan) + f_inboard = mfile.get("f_p_div_inboard_separatrix", scan=scan) + p_outboard = p_sep * f_outboard + p_inboard = p_sep * f_inboard + p_lower_inboard = mfile.get("p_div_lower_inboard_separatrix_mw", scan=scan) + p_lower_outboard = mfile.get("p_div_lower_outboard_separatrix_mw", scan=scan) + + power_values = [ + p_sep, + p_outboard, + p_inboard, + p_lower_inboard, + p_lower_outboard, + ] + + p_upper_inboard = None + p_upper_outboard = None + if is_double_null: + p_upper_inboard = mfile.get("p_div_upper_inboard_separatrix_mw", scan=scan) + p_upper_outboard = mfile.get("p_div_upper_outboard_separatrix_mw", scan=scan) + power_values.extend([p_upper_inboard, p_upper_outboard]) + + power_min = min(power_values) + power_max = max(power_values) + colour_map = mpl.colormaps["coolwarm"] + + def make_bbox_props(power: float) -> dict[str, Any]: + norm_power = ( + 1.0 + if np.isclose(power_max, power_min) + else (power - power_min) / (power_max - power_min) + ) + return { + "boxstyle": f"round,pad={0.3 * scale_factor:.3f}", + "facecolor": colour_map(norm_power), + "alpha": 1.0, + "linewidth": 2 * scale_factor, + "edgecolor": "black", + } + + centre_pos = (rmajor, 0.0) + outboard_pos = (rmajor + rminor, 0.0) + inboard_pos = (rmajor - rminor, 0.0) + lower_inboard_pos = (rmajor - rminor, -kappa * rminor) + lower_outboard_pos = (rmajor + rminor, -kappa * rminor) + upper_inboard_pos = (rmajor - rminor, kappa * rminor) + upper_outboard_pos = (rmajor + rminor, kappa * rminor) + + axis.text( + *centre_pos, + f"$P_{{\\mathrm{{sep}}}} = {p_sep:.3f}$ MW", + fontsize=text_fontsize, + verticalalignment="center", + horizontalalignment="center", + bbox=make_bbox_props(p_sep), + zorder=101, + ) + axis.text( + *outboard_pos, + f"$f_{{\\mathrm{{outboard}}}} = {f_outboard:.3f}$\n" + f"$\\Delta r_{{\\mathrm{{sep}}}} = {dr_sep:.3f}$ m", + fontsize=text_fontsize, + verticalalignment="center", + horizontalalignment="center", + bbox=make_bbox_props(p_outboard), + zorder=101, + ) + axis.text( + *inboard_pos, + f"$f_{{\\mathrm{{inboard}}}} = {f_inboard:.3f}$", + fontsize=text_fontsize, + verticalalignment="center", + horizontalalignment="center", + bbox=make_bbox_props(p_inboard), + zorder=101, + ) + axis.text( + *lower_inboard_pos, + f"$f_{{\\mathrm{{lower\\ inboard}}}} = {mfile.get('f_p_div_lower_inboard_separatrix', scan=scan):.3f}$\n" + f"$P_{{\\mathrm{{lower\\ inboard}}}} = {p_lower_inboard:.3f}$ MW", + fontsize=text_fontsize, + verticalalignment="center", + horizontalalignment="center", + bbox=make_bbox_props(p_lower_inboard), + zorder=101, + ) + axis.text( + *lower_outboard_pos, + f"$f_{{\\mathrm{{lower\\ outboard}}}} = {mfile.get('f_p_div_lower_outboard_separatrix', scan=scan):.3f}$\n" + f"$P_{{\\mathrm{{lower\\ outboard}}}} = {p_lower_outboard:.3f}$ MW", + fontsize=text_fontsize, + verticalalignment="center", + horizontalalignment="center", + bbox=make_bbox_props(p_lower_outboard), + zorder=101, + ) + if is_double_null: + axis.text( + *upper_inboard_pos, + f"$f_{{\\mathrm{{upper\\ inboard}}}} = {mfile.get('f_p_div_upper_inboard_separatrix', scan=scan):.3f}$\n" + f"$P_{{\\mathrm{{upper\\ inboard}}}} = {p_upper_inboard:.3f}$ MW", + fontsize=text_fontsize, + verticalalignment="center", + horizontalalignment="center", + bbox=make_bbox_props(p_upper_inboard), + zorder=101, + ) + axis.text( + *upper_outboard_pos, + f"$f_{{\\mathrm{{upper\\ outboard}}}} = {mfile.get('f_p_div_upper_outboard_separatrix', scan=scan):.3f}$\n" + f"$P_{{\\mathrm{{upper\\ outboard}}}} = {p_upper_outboard:.3f}$ MW", + fontsize=text_fontsize, + verticalalignment="center", + horizontalalignment="center", + bbox=make_bbox_props(p_upper_outboard), + zorder=101, + ) + + arrow_props = { + "arrowstyle": "->", + "color": "red", + "linewidth": 3 * scale_factor, + "shrinkA": 14 * scale_factor, + "shrinkB": 14 * scale_factor, + "mutation_scale": 12 * scale_factor, + } + axis.annotate( + "", xy=outboard_pos, xytext=centre_pos, arrowprops=arrow_props, zorder=1 + ) + axis.annotate( + "", xy=inboard_pos, xytext=centre_pos, arrowprops=arrow_props, zorder=1 + ) + axis.annotate( + "", + xy=lower_outboard_pos, + xytext=outboard_pos, + arrowprops={**arrow_props, "connectionstyle": "angle3,angleA=0,angleB=-90"}, + zorder=102, + ) + axis.annotate( + "", + xy=lower_inboard_pos, + xytext=inboard_pos, + arrowprops={**arrow_props, "connectionstyle": "angle3,angleA=180,angleB=-90"}, + zorder=102, + ) + if is_double_null: + axis.annotate( + "", + xy=upper_outboard_pos, + xytext=outboard_pos, + arrowprops={**arrow_props, "connectionstyle": "angle3,angleA=0,angleB=90"}, + zorder=102, + ) + axis.annotate( + "", + xy=upper_inboard_pos, + xytext=inboard_pos, + arrowprops={**arrow_props, "connectionstyle": "angle3,angleA=180,angleB=90"}, + zorder=102, + ) + + axis.spines["top"].set_visible(False) + axis.spines["right"].set_visible(False) + axis.spines["bottom"].set_visible(False) + axis.spines["left"].set_visible(False) + axis.get_xaxis().set_ticks([]) + axis.get_yaxis().set_ticks([]) + + def plot_h_threshold_comparison(axis: plt.Axes, mfile: MFile, scan: int, u_seed=None): """Function to plot a scatter box plot of L-H threshold power comparisons. @@ -16506,10 +16699,14 @@ def _add_page(name: str | None = None): _add_page("plasma_compare_3").add_subplot(221), m_file, scan ) - plot_brunner_divertor_power_plit_comparison( + plot_brunner_divertor_power_split_comparison_stackplot( _add_page("plasma_exhaust").add_subplot(121), m_file, scan ) + plot_separatrix_power_split( + pages["plasma_exhaust"].add_subplot(122), m_file, scan, colour_scheme + ) + plot_debye_length_profile( _add_page("microscopic_quantities").add_subplot(232), m_file, scan ) From 5f066b0f423dd42866a481999ef0636ee042110d Mon Sep 17 00:00:00 2001 From: mn3981 Date: Wed, 12 Aug 2026 11:45:32 +0100 Subject: [PATCH 21/29] Add Plasma Exhaust documentation and update navigation in mkdocs.yml --- documentation/source/physics-models/plasma_exhaust.md | 0 mkdocs.yml | 1 + 2 files changed, 1 insertion(+) create mode 100644 documentation/source/physics-models/plasma_exhaust.md diff --git a/documentation/source/physics-models/plasma_exhaust.md b/documentation/source/physics-models/plasma_exhaust.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/mkdocs.yml b/mkdocs.yml index 1c80b5d1de..6cfa762788 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -67,6 +67,7 @@ nav: - Confinement time: physics-models/plasma_confinement.md - L-H transition: physics-models/plasma_h_mode.md - Plasma Core Power Balance: physics-models/plasma_power_balance.md + - Plasma Exhaust: physics-models/plasma_exhaust.md - Plasma Scrape-off Layer: physics-models/plasma_scrape_off_layer.md - Detailed Plasma Physics: physics-models/detailed_physics.md - Pulsed Plant Operation: physics-models/pulsed-plant.md From d3c60c3a3fd9da07061a8ce7669e42cacc51d29c Mon Sep 17 00:00:00 2001 From: mn3981 Date: Wed, 12 Aug 2026 12:52:21 +0100 Subject: [PATCH 22/29] Add documentation for Plasma Exhaust and Divertor Heat Splits metrics --- .../source/physics-models/plasma_exhaust.md | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/documentation/source/physics-models/plasma_exhaust.md b/documentation/source/physics-models/plasma_exhaust.md index e69de29bb2..111d1e3ee1 100644 --- a/documentation/source/physics-models/plasma_exhaust.md +++ b/documentation/source/physics-models/plasma_exhaust.md @@ -0,0 +1,50 @@ +# Plasma Exhaust | `PlasmaExhaust` + +## Separatrix Power | `calculate_separatrix_power()` + +The separatrix power is defined as the thermal and conducted power flowing outward from the core plasma across the separatrix into the scrape-off layer. + +$$ +\overbrace{P_{\text{sep}}}^{\texttt{p_plasma_separatrix_mw}} = \underbrace{f_{\alpha}P_{\alpha} + P_{\text{c}} + P_{\text{OH}} + P_{\text{HCD}}}_{\text{Plasma heating}} - \underbrace{P_{\text{rad}}}_{\text{Plasma Loss}} +$$ + +where $f_{\alpha}$ is the [fraction of alpha power that is coupled to the plasma](../physics-models/fusion_reactions/plasma_reactions.md#coupled-alpha-particle-power), $P_{\alpha}$ is the alpha power, $P_{\text{c}}$ is the charged particle power, $P_{\text{OH}}$ is the ohmic heating power, $P_{\text{HCD}}$ is the plasma heating done by the external heating & current drive systems and $P_{\text{rad}}$ is the total radiation power given off by the plasma. + +-------------- + +## Divertor Protection Metrics + +### `calculate_psep_over_r_metric()` + +A simple metric for the divertor heat flux challenege is to simply divide by total separatrix power by the plasma major radius. This works as a stand in metric for the total available divertor area in which to spread the heat + + +$$ +\texttt{p_plasma_separatrix_rmajor_mw}=\frac{P_{\text{sep}}}{R_0} +$$ + +------------ + +### EU-DEMO Protection Re-Attachment Metric | `calculate_eu_demo_re_attachment_metric()` + +The divertor protection metric is obtained by starting from the target heat flux formula, inserting the Eich heat-flux-width scaling and $q_{95}-B_{\text{p}}$​ relationship, and reducing the resulting dependence to the engineering quantity whose value is constrained to remain below a reference EU-DEMO limit to ensure the divertor can withstand temporary reattachment without[^1][^2]. + +$$ +\texttt{p_div_bt_q_aspect_rmajor_mw}=\frac{P_{\text{sep}}B_{\text{T}}}{q_{95}AR} +$$ + + +----------- + +## Divertor Heat Splits + +### Brunner Model | `calculate_brunner_divertor_power_splits()` + + +[^1] M. Siccinio, G. Federici, R. Kembleton, H. Lux, F. Maviglia, and J. Morris, +"Figure of merit for divertor protection in the preliminary design of the +EU-DEMO reactor," Nuclear Fusion, vol. 59, no. 10, pp. 106026-106026, +Jul. 2019, doi: https://doi.org/10.1088/1741-4326/ab3153. + +[^2] H. Zohm et al., "A stepladder approach to a tokamak fusion power plant," +Nuclear Fusion, vol. 57, no. 8, pp. 086002-086002, May 2017, doi: https://doi.org/10.1088/1741-4326/aa739e. \ No newline at end of file From d38df644f572d33e9f2c1b2fcb0fa239a765b5a7 Mon Sep 17 00:00:00 2001 From: mn3981 Date: Wed, 12 Aug 2026 13:58:34 +0100 Subject: [PATCH 23/29] Add Brunner model description and update references in documentation and code --- .../source/physics-models/plasma_exhaust.md | 17 +++++++++++++++-- process/models/physics/exhaust.py | 2 +- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/documentation/source/physics-models/plasma_exhaust.md b/documentation/source/physics-models/plasma_exhaust.md index 111d1e3ee1..3d6beaacee 100644 --- a/documentation/source/physics-models/plasma_exhaust.md +++ b/documentation/source/physics-models/plasma_exhaust.md @@ -36,10 +36,19 @@ $$ ----------- -## Divertor Heat Splits +## Divertor Separatrix Power Heat Splits ### Brunner Model | `calculate_brunner_divertor_power_splits()` +The Brunner model[^brunner_model] was made to estimate the power-sharing between four divertors in a dynamic double null configuration. Consisting of an in-out and up-down sharing scheme. The model considers the separatrix power flow going to each divertor rather than that which reaches the target plate after dissipation. The power split assumes that the fraction of power to the side walls is negligibly small and there is no radiation in the SOL. We thus have: + +$$ +P_{\text{sep}} \approx P_{\text{div,tot}} = P_{\text{lower,in}} + P_{\text{lower,out}} + P_{\text{upper,in}} + P_{\text{upper,in}} +$$ + + +-------------------- + [^1] M. Siccinio, G. Federici, R. Kembleton, H. Lux, F. Maviglia, and J. Morris, "Figure of merit for divertor protection in the preliminary design of the @@ -47,4 +56,8 @@ EU-DEMO reactor," Nuclear Fusion, vol. 59, no. 10, pp. 106026-106026, Jul. 2019, doi: https://doi.org/10.1088/1741-4326/ab3153. [^2] H. Zohm et al., "A stepladder approach to a tokamak fusion power plant," -Nuclear Fusion, vol. 57, no. 8, pp. 086002-086002, May 2017, doi: https://doi.org/10.1088/1741-4326/aa739e. \ No newline at end of file +Nuclear Fusion, vol. 57, no. 8, pp. 086002-086002, May 2017, doi: https://doi.org/10.1088/1741-4326/aa739e. + +[^brunner_model] D. Brunner, A. Q. Kuang, B. LaBombard, and J. L. Terry, “The dependence of +divertor power sharing on magnetic flux balance in near double-null configurations on +Alcator C-Mod,” Nuclear Fusion, vol. 58, no. 7, p. 076010, May 2018, doi: https://doi.org/10.1088/1741-4326/aac006. \ No newline at end of file diff --git a/process/models/physics/exhaust.py b/process/models/physics/exhaust.py index de89d30567..b2d104570f 100644 --- a/process/models/physics/exhaust.py +++ b/process/models/physics/exhaust.py @@ -276,7 +276,7 @@ def calculate_brunner_divertor_power_splits( [1] D. Brunner, A. Q. Kuang, B. LaBombard, and J. L. Terry, “The dependence of divertor power sharing on magnetic flux balance in near double-null configurations on Alcator C-Mod,” Nuclear Fusion, vol. 58, no. 7, p. 076010, - May 2018, doi: 10.1088/1741-4326/aac006. + May 2018, doi: https://doi.org/10.1088/1741-4326/aac006. [2] T. W. Petrie et al., “The effect of divertor magnetic balance on H-mode performance in DIII-D,” Journal of Nuclear Materials, vol. 290-293, pp. 935-939, From 4729cb92e4a6fbb2469ed0f847b956258a0ffa52 Mon Sep 17 00:00:00 2001 From: mn3981 Date: Wed, 12 Aug 2026 14:55:21 +0100 Subject: [PATCH 24/29] Enhance PlasmaExhaust documentation with details on fitted values for inner divertor power splits and update references --- .../source/physics-models/plasma_exhaust.md | 49 ++++++++++++++++++- process/models/physics/exhaust.py | 7 ++- 2 files changed, 53 insertions(+), 3 deletions(-) diff --git a/documentation/source/physics-models/plasma_exhaust.md b/documentation/source/physics-models/plasma_exhaust.md index 3d6beaacee..1d62172763 100644 --- a/documentation/source/physics-models/plasma_exhaust.md +++ b/documentation/source/physics-models/plasma_exhaust.md @@ -43,9 +43,52 @@ $$ The Brunner model[^brunner_model] was made to estimate the power-sharing between four divertors in a dynamic double null configuration. Consisting of an in-out and up-down sharing scheme. The model considers the separatrix power flow going to each divertor rather than that which reaches the target plate after dissipation. The power split assumes that the fraction of power to the side walls is negligibly small and there is no radiation in the SOL. We thus have: $$ -P_{\text{sep}} \approx P_{\text{div,tot}} = P_{\text{lower,in}} + P_{\text{lower,out}} + P_{\text{upper,in}} + P_{\text{upper,in}} +P_{\text{sep}} \approx P_{\text{div,tot}} = P_{\text{lower,in}} + P_{\text{lower,out}} + P_{\text{upper,in}} + P_{\text{upper,out}} $$ +The main determining variable for the power split is $\delta R_{\text{sep}}$ representing the radial distance between the first and second separatrixes at the plasma outboard mid-plane. + +A negative value of $\delta R_{\text{sep}}$ represents a dominant lower null configuration, A $\delta R_{\text{sep}}$ of 0 represents a perfect double null configuration and a positive $\delta R_{\text{sep}}$ is a upper null configuration. + +It is observed that the majority of the power crosses the plasma boundary at the outer mid-plane. Assuming that the resultant heat flux profile is exponential with a single e-folding width equal to the scrape off layer power decay length, $\lambda_{\text{q,o}}$. + +For the outboard, side the fraction of the outboard power going to the lower target is: + +$$ +\frac{P_{\text{lower,out}}}{P_{\text{lower,out}}+P_{\text{upper,out}}} = \frac{1}{1+e^{\frac{\delta R_{\text{sep}}}{\lambda_{\text{q,o}}}}} +$$ + +For the upper target: + +$$ +\frac{P_{\text{upper,out}}}{P_{\text{lower,out}}+P_{\text{upper,out}}} = \frac{1}{1+e^{-\frac{\delta R_{\text{sep}}}{\lambda_{\text{q,o}}}}} +$$ + +For the inboard divertors it was seen that the power sharing follows a similar logistic function trend but with a distinctly different power decay length, $\lambda_{\text{q,i}}$. + +For the inboard side the fraction of the inboard power going to the lower target is: + +$$ +\frac{P_{\text{lower,in}}}{P_{\text{lower,in}}+P_{\text{upper,in}}} = \frac{1}{1+e^{\frac{\delta R_{\text{sep}}}{\lambda_{\text{q,i}}}}} +$$ + +The total inboard split compared to the outboard was found to give a Gaussian like dependence as follows: + +$$ +\frac{P_{\text{lower,in}}+P_{\text{lower,out}}}{P_{\text{lower,in}}+P_{\text{upper,in}}+P_{\text{lower,out}}+P_{\text{upper,out}}} = \\ P_{\text{in,0}}+(P_{\text{in,0}}-P_{\text{in,}\infin})\times \left(1-\frac{2}{1+e^{-\left(\frac{\delta R_{\text{sep}}}{\lambda_{\text{q,io}}}\right)^2}}\right) +$$ + +where $P_{\text{in,0}}$ is the fraction of power to the inner divertors at $\delta R_{\text{sep}} = 0$, $P_{\text{in,}\infin}$ is the fraction of the power to the inner divertors at $R_{\text{sep}} = \infin$ + +Fitted values to data from DIII-D[^d3_d_values] have shown the values of $P_{\text{in,0}}$ and $P_{\text{in,}\infin}$ to be 0.16 and 0.41 respectively which is what we use here. We also assume that out Gaussian width in the total power sharing is equal to the outboard power decay length, $\lambda_{\text{q,io}} = \lambda_{\text{q,o}}$. + +In `PROCESS` we normally want to control how much of the separatrix power is directed to either the upper or lower divertors, this is done through changing `f_p_div_lower_separatrix`. Fitting to the DIII-D[^d3_d_values] we calculate the value of $\delta R_{\text{sep}}$ via: + +$$ +\delta R_{\text{sep}} = -3\times 10^{-3} \tanh^{-1}\left(2(\texttt{f_p_div_lower_separatrix}-0.5)\right) +$$ + +Truncated values of the $\delta R_{\text{sep}}$ are set to $1.5\times 10^{-2}\ \text{m}$ and $-1.5\times 10^{-2} \ \text{m}$ respectively based on when the power split gets close to a perfect upper or lower null. -------------------- @@ -60,4 +103,6 @@ Nuclear Fusion, vol. 57, no. 8, pp. 086002-086002, May 2017, doi: https://doi.or [^brunner_model] D. Brunner, A. Q. Kuang, B. LaBombard, and J. L. Terry, “The dependence of divertor power sharing on magnetic flux balance in near double-null configurations on -Alcator C-Mod,” Nuclear Fusion, vol. 58, no. 7, p. 076010, May 2018, doi: https://doi.org/10.1088/1741-4326/aac006. \ No newline at end of file +Alcator C-Mod,” Nuclear Fusion, vol. 58, no. 7, p. 076010, May 2018, doi: https://doi.org/10.1088/1741-4326/aac006. + +[^d3_d_values] T. W. Petrie et al., “The effect of divertor magnetic balance on H-mode performance in DIII-D,” Journal of Nuclear Materials, vol. 290-293, pp. 935-939, Mar. 2001, doi: https://doi.org/10.1016/S0022-3115(00)00492-X \ No newline at end of file diff --git a/process/models/physics/exhaust.py b/process/models/physics/exhaust.py index b2d104570f..35b405ab6e 100644 --- a/process/models/physics/exhaust.py +++ b/process/models/physics/exhaust.py @@ -271,6 +271,11 @@ def calculate_brunner_divertor_power_splits( Dataclass containing the fractions of total separatrix power to each divertor target. + Notes + ----- + - The fitted value for `F_P_INNER_SEP_0` and `F_P_INNER_SEP_INFINITY` are taken + from the fit given by Petrie et al. [2] + References ---------- [1] D. Brunner, A. Q. Kuang, B. LaBombard, and J. L. Terry, “The dependence of @@ -280,7 +285,7 @@ def calculate_brunner_divertor_power_splits( [2] T. W. Petrie et al., “The effect of divertor magnetic balance on H-mode performance in DIII-D,” Journal of Nuclear Materials, vol. 290-293, pp. 935-939, - Mar. 2001, doi: 10.1016/s0022-3115(00)00492-x. + Mar. 2001, doi: https://doi.org/10.1016/S0022-3115(00)00492-X """ # Fraction of the power to the inner divertors at δR_sep = 0 and δR_sep → ∞ F_P_INNER_SEP_0 = 0.16e0 From 6168a3199782573d8f82c19eb65f45ac07372bd7 Mon Sep 17 00:00:00 2001 From: mn3981 Date: Wed, 12 Aug 2026 15:05:51 +0100 Subject: [PATCH 25/29] Add unit tests for Brunner divertor power splits calculations --- .../models/physics/test_plasma_exhaust.py | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 tests/unit/models/physics/test_plasma_exhaust.py diff --git a/tests/unit/models/physics/test_plasma_exhaust.py b/tests/unit/models/physics/test_plasma_exhaust.py new file mode 100644 index 0000000000..f1ba958889 --- /dev/null +++ b/tests/unit/models/physics/test_plasma_exhaust.py @@ -0,0 +1,44 @@ +import pytest + +from process.models.physics.exhaust import PlasmaExhaust + + +def test_calculate_brunner_divertor_power_splits_at_zero_separatrix_separation(): + result = PlasmaExhaust().calculate_brunner_divertor_power_splits( + dr_plasma_outboard_midplane_separatrix_separation=0.0, + len_plasma_sol_outboard_power_decay=0.01, + f_len_sol_power_decay_inboard=1.0, + ) + + assert result.f_p_div_inboard_separatrix == pytest.approx(0.16) + assert result.f_p_div_outboard_separatrix == pytest.approx(0.84) + assert result.f_p_div_inboard_lower_separatrix == pytest.approx(0.08) + assert result.f_p_div_inboard_upper_separatrix == pytest.approx(0.08) + assert result.f_p_div_outboard_lower_separatrix == pytest.approx(0.42) + assert result.f_p_div_outboard_upper_separatrix == pytest.approx(0.42) + + +def test_calculate_brunner_divertor_power_splits_asymmetric_case(): + result = PlasmaExhaust().calculate_brunner_divertor_power_splits( + dr_plasma_outboard_midplane_separatrix_separation=0.01, + len_plasma_sol_outboard_power_decay=0.005, + f_len_sol_power_decay_inboard=1.5, + ) + + assert result.f_p_div_inboard_separatrix == pytest.approx(0.4010068950189542) + assert result.f_p_div_outboard_separatrix == pytest.approx(0.5989931049810458) + assert result.f_p_div_inboard_lower_separatrix == pytest.approx(0.08365345781749392) + assert result.f_p_div_inboard_upper_separatrix == pytest.approx(0.31735343720146025) + assert result.f_p_div_outboard_lower_separatrix == pytest.approx(0.07140172838484167) + assert result.f_p_div_outboard_upper_separatrix == pytest.approx(0.527591376596204) + + assert ( + result.f_p_div_inboard_lower_separatrix + result.f_p_div_inboard_upper_separatrix + ) == pytest.approx(result.f_p_div_inboard_separatrix) + assert ( + result.f_p_div_outboard_lower_separatrix + + result.f_p_div_outboard_upper_separatrix + ) == pytest.approx(result.f_p_div_outboard_separatrix) + assert ( + result.f_p_div_inboard_separatrix + result.f_p_div_outboard_separatrix + ) == pytest.approx(1.0) From cb0dbff9609d8f49277ed6cb54c6258cae6fbc8e Mon Sep 17 00:00:00 2001 From: mn3981 Date: Mon, 17 Aug 2026 10:44:32 +0100 Subject: [PATCH 26/29] Refactor Brunner divertor power split calculations to include inboard power decay length and update related variables and tests --- process/core/input.py | 3 +++ process/core/io/plot/summary.py | 20 ++++++++++++---- process/data_structure/physics_variables.py | 8 ++++++- process/models/physics/exhaust.py | 24 ++++++++++--------- process/models/physics/physics.py | 7 +++--- process/models/physics/scrape_off_layer.py | 20 +++++++++++++++- .../models/physics/test_plasma_exhaust.py | 4 ++-- 7 files changed, 63 insertions(+), 23 deletions(-) diff --git a/process/core/input.py b/process/core/input.py index cfb2ccc553..600d953816 100644 --- a/process/core/input.py +++ b/process/core/input.py @@ -1181,6 +1181,9 @@ def bounds(self) -> tuple[NumberType | None, NumberType | None]: ), "i_len_sol_outboard_power_decay": InputVariable("physics", int, choices=range(4)), "len_sol_outboard_power_decay": InputVariable("physics", float, range=(0.0, 1.0)), + "f_len_sol_power_decay_inboard_outboard": InputVariable( + "physics", float, range=(0.01, 2.0) + ), } diff --git a/process/core/io/plot/summary.py b/process/core/io/plot/summary.py index e61ca772e0..8bd7ce1c3f 100644 --- a/process/core/io/plot/summary.py +++ b/process/core/io/plot/summary.py @@ -9097,11 +9097,18 @@ def plot_brunner_divertor_power_split_comparison_stackplot( """Plot Brunner divertor power split fractions as a stack plot over dr_sep.""" # Use the case decay length when available; fall back to 1 mm if absent. - len_plasma_sol_power_decay = mfile.get("len_sol_outboard_power_decay", scan=scan) + len_plasma_sol_outboard_power_decay = mfile.get( + "len_sol_outboard_power_decay", scan=scan + ) + len_plasma_sol_inboard_power_decay = mfile.get( + "len_sol_inboard_power_decay", scan=scan + ) colors = plt.cm.plasma(np.linspace(0.15, 0.85, 4)) dr_sep_values = np.linspace( - -5 * len_plasma_sol_power_decay, 5 * len_plasma_sol_power_decay, 200 + -5 * len_plasma_sol_outboard_power_decay, + 5 * len_plasma_sol_outboard_power_decay, + 200, ) f_p_inboard_lower = np.zeros_like(dr_sep_values) f_p_inboard_upper = np.zeros_like(dr_sep_values) @@ -9111,8 +9118,8 @@ def plot_brunner_divertor_power_split_comparison_stackplot( for idx, dr_sep in enumerate(dr_sep_values): div_power_splits = PlasmaExhaust().calculate_brunner_divertor_power_splits( dr_plasma_outboard_midplane_separatrix_separation=dr_sep, - len_plasma_sol_outboard_power_decay=len_plasma_sol_power_decay, - f_len_sol_power_decay_inboard=0.7, + len_plasma_sol_outboard_power_decay=len_plasma_sol_outboard_power_decay, + len_plasma_sol_inboard_power_decay=len_plasma_sol_inboard_power_decay, ) f_p_inboard_lower[idx] = div_power_splits.f_p_div_inboard_lower_separatrix f_p_inboard_upper[idx] = div_power_splits.f_p_div_inboard_upper_separatrix @@ -9143,7 +9150,10 @@ def plot_brunner_divertor_power_split_comparison_stackplot( alpha=0.5, ) axis.set_ylim([0.0, 1.0]) - axis.set_xlim([-5 * len_plasma_sol_power_decay, 5 * len_plasma_sol_power_decay]) + axis.set_xlim([ + -5 * len_plasma_sol_outboard_power_decay, + 5 * len_plasma_sol_outboard_power_decay, + ]) axis.grid(True, which="both", linestyle="--", linewidth=0.5, alpha=0.35) axis.set_title("Brunner Divertor Power Split Fractions") axis.set_xlabel("$\\Delta r_{\\mathrm{sep}}$ [m]") diff --git a/process/data_structure/physics_variables.py b/process/data_structure/physics_variables.py index 9cde8d19b8..11539af97a 100644 --- a/process/data_structure/physics_variables.py +++ b/process/data_structure/physics_variables.py @@ -430,8 +430,14 @@ class PhysicsData: t_energy_confinement_beta: float = 0.0 """[s]""" + f_len_sol_power_decay_inboard_outboard: float = 0.7 + """Ratio of inboard to outboard scrape off layer power decay length (λᵢₙ_q/λₒᵤₜ_q)""" + + len_sol_inboard_power_decay: float = 0.0 + """Inboard scrape off layer power decay length (λᵢₙ_q) [m]""" + len_sol_outboard_power_decay: float = 0.0 - """Outboard scrape off layer power decay length (λ_q) [m]""" + """Outboard scrape off layer power decay length (λₒᵤₜ_q) [m]""" dr_plasma_outboard_midplane_separatrix_separation: float = 0.0 """Radial distance between the first and second plasma separatrixes at the outer midplane (δR_sep) [m]""" diff --git a/process/models/physics/exhaust.py b/process/models/physics/exhaust.py index 35b405ab6e..43a64ce28f 100644 --- a/process/models/physics/exhaust.py +++ b/process/models/physics/exhaust.py @@ -251,7 +251,7 @@ def calculate_radiation_fraction( def calculate_brunner_divertor_power_splits( dr_plasma_outboard_midplane_separatrix_separation: float, len_plasma_sol_outboard_power_decay: float, - f_len_sol_power_decay_inboard: float, + len_plasma_sol_inboard_power_decay: float, ) -> DivertorSeparatrixPowerSplits: """ Calculate the power splits to the divertor targets using Brunner's method. @@ -262,8 +262,8 @@ def calculate_brunner_divertor_power_splits( Radial separation of the plasma outboard midplane separatrix (δR_sep) [m]. len_plasma_sol_outboard_power_decay : float Power decay length in the scrape-off layer (λ_q) [m]. - f_len_sol_power_decay_inboard : float - Fraction of the scrape-off layer power decay length for the inboard side [-]. + len_plasma_sol_inboard_power_decay : float + Power decay length in the scrape-off layer for the inboard side (λᵢₙ_q) [m]. Returns ------- @@ -273,7 +273,7 @@ def calculate_brunner_divertor_power_splits( Notes ----- - - The fitted value for `F_P_INNER_SEP_0` and `F_P_INNER_SEP_INFINITY` are taken + - The fitted value for `f_p_inner_sep_0` and `f_p_inner_sep_infinity` are taken from the fit given by Petrie et al. [2] References @@ -288,8 +288,8 @@ def calculate_brunner_divertor_power_splits( Mar. 2001, doi: https://doi.org/10.1016/S0022-3115(00)00492-X """ # Fraction of the power to the inner divertors at δR_sep = 0 and δR_sep → ∞ - F_P_INNER_SEP_0 = 0.16e0 - F_P_INNER_SEP_INFINITY = 0.41e0 + f_p_inner_sep_0 = 0.16e0 + f_p_inner_sep_infinity = 0.41e0 # Fractions of total outboard power going to each target # Outboard lower divertor @@ -315,12 +315,12 @@ def calculate_brunner_divertor_power_splits( 1 + np.exp( dr_plasma_outboard_midplane_separatrix_separation - / (len_plasma_sol_outboard_power_decay * f_len_sol_power_decay_inboard) + / (len_plasma_sol_inboard_power_decay) ) ) - f_p_total_inboard = F_P_INNER_SEP_0 + ( - F_P_INNER_SEP_0 - F_P_INNER_SEP_INFINITY + f_p_total_inboard = f_p_inner_sep_0 + ( + f_p_inner_sep_0 - f_p_inner_sep_infinity ) * ( 1.0e0 - ( @@ -363,14 +363,16 @@ def output_brunner_divertor_power_splits(self): po.ovarre( self.outfile, - "Requested fraction of power to the lower divertor in double null configuration", + "Requested fraction of power to the lower divertor in double null " + "configuration", "(f_p_div_lower_separatrix)", self.data.physics.f_p_div_lower_separatrix, "IP ", ) po.ovarre( self.outfile, - "Required distance between the first and second plasma separatrixes at the outer midplane (δR_sep) [m]", + "Required distance between the first and second plasma separatrixes at " + "the outer midplane (δR_sep) [m]", "(dr_plasma_outboard_midplane_separatrix_separation)", self.data.physics.dr_plasma_outboard_midplane_separatrix_separation, "OP ", diff --git a/process/models/physics/physics.py b/process/models/physics/physics.py index dcb3b3341a..c7382c7009 100644 --- a/process/models/physics/physics.py +++ b/process/models/physics/physics.py @@ -845,8 +845,9 @@ def run(self): ) # if double null configuration share the power - # over the upper and lower divertor, where self.data.physics.f_p_div_lower_separatrix gives - # the factor of power conducted to the lower divertor + # over the upper and lower divertor, where + # self.data.physics.f_p_div_lower_separatrix gives the factor of power conducted + # to the lower divertor if self.data.divertor.n_divertors == 2: self.data.physics.p_div_lower_separatrix_mw = ( self.data.physics.f_p_div_lower_separatrix @@ -1013,7 +1014,7 @@ def run(self): div_power_plits = self.exhaust.calculate_brunner_divertor_power_splits( dr_plasma_outboard_midplane_separatrix_separation=self.data.physics.dr_plasma_outboard_midplane_separatrix_separation, len_plasma_sol_outboard_power_decay=self.data.physics.len_sol_outboard_power_decay, - f_len_sol_power_decay_inboard=0.7, + len_plasma_sol_inboard_power_decay=self.data.physics.len_sol_inboard_power_decay, ) self.data.physics.f_p_div_inboard_separatrix = ( diff --git a/process/models/physics/scrape_off_layer.py b/process/models/physics/scrape_off_layer.py index 37fc3d06a6..4388d79914 100644 --- a/process/models/physics/scrape_off_layer.py +++ b/process/models/physics/scrape_off_layer.py @@ -71,6 +71,11 @@ def run(self): self.data.physics.len_plasma_sol_mast14_power_decay_2 ) + self.data.physics.len_sol_inboard_power_decay = ( + self.data.physics.f_len_sol_power_decay_inboard_outboard + * self.data.physics.len_sol_outboard_power_decay + ) + self.data.physics.a_plasma_outboard_sol_parallel = self.calculate_upstream_sol_outboard_parallel_area( # noqa: E501 rmajor=self.data.physics.rmajor, rminor=self.data.physics.rminor, @@ -105,7 +110,7 @@ def output(self) -> None: po.ovarre( self.outfile, - "Outboard SOL power decay length (λ_q) [m]", + "Outboard SOL power decay length (λₒᵤₜ_q) [m]", "(len_sol_outboard_power_decay)", self.data.physics.len_sol_outboard_power_decay, ) @@ -118,6 +123,19 @@ def output(self) -> None: + " ", ) po.oblnkl(self.outfile) + po.ovarre( + self.outfile, + "Inboard to outboard SOL power decay length ratio (λᵢₙ_q/λₒᵤₜ_q)", + "(f_len_sol_power_decay_inboard_outboard)", + self.data.physics.f_len_sol_power_decay_inboard_outboard, + ) + po.ovarre( + self.outfile, + "Inboard SOL power decay length (λᵢₙ_q) [m]", + "(len_sol_inboard_power_decay)", + self.data.physics.len_sol_inboard_power_decay, + ) + po.oblnkl(self.outfile) po.ovarre( self.outfile, "Eich 2013 SOL power decay length (λ_q) [m]", diff --git a/tests/unit/models/physics/test_plasma_exhaust.py b/tests/unit/models/physics/test_plasma_exhaust.py index f1ba958889..6dd34904f3 100644 --- a/tests/unit/models/physics/test_plasma_exhaust.py +++ b/tests/unit/models/physics/test_plasma_exhaust.py @@ -7,7 +7,7 @@ def test_calculate_brunner_divertor_power_splits_at_zero_separatrix_separation() result = PlasmaExhaust().calculate_brunner_divertor_power_splits( dr_plasma_outboard_midplane_separatrix_separation=0.0, len_plasma_sol_outboard_power_decay=0.01, - f_len_sol_power_decay_inboard=1.0, + len_plasma_sol_inboard_power_decay=0.01, ) assert result.f_p_div_inboard_separatrix == pytest.approx(0.16) @@ -22,7 +22,7 @@ def test_calculate_brunner_divertor_power_splits_asymmetric_case(): result = PlasmaExhaust().calculate_brunner_divertor_power_splits( dr_plasma_outboard_midplane_separatrix_separation=0.01, len_plasma_sol_outboard_power_decay=0.005, - f_len_sol_power_decay_inboard=1.5, + len_plasma_sol_inboard_power_decay=0.0075, ) assert result.f_p_div_inboard_separatrix == pytest.approx(0.4010068950189542) From 7769f8aa0baf9a7e9e827b0ef99c3aca7123d064 Mon Sep 17 00:00:00 2001 From: mn3981 Date: Mon, 17 Aug 2026 10:48:52 +0100 Subject: [PATCH 27/29] Add label for separation distance in Brunner divertor power split comparison plot --- process/core/io/plot/summary.py | 1 + 1 file changed, 1 insertion(+) diff --git a/process/core/io/plot/summary.py b/process/core/io/plot/summary.py index 8bd7ce1c3f..eb5b333bea 100644 --- a/process/core/io/plot/summary.py +++ b/process/core/io/plot/summary.py @@ -9148,6 +9148,7 @@ def plot_brunner_divertor_power_split_comparison_stackplot( linestyle="--", linewidth=1.0, alpha=0.5, + label="$\u0394 r_{\\mathrm{sep}}$", ) axis.set_ylim([0.0, 1.0]) axis.set_xlim([ From 84467c178944ccd633cfa6cadc762f7823a5ca0d Mon Sep 17 00:00:00 2001 From: mn3981 Date: Fri, 21 Aug 2026 10:17:48 +0100 Subject: [PATCH 28/29] Requested changes --- process/core/io/plot/summary.py | 22 +- process/models/physics/exhaust.py | 218 ++++++++---------- process/models/physics/physics.py | 2 +- .../models/physics/test_plasma_exhaust.py | 4 +- 4 files changed, 109 insertions(+), 137 deletions(-) diff --git a/process/core/io/plot/summary.py b/process/core/io/plot/summary.py index eb5b333bea..6dc7076265 100644 --- a/process/core/io/plot/summary.py +++ b/process/core/io/plot/summary.py @@ -9097,17 +9097,13 @@ def plot_brunner_divertor_power_split_comparison_stackplot( """Plot Brunner divertor power split fractions as a stack plot over dr_sep.""" # Use the case decay length when available; fall back to 1 mm if absent. - len_plasma_sol_outboard_power_decay = mfile.get( - "len_sol_outboard_power_decay", scan=scan - ) - len_plasma_sol_inboard_power_decay = mfile.get( - "len_sol_inboard_power_decay", scan=scan - ) + len_plasma_sol_outboard_pd = mfile.get("len_sol_outboard_power_decay", scan=scan) + len_plasma_sol_inboard_pd = mfile.get("len_sol_inboard_power_decay", scan=scan) colors = plt.cm.plasma(np.linspace(0.15, 0.85, 4)) dr_sep_values = np.linspace( - -5 * len_plasma_sol_outboard_power_decay, - 5 * len_plasma_sol_outboard_power_decay, + -5 * len_plasma_sol_outboard_pd, + 5 * len_plasma_sol_outboard_pd, 200, ) f_p_inboard_lower = np.zeros_like(dr_sep_values) @@ -9117,9 +9113,9 @@ def plot_brunner_divertor_power_split_comparison_stackplot( for idx, dr_sep in enumerate(dr_sep_values): div_power_splits = PlasmaExhaust().calculate_brunner_divertor_power_splits( - dr_plasma_outboard_midplane_separatrix_separation=dr_sep, - len_plasma_sol_outboard_power_decay=len_plasma_sol_outboard_power_decay, - len_plasma_sol_inboard_power_decay=len_plasma_sol_inboard_power_decay, + dr_outboard_midplane_sep=dr_sep, + len_plasma_sol_outboard_power_decay=len_plasma_sol_outboard_pd, + len_plasma_sol_inboard_power_decay=len_plasma_sol_inboard_pd, ) f_p_inboard_lower[idx] = div_power_splits.f_p_div_inboard_lower_separatrix f_p_inboard_upper[idx] = div_power_splits.f_p_div_inboard_upper_separatrix @@ -9152,8 +9148,8 @@ def plot_brunner_divertor_power_split_comparison_stackplot( ) axis.set_ylim([0.0, 1.0]) axis.set_xlim([ - -5 * len_plasma_sol_outboard_power_decay, - 5 * len_plasma_sol_outboard_power_decay, + -5 * len_plasma_sol_outboard_pd, + 5 * len_plasma_sol_outboard_pd, ]) axis.grid(True, which="both", linestyle="--", linewidth=0.5, alpha=0.35) axis.set_title("Brunner Divertor Power Split Fractions") diff --git a/process/models/physics/exhaust.py b/process/models/physics/exhaust.py index 43a64ce28f..b00a1401a9 100644 --- a/process/models/physics/exhaust.py +++ b/process/models/physics/exhaust.py @@ -249,7 +249,7 @@ def calculate_radiation_fraction( @staticmethod def calculate_brunner_divertor_power_splits( - dr_plasma_outboard_midplane_separatrix_separation: float, + dr_outboard_midplane_sep: float, len_plasma_sol_outboard_power_decay: float, len_plasma_sol_inboard_power_decay: float, ) -> DivertorSeparatrixPowerSplits: @@ -258,7 +258,7 @@ def calculate_brunner_divertor_power_splits( Parameters ---------- - dr_plasma_outboard_midplane_separatrix_separation : float + dr_outboard_midplane_sep : float Radial separation of the plasma outboard midplane separatrix (δR_sep) [m]. len_plasma_sol_outboard_power_decay : float Power decay length in the scrape-off layer (λ_q) [m]. @@ -294,29 +294,17 @@ def calculate_brunner_divertor_power_splits( # Fractions of total outboard power going to each target # Outboard lower divertor f_p_outboard_lower = 1 / ( - 1 - + np.exp( - dr_plasma_outboard_midplane_separatrix_separation - / len_plasma_sol_outboard_power_decay - ) + 1 + np.exp(dr_outboard_midplane_sep / len_plasma_sol_outboard_power_decay) ) # Outboard upper divertor f_p_outboard_upper = 1 / ( - 1 - + np.exp( - -dr_plasma_outboard_midplane_separatrix_separation - / len_plasma_sol_outboard_power_decay - ) + 1 + np.exp(-dr_outboard_midplane_sep / len_plasma_sol_outboard_power_decay) ) # Fractions of the total inboard power going to each target f_p_inboard_lower = 1 / ( - 1 - + np.exp( - dr_plasma_outboard_midplane_separatrix_separation - / (len_plasma_sol_inboard_power_decay) - ) + 1 + np.exp(dr_outboard_midplane_sep / (len_plasma_sol_inboard_power_decay)) ) f_p_total_inboard = f_p_inner_sep_0 + ( @@ -330,7 +318,7 @@ def calculate_brunner_divertor_power_splits( + np.exp( -( ( - dr_plasma_outboard_midplane_separatrix_separation + dr_outboard_midplane_sep / len_plasma_sol_outboard_power_decay ) ** 2 @@ -361,80 +349,68 @@ def output_brunner_divertor_power_splits(self): if self.data.stellarator.istell == 0: po.osubhd(self.outfile, "Brunner Divertor Power Splits:") - po.ovarre( - self.outfile, - "Requested fraction of power to the lower divertor in double null " - "configuration", - "(f_p_div_lower_separatrix)", - self.data.physics.f_p_div_lower_separatrix, - "IP ", - ) - po.ovarre( - self.outfile, - "Required distance between the first and second plasma separatrixes at " - "the outer midplane (δR_sep) [m]", - "(dr_plasma_outboard_midplane_separatrix_separation)", - self.data.physics.dr_plasma_outboard_midplane_separatrix_separation, - "OP ", - ) - po.oblnkl(self.outfile) - - po.ovarre( - self.outfile, - "Outboard side heat flux decay length (m)", - "(len_sol_outboard_power_decay)", - self.data.physics.len_sol_outboard_power_decay, - "OP ", - ) - po.oblnkl(self.outfile) - - po.ovarre( - self.outfile, - "Fraction of separatrix power on the inner target(s)", - "(f_p_div_inboard_separatrix)", - self.data.physics.f_p_div_inboard_separatrix, - "OP ", - ) - po.ovarre( - self.outfile, - "Fraction of separatrix power on the outer target(s)", - "(f_p_div_outboard_separatrix)", - self.data.physics.f_p_div_outboard_separatrix, - "OP ", - ) - - po.oblnkl(self.outfile) - - po.ovarre( - self.outfile, - "Fraction of separatrix power on the inner lower target", - "(f_p_div_lower_inboard_separatrix)", - self.data.physics.f_p_div_lower_inboard_separatrix, - "OP ", - ) - po.ovarre( - self.outfile, - "Separatrix power on the inner lower target", - "(p_div_lower_inboard_separatrix_mw)", - self.data.physics.p_div_lower_inboard_separatrix_mw, - "OP ", - ) - po.oblnkl(self.outfile) - - po.ovarre( - self.outfile, - "Fraction of separatrix power on the outer lower target", - "(f_p_div_lower_outboard_separatrix)", - self.data.physics.f_p_div_lower_outboard_separatrix, - "OP ", - ) - po.ovarre( - self.outfile, - "Separatrix power on the outer lower target", - "(p_div_lower_outboard_separatrix_mw)", - self.data.physics.p_div_lower_outboard_separatrix_mw, - "OP ", - ) + for op in [ + ( + ( + "Requested fraction of power to the lower divertor in double " + "null configuration" + ), + "(f_p_div_lower_separatrix)", + self.data.physics.f_p_div_lower_separatrix, + ), + ( + ( + "Required distance between the first and second plasma " + "separatrixes at the outer midplane (δR_sep) [m]" + ), + "(dr_plasma_outboard_midplane_separatrix_separation)", + self.data.physics.dr_plasma_outboard_midplane_separatrix_separation, + ), + None, + ( + "Outboard side heat flux decay length (m)", + "(len_sol_outboard_power_decay)", + self.data.physics.len_sol_outboard_power_decay, + ), + None, + ( + "Fraction of separatrix power on the inner target(s)", + "(f_p_div_inboard_separatrix)", + self.data.physics.f_p_div_inboard_separatrix, + ), + ( + "Fraction of separatrix power on the outer target(s)", + "(f_p_div_outboard_separatrix)", + self.data.physics.f_p_div_outboard_separatrix, + ), + None, + ( + "Fraction of separatrix power on the inner lower target", + "(f_p_div_lower_inboard_separatrix)", + self.data.physics.f_p_div_lower_inboard_separatrix, + ), + ( + "Separatrix power on the inner lower target", + "(p_div_lower_inboard_separatrix_mw)", + self.data.physics.p_div_lower_inboard_separatrix_mw, + ), + None, + ( + "Fraction of separatrix power on the outer lower target", + "(f_p_div_lower_outboard_separatrix)", + self.data.physics.f_p_div_lower_outboard_separatrix, + ), + ( + "Separatrix power on the outer lower target", + "(p_div_lower_outboard_separatrix_mw)", + self.data.physics.p_div_lower_outboard_separatrix_mw, + ), + ]: + if op is None: + po.oblnkl(self.outfile) + else: + desc, var, val = op + po.ovarre(self.outfile, desc, var, val, "OP ") if ( DivertorNumberModels(self.data.physics.i_single_null) @@ -443,32 +419,32 @@ def output_brunner_divertor_power_splits(self): po.oblnkl(self.outfile) po.ocmmnt(self.outfile, "----------------------------") po.oblnkl(self.outfile) - po.ovarre( - self.outfile, - "Fraction of separatrix power on the inner upper target", - "(f_p_div_upper_inboard_separatrix)", - self.data.physics.f_p_div_upper_inboard_separatrix, - "OP ", - ) - po.ovarre( - self.outfile, - "Separatrix power on the inner upper target", - "(p_div_upper_inboard_separatrix_mw)", - self.data.physics.p_div_upper_inboard_separatrix_mw, - "OP ", - ) - po.oblnkl(self.outfile) - po.ovarre( - self.outfile, - "Fraction of separatrix power on the outer upper target", - "(f_p_div_upper_outboard_separatrix)", - self.data.physics.f_p_div_upper_outboard_separatrix, - "OP ", - ) - po.ovarre( - self.outfile, - "Separatrix power on the outer upper target", - "(p_div_upper_outboard_separatrix_mw)", - self.data.physics.p_div_upper_outboard_separatrix_mw, - "OP ", - ) + + for op in [ + ( + "Fraction of separatrix power on the inner upper target", + "(f_p_div_upper_inboard_separatrix)", + self.data.physics.f_p_div_upper_inboard_separatrix, + ), + ( + "Separatrix power on the inner upper target", + "(p_div_upper_inboard_separatrix_mw)", + self.data.physics.p_div_upper_inboard_separatrix_mw, + ), + None, + ( + "Fraction of separatrix power on the outer upper target", + "(f_p_div_upper_outboard_separatrix)", + self.data.physics.f_p_div_upper_outboard_separatrix, + ), + ( + "Separatrix power on the outer upper target", + "(p_div_upper_outboard_separatrix_mw)", + self.data.physics.p_div_upper_outboard_separatrix_mw, + ), + ]: + if op is None: + po.oblnkl(self.outfile) + else: + desc, var, val = op + po.ovarre(self.outfile, desc, var, val, "OP ") diff --git a/process/models/physics/physics.py b/process/models/physics/physics.py index c7382c7009..51d79ea832 100644 --- a/process/models/physics/physics.py +++ b/process/models/physics/physics.py @@ -1012,7 +1012,7 @@ def run(self): ) div_power_plits = self.exhaust.calculate_brunner_divertor_power_splits( - dr_plasma_outboard_midplane_separatrix_separation=self.data.physics.dr_plasma_outboard_midplane_separatrix_separation, + dr_outboard_midplane_sep=self.data.physics.dr_plasma_outboard_midplane_separatrix_separation, len_plasma_sol_outboard_power_decay=self.data.physics.len_sol_outboard_power_decay, len_plasma_sol_inboard_power_decay=self.data.physics.len_sol_inboard_power_decay, ) diff --git a/tests/unit/models/physics/test_plasma_exhaust.py b/tests/unit/models/physics/test_plasma_exhaust.py index 6dd34904f3..bf57499a2c 100644 --- a/tests/unit/models/physics/test_plasma_exhaust.py +++ b/tests/unit/models/physics/test_plasma_exhaust.py @@ -5,7 +5,7 @@ def test_calculate_brunner_divertor_power_splits_at_zero_separatrix_separation(): result = PlasmaExhaust().calculate_brunner_divertor_power_splits( - dr_plasma_outboard_midplane_separatrix_separation=0.0, + dr_outboard_midplane_sep=0.0, len_plasma_sol_outboard_power_decay=0.01, len_plasma_sol_inboard_power_decay=0.01, ) @@ -20,7 +20,7 @@ def test_calculate_brunner_divertor_power_splits_at_zero_separatrix_separation() def test_calculate_brunner_divertor_power_splits_asymmetric_case(): result = PlasmaExhaust().calculate_brunner_divertor_power_splits( - dr_plasma_outboard_midplane_separatrix_separation=0.01, + dr_outboard_midplane_sep=0.01, len_plasma_sol_outboard_power_decay=0.005, len_plasma_sol_inboard_power_decay=0.0075, ) From 3fb4d6e2e62777e27dff739e7dea927b6b32a8b9 Mon Sep 17 00:00:00 2001 From: mn3981 Date: Fri, 21 Aug 2026 14:25:46 +0100 Subject: [PATCH 29/29] Move heat split method out of Exhaust class --- process/core/io/plot/summary.py | 4 +- process/models/physics/exhaust.py | 189 +++++++++--------- process/models/physics/physics.py | 3 +- .../models/physics/test_plasma_exhaust.py | 8 +- 4 files changed, 101 insertions(+), 103 deletions(-) diff --git a/process/core/io/plot/summary.py b/process/core/io/plot/summary.py index 6dc7076265..887eea4f09 100644 --- a/process/core/io/plot/summary.py +++ b/process/core/io/plot/summary.py @@ -67,7 +67,7 @@ ElectronCyclotron, ) from process.models.physics.density_limit import DensityLimitModel -from process.models.physics.exhaust import PlasmaExhaust +from process.models.physics.exhaust import calculate_brunner_divertor_power_splits from process.models.physics.impurity_radiation import read_impurity_file from process.models.physics.l_h_transition import PlasmaConfinementTransitionModel from process.models.physics.physics import ( @@ -9112,7 +9112,7 @@ def plot_brunner_divertor_power_split_comparison_stackplot( f_p_outboard_upper = np.zeros_like(dr_sep_values) for idx, dr_sep in enumerate(dr_sep_values): - div_power_splits = PlasmaExhaust().calculate_brunner_divertor_power_splits( + div_power_splits = calculate_brunner_divertor_power_splits( dr_outboard_midplane_sep=dr_sep, len_plasma_sol_outboard_power_decay=len_plasma_sol_outboard_pd, len_plasma_sol_inboard_power_decay=len_plasma_sol_inboard_pd, diff --git a/process/models/physics/exhaust.py b/process/models/physics/exhaust.py index b00a1401a9..95a12fee60 100644 --- a/process/models/physics/exhaust.py +++ b/process/models/physics/exhaust.py @@ -247,103 +247,6 @@ def calculate_radiation_fraction( return p_plasma_rad_mw / p_plasma_heating_mw - @staticmethod - def calculate_brunner_divertor_power_splits( - dr_outboard_midplane_sep: float, - len_plasma_sol_outboard_power_decay: float, - len_plasma_sol_inboard_power_decay: float, - ) -> DivertorSeparatrixPowerSplits: - """ - Calculate the power splits to the divertor targets using Brunner's method. - - Parameters - ---------- - dr_outboard_midplane_sep : float - Radial separation of the plasma outboard midplane separatrix (δR_sep) [m]. - len_plasma_sol_outboard_power_decay : float - Power decay length in the scrape-off layer (λ_q) [m]. - len_plasma_sol_inboard_power_decay : float - Power decay length in the scrape-off layer for the inboard side (λᵢₙ_q) [m]. - - Returns - ------- - DivertorSeparatrixPowerSplits - Dataclass containing the fractions of total separatrix power to each - divertor target. - - Notes - ----- - - The fitted value for `f_p_inner_sep_0` and `f_p_inner_sep_infinity` are taken - from the fit given by Petrie et al. [2] - - References - ---------- - [1] D. Brunner, A. Q. Kuang, B. LaBombard, and J. L. Terry, “The dependence of - divertor power sharing on magnetic flux balance in near double-null - configurations on Alcator C-Mod,” Nuclear Fusion, vol. 58, no. 7, p. 076010, - May 2018, doi: https://doi.org/10.1088/1741-4326/aac006. - - [2] T. W. Petrie et al., “The effect of divertor magnetic balance on H-mode - performance in DIII-D,” Journal of Nuclear Materials, vol. 290-293, pp. 935-939, - Mar. 2001, doi: https://doi.org/10.1016/S0022-3115(00)00492-X - """ - # Fraction of the power to the inner divertors at δR_sep = 0 and δR_sep → ∞ - f_p_inner_sep_0 = 0.16e0 - f_p_inner_sep_infinity = 0.41e0 - - # Fractions of total outboard power going to each target - # Outboard lower divertor - f_p_outboard_lower = 1 / ( - 1 + np.exp(dr_outboard_midplane_sep / len_plasma_sol_outboard_power_decay) - ) - - # Outboard upper divertor - f_p_outboard_upper = 1 / ( - 1 + np.exp(-dr_outboard_midplane_sep / len_plasma_sol_outboard_power_decay) - ) - - # Fractions of the total inboard power going to each target - f_p_inboard_lower = 1 / ( - 1 + np.exp(dr_outboard_midplane_sep / (len_plasma_sol_inboard_power_decay)) - ) - - f_p_total_inboard = f_p_inner_sep_0 + ( - f_p_inner_sep_0 - f_p_inner_sep_infinity - ) * ( - 1.0e0 - - ( - 2.0e0 - / ( - 1.0e0 - + np.exp( - -( - ( - dr_outboard_midplane_sep - / len_plasma_sol_outboard_power_decay - ) - ** 2 - ) - ) - ) - ) - ) - - f_p_total_outboard = 1.0e0 - f_p_total_inboard - - f_p_inboard_upper = f_p_total_inboard * (1.0e0 - f_p_inboard_lower) - f_p_outboard_upper = f_p_total_outboard * (1.0e0 - f_p_outboard_lower) - f_p_inboard_lower = f_p_total_inboard * f_p_inboard_lower - f_p_outboard_lower = f_p_total_outboard * f_p_outboard_lower - - return DivertorSeparatrixPowerSplits( - f_p_div_inboard_separatrix=f_p_total_inboard, - f_p_div_outboard_separatrix=f_p_total_outboard, - f_p_div_inboard_lower_separatrix=f_p_inboard_lower, - f_p_div_inboard_upper_separatrix=f_p_inboard_upper, - f_p_div_outboard_lower_separatrix=f_p_outboard_lower, - f_p_div_outboard_upper_separatrix=f_p_outboard_upper, - ) - def output_brunner_divertor_power_splits(self): """Output the Brunner divertor power splits to the output file.""" if self.data.stellarator.istell == 0: @@ -448,3 +351,95 @@ def output_brunner_divertor_power_splits(self): else: desc, var, val = op po.ovarre(self.outfile, desc, var, val, "OP ") + + +def calculate_brunner_divertor_power_splits( + dr_outboard_midplane_sep: float, + len_plasma_sol_outboard_power_decay: float, + len_plasma_sol_inboard_power_decay: float, +) -> DivertorSeparatrixPowerSplits: + """ + Calculate the power splits to the divertor targets using Brunner's method. + + Parameters + ---------- + dr_outboard_midplane_sep : float + Radial separation of the plasma outboard midplane separatrix (δR_sep) [m]. + len_plasma_sol_outboard_power_decay : float + Power decay length in the scrape-off layer (λ_q) [m]. + len_plasma_sol_inboard_power_decay : float + Power decay length in the scrape-off layer for the inboard side (λᵢₙ_q) [m]. + + Returns + ------- + DivertorSeparatrixPowerSplits + Dataclass containing the fractions of total separatrix power to each + divertor target. + + Notes + ----- + - The fitted value for `f_p_inner_sep_0` and `f_p_inner_sep_infinity` are taken + from the fit given by Petrie et al. [2] + + References + ---------- + [1] D. Brunner, A. Q. Kuang, B. LaBombard, and J. L. Terry, “The dependence of + divertor power sharing on magnetic flux balance in near double-null + configurations on Alcator C-Mod,” Nuclear Fusion, vol. 58, no. 7, p. 076010, + May 2018, doi: https://doi.org/10.1088/1741-4326/aac006. + + [2] T. W. Petrie et al., “The effect of divertor magnetic balance on H-mode + performance in DIII-D,” Journal of Nuclear Materials, vol. 290-293, pp. 935-939, + Mar. 2001, doi: https://doi.org/10.1016/S0022-3115(00)00492-X + """ + # Fraction of the power to the inner divertors at δR_sep = 0 and δR_sep → ∞ + f_p_inner_sep_0 = 0.16e0 + f_p_inner_sep_infinity = 0.41e0 + + # Fractions of total outboard power going to each target + # Outboard lower divertor + f_p_outboard_lower = 1 / ( + 1 + np.exp(dr_outboard_midplane_sep / len_plasma_sol_outboard_power_decay) + ) + + # Outboard upper divertor + f_p_outboard_upper = 1 / ( + 1 + np.exp(-dr_outboard_midplane_sep / len_plasma_sol_outboard_power_decay) + ) + + # Fractions of the total inboard power going to each target + f_p_inboard_lower = 1 / ( + 1 + np.exp(dr_outboard_midplane_sep / (len_plasma_sol_inboard_power_decay)) + ) + + f_p_total_inboard = f_p_inner_sep_0 + (f_p_inner_sep_0 - f_p_inner_sep_infinity) * ( + 1.0e0 + - ( + 2.0e0 + / ( + 1.0e0 + + np.exp( + -( + (dr_outboard_midplane_sep / len_plasma_sol_outboard_power_decay) + ** 2 + ) + ) + ) + ) + ) + + f_p_total_outboard = 1.0e0 - f_p_total_inboard + + f_p_inboard_upper = f_p_total_inboard * (1.0e0 - f_p_inboard_lower) + f_p_outboard_upper = f_p_total_outboard * (1.0e0 - f_p_outboard_lower) + f_p_inboard_lower = f_p_total_inboard * f_p_inboard_lower + f_p_outboard_lower = f_p_total_outboard * f_p_outboard_lower + + return DivertorSeparatrixPowerSplits( + f_p_div_inboard_separatrix=f_p_total_inboard, + f_p_div_outboard_separatrix=f_p_total_outboard, + f_p_div_inboard_lower_separatrix=f_p_inboard_lower, + f_p_div_inboard_upper_separatrix=f_p_inboard_upper, + f_p_div_outboard_lower_separatrix=f_p_outboard_lower, + f_p_div_outboard_upper_separatrix=f_p_outboard_upper, + ) diff --git a/process/models/physics/physics.py b/process/models/physics/physics.py index 51d79ea832..a198f75358 100644 --- a/process/models/physics/physics.py +++ b/process/models/physics/physics.py @@ -24,6 +24,7 @@ ) from process.models.physics import impurity_radiation from process.models.physics.bootstrap_current import BootstrapCurrentFractionModel +from process.models.physics.exhaust import calculate_brunner_divertor_power_splits from process.models.physics.profiles import ( DensityProfilePedestalType, PlasmaProfileShapeType, @@ -1011,7 +1012,7 @@ def run(self): ) ) - div_power_plits = self.exhaust.calculate_brunner_divertor_power_splits( + div_power_plits = calculate_brunner_divertor_power_splits( dr_outboard_midplane_sep=self.data.physics.dr_plasma_outboard_midplane_separatrix_separation, len_plasma_sol_outboard_power_decay=self.data.physics.len_sol_outboard_power_decay, len_plasma_sol_inboard_power_decay=self.data.physics.len_sol_inboard_power_decay, diff --git a/tests/unit/models/physics/test_plasma_exhaust.py b/tests/unit/models/physics/test_plasma_exhaust.py index bf57499a2c..1bce228473 100644 --- a/tests/unit/models/physics/test_plasma_exhaust.py +++ b/tests/unit/models/physics/test_plasma_exhaust.py @@ -1,10 +1,12 @@ import pytest -from process.models.physics.exhaust import PlasmaExhaust +from process.models.physics.exhaust import ( + calculate_brunner_divertor_power_splits, +) def test_calculate_brunner_divertor_power_splits_at_zero_separatrix_separation(): - result = PlasmaExhaust().calculate_brunner_divertor_power_splits( + result = calculate_brunner_divertor_power_splits( dr_outboard_midplane_sep=0.0, len_plasma_sol_outboard_power_decay=0.01, len_plasma_sol_inboard_power_decay=0.01, @@ -19,7 +21,7 @@ def test_calculate_brunner_divertor_power_splits_at_zero_separatrix_separation() def test_calculate_brunner_divertor_power_splits_asymmetric_case(): - result = PlasmaExhaust().calculate_brunner_divertor_power_splits( + result = calculate_brunner_divertor_power_splits( dr_outboard_midplane_sep=0.01, len_plasma_sol_outboard_power_decay=0.005, len_plasma_sol_inboard_power_decay=0.0075,