From 2b976dca395105acad6e1007c98f3a00499e0f32 Mon Sep 17 00:00:00 2001 From: chris-ashe Date: Sat, 25 Jul 2026 23:36:01 +0100 Subject: [PATCH] Introduce TFWPIntegerTurnType enum and update references in TF coil models and tests --- process/core/init.py | 32 ++++++++++---- process/core/io/plot/summary.py | 43 +++++++++++-------- .../superconducting_tf_coil_variables.py | 13 ++++++ process/models/tfcoil/base.py | 3 +- process/models/tfcoil/superconducting.py | 36 +++++++++++++--- tests/unit/models/tfcoil/test_tfcoil.py | 5 ++- 6 files changed, 95 insertions(+), 37 deletions(-) diff --git a/process/core/init.py b/process/core/init.py index 5aba77d345..8b2263f804 100644 --- a/process/core/init.py +++ b/process/core/init.py @@ -28,6 +28,7 @@ ConfinementTimeModel, DivertorNumberModels, ) +from process.data_structure.superconducting_tf_coil_variables import TFWPIntegerTurnType from process.models.pfcoil import PFLocationTypes from process.models.physics.profiles import ( DensityProfilePedestalType, @@ -266,9 +267,9 @@ def check_process(inputs, data): # noqa: ARG001 # Can't use c_tf_turn as iteration var, constraint or # input if i_tf_turns_integer == 1 - if ( - data.numerics.ixc[: data.numerics.nvar] == 60 - ).any() and data.tfcoil.i_tf_turns_integer == 1: + if (data.numerics.ixc[: data.numerics.nvar] == 60).any() and TFWPIntegerTurnType( + data.tfcoil.i_tf_turns_integer + ) == TFWPIntegerTurnType.INTEGER: raise ProcessValidationError( "Iteration variable 60 (TF current per turn, c_tf_turn) cannot be used with" " the TF coil integer turn model (i_tf_turns_integer == 1) as it is a" @@ -871,7 +872,8 @@ def check_process(inputs, data): # noqa: ARG001 if ( data.tfcoil.i_tf_sc_mat == SuperconductorModel.CROCO_REBCO - and data.tfcoil.i_tf_turns_integer == 1 + and TFWPIntegerTurnType(data.tfcoil.i_tf_turns_integer) + == TFWPIntegerTurnType.INTEGER ): raise ProcessValidationError( "Integer turns (i_tf_turns_integer = 1) not supported for REBCO" @@ -898,9 +900,15 @@ def check_process(inputs, data): # noqa: ARG001 # Setting the default WP geometry i_tf_wp_geom = SuperconductingTFWPShapeType(data.tfcoil.i_tf_wp_geom) if i_tf_wp_geom == SuperconductingTFWPShapeType.UNSET: - if data.tfcoil.i_tf_turns_integer == 0: + if ( + TFWPIntegerTurnType(data.tfcoil.i_tf_turns_integer) + == TFWPIntegerTurnType.NON_INTEGER + ): data.tfcoil.i_tf_wp_geom = SuperconductingTFWPShapeType.DOUBLE_RECTANGULAR - if data.tfcoil.i_tf_turns_integer == 1: + if ( + TFWPIntegerTurnType(data.tfcoil.i_tf_turns_integer) + == TFWPIntegerTurnType.INTEGER + ): data.tfcoil.i_tf_wp_geom = SuperconductingTFWPShapeType.RECTANGULAR # Setting the TF coil conductor elastic properties @@ -989,7 +997,11 @@ def check_process(inputs, data): # noqa: ARG001 data.tfcoil.i_dx_tf_turn_general_input = abs(data.tfcoil.dx_tf_turn_general) > 0 # Impossible to set the turn size of integer turn option - if data.tfcoil.i_dx_tf_turn_general_input and data.tfcoil.i_tf_turns_integer == 1: + if ( + data.tfcoil.i_dx_tf_turn_general_input + and TFWPIntegerTurnType(data.tfcoil.i_tf_turns_integer) + == TFWPIntegerTurnType.INTEGER + ): raise ProcessValidationError( "Impossible to set the TF turn/cable size with the integer turn option" " (i_tf_turns_integer: 1)" @@ -997,7 +1009,8 @@ def check_process(inputs, data): # noqa: ARG001 if ( data.tfcoil.i_tf_wp_geom != SuperconductingTFWPShapeType.RECTANGULAR - and data.tfcoil.i_tf_turns_integer == 1 + and TFWPIntegerTurnType(data.tfcoil.i_tf_turns_integer) + == TFWPIntegerTurnType.INTEGER ): raise ProcessValidationError( "Can only have i_tf_turns_integer = 1 with i_tf_wp_geom = 0" @@ -1018,7 +1031,8 @@ def check_process(inputs, data): # noqa: ARG001 # Impossible to set the cable size of integer turn option if ( data.tfcoil.i_dx_tf_turn_cable_space_general_input - and data.tfcoil.i_tf_turns_integer == 1 + and TFWPIntegerTurnType(data.tfcoil.i_tf_turns_integer) + == TFWPIntegerTurnType.INTEGER ): raise ProcessValidationError( "Impossible to set the TF turn/cable size with the integer turn option" diff --git a/process/core/io/plot/summary.py b/process/core/io/plot/summary.py index e8b2fea071..e92d96ff09 100644 --- a/process/core/io/plot/summary.py +++ b/process/core/io/plot/summary.py @@ -24,6 +24,7 @@ from process.data_structure.numerics import FiguresOfMerit, PROCESSRunMode from process.data_structure.pfcoil_variables import NFIXMX from process.data_structure.physics_variables import ConfinementTimeModel +from process.data_structure.superconducting_tf_coil_variables import TFWPIntegerTurnType from process.models.build import Build from process.models.engineering.materials import ( calculate_tresca_stress, @@ -5793,7 +5794,7 @@ def plot_superconducting_tf_wp(axis: plt.Axes, mfile: MFile, scan: int, fig): r_tf_wp_inboard_outer = mfile.get("r_tf_wp_inboard_outer", scan=scan) r_tf_wp_inboard_centre = mfile.get("r_tf_wp_inboard_centre", scan=scan) - if i_tf_turns_integer == 1: + if TFWPIntegerTurnType(i_tf_turns_integer) == TFWPIntegerTurnType.INTEGER: turn_layers = mfile.get("n_tf_wp_layers", scan=scan) turn_pancakes = mfile.get("n_tf_wp_pancakes", scan=scan) @@ -5953,7 +5954,7 @@ def plot_superconducting_tf_wp(axis: plt.Axes, mfile: MFile, scan: int, fig): # Plot the rectangular WP if i_tf_wp_geom == 0: - if i_tf_turns_integer == 1: + if TFWPIntegerTurnType(i_tf_turns_integer) == TFWPIntegerTurnType.INTEGER: long_turns = round(turn_layers) short_turns = round(turn_pancakes) else: @@ -7149,13 +7150,13 @@ def _pack_strands_rectangular_with_obstacles( # Import the TF turn variables then multiply into mm i_tf_turns_integer = mfile.get("i_tf_turns_integer", scan=scan) # If integer turns switch is on then the turns can have non square dimensions - if i_tf_turns_integer == 1: + if TFWPIntegerTurnType(i_tf_turns_integer) == TFWPIntegerTurnType.INTEGER: turn_width = mfile.get("dr_tf_turn", scan=scan) turn_height = mfile.get("dx_tf_turn", scan=scan) cable_space_width_radial = mfile.get("dr_tf_turn_cable_space", scan=scan) cable_space_width_toroidal = mfile.get("dx_tf_turn_cable_space", scan=scan) - elif i_tf_turns_integer == 0: + elif TFWPIntegerTurnType(i_tf_turns_integer) == TFWPIntegerTurnType.NON_INTEGER: turn_width = mfile.get("dx_tf_turn_general", scan=scan) cable_space_width = mfile.get("dx_tf_turn_cable_space_average", scan=scan) @@ -7179,7 +7180,7 @@ def _pack_strands_rectangular_with_obstacles( ) # Plot the total turn shape - if i_tf_turns_integer == 0: + if TFWPIntegerTurnType(i_tf_turns_integer) == TFWPIntegerTurnType.NON_INTEGER: axis.add_patch( Rectangle( [0, 0], @@ -7261,7 +7262,13 @@ def _pack_strands_rectangular_with_obstacles( cable_space_bounds=cable_bounds, pipe_center=( turn_width / 2, - (turn_width if i_tf_turns_integer == 0 else turn_height) / 2, + ( + turn_width + if TFWPIntegerTurnType(i_tf_turns_integer) + == TFWPIntegerTurnType.NON_INTEGER + else turn_height + ) + / 2, ), pipe_radius=he_pipe_diameter / 2, strand_diameter=strand_diameter, @@ -7278,7 +7285,7 @@ def _pack_strands_rectangular_with_obstacles( axis.set_ylim(-turn_width * 0.05, turn_width * 1.05) # Non square turns - elif i_tf_turns_integer == 1: + elif TFWPIntegerTurnType(i_tf_turns_integer) == TFWPIntegerTurnType.INTEGER: axis.add_patch( Rectangle( [0, 0], @@ -7423,7 +7430,7 @@ def _pack_strands_rectangular_with_obstacles( }, ) - if i_tf_turns_integer == 0: + if TFWPIntegerTurnType(i_tf_turns_integer) == TFWPIntegerTurnType.NON_INTEGER: # Add info about the steel casing surrounding the WP textstr_turn_cable_space = ( f"$\\mathbf{{Cable \\ Space:}}$\n\n" @@ -7433,7 +7440,7 @@ def _pack_strands_rectangular_with_obstacles( f"Extra cable space area void fraction: {f_a_tf_turn_cable_space_extra_void}\n" f"True cable space area: {a_tf_turn_cable_space_effective:.3e} m$^2$" ) - elif i_tf_turns_integer == 1: + elif TFWPIntegerTurnType(i_tf_turns_integer) == TFWPIntegerTurnType.INTEGER: textstr_turn_cable_space = ( f"$\\mathbf{{Cable \\ Space:}}$\n\n" f"Cable space: \n$\\Delta r$: {cable_space_width_radial:.3e} m \n" @@ -7460,14 +7467,14 @@ def _pack_strands_rectangular_with_obstacles( }, ) - if i_tf_turns_integer == 0: + if TFWPIntegerTurnType(i_tf_turns_integer) == TFWPIntegerTurnType.NON_INTEGER: textstr_turn = ( f"$\\mathbf{{Turn:}}$\n\n" f"$\\Delta r$: {turn_width:.3e} m\n" f"$\\Delta x$: {turn_width:.3e} m" ) - if i_tf_turns_integer == 1: + if TFWPIntegerTurnType(i_tf_turns_integer) == TFWPIntegerTurnType.INTEGER: textstr_turn = ( f"$\\mathbf{{Turn:}}$\n\n" f"$\\Delta r$: {turn_width:.3e} m\n" @@ -7552,13 +7559,13 @@ def plot_tf_croco_turn(axis: plt.Axes, fig, mfile: MFile, scan: int): # Import the TF turn variables then multiply into mm i_tf_turns_integer = mfile.get("i_tf_turns_integer", scan=scan) # If integer turns switch is on then the turns can have non square dimensions - if i_tf_turns_integer == 1: + if TFWPIntegerTurnType(i_tf_turns_integer) == TFWPIntegerTurnType.INTEGER: turn_width = mfile.get("dr_tf_turn", scan=scan) turn_height = mfile.get("dx_tf_turn", scan=scan) cable_space_width_radial = mfile.get("dr_tf_turn_cable_space", scan=scan) cable_space_width_toroidal = mfile.get("dx_tf_turn_cable_space", scan=scan) - elif i_tf_turns_integer == 0: + elif TFWPIntegerTurnType(i_tf_turns_integer) == TFWPIntegerTurnType.NON_INTEGER: turn_width = mfile.get("dx_tf_turn_general", scan=scan) cable_space_width = mfile.get("dx_tf_turn_cable_space_average", scan=scan) @@ -7584,7 +7591,7 @@ def plot_tf_croco_turn(axis: plt.Axes, fig, mfile: MFile, scan: int): dia_tf_turn_croco_cable = mfile.get("dia_tf_turn_croco_cable", scan=scan) # Plot the total turn shape - if i_tf_turns_integer == 0: + if TFWPIntegerTurnType(i_tf_turns_integer) == TFWPIntegerTurnType.NON_INTEGER: axis.add_patch( Rectangle( [0, 0], @@ -7702,7 +7709,7 @@ def plot_tf_croco_turn(axis: plt.Axes, fig, mfile: MFile, scan: int): }, ) - if i_tf_turns_integer == 0: + if TFWPIntegerTurnType(i_tf_turns_integer) == TFWPIntegerTurnType.NON_INTEGER: # Add info about the steel casing surrounding the WP textstr_turn_cable_space = ( f"$\\mathbf{{Cable \\ Space:}}$\n\n" @@ -7712,7 +7719,7 @@ def plot_tf_croco_turn(axis: plt.Axes, fig, mfile: MFile, scan: int): f"Extra cable space area void fraction: {f_a_tf_turn_cable_space_extra_void}\n" f"True cable space area: {a_tf_turn_cable_space_effective:.3e} m$^2$" ) - elif i_tf_turns_integer == 1: + elif TFWPIntegerTurnType(i_tf_turns_integer) == TFWPIntegerTurnType.INTEGER: textstr_turn_cable_space = ( f"$\\mathbf{{Cable \\ Space:}}$\n\n" f"Cable space: \n$\\Delta r$: {cable_space_width_radial:.3e} m \n" @@ -7739,14 +7746,14 @@ def plot_tf_croco_turn(axis: plt.Axes, fig, mfile: MFile, scan: int): }, ) - if i_tf_turns_integer == 0: + if TFWPIntegerTurnType(i_tf_turns_integer) == TFWPIntegerTurnType.NON_INTEGER: textstr_turn = ( f"$\\mathbf{{Turn:}}$\n\n" f"$\\Delta r$: {turn_width:.3e} m\n" f"$\\Delta x$: {turn_width:.3e} m" ) - if i_tf_turns_integer == 1: + if TFWPIntegerTurnType(i_tf_turns_integer) == TFWPIntegerTurnType.INTEGER: textstr_turn = ( f"$\\mathbf{{Turn:}}$\n\n" f"$\\Delta r$: {turn_width:.3e} m\n" diff --git a/process/data_structure/superconducting_tf_coil_variables.py b/process/data_structure/superconducting_tf_coil_variables.py index 5b5574c1b4..285555102f 100644 --- a/process/data_structure/superconducting_tf_coil_variables.py +++ b/process/data_structure/superconducting_tf_coil_variables.py @@ -10,6 +10,19 @@ """ from dataclasses import dataclass +from enum import IntEnum + + +class TFWPIntegerTurnType(IntEnum): + """TF winding pack integer turn type, controlled via the `i_tf_turns_integer` + variable. + """ + + NON_INTEGER = 0 + """Non integer number of turns in TF winding pack""" + + INTEGER = 1 + """Integer number of turns in TF winding pack""" @dataclass(slots=True) diff --git a/process/models/tfcoil/base.py b/process/models/tfcoil/base.py index 549d2d1a3b..ec837ce5fa 100644 --- a/process/models/tfcoil/base.py +++ b/process/models/tfcoil/base.py @@ -19,6 +19,7 @@ from process.data_structure.build_variables import TFCSRadialConfiguration from process.data_structure.pfcoil_variables import PFConductorModel from process.data_structure.physics_variables import DivertorNumberModels +from process.data_structure.superconducting_tf_coil_variables import TFWPIntegerTurnType from process.models.engineering.materials import ( calculate_tresca_stress, calculate_von_mises_stress, @@ -2741,7 +2742,7 @@ def stresscl( # Non-integer or interger number of turns t_cable_eyng = ( dx_tf_turn_cable_space_average - if i_tf_turns_integer == 0 + if i_tf_turns_integer == TFWPIntegerTurnType.NON_INTEGER else dr_tf_turn_cable_space ) diff --git a/process/models/tfcoil/superconducting.py b/process/models/tfcoil/superconducting.py index fdc25e2030..93b9128336 100644 --- a/process/models/tfcoil/superconducting.py +++ b/process/models/tfcoil/superconducting.py @@ -12,6 +12,7 @@ from process.core import process_output as po from process.core.exceptions import ProcessValueError from process.core.model import DataStructure +from process.data_structure.superconducting_tf_coil_variables import TFWPIntegerTurnType from process.models import superconductors from process.models.superconductors import ( N_CROCO_STRANDS_TURN, @@ -416,7 +417,10 @@ def output_general_superconducting_tf_info(self) -> None: "OP ", ) - if self.data.tfcoil.i_tf_turns_integer == 1: + if ( + TFWPIntegerTurnType(self.data.tfcoil.i_tf_turns_integer) + == TFWPIntegerTurnType.INTEGER + ): po.ovarre( self.outfile, "Winding pack toroidal width (m)", @@ -501,7 +505,10 @@ def output_general_superconducting_tf_info(self) -> None: "(i_tf_turns_integer)", self.data.tfcoil.i_tf_turns_integer, ) - if self.data.tfcoil.i_tf_turns_integer == 0: + if ( + TFWPIntegerTurnType(self.data.tfcoil.i_tf_turns_integer) + == TFWPIntegerTurnType.NON_INTEGER + ): po.ocmmnt(self.outfile, " Non-integer number of turns") else: po.ocmmnt(self.outfile, " Integer number of turns") @@ -513,7 +520,10 @@ def output_general_superconducting_tf_info(self) -> None: self.data.tfcoil.n_tf_coil_turns, "OP ", ) - if self.data.tfcoil.i_tf_turns_integer == 1: + if ( + TFWPIntegerTurnType(self.data.tfcoil.i_tf_turns_integer) + == TFWPIntegerTurnType.INTEGER + ): po.ovarre( self.outfile, "Number of TF pancakes", @@ -529,7 +539,10 @@ def output_general_superconducting_tf_info(self) -> None: po.oblnkl(self.outfile) - if self.data.tfcoil.i_tf_turns_integer == 1: + if ( + TFWPIntegerTurnType(self.data.tfcoil.i_tf_turns_integer) + == TFWPIntegerTurnType.INTEGER + ): po.ovarre( self.outfile, "Radial width of turn (m)", @@ -2221,7 +2234,10 @@ def run(self, output: bool = False): d_sc_tf = self.data.superconducting_tfcoil # Setting the WP turn geometry / areas - if self.data.tfcoil.i_tf_turns_integer == 0: + if ( + TFWPIntegerTurnType(self.data.tfcoil.i_tf_turns_integer) + == TFWPIntegerTurnType.NON_INTEGER + ): # Non-ingeger number of turns avg_turn_geometry = self.tf_cable_in_conduit_averaged_turn_geometry( j_tf_wp=self.data.tfcoil.j_tf_wp, @@ -3775,7 +3791,10 @@ def run(self, output: bool = False): d_sc_tf = self.data.superconducting_tfcoil # Setting the WP turn geometry / areas - if self.data.tfcoil.i_tf_turns_integer == 0: + if ( + TFWPIntegerTurnType(self.data.tfcoil.i_tf_turns_integer) + == TFWPIntegerTurnType.NON_INTEGER + ): # Non-ingeger number of turns avg_turn_geometry = self.tf_croco_averaged_turn_geometry( @@ -3809,7 +3828,10 @@ def run(self, output: bool = False): avg_turn_geometry.dx_tf_turn_cable_space_average ) - elif self.data.tfcoil.i_tf_turns_integer == 1: + elif ( + TFWPIntegerTurnType(self.data.tfcoil.i_tf_turns_integer) + == TFWPIntegerTurnType.INTEGER + ): raise ProcessValueError( "Integer turn geometry not implemented for CroCo conductor." ) diff --git a/tests/unit/models/tfcoil/test_tfcoil.py b/tests/unit/models/tfcoil/test_tfcoil.py index cea20f7dce..14de1cc5db 100644 --- a/tests/unit/models/tfcoil/test_tfcoil.py +++ b/tests/unit/models/tfcoil/test_tfcoil.py @@ -12,6 +12,7 @@ import process.models.tfcoil.base as tfcoil_module from process.data_structure.build_variables import TFCSRadialConfiguration from process.data_structure.pfcoil_variables import PFConductorModel +from process.data_structure.superconducting_tf_coil_variables import TFWPIntegerTurnType @pytest.fixture @@ -1020,7 +1021,7 @@ class StressclParam(NamedTuple): n_tf_coils=16, i_tf_stress_model=1, sig_tf_wp_max=580000000, - i_tf_turns_integer=1, + i_tf_turns_integer=TFWPIntegerTurnType.INTEGER, dr_tf_plasma_case=0.060000000000000012, a_tf_wp_conductor=0.1653572639592335, a_tf_wp_extra_void=0.07759938309736393, @@ -1136,7 +1137,7 @@ class StressclParam(NamedTuple): n_tf_coils=16, i_tf_stress_model=1, sig_tf_wp_max=580000000, - i_tf_turns_integer=1, + i_tf_turns_integer=TFWPIntegerTurnType.INTEGER, dr_tf_plasma_case=0.060000000000000012, a_tf_wp_conductor=0.1653572639592335, a_tf_wp_extra_void=0.07759938309736393,