diff --git a/documentation/physics-models/plasma_current/plasma_inductance.md b/documentation/physics-models/plasma_current/plasma_inductance.md index 8644e6de0..0d24f321a 100644 --- a/documentation/physics-models/plasma_current/plasma_inductance.md +++ b/documentation/physics-models/plasma_current/plasma_inductance.md @@ -1,4 +1,4 @@ -# Plasma Inductance +# Plasma Inductance | `PlasmaInductance` ## Setting the normalised internal inductance @@ -22,7 +22,7 @@ ind_plasma_internal_norm = 1.0 ---------- -### Wesson relation +### Wesson relation | `calculate_internal_inductance_wesson()` This can be activated by stating `i_ind_plasma_internal_normx = 1` in the input file. @@ -42,7 +42,7 @@ This is only recommended for high aspect ratio tokamaks[^2]. --------- -### Menard Inductance Relation +### Menard Inductance Relation | `calculate_internal_inductance_menard()` This can be activated by stating `ind_plasma_internal_norm = 2` in the input file. diff --git a/process/main.py b/process/main.py index 74f042ff7..07b0ae868 100644 --- a/process/main.py +++ b/process/main.py @@ -93,7 +93,7 @@ ) from process.log import logging_model_handler, show_errors from process.pfcoil import PFCoil -from process.physics import DetailedPhysics, Physics, PlasmaBeta +from process.physics import DetailedPhysics, Physics, PlasmaBeta, PlasmaInductance from process.plasma_geometry import PlasmaGeom from process.plasma_profiles import PlasmaProfile from process.power import Power @@ -683,10 +683,12 @@ def __init__(self): electron_bernstein=ElectronBernstein(plasma_profile=self.plasma_profile), ) self.plasma_beta = PlasmaBeta() + self.plasma_inductance = PlasmaInductance() self.physics = Physics( plasma_profile=self.plasma_profile, current_drive=self.current_drive, plasma_beta=self.plasma_beta, + plasma_inductance=self.plasma_inductance, ) self.physics_detailed = DetailedPhysics( plasma_profile=self.plasma_profile, @@ -704,6 +706,7 @@ def __init__(self): physics=self.physics, neoclassics=self.neoclassics, plasma_beta=self.plasma_beta, + plasma_inductance=self.plasma_inductance, ) self.dcll = DCLL(fw=self.fw) diff --git a/process/physics.py b/process/physics.py index 94d7eeefb..de5922c93 100644 --- a/process/physics.py +++ b/process/physics.py @@ -73,135 +73,6 @@ def rether( return conie * (temp_plasma_ion_vol_avg_kev - te) / (te**1.5) -@nb.jit(nopython=True, cache=True) -def calculate_volt_second_requirements( - csawth: float, - eps: float, - f_c_plasma_inductive: float, - ejima_coeff: float, - kappa: float, - rmajor: float, - res_plasma: float, - plasma_current: float, - t_plant_pulse_fusion_ramp: float, - t_plant_pulse_burn: float, - ind_plasma_internal_norm: float, -) -> tuple[float, float, float, float, float, float]: - """Calculate the volt-second requirements and related parameters for plasma physics. - - :param csawth: Coefficient for sawteeth effects - :type csawth: float - :param eps: Inverse aspect ratio - :type eps: float - :param f_c_plasma_inductive: Fraction of plasma current produced inductively - :type f_c_plasma_inductive: float - :param ejima_coeff: Ejima coefficient for resistive start-up V-s component - :type ejima_coeff: float - :param kappa: Plasma elongation - :type kappa: float - :param rmajor: Plasma major radius (m) - :type rmajor: float - :param res_plasma: Plasma resistance (ohm) - :type res_plasma: float - :param plasma_current: Plasma current (A) - :type plasma_current: float - :param t_plant_pulse_fusion_ramp: Heating time (s) - :type t_plant_pulse_fusion_ramp: float - :param t_plant_pulse_burn: Burn time (s) - :type t_plant_pulse_burn: float - :param ind_plasma_internal_norm: Plasma normalized internal inductance - :type ind_plasma_internal_norm: float - - :return: A tuple containing: - - vs_plasma_internal: Internal plasma volt-seconds (Wb) - - ind_plasma_internal: Plasma inductance (H) - - vs_plasma_burn_required: Volt-seconds needed during flat-top (heat+burn) (Wb) - - vs_plasma_ramp_required: Volt-seconds needed during ramp-up (Wb) - - ind_plasma_total,: Internal and external plasma inductance V-s (Wb) - - vs_res_ramp: Resistive losses in start-up volt-seconds (Wb) - - vs_plasma_total_required: Total volt-seconds needed (Wb) - :rtype: tuple[float, float, float, float, float, float] - - :notes: - - :references: - - S. Ejima, R. W. Callis, J. L. Luxon, R. D. Stambaugh, T. S. Taylor, and J. C. Wesley, - “Volt-second analysis and consumption in Doublet III plasmas,” - Nuclear Fusion, vol. 22, no. 10, pp. 1313-1319, Oct. 1982, doi: - https://doi.org/10.1088/0029-5515/22/10/006. - - - S. C. Jardin, C. E. Kessel, and N Pomphrey, - “Poloidal flux linkage requirements for the International Thermonuclear Experimental Reactor,” - Nuclear Fusion, vol. 34, no. 8, pp. 1145-1160, Aug. 1994, - doi: https://doi.org/10.1088/0029-5515/34/8/i07. - - - S. P. Hirshman and G. H. Neilson, “External inductance of an axisymmetric plasma,” - The Physics of Fluids, vol. 29, no. 3, pp. 790-793, Mar. 1986, - doi: https://doi.org/10.1063/1.865934. - """ - # Plasma internal inductance - - ind_plasma_internal = constants.RMU0 * rmajor * ind_plasma_internal_norm / 2.0 - - # Internal plasma flux (V-s) component - vs_plasma_internal = ind_plasma_internal * plasma_current - - # Start-up resistive component - # Uses ITER formula without the 10 V-s add-on - - vs_res_ramp = ejima_coeff * constants.RMU0 * plasma_current * rmajor - - # ====================================================================== - - # Hirshman and Neilson fit for external inductance - - aeps = (1.0 + 1.81 * np.sqrt(eps) + 2.05 * eps) * np.log(8.0 / eps) - ( - 2.0 + 9.25 * np.sqrt(eps) - 1.21 * eps - ) - beps = 0.73 * np.sqrt(eps) * (1.0 + 2.0 * eps**4 - 6.0 * eps**5 + 3.7 * eps**6) - - ind_plasma_external = ( - rmajor * constants.RMU0 * aeps * (1.0 - eps) / (1.0 - eps + beps * kappa) - ) - - # ====================================================================== - - ind_plasma_total = ind_plasma_external + ind_plasma_internal - - # Inductive V-s component - - vs_self_ind_ramp = ind_plasma_total * plasma_current - vs_plasma_ramp_required = vs_res_ramp + vs_self_ind_ramp - - # Plasma loop voltage during flat-top - # Include enhancement factor in flattop V-s requirement - # to account for MHD sawtooth effects. - - v_plasma_loop_burn = plasma_current * res_plasma * f_c_plasma_inductive - - v_burn_resistive = v_plasma_loop_burn * csawth - - # N.B. t_plant_pulse_burn on first iteration will not be correct - # if the pulsed reactor option is used, but the value - # will be correct on subsequent calls. - - vs_plasma_burn_required = v_burn_resistive * ( - t_plant_pulse_fusion_ramp + t_plant_pulse_burn - ) - vs_plasma_total_required = vs_plasma_ramp_required + vs_plasma_burn_required - - return ( - vs_plasma_internal, - ind_plasma_total, - vs_plasma_burn_required, - vs_plasma_ramp_required, - vs_self_ind_ramp, - vs_res_ramp, - vs_plasma_total_required, - v_plasma_loop_burn, - ) - - # ----------------------------------------------------- # Plasma Current & Poloidal Field Calculations # ----------------------------------------------------- @@ -1516,12 +1387,13 @@ def _trapped_particle_fraction_sauter( class Physics: - def __init__(self, plasma_profile, current_drive, plasma_beta): + def __init__(self, plasma_profile, current_drive, plasma_beta, plasma_inductance): self.outfile = constants.NOUT self.mfile = constants.MFILE self.plasma_profile = plasma_profile self.current_drive = current_drive self.beta = plasma_beta + self.inductance = plasma_inductance def physics(self): """ @@ -1625,47 +1497,7 @@ def physics(self): # Plasma Normalised Internal Inductance # ----------------------------------------------------- - physics_variables.ind_plasma_internal_norm_wesson = ( - self.calculate_internal_inductance_wesson(alphaj=physics_variables.alphaj) - ) - - # Spherical Tokamak relation for internal inductance - # Menard et al. (2016), Nuclear Fusion, 56, 106023 - physics_variables.ind_plasma_internal_norm_menard = ( - self.calculate_internal_inductance_menard(kappa=physics_variables.kappa) - ) - - physics_variables.ind_plasma_internal_norm_iter_3 = ( - self.calculate_normalised_internal_inductance_iter_3( - b_plasma_poloidal_vol_avg=physics_variables.b_plasma_poloidal_average, - c_plasma=physics_variables.plasma_current, - vol_plasma=physics_variables.vol_plasma, - rmajor=physics_variables.rmajor, - ) - ) - - # Map calculation methods to a dictionary - ind_plasma_internal_norm_calculations = { - 0: physics_variables.ind_plasma_internal_norm, - 1: physics_variables.ind_plasma_internal_norm_wesson, - 2: physics_variables.ind_plasma_internal_norm_menard, - } - - # Calculate ind_plasma_internal_normx based on i_ind_plasma_internal_norm - if ( - int(physics_variables.i_ind_plasma_internal_norm) - in ind_plasma_internal_norm_calculations - ): - physics_variables.ind_plasma_internal_norm = ( - ind_plasma_internal_norm_calculations[ - int(physics_variables.i_ind_plasma_internal_norm) - ] - ) - else: - raise ProcessValueError( - "Illegal value of i_ind_plasma_internal_norm", - i_ind_plasma_internal_norm=physics_variables.i_ind_plasma_internal_norm, - ) + self.inductance.run() # =================================================== @@ -2456,7 +2288,7 @@ def physics(self): physics_variables.vs_plasma_res_ramp, physics_variables.vs_plasma_total_required, physics_variables.v_plasma_loop_burn, - ) = calculate_volt_second_requirements( + ) = self.inductance.calculate_volt_second_requirements( physics_variables.csawth, physics_variables.eps, physics_variables.f_c_plasma_inductive, @@ -2714,50 +2546,6 @@ def calculate_current_profile_index_wesson(qstar: float, q0: float) -> float: """ return qstar / q0 - 1.0 - @staticmethod - def calculate_internal_inductance_wesson(alphaj: float) -> float: - """ - Calculate the Wesson plasma normalized internal inductance. - - :param alphaj: Current profile index. - :type alphaj: float - - :return: The Wesson plasma normalised internal inductance. - :rtype: float - - :Notes: - - It is recommended to use this method with the other Wesson relations for normalised beta and - current profile index. - - This relation is only true for the cyclindrical plasma approximation with parabolic profiles. - - :References: - - Wesson, J. (2011) Tokamaks. 4th Edition, 2011 Oxford Science Publications, - International Series of Monographs on Physics, Volume 149. - """ - return np.log(1.65 + 0.89 * alphaj) - - @staticmethod - def calculate_internal_inductance_menard(kappa: float) -> float: - """ - Calculate the Menard plasma normalized internal inductance. - - :param kappa: Plasma separatrix elongation. - :type kappa: float - - :return: The Menard plasma normalised internal inductance. - :rtype: float - - :Notes: - - This relation is based off of data from NSTX for l_i in the range of 0.4-0.85 - - This model is only recommneded to be used for ST's with kappa > 2.5 - - :References: - - J. E. Menard et al., “Fusion nuclear science facilities and pilot plants based on the spherical tokamak,” - Nuclear Fusion, vol. 56, no. 10, p. 106023, Aug. 2016, - doi: https://doi.org/10.1088/0029-5515/56/10/106023. - """ - return 3.4 - kappa - @staticmethod def calculate_density_limit( b_plasma_toroidal_on_axis: float, @@ -3318,47 +3106,6 @@ def phyaux( f_alpha_energy_confinement, ) - @staticmethod - def calculate_normalised_internal_inductance_iter_3( - b_plasma_poloidal_vol_avg: float, - c_plasma: float, - vol_plasma: float, - rmajor: float, - ) -> float: - """ - Calculate the normalised internal inductance using ITER-3 scaling li(3). - - :param b_plasma_poloidal_vol_avg: Volume-averaged poloidal magnetic field (T). - :type b_plasma_poloidal_vol_avg: float - :param c_plasma: Plasma current (A). - :type c_plasma: float - :param vol_plasma: Plasma volume (m^3). - :type vol_plasma: float - :param rmajor: Plasma major radius (m). - :type rmajor: float - - :returns: The li(3) normalised internal inductance. - :rtype: float - - :references: - - T. C. Luce, D. A. Humphreys, G. L. Jackson, and W. M. Solomon, - “Inductive flux usage and its optimization in tokamak operation,” - Nuclear Fusion, vol. 54, no. 9, p. 093005, Jul. 2014, - doi: https://doi.org/10.1088/0029-5515/54/9/093005. - - - G. L. Jackson et al., “ITER startup studies in the DIII-D tokamak,” - Nuclear Fusion, vol. 48, no. 12, p. 125002, Nov. 2008, - doi: https://doi.org/10.1088/0029-5515/48/12/125002. - ‌ - """ - - return ( - 2 - * vol_plasma - * b_plasma_poloidal_vol_avg**2 - / (constants.RMU0**2 * c_plasma**2 * rmajor) - ) - @staticmethod def plasma_ohmic_heating( f_c_plasma_inductive: float, @@ -5773,112 +5520,7 @@ def outplas(self): po.ostars(self.outfile, 110) po.oblnkl(self.outfile) - po.osubhd(self.outfile, "Plasma Volt-second Requirements :") - po.ovarre( - self.outfile, - "Total plasma volt-seconds required for pulse (Wb)", - "(vs_plasma_total_required)", - physics_variables.vs_plasma_total_required, - "OP ", - ) - po.oblnkl(self.outfile) - po.ovarre( - self.outfile, - "Total plasma inductive flux consumption for plasma current ramp-up (Wb)", - "(vs_plasma_ind_ramp)", - physics_variables.vs_plasma_ind_ramp, - "OP ", - ) - po.ovarre( - self.outfile, - "Plasma resistive flux consumption for plasma current ramp-up (Wb)", - "(vs_plasma_res_ramp)", - physics_variables.vs_plasma_res_ramp, - "OP ", - ) - po.ovarre( - self.outfile, - "Total flux consumption for plasma current ramp-up (Wb)", - "(vs_plasma_ramp_required)", - physics_variables.vs_plasma_ramp_required, - "OP ", - ) - po.ovarrf( - self.outfile, - "Ejima coefficient", - "(ejima_coeff)", - physics_variables.ejima_coeff, - ) - po.oblnkl(self.outfile) - po.ovarre( - self.outfile, - "Internal plasma V-s", - "(vs_plasma_internal)", - physics_variables.vs_plasma_internal, - ) - - po.ovarre( - self.outfile, - "Plasma volt-seconds needed for flat-top (heat + burn times) (Wb)", - "(vs_plasma_burn_required)", - physics_variables.vs_plasma_burn_required, - "OP ", - ) - - po.ovarre( - self.outfile, - "Plasma loop voltage during burn (V)", - "(v_plasma_loop_burn)", - physics_variables.v_plasma_loop_burn, - "OP ", - ) - po.ovarrf( - self.outfile, - "Coefficient for sawtooth effects on burn V-s requirement", - "(csawth)", - physics_variables.csawth, - ) - po.oblnkl(self.outfile) - po.ovarre( - self.outfile, - "Plasma resistance (ohm)", - "(res_plasma)", - physics_variables.res_plasma, - "OP ", - ) - - po.ovarre( - self.outfile, - "Plasma resistive diffusion time (s)", - "(t_plasma_res_diffusion)", - physics_variables.t_plasma_res_diffusion, - "OP ", - ) - po.ovarre( - self.outfile, - "Plasma inductance (H)", - "(ind_plasma)", - physics_variables.ind_plasma, - "OP ", - ) - po.ovarre( - self.outfile, - "Plasma magnetic energy stored (J)", - "(e_plasma_magnetic_stored)", - physics_variables.e_plasma_magnetic_stored, - "OP ", - ) - po.ovarrf( - self.outfile, - "Plasma normalised internal inductance", - "(ind_plasma_internal_norm)", - physics_variables.ind_plasma_internal_norm, - "OP ", - ) - - po.oblnkl(self.outfile) - po.ostars(self.outfile, 110) - po.oblnkl(self.outfile) + self.inductance.output_volt_second_information() po.ovarrf( self.outfile, @@ -9243,6 +8885,388 @@ def output_beta_information(self): po.oblnkl(self.outfile) +class IndInternalNormModel(IntEnum): + """Normalised internal inductance (l_i) model types""" + + USER_INPUT = 0 + WESSON = 1 + MENARD = 2 + + +class PlasmaInductance: + """Class to hold plasma inductance calculations for plasma processing.""" + + def __init__(self): + self.outfile = constants.NOUT + self.mfile = constants.MFILE + + def run(self): + physics_variables.ind_plasma_internal_norm_wesson = ( + self.calculate_internal_inductance_wesson(alphaj=physics_variables.alphaj) + ) + + # Spherical Tokamak relation for internal inductance + # Menard et al. (2016), Nuclear Fusion, 56, 106023 + physics_variables.ind_plasma_internal_norm_menard = ( + self.calculate_internal_inductance_menard(kappa=physics_variables.kappa) + ) + + physics_variables.ind_plasma_internal_norm_iter_3 = ( + self.calculate_normalised_internal_inductance_iter_3( + b_plasma_poloidal_vol_avg=physics_variables.b_plasma_poloidal_average, + c_plasma=physics_variables.plasma_current, + vol_plasma=physics_variables.vol_plasma, + rmajor=physics_variables.rmajor, + ) + ) + + # Calculate ind_plasma_internal_norm based on i_ind_plasma_internal_norm + try: + model = IndInternalNormModel( + int(physics_variables.i_ind_plasma_internal_norm) + ) + physics_variables.ind_plasma_internal_norm = ( + self.get_ind_internal_norm_value(model) + ) + except ValueError: + raise ProcessValueError( + "Illegal value of i_ind_plasma_internal_norm", + i_ind_plasma_internal_norm=physics_variables.i_ind_plasma_internal_norm, + ) from None + + def get_ind_internal_norm_value(self, model: IndInternalNormModel) -> float: + """Get the normalised internal inductance (l_i) for the specified model.""" + model_map = { + IndInternalNormModel.USER_INPUT: physics_variables.ind_plasma_internal_norm, + IndInternalNormModel.WESSON: physics_variables.ind_plasma_internal_norm_wesson, + IndInternalNormModel.MENARD: physics_variables.ind_plasma_internal_norm_menard, + } + return model_map[model] + + @staticmethod + def calculate_volt_second_requirements( + csawth: float, + eps: float, + f_c_plasma_inductive: float, + ejima_coeff: float, + kappa: float, + rmajor: float, + res_plasma: float, + plasma_current: float, + t_plant_pulse_fusion_ramp: float, + t_plant_pulse_burn: float, + ind_plasma_internal_norm: float, + ) -> tuple[float, float, float, float, float, float]: + """Calculate the volt-second requirements and related parameters for plasma physics. + + :param csawth: Coefficient for sawteeth effects + :type csawth: float + :param eps: Inverse aspect ratio + :type eps: float + :param f_c_plasma_inductive: Fraction of plasma current produced inductively + :type f_c_plasma_inductive: float + :param ejima_coeff: Ejima coefficient for resistive start-up V-s component + :type ejima_coeff: float + :param kappa: Plasma elongation + :type kappa: float + :param rmajor: Plasma major radius (m) + :type rmajor: float + :param res_plasma: Plasma resistance (ohm) + :type res_plasma: float + :param plasma_current: Plasma current (A) + :type plasma_current: float + :param t_plant_pulse_fusion_ramp: Heating time (s) + :type t_plant_pulse_fusion_ramp: float + :param t_plant_pulse_burn: Burn time (s) + :type t_plant_pulse_burn: float + :param ind_plasma_internal_norm: Plasma normalized internal inductance + :type ind_plasma_internal_norm: float + + :return: A tuple containing: + - vs_plasma_internal: Internal plasma volt-seconds (Wb) + - ind_plasma_internal: Plasma inductance (H) + - vs_plasma_burn_required: Volt-seconds needed during flat-top (heat+burn) (Wb) + - vs_plasma_ramp_required: Volt-seconds needed during ramp-up (Wb) + - ind_plasma_total,: Internal and external plasma inductance V-s (Wb) + - vs_res_ramp: Resistive losses in start-up volt-seconds (Wb) + - vs_plasma_total_required: Total volt-seconds needed (Wb) + :rtype: tuple[float, float, float, float, float, float] + + :notes: + + :references: + - S. Ejima, R. W. Callis, J. L. Luxon, R. D. Stambaugh, T. S. Taylor, and J. C. Wesley, + “Volt-second analysis and consumption in Doublet III plasmas,” + Nuclear Fusion, vol. 22, no. 10, pp. 1313-1319, Oct. 1982, doi: + https://doi.org/10.1088/0029-5515/22/10/006. + + - S. C. Jardin, C. E. Kessel, and N Pomphrey, + “Poloidal flux linkage requirements for the International Thermonuclear Experimental Reactor,” + Nuclear Fusion, vol. 34, no. 8, pp. 1145-1160, Aug. 1994, + doi: https://doi.org/10.1088/0029-5515/34/8/i07. + + - S. P. Hirshman and G. H. Neilson, “External inductance of an axisymmetric plasma,” + The Physics of Fluids, vol. 29, no. 3, pp. 790-793, Mar. 1986, + doi: https://doi.org/10.1063/1.865934. + """ + # Plasma internal inductance + + ind_plasma_internal = constants.RMU0 * rmajor * ind_plasma_internal_norm / 2.0 + + # Internal plasma flux (V-s) component + vs_plasma_internal = ind_plasma_internal * plasma_current + + # Start-up resistive component + # Uses ITER formula without the 10 V-s add-on + + vs_res_ramp = ejima_coeff * constants.RMU0 * plasma_current * rmajor + + # ====================================================================== + + # Hirshman and Neilson fit for external inductance + + aeps = (1.0 + 1.81 * np.sqrt(eps) + 2.05 * eps) * np.log(8.0 / eps) - ( + 2.0 + 9.25 * np.sqrt(eps) - 1.21 * eps + ) + beps = 0.73 * np.sqrt(eps) * (1.0 + 2.0 * eps**4 - 6.0 * eps**5 + 3.7 * eps**6) + + ind_plasma_external = ( + rmajor * constants.RMU0 * aeps * (1.0 - eps) / (1.0 - eps + beps * kappa) + ) + + # ====================================================================== + + ind_plasma_total = ind_plasma_external + ind_plasma_internal + + # Inductive V-s component + + vs_self_ind_ramp = ind_plasma_total * plasma_current + vs_plasma_ramp_required = vs_res_ramp + vs_self_ind_ramp + + # Plasma loop voltage during flat-top + # Include enhancement factor in flattop V-s requirement + # to account for MHD sawtooth effects. + + v_plasma_loop_burn = plasma_current * res_plasma * f_c_plasma_inductive + + v_burn_resistive = v_plasma_loop_burn * csawth + + # N.B. t_plant_pulse_burn on first iteration will not be correct + # if the pulsed reactor option is used, but the value + # will be correct on subsequent calls. + + vs_plasma_burn_required = v_burn_resistive * ( + t_plant_pulse_fusion_ramp + t_plant_pulse_burn + ) + vs_plasma_total_required = vs_plasma_ramp_required + vs_plasma_burn_required + + return ( + vs_plasma_internal, + ind_plasma_total, + vs_plasma_burn_required, + vs_plasma_ramp_required, + vs_self_ind_ramp, + vs_res_ramp, + vs_plasma_total_required, + v_plasma_loop_burn, + ) + + @staticmethod + def calculate_normalised_internal_inductance_iter_3( + b_plasma_poloidal_vol_avg: float, + c_plasma: float, + vol_plasma: float, + rmajor: float, + ) -> float: + """ + Calculate the normalised internal inductance using ITER-3 scaling li(3). + + :param b_plasma_poloidal_vol_avg: Volume-averaged poloidal magnetic field (T). + :type b_plasma_poloidal_vol_avg: float + :param c_plasma: Plasma current (A). + :type c_plasma: float + :param vol_plasma: Plasma volume (m^3). + :type vol_plasma: float + :param rmajor: Plasma major radius (m). + :type rmajor: float + + :returns: The li(3) normalised internal inductance. + :rtype: float + + :references: + - T. C. Luce, D. A. Humphreys, G. L. Jackson, and W. M. Solomon, + “Inductive flux usage and its optimization in tokamak operation,” + Nuclear Fusion, vol. 54, no. 9, p. 093005, Jul. 2014, + doi: https://doi.org/10.1088/0029-5515/54/9/093005. + + - G. L. Jackson et al., “ITER startup studies in the DIII-D tokamak,” + Nuclear Fusion, vol. 48, no. 12, p. 125002, Nov. 2008, + doi: https://doi.org/10.1088/0029-5515/48/12/125002. + ‌ + """ + + return ( + 2 + * vol_plasma + * b_plasma_poloidal_vol_avg**2 + / (constants.RMU0**2 * c_plasma**2 * rmajor) + ) + + @staticmethod + def calculate_internal_inductance_menard(kappa: float) -> float: + """ + Calculate the Menard plasma normalized internal inductance. + + :param kappa: Plasma separatrix elongation. + :type kappa: float + + :return: The Menard plasma normalised internal inductance. + :rtype: float + + :Notes: + - This relation is based off of data from NSTX for l_i in the range of 0.4-0.85 + - This model is only recommneded to be used for ST's with kappa > 2.5 + + :References: + - J. E. Menard et al., “Fusion nuclear science facilities and pilot plants based on the spherical tokamak,” + Nuclear Fusion, vol. 56, no. 10, p. 106023, Aug. 2016, + doi: https://doi.org/10.1088/0029-5515/56/10/106023. + """ + return 3.4 - kappa + + @staticmethod + def calculate_internal_inductance_wesson(alphaj: float) -> float: + """ + Calculate the Wesson plasma normalized internal inductance. + + :param alphaj: Current profile index. + :type alphaj: float + + :return: The Wesson plasma normalised internal inductance. + :rtype: float + + :Notes: + - It is recommended to use this method with the other Wesson relations for normalised beta and + current profile index. + - This relation is only true for the cyclindrical plasma approximation with parabolic profiles. + + :References: + - Wesson, J. (2011) Tokamaks. 4th Edition, 2011 Oxford Science Publications, + International Series of Monographs on Physics, Volume 149. + """ + return np.log(1.65 + 0.89 * alphaj) + + def output_volt_second_information(self): + """Output volt-second information to file.""" + + po.osubhd(self.outfile, "Plasma Volt-second Requirements :") + po.ovarre( + self.outfile, + "Total plasma volt-seconds required for pulse (Wb)", + "(vs_plasma_total_required)", + physics_variables.vs_plasma_total_required, + "OP ", + ) + po.oblnkl(self.outfile) + po.ovarre( + self.outfile, + "Total plasma inductive flux consumption for plasma current ramp-up (Wb)", + "(vs_plasma_ind_ramp)", + physics_variables.vs_plasma_ind_ramp, + "OP ", + ) + po.ovarre( + self.outfile, + "Plasma resistive flux consumption for plasma current ramp-up (Wb)", + "(vs_plasma_res_ramp)", + physics_variables.vs_plasma_res_ramp, + "OP ", + ) + po.ovarre( + self.outfile, + "Total flux consumption for plasma current ramp-up (Wb)", + "(vs_plasma_ramp_required)", + physics_variables.vs_plasma_ramp_required, + "OP ", + ) + po.ovarrf( + self.outfile, + "Ejima coefficient", + "(ejima_coeff)", + physics_variables.ejima_coeff, + ) + po.oblnkl(self.outfile) + po.ovarre( + self.outfile, + "Internal plasma V-s", + "(vs_plasma_internal)", + physics_variables.vs_plasma_internal, + ) + + po.ovarre( + self.outfile, + "Plasma volt-seconds needed for flat-top (heat + burn times) (Wb)", + "(vs_plasma_burn_required)", + physics_variables.vs_plasma_burn_required, + "OP ", + ) + + po.ovarre( + self.outfile, + "Plasma loop voltage during burn (V)", + "(v_plasma_loop_burn)", + physics_variables.v_plasma_loop_burn, + "OP ", + ) + po.ovarrf( + self.outfile, + "Coefficient for sawtooth effects on burn V-s requirement", + "(csawth)", + physics_variables.csawth, + ) + po.oblnkl(self.outfile) + po.ovarre( + self.outfile, + "Plasma resistance (ohm)", + "(res_plasma)", + physics_variables.res_plasma, + "OP ", + ) + + po.ovarre( + self.outfile, + "Plasma resistive diffusion time (s)", + "(t_plasma_res_diffusion)", + physics_variables.t_plasma_res_diffusion, + "OP ", + ) + po.ovarre( + self.outfile, + "Plasma inductance (H)", + "(ind_plasma)", + physics_variables.ind_plasma, + "OP ", + ) + po.ovarre( + self.outfile, + "Plasma magnetic energy stored (J)", + "(e_plasma_magnetic_stored)", + physics_variables.e_plasma_magnetic_stored, + "OP ", + ) + po.ovarrf( + self.outfile, + "Plasma normalised internal inductance", + "(ind_plasma_internal_norm)", + physics_variables.ind_plasma_internal_norm, + "OP ", + ) + + po.oblnkl(self.outfile) + po.ostars(self.outfile, 110) + po.oblnkl(self.outfile) + + class DetailedPhysics: """Class to hold detailed physics models for plasma processing.""" diff --git a/process/stellarator.py b/process/stellarator.py index 6df9cab1c..ed5118b12 100644 --- a/process/stellarator.py +++ b/process/stellarator.py @@ -68,6 +68,7 @@ def __init__( physics, neoclassics, plasma_beta, + plasma_inductance, ) -> None: """Initialises the Stellarator model's variables @@ -105,6 +106,7 @@ def __init__( self.physics = physics self.neoclassics = neoclassics self.beta = plasma_beta + self.inductance = plasma_inductance def run(self, output: bool): """Routine to call the physics and engineering modules diff --git a/tests/unit/test_physics.py b/tests/unit/test_physics.py index 3736b45c9..9992a24a7 100644 --- a/tests/unit/test_physics.py +++ b/tests/unit/test_physics.py @@ -24,10 +24,10 @@ DetailedPhysics, Physics, PlasmaBeta, + PlasmaInductance, calculate_current_coefficient_hastie, calculate_plasma_current_peng, calculate_poloidal_field, - calculate_volt_second_requirements, diamagnetic_fraction_hender, diamagnetic_fraction_scene, ps_fraction_scene, @@ -55,6 +55,7 @@ def physics(): lower_hybrid=LowerHybrid(plasma_profile=PlasmaProfile()), ), PlasmaBeta(), + PlasmaInductance(), ) @@ -2099,7 +2100,7 @@ def test_vscalc(voltsecondreqparam): vs_plasma_res_ramp, vs_plasma_total_required, v_plasma_loop_burn, - ) = calculate_volt_second_requirements( + ) = PlasmaInductance.calculate_volt_second_requirements( csawth=voltsecondreqparam.csawth, eps=voltsecondreqparam.eps, f_c_plasma_inductive=voltsecondreqparam.f_c_plasma_inductive, @@ -3399,14 +3400,14 @@ def test_calculate_current_profile_index_wesson(): def test_calculate_internal_inductance_wesson(): """Test calculate_internal_inductance_wesson().""" alphaj = 0.8 - result = Physics.calculate_internal_inductance_wesson(alphaj) + result = PlasmaInductance.calculate_internal_inductance_wesson(alphaj) assert result == pytest.approx(0.8595087177751706, abs=0.0001) def test_calculate_internal_inductance_menard(): """Test calculate_internal_inductance_menard().""" kappa = 2.8 - result = Physics.calculate_internal_inductance_menard(kappa) + result = PlasmaInductance.calculate_internal_inductance_menard(kappa) assert result == pytest.approx(0.6, abs=0.001) @@ -3455,7 +3456,7 @@ def test_calculate_beta_norm_max_stambaugh(): def test_calculate_internal_inductance_iter_3(): """Test calculate_normalised_internal_inductance_iter_3.""" - result = Physics.calculate_normalised_internal_inductance_iter_3( + result = PlasmaInductance.calculate_normalised_internal_inductance_iter_3( b_plasma_poloidal_vol_avg=1.0, c_plasma=1.5e7, vol_plasma=1000.0, rmajor=6.2 ) assert result == pytest.approx(0.9078959099585583, abs=0.00001) diff --git a/tests/unit/test_stellarator.py b/tests/unit/test_stellarator.py index c7f063366..d3f38585e 100644 --- a/tests/unit/test_stellarator.py +++ b/tests/unit/test_stellarator.py @@ -29,7 +29,7 @@ ) from process.fw import FirstWall from process.hcpb import CCFE_HCPB -from process.physics import Physics, PlasmaBeta +from process.physics import Physics, PlasmaBeta, PlasmaInductance from process.plasma_profiles import PlasmaProfile from process.power import Power from process.stellarator import Neoclassics, Stellarator @@ -70,9 +70,11 @@ def stellarator(): ElectronBernstein(plasma_profile=PlasmaProfile()), ), PlasmaBeta(), + PlasmaInductance(), ), Neoclassics(), plasma_beta=PlasmaBeta(), + plasma_inductance=PlasmaInductance(), )