From d0363088aab57e872417005b9cdf81bbf59c66c7 Mon Sep 17 00:00:00 2001 From: mn3981 Date: Wed, 10 Sep 2025 09:44:55 +0100 Subject: [PATCH 01/20] Refactor PF coil placement logic for improved readability and consistency --- process/pfcoil.py | 64 +++++++++++++++++++++++------------------------ 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/process/pfcoil.py b/process/pfcoil.py index 03627f0b7f..68c50abf84 100644 --- a/process/pfcoil.py +++ b/process/pfcoil.py @@ -218,18 +218,18 @@ def pfcoil(self): # Place the PF coils: - # N.B. Problems here if k=n_pf_coils_in_group(group) is greater than 2. - for j in range(pfcoil_variables.n_pf_coil_groups): - if pfcoil_variables.i_pf_location[j] == 1: + # N.B. Problems here if coil=n_pf_coils_in_group(group) is greater than 2. + for group in range(pfcoil_variables.n_pf_coil_groups): + if pfcoil_variables.i_pf_location[group] == 1: # PF coil is stacked on top of the Central Solenoid - for k in range(pfcoil_variables.n_pf_coils_in_group[j]): - pfcoil_variables.rcls[j, k] = ( + for coil in range(pfcoil_variables.n_pf_coils_in_group[group]): + pfcoil_variables.rcls[group, coil] = ( pfcoil_variables.r_cs_middle + pfcoil_variables.rpf1 ) # Z coordinate of coil enforced so as not # to occupy the same space as the Central Solenoid - pfcoil_variables.zcls[j, k] = signn[k] * ( + pfcoil_variables.zcls[group, coil] = signn[coil] * ( bv.z_tf_inside_half * pfcoil_variables.f_z_cs_tf_internal + 0.1e0 + 0.5e0 @@ -241,67 +241,67 @@ def pfcoil(self): ) ) - elif pfcoil_variables.i_pf_location[j] == 2: + elif pfcoil_variables.i_pf_location[group] == 2: # PF coil is on top of the TF coil - for k in range(pfcoil_variables.n_pf_coils_in_group[j]): - pfcoil_variables.rcls[j, k] = ( + for coil in range(pfcoil_variables.n_pf_coils_in_group[group]): + pfcoil_variables.rcls[group, coil] = ( pv.rmajor + pfcoil_variables.rpf2 * pv.triang * pv.rminor ) if pv.itart == 1 and pv.itartpf == 0: - pfcoil_variables.zcls[j, k] = ( - bv.z_tf_inside_half - pfcoil_variables.zref[j] - ) * signn[k] + pfcoil_variables.zcls[group, coil] = ( + bv.z_tf_inside_half - pfcoil_variables.zref[group] + ) * signn[coil] else: - # pfcoil_variables.zcls(j,k) = (bv.z_tf_inside_half + bv.dr_tf_inboard + 0.86e0) * signn(k) + # pfcoil_variables.zcls(group,coil) = (bv.z_tf_inside_half + bv.dr_tf_inboard + 0.86e0) * signn(coil) if top_bottom == 1: # this coil is above midplane - pfcoil_variables.zcls[j, k] = bv.z_tf_top + 0.86e0 + pfcoil_variables.zcls[group, coil] = bv.z_tf_top + 0.86e0 top_bottom = -1 else: # this coil is below midplane - pfcoil_variables.zcls[j, k] = -1.0e0 * ( + pfcoil_variables.zcls[group, coil] = -1.0e0 * ( bv.z_tf_top - 2.0e0 * bv.hpfdif + 0.86e0 ) top_bottom = 1 - elif pfcoil_variables.i_pf_location[j] == 3: + elif pfcoil_variables.i_pf_location[group] == 3: # PF coil is radially outside the TF coil - for k in range(pfcoil_variables.n_pf_coils_in_group[j]): - pfcoil_variables.zcls[j, k] = ( - pv.rminor * pfcoil_variables.zref[j] * signn[k] + for coil in range(pfcoil_variables.n_pf_coils_in_group[group]): + pfcoil_variables.zcls[group, coil] = ( + pv.rminor * pfcoil_variables.zref[group] * signn[coil] ) # Coil radius follows TF coil curve for SC TF (D-shape) # otherwise stacked for resistive TF (rectangle-shape) if tfv.i_tf_sup != 1 or pfcoil_variables.i_sup_pf_shape == 1: - pfcoil_variables.rcls[j, k] = pfcoil_variables.rclsnorm + pfcoil_variables.rcls[group, coil] = pfcoil_variables.rclsnorm else: - pfcoil_variables.rcls[j, k] = math.sqrt( + pfcoil_variables.rcls[group, coil] = math.sqrt( pfcoil_variables.rclsnorm**2 - - pfcoil_variables.zcls[j, k] ** 2 + - pfcoil_variables.zcls[group, coil] ** 2 ) try: - assert pfcoil_variables.rcls[j, k] < np.inf + assert pfcoil_variables.rcls[group, coil] < np.inf except AssertionError: logger.exception( "Element of pfcoil_variables.rcls is inf. Kludging to 1e10." ) - pfcoil_variables.rcls[j, k] = 1e10 + pfcoil_variables.rcls[group, coil] = 1e10 - elif pfcoil_variables.i_pf_location[j] == 4: + elif pfcoil_variables.i_pf_location[group] == 4: # PF coil is in general location # See issue 1418 # https://git.ccfe.ac.uk/process/process/-/issues/1418 - for k in range(pfcoil_variables.n_pf_coils_in_group[j]): - pfcoil_variables.zcls[j, k] = ( - pv.rminor * pfcoil_variables.zref[j] * signn[k] + for coil in range(pfcoil_variables.n_pf_coils_in_group[group]): + pfcoil_variables.zcls[group, coil] = ( + pv.rminor * pfcoil_variables.zref[group] * signn[coil] ) - pfcoil_variables.rcls[j, k] = ( - pv.rminor * pfcoil_variables.rref[j] + pv.rmajor + pfcoil_variables.rcls[group, coil] = ( + pv.rminor * pfcoil_variables.rref[group] + pv.rmajor ) else: raise ProcessValueError( "Illegal i_pf_location value", - j=j, - i_pf_location=pfcoil_variables.i_pf_location[j], + group=group, + i_pf_location=pfcoil_variables.i_pf_location[group], ) # Allocate current to the PF coils: From 5a7033d94a93e6c36aa4f60a8ff699083b3fbe3a Mon Sep 17 00:00:00 2001 From: mn3981 Date: Wed, 10 Sep 2025 10:15:08 +0100 Subject: [PATCH 02/20] Refactor PF coil placement logic by introducing a helper function for improved clarity and maintainability --- process/pfcoil.py | 84 +++++++++++++++++++++++++++++++++++++---------- 1 file changed, 67 insertions(+), 17 deletions(-) diff --git a/process/pfcoil.py b/process/pfcoil.py index 68c50abf84..6bd8617eba 100644 --- a/process/pfcoil.py +++ b/process/pfcoil.py @@ -222,24 +222,19 @@ def pfcoil(self): for group in range(pfcoil_variables.n_pf_coil_groups): if pfcoil_variables.i_pf_location[group] == 1: # PF coil is stacked on top of the Central Solenoid + # Use a helper function to compute rcls and zcls arrays for this group + rcls, zcls = self.place_pf_above_cs( + n_pf_coils_in_group=pfcoil_variables.n_pf_coils_in_group, + n_pf_group=group, + r_cs_middle=pfcoil_variables.r_cs_middle, + rpf1=pfcoil_variables.rpf1, + z_tf_inside_half=bv.z_tf_inside_half, + dr_tf_inboard=bv.dr_tf_inboard, + z_cs_coil_upper=pfcoil_variables.dz_cs_full / 2, + ) for coil in range(pfcoil_variables.n_pf_coils_in_group[group]): - pfcoil_variables.rcls[group, coil] = ( - pfcoil_variables.r_cs_middle + pfcoil_variables.rpf1 - ) - - # Z coordinate of coil enforced so as not - # to occupy the same space as the Central Solenoid - pfcoil_variables.zcls[group, coil] = signn[coil] * ( - bv.z_tf_inside_half * pfcoil_variables.f_z_cs_tf_internal - + 0.1e0 - + 0.5e0 - * ( - bv.z_tf_inside_half - * (1.0e0 - pfcoil_variables.f_z_cs_tf_internal) - + bv.dr_tf_inboard - + 0.1e0 - ) - ) + pfcoil_variables.rcls[group, coil] = rcls[group, coil] + pfcoil_variables.zcls[group, coil] = zcls[group, coil] elif pfcoil_variables.i_pf_location[group] == 2: # PF coil is on top of the TF coil @@ -1023,6 +1018,61 @@ def pfcoil(self): pfcoil_variables.n_pf_cs_plasma_circuits - 1, 5 ] = 0.0e0 + def place_pf_above_cs( + self, + n_pf_coils_in_group: np.ndarray, + n_pf_group: int, + r_cs_middle: float, + rpf1: float, + z_tf_inside_half: float, + dr_tf_inboard: float, + z_cs_coil_upper: float, + ) -> tuple[np.ndarray, np.ndarray]: + """ + Calculate the placement of PF coils stacked above the Central Solenoid. + + :param n_pf_coils_in_group: Array containing the number of coils in each PF group. + :type n_pf_coils_in_group: np.ndarray + :param n_pf_group: Index of the PF coil group. + :type n_pf_group: int + :param r_cs_middle: Radial coordinate of CS coil centre (m). + :type r_cs_middle: float + :param rpf1: Radial offset for PF coil placement (m). + :type rpf1: float + :param z_tf_inside_half: Half-height of the TF bore (m). + :type z_tf_inside_half: float + :param dr_tf_inboard: Thickness of the TF inboard leg (m). + :type dr_tf_inboard: float + :param z_cs_coil_upper: Upper z coordinate of the CS coil (m). + :type z_cs_coil_upper: float + :return: Tuple of arrays containing the radial and vertical coordinates of PF coils in the group. + :rtype: tuple[np.ndarray, np.ndarray] + """ + # Assign empty 2D arrays to be filled + r_pf_coil_middle_group_array = np.zeros(( + n_pf_group, + n_pf_coils_in_group[n_pf_group], + )) + z_pf_coil_middle_group_array = np.zeros(( + n_pf_group, + n_pf_coils_in_group[n_pf_group], + )) + + for coil in range(n_pf_coils_in_group[n_pf_group]): + # Positions PF coil directly above centre of CS with offset from rpf1 + r_pf_coil_middle_group_array[n_pf_group, coil] = r_cs_middle + rpf1 + + # Z coordinate of coil enforced so as not + # to occupy the same space as the Central Solenoid + # Set sign: +1 for coil 0, -1 for coil 1 + sign = 1.0 if coil == 0 else -1.0 + z_pf_coil_middle_group_array[n_pf_group, coil] = sign * ( + z_cs_coil_upper + + 0.1e0 + + 0.5e0 * ((z_tf_inside_half - z_cs_coil_upper) + dr_tf_inboard + 0.1e0) + ) + return r_pf_coil_middle_group_array, z_pf_coil_middle_group_array + def efc( self, npts, From 0bdd91aaf9fe26acc751f4a1a3aa0ba8a3f9955f Mon Sep 17 00:00:00 2001 From: mn3981 Date: Wed, 10 Sep 2025 10:48:38 +0100 Subject: [PATCH 03/20] =?UTF-8?q?=F0=9F=94=84=20-=20Rename=20rcls=20to=20r?= =?UTF-8?q?=5Fpf=5Fcoil=5Fmiddle=5Fgroup=5Farray=20for=20clarity=20and=20u?= =?UTF-8?q?pdate=20references=20throughout=20the=20PFCoil=20module=20and?= =?UTF-8?q?=20integration=20tests?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- process/data_structure/pfcoil_variables.py | 6 +-- process/pfcoil.py | 56 +++++++++++----------- tests/integration/test_pfcoil_int.py | 8 ++-- 3 files changed, 35 insertions(+), 35 deletions(-) diff --git a/process/data_structure/pfcoil_variables.py b/process/data_structure/pfcoil_variables.py index 63692064e9..fa39c5dee6 100644 --- a/process/data_structure/pfcoil_variables.py +++ b/process/data_structure/pfcoil_variables.py @@ -43,7 +43,7 @@ xind: list[float] = None -rcls: list[float] = None +r_pf_coil_middle_group_array: list[float] = None zcls: list[float] = None @@ -571,7 +571,7 @@ def init_pfcoil_module(): global zfxf global cfxf global xind - global rcls + global r_pf_coil_middle_group_array global zcls global ccls global ccl0 @@ -592,7 +592,7 @@ def init_pfcoil_module(): zfxf = np.zeros(NFIXMX) cfxf = np.zeros(NFIXMX) xind = np.zeros(NFIXMX) - rcls = np.zeros((N_PF_GROUPS_MAX, N_PF_COILS_IN_GROUP_MAX)) + r_pf_coil_middle_group_array = np.zeros((N_PF_GROUPS_MAX, N_PF_COILS_IN_GROUP_MAX)) zcls = np.zeros((N_PF_GROUPS_MAX, N_PF_COILS_IN_GROUP_MAX)) ccls = np.zeros(N_PF_GROUPS_MAX) ccl0 = np.zeros(N_PF_GROUPS_MAX) diff --git a/process/pfcoil.py b/process/pfcoil.py index 6bd8617eba..456905dd33 100644 --- a/process/pfcoil.py +++ b/process/pfcoil.py @@ -222,8 +222,8 @@ def pfcoil(self): for group in range(pfcoil_variables.n_pf_coil_groups): if pfcoil_variables.i_pf_location[group] == 1: # PF coil is stacked on top of the Central Solenoid - # Use a helper function to compute rcls and zcls arrays for this group - rcls, zcls = self.place_pf_above_cs( + # Use a helper function to compute r_pf_coil_middle_group_array and zcls arrays for this group + r_pf_coil_middle_group_array, zcls = self.place_pf_above_cs( n_pf_coils_in_group=pfcoil_variables.n_pf_coils_in_group, n_pf_group=group, r_cs_middle=pfcoil_variables.r_cs_middle, @@ -233,13 +233,13 @@ def pfcoil(self): z_cs_coil_upper=pfcoil_variables.dz_cs_full / 2, ) for coil in range(pfcoil_variables.n_pf_coils_in_group[group]): - pfcoil_variables.rcls[group, coil] = rcls[group, coil] + pfcoil_variables.r_pf_coil_middle_group_array[group, coil] = r_pf_coil_middle_group_array[group, coil] pfcoil_variables.zcls[group, coil] = zcls[group, coil] elif pfcoil_variables.i_pf_location[group] == 2: # PF coil is on top of the TF coil for coil in range(pfcoil_variables.n_pf_coils_in_group[group]): - pfcoil_variables.rcls[group, coil] = ( + pfcoil_variables.r_pf_coil_middle_group_array[group, coil] = ( pv.rmajor + pfcoil_variables.rpf2 * pv.triang * pv.rminor ) if pv.itart == 1 and pv.itartpf == 0: @@ -266,19 +266,19 @@ def pfcoil(self): # Coil radius follows TF coil curve for SC TF (D-shape) # otherwise stacked for resistive TF (rectangle-shape) if tfv.i_tf_sup != 1 or pfcoil_variables.i_sup_pf_shape == 1: - pfcoil_variables.rcls[group, coil] = pfcoil_variables.rclsnorm + pfcoil_variables.r_pf_coil_middle_group_array[group, coil] = pfcoil_variables.rclsnorm else: - pfcoil_variables.rcls[group, coil] = math.sqrt( + pfcoil_variables.r_pf_coil_middle_group_array[group, coil] = math.sqrt( pfcoil_variables.rclsnorm**2 - pfcoil_variables.zcls[group, coil] ** 2 ) try: - assert pfcoil_variables.rcls[group, coil] < np.inf + assert pfcoil_variables.r_pf_coil_middle_group_array[group, coil] < np.inf except AssertionError: logger.exception( - "Element of pfcoil_variables.rcls is inf. Kludging to 1e10." + "Element of pfcoil_variables.r_pf_coil_middle_group_array is inf. Kludging to 1e10." ) - pfcoil_variables.rcls[group, coil] = 1e10 + pfcoil_variables.r_pf_coil_middle_group_array[group, coil] = 1e10 elif pfcoil_variables.i_pf_location[group] == 4: # PF coil is in general location @@ -288,7 +288,7 @@ def pfcoil(self): pfcoil_variables.zcls[group, coil] = ( pv.rminor * pfcoil_variables.zref[group] * signn[coil] ) - pfcoil_variables.rcls[group, coil] = ( + pfcoil_variables.r_pf_coil_middle_group_array[group, coil] = ( pv.rminor * pfcoil_variables.rref[group] + pv.rmajor ) @@ -342,7 +342,7 @@ def pfcoil(self): pfcoil_variables.cfxf, pfcoil_variables.n_pf_coil_groups, pfcoil_variables.n_pf_coils_in_group, - pfcoil_variables.rcls, + pfcoil_variables.r_pf_coil_middle_group_array, pfcoil_variables.zcls, pfcoil_variables.alfapf, bfix, @@ -411,7 +411,7 @@ def pfcoil(self): pfcoil_variables.ccls[i] = 0.0e0 nfxf0 = nfxf0 + pfcoil_variables.n_pf_coils_in_group[i] for ccount in range(pfcoil_variables.n_pf_coils_in_group[i]): - pfcoil_variables.rfxf[nocoil] = pfcoil_variables.rcls[ + pfcoil_variables.rfxf[nocoil] = pfcoil_variables.r_pf_coil_middle_group_array[ i, ccount ] pfcoil_variables.zfxf[nocoil] = pfcoil_variables.zcls[ @@ -435,7 +435,7 @@ def pfcoil(self): ) nfxf0 = nfxf0 + pfcoil_variables.n_pf_coils_in_group[i] for ccount in range(pfcoil_variables.n_pf_coils_in_group[i]): - pfcoil_variables.rfxf[nocoil] = pfcoil_variables.rcls[ + pfcoil_variables.rfxf[nocoil] = pfcoil_variables.r_pf_coil_middle_group_array[ i, ccount ] pfcoil_variables.zfxf[nocoil] = pfcoil_variables.zcls[ @@ -469,10 +469,10 @@ def pfcoil(self): for ccount in range(ngrp0): ncls0[ccount] = 2 - pfcoil_variables.rcls0[ccount, 0] = pfcoil_variables.rcls[ + pfcoil_variables.rcls0[ccount, 0] = pfcoil_variables.r_pf_coil_middle_group_array[ pcls0[ccount] - 1, 0 ] - pfcoil_variables.rcls0[ccount, 1] = pfcoil_variables.rcls[ + pfcoil_variables.rcls0[ccount, 1] = pfcoil_variables.r_pf_coil_middle_group_array[ pcls0[ccount] - 1, 1 ] pfcoil_variables.zcls0[ccount, 0] = pfcoil_variables.zcls[ @@ -591,7 +591,7 @@ def pfcoil(self): ncl = 0 for nng in range(pfcoil_variables.n_pf_coil_groups): for ng2 in range(pfcoil_variables.n_pf_coils_in_group[nng]): - pfcoil_variables.r_pf_coil_middle[ncl] = pfcoil_variables.rcls[nng, ng2] + pfcoil_variables.r_pf_coil_middle[ncl] = pfcoil_variables.r_pf_coil_middle_group_array[nng, ng2] pfcoil_variables.z_pf_coil_middle[ncl] = pfcoil_variables.zcls[nng, ng2] # Currents at different times: @@ -1086,7 +1086,7 @@ def efc( cfix, n_pf_coil_groups, n_pf_coils_in_group, - rcls, + r_pf_coil_middle_group_array, zcls, alfa, bfix, @@ -1128,8 +1128,8 @@ def efc( :type n_pf_coil_groups: int :param n_pf_coils_in_group: number of coils in each group, each value <= n_pf_coils_in_group_max :type n_pf_coils_in_group: np.ndarray - :param rcls: coords R(i,j), Z(i,j) of coil j in group i (m) - :type rcls: np.ndarray + :param r_pf_coil_middle_group_array: coords R(i,j), Z(i,j) of coil j in group i (m) + :type r_pf_coil_middle_group_array: np.ndarray :param zcls: coords R(i,j), Z(i,j) of coil j in group i (m) :type zcls: np.ndarray :param alfa: smoothing parameter (0 = no smoothing, 1.0D-9 = large @@ -1176,7 +1176,7 @@ def efc( bzin, int(n_pf_coil_groups), n_pf_coils_in_group, - rcls, + r_pf_coil_middle_group_array, zcls, alfa, bfix, @@ -1206,19 +1206,19 @@ def tf_pf_collision_detector(self): for i in range(pfcoil_variables.n_pf_coil_groups): for ii in range(pfcoil_variables.n_pf_coil_groups): for ij in range(pfcoil_variables.n_pf_coils_in_group[ii]): - if pfcoil_variables.rcls[ + if pfcoil_variables.r_pf_coil_middle_group_array[ ii, ij ] <= ( # Outboard TF coil collision pfcoil_variables.rclsnorm - pfcoil_variables.routr + pfcoil_variables.r_pf_coil_middle[i] - ) and pfcoil_variables.rcls[ii, ij] >= ( + ) and pfcoil_variables.r_pf_coil_middle_group_array[ii, ij] >= ( bv.r_tf_outboard_mid - (0.5 * bv.dr_tf_outboard) - pfcoil_variables.r_pf_coil_middle[i] ): pf_tf_collision += 1 - if pfcoil_variables.rcls[ + if pfcoil_variables.r_pf_coil_middle_group_array[ ii, ij ] <= ( # Inboard TF coil collision bv.dr_bore @@ -1227,7 +1227,7 @@ def tf_pf_collision_detector(self): + bv.dr_cs_tf_gap + bv.dr_tf_inboard + pfcoil_variables.r_pf_coil_middle[i] - ) and pfcoil_variables.rcls[ii, ij] >= ( + ) and pfcoil_variables.r_pf_coil_middle_group_array[ii, ij] >= ( bv.dr_bore + bv.dr_cs + bv.dr_cs_precomp @@ -3997,7 +3997,7 @@ def mtrx( bzin, n_pf_coil_groups, n_pf_coils_in_group, - rcls, + r_pf_coil_middle_group_array, zcls, alfa, bfix, @@ -4033,8 +4033,8 @@ def mtrx( :type n_pf_coil_groups: int :param n_pf_coils_in_group: number of coils in each group, each value <= n_pf_coils_in_group_max :type n_pf_coils_in_group: numpy.ndarray - :param rcls: coords R(i,j), Z(i,j) of coil j in group i (m) - :type rcls: numpy.ndarray + :param r_pf_coil_middle_group_array: coords R(i,j), Z(i,j) of coil j in group i (m) + :type r_pf_coil_middle_group_array: numpy.ndarray :param zcls: coords R(i,j), Z(i,j) of coil j in group i (m) :type zcls: numpy.ndarray :param alfa: smoothing parameter (0 = no smoothing, 1.0D-9 = large @@ -4060,7 +4060,7 @@ def mtrx( nc = n_pf_coils_in_group[j] _, gmat[i, j], gmat[i + npts, j], _ = bfield( - rcls[j, :nc], zcls[j, :nc], cc[:nc], rpts[i], zpts[i] + r_pf_coil_middle_group_array[j, :nc], zcls[j, :nc], cc[:nc], rpts[i], zpts[i] ) # Add constraint equations diff --git a/tests/integration/test_pfcoil_int.py b/tests/integration/test_pfcoil_int.py index ca4cc5b883..e40d0b8f8b 100644 --- a/tests/integration/test_pfcoil_int.py +++ b/tests/integration/test_pfcoil_int.py @@ -378,7 +378,7 @@ def test_efc(pfcoil: PFCoil, monkeypatch: pytest.MonkeyPatch): # This 2D array argument discovered via gdb prints as a 1D array, therefore # needs to be reshaped into its original 2D. Fortran ordering is essential # when passing greater-than-1D arrays from Python to Fortran - rcls = np.reshape( + r_pf_coil_middle_group_array = np.reshape( [ 6.7651653417201345, 6.7651653417201345, @@ -447,7 +447,7 @@ def test_efc(pfcoil: PFCoil, monkeypatch: pytest.MonkeyPatch): cfix, n_pf_coil_groups, n_pf_coils_in_group, - rcls, + r_pf_coil_middle_group_array, zcls, alfa, bfix, @@ -519,7 +519,7 @@ def test_mtrx(pfcoil: PFCoil): bzin = np.zeros(nptsmx) n_pf_coil_groups = 4 n_pf_coils_in_group = np.array([1, 1, 2, 2, 1, 0, 0, 0, 0, 0, 0, 0]) - rcls = np.reshape( + r_pf_coil_middle_group_array = np.reshape( [ 0, 0, @@ -659,7 +659,7 @@ def test_mtrx(pfcoil: PFCoil): bzin, n_pf_coil_groups, n_pf_coils_in_group, - rcls, + r_pf_coil_middle_group_array, zcls, alfa, bfix, From a823b17a737b76a60a91316a5cbf25d25fdc1c84 Mon Sep 17 00:00:00 2001 From: mn3981 Date: Wed, 10 Sep 2025 10:50:15 +0100 Subject: [PATCH 04/20] =?UTF-8?q?=F0=9F=94=84=20-=20Rename=20zcls=20to=20z?= =?UTF-8?q?=5Fpf=5Fcoil=5Fmiddle=5Fgroup=5Farray=20for=20clarity=20and=20u?= =?UTF-8?q?pdate=20references=20in=20PFCoil=20module=20and=20integration?= =?UTF-8?q?=20tests?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- process/data_structure/pfcoil_variables.py | 6 +-- process/pfcoil.py | 54 +++++++++++----------- tests/integration/test_pfcoil_int.py | 8 ++-- 3 files changed, 34 insertions(+), 34 deletions(-) diff --git a/process/data_structure/pfcoil_variables.py b/process/data_structure/pfcoil_variables.py index fa39c5dee6..3a430476ca 100644 --- a/process/data_structure/pfcoil_variables.py +++ b/process/data_structure/pfcoil_variables.py @@ -45,7 +45,7 @@ r_pf_coil_middle_group_array: list[float] = None -zcls: list[float] = None +z_pf_coil_middle_group_array: list[float] = None ccls: list[float] = None @@ -572,7 +572,7 @@ def init_pfcoil_module(): global cfxf global xind global r_pf_coil_middle_group_array - global zcls + global z_pf_coil_middle_group_array global ccls global ccl0 global bpf2 @@ -593,7 +593,7 @@ def init_pfcoil_module(): cfxf = np.zeros(NFIXMX) xind = np.zeros(NFIXMX) r_pf_coil_middle_group_array = np.zeros((N_PF_GROUPS_MAX, N_PF_COILS_IN_GROUP_MAX)) - zcls = np.zeros((N_PF_GROUPS_MAX, N_PF_COILS_IN_GROUP_MAX)) + z_pf_coil_middle_group_array = np.zeros((N_PF_GROUPS_MAX, N_PF_COILS_IN_GROUP_MAX)) ccls = np.zeros(N_PF_GROUPS_MAX) ccl0 = np.zeros(N_PF_GROUPS_MAX) bpf2 = np.zeros(NGC2) diff --git a/process/pfcoil.py b/process/pfcoil.py index 456905dd33..e289c3e5a3 100644 --- a/process/pfcoil.py +++ b/process/pfcoil.py @@ -222,8 +222,8 @@ def pfcoil(self): for group in range(pfcoil_variables.n_pf_coil_groups): if pfcoil_variables.i_pf_location[group] == 1: # PF coil is stacked on top of the Central Solenoid - # Use a helper function to compute r_pf_coil_middle_group_array and zcls arrays for this group - r_pf_coil_middle_group_array, zcls = self.place_pf_above_cs( + # Use a helper function to compute r_pf_coil_middle_group_array and z_pf_coil_middle_group_array arrays for this group + r_pf_coil_middle_group_array, z_pf_coil_middle_group_array = self.place_pf_above_cs( n_pf_coils_in_group=pfcoil_variables.n_pf_coils_in_group, n_pf_group=group, r_cs_middle=pfcoil_variables.r_cs_middle, @@ -234,7 +234,7 @@ def pfcoil(self): ) for coil in range(pfcoil_variables.n_pf_coils_in_group[group]): pfcoil_variables.r_pf_coil_middle_group_array[group, coil] = r_pf_coil_middle_group_array[group, coil] - pfcoil_variables.zcls[group, coil] = zcls[group, coil] + pfcoil_variables.z_pf_coil_middle_group_array[group, coil] = z_pf_coil_middle_group_array[group, coil] elif pfcoil_variables.i_pf_location[group] == 2: # PF coil is on top of the TF coil @@ -243,16 +243,16 @@ def pfcoil(self): pv.rmajor + pfcoil_variables.rpf2 * pv.triang * pv.rminor ) if pv.itart == 1 and pv.itartpf == 0: - pfcoil_variables.zcls[group, coil] = ( + pfcoil_variables.z_pf_coil_middle_group_array[group, coil] = ( bv.z_tf_inside_half - pfcoil_variables.zref[group] ) * signn[coil] else: - # pfcoil_variables.zcls(group,coil) = (bv.z_tf_inside_half + bv.dr_tf_inboard + 0.86e0) * signn(coil) + # pfcoil_variables.z_pf_coil_middle_group_array(group,coil) = (bv.z_tf_inside_half + bv.dr_tf_inboard + 0.86e0) * signn(coil) if top_bottom == 1: # this coil is above midplane - pfcoil_variables.zcls[group, coil] = bv.z_tf_top + 0.86e0 + pfcoil_variables.z_pf_coil_middle_group_array[group, coil] = bv.z_tf_top + 0.86e0 top_bottom = -1 else: # this coil is below midplane - pfcoil_variables.zcls[group, coil] = -1.0e0 * ( + pfcoil_variables.z_pf_coil_middle_group_array[group, coil] = -1.0e0 * ( bv.z_tf_top - 2.0e0 * bv.hpfdif + 0.86e0 ) top_bottom = 1 @@ -260,7 +260,7 @@ def pfcoil(self): elif pfcoil_variables.i_pf_location[group] == 3: # PF coil is radially outside the TF coil for coil in range(pfcoil_variables.n_pf_coils_in_group[group]): - pfcoil_variables.zcls[group, coil] = ( + pfcoil_variables.z_pf_coil_middle_group_array[group, coil] = ( pv.rminor * pfcoil_variables.zref[group] * signn[coil] ) # Coil radius follows TF coil curve for SC TF (D-shape) @@ -270,7 +270,7 @@ def pfcoil(self): else: pfcoil_variables.r_pf_coil_middle_group_array[group, coil] = math.sqrt( pfcoil_variables.rclsnorm**2 - - pfcoil_variables.zcls[group, coil] ** 2 + - pfcoil_variables.z_pf_coil_middle_group_array[group, coil] ** 2 ) try: assert pfcoil_variables.r_pf_coil_middle_group_array[group, coil] < np.inf @@ -285,7 +285,7 @@ def pfcoil(self): # See issue 1418 # https://git.ccfe.ac.uk/process/process/-/issues/1418 for coil in range(pfcoil_variables.n_pf_coils_in_group[group]): - pfcoil_variables.zcls[group, coil] = ( + pfcoil_variables.z_pf_coil_middle_group_array[group, coil] = ( pv.rminor * pfcoil_variables.zref[group] * signn[coil] ) pfcoil_variables.r_pf_coil_middle_group_array[group, coil] = ( @@ -343,7 +343,7 @@ def pfcoil(self): pfcoil_variables.n_pf_coil_groups, pfcoil_variables.n_pf_coils_in_group, pfcoil_variables.r_pf_coil_middle_group_array, - pfcoil_variables.zcls, + pfcoil_variables.z_pf_coil_middle_group_array, pfcoil_variables.alfapf, bfix, gmat, @@ -414,7 +414,7 @@ def pfcoil(self): pfcoil_variables.rfxf[nocoil] = pfcoil_variables.r_pf_coil_middle_group_array[ i, ccount ] - pfcoil_variables.zfxf[nocoil] = pfcoil_variables.zcls[ + pfcoil_variables.zfxf[nocoil] = pfcoil_variables.z_pf_coil_middle_group_array[ i, ccount ] pfcoil_variables.cfxf[nocoil] = pfcoil_variables.ccls[i] @@ -430,7 +430,7 @@ def pfcoil(self): * ( 1.0e0 - (pv.kappa * pv.rminor) - / abs(pfcoil_variables.zcls[i, 0]) + / abs(pfcoil_variables.z_pf_coil_middle_group_array[i, 0]) ) ) nfxf0 = nfxf0 + pfcoil_variables.n_pf_coils_in_group[i] @@ -438,7 +438,7 @@ def pfcoil(self): pfcoil_variables.rfxf[nocoil] = pfcoil_variables.r_pf_coil_middle_group_array[ i, ccount ] - pfcoil_variables.zfxf[nocoil] = pfcoil_variables.zcls[ + pfcoil_variables.zfxf[nocoil] = pfcoil_variables.z_pf_coil_middle_group_array[ i, ccount ] pfcoil_variables.cfxf[nocoil] = pfcoil_variables.ccls[i] @@ -475,10 +475,10 @@ def pfcoil(self): pfcoil_variables.rcls0[ccount, 1] = pfcoil_variables.r_pf_coil_middle_group_array[ pcls0[ccount] - 1, 1 ] - pfcoil_variables.zcls0[ccount, 0] = pfcoil_variables.zcls[ + pfcoil_variables.zcls0[ccount, 0] = pfcoil_variables.z_pf_coil_middle_group_array[ pcls0[ccount] - 1, 0 ] - pfcoil_variables.zcls0[ccount, 1] = pfcoil_variables.zcls[ + pfcoil_variables.zcls0[ccount, 1] = pfcoil_variables.z_pf_coil_middle_group_array[ pcls0[ccount] - 1, 1 ] @@ -592,7 +592,7 @@ def pfcoil(self): for nng in range(pfcoil_variables.n_pf_coil_groups): for ng2 in range(pfcoil_variables.n_pf_coils_in_group[nng]): pfcoil_variables.r_pf_coil_middle[ncl] = pfcoil_variables.r_pf_coil_middle_group_array[nng, ng2] - pfcoil_variables.z_pf_coil_middle[ncl] = pfcoil_variables.zcls[nng, ng2] + pfcoil_variables.z_pf_coil_middle[ncl] = pfcoil_variables.z_pf_coil_middle_group_array[nng, ng2] # Currents at different times: @@ -1087,7 +1087,7 @@ def efc( n_pf_coil_groups, n_pf_coils_in_group, r_pf_coil_middle_group_array, - zcls, + z_pf_coil_middle_group_array, alfa, bfix, gmat, @@ -1130,8 +1130,8 @@ def efc( :type n_pf_coils_in_group: np.ndarray :param r_pf_coil_middle_group_array: coords R(i,j), Z(i,j) of coil j in group i (m) :type r_pf_coil_middle_group_array: np.ndarray - :param zcls: coords R(i,j), Z(i,j) of coil j in group i (m) - :type zcls: np.ndarray + :param z_pf_coil_middle_group_array: coords R(i,j), Z(i,j) of coil j in group i (m) + :type z_pf_coil_middle_group_array: np.ndarray :param alfa: smoothing parameter (0 = no smoothing, 1.0D-9 = large smoothing) :type alfa: float @@ -1177,7 +1177,7 @@ def efc( int(n_pf_coil_groups), n_pf_coils_in_group, r_pf_coil_middle_group_array, - zcls, + z_pf_coil_middle_group_array, alfa, bfix, int(pfcoil_variables.N_PF_COILS_IN_GROUP_MAX), @@ -1236,9 +1236,9 @@ def tf_pf_collision_detector(self): ): pf_tf_collision += 1 if ( # Vertical TF coil collision - abs(pfcoil_variables.zcls[ii, ij]) + abs(pfcoil_variables.z_pf_coil_middle_group_array[ii, ij]) <= bv.z_tf_top + pfcoil_variables.r_pf_coil_middle[i] - and abs(pfcoil_variables.zcls[ii, ij]) + and abs(pfcoil_variables.z_pf_coil_middle_group_array[ii, ij]) >= bv.z_tf_top - (0.5 * bv.dr_tf_outboard) - pfcoil_variables.r_pf_coil_middle[i] @@ -3998,7 +3998,7 @@ def mtrx( n_pf_coil_groups, n_pf_coils_in_group, r_pf_coil_middle_group_array, - zcls, + z_pf_coil_middle_group_array, alfa, bfix, n_pf_coils_in_group_max, @@ -4035,8 +4035,8 @@ def mtrx( :type n_pf_coils_in_group: numpy.ndarray :param r_pf_coil_middle_group_array: coords R(i,j), Z(i,j) of coil j in group i (m) :type r_pf_coil_middle_group_array: numpy.ndarray - :param zcls: coords R(i,j), Z(i,j) of coil j in group i (m) - :type zcls: numpy.ndarray + :param z_pf_coil_middle_group_array: coords R(i,j), Z(i,j) of coil j in group i (m) + :type z_pf_coil_middle_group_array: numpy.ndarray :param alfa: smoothing parameter (0 = no smoothing, 1.0D-9 = large smoothing) :type alfa: float @@ -4060,7 +4060,7 @@ def mtrx( nc = n_pf_coils_in_group[j] _, gmat[i, j], gmat[i + npts, j], _ = bfield( - r_pf_coil_middle_group_array[j, :nc], zcls[j, :nc], cc[:nc], rpts[i], zpts[i] + r_pf_coil_middle_group_array[j, :nc], z_pf_coil_middle_group_array[j, :nc], cc[:nc], rpts[i], zpts[i] ) # Add constraint equations diff --git a/tests/integration/test_pfcoil_int.py b/tests/integration/test_pfcoil_int.py index e40d0b8f8b..73e78f3239 100644 --- a/tests/integration/test_pfcoil_int.py +++ b/tests/integration/test_pfcoil_int.py @@ -404,7 +404,7 @@ def test_efc(pfcoil: PFCoil, monkeypatch: pytest.MonkeyPatch): (10, 2), order="F", ) - zcls = np.reshape( + z_pf_coil_middle_group_array = np.reshape( [ 9.8904697261474404, -11.124884737289973, @@ -448,7 +448,7 @@ def test_efc(pfcoil: PFCoil, monkeypatch: pytest.MonkeyPatch): n_pf_coil_groups, n_pf_coils_in_group, r_pf_coil_middle_group_array, - zcls, + z_pf_coil_middle_group_array, alfa, bfix, gmat, @@ -545,7 +545,7 @@ def test_mtrx(pfcoil: PFCoil): (10, 2), order="F", ) - zcls = np.reshape( + z_pf_coil_middle_group_array = np.reshape( [ 0, 0, @@ -660,7 +660,7 @@ def test_mtrx(pfcoil: PFCoil): n_pf_coil_groups, n_pf_coils_in_group, r_pf_coil_middle_group_array, - zcls, + z_pf_coil_middle_group_array, alfa, bfix, int(pfcoil_variables.N_PF_COILS_IN_GROUP_MAX), From 315589139456a6eb10659bc418f34c2af46264d3 Mon Sep 17 00:00:00 2001 From: mn3981 Date: Wed, 10 Sep 2025 10:52:05 +0100 Subject: [PATCH 05/20] =?UTF-8?q?=F0=9F=94=84=20-=20Rename=20rpf1=20to=20d?= =?UTF-8?q?r=5Fpf=5Fcs=5Fmiddle=5Foffset=20for=20clarity=20and=20update=20?= =?UTF-8?q?references=20in=20PFCoil=20module=20and=20integration=20tests?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- documentation/eng-models/pf-coil.md | 4 +- process/data_structure/pfcoil_variables.py | 6 +- process/input.py | 4 +- process/pfcoil.py | 152 +++++++++++------- tests/integration/test_pfcoil_int.py | 2 +- .../input_files/st_regression.IN.DAT | 2 +- 6 files changed, 108 insertions(+), 62 deletions(-) diff --git a/documentation/eng-models/pf-coil.md b/documentation/eng-models/pf-coil.md index 493a823256..fd2b06b36e 100644 --- a/documentation/eng-models/pf-coil.md +++ b/documentation/eng-models/pf-coil.md @@ -21,14 +21,14 @@ and `n_pf_coils_in_group(j)` should be assigned the number of coils in each grou In the following, all variables are defined in the variable descriptor file `vardes.html`. The -values for `rpf1`, `rpf2`, `zref(j)` and `routr` should be adjusted by the user to locate the PF +values for `dr_pf_cs_middle_offset`, `rpf2`, `zref(j)` and `routr` should be adjusted by the user to locate the PF coils accurately. The three possible values of `i_pf_location(j)` correspond to the following PF coil positions: (Redo taking into account `i_single_null` and other recent changes e.g. rclsnorm) `i_pf_location(j)` = 1: PF coils are placed above the central solenoid (one group only); -*R* = `r_cs_middle` + `rpf1`
+*R* = `r_cs_middle` + `dr_pf_cs_middle_offset`
*Z* = $\pm$(`z_tf_inside_half` * `f_z_cs_tf_internal` + 0.1 + 0.5 * (`z_tf_inside_half` * (1 - `f_z_cs_tf_internal`) + `dr_tf_inboard` + 0.1)) `i_pf_location(j)` = 2: PF coils are placed above the TF coils (one group only);
diff --git a/process/data_structure/pfcoil_variables.py b/process/data_structure/pfcoil_variables.py index 3a430476ca..004165f83d 100644 --- a/process/data_structure/pfcoil_variables.py +++ b/process/data_structure/pfcoil_variables.py @@ -387,7 +387,7 @@ """radius of PF coil i (m)""" -rpf1: float = None +dr_pf_cs_middle_offset: float = None """offset (m) of radial position of `i_pf_location=1` PF coils from being directly above the central solenoid """ @@ -666,7 +666,7 @@ def init_pfcoil_variables(): global dz_cs_full global routr global r_pf_coil_middle - global rpf1 + global dr_pf_cs_middle_offset global rpf2 global rref global s_shear_cs_peak @@ -768,7 +768,7 @@ def init_pfcoil_variables(): dz_cs_full = 0.0 routr = 1.5 r_pf_coil_middle = np.zeros(NGC2) - rpf1 = 0.0 + dr_pf_cs_middle_offset = 0.0 rpf2 = -1.63 rref = np.full(N_PF_GROUPS_MAX, 7.0) s_shear_cs_peak = 0.0 diff --git a/process/input.py b/process/input.py index e38ec51f85..d2eaa8f972 100644 --- a/process/input.py +++ b/process/input.py @@ -1492,7 +1492,9 @@ def __post_init__(self): ), "routr": InputVariable(data_structure.pfcoil_variables, float, range=(-3.0, 3.0)), "row": InputVariable(data_structure.buildings_variables, float, range=(0.0, 10.0)), - "rpf1": InputVariable(data_structure.pfcoil_variables, float, range=(0.0, 3.0)), + "dr_pf_cs_middle_offset": InputVariable( + data_structure.pfcoil_variables, float, range=(0.0, 3.0) + ), "rpf2": InputVariable(data_structure.pfcoil_variables, float, range=(-3.0, 3.0)), "rrin": InputVariable(data_structure.ife_variables, float, range=(0.1, 50.0)), "rrmax": InputVariable(data_structure.ife_variables, float, range=(1.0, 50.0)), diff --git a/process/pfcoil.py b/process/pfcoil.py index e289c3e5a3..af0bc676ab 100644 --- a/process/pfcoil.py +++ b/process/pfcoil.py @@ -223,18 +223,24 @@ def pfcoil(self): if pfcoil_variables.i_pf_location[group] == 1: # PF coil is stacked on top of the Central Solenoid # Use a helper function to compute r_pf_coil_middle_group_array and z_pf_coil_middle_group_array arrays for this group - r_pf_coil_middle_group_array, z_pf_coil_middle_group_array = self.place_pf_above_cs( - n_pf_coils_in_group=pfcoil_variables.n_pf_coils_in_group, - n_pf_group=group, - r_cs_middle=pfcoil_variables.r_cs_middle, - rpf1=pfcoil_variables.rpf1, - z_tf_inside_half=bv.z_tf_inside_half, - dr_tf_inboard=bv.dr_tf_inboard, - z_cs_coil_upper=pfcoil_variables.dz_cs_full / 2, + r_pf_coil_middle_group_array, z_pf_coil_middle_group_array = ( + self.place_pf_above_cs( + n_pf_coils_in_group=pfcoil_variables.n_pf_coils_in_group, + n_pf_group=group, + r_cs_middle=pfcoil_variables.r_cs_middle, + dr_pf_cs_middle_offset=pfcoil_variables.dr_pf_cs_middle_offset, + z_tf_inside_half=bv.z_tf_inside_half, + dr_tf_inboard=bv.dr_tf_inboard, + z_cs_coil_upper=pfcoil_variables.dz_cs_full / 2, + ) ) for coil in range(pfcoil_variables.n_pf_coils_in_group[group]): - pfcoil_variables.r_pf_coil_middle_group_array[group, coil] = r_pf_coil_middle_group_array[group, coil] - pfcoil_variables.z_pf_coil_middle_group_array[group, coil] = z_pf_coil_middle_group_array[group, coil] + pfcoil_variables.r_pf_coil_middle_group_array[group, coil] = ( + r_pf_coil_middle_group_array[group, coil] + ) + pfcoil_variables.z_pf_coil_middle_group_array[group, coil] = ( + z_pf_coil_middle_group_array[group, coil] + ) elif pfcoil_variables.i_pf_location[group] == 2: # PF coil is on top of the TF coil @@ -249,12 +255,14 @@ def pfcoil(self): else: # pfcoil_variables.z_pf_coil_middle_group_array(group,coil) = (bv.z_tf_inside_half + bv.dr_tf_inboard + 0.86e0) * signn(coil) if top_bottom == 1: # this coil is above midplane - pfcoil_variables.z_pf_coil_middle_group_array[group, coil] = bv.z_tf_top + 0.86e0 + pfcoil_variables.z_pf_coil_middle_group_array[ + group, coil + ] = bv.z_tf_top + 0.86e0 top_bottom = -1 else: # this coil is below midplane - pfcoil_variables.z_pf_coil_middle_group_array[group, coil] = -1.0e0 * ( - bv.z_tf_top - 2.0e0 * bv.hpfdif + 0.86e0 - ) + pfcoil_variables.z_pf_coil_middle_group_array[ + group, coil + ] = -1.0e0 * (bv.z_tf_top - 2.0e0 * bv.hpfdif + 0.86e0) top_bottom = 1 elif pfcoil_variables.i_pf_location[group] == 3: @@ -266,19 +274,33 @@ def pfcoil(self): # Coil radius follows TF coil curve for SC TF (D-shape) # otherwise stacked for resistive TF (rectangle-shape) if tfv.i_tf_sup != 1 or pfcoil_variables.i_sup_pf_shape == 1: - pfcoil_variables.r_pf_coil_middle_group_array[group, coil] = pfcoil_variables.rclsnorm + pfcoil_variables.r_pf_coil_middle_group_array[group, coil] = ( + pfcoil_variables.rclsnorm + ) else: - pfcoil_variables.r_pf_coil_middle_group_array[group, coil] = math.sqrt( - pfcoil_variables.rclsnorm**2 - - pfcoil_variables.z_pf_coil_middle_group_array[group, coil] ** 2 + pfcoil_variables.r_pf_coil_middle_group_array[group, coil] = ( + math.sqrt( + pfcoil_variables.rclsnorm**2 + - pfcoil_variables.z_pf_coil_middle_group_array[ + group, coil + ] + ** 2 + ) ) try: - assert pfcoil_variables.r_pf_coil_middle_group_array[group, coil] < np.inf + assert ( + pfcoil_variables.r_pf_coil_middle_group_array[ + group, coil + ] + < np.inf + ) except AssertionError: logger.exception( "Element of pfcoil_variables.r_pf_coil_middle_group_array is inf. Kludging to 1e10." ) - pfcoil_variables.r_pf_coil_middle_group_array[group, coil] = 1e10 + pfcoil_variables.r_pf_coil_middle_group_array[ + group, coil + ] = 1e10 elif pfcoil_variables.i_pf_location[group] == 4: # PF coil is in general location @@ -411,12 +433,12 @@ def pfcoil(self): pfcoil_variables.ccls[i] = 0.0e0 nfxf0 = nfxf0 + pfcoil_variables.n_pf_coils_in_group[i] for ccount in range(pfcoil_variables.n_pf_coils_in_group[i]): - pfcoil_variables.rfxf[nocoil] = pfcoil_variables.r_pf_coil_middle_group_array[ - i, ccount - ] - pfcoil_variables.zfxf[nocoil] = pfcoil_variables.z_pf_coil_middle_group_array[ - i, ccount - ] + pfcoil_variables.rfxf[nocoil] = ( + pfcoil_variables.r_pf_coil_middle_group_array[i, ccount] + ) + pfcoil_variables.zfxf[nocoil] = ( + pfcoil_variables.z_pf_coil_middle_group_array[i, ccount] + ) pfcoil_variables.cfxf[nocoil] = pfcoil_variables.ccls[i] nocoil = nocoil + 1 @@ -430,17 +452,19 @@ def pfcoil(self): * ( 1.0e0 - (pv.kappa * pv.rminor) - / abs(pfcoil_variables.z_pf_coil_middle_group_array[i, 0]) + / abs( + pfcoil_variables.z_pf_coil_middle_group_array[i, 0] + ) ) ) nfxf0 = nfxf0 + pfcoil_variables.n_pf_coils_in_group[i] for ccount in range(pfcoil_variables.n_pf_coils_in_group[i]): - pfcoil_variables.rfxf[nocoil] = pfcoil_variables.r_pf_coil_middle_group_array[ - i, ccount - ] - pfcoil_variables.zfxf[nocoil] = pfcoil_variables.z_pf_coil_middle_group_array[ - i, ccount - ] + pfcoil_variables.rfxf[nocoil] = ( + pfcoil_variables.r_pf_coil_middle_group_array[i, ccount] + ) + pfcoil_variables.zfxf[nocoil] = ( + pfcoil_variables.z_pf_coil_middle_group_array[i, ccount] + ) pfcoil_variables.cfxf[nocoil] = pfcoil_variables.ccls[i] nocoil = nocoil + 1 @@ -469,18 +493,26 @@ def pfcoil(self): for ccount in range(ngrp0): ncls0[ccount] = 2 - pfcoil_variables.rcls0[ccount, 0] = pfcoil_variables.r_pf_coil_middle_group_array[ - pcls0[ccount] - 1, 0 - ] - pfcoil_variables.rcls0[ccount, 1] = pfcoil_variables.r_pf_coil_middle_group_array[ - pcls0[ccount] - 1, 1 - ] - pfcoil_variables.zcls0[ccount, 0] = pfcoil_variables.z_pf_coil_middle_group_array[ - pcls0[ccount] - 1, 0 - ] - pfcoil_variables.zcls0[ccount, 1] = pfcoil_variables.z_pf_coil_middle_group_array[ - pcls0[ccount] - 1, 1 - ] + pfcoil_variables.rcls0[ccount, 0] = ( + pfcoil_variables.r_pf_coil_middle_group_array[ + pcls0[ccount] - 1, 0 + ] + ) + pfcoil_variables.rcls0[ccount, 1] = ( + pfcoil_variables.r_pf_coil_middle_group_array[ + pcls0[ccount] - 1, 1 + ] + ) + pfcoil_variables.zcls0[ccount, 0] = ( + pfcoil_variables.z_pf_coil_middle_group_array[ + pcls0[ccount] - 1, 0 + ] + ) + pfcoil_variables.zcls0[ccount, 1] = ( + pfcoil_variables.z_pf_coil_middle_group_array[ + pcls0[ccount] - 1, 1 + ] + ) npts0 = 1 rpts[0] = pv.rmajor @@ -591,8 +623,12 @@ def pfcoil(self): ncl = 0 for nng in range(pfcoil_variables.n_pf_coil_groups): for ng2 in range(pfcoil_variables.n_pf_coils_in_group[nng]): - pfcoil_variables.r_pf_coil_middle[ncl] = pfcoil_variables.r_pf_coil_middle_group_array[nng, ng2] - pfcoil_variables.z_pf_coil_middle[ncl] = pfcoil_variables.z_pf_coil_middle_group_array[nng, ng2] + pfcoil_variables.r_pf_coil_middle[ncl] = ( + pfcoil_variables.r_pf_coil_middle_group_array[nng, ng2] + ) + pfcoil_variables.z_pf_coil_middle[ncl] = ( + pfcoil_variables.z_pf_coil_middle_group_array[nng, ng2] + ) # Currents at different times: @@ -1023,7 +1059,7 @@ def place_pf_above_cs( n_pf_coils_in_group: np.ndarray, n_pf_group: int, r_cs_middle: float, - rpf1: float, + dr_pf_cs_middle_offset: float, z_tf_inside_half: float, dr_tf_inboard: float, z_cs_coil_upper: float, @@ -1037,8 +1073,8 @@ def place_pf_above_cs( :type n_pf_group: int :param r_cs_middle: Radial coordinate of CS coil centre (m). :type r_cs_middle: float - :param rpf1: Radial offset for PF coil placement (m). - :type rpf1: float + :param dr_pf_cs_middle_offset: Radial offset for PF coil placement (m). + :type dr_pf_cs_middle_offset: float :param z_tf_inside_half: Half-height of the TF bore (m). :type z_tf_inside_half: float :param dr_tf_inboard: Thickness of the TF inboard leg (m). @@ -1059,8 +1095,10 @@ def place_pf_above_cs( )) for coil in range(n_pf_coils_in_group[n_pf_group]): - # Positions PF coil directly above centre of CS with offset from rpf1 - r_pf_coil_middle_group_array[n_pf_group, coil] = r_cs_middle + rpf1 + # Positions PF coil directly above centre of CS with offset from dr_pf_cs_middle_offset + r_pf_coil_middle_group_array[n_pf_group, coil] = ( + r_cs_middle + dr_pf_cs_middle_offset + ) # Z coordinate of coil enforced so as not # to occupy the same space as the Central Solenoid @@ -1238,7 +1276,9 @@ def tf_pf_collision_detector(self): if ( # Vertical TF coil collision abs(pfcoil_variables.z_pf_coil_middle_group_array[ii, ij]) <= bv.z_tf_top + pfcoil_variables.r_pf_coil_middle[i] - and abs(pfcoil_variables.z_pf_coil_middle_group_array[ii, ij]) + and abs( + pfcoil_variables.z_pf_coil_middle_group_array[ii, ij] + ) >= bv.z_tf_top - (0.5 * bv.dr_tf_outboard) - pfcoil_variables.r_pf_coil_middle[i] @@ -4060,7 +4100,11 @@ def mtrx( nc = n_pf_coils_in_group[j] _, gmat[i, j], gmat[i + npts, j], _ = bfield( - r_pf_coil_middle_group_array[j, :nc], z_pf_coil_middle_group_array[j, :nc], cc[:nc], rpts[i], zpts[i] + r_pf_coil_middle_group_array[j, :nc], + z_pf_coil_middle_group_array[j, :nc], + cc[:nc], + rpts[i], + zpts[i], ) # Add constraint equations diff --git a/tests/integration/test_pfcoil_int.py b/tests/integration/test_pfcoil_int.py index 73e78f3239..ae4417b84e 100644 --- a/tests/integration/test_pfcoil_int.py +++ b/tests/integration/test_pfcoil_int.py @@ -55,7 +55,7 @@ def test_pfcoil(monkeypatch, pfcoil): monkeypatch.setattr(bv, "r_tf_outboard_mid", 1.66e1) monkeypatch.setattr(bv, "dr_bore", 2.15) monkeypatch.setattr(fwbsv, "den_steel", 7.8e3) - monkeypatch.setattr(pfcoil_variables, "rpf1", 0.0) + monkeypatch.setattr(pfcoil_variables, "dr_pf_cs_middle_offset", 0.0) monkeypatch.setattr(pfcoil_variables, "m_pf_coil_structure_total", 0.0) monkeypatch.setattr(pfcoil_variables, "c_pf_cs_coil_flat_top_ma", np.full(22, 0.0)) monkeypatch.setattr(pfcoil_variables, "n_cs_pf_coils", 0) diff --git a/tests/regression/input_files/st_regression.IN.DAT b/tests/regression/input_files/st_regression.IN.DAT index a3f7183e4c..a9f4372f47 100644 --- a/tests/regression/input_files/st_regression.IN.DAT +++ b/tests/regression/input_files/st_regression.IN.DAT @@ -1879,7 +1879,7 @@ i_sup_pf_shape = 1 * (default = 1.5) * JUSTIFICATION: Not used, no i_pf_location = 3 coils -*rpf1 = +*dr_pf_cs_middle_offset = * DESCRIPTION: offset (m) of radial position of i_pf_location=1 PF coils from being directly above * the central solenoid * JUSTIFICATION: Not used, no i_pf_location = 1 coils From 040b646cb7dc99367044cbc3b03ecc543e14ad34 Mon Sep 17 00:00:00 2001 From: mn3981 Date: Wed, 10 Sep 2025 11:22:19 +0100 Subject: [PATCH 06/20] =?UTF-8?q?=F0=9F=94=84=20-=20Rename=20'routr'=20to?= =?UTF-8?q?=20'dr=5Fpf=5Ftf=5Foutboard=5Fout=5Foffset'=20for=20clarity=20a?= =?UTF-8?q?nd=20update=20references=20in=20PF=20coil=20logic=20and=20tests?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- documentation/eng-models/pf-coil.md | 4 ++-- process/data_structure/pfcoil_variables.py | 6 +++--- process/input.py | 4 +++- process/pfcoil.py | 11 +++++++---- tests/integration/test_pfcoil_int.py | 2 +- tests/regression/input_files/st_regression.IN.DAT | 2 +- 6 files changed, 17 insertions(+), 12 deletions(-) diff --git a/documentation/eng-models/pf-coil.md b/documentation/eng-models/pf-coil.md index fd2b06b36e..9d7a76fb1f 100644 --- a/documentation/eng-models/pf-coil.md +++ b/documentation/eng-models/pf-coil.md @@ -21,7 +21,7 @@ and `n_pf_coils_in_group(j)` should be assigned the number of coils in each grou In the following, all variables are defined in the variable descriptor file `vardes.html`. The -values for `dr_pf_cs_middle_offset`, `rpf2`, `zref(j)` and `routr` should be adjusted by the user to locate the PF +values for `dr_pf_cs_middle_offset`, `rpf2`, `zref(j)` and `dr_pf_tf_outboard_out_offset` should be adjusted by the user to locate the PF coils accurately. The three possible values of `i_pf_location(j)` correspond to the following PF coil positions: (Redo taking @@ -36,7 +36,7 @@ into account `i_single_null` and other recent changes e.g. rclsnorm) *Z* = $\pm$(`z_tf_inside_half` * `dr_tf_inboard` + 0.86) `i_pf_location(j)` = 3: PF coils are placed radially outside the TF coils (any number of groups);
-*R* = `rtot` + `dr_tf_outboard`/2 + `routr`
+*R* = `rtot` + `dr_tf_outboard`/2 + `dr_pf_tf_outboard_out_offset`
*Z* = $\pm$(`rminor` * `zref(j)` The void fraction (for coolant) in each coil `i`'s winding pack is given by `f_a_pf_coil_void(i)`. diff --git a/process/data_structure/pfcoil_variables.py b/process/data_structure/pfcoil_variables.py index 004165f83d..2a1cfeea81 100644 --- a/process/data_structure/pfcoil_variables.py +++ b/process/data_structure/pfcoil_variables.py @@ -379,7 +379,7 @@ """Full height of the central solenoid (m)""" -routr: float = None +dr_pf_tf_outboard_out_offset: float = None """radial distance (m) from outboard TF coil leg to centre of `i_pf_location=3` PF coils""" @@ -664,7 +664,7 @@ def init_pfcoil_variables(): global j_pf_wp_critical global r_cs_middle global dz_cs_full - global routr + global dr_pf_tf_outboard_out_offset global r_pf_coil_middle global dr_pf_cs_middle_offset global rpf2 @@ -766,7 +766,7 @@ def init_pfcoil_variables(): j_pf_wp_critical = np.zeros(NGC2) r_cs_middle = 0.0 dz_cs_full = 0.0 - routr = 1.5 + dr_pf_tf_outboard_out_offset = 1.5 r_pf_coil_middle = np.zeros(NGC2) dr_pf_cs_middle_offset = 0.0 rpf2 = -1.63 diff --git a/process/input.py b/process/input.py index d2eaa8f972..dde6f5e820 100644 --- a/process/input.py +++ b/process/input.py @@ -1490,7 +1490,9 @@ def __post_init__(self): "roughness_fw_channel": InputVariable( data_structure.fwbs_variables, float, range=(0.0, 0.01) ), - "routr": InputVariable(data_structure.pfcoil_variables, float, range=(-3.0, 3.0)), + "dr_pf_tf_outboard_out_offset": InputVariable( + data_structure.pfcoil_variables, float, range=(-3.0, 3.0) + ), "row": InputVariable(data_structure.buildings_variables, float, range=(0.0, 10.0)), "dr_pf_cs_middle_offset": InputVariable( data_structure.pfcoil_variables, float, range=(0.0, 3.0) diff --git a/process/pfcoil.py b/process/pfcoil.py index af0bc676ab..935d304fb8 100644 --- a/process/pfcoil.py +++ b/process/pfcoil.py @@ -14,7 +14,7 @@ from process.data_structure import constraint_variables as ctv from process.data_structure import cs_fatigue_variables as csfv from process.data_structure import fwbs_variables as fwbsv -from process.data_structure import numerics, pfcoil_variables +from process.data_structure import numerics, pfcoil_variables, superconducting_tf_coil_variables from process.data_structure import physics_variables as pv from process.data_structure import rebco_variables as rcv from process.data_structure import tfcoil_variables as tfv @@ -213,7 +213,8 @@ def pfcoil(self): signn[0] = 1.0e0 signn[1] = -1.0e0 pfcoil_variables.rclsnorm = ( - bv.r_tf_outboard_mid + 0.5e0 * bv.dr_tf_outboard + pfcoil_variables.routr + superconducting_tf_coil_variables.r_tf_outboard_out + + pfcoil_variables.dr_pf_tf_outboard_out_offset ) # Place the PF coils: @@ -222,7 +223,9 @@ def pfcoil(self): for group in range(pfcoil_variables.n_pf_coil_groups): if pfcoil_variables.i_pf_location[group] == 1: # PF coil is stacked on top of the Central Solenoid - # Use a helper function to compute r_pf_coil_middle_group_array and z_pf_coil_middle_group_array arrays for this group + # Use a helper function to compute r_pf_coil_middle_group_array and + # z_pf_coil_middle_group_array arrays for this group + r_pf_coil_middle_group_array, z_pf_coil_middle_group_array = ( self.place_pf_above_cs( n_pf_coils_in_group=pfcoil_variables.n_pf_coils_in_group, @@ -1248,7 +1251,7 @@ def tf_pf_collision_detector(self): ii, ij ] <= ( # Outboard TF coil collision pfcoil_variables.rclsnorm - - pfcoil_variables.routr + - pfcoil_variables.dr_pf_tf_outboard_out_offset + pfcoil_variables.r_pf_coil_middle[i] ) and pfcoil_variables.r_pf_coil_middle_group_array[ii, ij] >= ( bv.r_tf_outboard_mid diff --git a/tests/integration/test_pfcoil_int.py b/tests/integration/test_pfcoil_int.py index ae4417b84e..cf5ab03883 100644 --- a/tests/integration/test_pfcoil_int.py +++ b/tests/integration/test_pfcoil_int.py @@ -85,7 +85,7 @@ def test_pfcoil(monkeypatch, pfcoil): monkeypatch.setattr( pfcoil_variables, "c_pf_cs_coil_pulse_start_ma", np.full(22, 0.0) ) - monkeypatch.setattr(pfcoil_variables, "routr", 1.5) + monkeypatch.setattr(pfcoil_variables, "dr_pf_tf_outboard_out_offset", 1.5) monkeypatch.setattr(pfcoil_variables, "c_pf_cs_coils_peak_ma", np.full(22, 0.0)) monkeypatch.setattr(pfcoil_variables, "f_j_cs_start_end_flat_top", 2.654e-1) monkeypatch.setattr(pfcoil_variables, "rpf2", -1.825) diff --git a/tests/regression/input_files/st_regression.IN.DAT b/tests/regression/input_files/st_regression.IN.DAT index a9f4372f47..061c5b3413 100644 --- a/tests/regression/input_files/st_regression.IN.DAT +++ b/tests/regression/input_files/st_regression.IN.DAT @@ -1874,7 +1874,7 @@ i_sup_pf_shape = 1 * winding surface * JUSTIFICATION: Not used, no i_pf_location = 3 coils -*routr = +*dr_pf_tf_outboard_out_offset = * DESCRIPTION: Radial distance (m) from outboard TF coil leg to centre of i_pf_location=3 PF coils * (default = 1.5) * JUSTIFICATION: Not used, no i_pf_location = 3 coils From ff81bce665ff4c815701a9c05567f11e406a4a09 Mon Sep 17 00:00:00 2001 From: mn3981 Date: Wed, 10 Sep 2025 11:24:54 +0100 Subject: [PATCH 07/20] =?UTF-8?q?=F0=9F=94=84=20-=20Rename=20'rclsnorm'=20?= =?UTF-8?q?to=20'r=5Fpf=5Foutside=5Ftf=5Fmidplane'=20for=20clarity=20and?= =?UTF-8?q?=20update=20references=20in=20PF=20coil=20logic=20and=20tests?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- documentation/eng-models/pf-coil.md | 2 +- process/pfcoil.py | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/documentation/eng-models/pf-coil.md b/documentation/eng-models/pf-coil.md index 9d7a76fb1f..20f0598d4a 100644 --- a/documentation/eng-models/pf-coil.md +++ b/documentation/eng-models/pf-coil.md @@ -25,7 +25,7 @@ values for `dr_pf_cs_middle_offset`, `rpf2`, `zref(j)` and `dr_pf_tf_outboard_ou coils accurately. The three possible values of `i_pf_location(j)` correspond to the following PF coil positions: (Redo taking -into account `i_single_null` and other recent changes e.g. rclsnorm) +into account `i_single_null` and other recent changes e.g. r_pf_outside_tf_midplane) `i_pf_location(j)` = 1: PF coils are placed above the central solenoid (one group only); *R* = `r_cs_middle` + `dr_pf_cs_middle_offset`
diff --git a/process/pfcoil.py b/process/pfcoil.py index 935d304fb8..ddee4a0e80 100644 --- a/process/pfcoil.py +++ b/process/pfcoil.py @@ -212,7 +212,7 @@ def pfcoil(self): # Scale PF coil locations signn[0] = 1.0e0 signn[1] = -1.0e0 - pfcoil_variables.rclsnorm = ( + pfcoil_variables.r_pf_outside_tf_midplane = ( superconducting_tf_coil_variables.r_tf_outboard_out + pfcoil_variables.dr_pf_tf_outboard_out_offset ) @@ -278,12 +278,12 @@ def pfcoil(self): # otherwise stacked for resistive TF (rectangle-shape) if tfv.i_tf_sup != 1 or pfcoil_variables.i_sup_pf_shape == 1: pfcoil_variables.r_pf_coil_middle_group_array[group, coil] = ( - pfcoil_variables.rclsnorm + pfcoil_variables.r_pf_outside_tf_midplane ) else: pfcoil_variables.r_pf_coil_middle_group_array[group, coil] = ( math.sqrt( - pfcoil_variables.rclsnorm**2 + pfcoil_variables.r_pf_outside_tf_midplane**2 - pfcoil_variables.z_pf_coil_middle_group_array[ group, coil ] @@ -1250,7 +1250,7 @@ def tf_pf_collision_detector(self): if pfcoil_variables.r_pf_coil_middle_group_array[ ii, ij ] <= ( # Outboard TF coil collision - pfcoil_variables.rclsnorm + pfcoil_variables.r_pf_outside_tf_midplane - pfcoil_variables.dr_pf_tf_outboard_out_offset + pfcoil_variables.r_pf_coil_middle[i] ) and pfcoil_variables.r_pf_coil_middle_group_array[ii, ij] >= ( From a4d0cb36afd73d9954cf8902c7f06ffdb7de4097 Mon Sep 17 00:00:00 2001 From: mn3981 Date: Wed, 10 Sep 2025 12:00:43 +0100 Subject: [PATCH 08/20] =?UTF-8?q?=F0=9F=94=84=20-=20Refactor=20PF=20coil?= =?UTF-8?q?=20placement=20logic=20and=20add=20'place=5Fpf=5Fabove=5Ftf'=20?= =?UTF-8?q?method=20for=20clarity=20and=20maintainability?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- process/pfcoil.py | 129 ++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 112 insertions(+), 17 deletions(-) diff --git a/process/pfcoil.py b/process/pfcoil.py index ddee4a0e80..c0ada644fc 100644 --- a/process/pfcoil.py +++ b/process/pfcoil.py @@ -245,28 +245,38 @@ def pfcoil(self): z_pf_coil_middle_group_array[group, coil] ) + # ========================================================================= + elif pfcoil_variables.i_pf_location[group] == 2: # PF coil is on top of the TF coil + ( + r_pf_coil_middle_group_array, + z_pf_coil_middle_group_array, + top_bottom, + ) = self.place_pf_above_tf( + n_pf_coils_in_group=pfcoil_variables.n_pf_coils_in_group, + n_pf_group=group, + rmajor=pv.rmajor, + triang=pv.triang, + rminor=pv.rminor, + itart=pv.itart, + itartpf=pv.itartpf, + z_tf_inside_half=bv.z_tf_inside_half, + hpfdif=bv.hpfdif, + z_tf_top=bv.z_tf_top, + top_bottom=top_bottom, + rpf2=pfcoil_variables.rpf2, + zref=pfcoil_variables.zref, + ) + for coil in range(pfcoil_variables.n_pf_coils_in_group[group]): pfcoil_variables.r_pf_coil_middle_group_array[group, coil] = ( - pv.rmajor + pfcoil_variables.rpf2 * pv.triang * pv.rminor + r_pf_coil_middle_group_array[group, coil] ) - if pv.itart == 1 and pv.itartpf == 0: - pfcoil_variables.z_pf_coil_middle_group_array[group, coil] = ( - bv.z_tf_inside_half - pfcoil_variables.zref[group] - ) * signn[coil] - else: - # pfcoil_variables.z_pf_coil_middle_group_array(group,coil) = (bv.z_tf_inside_half + bv.dr_tf_inboard + 0.86e0) * signn(coil) - if top_bottom == 1: # this coil is above midplane - pfcoil_variables.z_pf_coil_middle_group_array[ - group, coil - ] = bv.z_tf_top + 0.86e0 - top_bottom = -1 - else: # this coil is below midplane - pfcoil_variables.z_pf_coil_middle_group_array[ - group, coil - ] = -1.0e0 * (bv.z_tf_top - 2.0e0 * bv.hpfdif + 0.86e0) - top_bottom = 1 + pfcoil_variables.z_pf_coil_middle_group_array[group, coil] = ( + z_pf_coil_middle_group_array[group, coil] + ) + # ========================================================================= elif pfcoil_variables.i_pf_location[group] == 3: # PF coil is radially outside the TF coil @@ -305,6 +315,8 @@ def pfcoil(self): group, coil ] = 1e10 + # ========================================================================= + elif pfcoil_variables.i_pf_location[group] == 4: # PF coil is in general location # See issue 1418 @@ -1114,6 +1126,89 @@ def place_pf_above_cs( ) return r_pf_coil_middle_group_array, z_pf_coil_middle_group_array + def place_pf_above_tf( + self, + n_pf_coils_in_group: np.ndarray, + n_pf_group: int, + rmajor: float, + triang: float, + rminor: float, + itart: int, + itartpf: int, + z_tf_inside_half: float, + hpfdif: float, + z_tf_top: float, + top_bottom: int, + rpf2: float, + zref: np.ndarray, + ) -> tuple[np.ndarray, np.ndarray]: + """ + Calculates and places poloidal field (PF) coils above the toroidal field (TF) coils for a given group. + + :param n_pf_coils_in_group: Array containing the number of PF coils in each group. + :type n_pf_coils_in_group: np.ndarray + :param n_pf_group: Index of the PF coil group to process. + :type n_pf_group: int + :param rmajor: Major radius of the device. + :type rmajor: float + :param triang: Triangularity parameter for coil placement. + :type triang: float + :param rminor: Minor radius of the device. + :type rminor: float + :param itart: Flag indicating ST configuration. + :type itart: int + :param itartpf: Flag indicating PF coil configuration for ST. + :type itartpf: int + :param z_tf_inside_half: Half-height of the TF coil inside region. + :type z_tf_inside_half: float + :param hpfdif: Height difference parameter for PF coil placement. + :type hpfdif: float + :param z_tf_top: Top z-coordinate of the TF coil. + :type z_tf_top: float + :param top_bottom: Indicator for coil placement above (+1) or below (-1) the midplane. + :type top_bottom: int + :param rpf2: Radial offset parameter for PF coil placement. + :type rpf2: float + :param zref: Array of reference z-coordinates for PF coil placement. + :type zref: np.ndarray + + :returns: Tuple containing arrays of radial and vertical positions of PF coil middles for the specified group. + :rtype: tuple[np.ndarray, np.ndarray] + """ + # Assign empty 2D arrays to be filled + r_pf_coil_middle_group_array = np.zeros(( + n_pf_group, + n_pf_coils_in_group[n_pf_group], + )) + z_pf_coil_middle_group_array = np.zeros(( + n_pf_group, + n_pf_coils_in_group[n_pf_group], + )) + + for coil in range(n_pf_coils_in_group[n_pf_group]): + # Place PF coils at radius determined by rmajor, triang and rminor + r_pf_coil_middle_group_array[n_pf_group, coil] = ( + rmajor + rpf2 * triang * rminor + ) + + # Set sign: +1 for coil 0, -1 for coil 1 + sign = 1.0 if coil == 0 else -1.0 + if itart == 1 and itartpf == 0: + z_pf_coil_middle_group_array[n_pf_group, coil] = ( + z_tf_inside_half - zref[n_pf_group] + ) * sign + else: + if top_bottom == 1: # this coil is above midplane + z_pf_coil_middle_group_array[n_pf_group, coil] = z_tf_top + 0.86e0 + top_bottom = -1 + else: # this coil is below midplane + z_pf_coil_middle_group_array[n_pf_group, coil] = -1.0e0 * ( + z_tf_top - 2.0e0 * hpfdif + 0.86e0 + ) + top_bottom = 1 + + return r_pf_coil_middle_group_array, z_pf_coil_middle_group_array, top_bottom + def efc( self, npts, From 23ccfef13bb5d15c6882cffe8a1c5af28c269361 Mon Sep 17 00:00:00 2001 From: mn3981 Date: Wed, 10 Sep 2025 13:50:57 +0100 Subject: [PATCH 09/20] =?UTF-8?q?=F0=9F=94=84=20-=20Rename=20'hpfdif'=20to?= =?UTF-8?q?=20'dz=5Ftf=5Fupper=5Flower=5Fmidplane'=20for=20clarity=20and?= =?UTF-8?q?=20update=20references=20in=20Build=20and=20PFCoil=20classes=20?= =?UTF-8?q?and=20integration=20tests?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- process/build.py | 4 +-- process/data_structure/build_variables.py | 6 ++-- process/pfcoil.py | 37 +++++++++++++---------- tests/integration/test_pfcoil_int.py | 2 +- 4 files changed, 27 insertions(+), 22 deletions(-) diff --git a/process/build.py b/process/build.py index 3e97ede4f1..e1038d7ce5 100644 --- a/process/build.py +++ b/process/build.py @@ -826,7 +826,7 @@ def calculate_vertical_build(self, output: bool) -> None: build_variables.z_tf_top = ( build_variables.z_tf_inside_half + build_variables.dr_tf_inboard ) - build_variables.hpfdif = 0.0e0 + build_variables.dz_tf_upper_lower_midplane = 0.0e0 else: build_variables.z_tf_top = ( build_variables.dr_tf_inboard @@ -842,7 +842,7 @@ def calculate_vertical_build(self, output: bool) -> None: + build_variables.dz_fw_plasma_gap + build_variables.z_plasma_xpoint_upper ) - build_variables.hpfdif = ( + build_variables.dz_tf_upper_lower_midplane = ( build_variables.z_tf_top - (build_variables.z_tf_inside_half + build_variables.dr_tf_inboard) ) / 2.0e0 diff --git a/process/data_structure/build_variables.py b/process/data_structure/build_variables.py index 405908604f..6828986121 100644 --- a/process/data_structure/build_variables.py +++ b/process/data_structure/build_variables.py @@ -153,7 +153,7 @@ """maximum (half-)height of TF coil (inside edge) (m)""" -hpfdif: float = None +dz_tf_upper_lower_midplane: float = None """difference in distance from midplane of upper and lower portions of TF legs (non-zero for single-null devices) (m) """ @@ -443,7 +443,7 @@ def init_build_variables(): global gapomin global dr_shld_vv_gap_outboard global z_tf_inside_half - global hpfdif + global dz_tf_upper_lower_midplane global z_tf_top global hr1 global iohcl @@ -538,7 +538,7 @@ def init_build_variables(): gapomin = 0.234 dr_shld_vv_gap_outboard = 0.0 z_tf_inside_half = 0.0 - hpfdif = 0.0 + dz_tf_upper_lower_midplane = 0.0 z_tf_top = 0.0 hr1 = 0.0 iohcl = 1 diff --git a/process/pfcoil.py b/process/pfcoil.py index c0ada644fc..31cf604e4a 100644 --- a/process/pfcoil.py +++ b/process/pfcoil.py @@ -14,7 +14,11 @@ from process.data_structure import constraint_variables as ctv from process.data_structure import cs_fatigue_variables as csfv from process.data_structure import fwbs_variables as fwbsv -from process.data_structure import numerics, pfcoil_variables, superconducting_tf_coil_variables +from process.data_structure import ( + numerics, + pfcoil_variables, + superconducting_tf_coil_variables, +) from process.data_structure import physics_variables as pv from process.data_structure import rebco_variables as rcv from process.data_structure import tfcoil_variables as tfv @@ -262,7 +266,7 @@ def pfcoil(self): itart=pv.itart, itartpf=pv.itartpf, z_tf_inside_half=bv.z_tf_inside_half, - hpfdif=bv.hpfdif, + dz_tf_upper_lower_midplane=bv.dz_tf_upper_lower_midplane, z_tf_top=bv.z_tf_top, top_bottom=top_bottom, rpf2=pfcoil_variables.rpf2, @@ -1099,14 +1103,15 @@ def place_pf_above_cs( :return: Tuple of arrays containing the radial and vertical coordinates of PF coils in the group. :rtype: tuple[np.ndarray, np.ndarray] """ - # Assign empty 2D arrays to be filled + + # Initialise as empty arrays; will be resized in the loop r_pf_coil_middle_group_array = np.zeros(( - n_pf_group, - n_pf_coils_in_group[n_pf_group], + pfcoil_variables.N_PF_GROUPS_MAX, + pfcoil_variables.N_PF_COILS_IN_GROUP_MAX, )) z_pf_coil_middle_group_array = np.zeros(( - n_pf_group, - n_pf_coils_in_group[n_pf_group], + pfcoil_variables.N_PF_GROUPS_MAX, + pfcoil_variables.N_PF_COILS_IN_GROUP_MAX, )) for coil in range(n_pf_coils_in_group[n_pf_group]): @@ -1136,7 +1141,7 @@ def place_pf_above_tf( itart: int, itartpf: int, z_tf_inside_half: float, - hpfdif: float, + dz_tf_upper_lower_midplane: float, z_tf_top: float, top_bottom: int, rpf2: float, @@ -1161,8 +1166,8 @@ def place_pf_above_tf( :type itartpf: int :param z_tf_inside_half: Half-height of the TF coil inside region. :type z_tf_inside_half: float - :param hpfdif: Height difference parameter for PF coil placement. - :type hpfdif: float + :param dz_tf_upper_lower_midplane: Height difference parameter for PF coil placement. + :type dz_tf_upper_lower_midplane: float :param z_tf_top: Top z-coordinate of the TF coil. :type z_tf_top: float :param top_bottom: Indicator for coil placement above (+1) or below (-1) the midplane. @@ -1175,14 +1180,14 @@ def place_pf_above_tf( :returns: Tuple containing arrays of radial and vertical positions of PF coil middles for the specified group. :rtype: tuple[np.ndarray, np.ndarray] """ - # Assign empty 2D arrays to be filled + # Initialise as empty arrays; will be resized in the loop r_pf_coil_middle_group_array = np.zeros(( - n_pf_group, - n_pf_coils_in_group[n_pf_group], + pfcoil_variables.N_PF_GROUPS_MAX, + pfcoil_variables.N_PF_COILS_IN_GROUP_MAX, )) z_pf_coil_middle_group_array = np.zeros(( - n_pf_group, - n_pf_coils_in_group[n_pf_group], + pfcoil_variables.N_PF_GROUPS_MAX, + pfcoil_variables.N_PF_COILS_IN_GROUP_MAX, )) for coil in range(n_pf_coils_in_group[n_pf_group]): @@ -1203,7 +1208,7 @@ def place_pf_above_tf( top_bottom = -1 else: # this coil is below midplane z_pf_coil_middle_group_array[n_pf_group, coil] = -1.0e0 * ( - z_tf_top - 2.0e0 * hpfdif + 0.86e0 + z_tf_top - 2.0e0 * dz_tf_upper_lower_midplane + 0.86e0 ) top_bottom = 1 diff --git a/tests/integration/test_pfcoil_int.py b/tests/integration/test_pfcoil_int.py index cf5ab03883..69aa734309 100644 --- a/tests/integration/test_pfcoil_int.py +++ b/tests/integration/test_pfcoil_int.py @@ -46,7 +46,7 @@ def test_pfcoil(monkeypatch, pfcoil): """ monkeypatch.setattr(bv, "iohcl", 1) - monkeypatch.setattr(bv, "hpfdif", 0.0) + monkeypatch.setattr(bv, "dz_tf_upper_lower_midplane", 0.0) monkeypatch.setattr(bv, "z_tf_top", 4.0) # guess monkeypatch.setattr(bv, "z_tf_inside_half", 8.8) monkeypatch.setattr(bv, "dr_cs", 0.65) From 631294954d84e4502c9176acda30339b11fbfaca Mon Sep 17 00:00:00 2001 From: mn3981 Date: Wed, 10 Sep 2025 14:38:28 +0100 Subject: [PATCH 10/20] :bug: Fix calculated value of dz_tf_upper_lower_midplan --- process/build.py | 7 +++---- process/pfcoil.py | 9 +++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/process/build.py b/process/build.py index e1038d7ce5..18ff3c962e 100644 --- a/process/build.py +++ b/process/build.py @@ -842,10 +842,9 @@ def calculate_vertical_build(self, output: bool) -> None: + build_variables.dz_fw_plasma_gap + build_variables.z_plasma_xpoint_upper ) - build_variables.dz_tf_upper_lower_midplane = ( - build_variables.z_tf_top - - (build_variables.z_tf_inside_half + build_variables.dr_tf_inboard) - ) / 2.0e0 + build_variables.dz_tf_upper_lower_midplane = build_variables.z_tf_top - ( + build_variables.z_tf_inside_half + build_variables.dr_tf_inboard + ) def divgeom(self, output: bool): """ diff --git a/process/pfcoil.py b/process/pfcoil.py index 31cf604e4a..f50d420957 100644 --- a/process/pfcoil.py +++ b/process/pfcoil.py @@ -1146,7 +1146,7 @@ def place_pf_above_tf( top_bottom: int, rpf2: float, zref: np.ndarray, - ) -> tuple[np.ndarray, np.ndarray]: + ) -> tuple[np.ndarray, np.ndarray, int]: """ Calculates and places poloidal field (PF) coils above the toroidal field (TF) coils for a given group. @@ -1177,8 +1177,9 @@ def place_pf_above_tf( :param zref: Array of reference z-coordinates for PF coil placement. :type zref: np.ndarray - :returns: Tuple containing arrays of radial and vertical positions of PF coil middles for the specified group. - :rtype: tuple[np.ndarray, np.ndarray] + :returns: Tuple containing arrays of radial and vertical positions of PF coil middles for the specified group, + and the updated top_bottom indicator. + :rtype: tuple[np.ndarray, np.ndarray, int] """ # Initialise as empty arrays; will be resized in the loop r_pf_coil_middle_group_array = np.zeros(( @@ -1208,7 +1209,7 @@ def place_pf_above_tf( top_bottom = -1 else: # this coil is below midplane z_pf_coil_middle_group_array[n_pf_group, coil] = -1.0e0 * ( - z_tf_top - 2.0e0 * dz_tf_upper_lower_midplane + 0.86e0 + z_tf_top - dz_tf_upper_lower_midplane + 0.86e0 ) top_bottom = 1 From bd7a3e3074db2e306b7a973482809ac1a669cf37 Mon Sep 17 00:00:00 2001 From: mn3981 Date: Wed, 10 Sep 2025 14:38:42 +0100 Subject: [PATCH 11/20] :art: Add 'dz_tf_upper_lower_midplane' to plot and output for improved clarity in TF coil height difference representation --- process/io/plot_proc.py | 18 +++++++++++++++++- process/tf_coil.py | 7 +++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/process/io/plot_proc.py b/process/io/plot_proc.py index f1fdb474b0..c5baaaad60 100644 --- a/process/io/plot_proc.py +++ b/process/io/plot_proc.py @@ -8764,6 +8764,9 @@ def plot_tf_coil_structure(axis, mfile_data, scan, colour_scheme=1): r_tf_inboard_in = mfile_data.data["r_tf_inboard_in"].get_scan(scan) dr_tf_outboard = mfile_data.data["dr_tf_outboard"].get_scan(scan) len_tf_coil = mfile_data.data["len_tf_coil"].get_scan(scan) + dz_tf_upper_lower_midplane = mfile_data.data["dz_tf_upper_lower_midplane"].get_scan( + scan + ) # Plot the points as black dots, number them, and connect them with lines xs = [x1, x2, x3, x4, x5] @@ -9036,7 +9039,7 @@ def plot_tf_coil_structure(axis, mfile_data, scan, colour_scheme=1): # ============================================================== - # Add a label for the inboard thickness + # Add a label for the length of the coil axis.text( (r_tf_outboard_in + 2 * dr_tf_outboard), 0.0, @@ -9049,6 +9052,19 @@ def plot_tf_coil_structure(axis, mfile_data, scan, colour_scheme=1): # ============================================================== + # Add a label for the length of the coil + axis.text( + (r_tf_outboard_in + 2 * dr_tf_outboard), + -1.0, + f"$\\Delta Z$ upper and lower to midplane = {dz_tf_upper_lower_midplane:.3f} m", + fontsize=7, + color="black", + verticalalignment="center", + bbox={"boxstyle": "round", "facecolor": "pink", "alpha": 1.0}, + ) + + # ============================================================== + # Add arow for inboard coil radius axis.annotate( "", diff --git a/process/tf_coil.py b/process/tf_coil.py index 92cca87391..eaa684d5db 100644 --- a/process/tf_coil.py +++ b/process/tf_coil.py @@ -762,6 +762,13 @@ def outtf(self): build_variables.z_tf_top, "OP ", ) + po.ovarre( + self.outfile, + "Height difference in upper and lower TF from midplane (m)", + "(dz_tf_upper_lower_midplane)", + build_variables.dz_tf_upper_lower_midplane, + "OP ", + ) if physics_variables.itart == 1: po.ovarre( self.outfile, From 092551bdcf02e7fda51fdbe220d4b66729aaf9be Mon Sep 17 00:00:00 2001 From: mn3981 Date: Wed, 10 Sep 2025 15:22:40 +0100 Subject: [PATCH 12/20] =?UTF-8?q?=F0=9F=94=84=20-=20Rename=20'i=5Fsup=5Fpf?= =?UTF-8?q?=5Fshape'=20to=20'i=5Fr=5Fpf=5Foutside=5Ftf=5Fplacement'=20for?= =?UTF-8?q?=20clarity=20and=20update=20references=20in=20related=20files?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- process/data_structure/pfcoil_variables.py | 7 +++---- process/input.py | 2 +- process/pfcoil.py | 10 +++++----- tests/integration/test_pfcoil_int.py | 2 +- .../input_files/spherical_tokamak_eval.IN.DAT | 2 +- tests/regression/input_files/st_regression.IN.DAT | 2 +- 6 files changed, 12 insertions(+), 13 deletions(-) diff --git a/process/data_structure/pfcoil_variables.py b/process/data_structure/pfcoil_variables.py index 2a1cfeea81..502b2d5d46 100644 --- a/process/data_structure/pfcoil_variables.py +++ b/process/data_structure/pfcoil_variables.py @@ -245,9 +245,8 @@ """ -i_sup_pf_shape: int = None +i_r_pf_outside_tf_placement: int = None """Switch for the placement of Location 3 (outboard) PF coils -when the TF coils are superconducting (i_tf_sup = 1) - =0 (Default) Outboard PF coils follow TF shape in an ellipsoidal winding surface - =1 Outboard PF coils all have same radius, cylindrical @@ -634,7 +633,7 @@ def init_pfcoil_variables(): global j_crit_str_cs global j_crit_str_pf global i_pf_current - global i_sup_pf_shape + global i_r_pf_outside_tf_placement global j_cs_conductor_critical_pulse_start global j_cs_conductor_critical_flat_top_end global jcableoh_bop @@ -736,7 +735,7 @@ def init_pfcoil_variables(): j_crit_str_cs = 0.0 j_crit_str_pf = 0.0 i_pf_current = 1 - i_sup_pf_shape = 0 + i_r_pf_outside_tf_placement = 0 j_cs_conductor_critical_pulse_start = 0.0 j_cs_conductor_critical_flat_top_end = 0.0 jcableoh_bop = 0.0 diff --git a/process/input.py b/process/input.py index dde6f5e820..c16ec00578 100644 --- a/process/input.py +++ b/process/input.py @@ -2061,7 +2061,7 @@ def __post_init__(self): data_structure.physics_variables, int, choices=[0, 1] ), "i_str_wp": InputVariable(data_structure.tfcoil_variables, int, choices=[0, 1]), - "i_sup_pf_shape": InputVariable( + "i_r_pf_outside_tf_placement": InputVariable( data_structure.pfcoil_variables, int, choices=[0, 1] ), "i_tf_bucking": InputVariable(data_structure.tfcoil_variables, int, range=(0, 3)), diff --git a/process/pfcoil.py b/process/pfcoil.py index f50d420957..edf9ae3421 100644 --- a/process/pfcoil.py +++ b/process/pfcoil.py @@ -290,7 +290,7 @@ def pfcoil(self): ) # Coil radius follows TF coil curve for SC TF (D-shape) # otherwise stacked for resistive TF (rectangle-shape) - if tfv.i_tf_sup != 1 or pfcoil_variables.i_sup_pf_shape == 1: + if tfv.i_tf_sup != 1 or pfcoil_variables.i_r_pf_outside_tf_placement == 1: pfcoil_variables.r_pf_coil_middle_group_array[group, coil] = ( pfcoil_variables.r_pf_outside_tf_midplane ) @@ -1106,11 +1106,11 @@ def place_pf_above_cs( # Initialise as empty arrays; will be resized in the loop r_pf_coil_middle_group_array = np.zeros(( - pfcoil_variables.N_PF_GROUPS_MAX, + pfcoil_variables.n_pf_coil_groups, pfcoil_variables.N_PF_COILS_IN_GROUP_MAX, )) z_pf_coil_middle_group_array = np.zeros(( - pfcoil_variables.N_PF_GROUPS_MAX, + pfcoil_variables.n_pf_coil_groups, pfcoil_variables.N_PF_COILS_IN_GROUP_MAX, )) @@ -1183,11 +1183,11 @@ def place_pf_above_tf( """ # Initialise as empty arrays; will be resized in the loop r_pf_coil_middle_group_array = np.zeros(( - pfcoil_variables.N_PF_GROUPS_MAX, + pfcoil_variables.n_pf_coil_groups, pfcoil_variables.N_PF_COILS_IN_GROUP_MAX, )) z_pf_coil_middle_group_array = np.zeros(( - pfcoil_variables.N_PF_GROUPS_MAX, + pfcoil_variables.n_pf_coil_groups, pfcoil_variables.N_PF_COILS_IN_GROUP_MAX, )) diff --git a/tests/integration/test_pfcoil_int.py b/tests/integration/test_pfcoil_int.py index 69aa734309..7a5dd2c060 100644 --- a/tests/integration/test_pfcoil_int.py +++ b/tests/integration/test_pfcoil_int.py @@ -122,7 +122,7 @@ def test_pfcoil(monkeypatch, pfcoil): monkeypatch.setattr(pfcoil_variables, "fcupfsu", 6.900e-1) monkeypatch.setattr(pfcoil_variables, "j_cs_pulse_start", 1.693e7) monkeypatch.setattr(pfcoil_variables, "j_pf_wp_critical", np.full(22, 0.0)) - monkeypatch.setattr(pfcoil_variables, "i_sup_pf_shape", 0) + monkeypatch.setattr(pfcoil_variables, "i_r_pf_outside_tf_placement", 0) monkeypatch.setattr(pfcoil_variables, "rref", np.full(10, 7.0)) monkeypatch.setattr(pfcoil_variables, "i_pf_current", 1) monkeypatch.setattr(pfcoil_variables, "ccl0_ma", np.full(10, 0.0)) diff --git a/tests/regression/input_files/spherical_tokamak_eval.IN.DAT b/tests/regression/input_files/spherical_tokamak_eval.IN.DAT index 02178642ba..3d3087bc32 100644 --- a/tests/regression/input_files/spherical_tokamak_eval.IN.DAT +++ b/tests/regression/input_files/spherical_tokamak_eval.IN.DAT @@ -242,7 +242,7 @@ f_nd_impurity_electrons(14) = 5e-05 i_pf_location = 2,3,3,4 * Switch for location of PF coil group i; i_pf_conductor = 0 * switch for PF & CS coil conductor type; i_pf_superconductor = 9 * switch for superconductor material in PF coils; -i_sup_pf_shape = 1 * Switch for the placement of Location 3 (outboard) PF coils +i_r_pf_outside_tf_placement = 1 * Switch for the placement of Location 3 (outboard) PF coils n_pf_coils_in_group = 2,2,2,2 * number of PF coils in group j n_pf_coil_groups = 4 * number of groups of PF coils; Symmetric coil pairs should all be in the same group rref = 7.0D0, 7.0D0, 7.0D0, 2.0, 7.0D0, 7.0D0, 7.0D0, 7.0D0, 7.0D0, 7.0D0 * PF coil radial positioning adjuster; diff --git a/tests/regression/input_files/st_regression.IN.DAT b/tests/regression/input_files/st_regression.IN.DAT index 061c5b3413..82be122222 100644 --- a/tests/regression/input_files/st_regression.IN.DAT +++ b/tests/regression/input_files/st_regression.IN.DAT @@ -1865,7 +1865,7 @@ i_pf_location = 2,3,3,4 * JUSTIFICATION: Design choice, one above x-point and two outboard -i_sup_pf_shape = 1 +i_r_pf_outside_tf_placement = 1 * DESCRIPTION: Switch for the placement of i_pf_location = 3 (outboard) PF coils * when the TF coils are superconducting (i_tf_sup = 1) * =0 (Default) Outboard PF coils follow TF shape From 1581920d62cae3197e70b6c437dd39d0e30700f6 Mon Sep 17 00:00:00 2001 From: mn3981 Date: Wed, 10 Sep 2025 15:25:55 +0100 Subject: [PATCH 13/20] :bug: Fix false association between TF coil shape and conductor type --- process/pfcoil.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/process/pfcoil.py b/process/pfcoil.py index edf9ae3421..36825467ee 100644 --- a/process/pfcoil.py +++ b/process/pfcoil.py @@ -288,13 +288,13 @@ def pfcoil(self): pfcoil_variables.z_pf_coil_middle_group_array[group, coil] = ( pv.rminor * pfcoil_variables.zref[group] * signn[coil] ) - # Coil radius follows TF coil curve for SC TF (D-shape) - # otherwise stacked for resistive TF (rectangle-shape) - if tfv.i_tf_sup != 1 or pfcoil_variables.i_r_pf_outside_tf_placement == 1: + # Coil radius is constant / stacked for picture frame TF or if placement switch is set + if tfv.i_tf_shape == 2 or pfcoil_variables.i_r_pf_outside_tf_placement == 1: pfcoil_variables.r_pf_coil_middle_group_array[group, coil] = ( pfcoil_variables.r_pf_outside_tf_midplane ) else: + # Coil radius follows TF coil curve for TF (D-shape) pfcoil_variables.r_pf_coil_middle_group_array[group, coil] = ( math.sqrt( pfcoil_variables.r_pf_outside_tf_midplane**2 From 77d23be5a59bd7b110bd9e38780b51463466a7b9 Mon Sep 17 00:00:00 2001 From: mn3981 Date: Wed, 10 Sep 2025 16:20:35 +0100 Subject: [PATCH 14/20] :recycle: Add new function for placing PF coils outside the TF --- process/data_structure/pfcoil_variables.py | 2 + process/pfcoil.py | 121 +++++++++++++++------ 2 files changed, 92 insertions(+), 31 deletions(-) diff --git a/process/data_structure/pfcoil_variables.py b/process/data_structure/pfcoil_variables.py index 502b2d5d46..0289ec4563 100644 --- a/process/data_structure/pfcoil_variables.py +++ b/process/data_structure/pfcoil_variables.py @@ -44,8 +44,10 @@ xind: list[float] = None r_pf_coil_middle_group_array: list[float] = None +"""2D array of PF coil middle radii, indexed by group and coil in group""" z_pf_coil_middle_group_array: list[float] = None +"""2D array of PF coil middle heights, indexed by group and coil in group""" ccls: list[float] = None diff --git a/process/pfcoil.py b/process/pfcoil.py index 36825467ee..429b89aefa 100644 --- a/process/pfcoil.py +++ b/process/pfcoil.py @@ -284,40 +284,26 @@ def pfcoil(self): elif pfcoil_variables.i_pf_location[group] == 3: # PF coil is radially outside the TF coil + ( + r_pf_coil_middle_group_array, + z_pf_coil_middle_group_array, + ) = self.place_pf_outside_tf( + n_pf_coils_in_group=pfcoil_variables.n_pf_coils_in_group, + n_pf_group=group, + rminor=pv.rminor, + zref=pfcoil_variables.zref, + i_tf_shape=tfv.i_tf_shape, + i_r_pf_outside_tf_placement=pfcoil_variables.i_r_pf_outside_tf_placement, + r_pf_outside_tf_midplane=pfcoil_variables.r_pf_outside_tf_midplane, + ) + for coil in range(pfcoil_variables.n_pf_coils_in_group[group]): + pfcoil_variables.r_pf_coil_middle_group_array[group, coil] = ( + r_pf_coil_middle_group_array[group, coil] + ) pfcoil_variables.z_pf_coil_middle_group_array[group, coil] = ( - pv.rminor * pfcoil_variables.zref[group] * signn[coil] + z_pf_coil_middle_group_array[group, coil] ) - # Coil radius is constant / stacked for picture frame TF or if placement switch is set - if tfv.i_tf_shape == 2 or pfcoil_variables.i_r_pf_outside_tf_placement == 1: - pfcoil_variables.r_pf_coil_middle_group_array[group, coil] = ( - pfcoil_variables.r_pf_outside_tf_midplane - ) - else: - # Coil radius follows TF coil curve for TF (D-shape) - pfcoil_variables.r_pf_coil_middle_group_array[group, coil] = ( - math.sqrt( - pfcoil_variables.r_pf_outside_tf_midplane**2 - - pfcoil_variables.z_pf_coil_middle_group_array[ - group, coil - ] - ** 2 - ) - ) - try: - assert ( - pfcoil_variables.r_pf_coil_middle_group_array[ - group, coil - ] - < np.inf - ) - except AssertionError: - logger.exception( - "Element of pfcoil_variables.r_pf_coil_middle_group_array is inf. Kludging to 1e10." - ) - pfcoil_variables.r_pf_coil_middle_group_array[ - group, coil - ] = 1e10 # ========================================================================= @@ -1215,6 +1201,79 @@ def place_pf_above_tf( return r_pf_coil_middle_group_array, z_pf_coil_middle_group_array, top_bottom + def place_pf_outside_tf( + self, + n_pf_coils_in_group: np.ndarray, + n_pf_group: int, + rminor: float, + zref: np.ndarray, + i_tf_shape: int, + i_r_pf_outside_tf_placement: int, + r_pf_outside_tf_midplane: float, + ) -> tuple[np.ndarray, np.ndarray]: + """ + Calculates the radial and vertical positions of poloidal field (PF) coils placed outside the toroidal field (TF) coil. + + :param n_pf_coils_in_group: Array containing the number of PF coils in each group. + :type n_pf_coils_in_group: np.ndarray + :param n_pf_group: Index of the PF coil group to process. + :type n_pf_group: int + :param rminor: Minor radius of the device. + :type rminor: float + :param zref: Reference vertical positions for each PF coil group. + :type zref: np.ndarray + :param i_tf_shape: Integer flag indicating TF coil shape (2 for picture frame, others for D-shape). + :type i_tf_shape: int + :param i_r_pf_outside_tf_placement: Placement switch for PF coil radius (1 for constant/stacked, 0 for following TF curve). + :type i_r_pf_outside_tf_placement: int + :param r_pf_outside_tf_midplane: Radial position of PF coil at the midplane. + :type r_pf_outside_tf_midplane: float + + :returns: Tuple containing arrays of radial and vertical positions of PF coil centers for the specified group. + :rtype: tuple[np.ndarray, np.ndarray] + """ + + # Initialise as empty arrays; will be resized in the loop + r_pf_coil_middle_group_array = np.zeros(( + pfcoil_variables.n_pf_coil_groups, + pfcoil_variables.N_PF_COILS_IN_GROUP_MAX, + )) + z_pf_coil_middle_group_array = np.zeros(( + pfcoil_variables.n_pf_coil_groups, + pfcoil_variables.N_PF_COILS_IN_GROUP_MAX, + )) + + # PF coil is radially outside the TF coil + + for coil in range(n_pf_coils_in_group[n_pf_group]): + sign = 1.0 if coil == 0 else -1.0 + + z_pf_coil_middle_group_array[n_pf_group, coil] = ( + rminor * zref[n_pf_group] * sign + ) + # Coil radius is constant / stacked for picture frame TF or if placement switch is set + if i_tf_shape == 2 or i_r_pf_outside_tf_placement == 1: + r_pf_coil_middle_group_array[n_pf_group, coil] = ( + r_pf_outside_tf_midplane + ) + else: + # Coil radius follows TF coil curve for TF (D-shape) + r_pf_coil_middle_group_array[n_pf_group, coil] = math.sqrt( + r_pf_outside_tf_midplane**2 + - z_pf_coil_middle_group_array[n_pf_group, coil] ** 2 + ) + try: + assert r_pf_coil_middle_group_array[n_pf_group, coil] < np.inf + except AssertionError: + logger.exception( + "Element of pfcoil_variables.r_pf_coil_middle_group_array is inf. Kludging to 1e10." + ) + r_pf_coil_middle_group_array[n_pf_group, coil] = 1e10 + return ( + r_pf_coil_middle_group_array, + z_pf_coil_middle_group_array, + ) + def efc( self, npts, From 8c9cfa97ea997272db50f9e3c06369547ce9b3cb Mon Sep 17 00:00:00 2001 From: mn3981 Date: Thu, 11 Sep 2025 09:12:16 +0100 Subject: [PATCH 15/20] :recycle: Refactor PF coil placement logic into a new method for improved clarity and maintainability --- process/pfcoil.py | 75 ++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 65 insertions(+), 10 deletions(-) diff --git a/process/pfcoil.py b/process/pfcoil.py index 429b89aefa..a2331400ac 100644 --- a/process/pfcoil.py +++ b/process/pfcoil.py @@ -308,16 +308,17 @@ def pfcoil(self): # ========================================================================= elif pfcoil_variables.i_pf_location[group] == 4: - # PF coil is in general location - # See issue 1418 - # https://git.ccfe.ac.uk/process/process/-/issues/1418 - for coil in range(pfcoil_variables.n_pf_coils_in_group[group]): - pfcoil_variables.z_pf_coil_middle_group_array[group, coil] = ( - pv.rminor * pfcoil_variables.zref[group] * signn[coil] - ) - pfcoil_variables.r_pf_coil_middle_group_array[group, coil] = ( - pv.rminor * pfcoil_variables.rref[group] + pv.rmajor - ) + ( + r_pf_coil_middle_group_array, + z_pf_coil_middle_group_array, + ) = self.place_pf_generally( + n_pf_coils_in_group=pfcoil_variables.n_pf_coils_in_group, + n_pf_group=group, + rminor=pv.rminor, + rmajor=pv.rmajor, + zref=pfcoil_variables.zref, + rref=pfcoil_variables.rref, + ) else: raise ProcessValueError( @@ -325,6 +326,7 @@ def pfcoil(self): group=group, i_pf_location=pfcoil_variables.i_pf_location[group], ) + # ========================================================================= # Allocate current to the PF coils: # "Flux swing coils" participate in cancellation of the CS @@ -1274,6 +1276,59 @@ def place_pf_outside_tf( z_pf_coil_middle_group_array, ) + def place_pf_generally( + self, + n_pf_coils_in_group: np.ndarray, + n_pf_group: int, + rminor: float, + rmajor: float, + zref: np.ndarray, + rref: np.ndarray, + ) -> tuple[np.ndarray, np.ndarray]: + """ + Calculates the radial and vertical positions of poloidal field (PF) coils placed in a general location. + + :param n_pf_coils_in_group: Array containing the number of PF coils in each group. + :type n_pf_coils_in_group: numpy.ndarray + :param n_pf_group: Index of the PF coil group to process. + :type n_pf_group: int + :param rminor: Minor radius of the device. + :type rminor: float + :param rmajor: Major radius of the device. + :type rmajor: float + :param zref: Reference vertical positions for each PF coil group. + :type zref: numpy.ndarray + :param rref: Reference radial positions for each PF coil group. + :type rref: numpy.ndarray + + :returns: Tuple containing arrays of radial and vertical positions of PF coil centers for the specified group. + :rtype: tuple[numpy.ndarray, numpy.ndarray] + """ + r_pf_coil_middle_group_array: np.ndarray = np.zeros(( + pfcoil_variables.n_pf_coil_groups, + pfcoil_variables.N_PF_COILS_IN_GROUP_MAX, + )) + z_pf_coil_middle_group_array: np.ndarray = np.zeros(( + pfcoil_variables.n_pf_coil_groups, + pfcoil_variables.N_PF_COILS_IN_GROUP_MAX, + )) + + for coil in range(n_pf_coils_in_group[n_pf_group]): + sign: float = 1.0 if coil == 0 else -1.0 + + # Place as mutiples of minor radius from the midplane + z_pf_coil_middle_group_array[n_pf_group, coil] = ( + rminor * zref[n_pf_group] * sign + ) + # Place as multiples of minor radius from the plasma centre + r_pf_coil_middle_group_array[n_pf_group, coil] = ( + rminor * rref[n_pf_group] + rmajor + ) + return ( + r_pf_coil_middle_group_array, + z_pf_coil_middle_group_array, + ) + def efc( self, npts, From 33c892d7348f7231a176c65a0fe7428ac47024d1 Mon Sep 17 00:00:00 2001 From: mn3981 Date: Thu, 11 Sep 2025 10:13:23 +0100 Subject: [PATCH 16/20] :art: Update PF coil positioning documentation for clarity and accuracy, including new equations and descriptions for outside TF coil placement --- documentation/eng-models/pf-coil.md | 106 ++++++++++++++++++++++------ 1 file changed, 85 insertions(+), 21 deletions(-) diff --git a/documentation/eng-models/pf-coil.md b/documentation/eng-models/pf-coil.md index 20f0598d4a..8160d2b04b 100644 --- a/documentation/eng-models/pf-coil.md +++ b/documentation/eng-models/pf-coil.md @@ -9,37 +9,101 @@ position and shape during the flat-top period. The positions and sizes of te PF coils are partly input, and partly calculated after consideration of the required currents and allowable current density. -The PF coil locations are controlled using a set of switched stored in array `i_pf_location` (see -Figure 1), and are calculated in routine `PFCOIL`. The coils are (usually) organised into groups +The PF coil locations are controlled using a set of switches stored in array `i_pf_location[]`. The coils are (usually) organised into groups containing two PF coils placed symmetrically above and below the midplane, and each group `j` has an element `i_pf_location(j)` assigned to it. Input parameter `n_pf_coil_groups` should be set to the number of groups, and `n_pf_coils_in_group(j)` should be assigned the number of coils in each group - which should be 2 in each case. -
-![Machine build](../images/vertical-build.png){ width="100%"} -
Figure 1: Machine build for D-shaped major components
-
- -In the following, all variables are defined in the variable descriptor file `vardes.html`. The -values for `dr_pf_cs_middle_offset`, `rpf2`, `zref(j)` and `dr_pf_tf_outboard_out_offset` should be adjusted by the user to locate the PF +The values for `dr_pf_cs_middle_offset`, `rpf2`, `zref(j)` and `dr_pf_tf_outboard_out_offset` should be adjusted by the user to locate the PF coils accurately. -The three possible values of `i_pf_location(j)` correspond to the following PF coil positions: (Redo taking -into account `i_single_null` and other recent changes e.g. r_pf_outside_tf_midplane) +For PF coils going to be placed outside the TF coils the key radius is defined as: + +$$ +\overbrace{R_{\text{PF, outside-TF}}}^{\texttt{r_pf_outside_tf_midplane}} = \overbrace{R_{\text{TF,outboard-out}}}^{\texttt{r_tf_outboard_out}} + \overbrace{dR_{\text{PF,TF-offset}}}^{\texttt{dr_tf_outboard_out_offset}} +$$ + +The four possible values of `i_pf_location(j)` correspond to the following PF coil positions: + +### Above the central solenoid (one group only) | `place_pf_above_cs()` + +- `i_pf_location(j) = 1`; + + $$ + R = \overbrace{R_{\text{CS,middle}}}^{\texttt{r_cs_middle}} + \overbrace{dR_{\text{offset}}}^{\texttt{dr_pf_cs_middle_offset}} + $$ + + $$ + Z = \pm \ \overbrace{Z_{\text{CS,top}}}^{\texttt{z_cs_coil_upper}} + 0.1 + \\ + \frac{1}{2}\left(\left(\underbrace{Z_{\text{TF,inside}}}_{\texttt{z_tf_inside_half}}-Z_{\text{CS,top}}\right)+\underbrace{dR_{\text{TF,inboard}}}_{\texttt{dr_tf_inboard}}+0.1\right) + $$ + +----------------------------- + +### Above the TF coils (one group only) | `place_pf_above_tf()` + +- `i_pf_location(j) = 2`; + + $$ + R = R_0 + \texttt{rpf2} \times \delta \times a + $$ + + Due to the nautre of the TF coils not being top-down symmetric for single null cases the positioning slightly differs if the coil is above or below the midplane. + + For above the midplane: + + $$ + Z = Z_{\text{TF,top}} + 0.86 + $$ + + For below the midplane: + + $$ + Z = - \left(Z_{\text{TF,top}} - \overbrace{dZ_{\text{TF,upper-lower-midplane}}}^{\texttt{dz_tf_upper_lower_midplane}} + 0.86 \right) + $$ + +------------------- + + +### Outside the TF coils | `place_pf_outside_tf()` + +- `i_pf_location(j) = 3`; + + The PF coils can either be stacked vertically outside the TF (ideal for a picture frame coil) or follow the TF coil curve (D-shaped): + + If the chosen value for `i_tf_shape` is that of a picture frame or `i_r_pf_outside_tf_placement == 1` then: + + $$ + R = \overbrace{R_{\text{PF, outside-TF}}}^{\texttt{r_pf_outside_tf_midplane}} + $$ + + Else, the coils follow a D-shape curve + + $$ + R = \sqrt{\left(\overbrace{R_{\text{PF, outside-TF}}}^{\texttt{r_pf_outside_tf_midplane}}\right)^2 - Z^2} + $$ + + $$ + Z = \pm \ a \times \texttt{zref} + $$ + +--------------------- + +### General placement | `place_pf_generally()` + +- `i_pf_location(j) = 4`; -`i_pf_location(j)` = 1: PF coils are placed above the central solenoid (one group only); -*R* = `r_cs_middle` + `dr_pf_cs_middle_offset`
-*Z* = $\pm$(`z_tf_inside_half` * `f_z_cs_tf_internal` + 0.1 + 0.5 * (`z_tf_inside_half` * (1 - `f_z_cs_tf_internal`) + `dr_tf_inboard` + 0.1)) + The PF coils are placed generally in units of minor radius ($a$) relative to the mid-plane and plasma major radius ($R_0$); -`i_pf_location(j)` = 2: PF coils are placed above the TF coils (one group only);
-*R* = `rmajor` + `rpf2`
-*Z* = $\pm$(`z_tf_inside_half` * `dr_tf_inboard` + 0.86) + $$ + R = \pm \ a \times \texttt{rref[]} + R_0 + $$ -`i_pf_location(j)` = 3: PF coils are placed radially outside the TF coils (any number of groups);
-*R* = `rtot` + `dr_tf_outboard`/2 + `dr_pf_tf_outboard_out_offset`
-*Z* = $\pm$(`rminor` * `zref(j)` + $$ + Z = \pm \ a \times \texttt{zref[]} + $$ -The void fraction (for coolant) in each coil `i`'s winding pack is given by `f_a_pf_coil_void(i)`. +------------------------- ## Coil currents From b5d52d10938fc3a1f64722ab4747c759eb37b6a4 Mon Sep 17 00:00:00 2001 From: mn3981 Date: Thu, 11 Sep 2025 10:53:38 +0100 Subject: [PATCH 17/20] :recycle: Add parameterized test for placing PF coils above the CS with various configurations --- tests/integration/test_pfcoil_int.py | 3 ++- tests/unit/test_pfcoil.py | 40 ++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/tests/integration/test_pfcoil_int.py b/tests/integration/test_pfcoil_int.py index 7a5dd2c060..755cc5d16c 100644 --- a/tests/integration/test_pfcoil_int.py +++ b/tests/integration/test_pfcoil_int.py @@ -17,7 +17,7 @@ from process.cs_fatigue import CsFatigue from process.data_structure import build_variables as bv from process.data_structure import fwbs_variables as fwbsv -from process.data_structure import pfcoil_variables +from process.data_structure import pfcoil_variables, superconducting_tf_coil_variables from process.data_structure import physics_variables as pv from process.data_structure import tfcoil_variables as tfv from process.data_structure import times_variables as tv @@ -86,6 +86,7 @@ def test_pfcoil(monkeypatch, pfcoil): pfcoil_variables, "c_pf_cs_coil_pulse_start_ma", np.full(22, 0.0) ) monkeypatch.setattr(pfcoil_variables, "dr_pf_tf_outboard_out_offset", 1.5) + monkeypatch.setattr(superconducting_tf_coil_variables, "r_tf_outboard_out", 10.0) monkeypatch.setattr(pfcoil_variables, "c_pf_cs_coils_peak_ma", np.full(22, 0.0)) monkeypatch.setattr(pfcoil_variables, "f_j_cs_start_end_flat_top", 2.654e-1) monkeypatch.setattr(pfcoil_variables, "rpf2", -1.825) diff --git a/tests/unit/test_pfcoil.py b/tests/unit/test_pfcoil.py index 7bb602a56a..e661b4ed44 100644 --- a/tests/unit/test_pfcoil.py +++ b/tests/unit/test_pfcoil.py @@ -2103,3 +2103,43 @@ def test_calculate_cs_geometry( dr_bore=dr_bore, ) assert pytest.approx(result) == expected + + +@pytest.mark.parametrize( + "n_pf_coils_in_group, n_pf_group, r_cs_middle, dr_pf_cs_middle_offset, z_tf_inside_half, dr_tf_inboard, z_cs_coil_upper, expected_r, expected_z", + [ + # Single coil, above CS + (np.array([1, 0]), 0, 2.0, 0.1, 5.0, 0.2, 2.5, [2.1], [4.0]), + # Two coils, above CS + (np.array([2, 0]), 0, 3.0, 0.2, 6.0, 0.3, 3.0, [3.2, 3.2], [4.8, -4.8]), + # Single coil, different offset + (np.array([1, 0]), 0, 1.5, 0.05, 4.0, 0.1, 2.0, [1.55], [3.2]), + # Two coils, different offset and heights + (np.array([2, 0]), 0, 2.5, 0.15, 7.0, 0.25, 3.5, [2.65, 2.65], [5.525,-5.525]), + ], +) +def test_place_pf_above_cs( + pfcoil, + n_pf_coils_in_group, + n_pf_group, + r_cs_middle, + dr_pf_cs_middle_offset, + z_tf_inside_half, + dr_tf_inboard, + z_cs_coil_upper, + expected_r, + expected_z, +): + r_array, z_array = pfcoil.place_pf_above_cs( + n_pf_coils_in_group=n_pf_coils_in_group, + n_pf_group=n_pf_group, + r_cs_middle=r_cs_middle, + dr_pf_cs_middle_offset=dr_pf_cs_middle_offset, + z_tf_inside_half=z_tf_inside_half, + dr_tf_inboard=dr_tf_inboard, + z_cs_coil_upper=z_cs_coil_upper, + ) + # Only check the relevant group and number of coils + n_coils = n_pf_coils_in_group[n_pf_group] + assert_array_almost_equal(r_array[n_pf_group, :n_coils], expected_r) + assert_array_almost_equal(z_array[n_pf_group, :n_coils], expected_z) From 3e4e4b4104d6fba66234b4692274c115f746dd72 Mon Sep 17 00:00:00 2001 From: mn3981 Date: Thu, 11 Sep 2025 10:59:43 +0100 Subject: [PATCH 18/20] :recycle: Add unit tests for placing PF coils above TF with various configurations --- tests/unit/test_pfcoil.py | 86 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 85 insertions(+), 1 deletion(-) diff --git a/tests/unit/test_pfcoil.py b/tests/unit/test_pfcoil.py index e661b4ed44..b68e43ffcb 100644 --- a/tests/unit/test_pfcoil.py +++ b/tests/unit/test_pfcoil.py @@ -2115,7 +2115,7 @@ def test_calculate_cs_geometry( # Single coil, different offset (np.array([1, 0]), 0, 1.5, 0.05, 4.0, 0.1, 2.0, [1.55], [3.2]), # Two coils, different offset and heights - (np.array([2, 0]), 0, 2.5, 0.15, 7.0, 0.25, 3.5, [2.65, 2.65], [5.525,-5.525]), + (np.array([2, 0]), 0, 2.5, 0.15, 7.0, 0.25, 3.5, [2.65, 2.65], [5.525, -5.525]), ], ) def test_place_pf_above_cs( @@ -2143,3 +2143,87 @@ def test_place_pf_above_cs( n_coils = n_pf_coils_in_group[n_pf_group] assert_array_almost_equal(r_array[n_pf_group, :n_coils], expected_r) assert_array_almost_equal(z_array[n_pf_group, :n_coils], expected_z) + + +@pytest.mark.parametrize( + "n_pf_coils_in_group, n_pf_group, rmajor, triang, rminor, itart, itartpf, z_tf_inside_half, dz_tf_upper_lower_midplane, z_tf_top, top_bottom, rpf2, zref, expected_r, expected_z, expected_top_bottom", + [ + # Test case 1: ST configuration, coil above midplane + ( + np.array([2, 2]), + 0, + 3.0, + 0.5, + 1.0, + 1, + 0, + 2.0, + 0.5, + 2.5, + 1, + 1.2, + np.array([0.3, 0.4]), + [3.6, 3.6], + [1.7, -1.7], + 1, + ), + # Test case 2: Conventional configuration, coil above and below midplane + ( + np.array([2, 2]), + 1, + 4.0, + 0.3, + 1.5, + 0, + 1, + 2.5, + 0.7, + 3.0, + 1, + 1.0, + np.array([0.2, 0.5]), + [4.45, 4.45], + [3.86, -3.16], + 1, + ), + ], +) +def test_place_pf_above_tf( + pfcoil, + n_pf_coils_in_group, + n_pf_group, + rmajor, + triang, + rminor, + itart, + itartpf, + z_tf_inside_half, + dz_tf_upper_lower_midplane, + z_tf_top, + top_bottom, + rpf2, + zref, + expected_r, + expected_z, + expected_top_bottom, +): + r_arr, z_arr, top_bottom_out = pfcoil.place_pf_above_tf( + n_pf_coils_in_group=n_pf_coils_in_group, + n_pf_group=n_pf_group, + rmajor=rmajor, + triang=triang, + rminor=rminor, + itart=itart, + itartpf=itartpf, + z_tf_inside_half=z_tf_inside_half, + dz_tf_upper_lower_midplane=dz_tf_upper_lower_midplane, + z_tf_top=z_tf_top, + top_bottom=top_bottom, + rpf2=rpf2, + zref=zref, + ) + # Only check the relevant group and number of coils + n_coils = n_pf_coils_in_group[n_pf_group] + assert np.allclose(r_arr[n_pf_group, :n_coils], expected_r) + assert np.allclose(z_arr[n_pf_group, :n_coils], expected_z) + assert top_bottom_out == expected_top_bottom From 1107d769919dc7ac8aedbe2e0421aec62d11c45c Mon Sep 17 00:00:00 2001 From: Timothy Nunn Date: Tue, 23 Sep 2025 15:27:58 +0100 Subject: [PATCH 19/20] Update r/z_pf_coil_middle_group_array in the general case --- process/pfcoil.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/process/pfcoil.py b/process/pfcoil.py index a2331400ac..935f7aa5c5 100644 --- a/process/pfcoil.py +++ b/process/pfcoil.py @@ -320,6 +320,14 @@ def pfcoil(self): rref=pfcoil_variables.rref, ) + for coil in range(pfcoil_variables.n_pf_coils_in_group[group]): + pfcoil_variables.r_pf_coil_middle_group_array[group, coil] = ( + r_pf_coil_middle_group_array[group, coil] + ) + pfcoil_variables.z_pf_coil_middle_group_array[group, coil] = ( + z_pf_coil_middle_group_array[group, coil] + ) + else: raise ProcessValueError( "Illegal i_pf_location value", From 2db8c9632236d77afc3ba302640894f79ad1c2f4 Mon Sep 17 00:00:00 2001 From: mn3981 Date: Tue, 23 Sep 2025 16:17:35 +0100 Subject: [PATCH 20/20] :recycle: Update obsolete variable mappings for PF coil placement offsets --- process/io/obsolete_vars.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/process/io/obsolete_vars.py b/process/io/obsolete_vars.py index f597796750..d661fd9738 100644 --- a/process/io/obsolete_vars.py +++ b/process/io/obsolete_vars.py @@ -425,6 +425,9 @@ "tdmptf": "t_tf_superconductor_quench", "tmaxpro": "temp_tf_conductor_quench_max", "coreradiationfraction": "f_p_plasma_core_rad_reduction", + "routr": "dr_pf_tf_outboard_out_offset", + "rpf1": "dr_pf_cs_middle_offset", + "i_sup_pf_shape": "i_r_pf_outside_tf_placement", } OBS_VARS_HELP = {