diff --git a/documentation/source/eng-models/power-requirements.md b/documentation/source/eng-models/power-requirements.md index 214989efe5..c538b42c04 100644 --- a/documentation/source/eng-models/power-requirements.md +++ b/documentation/source/eng-models/power-requirements.md @@ -214,7 +214,7 @@ $$ - $P_{\text{FW, nuclear}}$ & $P_{\text{Blkt, nuclear}}$ is the nuclear heating from neutron interaction (which includes the energy multiplication (`f_p_blkt_multiplication`) for the blanket.) - $P_{\text{FW,}\gamma}$ is the photon radiation incident on the FW (`p_fw_rad_total_mw`). -- $P_{\alpha,\text{loss}}$ is the plasma lost alpha power (`p_fw_alpha_mw`) +- $P_{\alpha,\text{loss}}$ is the plasma lost alpha power (`p_fw_alpha_surface_total_mw`) 8: The thermal power deposited in the shields is calculated: diff --git a/process/core/io/plot/summary.py b/process/core/io/plot/summary.py index a4ef142632..71b2892f75 100644 --- a/process/core/io/plot/summary.py +++ b/process/core/io/plot/summary.py @@ -520,7 +520,7 @@ def plot_main_power_flow(axis: plt.Axes, mfile: MFile, scan: int, fig: plt.Figur axis.text( 0.22, 0.81, - f"$P_{{\\alpha,{{loss}}}}$\n{mfile.get('p_fw_alpha_mw', scan=scan):,.2f} MW", + f"$P_{{\\alpha,{{loss}}}}$\n{mfile.get('p_fw_alpha_surface_total_mw', scan=scan):,.2f} MW", transform=fig.transFigure, horizontalalignment="left", verticalalignment="bottom", @@ -1364,7 +1364,7 @@ def plot_main_power_flow(axis: plt.Axes, mfile: MFile, scan: int, fig: plt.Figur axis.text( 0.46, 0.85, - f"$P_{{\\text{{FW, }}\\alpha}}$:\n{mfile.get('p_fw_alpha_mw', scan=scan):.2f} MW", + f"$P_{{\\text{{FW, }}\\alpha}}$:\n{mfile.get('p_fw_alpha_surface_total_mw', scan=scan):.2f} MW", fontsize=9, verticalalignment="bottom", horizontalalignment="left", @@ -2870,7 +2870,7 @@ def plot_main_plasma_information( ) textstr_alpha = ( - f"$P_{{\\alpha,\\text{{loss}}}}$ {mfile.get('p_fw_alpha_mw', scan=scan):.2f} MW \n" + f"$P_{{\\alpha,\\text{{loss}}}}$ {mfile.get('p_fw_alpha_surface_total_mw', scan=scan):.2f} MW \n" f"$f_{{\\alpha,\\text{{coupled}}}}$ {mfile.get('f_p_alpha_plasma_deposited', scan=scan):.2f}" ) @@ -14660,6 +14660,345 @@ def plot_blkt_structure( ) +def plot_poloidal_power_distribution( + ax: plt.Axes, + m_file: MFile, + scan: int, + radial_build: dict[str, float], + colour_scheme: Literal[1, 2], +): + """Plot the poloidal power distribution on the first wall and blanket and annotate 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) + triang = m_file.get("triang", scan=scan) + kappa = m_file.get("kappa", 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) + + 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) + ax.axhline( + dz_blkt_half, + color="purple", + linestyle="--", + linewidth=1.5, + label="Blanket Half Height", + ) + ax.axhline( + -dz_blkt_half, + color="purple", + linestyle="--", + linewidth=1.5, + 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, 0), + xytext=(rmajor, -dz_blkt_half), + arrowprops={"arrowstyle": "<-", "color": "purple"}, + zorder=5, + ) + + # 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) + + # Plot the info box for the outboard blanket + ax.text( + rmajor * 1.75, + 0.0, + f"$P_{{\\gamma}}$={m_file.get('p_fw_outboard_rad_mw', scan=scan):.3f} MW\n" + f"$\\Gamma_{{\\gamma}}$={m_file.get('pflux_fw_outboard_rad_surface_average_mw', scan=scan):.3f} MW/m²\n\n" + f"$P_{{\\alpha}}$={m_file.get('p_fw_outboard_alpha_surface_mw', scan=scan):.3f} MW\n\n" + f"$P_n$={m_file.get('p_fw_outboard_neutron_incident_mw', scan=scan):.3f} MW\n" + f"$\\Gamma_{{n}}$={m_file.get('pflux_fw_outboard_neutron_surface_average_mw', scan=scan):.3f} MW/m²\n", + fontsize=7, + color="purple", + ha="center", + va="center", + weight="bold", + bbox={ + "boxstyle": "round", + "facecolor": "white", + "alpha": 1.0, + "edgecolor": "purple", + "linewidth": 1.5, + }, + ) + + # Plot arrow for the outboard blanket + ax.annotate( + "", + xy=(rmajor, 0), + xytext=(rmajor + rminor, 0), + arrowprops={"arrowstyle": "<-", "color": "purple", "linewidth": 1.5}, + zorder=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, + ) + + # Plot the total loads at the centre of the plasma + ax.text( + rmajor, + 0.0, + f"$P_{{\\gamma}}$={m_file.get('p_plasma_rad_mw', scan=scan):.1f}MW\n" + f"$P_{{\\alpha}}$={m_file.get('p_fw_outboard_alpha_surface_mw', scan=scan):.1f}MW\n" + f"$P_n$={m_file.get('p_neutron_total_mw', scan=scan):.1f}MW", + fontsize=7, + color="black", + ha="center", + va="center", + weight="bold", + bbox={ + "boxstyle": "round", + "facecolor": "wheat", + "alpha": 1.0, + "edgecolor": "black", + "linewidth": 1.5, + }, + zorder=20, + ) + + 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/FW + ax.text( + rmajor / 3, + 0.0, + f"$P_{{\\gamma}}$={m_file.get('p_fw_inboard_rad_mw', scan=scan):.3f} MW\n" + f"$\\Gamma_{{\\gamma}}$={m_file.get('pflux_fw_inboard_rad_surface_average_mw', scan=scan):.3f} MW/m²\n\n" + f"$P_{{\\alpha}}$={m_file.get('p_fw_inboard_alpha_surface_mw', scan=scan):.3f} MW\n\n" + f"$P_n$={m_file.get('p_fw_inboard_neutron_incident_mw', scan=scan):.3f} MW\n" + f"$\\Gamma_{{n}}$={m_file.get('pflux_fw_inboard_neutron_surface_average_mw', scan=scan):.3f} MW/m²\n\n", + fontsize=7, + color="green", + ha="center", + va="center", + weight="bold", + bbox={ + "boxstyle": "round", + "facecolor": "white", + "alpha": 1.0, + "edgecolor": "green", + "linewidth": 1.5, + }, + zorder=5, + ) + + # Plot arrow for the inboard blanket + ax.annotate( + "", + xy=(rmajor, 0), + xytext=(rmajor - rminor, 0), + arrowprops={"arrowstyle": "<-", "color": "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": 1.0, + "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( + rmajor - (triang * rminor), + -(rminor * kappa), + f"$P_{{\\gamma}}$={m_file.get('p_div_rad_total_mw', scan=scan):.3f} MW\n\n" + f"$P_n$={m_file.get('p_div_nuclear_heat_total_mw', scan=scan):.3f} MW\n", + fontsize=7, + color="black", + ha="center", + va="center", + weight="bold", + bbox={ + "boxstyle": "round", + "facecolor": "white", + "alpha": 1.0, + "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) + ax.axhline( + 0.0, + color="black", + linestyle="--", + linewidth=1.5, + label="Midplane", + ) + + ax.set_xlim(-rmajor / 4, 2 * rmajor) + + def plot_detailed_plasma_parameters(axis: plt.Axes, fig, mfile: MFile, scan: int): """Function to plot detailed plasma parameters from physics data. @@ -15608,14 +15947,22 @@ def main_plot( ax_blanket = figs[34].add_subplot(122, aspect="equal") plot_blkt_structure(ax_blanket, figs[34], m_file, scan, radial_build, colour_scheme) + plot_poloidal_power_distribution( + ax=figs[35].add_subplot(111, aspect="equal"), + m_file=m_file, + scan=scan, + radial_build=radial_build, + colour_scheme=colour_scheme, + ) + plot_main_power_flow( - figs[35].add_subplot(111, aspect="equal"), m_file, scan, figs[35] + figs[36].add_subplot(111, aspect="equal"), m_file, scan, figs[36] ) - ax24 = figs[36].add_subplot(111) + ax24 = figs[37].add_subplot(111) # set_position([left, bottom, width, height]) -> height ~ 0.66 => ~2/3 of page height ax24.set_position([0.08, 0.35, 0.84, 0.57]) - plot_system_power_profiles_over_time(ax24, m_file, scan, figs[36]) + plot_system_power_profiles_over_time(ax24, m_file, scan, figs[37]) def create_thickness_builds(m_file, scan: int): @@ -15697,7 +16044,7 @@ def plot_summary( ): # create main plot # Increase range when adding new page - pages = [plt.figure(figsize=(12, 9), dpi=80) for i in range(37)] + pages = [plt.figure(figsize=(12, 9), dpi=80) for i in range(38)] # run main_plot main_plot( diff --git a/process/data_structure/fwbs_variables.py b/process/data_structure/fwbs_variables.py index 0bc6208d9b..4163290c92 100644 --- a/process/data_structure/fwbs_variables.py +++ b/process/data_structure/fwbs_variables.py @@ -170,11 +170,35 @@ class FWBSData: f_a_fw_coolant_outboard: float = 0.0 """Outboard FW coolant cross-sectional area void fraction""" - psurffwi: float = 0.0 - """Surface heat flux on first wall [MW] (sum = p_fw_rad_total_mw)""" + p_fw_inboard_surface_heat_mw: float = 0.0 + """Surface heat flux on inboard first wall [MW] """ - psurffwo: float = 0.0 - """Surface heat flux on first wall [MW] (sum = p_fw_rad_total_mw)""" + p_fw_outboard_surface_heat_mw: float = 0.0 + """Surface heat flux on outboard first wall [MW]""" + + p_fw_inboard_alpha_surface_mw: float = 0.0 + """Alpha particle heat flux on inboard first wall [MW]""" + + p_fw_outboard_alpha_surface_mw: float = 0.0 + """Alpha particle heat flux on outboard first wall [MW]""" + + p_fw_inboard_neutron_incident_mw: float = 0.0 + """Neutron heat flux incident on inboard first wall [MW]""" + + p_fw_outboard_neutron_incident_mw: float = 0.0 + """Neutron heat flux incident on outboard first wall [MW]""" + + pflux_fw_inboard_neutron_surface_average_mw: float = 0.0 + """Average neutron flux on inboard first wall surface [MW/m²]""" + + pflux_fw_outboard_neutron_surface_average_mw: float = 0.0 + """Average neutron flux on outboard first wall surface [MW/m²]""" + + pflux_fw_inboard_rad_surface_average_mw: float = 0.0 + """Average radiation flux on inboard first wall surface [MW/m²]""" + + pflux_fw_outboard_rad_surface_average_mw: float = 0.0 + """Average radiation flux on outboard first wall surface [MW/m²]""" vol_fw_total: float = 0.0 """First wall volume [m3]""" @@ -379,6 +403,12 @@ class FWBSData: p_fw_rad_total_mw: float = 0.0 """Radiation power incident on the first wall (MW)""" + p_fw_inboard_rad_mw: float = 0.0 + """Radiation power incident on the inboard first wall [MW]""" + + p_fw_outboard_rad_mw: float = 0.0 + """Radiation power incident on the outboard first wall [MW]""" + p_fw_hcd_rad_total_mw: float = 0.0 """Radiation power incident on the heating and current drive systems on the first wall (MW)""" diff --git a/process/data_structure/physics_variables.py b/process/data_structure/physics_variables.py index 43af54e6a7..d7c284984e 100644 --- a/process/data_structure/physics_variables.py +++ b/process/data_structure/physics_variables.py @@ -658,8 +658,8 @@ class PhysicsData: f_pden_alpha_electron_mw: float = 0.0 """Alpha power per volume to electrons [MW/m3]""" - p_fw_alpha_mw: float = 0.0 - """alpha power escaping plasma and reaching first wall (MW)""" + p_fw_alpha_surface_total_mw: float = 0.0 + """Total alpha power escaping plasma and reaching first walls (MW)""" f_pden_alpha_ions_mw: float = 0.0 """alpha power per volume to ions (MW/m3)""" diff --git a/process/models/blankets/blanket_library.py b/process/models/blankets/blanket_library.py index c310e831e0..feb132e0bd 100644 --- a/process/models/blankets/blanket_library.py +++ b/process/models/blankets/blanket_library.py @@ -2195,35 +2195,35 @@ def thermo_hydraulic_model(self, output: bool): if self.data.fwbs.i_blkt_dual_coolant == 2: f_nuc_fwi = ( self.data.blanket.p_fw_inboard_nuclear_heat_mw - + self.data.fwbs.psurffwi + + self.data.fwbs.p_fw_inboard_surface_heat_mw ) / ( self.data.blanket.p_fw_inboard_nuclear_heat_mw - + self.data.fwbs.psurffwi + + self.data.fwbs.p_fw_inboard_surface_heat_mw + pnucblkti_struct ) f_nuc_fwo = ( self.data.blanket.p_fw_outboard_nuclear_heat_mw - + self.data.fwbs.psurffwo + + self.data.fwbs.p_fw_outboard_surface_heat_mw ) / ( self.data.blanket.p_fw_outboard_nuclear_heat_mw - + self.data.fwbs.psurffwo + + self.data.fwbs.p_fw_outboard_surface_heat_mw + pnucblkto_struct ) else: f_nuc_fwi = ( self.data.blanket.p_fw_inboard_nuclear_heat_mw - + self.data.fwbs.psurffwi + + self.data.fwbs.p_fw_inboard_surface_heat_mw ) / ( self.data.blanket.p_fw_inboard_nuclear_heat_mw - + self.data.fwbs.psurffwi + + self.data.fwbs.p_fw_inboard_surface_heat_mw + self.data.blanket.p_blkt_nuclear_heat_inboard_mw ) f_nuc_fwo = ( self.data.blanket.p_fw_outboard_nuclear_heat_mw - + self.data.fwbs.psurffwo + + self.data.fwbs.p_fw_outboard_surface_heat_mw ) / ( self.data.blanket.p_fw_outboard_nuclear_heat_mw - + self.data.fwbs.psurffwo + + self.data.fwbs.p_fw_outboard_surface_heat_mw + self.data.blanket.p_blkt_nuclear_heat_outboard_mw ) @@ -2267,7 +2267,7 @@ def thermo_hydraulic_model(self, output: bool): self.data.fwbs.radius_fw_channel, self.data.build.dr_fw_inboard, self.data.first_wall.a_fw_inboard, - self.data.fwbs.psurffwi, + self.data.fwbs.p_fw_inboard_surface_heat_mw, self.data.blanket.p_fw_inboard_nuclear_heat_mw, "Inboard first wall", ) @@ -2281,7 +2281,7 @@ def thermo_hydraulic_model(self, output: bool): self.data.fwbs.radius_fw_channel, self.data.build.dr_fw_outboard, self.data.first_wall.a_fw_outboard, - self.data.fwbs.psurffwo, + self.data.fwbs.p_fw_outboard_surface_heat_mw, self.data.blanket.p_fw_outboard_nuclear_heat_mw, "Outboard first wall", ) @@ -2295,13 +2295,19 @@ def thermo_hydraulic_model(self, output: bool): # Total mass flow rate to remove inboard FW power (kg/s) self.data.blanket.mflow_fw_inboard_coolant_total = ( 1.0e6 - * (self.data.blanket.p_fw_inboard_nuclear_heat_mw + self.data.fwbs.psurffwi) + * ( + self.data.blanket.p_fw_inboard_nuclear_heat_mw + + self.data.fwbs.p_fw_inboard_surface_heat_mw + ) / (self.data.fwbs.cp_fw * (fwoutleti - self.data.fwbs.temp_fw_coolant_in)) ) # Total mass flow rate to remove outboard FW power (kg/s) self.data.blanket.mflow_fw_outboard_coolant_total = ( 1.0e6 - * (self.data.blanket.p_fw_outboard_nuclear_heat_mw + self.data.fwbs.psurffwo) + * ( + self.data.blanket.p_fw_outboard_nuclear_heat_mw + + self.data.fwbs.p_fw_outboard_surface_heat_mw + ) / (self.data.fwbs.cp_fw * (fwoutleto - self.data.fwbs.temp_fw_coolant_in)) ) diff --git a/process/models/blankets/dcll.py b/process/models/blankets/dcll.py index 25b49073eb..d38aa44897 100644 --- a/process/models/blankets/dcll.py +++ b/process/models/blankets/dcll.py @@ -212,26 +212,6 @@ def dcll_neutronics_and_power(self, output: bool): # FW - # Radiation power incident on first wall (MW) - self.data.fwbs.p_fw_rad_total_mw = ( - self.data.physics.p_plasma_rad_mw - - self.data.fwbs.p_div_rad_total_mw - - self.data.fwbs.p_fw_hcd_rad_total_mw - ) - - # Surface heat flux on first wall (MW) - # All of the fast particle losses go to the outer wall. - self.data.fwbs.psurffwo = ( - self.data.fwbs.p_fw_rad_total_mw - * self.data.first_wall.a_fw_outboard - / self.data.first_wall.a_fw_total - + self.data.current_drive.p_beam_orbit_loss_mw - + self.data.physics.p_fw_alpha_mw - ) - self.data.fwbs.psurffwi = self.data.fwbs.p_fw_rad_total_mw * ( - 1 - self.data.first_wall.a_fw_outboard / self.data.first_wall.a_fw_total - ) - if output: po.osubhd( self.outfile, "DCLL model: Nuclear and Radiation Heating of Components" @@ -339,8 +319,8 @@ def dcll_power_and_heating(self, output: bool): f_p_shld_coolant_pump_total_heat=self.data.heat_transport.f_p_shld_coolant_pump_total_heat, f_p_div_coolant_pump_total_heat=self.data.heat_transport.f_p_div_coolant_pump_total_heat, p_fw_nuclear_heat_total_mw=self.data.fwbs.p_fw_nuclear_heat_total_mw, - psurffwi=self.data.fwbs.psurffwi, - psurffwo=self.data.fwbs.psurffwo, + p_fw_inboard_surface_heat_mw=self.data.fwbs.p_fw_inboard_surface_heat_mw, + p_fw_outboard_surface_heat_mw=self.data.fwbs.p_fw_outboard_surface_heat_mw, p_blkt_nuclear_heat_total_mw=self.data.fwbs.p_blkt_nuclear_heat_total_mw, p_shld_nuclear_heat_mw=self.data.fwbs.p_shld_nuclear_heat_mw, p_cp_shield_nuclear_heat_mw=self.data.fwbs.p_cp_shield_nuclear_heat_mw, diff --git a/process/models/blankets/hcpb.py b/process/models/blankets/hcpb.py index b62ecc5d6f..dc5f552fb2 100644 --- a/process/models/blankets/hcpb.py +++ b/process/models/blankets/hcpb.py @@ -767,13 +767,6 @@ def powerflow_calc(self, output: bool): self.data.physics.p_plasma_rad_mw * self.data.fwbs.f_a_fw_outboard_hcd ) - # Radiation power incident on first wall (MW) - self.data.fwbs.p_fw_rad_total_mw = ( - self.data.physics.p_plasma_rad_mw - - self.data.fwbs.p_div_rad_total_mw - - self.data.fwbs.p_fw_hcd_rad_total_mw - ) - # If we have chosen pressurised water as the blanket coolant, set the # coolant outlet temperature as 20 deg C below the boiling point if self.data.fwbs.i_blkt_coolant_type == 2: @@ -786,19 +779,6 @@ def powerflow_calc(self, output: bool): outlet_saturated_fluid_properties.temperature - 20.0 ) # in K - # Surface heat flux on first wall (outboard and inboard) (MW) - # All of the fast particle losses go to the outer wall. - self.data.fwbs.psurffwo = ( - self.data.fwbs.p_fw_rad_total_mw - * self.data.first_wall.a_fw_outboard - / self.data.first_wall.a_fw_total - + self.data.current_drive.p_beam_orbit_loss_mw - + self.data.physics.p_fw_alpha_mw - ) - self.data.fwbs.psurffwi = self.data.fwbs.p_fw_rad_total_mw * ( - 1 - self.data.first_wall.a_fw_outboard / self.data.first_wall.a_fw_total - ) - i_p_coolant_pumping = PumpingPowerModelTypes(self.data.fwbs.i_p_coolant_pumping) if i_p_coolant_pumping == PumpingPowerModelTypes.FRACTION_OF_HEAT: # User sets mechanical pumping power directly @@ -813,8 +793,8 @@ def powerflow_calc(self, output: bool): f_p_shld_coolant_pump_total_heat=self.data.heat_transport.f_p_shld_coolant_pump_total_heat, f_p_div_coolant_pump_total_heat=self.data.heat_transport.f_p_div_coolant_pump_total_heat, p_fw_nuclear_heat_total_mw=self.data.fwbs.p_fw_nuclear_heat_total_mw, - psurffwi=self.data.fwbs.psurffwi, - psurffwo=self.data.fwbs.psurffwo, + p_fw_inboard_surface_heat_mw=self.data.fwbs.p_fw_inboard_surface_heat_mw, + p_fw_outboard_surface_heat_mw=self.data.fwbs.p_fw_outboard_surface_heat_mw, p_blkt_nuclear_heat_total_mw=self.data.fwbs.p_blkt_nuclear_heat_total_mw, p_shld_nuclear_heat_mw=self.data.heat_transport.p_shld_nuclear_heat_mw, p_cp_shield_nuclear_heat_mw=self.data.fwbs.p_cp_shield_nuclear_heat_mw, @@ -873,8 +853,8 @@ def powerflow_calc(self, output: bool): fpump = t_in_compressor / (self.data.fwbs.etaiso * dt_he) * (pfactor - 1) p_plasma = ( self.data.fwbs.p_fw_nuclear_heat_total_mw - + self.data.fwbs.psurffwi - + self.data.fwbs.psurffwo + + self.data.fwbs.p_fw_inboard_surface_heat_mw + + self.data.fwbs.p_fw_outboard_surface_heat_mw + self.data.fwbs.p_blkt_nuclear_heat_total_mw ) self.data.primary_pumping.p_fw_blkt_coolant_pump_mw = ( diff --git a/process/models/engineering/ivc_functions.py b/process/models/engineering/ivc_functions.py index b5aafc1913..a22122d214 100644 --- a/process/models/engineering/ivc_functions.py +++ b/process/models/engineering/ivc_functions.py @@ -30,8 +30,8 @@ def pumping_powers_as_fractions( f_p_shld_coolant_pump_total_heat: float, f_p_div_coolant_pump_total_heat: float, p_fw_nuclear_heat_total_mw: float, - psurffwi: float, - psurffwo: float, + p_fw_inboard_surface_heat_mw: float, + p_fw_outboard_surface_heat_mw: float, p_blkt_nuclear_heat_total_mw: float, p_shld_nuclear_heat_mw: float, p_cp_shield_nuclear_heat_mw: float, @@ -53,9 +53,9 @@ def pumping_powers_as_fractions( Fraction for divertor coolant pump. p_fw_nuclear_heat_total_mw : float Total FW nuclear heating (MW). - psurffwi : float + p_fw_inboard_surface_heat_mw : float Inboard FW surface heating (MW). - psurffwo : float + p_fw_outboard_surface_heat_mw : float Outboard FW surface heating (MW). p_blkt_nuclear_heat_total_mw : float Total blanket nuclear heating (MW). @@ -76,7 +76,9 @@ def pumping_powers_as_fractions( Tuple of pumping powers (MW) for FW, blanket, shield, and divertor. """ p_fw_coolant_pump_mw = f_p_fw_coolant_pump_total_heat * ( - p_fw_nuclear_heat_total_mw + psurffwi + psurffwo + p_fw_nuclear_heat_total_mw + + p_fw_inboard_surface_heat_mw + + p_fw_outboard_surface_heat_mw ) p_blkt_coolant_pump_mw = ( f_p_blkt_coolant_pump_total_heat * p_blkt_nuclear_heat_total_mw diff --git a/process/models/fw.py b/process/models/fw.py index be146e78ac..fe4556b9f3 100644 --- a/process/models/fw.py +++ b/process/models/fw.py @@ -119,6 +119,39 @@ def run(self): self.data.physics.p_neutron_total_mw / self.data.first_wall.a_fw_total ) + # Radiation surface heat flux on first wall (MW/m²) + # The full area is used as the radiation is assumed to be uniformly distributed + # across the first wall, so the coverage factors are not applied here. + ( + self.data.fwbs.p_fw_inboard_rad_mw, + self.data.fwbs.pflux_fw_inboard_rad_surface_average_mw, + ) = self.calculate_fw_surface_load( + p_plasma_source_mw=self.data.physics.p_plasma_rad_mw, + f_deg_blkt_poloidal_plasma=self.data.blanket.f_deg_blkt_inboard_poloidal_plasma, + a_fw_full_coverage=self.data.first_wall.a_fw_inboard_full_coverage, + f_a_fw_hcd_ports=0.0, # Inboard is toroidally continous with no ports or + # HCD, so no coverage factor applied + ) + + ( + self.data.fwbs.p_fw_outboard_rad_mw, + self.data.fwbs.pflux_fw_outboard_rad_surface_average_mw, + ) = self.calculate_fw_surface_load( + p_plasma_source_mw=self.data.physics.p_plasma_rad_mw, + f_deg_blkt_poloidal_plasma=self.data.blanket.f_deg_blkt_outboard_poloidal_plasma, + a_fw_full_coverage=self.data.first_wall.a_fw_outboard_full_coverage, + f_a_fw_hcd_ports=self.data.fwbs.f_a_fw_outboard_hcd, # Coverage factor + # applied to outboard wall to account for HCD and ports + ) + + # Radiation power incident on first wall (MW) + # Set based on the total radiation power and the angular fractions taken up by + # the inboard and outboard first wall, which are calculated based on the + # geometry of the first wall and the plasma. + self.data.fwbs.p_fw_rad_total_mw = ( + self.data.fwbs.p_fw_inboard_rad_mw + self.data.fwbs.p_fw_outboard_rad_mw + ) + if self.data.physics.i_pflux_fw_neutron == 1: self.data.physics.pflux_fw_rad_mw = ( self.data.physics.ffwal @@ -135,8 +168,69 @@ def run(self): ) # Power transported to the first wall by escaped alpha particles - self.data.physics.p_fw_alpha_mw = self.data.physics.p_alpha_total_mw * ( - 1.0e0 - self.data.physics.f_p_alpha_plasma_deposited + # Some is lost to HCD and ports on the outboard wall, so this is taken into + # account with a coverage factor. + + ( + self.data.fwbs.p_fw_outboard_alpha_surface_mw, + _, + ) = self.calculate_fw_surface_load( + p_plasma_source_mw=( + self.data.physics.p_alpha_total_mw + * (1.0e0 - self.data.physics.f_p_alpha_plasma_deposited) + ), + f_deg_blkt_poloidal_plasma=1.0, + a_fw_full_coverage=self.data.first_wall.a_fw_outboard_full_coverage, + f_a_fw_hcd_ports=self.data.fwbs.f_a_fw_outboard_hcd, # Coverage factor + # applied to outboard wall to account for HCD and ports + ) + + # Will assume that all alpha power reaching the first wall is deposited on the + # outboard side. + self.data.fwbs.p_fw_inboard_alpha_surface_mw = 0.0 + + self.data.physics.p_fw_alpha_surface_total_mw = ( + self.data.fwbs.p_fw_outboard_alpha_surface_mw + + self.data.fwbs.p_fw_inboard_alpha_surface_mw + ) + + # Surface heat flux on first wall (MW) + # All of the fast particle losses go to the outer wall, as do all beam losses + # and shine through. + # Some power is lost to HCD and ports on the outboard wall, so this is + # taken into account with a coverage factor. + self.data.fwbs.p_fw_outboard_surface_heat_mw = self.calculate_fw_outboard_surface_loads( + p_fw_outboard_rad_mw=self.data.fwbs.p_fw_outboard_rad_mw, + p_beam_orbit_loss_mw=self.data.current_drive.p_beam_orbit_loss_mw, + p_fw_outboard_alpha_surface_mw=self.data.fwbs.p_fw_outboard_alpha_surface_mw, + p_beam_shine_through_mw=self.data.current_drive.p_beam_shine_through_mw, + ) + + self.data.fwbs.p_fw_inboard_surface_heat_mw = ( + self.data.fwbs.p_fw_inboard_rad_mw + + self.data.fwbs.p_fw_inboard_alpha_surface_mw + ) + + ( + self.data.fwbs.p_fw_inboard_neutron_incident_mw, + self.data.fwbs.pflux_fw_inboard_neutron_surface_average_mw, + ) = self.calculate_fw_surface_load( + p_plasma_source_mw=self.data.physics.p_neutron_total_mw, + f_deg_blkt_poloidal_plasma=self.data.blanket.f_deg_blkt_inboard_poloidal_plasma, + a_fw_full_coverage=self.data.first_wall.a_fw_inboard_full_coverage, + f_a_fw_hcd_ports=0.0, # Inboard is toroidally continous with no ports + # or HCD, so no coverage factor applied + ) + + ( + self.data.fwbs.p_fw_outboard_neutron_incident_mw, + self.data.fwbs.pflux_fw_outboard_neutron_surface_average_mw, + ) = self.calculate_fw_surface_load( + p_plasma_source_mw=self.data.physics.p_neutron_total_mw, + f_deg_blkt_poloidal_plasma=self.data.blanket.f_deg_blkt_outboard_poloidal_plasma, + a_fw_full_coverage=self.data.first_wall.a_fw_outboard_full_coverage, + f_a_fw_hcd_ports=self.data.fwbs.f_a_fw_outboard_hcd, # Coverage factor + # applied to outboard wall to account for HCD and ports ) @staticmethod @@ -644,6 +738,47 @@ def fw_temp( mflow_fw_coolant, ) + @staticmethod + def calculate_fw_surface_load( + p_plasma_source_mw: float, + f_deg_blkt_poloidal_plasma: float, + a_fw_full_coverage: float, + f_a_fw_hcd_ports: float = 0.0, + ) -> tuple[float, float]: + """Calculate the surface load on the first wall due to some incident power + + Parameters + ---------- + p_plasma_source_mw: + Total plasma power source, can be neutrons or other forms of energy etc [MW] + f_deg_blkt_poloidal_plasma: + Fraction of the plasma poloidal circumference covered by the first wall. + a_fw_full_coverage: + Area of the first wall with full coverage [m²]. + f_a_fw_hcd_ports: + Fraction of the first wall area occupied by HCD and ports. + This is a loss term that reduces the effective area for power deposition on + the first wall. Default is 0.0 (no loss). + + Returns + ------- + tuple + Power incident on the first wall [MW] and the surface heat flux on the + first wall [MW/m²]. + + Notes + ----- + We use the full coverage area here as the surface is toroidally continous and + the radiation is assumed to be uniformly distributed across the first wall, + so the coverage factors are not applied here. + """ + p_fw_incident_mw = ( + p_plasma_source_mw * f_deg_blkt_poloidal_plasma * (1.0 - f_a_fw_hcd_ports) + ) + pflux_fw_inboard_surface_average_mw = p_fw_incident_mw / a_fw_full_coverage + + return p_fw_incident_mw, pflux_fw_inboard_surface_average_mw + @staticmethod def calculate_total_fw_channels( a_fw_inboard: float, @@ -791,7 +926,7 @@ def output_fw_surface_loads(self): po.ovarre( self.outfile, - "Nominal mean radiation load on vessel first-wall (MW/m^2)", + "Nominal mean radiation load on vessel first-wall [MW/m²]", "(pflux_fw_rad_mw)", self.data.physics.pflux_fw_rad_mw, "OP ", @@ -805,29 +940,139 @@ def output_fw_surface_loads(self): ) po.ovarre( self.outfile, - "Maximum permitted radiation first-wall load (MW/m^2)", + "Maximum permitted radiation first-wall load [MW/m²]", "(pflux_fw_rad_max)", self.data.constraints.pflux_fw_rad_max, "IP ", ) po.ovarre( self.outfile, - "Peak radiation wall load (MW/m^2)", + "Peak radiation wall load [MW/m²]", "(pflux_fw_rad_max_mw)", self.data.constraints.pflux_fw_rad_max_mw, "OP ", ) po.ovarre( self.outfile, - "Fast alpha particle power incident on the first-wall (MW)", - "(p_fw_alpha_mw)", - self.data.physics.p_fw_alpha_mw, + "Radiation heat flux on inboard first wall [MW]", + "(p_fw_inboard_rad_mw)", + self.data.fwbs.p_fw_inboard_rad_mw, + "OP ", + ) + po.ovarre( + self.outfile, + "Radiation surface heat flux on inboard first wall [MW/m²]", + "(pflux_fw_inboard_rad_surface_average_mw)", + self.data.fwbs.pflux_fw_inboard_rad_surface_average_mw, + "OP ", + ) + po.ovarre( + self.outfile, + "Radiation heat flux on outboard first wall [MW]", + "(p_fw_outboard_rad_mw)", + self.data.fwbs.p_fw_outboard_rad_mw, + "OP ", + ) + po.ovarre( + self.outfile, + "Radiation surface heat flux on outboard first wall [MW/m²]", + "(pflux_fw_outboard_rad_surface_average_mw)", + self.data.fwbs.pflux_fw_outboard_rad_surface_average_mw, + "OP ", + ) + po.oblnkl(self.outfile) + po.ovarre( + self.outfile, + "Alpha particle heat flux on inboard first wall [MW]", + "(p_fw_inboard_alpha_surface_mw)", + self.data.fwbs.p_fw_inboard_alpha_surface_mw, + "OP ", + ) + po.ovarre( + self.outfile, + "Alpha particle heat flux on outboard first wall [MW]", + "(p_fw_outboard_alpha_surface_mw)", + self.data.fwbs.p_fw_outboard_alpha_surface_mw, "OP ", ) po.ovarre( self.outfile, - "Nominal mean neutron load on vessel first-wall (MW/m^2)", + "Fast alpha particle power incident on the first-wall [MW]", + "(p_fw_alpha_surface_total_mw)", + self.data.physics.p_fw_alpha_surface_total_mw, + "OP ", + ) + po.oblnkl(self.outfile) + po.ovarre( + self.outfile, + "Nominal mean neutron load on vessel first-wall [MW/m²]", "(pflux_fw_neutron_mw)", self.data.physics.pflux_fw_neutron_mw, "OP ", ) + po.ovarre( + self.outfile, + "Neutron heat flux on inboard first wall [MW]", + "(p_fw_inboard_neutron_incident_mw)", + self.data.fwbs.p_fw_inboard_neutron_incident_mw, + "OP ", + ) + po.ovarre( + self.outfile, + "Nominal mean neutron load on inboard first-wall [MW/m²]", + "(pflux_fw_inboard_neutron_surface_average_mw)", + self.data.fwbs.pflux_fw_inboard_neutron_surface_average_mw, + "OP ", + ) + po.ovarre( + self.outfile, + "Neutron heat flux on outboard first wall [MW]", + "(p_fw_outboard_neutron_incident_mw)", + self.data.fwbs.p_fw_outboard_neutron_incident_mw, + "OP ", + ) + po.ovarre( + self.outfile, + "Nominal mean neutron load on outboard first-wall [MW/m²]", + "(pflux_fw_outboard_neutron_surface_average_mw)", + self.data.fwbs.pflux_fw_outboard_neutron_surface_average_mw, + "OP ", + ) + + @staticmethod + def calculate_fw_outboard_surface_loads( + p_fw_outboard_rad_mw: float, + p_beam_orbit_loss_mw: float, + p_fw_outboard_alpha_surface_mw: float, + p_beam_shine_through_mw: float, + ) -> float: + """Calculate the surface loads on the first wall. + + Parameters + ---------- + p_fw_outboard_rad_mw: + Radiation heat flux on outboard first wall [MW]. + p_beam_orbit_loss_mw: + Beam orbit loss power [MW]. + p_fw_outboard_alpha_surface_mw: + Alpha particle heat flux on outboard first wall [MW]. + p_beam_shine_through_mw: + Beam shine through power [MW]. + + Returns + ------- + p_fw_outboard_surface_heat_mw: + Total surface heat flux on the outboard first wall [MW]. + + """ + # Surface heat flux on first wall (MW) + # All of the fast particle losses go to the outer wall, as do all beam losses + # and shine through. + # Some power is lost to HCD and ports on the outboard wall, so this is + # taken into account with a coverage factor. + return ( + p_fw_outboard_rad_mw + + p_beam_orbit_loss_mw + + p_fw_outboard_alpha_surface_mw + + p_beam_shine_through_mw + ) diff --git a/process/models/power.py b/process/models/power.py index ede6e6f0ec..60afd88f3a 100644 --- a/process/models/power.py +++ b/process/models/power.py @@ -879,7 +879,7 @@ def component_thermal_powers(self): + self.data.heat_transport.p_blkt_breeder_pump_mw + self.data.primary_pumping.p_fw_blkt_coolant_pump_mw + self.data.current_drive.p_beam_orbit_loss_mw - + self.data.physics.p_fw_alpha_mw + + self.data.physics.p_fw_alpha_surface_total_mw + self.data.current_drive.p_beam_shine_through_mw ) else: @@ -890,7 +890,7 @@ def component_thermal_powers(self): + self.data.fwbs.p_blkt_nuclear_heat_total_mw + self.data.primary_pumping.p_fw_blkt_coolant_pump_mw + self.data.current_drive.p_beam_orbit_loss_mw - + self.data.physics.p_fw_alpha_mw + + self.data.physics.p_fw_alpha_surface_total_mw + self.data.current_drive.p_beam_shine_through_mw ) @@ -900,7 +900,7 @@ def component_thermal_powers(self): + self.data.fwbs.p_fw_rad_total_mw + self.data.heat_transport.p_fw_coolant_pump_mw + self.data.current_drive.p_beam_orbit_loss_mw - + self.data.physics.p_fw_alpha_mw + + self.data.physics.p_fw_alpha_surface_total_mw + self.data.current_drive.p_beam_shine_through_mw ) @@ -1110,8 +1110,8 @@ def output_plant_thermal_powers(self): po.ovarre( self.outfile, "Lost alpha-particle heat deposited in FW [MW]", - "(p_fw_alpha_mw)", - self.data.physics.p_fw_alpha_mw, + "(p_fw_alpha_surface_total_mw)", + self.data.physics.p_fw_alpha_surface_total_mw, ) po.ovarre( self.outfile, diff --git a/process/models/stellarator/stellarator.py b/process/models/stellarator/stellarator.py index a35695a1e7..adbfbc4b52 100644 --- a/process/models/stellarator/stellarator.py +++ b/process/models/stellarator/stellarator.py @@ -829,12 +829,12 @@ def st_fwbs(self, output: bool): # Surface heat flux on first wall (MW) (sum = self.data.fwbs.p_fw_rad_total_mw) - psurffwi = ( + p_fw_inboard_surface_heat_mw = ( self.data.fwbs.p_fw_rad_total_mw * self.data.first_wall.a_fw_inboard / self.data.first_wall.a_fw_total ) - psurffwo = ( + p_fw_outboard_surface_heat_mw = ( self.data.fwbs.p_fw_rad_total_mw * self.data.first_wall.a_fw_outboard / self.data.first_wall.a_fw_total @@ -894,8 +894,8 @@ def st_fwbs(self, output: bool): * ( p_fw_inboard_nuclear_heat_mw + p_fw_outboard_nuclear_heat_mw - + psurffwi - + psurffwo + + p_fw_inboard_surface_heat_mw + + p_fw_outboard_surface_heat_mw + self.data.current_drive.p_beam_orbit_loss_mw ) ) @@ -2229,8 +2229,9 @@ def st_phys(self, output): # Power transported to the first wall by escaped alpha particles - self.data.physics.p_fw_alpha_mw = self.data.physics.p_alpha_total_mw * ( - 1.0e0 - self.data.physics.f_p_alpha_plasma_deposited + self.data.physics.p_fw_alpha_surface_total_mw = ( + self.data.physics.p_alpha_total_mw + * (1.0e0 - self.data.physics.f_p_alpha_plasma_deposited) ) # Nominal mean photon wall load diff --git a/tests/unit/models/blankets/test_ccfe_hcpb.py b/tests/unit/models/blankets/test_ccfe_hcpb.py index 8e9f8ae9b9..155b2bc377 100644 --- a/tests/unit/models/blankets/test_ccfe_hcpb.py +++ b/tests/unit/models/blankets/test_ccfe_hcpb.py @@ -706,9 +706,9 @@ class PowerflowCalcParam(NamedTuple): p_cp_shield_nuclear_heat_mw: Any = None - psurffwi: Any = None + p_fw_inboard_surface_heat_mw: Any = None - psurffwo: Any = None + p_fw_outboard_surface_heat_mw: Any = None p_fw_coolant_pump_mw: Any = None @@ -730,7 +730,7 @@ class PowerflowCalcParam(NamedTuple): p_plasma_rad_mw: Any = None - p_fw_alpha_mw: Any = None + p_fw_alpha_surface_total_mw: Any = None p_plasma_separatrix_mw: Any = None @@ -748,12 +748,6 @@ class PowerflowCalcParam(NamedTuple): expected_p_div_rad_total_mw: Any = None - expected_p_fw_rad_total_mw: Any = None - - expected_psurffwi: Any = None - - expected_psurffwo: Any = None - expected_p_shld_coolant_pump_mw: Any = None expected_p_div_coolant_pump_mw: Any = None @@ -783,8 +777,8 @@ class PowerflowCalcParam(NamedTuple): p_shld_nuclear_heat_mw=1.3611259588044891, etaiso=0.90000000000000002, p_cp_shield_nuclear_heat_mw=0, - psurffwi=0, - psurffwo=0, + p_fw_inboard_surface_heat_mw=0, + p_fw_outboard_surface_heat_mw=0, p_fw_coolant_pump_mw=0, f_p_fw_coolant_pump_total_heat=0.0050000000000000001, p_blkt_coolant_pump_mw=0, @@ -795,7 +789,7 @@ class PowerflowCalcParam(NamedTuple): f_p_div_coolant_pump_total_heat=0.0050000000000000001, n_divertors=1, p_plasma_rad_mw=287.44866938104849, - p_fw_alpha_mw=19.835845058655043, + p_fw_alpha_surface_total_mw=19.835845058655043, p_plasma_separatrix_mw=143.6315222649435, p_he=8000000, dp_he=550000, @@ -803,12 +797,9 @@ class PowerflowCalcParam(NamedTuple): t_in_bb=573.13, t_out_bb=773.13, p_fw_blkt_coolant_pump_mw=0, - expected_p_fw_rad_total_mw=254.39207240222791, - expected_psurffwi=97.271629070225231, - expected_psurffwo=176.95628839065773, expected_p_shld_coolant_pump_mw=0.0068056297940224456, expected_p_div_coolant_pump_mw=1.7970292653352464, - expected_p_fw_blkt_coolant_pump_mw=202.00455086503842, + expected_p_fw_blkt_coolant_pump_mw=175.06074627472202, ), PowerflowCalcParam( a_fw_outboard=1168.1172772224481, @@ -829,8 +820,8 @@ class PowerflowCalcParam(NamedTuple): p_shld_nuclear_heat_mw=1.4038170956592293, etaiso=0.90000000000000002, p_cp_shield_nuclear_heat_mw=0, - psurffwi=97.271629070225231, - psurffwo=176.95628839065773, + p_fw_inboard_surface_heat_mw=97.271629070225231, + p_fw_outboard_surface_heat_mw=176.95628839065773, p_fw_coolant_pump_mw=0, f_p_fw_coolant_pump_total_heat=0.0050000000000000001, p_blkt_coolant_pump_mw=0, @@ -841,7 +832,7 @@ class PowerflowCalcParam(NamedTuple): f_p_div_coolant_pump_total_heat=0.0050000000000000001, n_divertors=1, p_plasma_rad_mw=287.44866938104849, - p_fw_alpha_mw=19.829653483586444, + p_fw_alpha_surface_total_mw=19.829653483586444, p_plasma_separatrix_mw=143.51338080047339, p_he=8000000, dp_he=550000, @@ -849,12 +840,9 @@ class PowerflowCalcParam(NamedTuple): t_in_bb=573.13, t_out_bb=773.13, p_fw_blkt_coolant_pump_mw=202.00455086503842, - expected_p_fw_rad_total_mw=254.39207240222791, - expected_psurffwi=97.271629070225259, - expected_psurffwo=176.95009681558912, expected_p_shld_coolant_pump_mw=0.007019085478296147, expected_p_div_coolant_pump_mw=1.7961533897828594, - expected_p_fw_blkt_coolant_pump_mw=201.94492795635171, + expected_p_fw_blkt_coolant_pump_mw=201.94553629918676, ), ], ) @@ -963,9 +951,17 @@ def test_powerflow_calc(powerflowcalcparam, monkeypatch, ccfe_hcpb): powerflowcalcparam.p_cp_shield_nuclear_heat_mw, ) - monkeypatch.setattr(ccfe_hcpb.data.fwbs, "psurffwi", powerflowcalcparam.psurffwi) + monkeypatch.setattr( + ccfe_hcpb.data.fwbs, + "p_fw_inboard_surface_heat_mw", + powerflowcalcparam.p_fw_inboard_surface_heat_mw, + ) - monkeypatch.setattr(ccfe_hcpb.data.fwbs, "psurffwo", powerflowcalcparam.psurffwo) + monkeypatch.setattr( + ccfe_hcpb.data.fwbs, + "p_fw_outboard_surface_heat_mw", + powerflowcalcparam.p_fw_outboard_surface_heat_mw, + ) monkeypatch.setattr( ccfe_hcpb.data.heat_transport, @@ -1024,7 +1020,9 @@ def test_powerflow_calc(powerflowcalcparam, monkeypatch, ccfe_hcpb): ) monkeypatch.setattr( - ccfe_hcpb.data.physics, "p_fw_alpha_mw", powerflowcalcparam.p_fw_alpha_mw + ccfe_hcpb.data.physics, + "p_fw_alpha_surface_total_mw", + powerflowcalcparam.p_fw_alpha_surface_total_mw, ) monkeypatch.setattr( @@ -1059,18 +1057,6 @@ def test_powerflow_calc(powerflowcalcparam, monkeypatch, ccfe_hcpb): ccfe_hcpb.powerflow_calc(False) - assert ccfe_hcpb.data.fwbs.p_fw_rad_total_mw == pytest.approx( - powerflowcalcparam.expected_p_fw_rad_total_mw - ) - - assert ccfe_hcpb.data.fwbs.psurffwi == pytest.approx( - powerflowcalcparam.expected_psurffwi - ) - - assert ccfe_hcpb.data.fwbs.psurffwo == pytest.approx( - powerflowcalcparam.expected_psurffwo - ) - assert ccfe_hcpb.data.heat_transport.p_shld_coolant_pump_mw == pytest.approx( powerflowcalcparam.expected_p_shld_coolant_pump_mw ) diff --git a/tests/unit/models/test_dcll.py b/tests/unit/models/test_dcll.py index 08c0d1585f..4e7fd23bc2 100644 --- a/tests/unit/models/test_dcll.py +++ b/tests/unit/models/test_dcll.py @@ -40,9 +40,9 @@ class DcllNeutronicsAndPowerParam(NamedTuple): p_fw_nuclear_heat_total_mw: Any = None - psurffwi: Any = None + p_fw_inboard_surface_heat_mw: Any = None - psurffwo: Any = None + p_fw_outboard_surface_heat_mw: Any = None p_blkt_nuclear_heat_total_mw: Any = None @@ -62,9 +62,7 @@ class DcllNeutronicsAndPowerParam(NamedTuple): p_plasma_rad_mw: Any = None - p_fw_alpha_mw: Any = None - - expected_p_fw_rad_total_mw: Any = None + p_fw_alpha_surface_total_mw: Any = None expected_p_fw_nuclear_heat_total_mw: Any = None @@ -89,8 +87,8 @@ class DcllNeutronicsAndPowerParam(NamedTuple): p_shld_nuclear_heat_mw=0, p_fw_rad_total_mw=0, p_fw_nuclear_heat_total_mw=0, - psurffwi=0, - psurffwo=0, + p_fw_inboard_surface_heat_mw=0, + p_fw_outboard_surface_heat_mw=0, p_blkt_nuclear_heat_total_mw=0, pnuc_fw_ratio_dcll=0.14000000000000001, pnuc_blkt_ratio_dcll=0.85999999999999999, @@ -100,8 +98,7 @@ class DcllNeutronicsAndPowerParam(NamedTuple): n_divertors=1, p_neutron_total_mw=1587.7386535917431, p_plasma_rad_mw=287.44866938104849, - p_fw_alpha_mw=19.835845058655043, - expected_p_fw_rad_total_mw=254.39207240222791, + p_fw_alpha_surface_total_mw=19.835845058655043, expected_p_fw_nuclear_heat_total_mw=196.72081918001697, expected_p_blkt_nuclear_heat_total_mw=1533.4949914565693, expected_p_blkt_multiplication_mw=325.06710220789364, @@ -119,8 +116,8 @@ class DcllNeutronicsAndPowerParam(NamedTuple): p_shld_nuclear_heat_mw=0, p_fw_rad_total_mw=254.39207240222791, p_fw_nuclear_heat_total_mw=196.72081918001697, - psurffwi=97.271629070225231, - psurffwo=176.95628839065773, + p_fw_inboard_surface_heat_mw=97.271629070225231, + p_fw_outboard_surface_heat_mw=176.95628839065773, p_blkt_nuclear_heat_total_mw=1533.4949914565693, pnuc_fw_ratio_dcll=0.14000000000000001, pnuc_blkt_ratio_dcll=0.85999999999999999, @@ -130,8 +127,7 @@ class DcllNeutronicsAndPowerParam(NamedTuple): n_divertors=1, p_neutron_total_mw=1587.2430556964196, p_plasma_rad_mw=287.44866938104849, - p_fw_alpha_mw=19.829653483586444, - expected_p_fw_rad_total_mw=254.39207240222791, + p_fw_alpha_surface_total_mw=19.829653483586444, expected_p_fw_nuclear_heat_total_mw=196.65941460078642, expected_p_blkt_nuclear_heat_total_mw=1533.0163252173013, expected_p_blkt_multiplication_mw=324.96563552675644, @@ -219,9 +215,17 @@ def test_dcll_neutronics_and_power(dcllneutronicsandpowerparam, monkeypatch, dcl dcllneutronicsandpowerparam.p_fw_nuclear_heat_total_mw, ) - monkeypatch.setattr(dcll.data.fwbs, "psurffwi", dcllneutronicsandpowerparam.psurffwi) + monkeypatch.setattr( + dcll.data.fwbs, + "p_fw_inboard_surface_heat_mw", + dcllneutronicsandpowerparam.p_fw_inboard_surface_heat_mw, + ) - monkeypatch.setattr(dcll.data.fwbs, "psurffwo", dcllneutronicsandpowerparam.psurffwo) + monkeypatch.setattr( + dcll.data.fwbs, + "p_fw_outboard_surface_heat_mw", + dcllneutronicsandpowerparam.p_fw_outboard_surface_heat_mw, + ) monkeypatch.setattr( dcll.data.fwbs, @@ -276,15 +280,13 @@ def test_dcll_neutronics_and_power(dcllneutronicsandpowerparam, monkeypatch, dcl ) monkeypatch.setattr( - dcll.data.physics, "p_fw_alpha_mw", dcllneutronicsandpowerparam.p_fw_alpha_mw + dcll.data.physics, + "p_fw_alpha_surface_total_mw", + dcllneutronicsandpowerparam.p_fw_alpha_surface_total_mw, ) dcll.dcll_neutronics_and_power(False) - assert dcll.data.fwbs.p_fw_rad_total_mw == pytest.approx( - dcllneutronicsandpowerparam.expected_p_fw_rad_total_mw - ) - assert dcll.data.fwbs.p_fw_nuclear_heat_total_mw == pytest.approx( dcllneutronicsandpowerparam.expected_p_fw_nuclear_heat_total_mw ) diff --git a/tests/unit/models/test_fw.py b/tests/unit/models/test_fw.py index 0d609f00a5..f20ee6e8a7 100644 --- a/tests/unit/models/test_fw.py +++ b/tests/unit/models/test_fw.py @@ -161,3 +161,144 @@ def test_fw_temp(fwtempparam, monkeypatch, fw): assert rhofmean == pytest.approx(fwtempparam.expected_rhofmean, rel=1e-4) assert massrate == pytest.approx(fwtempparam.expected_massrate, rel=1e-4) + + +class CalculateFwSurfaceLoadParam(NamedTuple): + p_plasma_source_mw: float + f_deg_blkt_poloidal_plasma: float + a_fw_full_coverage: float + f_a_fw_hcd_ports: float = 0.0 + expected_p_fw_incident_mw: float = None + expected_pflux_fw_surface_mw_m2: float = None + label: str = None + + +@pytest.mark.parametrize( + "calculatefwsurfaceloadparam", + [ + CalculateFwSurfaceLoadParam( + p_plasma_source_mw=1000.0, + f_deg_blkt_poloidal_plasma=0.8, + a_fw_full_coverage=100.0, + f_a_fw_hcd_ports=0.0, + expected_p_fw_incident_mw=800.0, + expected_pflux_fw_surface_mw_m2=8.0, + label="No HCD ports loss", + ), + CalculateFwSurfaceLoadParam( + p_plasma_source_mw=1000.0, + f_deg_blkt_poloidal_plasma=0.8, + a_fw_full_coverage=100.0, + f_a_fw_hcd_ports=0.1, + expected_p_fw_incident_mw=720.0, + expected_pflux_fw_surface_mw_m2=7.2, + label="With HCD ports loss", + ), + CalculateFwSurfaceLoadParam( + p_plasma_source_mw=500.0, + f_deg_blkt_poloidal_plasma=1.0, + a_fw_full_coverage=50.0, + f_a_fw_hcd_ports=0.2, + expected_p_fw_incident_mw=400.0, + expected_pflux_fw_surface_mw_m2=8.0, + label="Full poloidal coverage with ports loss", + ), + CalculateFwSurfaceLoadParam( + p_plasma_source_mw=2000.0, + f_deg_blkt_poloidal_plasma=0.5, + a_fw_full_coverage=200.0, + f_a_fw_hcd_ports=0.0, + expected_p_fw_incident_mw=1000.0, + expected_pflux_fw_surface_mw_m2=5.0, + label="Lower poloidal coverage", + ), + ], +) +def test_calculate_fw_surface_load(calculatefwsurfaceloadparam, fw): + """ + Unit test for calculate_fw_surface_load. + + :param calculatefwsurfaceloadparam: the data used to assert in this test. + :type calculatefwsurfaceloadparam: CalculateFwSurfaceLoadParam + """ + + p_fw_incident_mw, pflux_fw_surface_mw_m2 = fw.calculate_fw_surface_load( + p_plasma_source_mw=calculatefwsurfaceloadparam.p_plasma_source_mw, + f_deg_blkt_poloidal_plasma=calculatefwsurfaceloadparam.f_deg_blkt_poloidal_plasma, + a_fw_full_coverage=calculatefwsurfaceloadparam.a_fw_full_coverage, + f_a_fw_hcd_ports=calculatefwsurfaceloadparam.f_a_fw_hcd_ports, + ) + + assert p_fw_incident_mw == pytest.approx( + calculatefwsurfaceloadparam.expected_p_fw_incident_mw + ) + + assert pflux_fw_surface_mw_m2 == pytest.approx( + calculatefwsurfaceloadparam.expected_pflux_fw_surface_mw_m2 + ) + + +class CalculateFwOutboardSurfaceLoadsParam(NamedTuple): + p_fw_outboard_rad_mw: float + p_beam_orbit_loss_mw: float + p_fw_outboard_alpha_surface_mw: float + p_beam_shine_through_mw: float + expected_p_fw_outboard_surface_heat_mw: float + label: str = None + + +@pytest.mark.parametrize( + "calculatefwoutboardsurfaceloadsparam", + [ + CalculateFwOutboardSurfaceLoadsParam( + p_fw_outboard_rad_mw=100.0, + p_beam_orbit_loss_mw=50.0, + p_fw_outboard_alpha_surface_mw=75.0, + p_beam_shine_through_mw=25.0, + expected_p_fw_outboard_surface_heat_mw=250.0, + label="Equal power contributions", + ), + CalculateFwOutboardSurfaceLoadsParam( + p_fw_outboard_rad_mw=500.0, + p_beam_orbit_loss_mw=100.0, + p_fw_outboard_alpha_surface_mw=150.0, + p_beam_shine_through_mw=50.0, + expected_p_fw_outboard_surface_heat_mw=800.0, + label="Dominant radiation load", + ), + CalculateFwOutboardSurfaceLoadsParam( + p_fw_outboard_rad_mw=0.0, + p_beam_orbit_loss_mw=0.0, + p_fw_outboard_alpha_surface_mw=0.0, + p_beam_shine_through_mw=0.0, + expected_p_fw_outboard_surface_heat_mw=0.0, + label="Zero power contributions", + ), + CalculateFwOutboardSurfaceLoadsParam( + p_fw_outboard_rad_mw=200.0, + p_beam_orbit_loss_mw=75.0, + p_fw_outboard_alpha_surface_mw=100.0, + p_beam_shine_through_mw=25.0, + expected_p_fw_outboard_surface_heat_mw=400.0, + label="Mixed power contributions", + ), + ], +) +def test_calculate_fw_outboard_surface_loads(calculatefwoutboardsurfaceloadsparam, fw): + """ + Unit test for calculate_fw_outboard_surface_loads. + + :param calculatefwoutboardsurfaceloadsparam: the data used to assert in this test. + :type calculatefwoutboardsurfaceloadsparam: CalculateFwOutboardSurfaceLoadsParam + """ + + p_fw_outboard_surface_heat_mw = fw.calculate_fw_outboard_surface_loads( + p_fw_outboard_rad_mw=calculatefwoutboardsurfaceloadsparam.p_fw_outboard_rad_mw, + p_beam_orbit_loss_mw=calculatefwoutboardsurfaceloadsparam.p_beam_orbit_loss_mw, + p_fw_outboard_alpha_surface_mw=calculatefwoutboardsurfaceloadsparam.p_fw_outboard_alpha_surface_mw, + p_beam_shine_through_mw=calculatefwoutboardsurfaceloadsparam.p_beam_shine_through_mw, + ) + + assert p_fw_outboard_surface_heat_mw == pytest.approx( + calculatefwoutboardsurfaceloadsparam.expected_p_fw_outboard_surface_heat_mw + ) diff --git a/tests/unit/models/test_power.py b/tests/unit/models/test_power.py index 8ee838c978..fa18464814 100644 --- a/tests/unit/models/test_power.py +++ b/tests/unit/models/test_power.py @@ -2191,7 +2191,7 @@ class Power2Param(NamedTuple): p_plasma_separatrix_mw: Any = None - p_fw_alpha_mw: Any = None + p_fw_alpha_surface_total_mw: Any = None n_divertors: Any = None @@ -2334,7 +2334,7 @@ class Power2Param(NamedTuple): p_plasma_rad_mw=287.99550050743289, itart=0, p_plasma_separatrix_mw=143.03180561618876, - p_fw_alpha_mw=19.833077403424262, + p_fw_alpha_surface_total_mw=19.833077403424262, n_divertors=1, p_plasma_ohmic_mw=0.61391840981850698, i_rad_loss=1, @@ -2436,7 +2436,7 @@ class Power2Param(NamedTuple): p_plasma_rad_mw=287.99550050743289, itart=0, p_plasma_separatrix_mw=142.91368967092416, - p_fw_alpha_mw=19.826887164528632, + p_fw_alpha_surface_total_mw=19.826887164528632, n_divertors=1, p_plasma_ohmic_mw=0.61391840981850698, i_rad_loss=1, @@ -2788,7 +2788,11 @@ def test_power2(power2param, monkeypatch, power): power.data.physics, "p_plasma_separatrix_mw", power2param.p_plasma_separatrix_mw ) - monkeypatch.setattr(power.data.physics, "p_fw_alpha_mw", power2param.p_fw_alpha_mw) + monkeypatch.setattr( + power.data.physics, + "p_fw_alpha_surface_total_mw", + power2param.p_fw_alpha_surface_total_mw, + ) monkeypatch.setattr(power.data.divertor, "n_divertors", power2param.n_divertors)