From 7ef031f3e0047f9820ca07bd92fe79deaecc2401 Mon Sep 17 00:00:00 2001 From: mn3981 Date: Fri, 14 Nov 2025 20:38:08 +0000 Subject: [PATCH 1/7] Add method to calculate dimensionless current drive efficiency --- process/current_drive.py | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/process/current_drive.py b/process/current_drive.py index 3c7cb4f7fe..47672ecde8 100644 --- a/process/current_drive.py +++ b/process/current_drive.py @@ -1911,6 +1911,43 @@ def cudriv(self) -> None: ) ) + def calculate_dimensionless_current_drive_efficiency( + self, + nd_plasma_electrons_vol_avg: float, + rmajor: float, + temp_plasma_electron_vol_avg_kev: float, + c_hcd_driven: float, + p_hcd_injected: float, + ) -> float: + """ + Calculate the dimensionless current drive efficiency, ζ. + + This function computes the dimensionless current drive efficiency + based on the average electron density, major radius, and electron temperature. + + :param nd_plasma_electrons_vol_avg: Volume averaged electron density in m^-3. + :type nd_plasma_electrons_vol_avg: float + :param rmajor: Major radius of the plasma in meters. + :type rmajor: float + :param temp_plasma_electron_vol_avg_kev: Volume averaged electron temperature in keV. + :type temp_plasma_electron_vol_avg_kev: float + :param c_hcd_driven: Current driven by the heating and current drive system. + :type c_hcd_driven: float + :param p_hcd_injected: Power injected by the heating and current drive system. + :type p_hcd_injected: float + :return: The calculated dimensionless current drive efficiency. + :rtype: float + + :notes: + + """ + + return ( + (constants.ELECTRON_CHARGE**3 / constants.EPSILON0**2) + * (nd_plasma_electrons_vol_avg * rmajor / temp_plasma_electron_vol_avg_kev) + * (c_hcd_driven / p_hcd_injected) + ) + def output_current_drive(self): """ Output the current drive information to the output file. From 79a8557b95a6a35e5903a8b703c02012ca915f75 Mon Sep 17 00:00:00 2001 From: mn3981 Date: Fri, 14 Nov 2025 20:40:47 +0000 Subject: [PATCH 2/7] :sparkle: Add dimensionless current drive efficiency variable for primary HCD system --- process/data_structure/current_drive_variables.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/process/data_structure/current_drive_variables.py b/process/data_structure/current_drive_variables.py index 8c6fc0b70a..06bed164ba 100644 --- a/process/data_structure/current_drive_variables.py +++ b/process/data_structure/current_drive_variables.py @@ -216,6 +216,9 @@ eta_cd_norm_hcd_primary: float = None """Normalised current drive efficiency for primary HCD system [(1.0e20 A)/(W m^2)]""" +eta_cd_dimensionless_hcd_primary: float = None +"""Dimensionless current drive efficiency for primary HCD system (ζ)""" + eta_cd_norm_hcd_secondary: float = None """Normalised current drive efficiency for secondary HCD system [(1.0e20 A)/(W m^2)]""" @@ -424,6 +427,7 @@ def init_current_drive_variables(): global f_radius_beam_tangency_rmajor global f_beam_tritium global eta_cd_norm_hcd_primary + global eta_cd_dimensionless_hcd_primary global eta_cd_norm_hcd_secondary global eta_cd_norm_ecrh global xi_ebw @@ -497,6 +501,7 @@ def init_current_drive_variables(): f_radius_beam_tangency_rmajor = 1.05 f_beam_tritium = 1e-6 eta_cd_norm_hcd_primary = 0.0 + eta_cd_dimensionless_hcd_primary = 0.0 eta_cd_norm_ecrh = 0.35 xi_ebw = 0.8 i_hcd_primary = 5 From 66fe5cdcea1fc59c49b5a6075971015ff7e9986f Mon Sep 17 00:00:00 2001 From: mn3981 Date: Fri, 14 Nov 2025 20:42:01 +0000 Subject: [PATCH 3/7] Add dimensionless current drive efficiency variable for secondary HCD system --- process/data_structure/current_drive_variables.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/process/data_structure/current_drive_variables.py b/process/data_structure/current_drive_variables.py index 06bed164ba..e7c3718978 100644 --- a/process/data_structure/current_drive_variables.py +++ b/process/data_structure/current_drive_variables.py @@ -223,6 +223,9 @@ eta_cd_norm_hcd_secondary: float = None """Normalised current drive efficiency for secondary HCD system [(1.0e20 A)/(W m^2)]""" +eta_cd_dimensionless_hcd_secondary: float = None +"""Dimensionless current drive efficiency for secondary HCD system (ζ)""" + eta_cd_norm_ecrh: float = None """User input ECRH gamma (1.0e20 A/(W m^2))""" @@ -429,6 +432,7 @@ def init_current_drive_variables(): global eta_cd_norm_hcd_primary global eta_cd_dimensionless_hcd_primary global eta_cd_norm_hcd_secondary + global eta_cd_dimensionless_hcd_secondary global eta_cd_norm_ecrh global xi_ebw global i_hcd_primary @@ -526,6 +530,7 @@ def init_current_drive_variables(): n_beam_decay_lengths_core = 0.0 n_beam_decay_lengths_core_required = 3.0 eta_cd_norm_hcd_secondary = 0.0 + eta_cd_dimensionless_hcd_secondary = 0.0 eta_cd_hcd_secondary = 0.0 p_ebw_injected_mw = 0.0 p_hcd_ecrh_electric_mw = 0.0 From 0ff6a7dc78c1242ff834f09dce570e04216699eb Mon Sep 17 00:00:00 2001 From: mn3981 Date: Fri, 14 Nov 2025 20:48:13 +0000 Subject: [PATCH 4/7] Add dimensionless current drive efficiency calculations for primary and secondary heating methods --- process/current_drive.py | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/process/current_drive.py b/process/current_drive.py index 47672ecde8..6d660dd22a 100644 --- a/process/current_drive.py +++ b/process/current_drive.py @@ -1479,6 +1479,27 @@ def cudriv(self) -> None: / physics_variables.plasma_current ) + # Calculate the dimensionless current drive efficiency for the primary heating method (ζ) + current_drive_variables.eta_cd_dimensionless_hcd_primary = self.calculate_dimensionless_current_drive_efficiency( + nd_plasma_electrons_vol_avg=physics_variables.nd_plasma_electrons_vol_avg, + rmajor=physics_variables.rmajor, + temp_plasma_electron_vol_avg_kev=physics_variables.temp_plasma_electron_vol_avg_kev, + c_hcd_driven=current_drive_variables.c_hcd_primary_driven, + p_hcd_injected=current_drive_variables.p_hcd_primary_injected_mw + * 1.0e6, + ) + + if current_drive_variables.p_hcd_secondary_injected_mw > 0.0: + # Calculate the dimensionless current drive efficiency for the secondary heating method (ζ) + current_drive_variables.eta_cd_dimensionless_hcd_secondary = self.calculate_dimensionless_current_drive_efficiency( + nd_plasma_electrons_vol_avg=physics_variables.nd_plasma_electrons_vol_avg, + rmajor=physics_variables.rmajor, + temp_plasma_electron_vol_avg_kev=physics_variables.temp_plasma_electron_vol_avg_kev, + c_hcd_driven=current_drive_variables.c_hcd_secondary_driven, + p_hcd_injected=current_drive_variables.p_hcd_secondary_injected_mw + * 1.0e6, + ) + # =========================================================== # Calculate the wall plug power for the secondary heating method @@ -2027,6 +2048,13 @@ def output_current_drive(self): current_drive_variables.eta_cd_norm_hcd_primary, "OP ", ) + po.ovarre( + self.outfile, + "Dimensionless current drive efficiency of primary system, ζ", + "(eta_cd_dimensionless_hcd_primary)", + current_drive_variables.eta_cd_dimensionless_hcd_primary, + "OP ", + ) if current_drive_variables.i_hcd_primary == 10: po.ovarre( self.outfile, @@ -2248,6 +2276,13 @@ def output_current_drive(self): current_drive_variables.eta_cd_norm_hcd_secondary, "OP ", ) + po.ovarre( + self.outfile, + "Dimensionless current drive efficiency of secondary system, ζ", + "(eta_cd_dimensionless_hcd_secondary)", + current_drive_variables.eta_cd_dimensionless_hcd_secondary, + "OP ", + ) if current_drive_variables.i_hcd_secondary == 10: po.ovarre( self.outfile, From fe6393cf5852e6bf3adfcf070e539761468f02b0 Mon Sep 17 00:00:00 2001 From: mn3981 Date: Fri, 14 Nov 2025 20:55:59 +0000 Subject: [PATCH 5/7] Add output to plot_proc --- process/io/plot_proc.py | 97 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 95 insertions(+), 2 deletions(-) diff --git a/process/io/plot_proc.py b/process/io/plot_proc.py index 1e0d2c7258..c64157c336 100644 --- a/process/io/plot_proc.py +++ b/process/io/plot_proc.py @@ -36,6 +36,7 @@ import process.io.mfile as mf import process.superconducting_tf_coil as sctf from process.build import Build +from process.current_drive import ElectronBernstein, ElectronCyclotron from process.data_structure import physics_variables from process.geometry.blanket_geometry import ( blanket_geometry_double_null, @@ -2587,13 +2588,13 @@ def plot_main_plasma_information( f"$\\mathbf{{Primary \\ system: {primary_heating}}}$ \n" f"Current driving power {mfile_data.data['p_hcd_primary_injected_mw'].get_scan(scan):.4f} MW\n" f"Extra heat power: {mfile_data.data['p_hcd_primary_extra_heat_mw'].get_scan(scan):.4f} MW\n" - f"$\\gamma_{{\\text{{CD,prim}}}}$: {mfile_data.data['eta_cd_hcd_primary'].get_scan(scan):.4f} A/W\n" + f"$\\gamma_{{\\text{{CD,prim}}}}$: {mfile_data.data['eta_cd_hcd_primary'].get_scan(scan):.4f} A/W | $\\zeta_{{\\text{{CD,prim}}}}$: {mfile_data.data['eta_cd_dimensionless_hcd_primary'].get_scan(scan):.4f} \n" f"$\\eta_{{\\text{{CD,prim}}}}$: {mfile_data.data['eta_cd_norm_hcd_primary'].get_scan(scan):.4f} $\\times 10^{{20}} \\mathrm{{A}} / \\mathrm{{Wm}}^2$\n" f"Current driven by primary: {mfile_data.data['c_hcd_primary_driven'].get_scan(scan) / 1e6:.3f} MA\n\n" f"$\\mathbf{{Secondary \\ system: {secondary_heating}}}$ \n" f"Current driving power {mfile_data.data['p_hcd_secondary_injected_mw'].get_scan(scan):.4f} MW\n" f"Extra heat power: {mfile_data.data['p_hcd_secondary_extra_heat_mw'].get_scan(scan):.4f} MW\n" - f"$\\gamma_{{\\text{{CD,sec}}}}$: {mfile_data.data['eta_cd_hcd_secondary'].get_scan(scan):.4f} A/W\n" + f"$\\gamma_{{\\text{{CD,sec}}}}$: {mfile_data.data['eta_cd_hcd_secondary'].get_scan(scan):.4f} A/W | $\\zeta_{{\\text{{CD,sec}}}}$: {mfile_data.data['eta_cd_dimensionless_hcd_secondary'].get_scan(scan):.4f} \n" f"$\\eta_{{\\text{{CD,sec}}}}$: {mfile_data.data['eta_cd_norm_hcd_secondary'].get_scan(scan):.4f} $\\times 10^{{20}} \\mathrm{{A}} / \\mathrm{{Wm}}^2$\n" f"Current driven by secondary: {mfile_data.data['c_hcd_secondary_driven'].get_scan(scan) / 1e6:.3f} MA\n" ) @@ -12326,6 +12327,91 @@ def plot_plasma_outboard_toroidal_ripple_map( fig.tight_layout() +def plot_ebw_ecrh_coupling_graph(axis, mfile_data, scan): + # Plot EBW and ECRH coupling efficiency graph + ebw = ElectronBernstein(plasma_profile=0) + ecrg = ElectronCyclotron(plasma_profile=0) + b_on_axis = mfile_data.data["b_plasma_toroidal_on_axis"].get_scan(scan) + bs = np.linspace(b_on_axis - 2.0, b_on_axis + 2.0, 500) + # Use a color map for harmonics + colors = ["red", "green", "blue"] + linestyles = ["-", "--"] # EBW: solid, ECRH: dashed + + for idx, n_harmonic in enumerate(range(1, 4)): + eta_ebw_vals = [] + # For ECRH, store results for both wave modes (0: O-mode, 1: X-mode) + eta_ecrh_vals_omode = [] + eta_ecrh_vals_xmode = [] + for b in bs: + eta_ebw = ebw.electron_bernstein_freethy( + te=mfile_data.data["temp_plasma_electron_vol_avg_kev"].get_scan(scan), + rmajor=mfile_data.data["rmajor"].get_scan(scan), + dene20=mfile_data.data["nd_plasma_electrons_vol_avg"].get_scan(scan) + / 1e20, + b_plasma_toroidal_on_axis=b, + n_ecrh_harmonic=n_harmonic, + xi_ebw=0.8, + ) + eta_ecrh_omode = ecrg.electron_cyclotron_freethy( + te=mfile_data.data["temp_plasma_electron_vol_avg_kev"].get_scan(scan), + zeff=mfile_data.data["zeff"].get_scan(scan), + rmajor=mfile_data.data["rmajor"].get_scan(scan), + nd_plasma_electrons_vol_avg=mfile_data.data[ + "nd_plasma_electrons_vol_avg" + ].get_scan(scan), + b_plasma_toroidal_on_axis=b, + n_ecrh_harmonic=n_harmonic, + i_ecrh_wave_mode=0, # O-mode + ) + eta_ecrh_xmode = ecrg.electron_cyclotron_freethy( + te=mfile_data.data["temp_plasma_electron_vol_avg_kev"].get_scan(scan), + zeff=mfile_data.data["zeff"].get_scan(scan), + rmajor=mfile_data.data["rmajor"].get_scan(scan), + nd_plasma_electrons_vol_avg=mfile_data.data[ + "nd_plasma_electrons_vol_avg" + ].get_scan(scan), + b_plasma_toroidal_on_axis=b, + n_ecrh_harmonic=n_harmonic, + i_ecrh_wave_mode=1, # X-mode + ) + eta_ebw_vals.append(eta_ebw) + eta_ecrh_vals_omode.append(eta_ecrh_omode) + eta_ecrh_vals_xmode.append(eta_ecrh_xmode) + # EBW: solid, ECRH O-mode: dashed, ECRH X-mode: dotted, same color for same harmonic + axis.plot( + bs, + eta_ebw_vals, + label=f"EBW (harmonic {n_harmonic})", + color=colors[idx], + linestyle=linestyles[0], + ) + axis.plot( + bs, + eta_ecrh_vals_omode, + label=f"ECRH O-mode (harmonic {n_harmonic})", + color=colors[idx], + linestyle="--", + ) + axis.plot( + bs, + eta_ecrh_vals_xmode, + label=f"ECRH X-mode (harmonic {n_harmonic})", + color=colors[idx], + linestyle=":", + ) + axis.set_xlabel("On axis toroidal B-field [T]") + axis.set_ylabel("Coupling Efficiency") + axis.set_title("EBW/ECRH Coupling Efficiency vs Toroidal B-field") + axis.legend() + axis.grid(True) + # Plot a vertical line at the on-axis value of the toroidal B-field + b_on_axis = mfile_data.data["b_plasma_toroidal_on_axis"].get_scan(scan) + axis.axvline( + b_on_axis, color="black", linestyle="-", linewidth=2.5, label="On-axis $B_T$" + ) + axis.minorticks_on() + + def main_plot( fig0, fig1, @@ -12353,6 +12439,7 @@ def main_plot( fig23, fig24, fig25, + fig26, m_file_data, scan, imp="../data/lz_non_corona_14_elements/", @@ -12632,6 +12719,8 @@ def main_plot( ax24.set_position([0.08, 0.35, 0.84, 0.57]) plot_system_power_profiles_over_time(ax24, m_file_data, scan, fig25) + plot_ebw_ecrh_coupling_graph(fig26.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() @@ -12949,6 +13038,7 @@ def main(args=None): page23 = plt.figure(figsize=(12, 9), dpi=80) page24 = plt.figure(figsize=(12, 9), dpi=80) page25 = plt.figure(figsize=(12, 9), dpi=80) + page26 = plt.figure(figsize=(12, 9), dpi=80) # run main_plot main_plot( @@ -12978,6 +13068,7 @@ def main(args=None): page23, page24, page25, + page26, m_file, scan=scan, demo_ranges=demo_ranges, @@ -13012,6 +13103,7 @@ def main(args=None): pdf.savefig(page23) pdf.savefig(page24) pdf.savefig(page25) + pdf.savefig(page26) # show fig if option used if args.show: @@ -13043,6 +13135,7 @@ def main(args=None): plt.close(page23) plt.close(page24) plt.close(page25) + plt.close(page26) if __name__ == "__main__": From 484a9d2e21f86f7364e24bd22f193ac59bb8edfa Mon Sep 17 00:00:00 2001 From: mn3981 Date: Fri, 14 Nov 2025 21:14:58 +0000 Subject: [PATCH 6/7] Refine dimensionless current drive efficiency documentation and update plot labels for clarity --- process/current_drive.py | 50 ++++++++++++++++++++++++---------------- process/io/plot_proc.py | 4 ++-- 2 files changed, 32 insertions(+), 22 deletions(-) diff --git a/process/current_drive.py b/process/current_drive.py index 6d660dd22a..8a6b24e865 100644 --- a/process/current_drive.py +++ b/process/current_drive.py @@ -1941,31 +1941,41 @@ def calculate_dimensionless_current_drive_efficiency( p_hcd_injected: float, ) -> float: """ - Calculate the dimensionless current drive efficiency, ζ. - - This function computes the dimensionless current drive efficiency - based on the average electron density, major radius, and electron temperature. - - :param nd_plasma_electrons_vol_avg: Volume averaged electron density in m^-3. - :type nd_plasma_electrons_vol_avg: float - :param rmajor: Major radius of the plasma in meters. - :type rmajor: float - :param temp_plasma_electron_vol_avg_kev: Volume averaged electron temperature in keV. - :type temp_plasma_electron_vol_avg_kev: float - :param c_hcd_driven: Current driven by the heating and current drive system. - :type c_hcd_driven: float - :param p_hcd_injected: Power injected by the heating and current drive system. - :type p_hcd_injected: float - :return: The calculated dimensionless current drive efficiency. - :rtype: float - - :notes: + Calculate the dimensionless current drive efficiency, ζ. + + This function computes the dimensionless current drive efficiency + based on the average electron density, major radius, and electron temperature. + + :param nd_plasma_electrons_vol_avg: Volume averaged electron density in m^-3. + :type nd_plasma_electrons_vol_avg: float + :param rmajor: Major radius of the plasma in meters. + :type rmajor: float + :param temp_plasma_electron_vol_avg_kev: Volume averaged electron temperature in keV. + :type temp_plasma_electron_vol_avg_kev: float + :param c_hcd_driven: Current driven by the heating and current drive system. + :type c_hcd_driven: float + :param p_hcd_injected: Power injected by the heating and current drive system. + :type p_hcd_injected: float + :return: The calculated dimensionless current drive efficiency. + :rtype: float + + :references: + - E. Poli et al., “Electron-cyclotron-current-drive efficiency in DEMO plasmas,” + Nuclear Fusion, vol. 53, no. 1, pp. 013011–013011, Dec. 2012, + doi: https://doi.org/10.1088/0029-5515/53/1/013011. + ‌ + - T. C. Luce et al., “Generation of Localized Noninductive Current by Electron Cyclotron Waves on the DIII-D Tokamak,” + Physical Review Letters, vol. 83, no. 22, pp. 4550–4553, Nov. 1999, + doi: https://doi.org/10.1103/physrevlett.83.4550. """ return ( (constants.ELECTRON_CHARGE**3 / constants.EPSILON0**2) - * (nd_plasma_electrons_vol_avg * rmajor / temp_plasma_electron_vol_avg_kev) + * ( + (nd_plasma_electrons_vol_avg * rmajor) + / (temp_plasma_electron_vol_avg_kev * constants.KILOELECTRON_VOLT) + ) * (c_hcd_driven / p_hcd_injected) ) diff --git a/process/io/plot_proc.py b/process/io/plot_proc.py index c64157c336..6686e593f7 100644 --- a/process/io/plot_proc.py +++ b/process/io/plot_proc.py @@ -2588,13 +2588,13 @@ def plot_main_plasma_information( f"$\\mathbf{{Primary \\ system: {primary_heating}}}$ \n" f"Current driving power {mfile_data.data['p_hcd_primary_injected_mw'].get_scan(scan):.4f} MW\n" f"Extra heat power: {mfile_data.data['p_hcd_primary_extra_heat_mw'].get_scan(scan):.4f} MW\n" - f"$\\gamma_{{\\text{{CD,prim}}}}$: {mfile_data.data['eta_cd_hcd_primary'].get_scan(scan):.4f} A/W | $\\zeta_{{\\text{{CD,prim}}}}$: {mfile_data.data['eta_cd_dimensionless_hcd_primary'].get_scan(scan):.4f} \n" + f"$\\gamma_{{\\text{{CD,prim}}}}$: {mfile_data.data['eta_cd_hcd_primary'].get_scan(scan):.4f} A/W | $\\langle\\zeta_{{\\text{{CD,prim}}}}\\rangle$: {mfile_data.data['eta_cd_dimensionless_hcd_primary'].get_scan(scan):.4f} \n" f"$\\eta_{{\\text{{CD,prim}}}}$: {mfile_data.data['eta_cd_norm_hcd_primary'].get_scan(scan):.4f} $\\times 10^{{20}} \\mathrm{{A}} / \\mathrm{{Wm}}^2$\n" f"Current driven by primary: {mfile_data.data['c_hcd_primary_driven'].get_scan(scan) / 1e6:.3f} MA\n\n" f"$\\mathbf{{Secondary \\ system: {secondary_heating}}}$ \n" f"Current driving power {mfile_data.data['p_hcd_secondary_injected_mw'].get_scan(scan):.4f} MW\n" f"Extra heat power: {mfile_data.data['p_hcd_secondary_extra_heat_mw'].get_scan(scan):.4f} MW\n" - f"$\\gamma_{{\\text{{CD,sec}}}}$: {mfile_data.data['eta_cd_hcd_secondary'].get_scan(scan):.4f} A/W | $\\zeta_{{\\text{{CD,sec}}}}$: {mfile_data.data['eta_cd_dimensionless_hcd_secondary'].get_scan(scan):.4f} \n" + f"$\\gamma_{{\\text{{CD,sec}}}}$: {mfile_data.data['eta_cd_hcd_secondary'].get_scan(scan):.4f} A/W | $\\langle\\zeta_{{\\text{{CD,sec}}}}\\rangle$: {mfile_data.data['eta_cd_dimensionless_hcd_secondary'].get_scan(scan):.4f} \n" f"$\\eta_{{\\text{{CD,sec}}}}$: {mfile_data.data['eta_cd_norm_hcd_secondary'].get_scan(scan):.4f} $\\times 10^{{20}} \\mathrm{{A}} / \\mathrm{{Wm}}^2$\n" f"Current driven by secondary: {mfile_data.data['c_hcd_secondary_driven'].get_scan(scan) / 1e6:.3f} MA\n" ) From 37a265c3127c01a0cd26b5f0315e19ec20fcf1f2 Mon Sep 17 00:00:00 2001 From: mn3981 Date: Fri, 14 Nov 2025 21:18:37 +0000 Subject: [PATCH 7/7] Add unit test for dimensionless current drive efficiency calculation --- process/current_drive.py | 5 +- process/io/plot_proc.py | 93 -------------------------------- tests/unit/test_current_drive.py | 40 ++++++++++++++ 3 files changed, 42 insertions(+), 96 deletions(-) diff --git a/process/current_drive.py b/process/current_drive.py index 8a6b24e865..7540e79e09 100644 --- a/process/current_drive.py +++ b/process/current_drive.py @@ -1961,13 +1961,12 @@ def calculate_dimensionless_current_drive_efficiency( :references: - E. Poli et al., “Electron-cyclotron-current-drive efficiency in DEMO plasmas,” - Nuclear Fusion, vol. 53, no. 1, pp. 013011–013011, Dec. 2012, + Nuclear Fusion, vol. 53, no. 1, pp. 013011-013011, Dec. 2012, doi: https://doi.org/10.1088/0029-5515/53/1/013011. ‌ - T. C. Luce et al., “Generation of Localized Noninductive Current by Electron Cyclotron Waves on the DIII-D Tokamak,” - Physical Review Letters, vol. 83, no. 22, pp. 4550–4553, Nov. 1999, + Physical Review Letters, vol. 83, no. 22, pp. 4550-4553, Nov. 1999, doi: https://doi.org/10.1103/physrevlett.83.4550. - """ return ( diff --git a/process/io/plot_proc.py b/process/io/plot_proc.py index 6686e593f7..eb2f4a3036 100644 --- a/process/io/plot_proc.py +++ b/process/io/plot_proc.py @@ -36,7 +36,6 @@ import process.io.mfile as mf import process.superconducting_tf_coil as sctf from process.build import Build -from process.current_drive import ElectronBernstein, ElectronCyclotron from process.data_structure import physics_variables from process.geometry.blanket_geometry import ( blanket_geometry_double_null, @@ -12327,91 +12326,6 @@ def plot_plasma_outboard_toroidal_ripple_map( fig.tight_layout() -def plot_ebw_ecrh_coupling_graph(axis, mfile_data, scan): - # Plot EBW and ECRH coupling efficiency graph - ebw = ElectronBernstein(plasma_profile=0) - ecrg = ElectronCyclotron(plasma_profile=0) - b_on_axis = mfile_data.data["b_plasma_toroidal_on_axis"].get_scan(scan) - bs = np.linspace(b_on_axis - 2.0, b_on_axis + 2.0, 500) - # Use a color map for harmonics - colors = ["red", "green", "blue"] - linestyles = ["-", "--"] # EBW: solid, ECRH: dashed - - for idx, n_harmonic in enumerate(range(1, 4)): - eta_ebw_vals = [] - # For ECRH, store results for both wave modes (0: O-mode, 1: X-mode) - eta_ecrh_vals_omode = [] - eta_ecrh_vals_xmode = [] - for b in bs: - eta_ebw = ebw.electron_bernstein_freethy( - te=mfile_data.data["temp_plasma_electron_vol_avg_kev"].get_scan(scan), - rmajor=mfile_data.data["rmajor"].get_scan(scan), - dene20=mfile_data.data["nd_plasma_electrons_vol_avg"].get_scan(scan) - / 1e20, - b_plasma_toroidal_on_axis=b, - n_ecrh_harmonic=n_harmonic, - xi_ebw=0.8, - ) - eta_ecrh_omode = ecrg.electron_cyclotron_freethy( - te=mfile_data.data["temp_plasma_electron_vol_avg_kev"].get_scan(scan), - zeff=mfile_data.data["zeff"].get_scan(scan), - rmajor=mfile_data.data["rmajor"].get_scan(scan), - nd_plasma_electrons_vol_avg=mfile_data.data[ - "nd_plasma_electrons_vol_avg" - ].get_scan(scan), - b_plasma_toroidal_on_axis=b, - n_ecrh_harmonic=n_harmonic, - i_ecrh_wave_mode=0, # O-mode - ) - eta_ecrh_xmode = ecrg.electron_cyclotron_freethy( - te=mfile_data.data["temp_plasma_electron_vol_avg_kev"].get_scan(scan), - zeff=mfile_data.data["zeff"].get_scan(scan), - rmajor=mfile_data.data["rmajor"].get_scan(scan), - nd_plasma_electrons_vol_avg=mfile_data.data[ - "nd_plasma_electrons_vol_avg" - ].get_scan(scan), - b_plasma_toroidal_on_axis=b, - n_ecrh_harmonic=n_harmonic, - i_ecrh_wave_mode=1, # X-mode - ) - eta_ebw_vals.append(eta_ebw) - eta_ecrh_vals_omode.append(eta_ecrh_omode) - eta_ecrh_vals_xmode.append(eta_ecrh_xmode) - # EBW: solid, ECRH O-mode: dashed, ECRH X-mode: dotted, same color for same harmonic - axis.plot( - bs, - eta_ebw_vals, - label=f"EBW (harmonic {n_harmonic})", - color=colors[idx], - linestyle=linestyles[0], - ) - axis.plot( - bs, - eta_ecrh_vals_omode, - label=f"ECRH O-mode (harmonic {n_harmonic})", - color=colors[idx], - linestyle="--", - ) - axis.plot( - bs, - eta_ecrh_vals_xmode, - label=f"ECRH X-mode (harmonic {n_harmonic})", - color=colors[idx], - linestyle=":", - ) - axis.set_xlabel("On axis toroidal B-field [T]") - axis.set_ylabel("Coupling Efficiency") - axis.set_title("EBW/ECRH Coupling Efficiency vs Toroidal B-field") - axis.legend() - axis.grid(True) - # Plot a vertical line at the on-axis value of the toroidal B-field - b_on_axis = mfile_data.data["b_plasma_toroidal_on_axis"].get_scan(scan) - axis.axvline( - b_on_axis, color="black", linestyle="-", linewidth=2.5, label="On-axis $B_T$" - ) - axis.minorticks_on() - - def main_plot( fig0, fig1, @@ -12439,7 +12353,6 @@ def main_plot( fig23, fig24, fig25, - fig26, m_file_data, scan, imp="../data/lz_non_corona_14_elements/", @@ -12719,8 +12632,6 @@ def main_plot( ax24.set_position([0.08, 0.35, 0.84, 0.57]) plot_system_power_profiles_over_time(ax24, m_file_data, scan, fig25) - plot_ebw_ecrh_coupling_graph(fig26.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() @@ -13038,7 +12949,6 @@ def main(args=None): page23 = plt.figure(figsize=(12, 9), dpi=80) page24 = plt.figure(figsize=(12, 9), dpi=80) page25 = plt.figure(figsize=(12, 9), dpi=80) - page26 = plt.figure(figsize=(12, 9), dpi=80) # run main_plot main_plot( @@ -13068,7 +12978,6 @@ def main(args=None): page23, page24, page25, - page26, m_file, scan=scan, demo_ranges=demo_ranges, @@ -13103,7 +13012,6 @@ def main(args=None): pdf.savefig(page23) pdf.savefig(page24) pdf.savefig(page25) - pdf.savefig(page26) # show fig if option used if args.show: @@ -13135,7 +13043,6 @@ def main(args=None): plt.close(page23) plt.close(page24) plt.close(page25) - plt.close(page26) if __name__ == "__main__": diff --git a/tests/unit/test_current_drive.py b/tests/unit/test_current_drive.py index f18e27ed82..762f52276f 100644 --- a/tests/unit/test_current_drive.py +++ b/tests/unit/test_current_drive.py @@ -322,3 +322,43 @@ def test_cudriv_primary_electron_bernstein(current_drive): assert current_drive_variables.p_hcd_injected_total_mw == pytest.approx( 257.72205307406523, rel=1e-6 ) + + +@pytest.mark.parametrize( + "nd_plasma_electrons_vol_avg, rmajor, temp_plasma_electron_vol_avg_kev, c_hcd_driven, p_hcd_injected", + [ + (1e20, 1.0, 1.0, 1.0, 1.0), + ], +) +def test_calculate_dimensionless_current_drive_efficiency_simple( + nd_plasma_electrons_vol_avg, + rmajor, + temp_plasma_electron_vol_avg_kev, + c_hcd_driven, + p_hcd_injected, +): + plasma_profile = PlasmaProfile() + cd = CurrentDrive( + plasma_profile=plasma_profile, + electron_cyclotron=ElectronCyclotron(plasma_profile), + ion_cyclotron=IonCyclotron(plasma_profile), + lower_hybrid=LowerHybrid(plasma_profile), + neutral_beam=NeutralBeam(plasma_profile), + electron_bernstein=ElectronBernstein(plasma_profile), + ) + expected = ( + (constants.ELECTRON_CHARGE**3 / constants.EPSILON0**2) + * ( + (nd_plasma_electrons_vol_avg * rmajor) + / (temp_plasma_electron_vol_avg_kev * constants.KILOELECTRON_VOLT) + ) + * (c_hcd_driven / p_hcd_injected) + ) + result = cd.calculate_dimensionless_current_drive_efficiency( + nd_plasma_electrons_vol_avg, + rmajor, + temp_plasma_electron_vol_avg_kev, + c_hcd_driven, + p_hcd_injected, + ) + assert result == pytest.approx(expected, rel=1e-12)