From 759fa7f4af408efb66e2d6997811a0dde21209d7 Mon Sep 17 00:00:00 2001 From: mn3981 Date: Tue, 28 Apr 2026 10:06:42 +0100 Subject: [PATCH 01/18] Add calculation for outboard blanket poloidal angle subtended by plasma Co-authored-by: Copilot --- process/models/blankets/blanket_library.py | 48 ++++++++++++++++++++++ process/models/blankets/hcpb.py | 10 +++++ 2 files changed, 58 insertions(+) diff --git a/process/models/blankets/blanket_library.py b/process/models/blankets/blanket_library.py index 98c5c4109..22e22e417 100644 --- a/process/models/blankets/blanket_library.py +++ b/process/models/blankets/blanket_library.py @@ -656,6 +656,13 @@ def output_blkt_volumes_and_areas(self): "(a_blkt_total_surface_full_coverage)", self.data.build.a_blkt_total_surface_full_coverage, ) + po.oblnkl(self.outfile) + po.ovarre( + self.outfile, + "Outboard blanket poloidal angle subtended by plasma (degrees)", + "(deg_blkt_outboard_poloidal_plasma)", + blanket_library.deg_blkt_outboard_poloidal_plasma, + ) def primary_coolant_properties(self, output: bool): """Calculates the fluid properties of the Primary Coolant in the FW and BZ. @@ -3466,6 +3473,47 @@ def calculate_basic_geometry(self): b_bz_liq=self.data.fwbs.b_bz_liq, ) + @staticmethod + def calculate_blkt_outboard_poloidal_plasma_angle( + rminor: float, + dr_blkt_outboard: float, + dz_blkt_half: float, + dr_fw_plasma_gap_outboard: float, + dr_fw_outboard: float, + ) -> float: + """Calculate the poloidal angle subtended by the outboard blanket at the plasma mid-plane. + + Parameters + ---------- + rminor : + Plasma minor radius (m). + dr_blkt_outboard : + Radial thickness of outboard blanket (m). + dz_blkt_half : + Vertical half-height of outboard blanket (m). + dr_fw_plasma_gap_outboard : + Outboard first wall to plasma gap (m). + dr_fw_outboard : + Radial thickness of outboard first wall (m). + + Returns + ------- + deg_blkt_outboard_poloidal_plasma : + Poloidal angle subtended by outboard blanket at plasma mid-plane (degrees). + """ + return np.degrees( + 2.0 + * np.arctan( + dz_blkt_half + / ( + rminor + + dr_blkt_outboard + + dr_fw_plasma_gap_outboard + + dr_fw_outboard + ) + ) + ) + def calculate_blanket_outboard_module_geometry( self, n_blkt_outboard_modules_toroidal: int, diff --git a/process/models/blankets/hcpb.py b/process/models/blankets/hcpb.py index 455990b8e..0fff8d38c 100644 --- a/process/models/blankets/hcpb.py +++ b/process/models/blankets/hcpb.py @@ -46,6 +46,16 @@ def run(self, output: bool = False): # Calculate blanket, shield, vacuum vessel and cryostat volumes self.component_volumes() + blanket_vars.deg_blkt_outboard_poloidal_plasma = ( + self.calculate_blkt_outboard_poloidal_plasma_angle( + rminor=physics_variables.rminor, + dr_blkt_outboard=build_variables.dr_blkt_outboard, + dz_blkt_half=blanket_vars.dz_blkt_half, + dr_fw_plasma_gap_outboard=build_variables.dr_fw_plasma_gap_outboard, + dr_fw_outboard=build_variables.dr_fw_outboard, + ) + ) + dia_blkt_channel = self.pipe_hydraulic_diameter(i_channel_shape=1) self.data.fwbs.radius_blkt_channel = dia_blkt_channel / 2 ( From efc8084bc607844faf1591d2612aaec5a3228c5b Mon Sep 17 00:00:00 2001 From: mn3981 Date: Tue, 28 Apr 2026 10:58:09 +0100 Subject: [PATCH 02/18] Add poloidal angle visualization for blanket structure in plot Co-authored-by: Copilot --- process/core/io/plot/summary.py | 101 ++++++++++++++++++++++++++++---- 1 file changed, 88 insertions(+), 13 deletions(-) diff --git a/process/core/io/plot/summary.py b/process/core/io/plot/summary.py index bce8c6173..2c8cc7e58 100644 --- a/process/core/io/plot/summary.py +++ b/process/core/io/plot/summary.py @@ -13832,14 +13832,36 @@ def plot_blkt_structure( ): """Plot the BLKT structure on the given axis.""" rmajor = m_file.get("rmajor", scan=scan) + rminor = m_file.get("rminor", scan=scan) + dr_fw_plasma_gap_outboard = m_file.get("dr_fw_plasma_gap_outboard", scan=scan) + dr_fw_plasma_gap_inboard = m_file.get("dr_fw_plasma_gap_inboard", scan=scan) + dr_fw_inboard = m_file.get("dr_fw_inboard", scan=scan) + dr_fw_outboard = m_file.get("dr_fw_outboard", scan=scan) + dr_blkt_outboard = m_file.get("dr_blkt_outboard", scan=scan) + dr_blkt_inboard = m_file.get("dr_blkt_inboard", scan=scan) + dz_blkt_half = m_file.get("dz_blkt_half", scan=scan) + deg_blkt_outboard_poloidal_plasma = m_file.get( + "deg_blkt_outboard_poloidal_plasma", scan=scan + ) plot_blanket(ax, m_file, scan, radial_build, colour_scheme) + plot_plasma(ax, m_file, scan, colour_scheme) plot_firstwall(ax, m_file, scan, radial_build, colour_scheme) ax.set_xlabel("Radial position [m]") ax.set_ylabel("Vertical position [m]") ax.set_title("Blanket and First Wall Poloidal Cross-Section") ax.minorticks_on() ax.grid(which="minor", linestyle=":", linewidth=0.5, alpha=0.5) + + r_blkt_outboard_out = ( + rmajor + rminor + dr_fw_outboard + dr_fw_plasma_gap_outboard + dr_blkt_outboard + ) + r_blkt_inboard_in = ( + rmajor - rminor - dr_fw_plasma_gap_inboard - dr_fw_inboard - dr_blkt_inboard + ) + r_fw_outboard_in = r_blkt_outboard_out - dr_blkt_outboard - dr_fw_outboard + r_fw_inboard_out = r_blkt_inboard_in + dr_blkt_inboard + dr_fw_inboard + # Plot major radius line (vertical dashed line at rmajor) ax.axvline( m_file.get("rminor", scan=scan), @@ -13849,7 +13871,6 @@ def plot_blkt_structure( label="Major Radius $R_0$", ) # Plot a horizontal line at dz_blkt_half (blanket half height) - dz_blkt_half = m_file.get("dz_blkt_half", scan=scan) ax.axhline( dz_blkt_half, color="purple", @@ -13867,23 +13888,77 @@ def plot_blkt_structure( ax.annotate( "", - xy=(rmajor, dz_blkt_half), - xytext=(rmajor, -dz_blkt_half), - arrowprops={"arrowstyle": "<->", "color": "black"}, + xy=(rmajor, 0), + xytext=(r_blkt_outboard_out, dz_blkt_half), + arrowprops={"arrowstyle": "<-", "color": "purple"}, ) - # Add a label for the internal coil width + ax.annotate( + "", + xy=(rmajor, 0), + xytext=(r_blkt_outboard_out, -dz_blkt_half), + arrowprops={"arrowstyle": "<-", "color": "purple"}, + ) + + # Plot arc showing the angle between the two arrows + arc_radius = 1.0 + angle_start = -deg_blkt_outboard_poloidal_plasma / 2 + angle_end = deg_blkt_outboard_poloidal_plasma / 2 + + theta = np.linspace(np.deg2rad(angle_start), np.deg2rad(angle_end), 50) + arc_x = rmajor + arc_radius * np.cos(theta) + arc_y = arc_radius * np.sin(theta) + + ax.plot(arc_x, arc_y, color="purple", linewidth=2) + + # Add angle label at the arc + mid_angle = np.deg2rad((angle_start + angle_end) / 2) + label_radius = arc_radius * 1.5 + label_x = rmajor + label_radius * np.cos(mid_angle) + label_y = label_radius * np.sin(mid_angle) + ax.text( + label_x, + label_y, + f"{deg_blkt_outboard_poloidal_plasma:.1f}°", + fontsize=10, + color="purple", + ha="center", + va="center", + weight="bold", + ) + + ax.annotate( + "", + xy=(rmajor, 0), + xytext=(r_blkt_inboard_in, dz_blkt_half), + arrowprops={"arrowstyle": "<-", "color": "purple"}, + ) + + ax.annotate( + "", + xy=(rmajor, 0), + xytext=(r_blkt_inboard_in, -dz_blkt_half), + arrowprops={"arrowstyle": "<-", "color": "purple"}, + ) + + # Plot vertical lines at the inner and outer radial boundaries of the blanket + ax.axvline( + r_blkt_inboard_in, color="black", linestyle="--", linewidth=1.5, zorder=10 + ) + ax.axvline( + r_blkt_outboard_out, color="black", linestyle="--", linewidth=1.5, zorder=10 + ) + ax.axvline(r_fw_inboard_out, color="black", linestyle="--", linewidth=1.5, zorder=10) + + ax.axvline(r_fw_outboard_in, color="black", linestyle="--", linewidth=1.5, zorder=10) + + ax.axvline( rmajor, - 0.0, - f"{2 * dz_blkt_half:.3f} m", - fontsize=7, color="black", - rotation=270, - verticalalignment="center", - horizontalalignment="center", - bbox={"boxstyle": "round", "facecolor": "pink", "alpha": 1.0}, - zorder=101, # Ensure label is on top of all plots + linestyle="--", + linewidth=1.5, + label="Major Radius $R_0$", ) # Plot midplane line (horizontal dashed line at Z=0) From 936de2e92bd44f1b0d04fb16ff29328334704b76 Mon Sep 17 00:00:00 2001 From: mn3981 Date: Tue, 28 Apr 2026 11:34:05 +0100 Subject: [PATCH 03/18] Add calculation and visualization for inboard blanket poloidal angle Co-authored-by: Copilot --- process/core/io/plot/summary.py | 53 ++++++++++++++++++++-- process/models/blankets/blanket_library.py | 34 ++++++++++++++ process/models/blankets/hcpb.py | 8 ++++ 3 files changed, 91 insertions(+), 4 deletions(-) diff --git a/process/core/io/plot/summary.py b/process/core/io/plot/summary.py index 2c8cc7e58..4a750a786 100644 --- a/process/core/io/plot/summary.py +++ b/process/core/io/plot/summary.py @@ -13843,6 +13843,9 @@ def plot_blkt_structure( deg_blkt_outboard_poloidal_plasma = m_file.get( "deg_blkt_outboard_poloidal_plasma", scan=scan ) + deg_blkt_inboard_poloidal_plasma = m_file.get( + "deg_blkt_inboard_poloidal_plasma", scan=scan + ) plot_blanket(ax, m_file, scan, radial_build, colour_scheme) plot_plasma(ax, m_file, scan, colour_scheme) @@ -13913,7 +13916,7 @@ def plot_blkt_structure( # Add angle label at the arc mid_angle = np.deg2rad((angle_start + angle_end) / 2) - label_radius = arc_radius * 1.5 + label_radius = arc_radius * 1.8 label_x = rmajor + label_radius * np.cos(mid_angle) label_y = label_radius * np.sin(mid_angle) @@ -13921,27 +13924,69 @@ def plot_blkt_structure( label_x, label_y, f"{deg_blkt_outboard_poloidal_plasma:.1f}°", - fontsize=10, + fontsize=8, color="purple", ha="center", va="center", weight="bold", + bbox={ + "boxstyle": "round", + "facecolor": "white", + "alpha": 0.8, + "edgecolor": "purple", + "linewidth": 1.5, + }, ) ax.annotate( "", xy=(rmajor, 0), - xytext=(r_blkt_inboard_in, dz_blkt_half), + xytext=(r_fw_inboard_out, dz_blkt_half), arrowprops={"arrowstyle": "<-", "color": "purple"}, ) ax.annotate( "", xy=(rmajor, 0), - xytext=(r_blkt_inboard_in, -dz_blkt_half), + xytext=(r_fw_inboard_out, -dz_blkt_half), arrowprops={"arrowstyle": "<-", "color": "purple"}, ) + # Plot arc showing the angle between the two arrows + arc_radius = 1.0 + angle_start = -deg_blkt_inboard_poloidal_plasma / 2 + angle_end = deg_blkt_inboard_poloidal_plasma / 2 + + theta = np.linspace(np.deg2rad(angle_start), np.deg2rad(angle_end), 50) + arc_x = rmajor - arc_radius * np.cos(theta) + arc_y = arc_radius * np.sin(theta) + + ax.plot(arc_x, arc_y, color="purple", linewidth=2) + + # Add angle label at the arc + mid_angle = np.deg2rad((angle_start + angle_end) / 2) + label_radius = arc_radius * 1.8 + label_x = rmajor - label_radius * np.cos(mid_angle) + label_y = label_radius * np.sin(mid_angle) + + ax.text( + label_x, + label_y, + f"{deg_blkt_inboard_poloidal_plasma:.1f}°", + fontsize=8, + color="purple", + ha="center", + va="center", + weight="bold", + bbox={ + "boxstyle": "round", + "facecolor": "white", + "alpha": 0.8, + "edgecolor": "purple", + "linewidth": 1.5, + }, + ) + # Plot vertical lines at the inner and outer radial boundaries of the blanket ax.axvline( r_blkt_inboard_in, color="black", linestyle="--", linewidth=1.5, zorder=10 diff --git a/process/models/blankets/blanket_library.py b/process/models/blankets/blanket_library.py index 22e22e417..4c2c9eab4 100644 --- a/process/models/blankets/blanket_library.py +++ b/process/models/blankets/blanket_library.py @@ -663,6 +663,12 @@ def output_blkt_volumes_and_areas(self): "(deg_blkt_outboard_poloidal_plasma)", blanket_library.deg_blkt_outboard_poloidal_plasma, ) + po.ovarre( + self.outfile, + "Inboard blanket poloidal angle subtended by plasma (degrees)", + "(deg_blkt_inboard_poloidal_plasma)", + blanket_library.deg_blkt_inboard_poloidal_plasma, + ) def primary_coolant_properties(self, output: bool): """Calculates the fluid properties of the Primary Coolant in the FW and BZ. @@ -3561,6 +3567,34 @@ def calculate_basic_geometry(self): self.set_blanket_module_geometry() + @staticmethod + def calculate_blkt_inboard_poloidal_plasma_angle( + rminor: float, + dz_blkt_half: float, + dr_fw_plasma_gap_inboard: float, + ) -> float: + """Calculate the poloidal angle subtended by the inboard blanket at the plasma mid-plane. + + Angle is taken from the FW surface + + Parameters + ---------- + rminor : + Plasma minor radius (m). + dz_blkt_half : + Vertical half-height of inboard blanket (m). + dr_fw_plasma_gap_inboard : + Inboard first wall to plasma gap (m). + + Returns + ------- + deg_blkt_inboard_poloidal_plasma : + Poloidal angle subtended by inboard blanket at plasma mid-plane (degrees). + """ + return np.degrees( + 2.0 * np.arctan(dz_blkt_half / (rminor + dr_fw_plasma_gap_inboard)) + ) + def calculate_blanket_inboard_module_geometry( self, n_blkt_inboard_modules_toroidal: int, diff --git a/process/models/blankets/hcpb.py b/process/models/blankets/hcpb.py index 0fff8d38c..35c300709 100644 --- a/process/models/blankets/hcpb.py +++ b/process/models/blankets/hcpb.py @@ -56,6 +56,14 @@ def run(self, output: bool = False): ) ) + blanket_vars.deg_blkt_inboard_poloidal_plasma = ( + self.calculate_blkt_inboard_poloidal_plasma_angle( + rminor=physics_variables.rminor, + dz_blkt_half=blanket_vars.dz_blkt_half, + dr_fw_plasma_gap_inboard=build_variables.dr_fw_plasma_gap_inboard, + ) + ) + dia_blkt_channel = self.pipe_hydraulic_diameter(i_channel_shape=1) self.data.fwbs.radius_blkt_channel = dia_blkt_channel / 2 ( From 2e1d87eaba1f6f6e15b0954797575699a3899a8c Mon Sep 17 00:00:00 2001 From: mn3981 Date: Tue, 28 Apr 2026 11:59:36 +0100 Subject: [PATCH 04/18] Add outboard blanket poloidal angle calculation and update plot annotations Co-authored-by: Copilot --- process/core/io/plot/summary.py | 18 +++++++++++------- process/models/blankets/blanket_library.py | 5 +++++ process/models/blankets/hcpb.py | 9 ++------- 3 files changed, 18 insertions(+), 14 deletions(-) diff --git a/process/core/io/plot/summary.py b/process/core/io/plot/summary.py index 4a750a786..df8324dc3 100644 --- a/process/core/io/plot/summary.py +++ b/process/core/io/plot/summary.py @@ -13892,15 +13892,17 @@ def plot_blkt_structure( ax.annotate( "", xy=(rmajor, 0), - xytext=(r_blkt_outboard_out, dz_blkt_half), + xytext=(rmajor, dz_blkt_half), arrowprops={"arrowstyle": "<-", "color": "purple"}, + zorder=5, ) ax.annotate( "", xy=(rmajor, 0), - xytext=(r_blkt_outboard_out, -dz_blkt_half), + xytext=(rmajor, -dz_blkt_half), arrowprops={"arrowstyle": "<-", "color": "purple"}, + zorder=5, ) # Plot arc showing the angle between the two arrows @@ -13942,14 +13944,16 @@ def plot_blkt_structure( "", xy=(rmajor, 0), xytext=(r_fw_inboard_out, dz_blkt_half), - arrowprops={"arrowstyle": "<-", "color": "purple"}, + arrowprops={"arrowstyle": "<-", "color": "green"}, + zorder=5, ) ax.annotate( "", xy=(rmajor, 0), xytext=(r_fw_inboard_out, -dz_blkt_half), - arrowprops={"arrowstyle": "<-", "color": "purple"}, + arrowprops={"arrowstyle": "<-", "color": "green"}, + zorder=5, ) # Plot arc showing the angle between the two arrows @@ -13961,7 +13965,7 @@ def plot_blkt_structure( arc_x = rmajor - arc_radius * np.cos(theta) arc_y = arc_radius * np.sin(theta) - ax.plot(arc_x, arc_y, color="purple", linewidth=2) + ax.plot(arc_x, arc_y, color="green", linewidth=2) # Add angle label at the arc mid_angle = np.deg2rad((angle_start + angle_end) / 2) @@ -13974,7 +13978,7 @@ def plot_blkt_structure( label_y, f"{deg_blkt_inboard_poloidal_plasma:.1f}°", fontsize=8, - color="purple", + color="green", ha="center", va="center", weight="bold", @@ -13982,7 +13986,7 @@ def plot_blkt_structure( "boxstyle": "round", "facecolor": "white", "alpha": 0.8, - "edgecolor": "purple", + "edgecolor": "green", "linewidth": 1.5, }, ) diff --git a/process/models/blankets/blanket_library.py b/process/models/blankets/blanket_library.py index 4c2c9eab4..6e3e498a8 100644 --- a/process/models/blankets/blanket_library.py +++ b/process/models/blankets/blanket_library.py @@ -3479,6 +3479,11 @@ def calculate_basic_geometry(self): b_bz_liq=self.data.fwbs.b_bz_liq, ) + @property + def blkt_outboard_poloidal_plasma_angle(self) -> float: + """Calculate the poloidal angle subtended by the outboard blanket at the plasma mid-plane.""" + return 180.0 + @staticmethod def calculate_blkt_outboard_poloidal_plasma_angle( rminor: float, diff --git a/process/models/blankets/hcpb.py b/process/models/blankets/hcpb.py index 35c300709..85704d620 100644 --- a/process/models/blankets/hcpb.py +++ b/process/models/blankets/hcpb.py @@ -46,14 +46,9 @@ def run(self, output: bool = False): # Calculate blanket, shield, vacuum vessel and cryostat volumes self.component_volumes() + # If Shfranov shift is added, the angle formula can be used where the shift is added to the minor radius. For now, the shift is neglected and the angle is calculated using the minor radius only. blanket_vars.deg_blkt_outboard_poloidal_plasma = ( - self.calculate_blkt_outboard_poloidal_plasma_angle( - rminor=physics_variables.rminor, - dr_blkt_outboard=build_variables.dr_blkt_outboard, - dz_blkt_half=blanket_vars.dz_blkt_half, - dr_fw_plasma_gap_outboard=build_variables.dr_fw_plasma_gap_outboard, - dr_fw_outboard=build_variables.dr_fw_outboard, - ) + self.blkt_outboard_poloidal_plasma_angle ) blanket_vars.deg_blkt_inboard_poloidal_plasma = ( From 0f98735c2882cd333365cd60353dfb54ff2cb12e Mon Sep 17 00:00:00 2001 From: mn3981 Date: Tue, 28 Apr 2026 12:05:01 +0100 Subject: [PATCH 05/18] Add divertor poloidal angle subtended by plasma variable Co-authored-by: Copilot --- process/data_structure/divertor_variables.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/process/data_structure/divertor_variables.py b/process/data_structure/divertor_variables.py index cca7a800a..6ae42bd41 100644 --- a/process/data_structure/divertor_variables.py +++ b/process/data_structure/divertor_variables.py @@ -78,5 +78,8 @@ class DivertorData: n_divertors: int = 2 """Number of divertors (calculated from `i_single_null`)""" +deg_div_poloidal_plasma: float = None +"""Divertor poloidal angle subtended by plasma (degrees)""" + CREATE_DICTS_FROM_DATACLASS = DivertorData From 36f4c41a177bcbd2e8415c12ace26d9eea17cc71 Mon Sep 17 00:00:00 2001 From: mn3981 Date: Tue, 28 Apr 2026 12:30:30 +0100 Subject: [PATCH 06/18] Add calculation and visualization for divertor poloidal angle subtended by plasma Co-authored-by: Copilot --- process/core/io/plot/summary.py | 36 +++++++++++++++++++++++++++++++++ process/models/build.py | 14 +++++++++++++ process/models/divertor.py | 18 +++++++++++++++++ 3 files changed, 68 insertions(+) diff --git a/process/core/io/plot/summary.py b/process/core/io/plot/summary.py index df8324dc3..c37b7a5e8 100644 --- a/process/core/io/plot/summary.py +++ b/process/core/io/plot/summary.py @@ -13846,6 +13846,7 @@ def plot_blkt_structure( deg_blkt_inboard_poloidal_plasma = m_file.get( "deg_blkt_inboard_poloidal_plasma", scan=scan ) + deg_div_poloidal_plasma = m_file.get("deg_div_poloidal_plasma", scan=scan) plot_blanket(ax, m_file, scan, radial_build, colour_scheme) plot_plasma(ax, m_file, scan, colour_scheme) @@ -13991,6 +13992,41 @@ def plot_blkt_structure( }, ) + # Plot arc showing the angle between the two arrows (divertor angle) + arc_radius = 1.5 + angle_start = -deg_blkt_outboard_poloidal_plasma / 2 - deg_div_poloidal_plasma + angle_end = -deg_blkt_inboard_poloidal_plasma / 2 - deg_div_poloidal_plasma + + theta = np.linspace(np.deg2rad(angle_start), np.deg2rad(angle_end), 50) + arc_x = rmajor + arc_radius * np.cos(theta) + arc_y = arc_radius * np.sin(theta) + + ax.plot(arc_x, arc_y, color="black", linewidth=2) + + # Add angle label at the arc + mid_angle = np.deg2rad((angle_start + angle_end) / 2) + label_radius = arc_radius * 1.8 + label_x = rmajor + label_radius * np.cos(mid_angle) + label_y = label_radius * np.sin(mid_angle) + + ax.text( + label_x, + label_y, + f"{deg_div_poloidal_plasma:.1f}°", + fontsize=8, + color="black", + ha="center", + va="center", + weight="bold", + bbox={ + "boxstyle": "round", + "facecolor": "white", + "alpha": 0.8, + "edgecolor": "black", + "linewidth": 1.5, + }, + ) + # Plot vertical lines at the inner and outer radial boundaries of the blanket ax.axvline( r_blkt_inboard_in, color="black", linestyle="--", linewidth=1.5, zorder=10 diff --git a/process/models/build.py b/process/models/build.py index c9e4077a1..eac57c6fa 100644 --- a/process/models/build.py +++ b/process/models/build.py @@ -1142,6 +1142,13 @@ def divgeom(self, output: bool): divht, "OP ", ) + po.ovarrf( + self.outfile, + "Divertor poloidal angle subtended by plasma (degrees)", + "(deg_div_poloidal_plasma)", + divertor_variables.deg_div_poloidal_plasma, + "OP ", + ) elif self.data.divertor.n_divertors == 2: po.oheadr(self.outfile, "Divertor build and plasma position") @@ -1452,6 +1459,13 @@ def divgeom(self, output: bool): divht, "OP ", ) + po.ovarrf( + self.outfile, + "Divertor poloidal angle subtended by plasma (degrees)", + "(deg_div_poloidal_plasma)", + divertor_variables.deg_div_poloidal_plasma, + "OP ", + ) else: po.oheadr(self.outfile, "Divertor build and plasma position") po.ocmmnt( diff --git a/process/models/divertor.py b/process/models/divertor.py index 0b8cffca7..8968ed0bf 100644 --- a/process/models/divertor.py +++ b/process/models/divertor.py @@ -6,6 +6,7 @@ from process.core import process_output as po from process.core.exceptions import ProcessValueError from process.core.model import Model +from process.data_structure import blanket_library from process.data_structure import physics_variables as pv from process.data_structure import tfcoil_variables as tfv @@ -35,6 +36,8 @@ def run(self, output: bool = False): output : indicate whether output should be written to the output file, or not """ + dv.deg_div_poloidal_plasma = self.single_divertor_angle + self.data.fwbs.p_div_nuclear_heat_total_mw = self.incident_neutron_power( p_plasma_neutron_mw=pv.p_plasma_neutron_mw, f_ster_div_single=self.data.fwbs.f_ster_div_single, @@ -85,6 +88,21 @@ def run(self, output: bool = False): ) return + @property + def single_divertor_angle(self): + """ + Calculate the angle subtended by a single divertor. + Angle is calculated as 360 degrees minus the sum of the inboard + and outboard blanket poloidal angles, divided by 2 (for two divertors). + """ + return ( + 360.0 + - ( + blanket_library.deg_blkt_inboard_poloidal_plasma + + blanket_library.deg_blkt_outboard_poloidal_plasma + ) + ) / 2.0 + def divtart( self, rmajor: float, From d20afe04716f0cab7086543d52cb92125d310c15 Mon Sep 17 00:00:00 2001 From: mn3981 Date: Tue, 28 Apr 2026 12:42:58 +0100 Subject: [PATCH 07/18] Add divertor poloidal angle output and calculation updates Co-authored-by: Copilot --- process/models/build.py | 14 -------------- process/models/divertor.py | 1 + process/models/physics/physics.py | 7 +++++++ 3 files changed, 8 insertions(+), 14 deletions(-) diff --git a/process/models/build.py b/process/models/build.py index eac57c6fa..c9e4077a1 100644 --- a/process/models/build.py +++ b/process/models/build.py @@ -1142,13 +1142,6 @@ def divgeom(self, output: bool): divht, "OP ", ) - po.ovarrf( - self.outfile, - "Divertor poloidal angle subtended by plasma (degrees)", - "(deg_div_poloidal_plasma)", - divertor_variables.deg_div_poloidal_plasma, - "OP ", - ) elif self.data.divertor.n_divertors == 2: po.oheadr(self.outfile, "Divertor build and plasma position") @@ -1459,13 +1452,6 @@ def divgeom(self, output: bool): divht, "OP ", ) - po.ovarrf( - self.outfile, - "Divertor poloidal angle subtended by plasma (degrees)", - "(deg_div_poloidal_plasma)", - divertor_variables.deg_div_poloidal_plasma, - "OP ", - ) else: po.oheadr(self.outfile, "Divertor build and plasma position") po.ocmmnt( diff --git a/process/models/divertor.py b/process/models/divertor.py index 8968ed0bf..8328ec486 100644 --- a/process/models/divertor.py +++ b/process/models/divertor.py @@ -37,6 +37,7 @@ def run(self, output: bool = False): indicate whether output should be written to the output file, or not """ dv.deg_div_poloidal_plasma = self.single_divertor_angle + fwbs.f_ster_div_single = dv.deg_div_poloidal_plasma / 360.0 self.data.fwbs.p_div_nuclear_heat_total_mw = self.incident_neutron_power( p_plasma_neutron_mw=pv.p_plasma_neutron_mw, diff --git a/process/models/physics/physics.py b/process/models/physics/physics.py index 851d6abe5..1ed4bce32 100644 --- a/process/models/physics/physics.py +++ b/process/models/physics/physics.py @@ -2095,6 +2095,13 @@ def outplas(self): physics_variables.ptarmw, "OP ", ) + po.ovarrf( + self.outfile, + "Divertor poloidal angle subtended by plasma (degrees)", + "(deg_div_poloidal_plasma)", + divertor_variables.deg_div_poloidal_plasma, + "OP ", + ) po.ovarre( self.outfile, "Fraction of power to the lower divertor", From e1e8b501cc6ca3b5634c9a905448c379a4628e04 Mon Sep 17 00:00:00 2001 From: mn3981 Date: Tue, 28 Apr 2026 12:50:39 +0100 Subject: [PATCH 08/18] Remove major radius line plotting from blanket structure visualization Co-authored-by: Copilot --- process/core/io/plot/summary.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/process/core/io/plot/summary.py b/process/core/io/plot/summary.py index c37b7a5e8..d423407ca 100644 --- a/process/core/io/plot/summary.py +++ b/process/core/io/plot/summary.py @@ -13866,14 +13866,6 @@ def plot_blkt_structure( r_fw_outboard_in = r_blkt_outboard_out - dr_blkt_outboard - dr_fw_outboard r_fw_inboard_out = r_blkt_inboard_in + dr_blkt_inboard + dr_fw_inboard - # Plot major radius line (vertical dashed line at rmajor) - ax.axvline( - m_file.get("rminor", scan=scan), - color="black", - linestyle="--", - linewidth=1.5, - label="Major Radius $R_0$", - ) # Plot a horizontal line at dz_blkt_half (blanket half height) ax.axhline( dz_blkt_half, From 16187913a89723f8443ad6532f04b12764e2c855 Mon Sep 17 00:00:00 2001 From: mn3981 Date: Tue, 28 Apr 2026 13:07:12 +0100 Subject: [PATCH 09/18] Add calculations and output for blanket poloidal angles subtended by plasma Co-authored-by: Copilot --- process/core/io/plot/summary.py | 13 ++++++++++--- process/models/blankets/blanket_library.py | 17 +++++++++++++++++ process/models/blankets/hcpb.py | 7 +++++++ 3 files changed, 34 insertions(+), 3 deletions(-) diff --git a/process/core/io/plot/summary.py b/process/core/io/plot/summary.py index d423407ca..7ddbdbfaa 100644 --- a/process/core/io/plot/summary.py +++ b/process/core/io/plot/summary.py @@ -13846,7 +13846,14 @@ def plot_blkt_structure( deg_blkt_inboard_poloidal_plasma = m_file.get( "deg_blkt_inboard_poloidal_plasma", scan=scan ) + f_deg_blkt_outboard_poloidal_plasma = m_file.get( + "f_deg_blkt_outboard_poloidal_plasma", scan=scan + ) + f_deg_blkt_inboard_poloidal_plasma = m_file.get( + "f_deg_blkt_inboard_poloidal_plasma", scan=scan + ) deg_div_poloidal_plasma = m_file.get("deg_div_poloidal_plasma", scan=scan) + f_ster_div_single = m_file.get("f_ster_div_single", scan=scan) plot_blanket(ax, m_file, scan, radial_build, colour_scheme) plot_plasma(ax, m_file, scan, colour_scheme) @@ -13918,7 +13925,7 @@ def plot_blkt_structure( ax.text( label_x, label_y, - f"{deg_blkt_outboard_poloidal_plasma:.1f}°", + f"{deg_blkt_outboard_poloidal_plasma:.1f}°\n({f_deg_blkt_outboard_poloidal_plasma * 100:.1f}%)", fontsize=8, color="purple", ha="center", @@ -13969,7 +13976,7 @@ def plot_blkt_structure( ax.text( label_x, label_y, - f"{deg_blkt_inboard_poloidal_plasma:.1f}°", + f"{deg_blkt_inboard_poloidal_plasma:.1f}°\n({f_deg_blkt_inboard_poloidal_plasma * 100:.1f}%)", fontsize=8, color="green", ha="center", @@ -14004,7 +14011,7 @@ def plot_blkt_structure( ax.text( label_x, label_y, - f"{deg_div_poloidal_plasma:.1f}°", + f"{deg_div_poloidal_plasma:.1f}°\n({f_ster_div_single * 100:.1f}%)", fontsize=8, color="black", ha="center", diff --git a/process/models/blankets/blanket_library.py b/process/models/blankets/blanket_library.py index 6e3e498a8..e1a5abe65 100644 --- a/process/models/blankets/blanket_library.py +++ b/process/models/blankets/blanket_library.py @@ -663,12 +663,24 @@ def output_blkt_volumes_and_areas(self): "(deg_blkt_outboard_poloidal_plasma)", blanket_library.deg_blkt_outboard_poloidal_plasma, ) + po.ovarre( + self.outfile, + "Angle fraction of outboard blanket poloidal angle subtended by plasma", + "(f_deg_blkt_outboard_poloidal_plasma)", + blanket_library.f_deg_blkt_outboard_poloidal_plasma, + ) po.ovarre( self.outfile, "Inboard blanket poloidal angle subtended by plasma (degrees)", "(deg_blkt_inboard_poloidal_plasma)", blanket_library.deg_blkt_inboard_poloidal_plasma, ) + po.ovarre( + self.outfile, + "Angle fraction of inboard blanket poloidal angle subtended by plasma", + "(f_deg_blkt_inboard_poloidal_plasma)", + blanket_library.f_deg_blkt_inboard_poloidal_plasma, + ) def primary_coolant_properties(self, output: bool): """Calculates the fluid properties of the Primary Coolant in the FW and BZ. @@ -3484,6 +3496,11 @@ def blkt_outboard_poloidal_plasma_angle(self) -> float: """Calculate the poloidal angle subtended by the outboard blanket at the plasma mid-plane.""" return 180.0 + @property + def f_deg_blkt_outboard_poloidal_plasma(self) -> float: + """Calculate the poloidal angle subtended by the outboard blanket at the plasma mid-plane.""" + return self.blkt_outboard_poloidal_plasma_angle / 360.0 + @staticmethod def calculate_blkt_outboard_poloidal_plasma_angle( rminor: float, diff --git a/process/models/blankets/hcpb.py b/process/models/blankets/hcpb.py index 85704d620..9a0c45c59 100644 --- a/process/models/blankets/hcpb.py +++ b/process/models/blankets/hcpb.py @@ -50,6 +50,9 @@ def run(self, output: bool = False): blanket_vars.deg_blkt_outboard_poloidal_plasma = ( self.blkt_outboard_poloidal_plasma_angle ) + blanket_vars.f_deg_blkt_outboard_poloidal_plasma = ( + self.f_deg_blkt_outboard_poloidal_plasma + ) blanket_vars.deg_blkt_inboard_poloidal_plasma = ( self.calculate_blkt_inboard_poloidal_plasma_angle( @@ -59,6 +62,10 @@ def run(self, output: bool = False): ) ) + blanket_vars.f_deg_blkt_inboard_poloidal_plasma = ( + blanket_vars.deg_blkt_inboard_poloidal_plasma / 360.0 + ) + dia_blkt_channel = self.pipe_hydraulic_diameter(i_channel_shape=1) self.data.fwbs.radius_blkt_channel = dia_blkt_channel / 2 ( From 7d968f454fd98cdb2ada32c53a0c61390bde7751 Mon Sep 17 00:00:00 2001 From: mn3981 Date: Tue, 28 Apr 2026 13:12:01 +0100 Subject: [PATCH 10/18] Adjust font sizes and z-order for blanket and divertor angle labels in structure plot Co-authored-by: Copilot --- process/core/io/plot/summary.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/process/core/io/plot/summary.py b/process/core/io/plot/summary.py index 7ddbdbfaa..28368e06c 100644 --- a/process/core/io/plot/summary.py +++ b/process/core/io/plot/summary.py @@ -13926,7 +13926,7 @@ def plot_blkt_structure( label_x, label_y, f"{deg_blkt_outboard_poloidal_plasma:.1f}°\n({f_deg_blkt_outboard_poloidal_plasma * 100:.1f}%)", - fontsize=8, + fontsize=7, color="purple", ha="center", va="center", @@ -13977,7 +13977,7 @@ def plot_blkt_structure( label_x, label_y, f"{deg_blkt_inboard_poloidal_plasma:.1f}°\n({f_deg_blkt_inboard_poloidal_plasma * 100:.1f}%)", - fontsize=8, + fontsize=7, color="green", ha="center", va="center", @@ -13989,6 +13989,7 @@ def plot_blkt_structure( "edgecolor": "green", "linewidth": 1.5, }, + zorder=5, ) # Plot arc showing the angle between the two arrows (divertor angle) @@ -14012,7 +14013,7 @@ def plot_blkt_structure( label_x, label_y, f"{deg_div_poloidal_plasma:.1f}°\n({f_ster_div_single * 100:.1f}%)", - fontsize=8, + fontsize=7, color="black", ha="center", va="center", @@ -14024,6 +14025,7 @@ def plot_blkt_structure( "edgecolor": "black", "linewidth": 1.5, }, + zorder=5, ) # Plot vertical lines at the inner and outer radial boundaries of the blanket From 3739636e3adf83d1d46639abcc933ee92bbd53f8 Mon Sep 17 00:00:00 2001 From: mn3981 Date: Tue, 28 Apr 2026 13:45:50 +0100 Subject: [PATCH 11/18] Enhance BLKT structure plotting by adding poloidal angle annotations for outboard, inboard, and divertor angles Co-authored-by: Copilot --- process/core/io/plot/summary.py | 57 ++++++++++++++++++++++++++++++--- 1 file changed, 53 insertions(+), 4 deletions(-) diff --git a/process/core/io/plot/summary.py b/process/core/io/plot/summary.py index 28368e06c..743e9bc2f 100644 --- a/process/core/io/plot/summary.py +++ b/process/core/io/plot/summary.py @@ -13830,7 +13830,8 @@ def plot_blkt_structure( radial_build: dict[str, float], colour_scheme: Literal[1, 2], ): - """Plot the BLKT structure on the given axis.""" + """Plot the blkt structure and relevant angles""" + # MFILE variables needed to plot the blkt structure and angles rmajor = m_file.get("rmajor", scan=scan) rminor = m_file.get("rminor", scan=scan) dr_fw_plasma_gap_outboard = m_file.get("dr_fw_plasma_gap_outboard", scan=scan) @@ -13854,10 +13855,14 @@ def plot_blkt_structure( ) deg_div_poloidal_plasma = m_file.get("deg_div_poloidal_plasma", scan=scan) f_ster_div_single = m_file.get("f_ster_div_single", scan=scan) + i_single_null = m_file.get("i_single_null", scan=scan) + + # ====================== plot_blanket(ax, m_file, scan, radial_build, colour_scheme) plot_plasma(ax, m_file, scan, colour_scheme) plot_firstwall(ax, m_file, scan, radial_build, colour_scheme) + ax.set_xlabel("Radial position [m]") ax.set_ylabel("Vertical position [m]") ax.set_title("Blanket and First Wall Poloidal Cross-Section") @@ -13889,6 +13894,7 @@ def plot_blkt_structure( label="Blanket Half Height", ) + # Plot arrows for the outboard blanket angles ax.annotate( "", xy=(rmajor, 0), @@ -13905,7 +13911,7 @@ def plot_blkt_structure( zorder=5, ) - # Plot arc showing the angle between the two arrows + # Plot arc showing the angle between the two outboard blanket arrows arc_radius = 1.0 angle_start = -deg_blkt_outboard_poloidal_plasma / 2 angle_end = deg_blkt_outboard_poloidal_plasma / 2 @@ -13922,6 +13928,7 @@ def plot_blkt_structure( label_x = rmajor + label_radius * np.cos(mid_angle) label_y = label_radius * np.sin(mid_angle) + # Plot the info box for the outboard blanket ax.text( label_x, label_y, @@ -13940,6 +13947,7 @@ def plot_blkt_structure( }, ) + # Plot arrows for the inboard blanket angles ax.annotate( "", xy=(rmajor, 0), @@ -13956,7 +13964,7 @@ def plot_blkt_structure( zorder=5, ) - # Plot arc showing the angle between the two arrows + # Plot arc showing the angle between the two inboard blanket arrows arc_radius = 1.0 angle_start = -deg_blkt_inboard_poloidal_plasma / 2 angle_end = deg_blkt_inboard_poloidal_plasma / 2 @@ -13973,6 +13981,7 @@ def plot_blkt_structure( label_x = rmajor - label_radius * np.cos(mid_angle) label_y = label_radius * np.sin(mid_angle) + # Plot the info box for the inboard blanket ax.text( label_x, label_y, @@ -13992,7 +14001,46 @@ def plot_blkt_structure( zorder=5, ) - # Plot arc showing the angle between the two arrows (divertor angle) + # Plot arrows for the divertor angles + # If double null then plot the upper also + if i_single_null == 0: + # Plot arc showing the angle between the two arrows (divertor angle) + arc_radius = 1.5 + angle_start = deg_blkt_outboard_poloidal_plasma / 2 + deg_div_poloidal_plasma + angle_end = deg_blkt_inboard_poloidal_plasma / 2 + deg_div_poloidal_plasma + + theta = np.linspace(np.deg2rad(angle_start), np.deg2rad(angle_end), 50) + arc_x = rmajor + arc_radius * np.cos(theta) + arc_y = arc_radius * np.sin(theta) + + ax.plot(arc_x, arc_y, color="black", linewidth=2) + + # Add angle label at the arc + mid_angle = np.deg2rad((angle_start + angle_end) / 2) + label_radius = arc_radius * 1.8 + label_x = rmajor + label_radius * np.cos(mid_angle) + label_y = label_radius * np.sin(mid_angle) + + ax.text( + label_x, + label_y, + f"{deg_div_poloidal_plasma:.1f}°\n({f_ster_div_single * 100:.1f}%)", + fontsize=7, + color="black", + ha="center", + va="center", + weight="bold", + bbox={ + "boxstyle": "round", + "facecolor": "white", + "alpha": 0.8, + "edgecolor": "black", + "linewidth": 1.5, + }, + zorder=5, + ) + + # Plot arc showing the angle between the two arrows for the lower divertor (divertor angle) arc_radius = 1.5 angle_start = -deg_blkt_outboard_poloidal_plasma / 2 - deg_div_poloidal_plasma angle_end = -deg_blkt_inboard_poloidal_plasma / 2 - deg_div_poloidal_plasma @@ -14009,6 +14057,7 @@ def plot_blkt_structure( label_x = rmajor + label_radius * np.cos(mid_angle) label_y = label_radius * np.sin(mid_angle) + # Plot the info box for the lower divertor angle ax.text( label_x, label_y, From 159e16c27cfa10b86fffeec50f0b8357ae6d0f75 Mon Sep 17 00:00:00 2001 From: mn3981 Date: Tue, 28 Apr 2026 14:49:04 +0100 Subject: [PATCH 12/18] Fix calculation of single divertor angle to use inboard blanket poloidal angle Co-authored-by: Copilot --- process/models/divertor.py | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/process/models/divertor.py b/process/models/divertor.py index 8328ec486..38fb3d812 100644 --- a/process/models/divertor.py +++ b/process/models/divertor.py @@ -93,16 +93,10 @@ def run(self, output: bool = False): def single_divertor_angle(self): """ Calculate the angle subtended by a single divertor. - Angle is calculated as 360 degrees minus the sum of the inboard - and outboard blanket poloidal angles, divided by 2 (for two divertors). + Angle is calculated as 180 degrees minus the inboard + blanket poloidal angle, divided by 2 (for two divertors). """ - return ( - 360.0 - - ( - blanket_library.deg_blkt_inboard_poloidal_plasma - + blanket_library.deg_blkt_outboard_poloidal_plasma - ) - ) / 2.0 + return (180.0 - blanket_library.deg_blkt_inboard_poloidal_plasma) / 2.0 def divtart( self, From c99c5705fa2610d3c88630595d8c4c6bb8079376 Mon Sep 17 00:00:00 2001 From: mn3981 Date: Tue, 28 Apr 2026 14:53:39 +0100 Subject: [PATCH 13/18] Add unit test for calculating inboard blanket poloidal plasma angle Co-authored-by: Copilot --- .../models/blankets/test_blanket_library.py | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/tests/unit/models/blankets/test_blanket_library.py b/tests/unit/models/blankets/test_blanket_library.py index 3375df1c0..123b72359 100644 --- a/tests/unit/models/blankets/test_blanket_library.py +++ b/tests/unit/models/blankets/test_blanket_library.py @@ -6,6 +6,11 @@ from process.data_structure import ( physics_variables, ) +from process.data_structure import build_variables as bv +from process.data_structure import fwbs_variables as fwbs +from process.data_structure import physics_variables as pv +from process.models.blankets.blanket_library import BlanketLibrary, InboardBlanket +from process.models.fw import FirstWall @pytest.fixture @@ -1758,3 +1763,48 @@ def test_liquid_breeder_properties_part_3(monkeypatch, blanket_library): blanket_library.liquid_breeder_properties() assert pytest.approx(blanket_library.data.fwbs.den_liq, rel=1e-3) == 504.0 + + +class CalculateBlktInboardPoloidalPlasmaAngleParam(NamedTuple): + rminor: Any = None + dz_blkt_half: Any = None + dr_fw_plasma_gap_inboard: Any = None + expected_angle: Any = None + + +@pytest.mark.parametrize( + "calculate_blkt_inboard_poloidal_plasma_angle_param", + [ + CalculateBlktInboardPoloidalPlasmaAngleParam( + rminor=2.0, + dz_blkt_half=0.0, + dr_fw_plasma_gap_inboard=0.25, + expected_angle=0.0, + ), + CalculateBlktInboardPoloidalPlasmaAngleParam( + rminor=2.0, + dz_blkt_half=2.25, # dz = rminor + gap -> 2 * atan(1) = 90 deg + dr_fw_plasma_gap_inboard=0.25, + expected_angle=90.0, + ), + CalculateBlktInboardPoloidalPlasmaAngleParam( + rminor=2.0, + dz_blkt_half=np.sqrt(3.0) * 2.25, # dz = sqrt(3) * (rminor + gap) -> 120 deg + dr_fw_plasma_gap_inboard=0.25, + expected_angle=120.0, + ), + ], +) +def test_calculate_blkt_inboard_poloidal_plasma_angle( + calculate_blkt_inboard_poloidal_plasma_angle_param, +): + """Test for calculate_blkt_inboard_poloidal_plasma_angle.""" + result = InboardBlanket.calculate_blkt_inboard_poloidal_plasma_angle( + rminor=calculate_blkt_inboard_poloidal_plasma_angle_param.rminor, + dz_blkt_half=calculate_blkt_inboard_poloidal_plasma_angle_param.dz_blkt_half, + dr_fw_plasma_gap_inboard=calculate_blkt_inboard_poloidal_plasma_angle_param.dr_fw_plasma_gap_inboard, + ) + + assert result == pytest.approx( + calculate_blkt_inboard_poloidal_plasma_angle_param.expected_angle + ) From 3ff054d07ade60d713d98c777990681bb9a7c744 Mon Sep 17 00:00:00 2001 From: mn3981 Date: Tue, 28 Apr 2026 15:03:13 +0100 Subject: [PATCH 14/18] Remove obsolete variable 'f_ster_div_single' from input variables and mark it as deprecated Co-authored-by: Copilot --- process/core/input.py | 1 - process/core/io/obsolete_vars.py | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/process/core/input.py b/process/core/input.py index 5f299d6c4..fe2d7c419 100644 --- a/process/core/input.py +++ b/process/core/input.py @@ -530,7 +530,6 @@ def __post_init__(self): "fiooic": InputVariable("constraints", float, range=(0.001, 1.0)), "fjohc": InputVariable("constraints", float, range=(0.001, 1.0)), "fjohc0": InputVariable("constraints", float, range=(0.001, 1.0)), - "f_ster_div_single": InputVariable("fwbs", float, range=(0.0, 1.0)), "fdiva": InputVariable("divertor", float, range=(0.1, 2.0)), "fdivwet": InputVariable( data_structure.stellarator_variables, float, range=(0.01, 1.0) diff --git a/process/core/io/obsolete_vars.py b/process/core/io/obsolete_vars.py index 4d0197d86..df3a7a829 100644 --- a/process/core/io/obsolete_vars.py +++ b/process/core/io/obsolete_vars.py @@ -448,6 +448,7 @@ "vcool": "vel_cp_coolant_midplane", "rcool": "radius_cp_coolant_channel", "fl_h_threshold": None, + "f_ster_div_single": None, } OBS_VARS_HELP = { From da260322f9c4061c9ca1d78e6268a1c3eba92a9a Mon Sep 17 00:00:00 2001 From: mn3981 Date: Tue, 28 Apr 2026 16:05:46 +0100 Subject: [PATCH 15/18] Add calculations for blanket poloidal plasma angles in DCLL class --- process/models/blankets/dcll.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/process/models/blankets/dcll.py b/process/models/blankets/dcll.py index 521bb5c70..ff3093aba 100644 --- a/process/models/blankets/dcll.py +++ b/process/models/blankets/dcll.py @@ -2,6 +2,7 @@ from process.core import ( process_output as po, ) +from process.data_structure import blanket_library as blanket_vars from process.data_structure import ( physics_variables, ) @@ -90,6 +91,27 @@ def output(self): def run(self, output: bool = False): self.component_volumes() + + # If Shfranov shift is added, the angle formula can be used where the shift is added to the minor radius. For now, the shift is neglected and the angle is calculated using the minor radius only. + blanket_vars.deg_blkt_outboard_poloidal_plasma = ( + self.blkt_outboard_poloidal_plasma_angle + ) + blanket_vars.f_deg_blkt_outboard_poloidal_plasma = ( + self.f_deg_blkt_outboard_poloidal_plasma + ) + + blanket_vars.deg_blkt_inboard_poloidal_plasma = ( + self.calculate_blkt_inboard_poloidal_plasma_angle( + rminor=physics_variables.rminor, + dz_blkt_half=blanket_vars.dz_blkt_half, + dr_fw_plasma_gap_inboard=build_variables.dr_fw_plasma_gap_inboard, + ) + ) + + blanket_vars.f_deg_blkt_inboard_poloidal_plasma = ( + blanket_vars.deg_blkt_inboard_poloidal_plasma / 360.0 + ) + dia_blkt_channel = self.pipe_hydraulic_diameter(i_channel_shape=1) self.data.fwbs.radius_blkt_channel = dia_blkt_channel / 2 ( From 37996fe0d7d035cc0ba26e23c35ef2683d4626a5 Mon Sep 17 00:00:00 2001 From: mn3981 Date: Tue, 12 May 2026 14:48:24 +0100 Subject: [PATCH 16/18] Post rebase commit --- process/data_structure/blanket_variables.py | 12 ++++++++++++ process/models/blankets/blanket_library.py | 8 ++++---- process/models/blankets/dcll.py | 13 ++++++------- process/models/blankets/hcpb.py | 12 ++++++------ process/models/divertor.py | 5 ++--- tests/unit/models/blankets/test_blanket_library.py | 6 +----- 6 files changed, 31 insertions(+), 25 deletions(-) diff --git a/process/data_structure/blanket_variables.py b/process/data_structure/blanket_variables.py index 51d37e4b3..c4197e8a7 100644 --- a/process/data_structure/blanket_variables.py +++ b/process/data_structure/blanket_variables.py @@ -186,5 +186,17 @@ class BlanketData: dz_vv_half: float = 0.0 """Vacuum vessel internal half-height (m)""" + deg_blkt_outboard_poloidal_plasma: float = 0.0 + """Outboard blanket poloidal angle subtended by plasma (degrees)""" + + f_deg_blkt_outboard_poloidal_plasma: float = 0.0 + """Fraction of outboard blanket poloidal angle subtended by plasma (degrees)""" + + deg_blkt_inboard_poloidal_plasma: float = 0.0 + """Inboard blanket poloidal angle subtended by plasma (degrees)""" + + f_deg_blkt_inboard_poloidal_plasma: float = 0.0 + """Fraction of inboard blanket poloidal angle subtended by plasma (degrees)""" + CREATE_DICTS_FROM_DATACLASS = BlanketData diff --git a/process/models/blankets/blanket_library.py b/process/models/blankets/blanket_library.py index e1a5abe65..56950ae2e 100644 --- a/process/models/blankets/blanket_library.py +++ b/process/models/blankets/blanket_library.py @@ -661,25 +661,25 @@ def output_blkt_volumes_and_areas(self): self.outfile, "Outboard blanket poloidal angle subtended by plasma (degrees)", "(deg_blkt_outboard_poloidal_plasma)", - blanket_library.deg_blkt_outboard_poloidal_plasma, + self.data.blanket.deg_blkt_outboard_poloidal_plasma, ) po.ovarre( self.outfile, "Angle fraction of outboard blanket poloidal angle subtended by plasma", "(f_deg_blkt_outboard_poloidal_plasma)", - blanket_library.f_deg_blkt_outboard_poloidal_plasma, + self.data.blanket.f_deg_blkt_outboard_poloidal_plasma, ) po.ovarre( self.outfile, "Inboard blanket poloidal angle subtended by plasma (degrees)", "(deg_blkt_inboard_poloidal_plasma)", - blanket_library.deg_blkt_inboard_poloidal_plasma, + self.data.blanket.deg_blkt_inboard_poloidal_plasma, ) po.ovarre( self.outfile, "Angle fraction of inboard blanket poloidal angle subtended by plasma", "(f_deg_blkt_inboard_poloidal_plasma)", - blanket_library.f_deg_blkt_inboard_poloidal_plasma, + self.data.blanket.f_deg_blkt_inboard_poloidal_plasma, ) def primary_coolant_properties(self, output: bool): diff --git a/process/models/blankets/dcll.py b/process/models/blankets/dcll.py index ff3093aba..2d65f0cd5 100644 --- a/process/models/blankets/dcll.py +++ b/process/models/blankets/dcll.py @@ -2,7 +2,6 @@ from process.core import ( process_output as po, ) -from process.data_structure import blanket_library as blanket_vars from process.data_structure import ( physics_variables, ) @@ -93,23 +92,23 @@ def run(self, output: bool = False): self.component_volumes() # If Shfranov shift is added, the angle formula can be used where the shift is added to the minor radius. For now, the shift is neglected and the angle is calculated using the minor radius only. - blanket_vars.deg_blkt_outboard_poloidal_plasma = ( + self.data.blanket.deg_blkt_outboard_poloidal_plasma = ( self.blkt_outboard_poloidal_plasma_angle ) - blanket_vars.f_deg_blkt_outboard_poloidal_plasma = ( + self.data.blanket.f_deg_blkt_outboard_poloidal_plasma = ( self.f_deg_blkt_outboard_poloidal_plasma ) - blanket_vars.deg_blkt_inboard_poloidal_plasma = ( + self.data.blanket.deg_blkt_inboard_poloidal_plasma = ( self.calculate_blkt_inboard_poloidal_plasma_angle( rminor=physics_variables.rminor, - dz_blkt_half=blanket_vars.dz_blkt_half, + dz_blkt_half=self.data.blanket.dz_blkt_half, dr_fw_plasma_gap_inboard=build_variables.dr_fw_plasma_gap_inboard, ) ) - blanket_vars.f_deg_blkt_inboard_poloidal_plasma = ( - blanket_vars.deg_blkt_inboard_poloidal_plasma / 360.0 + self.data.blanket.f_deg_blkt_inboard_poloidal_plasma = ( + self.data.blanket.deg_blkt_inboard_poloidal_plasma / 360.0 ) dia_blkt_channel = self.pipe_hydraulic_diameter(i_channel_shape=1) diff --git a/process/models/blankets/hcpb.py b/process/models/blankets/hcpb.py index 9a0c45c59..99b910d2e 100644 --- a/process/models/blankets/hcpb.py +++ b/process/models/blankets/hcpb.py @@ -47,23 +47,23 @@ def run(self, output: bool = False): self.component_volumes() # If Shfranov shift is added, the angle formula can be used where the shift is added to the minor radius. For now, the shift is neglected and the angle is calculated using the minor radius only. - blanket_vars.deg_blkt_outboard_poloidal_plasma = ( + self.data.blanket.deg_blkt_outboard_poloidal_plasma = ( self.blkt_outboard_poloidal_plasma_angle ) - blanket_vars.f_deg_blkt_outboard_poloidal_plasma = ( + self.data.blanket.f_deg_blkt_outboard_poloidal_plasma = ( self.f_deg_blkt_outboard_poloidal_plasma ) - blanket_vars.deg_blkt_inboard_poloidal_plasma = ( + self.data.blanket.deg_blkt_inboard_poloidal_plasma = ( self.calculate_blkt_inboard_poloidal_plasma_angle( rminor=physics_variables.rminor, - dz_blkt_half=blanket_vars.dz_blkt_half, + dz_blkt_half=self.data.blanket.dz_blkt_half, dr_fw_plasma_gap_inboard=build_variables.dr_fw_plasma_gap_inboard, ) ) - blanket_vars.f_deg_blkt_inboard_poloidal_plasma = ( - blanket_vars.deg_blkt_inboard_poloidal_plasma / 360.0 + self.data.blanket.f_deg_blkt_inboard_poloidal_plasma = ( + self.data.blanket.deg_blkt_inboard_poloidal_plasma / 360.0 ) dia_blkt_channel = self.pipe_hydraulic_diameter(i_channel_shape=1) diff --git a/process/models/divertor.py b/process/models/divertor.py index 38fb3d812..2af0359f5 100644 --- a/process/models/divertor.py +++ b/process/models/divertor.py @@ -6,7 +6,6 @@ from process.core import process_output as po from process.core.exceptions import ProcessValueError from process.core.model import Model -from process.data_structure import blanket_library from process.data_structure import physics_variables as pv from process.data_structure import tfcoil_variables as tfv @@ -37,7 +36,7 @@ def run(self, output: bool = False): indicate whether output should be written to the output file, or not """ dv.deg_div_poloidal_plasma = self.single_divertor_angle - fwbs.f_ster_div_single = dv.deg_div_poloidal_plasma / 360.0 + self.data.fwbs.f_ster_div_single = dv.deg_div_poloidal_plasma / 360.0 self.data.fwbs.p_div_nuclear_heat_total_mw = self.incident_neutron_power( p_plasma_neutron_mw=pv.p_plasma_neutron_mw, @@ -96,7 +95,7 @@ def single_divertor_angle(self): Angle is calculated as 180 degrees minus the inboard blanket poloidal angle, divided by 2 (for two divertors). """ - return (180.0 - blanket_library.deg_blkt_inboard_poloidal_plasma) / 2.0 + return (180.0 - self.data.blanket.deg_blkt_inboard_poloidal_plasma) / 2.0 def divtart( self, diff --git a/tests/unit/models/blankets/test_blanket_library.py b/tests/unit/models/blankets/test_blanket_library.py index 123b72359..06e775057 100644 --- a/tests/unit/models/blankets/test_blanket_library.py +++ b/tests/unit/models/blankets/test_blanket_library.py @@ -6,11 +6,7 @@ from process.data_structure import ( physics_variables, ) -from process.data_structure import build_variables as bv -from process.data_structure import fwbs_variables as fwbs -from process.data_structure import physics_variables as pv -from process.models.blankets.blanket_library import BlanketLibrary, InboardBlanket -from process.models.fw import FirstWall +from process.models.blankets.blanket_library import InboardBlanket @pytest.fixture From 5012f03a15c2c708a9fc3907bd81605261f4f3b7 Mon Sep 17 00:00:00 2001 From: mn3981 Date: Tue, 12 May 2026 15:25:01 +0100 Subject: [PATCH 17/18] Assign extra angle to outboard blanket if in single null --- process/core/io/plot/summary.py | 39 +++++++++++------- process/models/blankets/blanket_library.py | 47 ++++++++++++++++++---- process/models/blankets/dcll.py | 5 ++- process/models/blankets/hcpb.py | 5 ++- 4 files changed, 71 insertions(+), 25 deletions(-) diff --git a/process/core/io/plot/summary.py b/process/core/io/plot/summary.py index 743e9bc2f..bf70919f8 100644 --- a/process/core/io/plot/summary.py +++ b/process/core/io/plot/summary.py @@ -13894,15 +13894,16 @@ def plot_blkt_structure( label="Blanket Half Height", ) - # Plot arrows for the outboard blanket angles - ax.annotate( - "", - xy=(rmajor, 0), - xytext=(rmajor, dz_blkt_half), - arrowprops={"arrowstyle": "<-", "color": "purple"}, - zorder=5, - ) - + if i_single_null == 0: + # Plot arrows for the outboard blanket angles + ax.annotate( + "", + xy=(rmajor, 0), + xytext=(rmajor, dz_blkt_half), + arrowprops={"arrowstyle": "<-", "color": "purple"}, + zorder=5, + ) + # If single null then only plot the lower arrow for the outboard blanket angle ax.annotate( "", xy=(rmajor, 0), @@ -13913,8 +13914,14 @@ def plot_blkt_structure( # Plot arc showing the angle between the two outboard blanket arrows arc_radius = 1.0 - angle_start = -deg_blkt_outboard_poloidal_plasma / 2 - angle_end = deg_blkt_outboard_poloidal_plasma / 2 + + # 3 to 6 o'clock position is -90 degrees, + angle_start = -90.0 + if i_single_null == 1: + angle_end = 90.0 + deg_div_poloidal_plasma + elif i_single_null == 0: + # 3 to 12 o'clock position is +90 degrees + angle_end = 90.0 theta = np.linspace(np.deg2rad(angle_start), np.deg2rad(angle_end), 50) arc_x = rmajor + arc_radius * np.cos(theta) @@ -14006,8 +14013,9 @@ def plot_blkt_structure( if i_single_null == 0: # Plot arc showing the angle between the two arrows (divertor angle) arc_radius = 1.5 - angle_start = deg_blkt_outboard_poloidal_plasma / 2 + deg_div_poloidal_plasma - angle_end = deg_blkt_inboard_poloidal_plasma / 2 + deg_div_poloidal_plasma + # 3 to 12 o'clock position is +90 degrees, + angle_start = 90.0 + angle_end = 90.0 + deg_div_poloidal_plasma theta = np.linspace(np.deg2rad(angle_start), np.deg2rad(angle_end), 50) arc_x = rmajor + arc_radius * np.cos(theta) @@ -14042,8 +14050,9 @@ def plot_blkt_structure( # Plot arc showing the angle between the two arrows for the lower divertor (divertor angle) arc_radius = 1.5 - angle_start = -deg_blkt_outboard_poloidal_plasma / 2 - deg_div_poloidal_plasma - angle_end = -deg_blkt_inboard_poloidal_plasma / 2 - deg_div_poloidal_plasma + # 3 to 6 o'clock is -90 degrees + angle_start = -90.0 + angle_end = -90.0 - deg_div_poloidal_plasma theta = np.linspace(np.deg2rad(angle_start), np.deg2rad(angle_end), 50) arc_x = rmajor + arc_radius * np.cos(theta) diff --git a/process/models/blankets/blanket_library.py b/process/models/blankets/blanket_library.py index 56950ae2e..42c41ee73 100644 --- a/process/models/blankets/blanket_library.py +++ b/process/models/blankets/blanket_library.py @@ -3491,15 +3491,44 @@ def calculate_basic_geometry(self): b_bz_liq=self.data.fwbs.b_bz_liq, ) - @property - def blkt_outboard_poloidal_plasma_angle(self) -> float: - """Calculate the poloidal angle subtended by the outboard blanket at the plasma mid-plane.""" - return 180.0 + def blkt_outboard_poloidal_plasma_angle( + self, n_divertors: int, deg_div_poloidal_plasma: float + ) -> float: + """Calculate the poloidal angle subtended by the outboard blanket at the + plasma mid-plane. + + Parameters + ---------- + n_divertors : int + Number of divertors in the design (1 or 2). + deg_div_poloidal_plasma : float + Poloidal angle subtended by the divertor at the plasma mid-plane (degrees). + + Returns + ------- + float + Poloidal angle subtended by outboard blanket at plasma mid-plane (degrees). + + Raises + ------ + ProcessValueError + If n_divertors is not 1 or 2. + """ + if n_divertors == 1: + return 180.0 + deg_div_poloidal_plasma + if n_divertors == 2: + return 180.0 + raise ProcessValueError( + f"n_divertors = {n_divertors} is an invalid option. Only 1 or 2 divertors " + f"are supported." + ) @property def f_deg_blkt_outboard_poloidal_plasma(self) -> float: - """Calculate the poloidal angle subtended by the outboard blanket at the plasma mid-plane.""" - return self.blkt_outboard_poloidal_plasma_angle / 360.0 + """Calculate the poloidal angle subtended by the outboard blanket at the + plasma mid-plane. + """ + return self.data.blanket.deg_blkt_outboard_poloidal_plasma / 360.0 @staticmethod def calculate_blkt_outboard_poloidal_plasma_angle( @@ -3509,7 +3538,8 @@ def calculate_blkt_outboard_poloidal_plasma_angle( dr_fw_plasma_gap_outboard: float, dr_fw_outboard: float, ) -> float: - """Calculate the poloidal angle subtended by the outboard blanket at the plasma mid-plane. + """Calculate the poloidal angle subtended by the outboard blanket at the + plasma mid-plane. Parameters ---------- @@ -3549,7 +3579,8 @@ def calculate_blanket_outboard_module_geometry( rminor: float, dr_fw_plasma_gap_outboard: float, ) -> float: - """Calculate the mid-plane toroidal circumference and segment length of the outboard blanket. + """Calculate the mid-plane toroidal circumference and segment length of the + outboard blanket. Parameters ---------- diff --git a/process/models/blankets/dcll.py b/process/models/blankets/dcll.py index 2d65f0cd5..96d32a571 100644 --- a/process/models/blankets/dcll.py +++ b/process/models/blankets/dcll.py @@ -93,7 +93,10 @@ def run(self, output: bool = False): # If Shfranov shift is added, the angle formula can be used where the shift is added to the minor radius. For now, the shift is neglected and the angle is calculated using the minor radius only. self.data.blanket.deg_blkt_outboard_poloidal_plasma = ( - self.blkt_outboard_poloidal_plasma_angle + self.blkt_outboard_poloidal_plasma_angle( + n_divertors=divertor_variables.n_divertors, + deg_div_poloidal_plasma=divertor_variables.deg_div_poloidal_plasma, + ) ) self.data.blanket.f_deg_blkt_outboard_poloidal_plasma = ( self.f_deg_blkt_outboard_poloidal_plasma diff --git a/process/models/blankets/hcpb.py b/process/models/blankets/hcpb.py index 99b910d2e..861b8eb16 100644 --- a/process/models/blankets/hcpb.py +++ b/process/models/blankets/hcpb.py @@ -48,7 +48,10 @@ def run(self, output: bool = False): # If Shfranov shift is added, the angle formula can be used where the shift is added to the minor radius. For now, the shift is neglected and the angle is calculated using the minor radius only. self.data.blanket.deg_blkt_outboard_poloidal_plasma = ( - self.blkt_outboard_poloidal_plasma_angle + self.blkt_outboard_poloidal_plasma_angle( + n_divertors=divertor_variables.n_divertors, + deg_div_poloidal_plasma=divertor_variables.deg_div_poloidal_plasma, + ) ) self.data.blanket.f_deg_blkt_outboard_poloidal_plasma = ( self.f_deg_blkt_outboard_poloidal_plasma From 55fe3abdafe9b6b46c10bc84ebabbcb1b5ad131e Mon Sep 17 00:00:00 2001 From: mn3981 Date: Tue, 19 May 2026 10:40:27 +0100 Subject: [PATCH 18/18] Post rebase fixes --- process/data_structure/divertor_variables.py | 4 ++-- process/models/blankets/dcll.py | 6 +++--- process/models/blankets/hcpb.py | 6 +++--- process/models/divertor.py | 6 ++++-- process/models/physics/physics.py | 2 +- 5 files changed, 13 insertions(+), 11 deletions(-) diff --git a/process/data_structure/divertor_variables.py b/process/data_structure/divertor_variables.py index 6ae42bd41..28f2707aa 100644 --- a/process/data_structure/divertor_variables.py +++ b/process/data_structure/divertor_variables.py @@ -78,8 +78,8 @@ class DivertorData: n_divertors: int = 2 """Number of divertors (calculated from `i_single_null`)""" -deg_div_poloidal_plasma: float = None -"""Divertor poloidal angle subtended by plasma (degrees)""" + deg_div_poloidal_plasma: float = 0.0 + """Divertor poloidal angle subtended by plasma (degrees)""" CREATE_DICTS_FROM_DATACLASS = DivertorData diff --git a/process/models/blankets/dcll.py b/process/models/blankets/dcll.py index 96d32a571..5c6fe6eb1 100644 --- a/process/models/blankets/dcll.py +++ b/process/models/blankets/dcll.py @@ -94,8 +94,8 @@ def run(self, output: bool = False): # If Shfranov shift is added, the angle formula can be used where the shift is added to the minor radius. For now, the shift is neglected and the angle is calculated using the minor radius only. self.data.blanket.deg_blkt_outboard_poloidal_plasma = ( self.blkt_outboard_poloidal_plasma_angle( - n_divertors=divertor_variables.n_divertors, - deg_div_poloidal_plasma=divertor_variables.deg_div_poloidal_plasma, + n_divertors=self.data.divertor.n_divertors, + deg_div_poloidal_plasma=self.data.divertor.deg_div_poloidal_plasma, ) ) self.data.blanket.f_deg_blkt_outboard_poloidal_plasma = ( @@ -106,7 +106,7 @@ def run(self, output: bool = False): self.calculate_blkt_inboard_poloidal_plasma_angle( rminor=physics_variables.rminor, dz_blkt_half=self.data.blanket.dz_blkt_half, - dr_fw_plasma_gap_inboard=build_variables.dr_fw_plasma_gap_inboard, + dr_fw_plasma_gap_inboard=self.data.build.dr_fw_plasma_gap_inboard, ) ) diff --git a/process/models/blankets/hcpb.py b/process/models/blankets/hcpb.py index 861b8eb16..34ccef691 100644 --- a/process/models/blankets/hcpb.py +++ b/process/models/blankets/hcpb.py @@ -49,8 +49,8 @@ def run(self, output: bool = False): # If Shfranov shift is added, the angle formula can be used where the shift is added to the minor radius. For now, the shift is neglected and the angle is calculated using the minor radius only. self.data.blanket.deg_blkt_outboard_poloidal_plasma = ( self.blkt_outboard_poloidal_plasma_angle( - n_divertors=divertor_variables.n_divertors, - deg_div_poloidal_plasma=divertor_variables.deg_div_poloidal_plasma, + n_divertors=self.data.divertor.n_divertors, + deg_div_poloidal_plasma=self.data.divertor.deg_div_poloidal_plasma, ) ) self.data.blanket.f_deg_blkt_outboard_poloidal_plasma = ( @@ -61,7 +61,7 @@ def run(self, output: bool = False): self.calculate_blkt_inboard_poloidal_plasma_angle( rminor=physics_variables.rminor, dz_blkt_half=self.data.blanket.dz_blkt_half, - dr_fw_plasma_gap_inboard=build_variables.dr_fw_plasma_gap_inboard, + dr_fw_plasma_gap_inboard=self.data.build.dr_fw_plasma_gap_inboard, ) ) diff --git a/process/models/divertor.py b/process/models/divertor.py index 2af0359f5..844eb6598 100644 --- a/process/models/divertor.py +++ b/process/models/divertor.py @@ -35,8 +35,10 @@ def run(self, output: bool = False): output : indicate whether output should be written to the output file, or not """ - dv.deg_div_poloidal_plasma = self.single_divertor_angle - self.data.fwbs.f_ster_div_single = dv.deg_div_poloidal_plasma / 360.0 + self.data.divertor.deg_div_poloidal_plasma = self.single_divertor_angle + self.data.fwbs.f_ster_div_single = ( + self.data.divertor.deg_div_poloidal_plasma / 360.0 + ) self.data.fwbs.p_div_nuclear_heat_total_mw = self.incident_neutron_power( p_plasma_neutron_mw=pv.p_plasma_neutron_mw, diff --git a/process/models/physics/physics.py b/process/models/physics/physics.py index 1ed4bce32..f1aa51d0d 100644 --- a/process/models/physics/physics.py +++ b/process/models/physics/physics.py @@ -2099,7 +2099,7 @@ def outplas(self): self.outfile, "Divertor poloidal angle subtended by plasma (degrees)", "(deg_div_poloidal_plasma)", - divertor_variables.deg_div_poloidal_plasma, + self.data.divertor.deg_div_poloidal_plasma, "OP ", ) po.ovarre(