diff --git a/process/core/init.py b/process/core/init.py index 634d404e4b..ff52d93341 100644 --- a/process/core/init.py +++ b/process/core/init.py @@ -2,6 +2,7 @@ import datetime import getpass +import logging import socket import subprocess # noqa: S404 from pathlib import Path @@ -22,9 +23,16 @@ from process.data_structure.impurity_radiation_variables import N_IMPURITIES from process.data_structure.numerics import FiguresOfMerit, PROCESSRunMode from process.data_structure.pfcoil_variables import PFConductorModel -from process.data_structure.physics_variables import DivertorNumberModels +from process.data_structure.physics_variables import ( + ConfinementMode, + ConfinementTimeModel, + DivertorNumberModels, +) from process.models.pfcoil import PFLocationTypes -from process.models.physics.profiles import DensityProfilePedestalType +from process.models.physics.profiles import ( + DensityProfilePedestalType, + PlasmaProfileShapeType, +) from process.models.stellarator.initialization import st_init from process.models.superconductors import ( SuperconductorMaterial, @@ -34,6 +42,7 @@ from process.models.tfcoil.base import TFCoilShapeModel, TFConductorModel from process.models.tfcoil.superconducting import SuperconductingTFWPShapeType +logger = logging.getLogger(__name__) if TYPE_CHECKING: from process.core.model import DataStructure @@ -1018,19 +1027,44 @@ def check_process(inputs, data): # noqa: ARG001 data.tfcoil.temp_tf_superconductor_margin_min = data.tfcoil.tmargmin data.tfcoil.temp_cs_superconductor_margin_min = data.tfcoil.tmargmin - if data.physics.tauee_in > 1e-10 and data.physics.i_confinement_time != 48: + if ( + data.physics.tauee_in > 1e-10 + and data.physics.i_confinement_time != ConfinementTimeModel.NSTX_GYRO_BOHM + ): # Report error if confinement time is in the input # but the scaling to use it is not selected. warn("tauee_in is for use with i_confinement_time=48 only", stacklevel=2) - if data.physics.aspect > 1.7 and data.physics.i_confinement_time == 46: + if ( + data.physics.aspect > 1.7 + and data.physics.i_confinement_time == ConfinementTimeModel.MENARD_NSTX + ): # NSTX scaling is for A<1.7 warn("NSTX scaling is for A<1.7", stacklevel=2) - if data.physics.i_plasma_current == 2 and data.physics.i_confinement_time == 42: + if ( + data.physics.i_plasma_current == 2 + and data.physics.i_confinement_time == ConfinementTimeModel.LANG_HIGH_DENSITY + ): raise ProcessValidationError( "Lang 2012 confinement scaling cannot be used for i_plasma_current=2 due to wrong q" ) + if ( + data.stellarator.istell == 0 + and ConfinementTimeModel(data.physics.i_confinement_time).mode + == ConfinementMode.STELLARATOR + ): + raise ProcessValidationError( + "Stellarator confinement time scaling cannot be used for a tokamak" + ) + if ( + data.physics.i_plasma_pedestal == PlasmaProfileShapeType.PEDESTAL_PROFILE + and ConfinementTimeModel(data.physics.i_confinement_time).mode + not in [ConfinementMode.H_MODE, ConfinementMode.I_MODE] + ): + logger.warning( + "Non H-mode or I-mode confinement time scaling should not be used with a pedestal profile" + ) # Cannot use temperature margin constraint with REBCO TF coils if ( diff --git a/process/data_structure/physics_variables.py b/process/data_structure/physics_variables.py index 9084ee39b5..da2ea2f240 100644 --- a/process/data_structure/physics_variables.py +++ b/process/data_structure/physics_variables.py @@ -20,62 +20,293 @@ class DivertorNumberModels(IntEnum): SINGLE_NULL = 1 +class ConfinementMode(IntEnum): + """Enum for plasma confinement mode""" + + L_MODE = (0, "L") + H_MODE = (1, "H") + I_MODE = (2, "I") + STELLARATOR = (3, "Stell") + OHMIC = (4, "Ohmic") + + def __new__(cls, value: int, abbreviation: str): + """Create a new instance of ConfinementMode. + + Parameters + ---------- + value : int + The enum value + abbreviation : str + The abbreviation of the confinement mode + + Returns + ------- + ConfinementMode + A new enum instance with the given value and abbreviation + """ + obj = int.__new__(cls, value) + obj._value_ = value + obj.abbreviation = abbreviation + + return obj + + class ConfinementTimeModel(IntEnum): """Confinement time (τ_E) model types""" - USER_INPUT = (0, "User input electron confinement ") - NEO_ALCATOR = (1, "Neo-Alcator (Ohmic)") - MIRNOV = (2, "Mirnov (H)") - MEREZHKIN_MUHKOVATOV = (3, "Merezkhin-Muhkovatov (Ohmic)(L)") - SHIMOMURA = (4, "Shimomura (H)") - KAYE_GOLDSTON = (5, "Kaye-Goldston (L)") - ITER_89P = (6, "ITER 89-P (L)") - ITER_89_0 = (7, "ITER 89-O (L)") - REBUT_LALLIA = (8, "Rebut-Lallia (L)") - GOLDSTON = (9, "Goldston (L)") - T_10 = (10, "T10 (L)") - JAERI = (11, "JAERI / Odajima-Shimomura (L)") - KAYE_BIG = (12, "Kaye-Big Complex (L)") - ITER_H90_P = (13, "ITER H90-P (H)") - MINIMUM_OF_ITER_89P_AND_ITER_89_0 = (14, "ITER 89-P & 89-O min (L)") - RIEDEL_L = (15, "Riedel (L)") - CHRISTIANSEN = (16, "Christiansen (L)") - LACKNER_GOTTARDI = (17, "Lackner-Gottardi (L)") - NEO_KAYE = (18, "Neo-Kaye (L)") - RIEDEL_H = (19, "Riedel (H)") - ITER_H90_P_AMENDED = (20, "ITER H90-P amended (H)") - SUDO_ET_AL = (21, "LHD (Stell)") - GYRO_REDUCED_BOHM = (22, "Gyro-reduced Bohm (Stell)") - LACKNER_GOTTARDI_STELLARATOR = (23, "Lackner-Gottardi (Stell)") - ITER_93H = (24, "ITER-93H ELM-free (H)") - TITAN_REMOVED = (25, "TITAN RFP OBSOLETE ") - ITER_H97P = (26, "ITER H-97P ELM-free (H)") - ITER_H97P_ELMY = (27, "ITER H-97P ELMy (H)") - ITER_96P = (28, "ITER-96P (ITER-97L) (L)") - VALOVIC_ELMY = (29, "Valovic modified ELMy (H)") - KAYE = (30, "Kaye 98 modified (L)") - ITER_PB98P_Y = (31, "ITERH-PB98P(y) (H)") - IPB98_Y = (32, "IPB98(y) (H)") - ITER_IPB98Y1 = (33, "IPB98(y,1) (H)") - ITER_IPB98Y2 = (34, "IPB98(y,2) (H)") - ITER_IPB98Y3 = (35, "IPB98(y,3) (H)") - ITER_IPB98Y4 = (36, "IPB98(y,4) (H)") - ISS95_STELLARATOR = (37, "ISS95 (Stell)") - ISS04_STELLARATOR = (38, "ISS04 (Stell)") - DS03 = (39, "DS03 beta-independent (H)") - MURARI = (40, 'Murari "Non-power law" (H)') - PETTY08 = (41, "Petty 2008 (ST)(H)") - LANG_HIGH_DENSITY = (42, "Lang high density (H)") - HUBBARD_NOMINAL = (43, "Hubbard 2017 - nominal (I)") - HUBBARD_LOWER = (44, "Hubbard 2017 - lower (I)") - HUBBARD_UPPER = (45, "Hubbard 2017 - upper (I)") - MENARD_NSTX = (46, "Menard NSTX (ST)(H)") - MENARD_NSTX_PETTY08_HYBRID = (47, "Menard NSTX-Petty08 hybrid (ST)(H)") - NSTX_GYRO_BOHM = (48, "Buxton NSTX gyro-Bohm (ST)(H)") - ITPA20 = (49, "ITPA20 (H)") - ITPA20_IL = (50, "ITPA20-IL (H)") - - def __new__(cls, value: int, full_name: str): + USER_INPUT = (0, "User input electron confinement ", None) + NEO_ALCATOR = ( + 1, + f"Neo-Alcator ({ConfinementMode.OHMIC.abbreviation})", + ConfinementMode.OHMIC, + ) + MIRNOV = ( + 2, + f"Mirnov ({ConfinementMode.H_MODE.abbreviation})", + ConfinementMode.H_MODE, + ) + MEREZHKIN_MUHKOVATOV = ( + 3, + f"Merezkhin-Muhkovatov ({ConfinementMode.OHMIC.abbreviation})({ConfinementMode.L_MODE.abbreviation})", + ConfinementMode.OHMIC | ConfinementMode.L_MODE, + ) + SHIMOMURA = ( + 4, + f"Shimomura ({ConfinementMode.H_MODE.abbreviation})", + ConfinementMode.H_MODE, + ) + KAYE_GOLDSTON = ( + 5, + f"Kaye-Goldston ({ConfinementMode.L_MODE.abbreviation})", + ConfinementMode.L_MODE, + ) + ITER_89P = ( + 6, + f"ITER 89-P ({ConfinementMode.L_MODE.abbreviation})", + ConfinementMode.L_MODE, + ) + ITER_89_0 = ( + 7, + f"ITER 89-O ({ConfinementMode.L_MODE.abbreviation})", + ConfinementMode.L_MODE, + ) + REBUT_LALLIA = ( + 8, + f"Rebut-Lallia ({ConfinementMode.L_MODE.abbreviation})", + ConfinementMode.L_MODE, + ) + GOLDSTON = ( + 9, + f"Goldston ({ConfinementMode.L_MODE.abbreviation})", + ConfinementMode.L_MODE, + ) + T_10 = ( + 10, + f"T10 ({ConfinementMode.L_MODE.abbreviation})", + ConfinementMode.L_MODE, + ) + JAERI = ( + 11, + f"JAERI / Odajima-Shimomura ({ConfinementMode.L_MODE.abbreviation})", + ConfinementMode.L_MODE, + ) + KAYE_BIG = ( + 12, + f"Kaye-Big Complex ({ConfinementMode.L_MODE.abbreviation})", + ConfinementMode.L_MODE, + ) + ITER_H90_P = ( + 13, + f"ITER H90-P ({ConfinementMode.H_MODE.abbreviation})", + ConfinementMode.H_MODE, + ) + MINIMUM_OF_ITER_89P_AND_ITER_89_0 = ( + 14, + f"ITER 89-P & 89-O min ({ConfinementMode.L_MODE.abbreviation})", + ConfinementMode.L_MODE, + ) + RIEDEL_L = ( + 15, + f"Riedel ({ConfinementMode.L_MODE.abbreviation})", + ConfinementMode.L_MODE, + ) + CHRISTIANSEN = ( + 16, + f"Christiansen ({ConfinementMode.L_MODE.abbreviation})", + ConfinementMode.L_MODE, + ) + LACKNER_GOTTARDI = ( + 17, + f"Lackner-Gottardi ({ConfinementMode.L_MODE.abbreviation})", + ConfinementMode.L_MODE, + ) + NEO_KAYE = ( + 18, + f"Neo-Kaye ({ConfinementMode.L_MODE.abbreviation})", + ConfinementMode.L_MODE, + ) + RIEDEL_H = ( + 19, + f"Riedel ({ConfinementMode.H_MODE.abbreviation})", + ConfinementMode.H_MODE, + ) + ITER_H90_P_AMENDED = ( + 20, + f"ITER H90-P amended ({ConfinementMode.H_MODE.abbreviation})", + ConfinementMode.H_MODE, + ) + SUDO_ET_AL = ( + 21, + f"LHD ({ConfinementMode.STELLARATOR.abbreviation})", + ConfinementMode.STELLARATOR, + ) + GYRO_REDUCED_BOHM = ( + 22, + f"Gyro-reduced Bohm ({ConfinementMode.STELLARATOR.abbreviation})", + ConfinementMode.STELLARATOR, + ) + LACKNER_GOTTARDI_STELLARATOR = ( + 23, + f"Lackner-Gottardi ({ConfinementMode.STELLARATOR.abbreviation})", + ConfinementMode.STELLARATOR, + ) + ITER_93H = ( + 24, + f"ITER-93H ELM-free ({ConfinementMode.H_MODE.abbreviation})", + ConfinementMode.H_MODE, + ) + TITAN_REMOVED = ( + 25, + "TITAN RFP OBSOLETE (N/A)", + None, + ) + ITER_H97P = ( + 26, + f"ITER H-97P ELM-free ({ConfinementMode.H_MODE.abbreviation})", + ConfinementMode.H_MODE, + ) + ITER_H97P_ELMY = ( + 27, + f"ITER H-97P ELMy ({ConfinementMode.H_MODE.abbreviation})", + ConfinementMode.H_MODE, + ) + ITER_96P = ( + 28, + f"ITER-96P (ITER-97L) ({ConfinementMode.L_MODE.abbreviation})", + ConfinementMode.L_MODE, + ) + VALOVIC_ELMY = ( + 29, + f"Valovic modified ELMy ({ConfinementMode.H_MODE.abbreviation})", + ConfinementMode.H_MODE, + ) + KAYE = ( + 30, + f"Kaye 98 modified ({ConfinementMode.L_MODE.abbreviation})", + ConfinementMode.L_MODE, + ) + ITER_PB98P_Y = ( + 31, + f"ITERH-PB98P(y) ({ConfinementMode.H_MODE.abbreviation})", + ConfinementMode.H_MODE, + ) + IPB98_Y = ( + 32, + f"IPB98(y) ({ConfinementMode.H_MODE.abbreviation})", + ConfinementMode.H_MODE, + ) + ITER_IPB98Y1 = ( + 33, + f"IPB98(y,1) ({ConfinementMode.H_MODE.abbreviation})", + ConfinementMode.H_MODE, + ) + ITER_IPB98Y2 = ( + 34, + f"IPB98(y,2) ({ConfinementMode.H_MODE.abbreviation})", + ConfinementMode.H_MODE, + ) + ITER_IPB98Y3 = ( + 35, + f"IPB98(y,3) ({ConfinementMode.H_MODE.abbreviation})", + ConfinementMode.H_MODE, + ) + ITER_IPB98Y4 = ( + 36, + f"IPB98(y,4) ({ConfinementMode.H_MODE.abbreviation})", + ConfinementMode.H_MODE, + ) + ISS95_STELLARATOR = ( + 37, + f"ISS95 ({ConfinementMode.STELLARATOR.abbreviation})", + ConfinementMode.STELLARATOR, + ) + ISS04_STELLARATOR = ( + 38, + f"ISS04 ({ConfinementMode.STELLARATOR.abbreviation})", + ConfinementMode.STELLARATOR, + ) + DS03 = ( + 39, + f"DS03 beta-independent ({ConfinementMode.H_MODE.abbreviation})", + ConfinementMode.H_MODE, + ) + MURARI = ( + 40, + f'Murari "Non-power law" ({ConfinementMode.H_MODE.abbreviation})', + ConfinementMode.H_MODE, + ) + PETTY08 = ( + 41, + f"Petty 2008 ({ConfinementMode.H_MODE.abbreviation})", + ConfinementMode.H_MODE, + ) + LANG_HIGH_DENSITY = ( + 42, + f"Lang high density ({ConfinementMode.H_MODE.abbreviation})", + ConfinementMode.H_MODE, + ) + HUBBARD_NOMINAL = ( + 43, + f"Hubbard 2017 - nominal ({ConfinementMode.I_MODE.abbreviation})", + ConfinementMode.I_MODE, + ) + HUBBARD_LOWER = ( + 44, + f"Hubbard 2017 - lower ({ConfinementMode.I_MODE.abbreviation})", + ConfinementMode.I_MODE, + ) + HUBBARD_UPPER = ( + 45, + f"Hubbard 2017 - upper ({ConfinementMode.I_MODE.abbreviation})", + ConfinementMode.I_MODE, + ) + MENARD_NSTX = ( + 46, + f"Menard NSTX ({ConfinementMode.H_MODE.abbreviation})", + ConfinementMode.H_MODE, + ) + MENARD_NSTX_PETTY08_HYBRID = ( + 47, + f"Menard NSTX-Petty08 hybrid ({ConfinementMode.H_MODE.abbreviation})", + ConfinementMode.H_MODE, + ) + NSTX_GYRO_BOHM = ( + 48, + f"Buxton NSTX gyro-Bohm ({ConfinementMode.H_MODE.abbreviation})", + ConfinementMode.H_MODE, + ) + ITPA20 = ( + 49, + f"ITPA20 ({ConfinementMode.H_MODE.abbreviation})", + ConfinementMode.H_MODE, + ) + ITPA20_IL = ( + 50, + f"ITPA20-IL ({ConfinementMode.H_MODE.abbreviation})", + ConfinementMode.H_MODE, + ) + + def __new__(cls, value: int, full_name: str, mode: ConfinementMode = None): """Create a new instance of ConfinementTimeModel. Parameters @@ -84,6 +315,8 @@ def __new__(cls, value: int, full_name: str): The enum value full_name : str The full name of the confinement time model + mode : ConfinementMode + The confinement mode associated with the model Returns ------- @@ -93,6 +326,7 @@ def __new__(cls, value: int, full_name: str): obj = int.__new__(cls, value) obj._value_ = value obj.full_name = full_name + obj.mode = mode return obj diff --git a/process/models/physics/confinement_time.py b/process/models/physics/confinement_time.py index 27a9b63415..c8afccff7e 100644 --- a/process/models/physics/confinement_time.py +++ b/process/models/physics/confinement_time.py @@ -11,6 +11,7 @@ from process.core.model import Model from process.data_structure.physics_variables import ( N_CONFINEMENT_SCALINGS, + ConfinementMode, ConfinementRadiationLossModel, ConfinementTimeModel, ) @@ -922,7 +923,7 @@ def calculate_confinement_time( # Calculate H* non-radiation corrected H factor # Note: we will assume the IPB-98y2 scaling. - if self.data.physics.i_rad_loss == 1: + if self.data.physics.i_rad_loss == ConfinementRadiationLossModel.CORE_ONLY: self.data.physics.hstar = ( hfact * ( @@ -935,7 +936,9 @@ def calculate_confinement_time( ) ** 0.31 ) - elif self.data.physics.i_rad_loss == 0: + elif ( + self.data.physics.i_rad_loss == ConfinementRadiationLossModel.FULL_RADIATION + ): self.data.physics.hstar = ( hfact * ( @@ -947,7 +950,7 @@ def calculate_confinement_time( ) ** 0.31 ) - elif self.data.physics.i_rad_loss == 2: + elif self.data.physics.i_rad_loss == ConfinementRadiationLossModel.NO_RADIATION: self.data.physics.hstar = hfact # Calculation of the transport power loss terms @@ -1116,9 +1119,12 @@ def fhz(hfact: float) -> float: ) # Include the radiation power if requested - if self.data.physics.i_rad_loss == 0: + if ( + self.data.physics.i_rad_loss + == ConfinementRadiationLossModel.FULL_RADIATION + ): fhz_value += self.data.physics.pden_plasma_rad_mw - elif self.data.physics.i_rad_loss == 1: + elif self.data.physics.i_rad_loss == ConfinementRadiationLossModel.CORE_ONLY: fhz_value += self.data.physics.pden_plasma_core_rad_mw return fhz_value @@ -1338,7 +1344,11 @@ def output_confinement_comparison(self, istell: int): po.oblnkl(self.outfile) # List of key values for stellarator scalings - stellarator_scalings = [21, 22, 23, 37, 38] + stellarator_scalings = [ + scaling.value + for scaling in ConfinementTimeModel + if ConfinementTimeModel(scaling) == ConfinementMode.STELLARATOR + ] # Plot all of the confinement scalings for comparison when H = 1 # Start from range 1 as the first i_confinement_time is a user input