From 94c9d6a00e4f22bd0c31f3c148475cfcab65297f Mon Sep 17 00:00:00 2001 From: mn3981 Date: Wed, 15 Oct 2025 13:49:53 +0100 Subject: [PATCH 01/11] Add function to plot system power profiles over time --- process/io/plot_proc.py | 136 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 135 insertions(+), 1 deletion(-) diff --git a/process/io/plot_proc.py b/process/io/plot_proc.py index 286d096c11..003dd3ca2f 100644 --- a/process/io/plot_proc.py +++ b/process/io/plot_proc.py @@ -3242,6 +3242,137 @@ def plot_current_profiles_over_time( axis.grid(True, linestyle="--", alpha=0.6) +def plot_system_power_profiles_over_time( + axis: plt.Axes, mfile_data: mf.MFile, scan: int +) -> None: + """ + Plots the current profiles over time for PF circuits, CS coil, and plasma. + + Arguments: + axis (plt.Axes): Axis object to plot to. + mfile_data (mf.MFile): MFILE data object. + scan (int): Scan number to use. + """ + + t_precharge = mfile_data.data["t_precharge"].get_scan(scan) + t_current_ramp_up = mfile_data.data["t_current_ramp_up"].get_scan(scan) + t_fusion_ramp = mfile_data.data["t_fusion_ramp"].get_scan(scan) + t_burn = mfile_data.data["t_burn"].get_scan(scan) + t_ramp_down = mfile_data.data["t_ramp_down"].get_scan(scan) + + p_plant_electric_base_total_mw = mfile_data.data[ + "p_plant_electric_base_total_mw" + ].get_scan(scan) + p_cryo_plant_electric_mw = mfile_data.data["p_cryo_plant_electric_mw"].get_scan( + scan + ) + p_tritium_plant_electric_mw = mfile_data.data[ + "p_tritium_plant_electric_mw" + ].get_scan(scan) + vachtmw = mfile_data.data["vachtmw"].get_scan(scan) + p_tf_electric_supplies_mw = mfile_data.data["p_tf_electric_supplies_mw"].get_scan( + scan + ) + p_pf_electric_supplies_mw = mfile_data.data["p_pf_electric_supplies_mw"].get_scan( + scan + ) + p_cp_coolant_pump_elec_mw = mfile_data.data["p_cp_coolant_pump_elec_mw"].get_scan( + scan + ) + p_fw_blkt_coolant_pump_elec_mw = mfile_data.data[ + "p_fw_blkt_coolant_pump_elec_mw" + ].get_scan(scan) + p_coolant_pump_elec_total_mw = mfile_data.data[ + "p_coolant_pump_elec_total_mw" + ].get_scan(scan) + p_hcd_electric_total_mw = mfile_data.data["p_hcd_electric_total_mw"].get_scan(scan) + p_fusion_total_mw = mfile_data.data["p_fusion_total_mw"].get_scan(scan) + + # Define a cumulative sum list for each point in the pulse + t_steps = np.cumsum([ + 0, + t_precharge, + t_current_ramp_up, + t_fusion_ramp, + t_burn, + t_ramp_down, + ]) + + # Create empty arrays for the power at each time step for each system + power_profiles = { + "Fusion Power": np.zeros(len(t_steps)), + "Plant Base Load": np.zeros(len(t_steps)), + "Cryo Plant": np.zeros(len(t_steps)), + "Tritium Plant": np.zeros(len(t_steps)), + "Vacuum Pumps": np.zeros(len(t_steps)), + "TF Coil Supplies": np.zeros(len(t_steps)), + "PF Coil Supplies": np.zeros(len(t_steps)), + "CP Coolant Pump Elec": np.zeros(len(t_steps)), + "FW+Blkt Coolant Pump Elec": np.zeros(len(t_steps)), + "Coolant Pump Elec Total": np.zeros(len(t_steps)), + "HCD Electric Total": np.zeros(len(t_steps)), + } + + power_profiles["Fusion Power"][:3] = 0 + power_profiles["Fusion Power"][3:] = p_fusion_total_mw + power_profiles["Plant Base Load"][:] = -p_plant_electric_base_total_mw + power_profiles["Cryo Plant"][:] = -p_cryo_plant_electric_mw + power_profiles["Tritium Plant"][:] = -p_tritium_plant_electric_mw + power_profiles["Vacuum Pumps"][:] = -vachtmw + # TF Coil Supplies: zero for first 3 time steps, then p_tf_electric_supplies_mw + power_profiles["TF Coil Supplies"][:3] = 0 + power_profiles["TF Coil Supplies"][3:] = -p_tf_electric_supplies_mw + power_profiles["PF Coil Supplies"][:2] = 0 + power_profiles["PF Coil Supplies"][2:] = -p_pf_electric_supplies_mw + power_profiles["CP Coolant Pump Elec"][:] = -p_cp_coolant_pump_elec_mw + power_profiles["FW+Blkt Coolant Pump Elec"][:3] = 0 + power_profiles["FW+Blkt Coolant Pump Elec"][3:] = -p_fw_blkt_coolant_pump_elec_mw + power_profiles["Coolant Pump Elec Total"][:] = -p_coolant_pump_elec_total_mw + power_profiles["HCD Electric Total"][:3] = 0 + power_profiles["HCD Electric Total"][3:] = -p_hcd_electric_total_mw + + # Plot each system's power profile over time + for label, powers in power_profiles.items(): + axis.plot(t_steps, powers, label=label) + + # Move the x-axis to 0 on the y-axis + axis.spines["bottom"].set_position("zero") + + # Annotate key points + # Create a secondary x-axis for annotations + secax = axis.secondary_xaxis("bottom") + secax.set_xticks(t_steps) + secax.set_xticklabels( + [ + "Precharge", + r"$I_{\text{P}}$ Ramp-Up", + "Fusion Ramp", + "Burn", + "Ramp Down", + "Between Pulse", + ], + rotation=60, + ) + secax.tick_params(axis="x", which="major") + + # Add axis labels + axis.set_xlabel("Time [s]", fontsize=12) + axis.xaxis.set_label_coords(1.05, 0.5) + axis.set_ylabel("Power [MW]", fontsize=12) + + # Add a title + axis.set_title("System Power Over Time", fontsize=14) + + # Add a legend + axis.legend() + + # axis.set_yscale("symlog") + # axis.set_xscale("symlog") + + # Add a grid for better readability + axis.grid(True, linestyle="--", alpha=0.6) + + def plot_cryostat(axis, _mfile_data, _scan, colour_scheme): """Function to plot cryostat in poloidal cross-section""" @@ -12092,6 +12223,7 @@ def main_plot( fig22, fig23, fig24, + fig25, m_file_data, scan, imp="../data/lz_non_corona_14_elements/", @@ -12366,6 +12498,8 @@ def main_plot( fig24.add_subplot(111, aspect="equal"), m_file_data, scan, fig24 ) + plot_system_power_profiles_over_time(fig20.add_subplot(111), m_file_data, scan) + def main(args=None): # TODO The use of globals here isn't ideal, but is required to get main() @@ -12773,7 +12907,7 @@ def main(args=None): plt.close(page22) plt.close(page23) plt.close(page24) - + plt.close(page25) if __name__ == "__main__": main() From 68d6e245412a413476086a6bb555c276ca089082 Mon Sep 17 00:00:00 2001 From: mn3981 Date: Wed, 15 Oct 2025 15:30:10 +0100 Subject: [PATCH 02/11] Enhance power profile plotting function to include gross and net electric power calculations and improve line styling for better visualization. --- process/io/plot_proc.py | 95 +++++++++++++++++++++++++++++------------ 1 file changed, 68 insertions(+), 27 deletions(-) diff --git a/process/io/plot_proc.py b/process/io/plot_proc.py index 003dd3ca2f..ff97c18daa 100644 --- a/process/io/plot_proc.py +++ b/process/io/plot_proc.py @@ -3246,7 +3246,7 @@ def plot_system_power_profiles_over_time( axis: plt.Axes, mfile_data: mf.MFile, scan: int ) -> None: """ - Plots the current profiles over time for PF circuits, CS coil, and plasma. + Plots the power profiles over time for various systems. Arguments: axis (plt.Axes): Axis object to plot to. @@ -3259,6 +3259,7 @@ def plot_system_power_profiles_over_time( t_fusion_ramp = mfile_data.data["t_fusion_ramp"].get_scan(scan) t_burn = mfile_data.data["t_burn"].get_scan(scan) t_ramp_down = mfile_data.data["t_ramp_down"].get_scan(scan) + t_between_pulse = mfile_data.data["t_between_pulse"].get_scan(scan) p_plant_electric_base_total_mw = mfile_data.data[ "p_plant_electric_base_total_mw" @@ -3276,18 +3277,14 @@ def plot_system_power_profiles_over_time( p_pf_electric_supplies_mw = mfile_data.data["p_pf_electric_supplies_mw"].get_scan( scan ) - p_cp_coolant_pump_elec_mw = mfile_data.data["p_cp_coolant_pump_elec_mw"].get_scan( - scan - ) - p_fw_blkt_coolant_pump_elec_mw = mfile_data.data[ - "p_fw_blkt_coolant_pump_elec_mw" - ].get_scan(scan) p_coolant_pump_elec_total_mw = mfile_data.data[ "p_coolant_pump_elec_total_mw" ].get_scan(scan) p_hcd_electric_total_mw = mfile_data.data["p_hcd_electric_total_mw"].get_scan(scan) p_fusion_total_mw = mfile_data.data["p_fusion_total_mw"].get_scan(scan) - + p_plant_electric_gross_mw = mfile_data.data["p_plant_electric_gross_mw"].get_scan( + scan + ) # Define a cumulative sum list for each point in the pulse t_steps = np.cumsum([ 0, @@ -3296,6 +3293,7 @@ def plot_system_power_profiles_over_time( t_fusion_ramp, t_burn, t_ramp_down, + t_between_pulse, ]) # Create empty arrays for the power at each time step for each system @@ -3307,33 +3305,73 @@ def plot_system_power_profiles_over_time( "Vacuum Pumps": np.zeros(len(t_steps)), "TF Coil Supplies": np.zeros(len(t_steps)), "PF Coil Supplies": np.zeros(len(t_steps)), - "CP Coolant Pump Elec": np.zeros(len(t_steps)), - "FW+Blkt Coolant Pump Elec": np.zeros(len(t_steps)), "Coolant Pump Elec Total": np.zeros(len(t_steps)), "HCD Electric Total": np.zeros(len(t_steps)), + "Gross Electric Power": np.zeros(len(t_steps)), + "Net Electric Power": np.zeros(len(t_steps)), } - power_profiles["Fusion Power"][:3] = 0 - power_profiles["Fusion Power"][3:] = p_fusion_total_mw + power_profiles["Fusion Power"][:2] = 0 + power_profiles["Fusion Power"][2:5] = p_fusion_total_mw + power_profiles["Fusion Power"][5:] = 0 power_profiles["Plant Base Load"][:] = -p_plant_electric_base_total_mw power_profiles["Cryo Plant"][:] = -p_cryo_plant_electric_mw power_profiles["Tritium Plant"][:] = -p_tritium_plant_electric_mw power_profiles["Vacuum Pumps"][:] = -vachtmw # TF Coil Supplies: zero for first 3 time steps, then p_tf_electric_supplies_mw - power_profiles["TF Coil Supplies"][:3] = 0 - power_profiles["TF Coil Supplies"][3:] = -p_tf_electric_supplies_mw - power_profiles["PF Coil Supplies"][:2] = 0 - power_profiles["PF Coil Supplies"][2:] = -p_pf_electric_supplies_mw - power_profiles["CP Coolant Pump Elec"][:] = -p_cp_coolant_pump_elec_mw - power_profiles["FW+Blkt Coolant Pump Elec"][:3] = 0 - power_profiles["FW+Blkt Coolant Pump Elec"][3:] = -p_fw_blkt_coolant_pump_elec_mw - power_profiles["Coolant Pump Elec Total"][:] = -p_coolant_pump_elec_total_mw - power_profiles["HCD Electric Total"][:3] = 0 - power_profiles["HCD Electric Total"][3:] = -p_hcd_electric_total_mw - - # Plot each system's power profile over time + power_profiles["TF Coil Supplies"][0] = 0 + power_profiles["TF Coil Supplies"][1:5] = -p_tf_electric_supplies_mw + power_profiles["TF Coil Supplies"][5:] = 0 + power_profiles["PF Coil Supplies"][0] = 0 + power_profiles["PF Coil Supplies"][1:] = -p_pf_electric_supplies_mw + power_profiles["PF Coil Supplies"][5:] = 0 + power_profiles["Coolant Pump Elec Total"][0:2] = 0 + power_profiles["Coolant Pump Elec Total"][2:] = -p_coolant_pump_elec_total_mw + power_profiles["Coolant Pump Elec Total"][5:] = 0 + power_profiles["HCD Electric Total"][:2] = 0 + power_profiles["HCD Electric Total"][2:5] = -p_hcd_electric_total_mw + power_profiles["HCD Electric Total"][5] = 0 + + power_profiles["Gross Electric Power"][:2] = 0 + power_profiles["Gross Electric Power"][2:5] = p_plant_electric_gross_mw + power_profiles["Gross Electric Power"][5:] = 0 + + # Calculate net electric profile by subtracting all power draws from gross electric + power_profiles["Net Electric Power"] = ( + power_profiles["Gross Electric Power"] + + power_profiles["Plant Base Load"] + + power_profiles["Cryo Plant"] + + power_profiles["Tritium Plant"] + + power_profiles["Vacuum Pumps"] + + power_profiles["TF Coil Supplies"] + + power_profiles["PF Coil Supplies"] + + power_profiles["Coolant Pump Elec Total"] + + power_profiles["HCD Electric Total"] + ) + # Define line styles for each system + # All net drains (negative power flows) use the same line style: dashed + line_styles = { + "Fusion Power": ":", + "Plant Base Load": "--", + "Cryo Plant": "--", + "Tritium Plant": "--", + "Vacuum Pumps": "--", + "TF Coil Supplies": "--", + "PF Coil Supplies": "--", + "Coolant Pump Elec Total": "--", + "HCD Electric Total": "--", + "Gross Electric Power": "-", + "Net Electric Power": "-", + } + + # Plot each system's power profile over time with different line styles for label, powers in power_profiles.items(): - axis.plot(t_steps, powers, label=label) + style = line_styles.get(label, "-") + axis.plot(t_steps, powers, label=label, linestyle=style) + + # power_profiles["Net Electric Power"][:2] = 0 + # power_profiles["Net Electric Power"][2:5] = p_plant_electric_net_mw + # power_profiles["Net Electric Power"][5] = 0 # Move the x-axis to 0 on the y-axis axis.spines["bottom"].set_position("zero") @@ -3350,6 +3388,7 @@ def plot_system_power_profiles_over_time( "Burn", "Ramp Down", "Between Pulse", + "Restart Pulse", ], rotation=60, ) @@ -3366,8 +3405,10 @@ def plot_system_power_profiles_over_time( # Add a legend axis.legend() - # axis.set_yscale("symlog") - # axis.set_xscale("symlog") + axis.set_yscale("symlog") + # axis.set_xscale() + axis.minorticks_on() + axis.grid(True, which="both", linestyle="--", linewidth=0.5, alpha=0.2) # Add a grid for better readability axis.grid(True, linestyle="--", alpha=0.6) From 88d56c940ff0f6286f137ea954a4ce6054025b0e Mon Sep 17 00:00:00 2001 From: mn3981 Date: Wed, 15 Oct 2025 16:31:14 +0100 Subject: [PATCH 03/11] Add power_profiles_over_time method to calculate time-dependent electric power profiles --- process/power.py | 175 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 175 insertions(+) diff --git a/process/power.py b/process/power.py index 7e08e455b2..0798f634c3 100644 --- a/process/power.py +++ b/process/power.py @@ -1408,6 +1408,26 @@ def plant_electric_production(self) -> None: - heat_transport_variables.p_plant_electric_net_mw ) / heat_transport_variables.p_plant_electric_gross_mw + _ = self.power_profiles_over_time( + t_precharge=times_variables.t_precharge, + t_current_ramp_up=times_variables.t_current_ramp_up, + t_fusion_ramp=times_variables.t_fusion_ramp, + t_burn=times_variables.t_burn, + t_ramp_down=times_variables.t_ramp_down, + t_between_pulse=times_variables.t_between_pulse, + p_plant_electric_base_total_mw=heat_transport_variables.p_plant_electric_base_total_mw, + p_cryo_plant_electric_mw=heat_transport_variables.p_cryo_plant_electric_mw, + p_tritium_plant_electric_mw=heat_transport_variables.p_tritium_plant_electric_mw, + vachtmw=heat_transport_variables.vachtmw, + p_tf_electric_supplies_mw=heat_transport_variables.p_tf_electric_supplies_mw, + p_pf_electric_supplies_mw=pfcoil_variables.p_pf_electric_supplies_mw, + p_coolant_pump_elec_total_mw=heat_transport_variables.p_coolant_pump_elec_total_mw, + p_hcd_electric_total_mw=heat_transport_variables.p_hcd_electric_total_mw, + p_fusion_total_mw=physics_variables.p_fusion_total_mw, + p_plant_electric_gross_mw=heat_transport_variables.p_plant_electric_gross_mw, + p_plant_electric_net_mw=heat_transport_variables.p_plant_electric_net_mw, + ) + def cryo( self, i_tf_sup, @@ -2197,3 +2217,158 @@ def tfcpwr( ) return (tfckw, len_tf_bus, drarea, tfcbv, p_tf_electric_supplies_mw) + + def power_profiles_over_time( + self, + t_precharge: float, + t_current_ramp_up: float, + t_fusion_ramp: float, + t_burn: float, + t_ramp_down: float, + t_between_pulse: float, + p_plant_electric_base_total_mw: float, + p_cryo_plant_electric_mw: float, + p_tritium_plant_electric_mw: float, + vachtmw: float, + p_tf_electric_supplies_mw: float, + p_pf_electric_supplies_mw: float, + p_coolant_pump_elec_total_mw: float, + p_hcd_electric_total_mw: float, + p_fusion_total_mw: float, + p_plant_electric_gross_mw: float, + p_plant_electric_net_mw: float, + ) -> float: + """ + Calculate time-dependent power profiles for different electric systems + + :param t_precharge: Precharge time (s). + :type t_precharge: float + :param t_current_ramp_up: Current ramp-up time (s). + :type t_current_ramp_up: float + :param t_fusion_ramp: Fusion ramp time (s). + :type t_fusion_ramp: float + :param t_burn: Burn time (s). + :type t_burn: float + :param t_ramp_down: Ramp-down time (s). + :type t_ramp_down: float + :param t_between_pulse: Time between pulses (s). + :type t_between_pulse: float + :param p_plant_electric_base_total_mw: Plant base electric load (MW). + :type p_plant_electric_base_total_mw: float + :param p_cryo_plant_electric_mw: Cryogenic plant electric load (MW). + :type p_cryo_plant_electric_mw: float + :param p_tritium_plant_electric_mw: Tritium plant electric load (MW). + :type p_tritium_plant_electric_mw: float + :param vachtmw: Vacuum pumps electric load (MW). + :type vachtmw: float + :param p_tf_electric_supplies_mw: TF coil electric supplies (MW). + :type p_tf_electric_supplies_mw: float + :param p_pf_electric_supplies_mw: PF coil electric supplies (MW). + :type p_pf_electric_supplies_mw: float + :param p_coolant_pump_elec_total_mw: Total coolant pump electric load (MW). + :type p_coolant_pump_elec_total_mw: float + :param p_hcd_electric_total_mw: HCD electric total (MW). + :type p_hcd_electric_total_mw: float + :param p_fusion_total_mw: Fusion power (MW). + :type p_fusion_total_mw: float + :param p_plant_electric_gross_mw: Gross electric power produced (MW). + :type p_plant_electric_gross_mw: float + :param p_plant_electric_net_mw: Net electric power produced (MW). + :type p_plant_electric_net_mw: float + + :notes: + - Assumes step-function changes in power at each phase transition. + - Negative values indicate power consumption (loads). + + :returns: Total net electric energy produced over the pulse (MJ). + :rtype: float + """ + + t_steps = np.cumsum([ + 0, + t_precharge, + t_current_ramp_up, + t_fusion_ramp, + t_burn, + t_ramp_down, + t_between_pulse, + ]) + + power_profiles = { + "Fusion Power": np.zeros(len(t_steps)), + "Plant Base Load": np.zeros(len(t_steps)), + "Cryo Plant": np.zeros(len(t_steps)), + "Tritium Plant": np.zeros(len(t_steps)), + "Vacuum Pumps": np.zeros(len(t_steps)), + "TF Coil Supplies": np.zeros(len(t_steps)), + "PF Coil Supplies": np.zeros(len(t_steps)), + "Coolant Pump Elec Total": np.zeros(len(t_steps)), + "HCD Electric Total": np.zeros(len(t_steps)), + "Gross Electric Power": np.zeros(len(t_steps)), + "Net Electric Power": np.zeros(len(t_steps)), + } + # No fusion power until start of current ramp-up, then p_fusion_total_mw during burn, then zero + power_profiles["Fusion Power"][:2] = 0 + power_profiles["Fusion Power"][2:5] = p_fusion_total_mw + power_profiles["Fusion Power"][5:] = 0 + + # Plant Base Load: constant negative load throughout + power_profiles["Plant Base Load"][:] = -p_plant_electric_base_total_mw + + # Cryo Plant: constant negative load throughout + power_profiles["Cryo Plant"][:] = -p_cryo_plant_electric_mw + + # Tritium Plant: constant negative load throughout + power_profiles["Tritium Plant"][:] = -p_tritium_plant_electric_mw + + # Vacuum Pumps: constant negative load throughout + power_profiles["Vacuum Pumps"][:] = -vachtmw + + # TF Coil Supplies: zero for first 3 time steps, then p_tf_electric_supplies_mw + power_profiles["TF Coil Supplies"][0] = 0 + power_profiles["TF Coil Supplies"][1:5] = -p_tf_electric_supplies_mw + power_profiles["TF Coil Supplies"][5:] = 0 + + # PF Coil Supplies: zero for first time step, then p_pf_electric_supplies_mw during ramp-up and burn, then zero + power_profiles["PF Coil Supplies"][0] = 0 + power_profiles["PF Coil Supplies"][1:] = -p_pf_electric_supplies_mw + power_profiles["PF Coil Supplies"][5:] = 0 + + # Coolant Pump Elec Total: zero for first 2 time steps, then p_coolant_pump_elec_total_mw during ramp-up and burn, then zero + power_profiles["Coolant Pump Elec Total"][0:2] = 0 + power_profiles["Coolant Pump Elec Total"][2:] = -p_coolant_pump_elec_total_mw + power_profiles["Coolant Pump Elec Total"][5:] = 0 + + # HCD Electric Total: zero for first 2 time steps, then p_hcd_electric_total_mw during ramp-up and burn, then zero + power_profiles["HCD Electric Total"][:2] = 0 + power_profiles["HCD Electric Total"][2:5] = -p_hcd_electric_total_mw + power_profiles["HCD Electric Total"][5] = 0 + + # Gross Electric Power: zero for first 2 time steps, then p_plant_electric_gross_mw during burn, then zero + power_profiles["Gross Electric Power"][:2] = 0 + power_profiles["Gross Electric Power"][2:5] = p_plant_electric_gross_mw + power_profiles["Gross Electric Power"][5:] = 0 + + # Calculate net electric profile by subtracting all power draws from gross electric + power_profiles["Net Electric Power"] = ( + power_profiles["Gross Electric Power"] + + power_profiles["Plant Base Load"] + + power_profiles["Cryo Plant"] + + power_profiles["Tritium Plant"] + + power_profiles["Vacuum Pumps"] + + power_profiles["TF Coil Supplies"] + + power_profiles["PF Coil Supplies"] + + power_profiles["Coolant Pump Elec Total"] + + power_profiles["HCD Electric Total"] + ) + + if power_profiles["Net Electric Power"][3] != p_plant_electric_net_mw: + logger.error( + "Calculated net electric power during burn does not match input value." + f"Calculated: {power_profiles['Net Electric Power'][3]}, Input: {p_plant_electric_net_mw}" + ) + + # Integrate net electric power over the pulse to get total energy produced (MJ) + # Assume t_steps in seconds, power in MW, so energy in MJ + energy_made_MJ = np.trapz(power_profiles["Net Electric Power"], t_steps) + return energy_made_MJ From f3fd1ca5b68f45d4b84b1bf7427f35fd60ba267f Mon Sep 17 00:00:00 2001 From: mn3981 Date: Wed, 15 Oct 2025 16:33:53 +0100 Subject: [PATCH 04/11] :sparkle: Add `e_plant_net_electric_pulse_mj` variable and update power_profiles_over_time assignment --- process/data_structure/power_variables.py | 6 ++++++ process/power.py | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/process/data_structure/power_variables.py b/process/data_structure/power_variables.py index bb292545e2..1b64fb7f3f 100644 --- a/process/data_structure/power_variables.py +++ b/process/data_structure/power_variables.py @@ -32,6 +32,9 @@ p_plant_core_systems_elec_mw: float = None +e_plant_net_electric_pulse_mj: float = None +"""Net electric energy output per pulse (MJ)""" + f_p_div_primary_heat: float = None delta_eta: float = None @@ -93,6 +96,9 @@ def init_power_variables(): global p_plant_core_systems_elec_mw p_plant_core_systems_elec_mw = 0.0 + global e_plant_net_electric_pulse_mj + e_plant_net_electric_pulse_mj = 0.0 + global f_p_div_primary_heat f_p_div_primary_heat = 0.0 diff --git a/process/power.py b/process/power.py index 0798f634c3..aa054a1072 100644 --- a/process/power.py +++ b/process/power.py @@ -1408,7 +1408,7 @@ def plant_electric_production(self) -> None: - heat_transport_variables.p_plant_electric_net_mw ) / heat_transport_variables.p_plant_electric_gross_mw - _ = self.power_profiles_over_time( + power_variables.e_plant_net_electric_pulse_mj = self.power_profiles_over_time( t_precharge=times_variables.t_precharge, t_current_ramp_up=times_variables.t_current_ramp_up, t_fusion_ramp=times_variables.t_fusion_ramp, From 1d76f99258b10537eb9c5255d13a55bc80d6b2dc Mon Sep 17 00:00:00 2001 From: chris-ashe Date: Wed, 15 Oct 2025 20:21:12 +0100 Subject: [PATCH 05/11] :sparkle: Add `e_plant_net_electric_pulse_kwh` variable and initialize in `init_power_variables` function --- process/data_structure/power_variables.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/process/data_structure/power_variables.py b/process/data_structure/power_variables.py index 1b64fb7f3f..48fc25bea2 100644 --- a/process/data_structure/power_variables.py +++ b/process/data_structure/power_variables.py @@ -35,6 +35,9 @@ e_plant_net_electric_pulse_mj: float = None """Net electric energy output per pulse (MJ)""" +e_plant_net_electric_pulse_kwh: float = None +"""Net electric energy output per pulse (kWh)""" + f_p_div_primary_heat: float = None delta_eta: float = None @@ -99,6 +102,9 @@ def init_power_variables(): global e_plant_net_electric_pulse_mj e_plant_net_electric_pulse_mj = 0.0 + global e_plant_net_electric_pulse_kwh + e_plant_net_electric_pulse_kwh = 0.0 + global f_p_div_primary_heat f_p_div_primary_heat = 0.0 From 159517d100187fdd91b1d64aef70553cf514db41 Mon Sep 17 00:00:00 2001 From: chris-ashe Date: Wed, 15 Oct 2025 22:26:49 +0100 Subject: [PATCH 06/11] Refactor power profile calculations and add electric energy output variables for kWh and MJ --- process/io/plot_proc.py | 84 +++++-------------- process/power.py | 179 ++++++++++++++++++++++++---------------- 2 files changed, 127 insertions(+), 136 deletions(-) diff --git a/process/io/plot_proc.py b/process/io/plot_proc.py index ff97c18daa..39f7175bfd 100644 --- a/process/io/plot_proc.py +++ b/process/io/plot_proc.py @@ -3261,30 +3261,6 @@ def plot_system_power_profiles_over_time( t_ramp_down = mfile_data.data["t_ramp_down"].get_scan(scan) t_between_pulse = mfile_data.data["t_between_pulse"].get_scan(scan) - p_plant_electric_base_total_mw = mfile_data.data[ - "p_plant_electric_base_total_mw" - ].get_scan(scan) - p_cryo_plant_electric_mw = mfile_data.data["p_cryo_plant_electric_mw"].get_scan( - scan - ) - p_tritium_plant_electric_mw = mfile_data.data[ - "p_tritium_plant_electric_mw" - ].get_scan(scan) - vachtmw = mfile_data.data["vachtmw"].get_scan(scan) - p_tf_electric_supplies_mw = mfile_data.data["p_tf_electric_supplies_mw"].get_scan( - scan - ) - p_pf_electric_supplies_mw = mfile_data.data["p_pf_electric_supplies_mw"].get_scan( - scan - ) - p_coolant_pump_elec_total_mw = mfile_data.data[ - "p_coolant_pump_elec_total_mw" - ].get_scan(scan) - p_hcd_electric_total_mw = mfile_data.data["p_hcd_electric_total_mw"].get_scan(scan) - p_fusion_total_mw = mfile_data.data["p_fusion_total_mw"].get_scan(scan) - p_plant_electric_gross_mw = mfile_data.data["p_plant_electric_gross_mw"].get_scan( - scan - ) # Define a cumulative sum list for each point in the pulse t_steps = np.cumsum([ 0, @@ -3311,43 +3287,25 @@ def plot_system_power_profiles_over_time( "Net Electric Power": np.zeros(len(t_steps)), } - power_profiles["Fusion Power"][:2] = 0 - power_profiles["Fusion Power"][2:5] = p_fusion_total_mw - power_profiles["Fusion Power"][5:] = 0 - power_profiles["Plant Base Load"][:] = -p_plant_electric_base_total_mw - power_profiles["Cryo Plant"][:] = -p_cryo_plant_electric_mw - power_profiles["Tritium Plant"][:] = -p_tritium_plant_electric_mw - power_profiles["Vacuum Pumps"][:] = -vachtmw - # TF Coil Supplies: zero for first 3 time steps, then p_tf_electric_supplies_mw - power_profiles["TF Coil Supplies"][0] = 0 - power_profiles["TF Coil Supplies"][1:5] = -p_tf_electric_supplies_mw - power_profiles["TF Coil Supplies"][5:] = 0 - power_profiles["PF Coil Supplies"][0] = 0 - power_profiles["PF Coil Supplies"][1:] = -p_pf_electric_supplies_mw - power_profiles["PF Coil Supplies"][5:] = 0 - power_profiles["Coolant Pump Elec Total"][0:2] = 0 - power_profiles["Coolant Pump Elec Total"][2:] = -p_coolant_pump_elec_total_mw - power_profiles["Coolant Pump Elec Total"][5:] = 0 - power_profiles["HCD Electric Total"][:2] = 0 - power_profiles["HCD Electric Total"][2:5] = -p_hcd_electric_total_mw - power_profiles["HCD Electric Total"][5] = 0 - - power_profiles["Gross Electric Power"][:2] = 0 - power_profiles["Gross Electric Power"][2:5] = p_plant_electric_gross_mw - power_profiles["Gross Electric Power"][5:] = 0 - - # Calculate net electric profile by subtracting all power draws from gross electric - power_profiles["Net Electric Power"] = ( - power_profiles["Gross Electric Power"] - + power_profiles["Plant Base Load"] - + power_profiles["Cryo Plant"] - + power_profiles["Tritium Plant"] - + power_profiles["Vacuum Pumps"] - + power_profiles["TF Coil Supplies"] - + power_profiles["PF Coil Supplies"] - + power_profiles["Coolant Pump Elec Total"] - + power_profiles["HCD Electric Total"] - ) + # Fill power_profiles arrays using vectorized assignment + for label, key in [ + ("Fusion Power", "p_fusion_total_mw"), + ("Gross Electric Power", "p_plant_electric_gross_mw"), + ("Net Electric Power", "p_plant_electric_net_mw"), + ("Plant Base Load", "p_plant_electric_base_total_mw"), + ("Cryo Plant", "p_cryo_plant_electric_mw"), + ("Tritium Plant", "p_tritium_plant_electric_mw"), + ("Vacuum Pumps", "vachtmw"), + ("TF Coil Supplies", "p_tf_electric_supplies_mw"), + ("PF Coil Supplies", "p_pf_electric_supplies_mw"), + ("Coolant Pump Elec Total", "p_coolant_pump_elec_total_mw"), + ("HCD Electric Total", "p_hcd_electric_total_mw"), + ]: + for time in range(len(t_steps)): + power_profiles[label][time] = mfile_data.data.get( + f"{key}_t{time}", mfile_data.data.get(key) + ).get_scan(scan) + # Define line styles for each system # All net drains (negative power flows) use the same line style: dashed line_styles = { @@ -3369,10 +3327,6 @@ def plot_system_power_profiles_over_time( style = line_styles.get(label, "-") axis.plot(t_steps, powers, label=label, linestyle=style) - # power_profiles["Net Electric Power"][:2] = 0 - # power_profiles["Net Electric Power"][2:5] = p_plant_electric_net_mw - # power_profiles["Net Electric Power"][5] = 0 - # Move the x-axis to 0 on the y-axis axis.spines["bottom"].set_position("zero") diff --git a/process/power.py b/process/power.py index aa054a1072..981d4486a4 100644 --- a/process/power.py +++ b/process/power.py @@ -30,6 +30,7 @@ class Power: def __init__(self): self.outfile = constants.NOUT + self.mfile = constants.MFILE def pfpwr(self, output: bool): """ @@ -1303,6 +1304,21 @@ def output_plant_electric_powers(self): heat_transport_variables.p_plant_electric_net_mw, ) + po.oblnkl(self.outfile) + + po.ovarre( + self.outfile, + "Total electric energy output per pulse (MJ)", + "(e_plant_net_electric_pulse_mj)", + power_variables.e_plant_net_electric_pulse_mj, + ) + po.ovarre( + self.outfile, + "Total electric energy output per pulse (kWh)", + "(e_plant_net_electric_pulse_kwh)", + power_variables.e_plant_net_electric_pulse_kwh, + ) + def plant_electric_production(self) -> None: """ This method completes the calculation of the plant's electrical and thermal power flows, @@ -1408,7 +1424,10 @@ def plant_electric_production(self) -> None: - heat_transport_variables.p_plant_electric_net_mw ) / heat_transport_variables.p_plant_electric_gross_mw - power_variables.e_plant_net_electric_pulse_mj = self.power_profiles_over_time( + ( + power_variables.e_plant_net_electric_pulse_kwh, + power_variables.e_plant_net_electric_pulse_mj, + ) = self.power_profiles_over_time( t_precharge=times_variables.t_precharge, t_current_ramp_up=times_variables.t_current_ramp_up, t_fusion_ramp=times_variables.t_fusion_ramp, @@ -2295,80 +2314,98 @@ def power_profiles_over_time( ]) power_profiles = { - "Fusion Power": np.zeros(len(t_steps)), - "Plant Base Load": np.zeros(len(t_steps)), - "Cryo Plant": np.zeros(len(t_steps)), - "Tritium Plant": np.zeros(len(t_steps)), - "Vacuum Pumps": np.zeros(len(t_steps)), - "TF Coil Supplies": np.zeros(len(t_steps)), - "PF Coil Supplies": np.zeros(len(t_steps)), - "Coolant Pump Elec Total": np.zeros(len(t_steps)), - "HCD Electric Total": np.zeros(len(t_steps)), - "Gross Electric Power": np.zeros(len(t_steps)), - "Net Electric Power": np.zeros(len(t_steps)), + "p_fusion_total_mw": np.zeros(len(t_steps)), + "p_plant_electric_base_total_mw": np.zeros(len(t_steps)), + "p_cryo_plant_electric_mw": np.zeros(len(t_steps)), + "p_tritium_plant_electric_mw": np.zeros(len(t_steps)), + "vachtmw": np.zeros(len(t_steps)), + "p_tf_electric_supplies_mw": np.zeros(len(t_steps)), + "p_pf_electric_supplies_mw": np.zeros(len(t_steps)), + "p_coolant_pump_elec_total_mw": np.zeros(len(t_steps)), + "p_hcd_electric_total_mw": np.zeros(len(t_steps)), + "p_plant_electric_gross_mw": np.zeros(len(t_steps)), + "p_plant_electric_net_mw": np.zeros(len(t_steps)), } - # No fusion power until start of current ramp-up, then p_fusion_total_mw during burn, then zero - power_profiles["Fusion Power"][:2] = 0 - power_profiles["Fusion Power"][2:5] = p_fusion_total_mw - power_profiles["Fusion Power"][5:] = 0 - - # Plant Base Load: constant negative load throughout - power_profiles["Plant Base Load"][:] = -p_plant_electric_base_total_mw - - # Cryo Plant: constant negative load throughout - power_profiles["Cryo Plant"][:] = -p_cryo_plant_electric_mw - - # Tritium Plant: constant negative load throughout - power_profiles["Tritium Plant"][:] = -p_tritium_plant_electric_mw - - # Vacuum Pumps: constant negative load throughout - power_profiles["Vacuum Pumps"][:] = -vachtmw - - # TF Coil Supplies: zero for first 3 time steps, then p_tf_electric_supplies_mw - power_profiles["TF Coil Supplies"][0] = 0 - power_profiles["TF Coil Supplies"][1:5] = -p_tf_electric_supplies_mw - power_profiles["TF Coil Supplies"][5:] = 0 - - # PF Coil Supplies: zero for first time step, then p_pf_electric_supplies_mw during ramp-up and burn, then zero - power_profiles["PF Coil Supplies"][0] = 0 - power_profiles["PF Coil Supplies"][1:] = -p_pf_electric_supplies_mw - power_profiles["PF Coil Supplies"][5:] = 0 - - # Coolant Pump Elec Total: zero for first 2 time steps, then p_coolant_pump_elec_total_mw during ramp-up and burn, then zero - power_profiles["Coolant Pump Elec Total"][0:2] = 0 - power_profiles["Coolant Pump Elec Total"][2:] = -p_coolant_pump_elec_total_mw - power_profiles["Coolant Pump Elec Total"][5:] = 0 - - # HCD Electric Total: zero for first 2 time steps, then p_hcd_electric_total_mw during ramp-up and burn, then zero - power_profiles["HCD Electric Total"][:2] = 0 - power_profiles["HCD Electric Total"][2:5] = -p_hcd_electric_total_mw - power_profiles["HCD Electric Total"][5] = 0 - - # Gross Electric Power: zero for first 2 time steps, then p_plant_electric_gross_mw during burn, then zero - power_profiles["Gross Electric Power"][:2] = 0 - power_profiles["Gross Electric Power"][2:5] = p_plant_electric_gross_mw - power_profiles["Gross Electric Power"][5:] = 0 - - # Calculate net electric profile by subtracting all power draws from gross electric - power_profiles["Net Electric Power"] = ( - power_profiles["Gross Electric Power"] - + power_profiles["Plant Base Load"] - + power_profiles["Cryo Plant"] - + power_profiles["Tritium Plant"] - + power_profiles["Vacuum Pumps"] - + power_profiles["TF Coil Supplies"] - + power_profiles["PF Coil Supplies"] - + power_profiles["Coolant Pump Elec Total"] - + power_profiles["HCD Electric Total"] - ) - - if power_profiles["Net Electric Power"][3] != p_plant_electric_net_mw: + + # Fusion power: zero until ramp-up, then during burn + power_profiles["p_fusion_total_mw"][:2] = 0 + power_profiles["p_fusion_total_mw"][2:5] = p_fusion_total_mw + power_profiles["p_fusion_total_mw"][5:] = 0 + + # Plant base load: constant negative load throughout + power_profiles["p_plant_electric_base_total_mw"][ + : + ] = -p_plant_electric_base_total_mw + + # Cryo plant: constant negative load throughout + power_profiles["p_cryo_plant_electric_mw"][:] = -p_cryo_plant_electric_mw + + # Tritium plant: constant negative load throughout + power_profiles["p_tritium_plant_electric_mw"][:] = -p_tritium_plant_electric_mw + + # Vacuum pumps: constant negative load throughout + power_profiles["vachtmw"][:] = -vachtmw + + # TF coil supplies: zero for first step, then negative during ramp-up and burn, then zero + power_profiles["p_tf_electric_supplies_mw"][0] = 0 + power_profiles["p_tf_electric_supplies_mw"][1:5] = -p_tf_electric_supplies_mw + power_profiles["p_tf_electric_supplies_mw"][5:] = 0 + + # PF coil supplies: zero for first step, then negative during ramp-up and burn, then zero + power_profiles["p_pf_electric_supplies_mw"][0] = 0 + power_profiles["p_pf_electric_supplies_mw"][1:5] = -p_pf_electric_supplies_mw + power_profiles["p_pf_electric_supplies_mw"][5:] = 0 + + # Coolant pump elec total: zero for first two steps, then negative during ramp-up and burn, then zero + power_profiles["p_coolant_pump_elec_total_mw"][:2] = 0 + power_profiles["p_coolant_pump_elec_total_mw"][ + 2:5 + ] = -p_coolant_pump_elec_total_mw + power_profiles["p_coolant_pump_elec_total_mw"][5:] = 0 + + # HCD electric total: zero for first two steps, then negative during ramp-up and burn, then zero + power_profiles["p_hcd_electric_total_mw"][:2] = 0 + power_profiles["p_hcd_electric_total_mw"][2:5] = -p_hcd_electric_total_mw + power_profiles["p_hcd_electric_total_mw"][5:] = 0 + + # Gross electric power: zero for first two steps, then positive during burn, then zero + power_profiles["p_plant_electric_gross_mw"][:2] = 0 + power_profiles["p_plant_electric_gross_mw"][2:5] = p_plant_electric_gross_mw + power_profiles["p_plant_electric_gross_mw"][5:] = 0 + + # Net electric power: calculated by subtracting all loads from gross electric power + power_profiles["p_plant_electric_net_mw"] = ( + power_profiles["p_plant_electric_gross_mw"] + + power_profiles["p_plant_electric_base_total_mw"] + + power_profiles["p_cryo_plant_electric_mw"] + + power_profiles["p_tritium_plant_electric_mw"] + + power_profiles["vachtmw"] + + power_profiles["p_tf_electric_supplies_mw"] + + power_profiles["p_pf_electric_supplies_mw"] + + power_profiles["p_coolant_pump_elec_total_mw"] + + power_profiles["p_hcd_electric_total_mw"] + ) + + if power_profiles["p_plant_electric_net_mw"][3] != p_plant_electric_net_mw: logger.error( "Calculated net electric power during burn does not match input value." - f"Calculated: {power_profiles['Net Electric Power'][3]}, Input: {p_plant_electric_net_mw}" + f"Calculated: {power_profiles['p_plant_electric_net_mw'][3]}, Input: {p_plant_electric_net_mw}" ) + for sys in power_profiles: + for i, t in enumerate(t_steps): + po.ovarre( + self.mfile, + f"{sys} at t={t:.1f}s", + f"({sys}_t{i})", + power_profiles[sys][i], + ) + # Integrate net electric power over the pulse to get total energy produced (MJ) # Assume t_steps in seconds, power in MW, so energy in MJ - energy_made_MJ = np.trapz(power_profiles["Net Electric Power"], t_steps) - return energy_made_MJ + energy_made_mj = np.trapzezoid( + power_profiles["p_plant_electric_net_mw"], t_steps + ) + energy_made_kwh = energy_made_mj / 3.6 + + return energy_made_kwh, energy_made_mj From 23a81ea409d422275cb06c271eb151c8e7a39cbd Mon Sep 17 00:00:00 2001 From: mn3981 Date: Thu, 16 Oct 2025 11:20:43 +0100 Subject: [PATCH 07/11] Add pulse timings and energy to plot_proc page --- process/io/plot_proc.py | 65 +++++++++++++++++++++++++++++++++++++++-- process/power.py | 4 +-- 2 files changed, 64 insertions(+), 5 deletions(-) diff --git a/process/io/plot_proc.py b/process/io/plot_proc.py index 39f7175bfd..9402ac5c04 100644 --- a/process/io/plot_proc.py +++ b/process/io/plot_proc.py @@ -3243,7 +3243,10 @@ def plot_current_profiles_over_time( def plot_system_power_profiles_over_time( - axis: plt.Axes, mfile_data: mf.MFile, scan: int + axis: plt.Axes, + mfile_data: mf.MFile, + scan: int, + fig, ) -> None: """ Plots the power profiles over time for various systems. @@ -3367,6 +3370,61 @@ def plot_system_power_profiles_over_time( # Add a grid for better readability axis.grid(True, linestyle="--", alpha=0.6) + # Add energy produced info + textstr_energy = ( + f"$\\mathbf{{Energy \\ Production:}}$\n\n" + f"Energy produced over whole pulse: {mfile_data.data['e_plant_net_electric_pulse_mj'].get_scan(scan):,.4f} MJ \n" + f"Energy produced over whole pulse: {mfile_data.data['e_plant_net_electric_pulse_kwh'].get_scan(scan):,.4f} kWh \n" + ) + + axis.text( + 0.075, + 0.2, + textstr_energy, + fontsize=9, + verticalalignment="top", + transform=fig.transFigure, + bbox={ + "boxstyle": "round", + "facecolor": "grey", + "alpha": 1.0, + "linewidth": 2, + }, + ) + + # Add energy produced info + # helper to convert seconds to "Hh Mm Ss" + def secs_to_hms(s): + """Convert seconds to 'Hh Mm Ss' string.""" + s = float(s) + return f"{int(s // 3600)}h {int((s % 3600) // 60)}m {int(s % 60)}s" + + textstr_times = ( + f"$\\mathbf{{Pulse \\ Timings:}}$\n\n" + f"Coil precharge, $t_{{\\text{{precharge}}}}$: {mfile_data.data['t_precharge'].get_scan(scan):,.1f} s ({secs_to_hms(mfile_data.data['t_precharge'].get_scan(scan))})\n" + f"Current ramp up, $t_{{\\text{{current ramp}}}}$: {mfile_data.data['t_current_ramp_up'].get_scan(scan):,.1f} s ({secs_to_hms(mfile_data.data['t_current_ramp_up'].get_scan(scan))})\n" + f"Fusion ramp, $t_{{\\text{{fusion ramp}}}}$: {mfile_data.data['t_fusion_ramp'].get_scan(scan):,.1f} s ({secs_to_hms(mfile_data.data['t_fusion_ramp'].get_scan(scan))})\n" + f"Burn, $t_{{\\text{{burn}}}}$: {mfile_data.data['t_burn'].get_scan(scan):,.1f} s ({secs_to_hms(mfile_data.data['t_burn'].get_scan(scan))})\n" + f"Ramp down, $t_{{\\text{{ramp down}}}}$: {mfile_data.data['t_ramp_down'].get_scan(scan):,.1f} s ({secs_to_hms(mfile_data.data['t_ramp_down'].get_scan(scan))})\n" + f"Between pulse, $t_{{\\text{{between pulse}}}}$: {mfile_data.data['t_between_pulse'].get_scan(scan):,.1f} s ({secs_to_hms(mfile_data.data['t_between_pulse'].get_scan(scan))})\n\n" + f"Total pulse length, $t_{{\\text{{cycle}}}}$: {mfile_data.data['t_cycle'].get_scan(scan):,.1f} s ({secs_to_hms(mfile_data.data['t_cycle'].get_scan(scan))})\n" + ) + + axis.text( + 0.6, + 0.2, + textstr_times, + fontsize=9, + verticalalignment="top", + transform=fig.transFigure, + bbox={ + "boxstyle": "round", + "facecolor": "grey", + "alpha": 1.0, + "linewidth": 2, + }, + ) + def plot_cryostat(axis, _mfile_data, _scan, colour_scheme): """Function to plot cryostat in poloidal cross-section""" @@ -12493,7 +12551,10 @@ def main_plot( fig24.add_subplot(111, aspect="equal"), m_file_data, scan, fig24 ) - plot_system_power_profiles_over_time(fig20.add_subplot(111), m_file_data, scan) + ax20 = fig20.add_subplot(111) + # set_position([left, bottom, width, height]) -> height ~ 0.66 => ~2/3 of page height + ax20.set_position([0.08, 0.35, 0.84, 0.57]) + plot_system_power_profiles_over_time(ax20, m_file_data, scan, fig20) def main(args=None): diff --git a/process/power.py b/process/power.py index 981d4486a4..783b0b524d 100644 --- a/process/power.py +++ b/process/power.py @@ -2403,9 +2403,7 @@ def power_profiles_over_time( # Integrate net electric power over the pulse to get total energy produced (MJ) # Assume t_steps in seconds, power in MW, so energy in MJ - energy_made_mj = np.trapzezoid( - power_profiles["p_plant_electric_net_mw"], t_steps - ) + energy_made_mj = np.trapz(power_profiles["p_plant_electric_net_mw"], t_steps) energy_made_kwh = energy_made_mj / 3.6 return energy_made_kwh, energy_made_mj From 49bc40dca67b620ba959ada61b6074a90d049e43 Mon Sep 17 00:00:00 2001 From: mn3981 Date: Mon, 27 Oct 2025 11:00:21 +0000 Subject: [PATCH 08/11] Post rebase changes --- process/io/plot_proc.py | 37 +++++++++++++++++++++---------------- process/power.py | 18 ++++++++---------- 2 files changed, 29 insertions(+), 26 deletions(-) diff --git a/process/io/plot_proc.py b/process/io/plot_proc.py index 9402ac5c04..9c0028e4f9 100644 --- a/process/io/plot_proc.py +++ b/process/io/plot_proc.py @@ -3257,12 +3257,16 @@ def plot_system_power_profiles_over_time( scan (int): Scan number to use. """ - t_precharge = mfile_data.data["t_precharge"].get_scan(scan) - t_current_ramp_up = mfile_data.data["t_current_ramp_up"].get_scan(scan) - t_fusion_ramp = mfile_data.data["t_fusion_ramp"].get_scan(scan) - t_burn = mfile_data.data["t_burn"].get_scan(scan) - t_ramp_down = mfile_data.data["t_ramp_down"].get_scan(scan) - t_between_pulse = mfile_data.data["t_between_pulse"].get_scan(scan) + t_precharge = mfile_data.data["t_plant_pulse_coil_precharge"].get_scan(scan) + t_current_ramp_up = mfile_data.data[ + "t_plant_pulse_plasma_current_ramp_up" + ].get_scan(scan) + t_fusion_ramp = mfile_data.data["t_plant_pulse_fusion_ramp"].get_scan(scan) + t_burn = mfile_data.data["t_plant_pulse_burn"].get_scan(scan) + t_ramp_down = mfile_data.data["t_plant_pulse_plasma_current_ramp_down"].get_scan( + scan + ) + t_between_pulse = mfile_data.data["t_plant_pulse_dwell"].get_scan(scan) # Define a cumulative sum list for each point in the pulse t_steps = np.cumsum([ @@ -3401,13 +3405,13 @@ def secs_to_hms(s): textstr_times = ( f"$\\mathbf{{Pulse \\ Timings:}}$\n\n" - f"Coil precharge, $t_{{\\text{{precharge}}}}$: {mfile_data.data['t_precharge'].get_scan(scan):,.1f} s ({secs_to_hms(mfile_data.data['t_precharge'].get_scan(scan))})\n" - f"Current ramp up, $t_{{\\text{{current ramp}}}}$: {mfile_data.data['t_current_ramp_up'].get_scan(scan):,.1f} s ({secs_to_hms(mfile_data.data['t_current_ramp_up'].get_scan(scan))})\n" - f"Fusion ramp, $t_{{\\text{{fusion ramp}}}}$: {mfile_data.data['t_fusion_ramp'].get_scan(scan):,.1f} s ({secs_to_hms(mfile_data.data['t_fusion_ramp'].get_scan(scan))})\n" - f"Burn, $t_{{\\text{{burn}}}}$: {mfile_data.data['t_burn'].get_scan(scan):,.1f} s ({secs_to_hms(mfile_data.data['t_burn'].get_scan(scan))})\n" - f"Ramp down, $t_{{\\text{{ramp down}}}}$: {mfile_data.data['t_ramp_down'].get_scan(scan):,.1f} s ({secs_to_hms(mfile_data.data['t_ramp_down'].get_scan(scan))})\n" - f"Between pulse, $t_{{\\text{{between pulse}}}}$: {mfile_data.data['t_between_pulse'].get_scan(scan):,.1f} s ({secs_to_hms(mfile_data.data['t_between_pulse'].get_scan(scan))})\n\n" - f"Total pulse length, $t_{{\\text{{cycle}}}}$: {mfile_data.data['t_cycle'].get_scan(scan):,.1f} s ({secs_to_hms(mfile_data.data['t_cycle'].get_scan(scan))})\n" + f"Coil precharge, $t_{{\\text{{precharge}}}}$: {mfile_data.data['t_plant_pulse_coil_precharge'].get_scan(scan):,.1f} s ({secs_to_hms(mfile_data.data['t_plant_pulse_coil_precharge'].get_scan(scan))})\n" + f"Current ramp up, $t_{{\\text{{current ramp}}}}$: {mfile_data.data['t_plant_pulse_plasma_current_ramp_up'].get_scan(scan):,.1f} s ({secs_to_hms(mfile_data.data['t_plant_pulse_plasma_current_ramp_up'].get_scan(scan))})\n" + f"Fusion ramp, $t_{{\\text{{fusion ramp}}}}$: {mfile_data.data['t_plant_pulse_fusion_ramp'].get_scan(scan):,.1f} s ({secs_to_hms(mfile_data.data['t_plant_pulse_fusion_ramp'].get_scan(scan))})\n" + f"Burn, $t_{{\\text{{burn}}}}$: {mfile_data.data['t_plant_pulse_burn'].get_scan(scan):,.1f} s ({secs_to_hms(mfile_data.data['t_plant_pulse_burn'].get_scan(scan))})\n" + f"Ramp down, $t_{{\\text{{ramp down}}}}$: {mfile_data.data['t_plant_pulse_plasma_current_ramp_down'].get_scan(scan):,.1f} s ({secs_to_hms(mfile_data.data['t_plant_pulse_plasma_current_ramp_down'].get_scan(scan))})\n" + f"Between pulse, $t_{{\\text{{between pulse}}}}$: {mfile_data.data['t_plant_pulse_dwell'].get_scan(scan):,.1f} s ({secs_to_hms(mfile_data.data['t_plant_pulse_dwell'].get_scan(scan))})\n\n" + f"Total pulse length, $t_{{\\text{{cycle}}}}$: {mfile_data.data['t_plant_pulse_total'].get_scan(scan):,.1f} s ({secs_to_hms(mfile_data.data['t_plant_pulse_total'].get_scan(scan))})\n" ) axis.text( @@ -12551,10 +12555,10 @@ def main_plot( fig24.add_subplot(111, aspect="equal"), m_file_data, scan, fig24 ) - ax20 = fig20.add_subplot(111) + ax24 = fig24.add_subplot(111) # set_position([left, bottom, width, height]) -> height ~ 0.66 => ~2/3 of page height - ax20.set_position([0.08, 0.35, 0.84, 0.57]) - plot_system_power_profiles_over_time(ax20, m_file_data, scan, fig20) + ax24.set_position([0.08, 0.35, 0.84, 0.57]) + plot_system_power_profiles_over_time(ax24, m_file_data, scan, fig24) def main(args=None): @@ -12965,5 +12969,6 @@ def main(args=None): plt.close(page24) plt.close(page25) + if __name__ == "__main__": main() diff --git a/process/power.py b/process/power.py index 783b0b524d..1f644f6390 100644 --- a/process/power.py +++ b/process/power.py @@ -1428,12 +1428,12 @@ def plant_electric_production(self) -> None: power_variables.e_plant_net_electric_pulse_kwh, power_variables.e_plant_net_electric_pulse_mj, ) = self.power_profiles_over_time( - t_precharge=times_variables.t_precharge, - t_current_ramp_up=times_variables.t_current_ramp_up, - t_fusion_ramp=times_variables.t_fusion_ramp, - t_burn=times_variables.t_burn, - t_ramp_down=times_variables.t_ramp_down, - t_between_pulse=times_variables.t_between_pulse, + t_precharge=times_variables.t_plant_pulse_coil_precharge, + t_current_ramp_up=times_variables.t_plant_pulse_plasma_current_ramp_up, + t_fusion_ramp=times_variables.t_plant_pulse_fusion_ramp, + t_burn=times_variables.t_plant_pulse_burn, + t_ramp_down=times_variables.t_plant_pulse_plasma_current_ramp_down, + t_between_pulse=times_variables.t_plant_pulse_dwell, p_plant_electric_base_total_mw=heat_transport_variables.p_plant_electric_base_total_mw, p_cryo_plant_electric_mw=heat_transport_variables.p_cryo_plant_electric_mw, p_tritium_plant_electric_mw=heat_transport_variables.p_tritium_plant_electric_mw, @@ -2346,10 +2346,8 @@ def power_profiles_over_time( # Vacuum pumps: constant negative load throughout power_profiles["vachtmw"][:] = -vachtmw - # TF coil supplies: zero for first step, then negative during ramp-up and burn, then zero - power_profiles["p_tf_electric_supplies_mw"][0] = 0 - power_profiles["p_tf_electric_supplies_mw"][1:5] = -p_tf_electric_supplies_mw - power_profiles["p_tf_electric_supplies_mw"][5:] = 0 + # TF coil supplies: assume coil is always charged, so constant negative load + power_profiles["p_tf_electric_supplies_mw"][:] = -p_tf_electric_supplies_mw # PF coil supplies: zero for first step, then negative during ramp-up and burn, then zero power_profiles["p_pf_electric_supplies_mw"][0] = 0 From 5fd09356965148c19ac9461bd161aebc61e12943 Mon Sep 17 00:00:00 2001 From: mn3981 Date: Wed, 29 Oct 2025 15:50:29 +0000 Subject: [PATCH 09/11] Add electric power profiles for various systems and update output methods --- process/data_structure/power_variables.py | 36 +++++++ process/io/plot_proc.py | 24 ++--- process/output.py | 1 + process/power.py | 119 ++++++++++++++++++++-- 4 files changed, 157 insertions(+), 23 deletions(-) diff --git a/process/data_structure/power_variables.py b/process/data_structure/power_variables.py index 48fc25bea2..464b992213 100644 --- a/process/data_structure/power_variables.py +++ b/process/data_structure/power_variables.py @@ -46,6 +46,39 @@ p_turbine_loss_mw: float = None +p_hcd_electric_total_profile_mw: list[float] = None +"""Profile of total HCD electric power (MW) over pulse""" + +p_tf_electric_supplies_profile_mw: list[float] = None +"""Profile of total TF coil electric power (MW) over pulse""" + +p_pf_electric_supplies_profile_mw: list[float] = None +"""Profile of total PF coil electric power (MW) over pulse""" + +p_coolant_pump_elec_total_profile_mw: list[float] = None +"""Profile of total coolant pump electric power (MW) over pulse""" + +vachtmw_profile_mw: list[float] = None +"""Profile of total active vacuum pump power (MW) over pulse""" + +p_tritium_plant_electric_profile_mw: list[float] = None +"""Profile of total tritium plant electric power (MW) over pulse""" + +p_cryo_plant_electric_profile_mw: list[float] = None +"""Profile of total cryo plant electric power (MW) over pulse""" + +p_plant_electric_base_total__profile_mw: list[float] = None +"""Profile of total plant electric base power (MW) over pulse""" + +p_plant_electric_gross_profile_mw: list[float] = None +"""Profile of total plant electric gross power (MW) over pulse""" + +p_plant_electric_net_profile_mw: list[float] = None +"""Profile of total plant electric net power (MW) over pulse""" + +p_fusion_total_profile_mw: list[float] = None +"""Profile of total fusion power (MW) over pulse""" + def init_power_variables(): global qmisc @@ -116,3 +149,6 @@ def init_power_variables(): global p_turbine_loss_mw p_turbine_loss_mw = 0.0 + + global p_hcd_electric_total_profile_mw + p_hcd_electric_total_profile_mw = [] diff --git a/process/io/plot_proc.py b/process/io/plot_proc.py index 9c0028e4f9..32755788e3 100644 --- a/process/io/plot_proc.py +++ b/process/io/plot_proc.py @@ -3296,21 +3296,21 @@ def plot_system_power_profiles_over_time( # Fill power_profiles arrays using vectorized assignment for label, key in [ - ("Fusion Power", "p_fusion_total_mw"), - ("Gross Electric Power", "p_plant_electric_gross_mw"), - ("Net Electric Power", "p_plant_electric_net_mw"), - ("Plant Base Load", "p_plant_electric_base_total_mw"), - ("Cryo Plant", "p_cryo_plant_electric_mw"), - ("Tritium Plant", "p_tritium_plant_electric_mw"), - ("Vacuum Pumps", "vachtmw"), - ("TF Coil Supplies", "p_tf_electric_supplies_mw"), - ("PF Coil Supplies", "p_pf_electric_supplies_mw"), - ("Coolant Pump Elec Total", "p_coolant_pump_elec_total_mw"), - ("HCD Electric Total", "p_hcd_electric_total_mw"), + ("Fusion Power", "p_fusion_total_profile_mw"), + ("Gross Electric Power", "p_plant_electric_gross_profile_mw"), + ("Net Electric Power", "p_plant_electric_net_profile_mw"), + ("Plant Base Load", "p_plant_electric_base_total_profile_mw"), + ("Cryo Plant", "p_cryo_plant_electric_profile_mw"), + ("Tritium Plant", "p_tritium_plant_electric_profile_mw"), + ("Vacuum Pumps", "vachtmw_profile_mw"), + ("TF Coil Supplies", "p_tf_electric_supplies_profile_mw"), + ("PF Coil Supplies", "p_pf_electric_supplies_profile_mw"), + ("Coolant Pump Elec Total", "p_coolant_pump_elec_total_profile_mw"), + ("HCD Electric Total", "p_hcd_electric_total_profile_mw"), ]: for time in range(len(t_steps)): power_profiles[label][time] = mfile_data.data.get( - f"{key}_t{time}", mfile_data.data.get(key) + f"{key}{time}", mfile_data.data.get(key) ).get_scan(scan) # Define line styles for each system diff --git a/process/output.py b/process/output.py index b6ebd9ac17..527fc678dc 100644 --- a/process/output.py +++ b/process/output.py @@ -140,6 +140,7 @@ def write(models, _outfile): models.power.output_cryogenics() models.power.output_plant_thermal_powers() models.power.output_plant_electric_powers() + models.power.output_power_profiles_over_time() # Water usage in secondary cooling system models.water_use.run(output=True) diff --git a/process/power.py b/process/power.py index 1f644f6390..d4902cbe2e 100644 --- a/process/power.py +++ b/process/power.py @@ -1427,6 +1427,17 @@ def plant_electric_production(self) -> None: ( power_variables.e_plant_net_electric_pulse_kwh, power_variables.e_plant_net_electric_pulse_mj, + power_variables.p_plant_electric_base_total_profile_mw, + power_variables.p_plant_electric_gross_profile_mw, + power_variables.p_plant_electric_net_profile_mw, + power_variables.p_hcd_electric_total_profile_mw, + power_variables.p_coolant_pump_elec_total_profile_mw, + power_variables.p_tf_electric_supplies_profile_mw, + power_variables.p_pf_electric_supplies_profile_mw, + power_variables.vachtmw_profile_mw, + power_variables.p_tritium_plant_electric_profile_mw, + power_variables.p_cryo_plant_electric_profile_mw, + power_variables.p_fusion_total_profile_mw, ) = self.power_profiles_over_time( t_precharge=times_variables.t_plant_pulse_coil_precharge, t_current_ramp_up=times_variables.t_plant_pulse_plasma_current_ramp_up, @@ -2390,18 +2401,104 @@ def power_profiles_over_time( f"Calculated: {power_profiles['p_plant_electric_net_mw'][3]}, Input: {p_plant_electric_net_mw}" ) - for sys in power_profiles: - for i, t in enumerate(t_steps): - po.ovarre( - self.mfile, - f"{sys} at t={t:.1f}s", - f"({sys}_t{i})", - power_profiles[sys][i], - ) - # Integrate net electric power over the pulse to get total energy produced (MJ) # Assume t_steps in seconds, power in MW, so energy in MJ - energy_made_mj = np.trapz(power_profiles["p_plant_electric_net_mw"], t_steps) + energy_made_mj = np.trapz(power_profiles["p_plant_electric_net_mw"], t_steps) # noqa: NPY201 energy_made_kwh = energy_made_mj / 3.6 - return energy_made_kwh, energy_made_mj + return ( + energy_made_kwh, + energy_made_mj, + power_profiles["p_plant_electric_base_total_mw"], + power_profiles["p_plant_electric_gross_mw"], + power_profiles["p_plant_electric_net_mw"], + power_profiles["p_hcd_electric_total_mw"], + power_profiles["p_coolant_pump_elec_total_mw"], + power_profiles["p_tf_electric_supplies_mw"], + power_profiles["p_pf_electric_supplies_mw"], + power_profiles["vachtmw"], + power_profiles["p_tritium_plant_electric_mw"], + power_profiles["p_cryo_plant_electric_mw"], + power_profiles["p_fusion_total_mw"], + ) + + def output_power_profiles_over_time( + self, + ): + for i in range(len(power_variables.p_plant_electric_base_total_profile_mw)): + po.ovarre( + self.mfile, + f"Plant total electric base load at time point {i}", + f"p_plant_electric_base_total_profile_mw{i}", + power_variables.p_plant_electric_base_total_profile_mw[i], + ) + for i in range(len(power_variables.p_plant_electric_gross_profile_mw)): + po.ovarre( + self.mfile, + f"Plant total electric gross at time point {i}", + f"p_plant_electric_gross_profile_mw{i}", + power_variables.p_plant_electric_gross_profile_mw[i], + ) + for i in range(len(power_variables.p_plant_electric_net_profile_mw)): + po.ovarre( + self.mfile, + f"Plant total electric net at time point {i}", + f"p_plant_electric_net_profile_mw{i}", + power_variables.p_plant_electric_net_profile_mw[i], + ) + for i in range(len(power_variables.p_hcd_electric_total_profile_mw)): + po.ovarre( + self.mfile, + f"Plant total electric HCD at time point {i}", + f"p_hcd_electric_total_profile_mw{i}", + power_variables.p_hcd_electric_total_profile_mw[i], + ) + for i in range(len(power_variables.p_coolant_pump_elec_total_profile_mw)): + po.ovarre( + self.mfile, + f"Plant total electric coolant pump at time point {i}", + f"p_coolant_pump_elec_total_profile_mw{i}", + power_variables.p_coolant_pump_elec_total_profile_mw[i], + ) + for i in range(len(power_variables.p_tf_electric_supplies_profile_mw)): + po.ovarre( + self.mfile, + f"Plant total electric TF supplies at time point {i}", + f"p_tf_electric_supplies_profile_mw{i}", + power_variables.p_tf_electric_supplies_profile_mw[i], + ) + for i in range(len(power_variables.p_pf_electric_supplies_profile_mw)): + po.ovarre( + self.mfile, + f"Plant total electric PF supplies at time point {i}", + f"p_pf_electric_supplies_profile_mw{i}", + power_variables.p_pf_electric_supplies_profile_mw[i], + ) + for i in range(len(power_variables.vachtmw_profile_mw)): + po.ovarre( + self.mfile, + f"Plant total electric vacuum pump power at time point {i}", + f"vachtmw_profile_mw{i}", + power_variables.vachtmw_profile_mw[i], + ) + for i in range(len(power_variables.p_tritium_plant_electric_profile_mw)): + po.ovarre( + self.mfile, + f"Plant total electric tritium plant power at time point {i}", + f"p_tritium_plant_electric_profile_mw{i}", + power_variables.p_tritium_plant_electric_profile_mw[i], + ) + for i in range(len(power_variables.p_cryo_plant_electric_profile_mw)): + po.ovarre( + self.mfile, + f"Plant total electric cryo plant power at time point {i}", + f"p_cryo_plant_electric_profile_mw{i}", + power_variables.p_cryo_plant_electric_profile_mw[i], + ) + for i in range(len(power_variables.p_fusion_total_profile_mw)): + po.ovarre( + self.mfile, + f"Plant total electric fusion plant power at time point {i}", + f"p_fusion_total_profile_mw{i}", + power_variables.p_fusion_total_profile_mw[i], + ) From 379a00bbd27202e9d0593d2d66928f062fd1903a Mon Sep 17 00:00:00 2001 From: mn3981 Date: Mon, 3 Nov 2025 14:51:20 +0000 Subject: [PATCH 10/11] Post rebase fixes --- process/io/plot_proc.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/process/io/plot_proc.py b/process/io/plot_proc.py index 32755788e3..d560973dc2 100644 --- a/process/io/plot_proc.py +++ b/process/io/plot_proc.py @@ -3309,9 +3309,7 @@ def plot_system_power_profiles_over_time( ("HCD Electric Total", "p_hcd_electric_total_profile_mw"), ]: for time in range(len(t_steps)): - power_profiles[label][time] = mfile_data.data.get( - f"{key}{time}", mfile_data.data.get(key) - ).get_scan(scan) + power_profiles[label][time] = mfile_data.data[f"{key}{time}"].get_scan(scan) # Define line styles for each system # All net drains (negative power flows) use the same line style: dashed @@ -3416,7 +3414,7 @@ def secs_to_hms(s): axis.text( 0.6, - 0.2, + 0.225, textstr_times, fontsize=9, verticalalignment="top", @@ -12555,10 +12553,10 @@ def main_plot( fig24.add_subplot(111, aspect="equal"), m_file_data, scan, fig24 ) - ax24 = fig24.add_subplot(111) + ax24 = fig25.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_data, scan, fig24) + plot_system_power_profiles_over_time(ax24, m_file_data, scan, fig25) def main(args=None): @@ -12876,6 +12874,7 @@ def main(args=None): page22 = plt.figure(figsize=(12, 9), dpi=80) page23 = plt.figure(figsize=(12, 9), dpi=80) page24 = plt.figure(figsize=(12, 9), dpi=80) + page25 = plt.figure(figsize=(12, 9), dpi=80) # run main_plot main_plot( @@ -12904,6 +12903,7 @@ def main(args=None): page22, page23, page24, + page25, m_file, scan=scan, demo_ranges=demo_ranges, @@ -12937,6 +12937,7 @@ def main(args=None): pdf.savefig(page22) pdf.savefig(page23) pdf.savefig(page24) + pdf.savefig(page25) # show fig if option used if args.show: From ca93576ae7e7ffa7105e7ed21aa28f6051123a31 Mon Sep 17 00:00:00 2001 From: mn3981 Date: Wed, 5 Nov 2025 09:06:32 +0000 Subject: [PATCH 11/11] Refactor output_power_profiles_over_time to use enumerate for cleaner iteration --- process/io/plot_proc.py | 12 +-- process/power.py | 193 ++++++++++++++++++++-------------------- 2 files changed, 104 insertions(+), 101 deletions(-) diff --git a/process/io/plot_proc.py b/process/io/plot_proc.py index d560973dc2..440ca3a699 100644 --- a/process/io/plot_proc.py +++ b/process/io/plot_proc.py @@ -3395,11 +3395,6 @@ def plot_system_power_profiles_over_time( ) # Add energy produced info - # helper to convert seconds to "Hh Mm Ss" - def secs_to_hms(s): - """Convert seconds to 'Hh Mm Ss' string.""" - s = float(s) - return f"{int(s // 3600)}h {int((s % 3600) // 60)}m {int(s % 60)}s" textstr_times = ( f"$\\mathbf{{Pulse \\ Timings:}}$\n\n" @@ -3501,6 +3496,13 @@ def color_key(axis, mfile_data, scan, colour_scheme): ) +# helper to convert seconds to "Hh Mm Ss" +def secs_to_hms(s): + """Convert seconds to 'Hh Mm Ss' string.""" + s = float(s) + return f"{int(s // 3600)}h {int((s % 3600) // 60)}m {int(s % 60)}s" + + def toroidal_cross_section(axis, mfile_data, scan, demo_ranges, colour_scheme): """Function to plot toroidal cross-section Arguments: diff --git a/process/power.py b/process/power.py index d4902cbe2e..936d1de535 100644 --- a/process/power.py +++ b/process/power.py @@ -2,6 +2,7 @@ import math import numpy as np +import scipy as sp from process import constants from process import process_output as po @@ -2324,181 +2325,181 @@ def power_profiles_over_time( t_between_pulse, ]) - power_profiles = { - "p_fusion_total_mw": np.zeros(len(t_steps)), - "p_plant_electric_base_total_mw": np.zeros(len(t_steps)), - "p_cryo_plant_electric_mw": np.zeros(len(t_steps)), - "p_tritium_plant_electric_mw": np.zeros(len(t_steps)), - "vachtmw": np.zeros(len(t_steps)), - "p_tf_electric_supplies_mw": np.zeros(len(t_steps)), - "p_pf_electric_supplies_mw": np.zeros(len(t_steps)), - "p_coolant_pump_elec_total_mw": np.zeros(len(t_steps)), - "p_hcd_electric_total_mw": np.zeros(len(t_steps)), - "p_plant_electric_gross_mw": np.zeros(len(t_steps)), - "p_plant_electric_net_mw": np.zeros(len(t_steps)), - } + # Number of time steps + n_steps = len(t_steps) + + # Initialize arrays for each power profile + p_fusion_total_profile_mw = np.zeros(n_steps) + p_plant_electric_base_total_profile_mw = np.zeros(n_steps) + p_cryo_plant_electric_profile_mw = np.zeros(n_steps) + p_tritium_plant_electric_profile_mw = np.zeros(n_steps) + vachtmw_profile_mw = np.zeros(n_steps) + p_tf_electric_supplies_profile_mw = np.zeros(n_steps) + p_pf_electric_supplies_profile_mw = np.zeros(n_steps) + p_coolant_pump_elec_total_profile_mw = np.zeros(n_steps) + p_hcd_electric_total_profile_mw = np.zeros(n_steps) + p_plant_electric_gross_profile_mw = np.zeros(n_steps) + p_plant_electric_net_profile_mw = np.zeros(n_steps) # Fusion power: zero until ramp-up, then during burn - power_profiles["p_fusion_total_mw"][:2] = 0 - power_profiles["p_fusion_total_mw"][2:5] = p_fusion_total_mw - power_profiles["p_fusion_total_mw"][5:] = 0 + p_fusion_total_profile_mw[:2] = 0 + p_fusion_total_profile_mw[2:5] = p_fusion_total_mw + p_fusion_total_profile_mw[5:] = 0 # Plant base load: constant negative load throughout - power_profiles["p_plant_electric_base_total_mw"][ - : - ] = -p_plant_electric_base_total_mw + p_plant_electric_base_total_profile_mw[:] = -p_plant_electric_base_total_mw # Cryo plant: constant negative load throughout - power_profiles["p_cryo_plant_electric_mw"][:] = -p_cryo_plant_electric_mw + p_cryo_plant_electric_profile_mw[:] = -p_cryo_plant_electric_mw # Tritium plant: constant negative load throughout - power_profiles["p_tritium_plant_electric_mw"][:] = -p_tritium_plant_electric_mw + p_tritium_plant_electric_profile_mw[:] = -p_tritium_plant_electric_mw # Vacuum pumps: constant negative load throughout - power_profiles["vachtmw"][:] = -vachtmw + vachtmw_profile_mw[:] = -vachtmw # TF coil supplies: assume coil is always charged, so constant negative load - power_profiles["p_tf_electric_supplies_mw"][:] = -p_tf_electric_supplies_mw + p_tf_electric_supplies_profile_mw[:] = -p_tf_electric_supplies_mw # PF coil supplies: zero for first step, then negative during ramp-up and burn, then zero - power_profiles["p_pf_electric_supplies_mw"][0] = 0 - power_profiles["p_pf_electric_supplies_mw"][1:5] = -p_pf_electric_supplies_mw - power_profiles["p_pf_electric_supplies_mw"][5:] = 0 + p_pf_electric_supplies_profile_mw[0] = 0 + p_pf_electric_supplies_profile_mw[1:5] = -p_pf_electric_supplies_mw + p_pf_electric_supplies_profile_mw[5:] = 0 # Coolant pump elec total: zero for first two steps, then negative during ramp-up and burn, then zero - power_profiles["p_coolant_pump_elec_total_mw"][:2] = 0 - power_profiles["p_coolant_pump_elec_total_mw"][ - 2:5 - ] = -p_coolant_pump_elec_total_mw - power_profiles["p_coolant_pump_elec_total_mw"][5:] = 0 + p_coolant_pump_elec_total_profile_mw[:2] = 0 + p_coolant_pump_elec_total_profile_mw[2:5] = -p_coolant_pump_elec_total_mw + p_coolant_pump_elec_total_profile_mw[5:] = 0 # HCD electric total: zero for first two steps, then negative during ramp-up and burn, then zero - power_profiles["p_hcd_electric_total_mw"][:2] = 0 - power_profiles["p_hcd_electric_total_mw"][2:5] = -p_hcd_electric_total_mw - power_profiles["p_hcd_electric_total_mw"][5:] = 0 + p_hcd_electric_total_profile_mw[:2] = 0 + p_hcd_electric_total_profile_mw[2:5] = -p_hcd_electric_total_mw + p_hcd_electric_total_profile_mw[5:] = 0 # Gross electric power: zero for first two steps, then positive during burn, then zero - power_profiles["p_plant_electric_gross_mw"][:2] = 0 - power_profiles["p_plant_electric_gross_mw"][2:5] = p_plant_electric_gross_mw - power_profiles["p_plant_electric_gross_mw"][5:] = 0 + p_plant_electric_gross_profile_mw[:2] = 0 + p_plant_electric_gross_profile_mw[2:5] = p_plant_electric_gross_mw + p_plant_electric_gross_profile_mw[5:] = 0 # Net electric power: calculated by subtracting all loads from gross electric power - power_profiles["p_plant_electric_net_mw"] = ( - power_profiles["p_plant_electric_gross_mw"] - + power_profiles["p_plant_electric_base_total_mw"] - + power_profiles["p_cryo_plant_electric_mw"] - + power_profiles["p_tritium_plant_electric_mw"] - + power_profiles["vachtmw"] - + power_profiles["p_tf_electric_supplies_mw"] - + power_profiles["p_pf_electric_supplies_mw"] - + power_profiles["p_coolant_pump_elec_total_mw"] - + power_profiles["p_hcd_electric_total_mw"] - ) - - if power_profiles["p_plant_electric_net_mw"][3] != p_plant_electric_net_mw: + p_plant_electric_net_profile_mw = ( + p_plant_electric_gross_profile_mw + + p_plant_electric_base_total_profile_mw + + p_cryo_plant_electric_profile_mw + + p_tritium_plant_electric_profile_mw + + vachtmw_profile_mw + + p_tf_electric_supplies_profile_mw + + p_pf_electric_supplies_profile_mw + + p_coolant_pump_elec_total_profile_mw + + p_hcd_electric_total_profile_mw + ) + + if p_plant_electric_net_profile_mw[3] != p_plant_electric_net_mw: logger.error( "Calculated net electric power during burn does not match input value." - f"Calculated: {power_profiles['p_plant_electric_net_mw'][3]}, Input: {p_plant_electric_net_mw}" + f"Calculated: {p_plant_electric_net_profile_mw[3]}, Input: {p_plant_electric_net_mw}" ) # Integrate net electric power over the pulse to get total energy produced (MJ) # Assume t_steps in seconds, power in MW, so energy in MJ - energy_made_mj = np.trapz(power_profiles["p_plant_electric_net_mw"], t_steps) # noqa: NPY201 + energy_made_mj = sp.integrate.trapezoid( + p_plant_electric_net_profile_mw, t_steps + ) energy_made_kwh = energy_made_mj / 3.6 return ( energy_made_kwh, energy_made_mj, - power_profiles["p_plant_electric_base_total_mw"], - power_profiles["p_plant_electric_gross_mw"], - power_profiles["p_plant_electric_net_mw"], - power_profiles["p_hcd_electric_total_mw"], - power_profiles["p_coolant_pump_elec_total_mw"], - power_profiles["p_tf_electric_supplies_mw"], - power_profiles["p_pf_electric_supplies_mw"], - power_profiles["vachtmw"], - power_profiles["p_tritium_plant_electric_mw"], - power_profiles["p_cryo_plant_electric_mw"], - power_profiles["p_fusion_total_mw"], + p_plant_electric_base_total_profile_mw, + p_plant_electric_gross_profile_mw, + p_plant_electric_net_profile_mw, + p_hcd_electric_total_profile_mw, + p_coolant_pump_elec_total_profile_mw, + p_tf_electric_supplies_profile_mw, + p_pf_electric_supplies_profile_mw, + vachtmw_profile_mw, + p_tritium_plant_electric_profile_mw, + p_cryo_plant_electric_profile_mw, + p_fusion_total_profile_mw, ) def output_power_profiles_over_time( self, ): - for i in range(len(power_variables.p_plant_electric_base_total_profile_mw)): + for i, val in enumerate(power_variables.p_plant_electric_base_total_profile_mw): po.ovarre( self.mfile, f"Plant total electric base load at time point {i}", - f"p_plant_electric_base_total_profile_mw{i}", - power_variables.p_plant_electric_base_total_profile_mw[i], + f"(p_plant_electric_base_total_profile_mw{i})", + val, ) - for i in range(len(power_variables.p_plant_electric_gross_profile_mw)): + for i, val in enumerate(power_variables.p_plant_electric_gross_profile_mw): po.ovarre( self.mfile, f"Plant total electric gross at time point {i}", - f"p_plant_electric_gross_profile_mw{i}", - power_variables.p_plant_electric_gross_profile_mw[i], + f"(p_plant_electric_gross_profile_mw{i})", + val, ) - for i in range(len(power_variables.p_plant_electric_net_profile_mw)): + for i, val in enumerate(power_variables.p_plant_electric_net_profile_mw): po.ovarre( self.mfile, f"Plant total electric net at time point {i}", - f"p_plant_electric_net_profile_mw{i}", - power_variables.p_plant_electric_net_profile_mw[i], + f"(p_plant_electric_net_profile_mw{i})", + val, ) - for i in range(len(power_variables.p_hcd_electric_total_profile_mw)): + for i, val in enumerate(power_variables.p_hcd_electric_total_profile_mw): po.ovarre( self.mfile, f"Plant total electric HCD at time point {i}", - f"p_hcd_electric_total_profile_mw{i}", - power_variables.p_hcd_electric_total_profile_mw[i], + f"(p_hcd_electric_total_profile_mw{i})", + val, ) - for i in range(len(power_variables.p_coolant_pump_elec_total_profile_mw)): + for i, val in enumerate(power_variables.p_coolant_pump_elec_total_profile_mw): po.ovarre( self.mfile, f"Plant total electric coolant pump at time point {i}", - f"p_coolant_pump_elec_total_profile_mw{i}", - power_variables.p_coolant_pump_elec_total_profile_mw[i], + f"(p_coolant_pump_elec_total_profile_mw{i})", + val, ) - for i in range(len(power_variables.p_tf_electric_supplies_profile_mw)): + for i, val in enumerate(power_variables.p_tf_electric_supplies_profile_mw): po.ovarre( self.mfile, f"Plant total electric TF supplies at time point {i}", - f"p_tf_electric_supplies_profile_mw{i}", - power_variables.p_tf_electric_supplies_profile_mw[i], + f"(p_tf_electric_supplies_profile_mw{i})", + val, ) - for i in range(len(power_variables.p_pf_electric_supplies_profile_mw)): + for i, val in enumerate(power_variables.p_pf_electric_supplies_profile_mw): po.ovarre( self.mfile, f"Plant total electric PF supplies at time point {i}", - f"p_pf_electric_supplies_profile_mw{i}", - power_variables.p_pf_electric_supplies_profile_mw[i], + f"(p_pf_electric_supplies_profile_mw{i})", + val, ) - for i in range(len(power_variables.vachtmw_profile_mw)): + for i, val in enumerate(power_variables.vachtmw_profile_mw): po.ovarre( self.mfile, f"Plant total electric vacuum pump power at time point {i}", - f"vachtmw_profile_mw{i}", - power_variables.vachtmw_profile_mw[i], + f"(vachtmw_profile_mw{i})", + val, ) - for i in range(len(power_variables.p_tritium_plant_electric_profile_mw)): + for i, val in enumerate(power_variables.p_tritium_plant_electric_profile_mw): po.ovarre( self.mfile, f"Plant total electric tritium plant power at time point {i}", - f"p_tritium_plant_electric_profile_mw{i}", - power_variables.p_tritium_plant_electric_profile_mw[i], + f"(p_tritium_plant_electric_profile_mw{i})", + val, ) - for i in range(len(power_variables.p_cryo_plant_electric_profile_mw)): + for i, val in enumerate(power_variables.p_cryo_plant_electric_profile_mw): po.ovarre( self.mfile, f"Plant total electric cryo plant power at time point {i}", - f"p_cryo_plant_electric_profile_mw{i}", - power_variables.p_cryo_plant_electric_profile_mw[i], + f"(p_cryo_plant_electric_profile_mw{i})", + val, ) - for i in range(len(power_variables.p_fusion_total_profile_mw)): + for i, val in enumerate(power_variables.p_fusion_total_profile_mw): po.ovarre( self.mfile, f"Plant total electric fusion plant power at time point {i}", - f"p_fusion_total_profile_mw{i}", - power_variables.p_fusion_total_profile_mw[i], + f"(p_fusion_total_profile_mw{i})", + val, )