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 = { diff --git a/process/core/io/plot/summary.py b/process/core/io/plot/summary.py index bce8c6173..bf70919f8 100644 --- a/process/core/io/plot/summary.py +++ b/process/core/io/plot/summary.py @@ -13830,26 +13830,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", @@ -13865,25 +13894,215 @@ def plot_blkt_structure( label="Blanket Half Height", ) + 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, dz_blkt_half), + 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 + + # 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) + 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 + # 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) + 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 + # 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) + 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_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/data_structure/divertor_variables.py b/process/data_structure/divertor_variables.py index cca7a800a..28f2707aa 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 = 0.0 + """Divertor poloidal angle subtended by plasma (degrees)""" + CREATE_DICTS_FROM_DATACLASS = DivertorData diff --git a/process/models/blankets/blanket_library.py b/process/models/blankets/blanket_library.py index 98c5c4109..42c41ee73 100644 --- a/process/models/blankets/blanket_library.py +++ b/process/models/blankets/blanket_library.py @@ -656,6 +656,31 @@ 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)", + 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)", + 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)", + 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)", + self.data.blanket.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. @@ -3466,6 +3491,87 @@ def calculate_basic_geometry(self): b_bz_liq=self.data.fwbs.b_bz_liq, ) + 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.data.blanket.deg_blkt_outboard_poloidal_plasma / 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, @@ -3473,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 ---------- @@ -3513,6 +3620,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 521bb5c70..5c6fe6eb1 100644 --- a/process/models/blankets/dcll.py +++ b/process/models/blankets/dcll.py @@ -90,6 +90,30 @@ 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. + self.data.blanket.deg_blkt_outboard_poloidal_plasma = ( + self.blkt_outboard_poloidal_plasma_angle( + 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 = ( + self.f_deg_blkt_outboard_poloidal_plasma + ) + + self.data.blanket.deg_blkt_inboard_poloidal_plasma = ( + 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=self.data.build.dr_fw_plasma_gap_inboard, + ) + ) + + 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) self.data.fwbs.radius_blkt_channel = dia_blkt_channel / 2 ( diff --git a/process/models/blankets/hcpb.py b/process/models/blankets/hcpb.py index 455990b8e..34ccef691 100644 --- a/process/models/blankets/hcpb.py +++ b/process/models/blankets/hcpb.py @@ -46,6 +46,29 @@ 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. + self.data.blanket.deg_blkt_outboard_poloidal_plasma = ( + self.blkt_outboard_poloidal_plasma_angle( + 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 = ( + self.f_deg_blkt_outboard_poloidal_plasma + ) + + self.data.blanket.deg_blkt_inboard_poloidal_plasma = ( + 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=self.data.build.dr_fw_plasma_gap_inboard, + ) + ) + + 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) self.data.fwbs.radius_blkt_channel = dia_blkt_channel / 2 ( diff --git a/process/models/divertor.py b/process/models/divertor.py index 0b8cffca7..844eb6598 100644 --- a/process/models/divertor.py +++ b/process/models/divertor.py @@ -35,6 +35,11 @@ def run(self, output: bool = False): output : indicate whether output should be written to the output file, or not """ + 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, f_ster_div_single=self.data.fwbs.f_ster_div_single, @@ -85,6 +90,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 - self.data.blanket.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 851d6abe5..f1aa51d0d 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)", + self.data.divertor.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 3375df1c0..06e775057 100644 --- a/tests/unit/models/blankets/test_blanket_library.py +++ b/tests/unit/models/blankets/test_blanket_library.py @@ -6,6 +6,7 @@ from process.data_structure import ( physics_variables, ) +from process.models.blankets.blanket_library import InboardBlanket @pytest.fixture @@ -1758,3 +1759,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 + )