diff --git a/process/core/input.py b/process/core/input.py index 825efef1d..338ef6ea1 100644 --- a/process/core/input.py +++ b/process/core/input.py @@ -764,9 +764,6 @@ def __post_init__(self): "fjohc0": InputVariable( data_structure.constraint_variables, float, range=(0.001, 1.0) ), - "f_ster_div_single": InputVariable( - data_structure.fwbs_variables, float, range=(0.0, 1.0) - ), "fdiva": InputVariable(data_structure.divertor_variables, 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 98525c127..882681d47 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 = { diff --git a/process/core/io/plot/summary.py b/process/core/io/plot/summary.py index 261cd8dd5..3aff8460a 100644 --- a/process/core/io/plot/summary.py +++ b/process/core/io/plot/summary.py @@ -13741,26 +13741,55 @@ 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) + 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 + ) + 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) + 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") ax.minorticks_on() ax.grid(which="minor", linestyle=":", linewidth=0.5, alpha=0.5) - # 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$", + + 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 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", @@ -13776,25 +13805,206 @@ def plot_blkt_structure( label="Blanket Half Height", ) + # Plot arrows for the outboard blanket angles ax.annotate( "", - xy=(rmajor, dz_blkt_half), + xy=(rmajor, 0), + xytext=(rmajor, dz_blkt_half), + arrowprops={"arrowstyle": "<-", "color": "purple"}, + zorder=5, + ) + + ax.annotate( + "", + xy=(rmajor, 0), xytext=(rmajor, -dz_blkt_half), - arrowprops={"arrowstyle": "<->", "color": "black"}, + arrowprops={"arrowstyle": "<-", "color": "purple"}, + zorder=5, ) - # Add a label for the internal coil width + # 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 + + 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) + + # Plot the info box for the outboard blanket ax.text( - rmajor, - 0.0, - f"{2 * dz_blkt_half:.3f} m", + label_x, + label_y, + f"{deg_blkt_outboard_poloidal_plasma:.1f}°\n({f_deg_blkt_outboard_poloidal_plasma * 100:.1f}%)", + fontsize=7, + color="purple", + ha="center", + va="center", + weight="bold", + bbox={ + "boxstyle": "round", + "facecolor": "white", + "alpha": 0.8, + "edgecolor": "purple", + "linewidth": 1.5, + }, + ) + + # Plot arrows for the inboard blanket angles + ax.annotate( + "", + xy=(rmajor, 0), + xytext=(r_fw_inboard_out, dz_blkt_half), + arrowprops={"arrowstyle": "<-", "color": "green"}, + zorder=5, + ) + + ax.annotate( + "", + xy=(rmajor, 0), + xytext=(r_fw_inboard_out, -dz_blkt_half), + arrowprops={"arrowstyle": "<-", "color": "green"}, + zorder=5, + ) + + # 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 + + 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="green", 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) + + # Plot the info box for the inboard blanket + ax.text( + label_x, + label_y, + f"{deg_blkt_inboard_poloidal_plasma:.1f}°\n({f_deg_blkt_inboard_poloidal_plasma * 100:.1f}%)", + fontsize=7, + color="green", + ha="center", + va="center", + weight="bold", + bbox={ + "boxstyle": "round", + "facecolor": "white", + "alpha": 0.8, + "edgecolor": "green", + "linewidth": 1.5, + }, + zorder=5, + ) + + # 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 + + 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) + + # Plot the info box for the lower divertor angle + ax.text( + label_x, + label_y, + f"{deg_div_poloidal_plasma:.1f}°\n({f_ster_div_single * 100:.1f}%)", 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 + ha="center", + va="center", + weight="bold", + bbox={ + "boxstyle": "round", + "facecolor": "white", + "alpha": 0.8, + "edgecolor": "black", + "linewidth": 1.5, + }, + zorder=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 + ) + 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, + color="black", + linestyle="--", + linewidth=1.5, + label="Major Radius $R_0$", ) # Plot midplane line (horizontal dashed line at Z=0) diff --git a/process/data_structure/blanket_library.py b/process/data_structure/blanket_library.py index 2cd99c364..1d5e22f01 100644 --- a/process/data_structure/blanket_library.py +++ b/process/data_structure/blanket_library.py @@ -236,6 +236,18 @@ icomponent: int = None """Switch used to specify selected component: blanket=0, shield=1, vacuum vessel=2""" +deg_blkt_outboard_poloidal_plasma: float = None +"""Outboard blanket poloidal angle subtended by plasma (degrees)""" + +f_deg_blkt_outboard_poloidal_plasma: float = None +"""Fraction of outboard blanket poloidal angle subtended by plasma (degrees)""" + +deg_blkt_inboard_poloidal_plasma: float = None +"""Inboard blanket poloidal angle subtended by plasma (degrees)""" + +f_deg_blkt_inboard_poloidal_plasma: float = None +"""Fraction of inboard blanket poloidal angle subtended by plasma (degrees)""" + def init_blanket_library(): global \ @@ -288,7 +300,11 @@ def init_blanket_library(): htpmw_blkti, \ htpmw_blkto, \ vfblkti, \ - vfblkto + vfblkto, \ + deg_blkt_outboard_poloidal_plasma, \ + deg_blkt_inboard_poloidal_plasma, \ + f_deg_blkt_outboard_poloidal_plasma, \ + f_deg_blkt_inboard_poloidal_plasma dz_blkt_half = 0.0 dz_shld_half = 0.0 @@ -340,3 +356,7 @@ def init_blanket_library(): htpmw_blkto = 0.0 vfblkti = 0.0 vfblkto = 0.0 + deg_blkt_outboard_poloidal_plasma = 0.0 + deg_blkt_inboard_poloidal_plasma = 0.0 + f_deg_blkt_outboard_poloidal_plasma = 0.0 + f_deg_blkt_inboard_poloidal_plasma = 0.0 diff --git a/process/data_structure/divertor_variables.py b/process/data_structure/divertor_variables.py index e096caac0..3769428b9 100644 --- a/process/data_structure/divertor_variables.py +++ b/process/data_structure/divertor_variables.py @@ -71,6 +71,9 @@ n_divertors: int = None """Number of divertors (calculated from `i_single_null`)""" +deg_div_poloidal_plasma: float = None +"""Divertor poloidal angle subtended by plasma (degrees)""" + def init_divertor_variables(): global \ @@ -96,7 +99,8 @@ def init_divertor_variables(): p_div_upper_nuclear_heat_mw, \ p_div_upper_rad_mw, \ p_div_lower_rad_mw, \ - n_divertors + n_divertors, \ + deg_div_poloidal_plasma anginc = 0.262 deg_div_field_plate = 1.0 @@ -121,3 +125,4 @@ def init_divertor_variables(): p_div_upper_rad_mw = 0.0 p_div_lower_rad_mw = 0.0 n_divertors = 2 + deg_div_poloidal_plasma = 0.0 diff --git a/process/models/blankets/blanket_library.py b/process/models/blankets/blanket_library.py index 3f1cf646c..583c1780f 100644 --- a/process/models/blankets/blanket_library.py +++ b/process/models/blankets/blanket_library.py @@ -643,6 +643,31 @@ def output_blkt_volumes_and_areas(self): "(a_blkt_total_surface_full_coverage)", build_variables.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, + ) + 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. @@ -3677,6 +3702,57 @@ def calculate_basic_geometry(self): fwbs_variables.radius_blkt_channel_180_bend, ) = self.calculate_pipe_bend_radius(i_ps=1) + @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 + + @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, + 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, @@ -3720,6 +3796,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/dcll.py b/process/models/blankets/dcll.py index c951eb3e9..6935756f9 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 ( build_variables, current_drive_variables, @@ -94,6 +95,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) fwbs_variables.radius_blkt_channel = dia_blkt_channel / 2 ( diff --git a/process/models/blankets/hcpb.py b/process/models/blankets/hcpb.py index a93ce4bd7..2231bad58 100644 --- a/process/models/blankets/hcpb.py +++ b/process/models/blankets/hcpb.py @@ -52,6 +52,26 @@ 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.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) fwbs_variables.radius_blkt_channel = dia_blkt_channel / 2 ( diff --git a/process/models/divertor.py b/process/models/divertor.py index f89a42b1e..06ebefa0c 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 build_variables as bv from process.data_structure import divertor_variables as dv from process.data_structure import fwbs_variables as fwbs @@ -38,6 +39,9 @@ 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 + fwbs.f_ster_div_single = dv.deg_div_poloidal_plasma / 360.0 + fwbs.p_div_nuclear_heat_total_mw = self.incident_neutron_power( p_plasma_neutron_mw=pv.p_plasma_neutron_mw, f_ster_div_single=fwbs.f_ster_div_single, @@ -88,6 +92,15 @@ 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 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 + def divtart( self, rmajor: float, diff --git a/process/models/physics/physics.py b/process/models/physics/physics.py index 8f1e10ccc..8143b0a2c 100644 --- a/process/models/physics/physics.py +++ b/process/models/physics/physics.py @@ -2681,6 +2681,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", diff --git a/tests/unit/models/blankets/test_blanket_library.py b/tests/unit/models/blankets/test_blanket_library.py index 802081873..d92fb833e 100644 --- a/tests/unit/models/blankets/test_blanket_library.py +++ b/tests/unit/models/blankets/test_blanket_library.py @@ -12,7 +12,7 @@ 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 +from process.models.blankets.blanket_library import BlanketLibrary, InboardBlanket from process.models.fw import FirstWall @@ -1684,3 +1684,48 @@ def test_liquid_breeder_properties_part_3(monkeypatch, blanket_library_fixture): blanket_library_fixture.liquid_breeder_properties() assert pytest.approx(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 + )