From 35bcf2f2456b3c16eecd248c74484fd395b6d0ab Mon Sep 17 00:00:00 2001 From: mn3981 Date: Wed, 20 Aug 2025 16:41:19 +0100 Subject: [PATCH 01/17] =?UTF-8?q?=E2=9C=A8=20Add=20strand=20packing=20func?= =?UTF-8?q?tions=20for=20rectangular=20spaces=20with=20obstacles=20and=20i?= =?UTF-8?q?ntegrate=20into=20TF=20coil=20plotting?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- process/io/plot_proc.py | 214 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 214 insertions(+) diff --git a/process/io/plot_proc.py b/process/io/plot_proc.py index 55756ab44d..a3f1291a43 100644 --- a/process/io/plot_proc.py +++ b/process/io/plot_proc.py @@ -6156,6 +6156,143 @@ def plot_resistive_tf_wp(axis, mfile_data, scan: int, fig) -> None: ) +def calculate_strand_count(shape_area, strand_diameter, void_fraction): + """Calculate number of strands that fit in shape with given void fraction""" + + # Effective area available for strands (excluding voids) + effective_area = shape_area * (1 - void_fraction) + + # Area per strand (circular) + strand_area = np.pi * (strand_diameter / 2) ** 2 + + # Number of strands that fit + n_strands = int(effective_area / strand_area) + + return n_strands + + +def pack_strands_rectangular_with_obstacles( + cable_space_bounds, + pipe_center, + pipe_radius, + strand_diameter, + void_fraction, + axis, + corner_radius=0, +): + """Pack circular strands in rectangular space with cooling pipe obstacle""" + + x, y, width, height = cable_space_bounds + shape_area = width * height + + # Subtract pipe area from total + pipe_area = np.pi * pipe_radius**2 + # Approximate corner area loss + corner_area = 4 * (corner_radius**2 * (1 - np.pi / 4)) if corner_radius > 0 else 0 + + usable_area = shape_area - pipe_area - corner_area + n_strands = calculate_strand_count(usable_area, strand_diameter, void_fraction) + + radius = strand_diameter / 2 + placed_strands = [] + attempts = 0 + max_attempts = 30000 + + pipe_x, pipe_y = pipe_center + + while len(placed_strands) < n_strands and attempts < max_attempts: + # Random position within cable space + candidate_x = np.random.uniform(x + radius, x + width - radius) + candidate_y = np.random.uniform(y + radius, y + height - radius) + + # Check collision with cooling pipe + pipe_distance = np.sqrt( + (candidate_x - pipe_x) ** 2 + (candidate_y - pipe_y) ** 2 + ) + if pipe_distance < (pipe_radius + radius): + attempts += 1 + continue + + # Check collision with corners if rounded + if corner_radius > 0: + corners = [ + (x + corner_radius, y + corner_radius), # bottom-left + (x + width - corner_radius, y + corner_radius), # bottom-right + (x + width - corner_radius, y + height - corner_radius), # top-right + (x + corner_radius, y + height - corner_radius), # top-left + ] + + corner_collision = False + # Check each corner + if candidate_x < corners[0][0] and candidate_y < corners[0][1]: + if ( + np.sqrt( + (candidate_x - corners[0][0]) ** 2 + + (candidate_y - corners[0][1]) ** 2 + ) + > corner_radius - radius + ): + corner_collision = True + elif candidate_x > corners[1][0] and candidate_y < corners[1][1]: + if ( + np.sqrt( + (candidate_x - corners[1][0]) ** 2 + + (candidate_y - corners[1][1]) ** 2 + ) + > corner_radius - radius + ): + corner_collision = True + elif candidate_x > corners[2][0] and candidate_y > corners[2][1]: + if ( + np.sqrt( + (candidate_x - corners[2][0]) ** 2 + + (candidate_y - corners[2][1]) ** 2 + ) + > corner_radius - radius + ): + corner_collision = True + elif candidate_x < corners[3][0] and candidate_y > corners[3][1]: + if ( + np.sqrt( + (candidate_x - corners[3][0]) ** 2 + + (candidate_y - corners[3][1]) ** 2 + ) + > corner_radius - radius + ): + corner_collision = True + + if corner_collision: + attempts += 1 + continue + + # Check collision with existing strands + collision = False + for existing_x, existing_y in placed_strands: + distance = np.sqrt( + (candidate_x - existing_x) ** 2 + (candidate_y - existing_y) ** 2 + ) + if distance < strand_diameter * 1.1: # Small buffer + collision = True + break + + if not collision: + placed_strands.append((candidate_x, candidate_y)) + # Plot the strand + circle = Circle( + (candidate_x, candidate_y), + radius, + facecolor="darkblue", + edgecolor="navy", + linewidth=0.3, + alpha=0.8, + ) + axis.add_patch(circle) + + attempts += 1 + + return len(placed_strands), attempts + + def plot_tf_turn(axis, fig, mfile_data, scan: int) -> None: """ Plots inboard TF coil individual turn structure. @@ -6281,6 +6418,45 @@ def plot_tf_turn(axis, fig, mfile_data, scan: int) -> None: edgecolor="black", ), ) + + # Cable strand packing parameters + strand_diameter = 0.0008 # 2mm diameter strands + void_fraction = 0.3 + #f_a_tf_turn_cable_space_extra_void + + # Cable space bounds + cable_bounds = [ + insulation_thickness + steel_thickness, + insulation_thickness + steel_thickness, + turn_width - 2 * (insulation_thickness + steel_thickness), + turn_width - 2 * (insulation_thickness + steel_thickness), + ] + + # Pack strands if significant void fraction + if void_fraction > 0.001: + n_strands, attempts = pack_strands_rectangular_with_obstacles( + cable_bounds, + (turn_width / 2, turn_width / 2), # pipe_center is the center of the turn + he_pipe_diameter / 2, + strand_diameter, + void_fraction, + axis, + radius_tf_turn_cable_space_corners, + ) + + # Calculate packing efficiency + total_cable_space = cable_bounds[2] * cable_bounds[3] + pipe_area = np.pi * (he_pipe_diameter / 2) ** 2 + corner_area = 4 * (radius_tf_turn_cable_space_corners**2 * (1 - np.pi / 4)) + usable_area = total_cable_space - pipe_area - corner_area + strand_area = np.pi * (strand_diameter / 2) ** 2 + achieved_packing = ( + (n_strands * strand_area) / usable_area if usable_area > 0 else 0 + ) + else: + n_strands = 0 + attempts = 0 + achieved_packing = 0 axis.set_xlim(-turn_width * 0.05, turn_width * 1.05) axis.set_ylim(-turn_width * 0.05, turn_width * 1.05) @@ -6348,6 +6524,44 @@ def plot_tf_turn(axis, fig, mfile_data, scan: int) -> None: ), ) + # Cable strand packing parameters + strand_diameter = 0.002 # 2mm diameter strands + void_fraction = f_a_tf_turn_cable_space_extra_void + + # Cable space bounds + cable_bounds = [ + insulation_thickness + steel_thickness, + insulation_thickness + steel_thickness, + turn_width - 2 * (insulation_thickness + steel_thickness), + turn_height - 2 * (insulation_thickness + steel_thickness), + ] + + # Pack strands if significant void fraction + if void_fraction > 0.001: + n_strands, attempts = pack_strands_rectangular_with_obstacles( + cable_bounds, + pipe_center, + he_pipe_diameter / 2, + strand_diameter, + void_fraction, + axis, + radius_tf_turn_cable_space_corners, + ) + + # Calculate packing efficiency + total_cable_space = cable_bounds[2] * cable_bounds[3] + pipe_area = np.pi * (he_pipe_diameter / 2) ** 2 + corner_area = 4 * (radius_tf_turn_cable_space_corners**2 * (1 - np.pi / 4)) + usable_area = total_cable_space - pipe_area - corner_area + strand_area = np.pi * (strand_diameter / 2) ** 2 + achieved_packing = ( + (n_strands * strand_area) / usable_area if usable_area > 0 else 0 + ) + else: + n_strands = 0 + attempts = 0 + achieved_packing = 0 + axis.set_xlim(-turn_width * 0.05, turn_width * 1.05) axis.set_ylim(-turn_height * 0.05, turn_height * 1.05) From eaaa38835d8f0be837406bd9fb95c922b7dbf14c Mon Sep 17 00:00:00 2001 From: mn3981 Date: Thu, 21 Aug 2025 10:27:56 +0100 Subject: [PATCH 02/17] =?UTF-8?q?=E2=9C=A8=20Enhance=20strand=20packing=20?= =?UTF-8?q?algorithm=20with=20hexagonal=20arrangement=20and=20increase=20m?= =?UTF-8?q?ax=20attempts=20for=20improved=20packing=20efficiency?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- process/io/plot_proc.py | 264 +++++++++++++++++++++++++--------------- 1 file changed, 169 insertions(+), 95 deletions(-) diff --git a/process/io/plot_proc.py b/process/io/plot_proc.py index a3f1291a43..4a114b77c1 100644 --- a/process/io/plot_proc.py +++ b/process/io/plot_proc.py @@ -6192,106 +6192,178 @@ def pack_strands_rectangular_with_obstacles( usable_area = shape_area - pipe_area - corner_area n_strands = calculate_strand_count(usable_area, strand_diameter, void_fraction) + print(n_strands) radius = strand_diameter / 2 placed_strands = [] attempts = 0 - max_attempts = 30000 + max_attempts = 1e5 pipe_x, pipe_y = pipe_center - while len(placed_strands) < n_strands and attempts < max_attempts: - # Random position within cable space - candidate_x = np.random.uniform(x + radius, x + width - radius) - candidate_y = np.random.uniform(y + radius, y + height - radius) - - # Check collision with cooling pipe - pipe_distance = np.sqrt( - (candidate_x - pipe_x) ** 2 + (candidate_y - pipe_y) ** 2 - ) - if pipe_distance < (pipe_radius + radius): - attempts += 1 - continue - - # Check collision with corners if rounded - if corner_radius > 0: - corners = [ - (x + corner_radius, y + corner_radius), # bottom-left - (x + width - corner_radius, y + corner_radius), # bottom-right - (x + width - corner_radius, y + height - corner_radius), # top-right - (x + corner_radius, y + height - corner_radius), # top-left - ] - - corner_collision = False - # Check each corner - if candidate_x < corners[0][0] and candidate_y < corners[0][1]: - if ( - np.sqrt( - (candidate_x - corners[0][0]) ** 2 - + (candidate_y - corners[0][1]) ** 2 - ) - > corner_radius - radius - ): - corner_collision = True - elif candidate_x > corners[1][0] and candidate_y < corners[1][1]: - if ( - np.sqrt( - (candidate_x - corners[1][0]) ** 2 - + (candidate_y - corners[1][1]) ** 2 - ) - > corner_radius - radius - ): - corner_collision = True - elif candidate_x > corners[2][0] and candidate_y > corners[2][1]: - if ( - np.sqrt( - (candidate_x - corners[2][0]) ** 2 - + (candidate_y - corners[2][1]) ** 2 - ) - > corner_radius - radius - ): - corner_collision = True - elif candidate_x < corners[3][0] and candidate_y > corners[3][1]: - if ( - np.sqrt( - (candidate_x - corners[3][0]) ** 2 - + (candidate_y - corners[3][1]) ** 2 - ) - > corner_radius - radius - ): - corner_collision = True - - if corner_collision: - attempts += 1 + # Hexagonal packing parameters + # Calculate the spacing between strand centers for the desired void fraction + # For hexagonal packing, packing fraction = pi/(2*sqrt(3)) ~ 0.9069 + # To achieve a lower packing fraction (higher void fraction), increase spacing + ideal_packing_fraction = np.pi / (2 * np.sqrt(3)) + target_packing_fraction = 1 - void_fraction + spacing_factor = np.sqrt(ideal_packing_fraction / target_packing_fraction) + strand_spacing = strand_diameter * spacing_factor + + # Number of rows and columns that fit in the cable space + n_rows = int((height - 2 * radius) // (strand_spacing * np.sqrt(3) / 2)) + n_cols = int((width - 2 * radius) // strand_spacing) + + # Generate hexagonal grid positions + for row in range(n_rows): + y_pos = (y + radius + row * strand_spacing * np.sqrt(3) / 2) * 1.07 + x_offset = strand_spacing / 2 if row % 2 else 0 + for col in range(n_cols): + candidate_x = (x + radius + col * strand_spacing + x_offset) * 1.05 + candidate_y = y_pos + + # Check if within bounds + if candidate_x > x + width - radius or candidate_y > y + height - radius: continue - # Check collision with existing strands - collision = False - for existing_x, existing_y in placed_strands: - distance = np.sqrt( - (candidate_x - existing_x) ** 2 + (candidate_y - existing_y) ** 2 + # Check collision with cooling pipe + pipe_distance = np.sqrt( + (candidate_x - pipe_x) ** 2 + (candidate_y - pipe_y) ** 2 ) - if distance < strand_diameter * 1.1: # Small buffer - collision = True - break + if pipe_distance < (pipe_radius + radius): + continue - if not collision: - placed_strands.append((candidate_x, candidate_y)) - # Plot the strand - circle = Circle( - (candidate_x, candidate_y), - radius, - facecolor="darkblue", - edgecolor="navy", - linewidth=0.3, - alpha=0.8, - ) - axis.add_patch(circle) + # Check collision with corners if rounded + if corner_radius > 0: + corners = [ + (x + corner_radius, y + corner_radius), # bottom-left + (x + width - corner_radius, y + corner_radius), # bottom-right + ( + x + width - corner_radius, + y + height - corner_radius, + ), # top-right + (x + corner_radius, y + height - corner_radius), # top-left + ] + # Check each corner + corner_collision = False + if candidate_x < corners[0][0] and candidate_y < corners[0][1]: + if ( + np.sqrt( + (candidate_x - corners[0][0]) ** 2 + + (candidate_y - corners[0][1]) ** 2 + ) + > corner_radius - radius + ): + corner_collision = True + elif candidate_x > corners[1][0] and candidate_y < corners[1][1]: + if ( + np.sqrt( + (candidate_x - corners[1][0]) ** 2 + + (candidate_y - corners[1][1]) ** 2 + ) + > corner_radius - radius + ): + corner_collision = True + elif candidate_x > corners[2][0] and candidate_y > corners[2][1]: + if ( + np.sqrt( + (candidate_x - corners[2][0]) ** 2 + + (candidate_y - corners[2][1]) ** 2 + ) + > corner_radius - radius + ): + corner_collision = True + elif candidate_x < corners[3][0] and candidate_y > corners[3][1]: + if ( + np.sqrt( + (candidate_x - corners[3][0]) ** 2 + + (candidate_y - corners[3][1]) ** 2 + ) + > corner_radius - radius + ): + corner_collision = True + if corner_collision: + continue + + # Check collision with existing strands + collision = False + for existing_x, existing_y in placed_strands: + distance = np.sqrt( + (candidate_x - existing_x) ** 2 + (candidate_y - existing_y) ** 2 + ) + if distance < strand_diameter: + collision = True + break + + if not collision: + placed_strands.append((candidate_x, candidate_y)) + # Plot the strand + circle_copper_surrounding = Circle( + (candidate_x, candidate_y), + radius, + facecolor="#b87333", # copper color + edgecolor="#8B4000", # darker copper edge + linewidth=0.1, + alpha=0.8, + ) + axis.add_patch(circle_copper_surrounding) + + circle_central_conductor = Circle( + (candidate_x, candidate_y), + radius / 2, + facecolor="black", + linewidth=0.3, + alpha=0.5, + ) + axis.add_patch(circle_central_conductor) + + if len(placed_strands) >= n_strands: + break + if len(placed_strands) >= n_strands: + break - attempts += 1 + attempts = n_rows * n_cols + print(f"{len(placed_strands)}/ {n_strands}, Attempts:{attempts}") return len(placed_strands), attempts + # # Check collision with existing strands + # collision = False + # for existing_x, existing_y in placed_strands: + # distance = np.sqrt( + # (candidate_x - existing_x) ** 2 + (candidate_y - existing_y) ** 2 + # ) + # if distance < strand_diameter: + # collision = True + # break + + # if not collision: + # placed_strands.append((candidate_x, candidate_y)) + # # Plot the strand + # circle_copper_surrounding = Circle( + # (candidate_x, candidate_y), + # radius, + # facecolor="#b87333", # copper color + # edgecolor="#8B4000", # darker copper edge + # linewidth=0.3, + # alpha=0.8, + # ) + # axis.add_patch(circle_copper_surrounding) + + # circle_central_conductor = Circle( + # (candidate_x, candidate_y), + # radius/2, + # facecolor="black", + # linewidth=0.3, + # alpha=0.5, + # ) + # axis.add_patch(circle_central_conductor) + + # attempts += 1 + # print(f"{len(placed_strands)}/ {n_strands}, Attempts:{attempts}") + + # return len(placed_strands), attempts + def plot_tf_turn(axis, fig, mfile_data, scan: int) -> None: """ @@ -6421,8 +6493,7 @@ def plot_tf_turn(axis, fig, mfile_data, scan: int) -> None: # Cable strand packing parameters strand_diameter = 0.0008 # 2mm diameter strands - void_fraction = 0.3 - #f_a_tf_turn_cable_space_extra_void + void_fraction = 0.5 # Cable space bounds cable_bounds = [ @@ -6435,13 +6506,16 @@ def plot_tf_turn(axis, fig, mfile_data, scan: int) -> None: # Pack strands if significant void fraction if void_fraction > 0.001: n_strands, attempts = pack_strands_rectangular_with_obstacles( - cable_bounds, - (turn_width / 2, turn_width / 2), # pipe_center is the center of the turn - he_pipe_diameter / 2, - strand_diameter, - void_fraction, - axis, - radius_tf_turn_cable_space_corners, + cable_space_bounds=cable_bounds, + pipe_center=( + turn_width / 2, + turn_width / 2, + ), # pipe_center is the center of the turn + pipe_radius=he_pipe_diameter / 2, + strand_diameter=strand_diameter, + void_fraction=void_fraction, + axis=axis, + corner_radius=radius_tf_turn_cable_space_corners, ) # Calculate packing efficiency @@ -6526,7 +6600,7 @@ def plot_tf_turn(axis, fig, mfile_data, scan: int) -> None: # Cable strand packing parameters strand_diameter = 0.002 # 2mm diameter strands - void_fraction = f_a_tf_turn_cable_space_extra_void + void_fraction = 0.9 # Cable space bounds cable_bounds = [ From 2c9421f8cb5543694254ea889be9e4d9d6e6933c Mon Sep 17 00:00:00 2001 From: mn3981 Date: Fri, 3 Oct 2025 18:57:48 +0100 Subject: [PATCH 03/17] =?UTF-8?q?=E2=9C=A8=20Add=20method=20to=20calculate?= =?UTF-8?q?=20maximum=20superconducting=20strand=20count=20in=20cable-in-c?= =?UTF-8?q?onduit=20conductors?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- process/superconducting_tf_coil.py | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/process/superconducting_tf_coil.py b/process/superconducting_tf_coil.py index aa99dda4df..8f482df9bd 100644 --- a/process/superconducting_tf_coil.py +++ b/process/superconducting_tf_coil.py @@ -1315,6 +1315,33 @@ def tf_cable_in_conduit_superconductor_properties( c_turn_cables_critical, ) + def calculate_cable_in_conduit_strand_count( + self, a_cable_space: float, dia_superconductor_strand: float, f_a_void: float + ) -> int: + """ + Calculates the maximum number of superconducting strands that can fit into a cable-in-conduit conductor, + based on the available cable space, strand diameter, and desired void fraction. + + :param a_cable_space: Total cross-sectional area available for the cable (in m²). + :type a_cable_space: float + :param dia_superconductor_strand: Diameter of a single superconducting strand (in meters). + :type dia_superconductor_strand: float + :param f_a_void: Fraction of the cable area to remain void (between 0 and 1). + :type f_a_void: float + + :returns: The maximum number of strands that can fit in the available space, accounting for the void fraction. + :rtype: int + """ + + # Effective area available for strands (excluding voids) + effective_area = a_cable_space * (1 - f_a_void) + + # Area per strand (circular) + strand_area = np.pi * (dia_superconductor_strand / 2) ** 2 + + # Number of strands that fit + return int(effective_area / strand_area) + def output_tf_superconductor_info(self): """Output TF superconductor information""" From c344d15a17afbc9918f7aa65b3ad224360ce2dd1 Mon Sep 17 00:00:00 2001 From: mn3981 Date: Fri, 3 Oct 2025 18:58:54 +0100 Subject: [PATCH 04/17] =?UTF-8?q?=E2=9C=A8=20Add=20variable=20for=20number?= =?UTF-8?q?=20of=20superconducting=20cables=20in=20TF=20turn?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- process/data_structure/superconducting_tf_coil_variables.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/process/data_structure/superconducting_tf_coil_variables.py b/process/data_structure/superconducting_tf_coil_variables.py index 05cbdc7c63..c3524dd236 100644 --- a/process/data_structure/superconducting_tf_coil_variables.py +++ b/process/data_structure/superconducting_tf_coil_variables.py @@ -183,6 +183,9 @@ dia_tf_turn_superconducting_cable: float = None """Diameter of the superconducting cable in the TF turn [m]""" +n_tf_turn_superconducting_cables: int = None +"""Number of superconducting cables in the TF turn""" + j_tf_superconductor_critical: float = None """Critical current density of the superconducting cable [A/m^2]""" @@ -300,6 +303,7 @@ def init_superconducting_tf_coil_variables(): global a_tf_turn_cable_space_effective global dr_tf_wp_no_insulation global dia_tf_turn_superconducting_cable + global n_tf_turn_superconducting_cables global j_tf_superconductor_critical global f_c_tf_turn_operating_critical global j_tf_coil_turn @@ -357,6 +361,7 @@ def init_superconducting_tf_coil_variables(): a_tf_turn_cable_space_effective = 0.0 dr_tf_wp_no_insulation = 0.0 dia_tf_turn_superconducting_cable = 0.0 + n_tf_turn_superconducting_cables = 0 j_tf_superconductor_critical = 0.0 f_c_tf_turn_operating_critical = 0.0 j_tf_coil_turn = 0.0 From 4b531e20d96a094be1b226b07d1d9ffa28d5bd4b Mon Sep 17 00:00:00 2001 From: mn3981 Date: Fri, 3 Oct 2025 19:12:22 +0100 Subject: [PATCH 05/17] =?UTF-8?q?=E2=9C=A8=20Update=20superconducting=20ca?= =?UTF-8?q?ble=20diameter=20and=20integrate=20cable=20count=20calculation?= =?UTF-8?q?=20into=20TF=20coil=20output?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../superconducting_tf_coil_variables.py | 2 +- process/superconducting_tf_coil.py | 9 +++++++++ process/tf_coil.py | 12 ++++++++++++ 3 files changed, 22 insertions(+), 1 deletion(-) diff --git a/process/data_structure/superconducting_tf_coil_variables.py b/process/data_structure/superconducting_tf_coil_variables.py index c3524dd236..e8406d01c8 100644 --- a/process/data_structure/superconducting_tf_coil_variables.py +++ b/process/data_structure/superconducting_tf_coil_variables.py @@ -360,7 +360,7 @@ def init_superconducting_tf_coil_variables(): radius_tf_turn_cable_space_corners = 0.0 a_tf_turn_cable_space_effective = 0.0 dr_tf_wp_no_insulation = 0.0 - dia_tf_turn_superconducting_cable = 0.0 + dia_tf_turn_superconducting_cable = 0.0073 n_tf_turn_superconducting_cables = 0 j_tf_superconductor_critical = 0.0 f_c_tf_turn_operating_critical = 0.0 diff --git a/process/superconducting_tf_coil.py b/process/superconducting_tf_coil.py index 8f482df9bd..81bf777ad5 100644 --- a/process/superconducting_tf_coil.py +++ b/process/superconducting_tf_coil.py @@ -2003,6 +2003,15 @@ def sc_tf_internal_geom(self, i_tf_wp_geom, i_tf_case_geom, i_tf_turns_integer): dx_tf_turn_insulation=tfcoil_variables.dx_tf_turn_insulation, ) + # Calculate number of cables in turn if CICC conductor + # --------------------------------------------------- + if tfcoil_variables.i_tf_sc_mat != 6: + superconducting_tf_coil_variables.n_tf_turn_superconducting_cables = self.calculate_cable_in_conduit_strand_count( + a_cable_space=tfcoil_variables.a_tf_turn_cable_space_no_void, + dia_superconductor_strand=superconducting_tf_coil_variables.dia_tf_turn_superconducting_cable, + f_a_void=tfcoil_variables.f_a_tf_turn_cable_space_extra_void, + ) + # Areas and fractions # ------------------- # Central helium channel down the conductor core [m2] diff --git a/process/tf_coil.py b/process/tf_coil.py index fff7f74344..9432fca46d 100644 --- a/process/tf_coil.py +++ b/process/tf_coil.py @@ -1195,6 +1195,18 @@ def outtf(self): "(dia_tf_turn_coolant_channel)", tfcoil_variables.dia_tf_turn_coolant_channel, ) + po.ovarre( + self.outfile, + "Diameter of superconducting cable", + "(dia_tf_turn_superconducting_cable)", + superconducting_tf_coil_variables.dia_tf_turn_superconducting_cable, + ) + po.ovarre( + self.outfile, + "Number of superconducting cables per turn", + "(n_tf_turn_superconducting_cables)", + superconducting_tf_coil_variables.n_tf_turn_superconducting_cables, + ) po.ocmmnt(self.outfile, "Fractions by area") po.ovarre( self.outfile, From b2846889d33be9add7f0f1b98c6d36d27676aa56 Mon Sep 17 00:00:00 2001 From: mn3981 Date: Fri, 3 Oct 2025 19:14:03 +0100 Subject: [PATCH 06/17] =?UTF-8?q?=E2=9C=A8=20Add=20input=20variable=20for?= =?UTF-8?q?=20superconducting=20cable=20diameter=20in=20TF=20coil=20config?= =?UTF-8?q?uration?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- process/data_structure/superconducting_tf_coil_variables.py | 2 +- process/input.py | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/process/data_structure/superconducting_tf_coil_variables.py b/process/data_structure/superconducting_tf_coil_variables.py index e8406d01c8..1e693253ea 100644 --- a/process/data_structure/superconducting_tf_coil_variables.py +++ b/process/data_structure/superconducting_tf_coil_variables.py @@ -360,7 +360,7 @@ def init_superconducting_tf_coil_variables(): radius_tf_turn_cable_space_corners = 0.0 a_tf_turn_cable_space_effective = 0.0 dr_tf_wp_no_insulation = 0.0 - dia_tf_turn_superconducting_cable = 0.0073 + dia_tf_turn_superconducting_cable = 0.00073 n_tf_turn_superconducting_cables = 0 j_tf_superconductor_critical = 0.0 f_c_tf_turn_operating_critical = 0.0 diff --git a/process/input.py b/process/input.py index 12994f3ddb..fc5b94b8c5 100644 --- a/process/input.py +++ b/process/input.py @@ -477,6 +477,9 @@ def __post_init__(self): "dia_tf_turn_coolant_channel": InputVariable( data_structure.tfcoil_variables, float, range=(0.0, 0.1) ), + "dia_tf_turn_superconducting_cable": InputVariable( + data_structure.superconducting_tf_coil_variables, float, range=(0.0001, 0.01) + ), "dintrt": InputVariable(data_structure.cost_variables, float, range=(0.0, 0.1)), "discount_rate": InputVariable( data_structure.cost_variables, float, range=(0.0, 0.5) From 4c6abd44b7981a396f53ce3695d9e1a99fe67faf Mon Sep 17 00:00:00 2001 From: mn3981 Date: Fri, 3 Oct 2025 19:32:39 +0100 Subject: [PATCH 07/17] =?UTF-8?q?=E2=9C=A8=20Refactor=20TF=20coil=20turn?= =?UTF-8?q?=20plotting=20to=20support=20cable-in-conduit=20configuration?= =?UTF-8?q?=20and=20enhance=20strand=20packing=20logic?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- process/io/plot_proc.py | 419 ++++++++++++++++++++-------------------- 1 file changed, 205 insertions(+), 214 deletions(-) diff --git a/process/io/plot_proc.py b/process/io/plot_proc.py index 4a114b77c1..eca1dd27a7 100644 --- a/process/io/plot_proc.py +++ b/process/io/plot_proc.py @@ -6156,218 +6156,9 @@ def plot_resistive_tf_wp(axis, mfile_data, scan: int, fig) -> None: ) -def calculate_strand_count(shape_area, strand_diameter, void_fraction): - """Calculate number of strands that fit in shape with given void fraction""" - - # Effective area available for strands (excluding voids) - effective_area = shape_area * (1 - void_fraction) - - # Area per strand (circular) - strand_area = np.pi * (strand_diameter / 2) ** 2 - - # Number of strands that fit - n_strands = int(effective_area / strand_area) - - return n_strands - - -def pack_strands_rectangular_with_obstacles( - cable_space_bounds, - pipe_center, - pipe_radius, - strand_diameter, - void_fraction, - axis, - corner_radius=0, -): - """Pack circular strands in rectangular space with cooling pipe obstacle""" - - x, y, width, height = cable_space_bounds - shape_area = width * height - - # Subtract pipe area from total - pipe_area = np.pi * pipe_radius**2 - # Approximate corner area loss - corner_area = 4 * (corner_radius**2 * (1 - np.pi / 4)) if corner_radius > 0 else 0 - - usable_area = shape_area - pipe_area - corner_area - n_strands = calculate_strand_count(usable_area, strand_diameter, void_fraction) - print(n_strands) - - radius = strand_diameter / 2 - placed_strands = [] - attempts = 0 - max_attempts = 1e5 - - pipe_x, pipe_y = pipe_center - - # Hexagonal packing parameters - # Calculate the spacing between strand centers for the desired void fraction - # For hexagonal packing, packing fraction = pi/(2*sqrt(3)) ~ 0.9069 - # To achieve a lower packing fraction (higher void fraction), increase spacing - ideal_packing_fraction = np.pi / (2 * np.sqrt(3)) - target_packing_fraction = 1 - void_fraction - spacing_factor = np.sqrt(ideal_packing_fraction / target_packing_fraction) - strand_spacing = strand_diameter * spacing_factor - - # Number of rows and columns that fit in the cable space - n_rows = int((height - 2 * radius) // (strand_spacing * np.sqrt(3) / 2)) - n_cols = int((width - 2 * radius) // strand_spacing) - - # Generate hexagonal grid positions - for row in range(n_rows): - y_pos = (y + radius + row * strand_spacing * np.sqrt(3) / 2) * 1.07 - x_offset = strand_spacing / 2 if row % 2 else 0 - for col in range(n_cols): - candidate_x = (x + radius + col * strand_spacing + x_offset) * 1.05 - candidate_y = y_pos - - # Check if within bounds - if candidate_x > x + width - radius or candidate_y > y + height - radius: - continue - - # Check collision with cooling pipe - pipe_distance = np.sqrt( - (candidate_x - pipe_x) ** 2 + (candidate_y - pipe_y) ** 2 - ) - if pipe_distance < (pipe_radius + radius): - continue - - # Check collision with corners if rounded - if corner_radius > 0: - corners = [ - (x + corner_radius, y + corner_radius), # bottom-left - (x + width - corner_radius, y + corner_radius), # bottom-right - ( - x + width - corner_radius, - y + height - corner_radius, - ), # top-right - (x + corner_radius, y + height - corner_radius), # top-left - ] - # Check each corner - corner_collision = False - if candidate_x < corners[0][0] and candidate_y < corners[0][1]: - if ( - np.sqrt( - (candidate_x - corners[0][0]) ** 2 - + (candidate_y - corners[0][1]) ** 2 - ) - > corner_radius - radius - ): - corner_collision = True - elif candidate_x > corners[1][0] and candidate_y < corners[1][1]: - if ( - np.sqrt( - (candidate_x - corners[1][0]) ** 2 - + (candidate_y - corners[1][1]) ** 2 - ) - > corner_radius - radius - ): - corner_collision = True - elif candidate_x > corners[2][0] and candidate_y > corners[2][1]: - if ( - np.sqrt( - (candidate_x - corners[2][0]) ** 2 - + (candidate_y - corners[2][1]) ** 2 - ) - > corner_radius - radius - ): - corner_collision = True - elif candidate_x < corners[3][0] and candidate_y > corners[3][1]: - if ( - np.sqrt( - (candidate_x - corners[3][0]) ** 2 - + (candidate_y - corners[3][1]) ** 2 - ) - > corner_radius - radius - ): - corner_collision = True - if corner_collision: - continue - - # Check collision with existing strands - collision = False - for existing_x, existing_y in placed_strands: - distance = np.sqrt( - (candidate_x - existing_x) ** 2 + (candidate_y - existing_y) ** 2 - ) - if distance < strand_diameter: - collision = True - break - - if not collision: - placed_strands.append((candidate_x, candidate_y)) - # Plot the strand - circle_copper_surrounding = Circle( - (candidate_x, candidate_y), - radius, - facecolor="#b87333", # copper color - edgecolor="#8B4000", # darker copper edge - linewidth=0.1, - alpha=0.8, - ) - axis.add_patch(circle_copper_surrounding) - - circle_central_conductor = Circle( - (candidate_x, candidate_y), - radius / 2, - facecolor="black", - linewidth=0.3, - alpha=0.5, - ) - axis.add_patch(circle_central_conductor) - - if len(placed_strands) >= n_strands: - break - if len(placed_strands) >= n_strands: - break - - attempts = n_rows * n_cols - print(f"{len(placed_strands)}/ {n_strands}, Attempts:{attempts}") - - return len(placed_strands), attempts - - # # Check collision with existing strands - # collision = False - # for existing_x, existing_y in placed_strands: - # distance = np.sqrt( - # (candidate_x - existing_x) ** 2 + (candidate_y - existing_y) ** 2 - # ) - # if distance < strand_diameter: - # collision = True - # break - - # if not collision: - # placed_strands.append((candidate_x, candidate_y)) - # # Plot the strand - # circle_copper_surrounding = Circle( - # (candidate_x, candidate_y), - # radius, - # facecolor="#b87333", # copper color - # edgecolor="#8B4000", # darker copper edge - # linewidth=0.3, - # alpha=0.8, - # ) - # axis.add_patch(circle_copper_surrounding) - - # circle_central_conductor = Circle( - # (candidate_x, candidate_y), - # radius/2, - # facecolor="black", - # linewidth=0.3, - # alpha=0.5, - # ) - # axis.add_patch(circle_central_conductor) - - # attempts += 1 - # print(f"{len(placed_strands)}/ {n_strands}, Attempts:{attempts}") - - # return len(placed_strands), attempts - - -def plot_tf_turn(axis, fig, mfile_data, scan: int) -> None: +def plot_tf_cable_in_conduit_turn(axis, fig, mfile_data, scan: int) -> None: """ - Plots inboard TF coil individual turn structure. + Plots inboard TF coil CICC individual turn structure. Author: C. Ashe Parameters @@ -6384,6 +6175,204 @@ def plot_tf_turn(axis, fig, mfile_data, scan: int) -> None: None """ + def _pack_strands_rectangular_with_obstacles( + cable_space_bounds, + pipe_center, + pipe_radius, + strand_diameter, + void_fraction, + n_strands, + axis, + corner_radius=0, + ): + """Pack circular strands in rectangular space with cooling pipe obstacle""" + + x, y, width, height = cable_space_bounds + shape_area = width * height + + # Subtract pipe area from total + pipe_area = np.pi * pipe_radius**2 + # Approximate corner area loss + corner_area = ( + 4 * (corner_radius**2 * (1 - np.pi / 4)) if corner_radius > 0 else 0 + ) + + usable_area = shape_area - pipe_area - corner_area + + radius = strand_diameter / 2 + placed_strands = [] + attempts = 0 + max_attempts = 1e5 + + pipe_x, pipe_y = pipe_center + + # Hexagonal packing parameters + # Calculate the spacing between strand centers for the desired void fraction + # For hexagonal packing, packing fraction = pi/(2*sqrt(3)) ~ 0.9069 + # To achieve a lower packing fraction (higher void fraction), increase spacing + ideal_packing_fraction = np.pi / (2 * np.sqrt(3)) + target_packing_fraction = 1 - void_fraction + spacing_factor = np.sqrt(ideal_packing_fraction / target_packing_fraction) + strand_spacing = strand_diameter * spacing_factor + + # Number of rows and columns that fit in the cable space + n_rows = int((height - 2 * radius) // (strand_spacing * np.sqrt(3) / 2)) + n_cols = int((width - 2 * radius) // strand_spacing) + + # Generate hexagonal grid positions + for row in range(n_rows): + y_pos = (y + radius + row * strand_spacing * np.sqrt(3) / 2) * 1.07 + x_offset = strand_spacing / 2 if row % 2 else 0 + for col in range(n_cols): + candidate_x = (x + radius + col * strand_spacing + x_offset) * 1.05 + candidate_y = y_pos + + # Check if within bounds + if ( + candidate_x > x + width - radius + or candidate_y > y + height - radius + ): + continue + + # Check collision with cooling pipe + pipe_distance = np.sqrt( + (candidate_x - pipe_x) ** 2 + (candidate_y - pipe_y) ** 2 + ) + if pipe_distance < (pipe_radius + radius): + continue + + # Check collision with corners if rounded + if corner_radius > 0: + corners = [ + (x + corner_radius, y + corner_radius), # bottom-left + (x + width - corner_radius, y + corner_radius), # bottom-right + ( + x + width - corner_radius, + y + height - corner_radius, + ), # top-right + (x + corner_radius, y + height - corner_radius), # top-left + ] + # Check each corner + corner_collision = False + if candidate_x < corners[0][0] and candidate_y < corners[0][1]: + if ( + np.sqrt( + (candidate_x - corners[0][0]) ** 2 + + (candidate_y - corners[0][1]) ** 2 + ) + > corner_radius - radius + ): + corner_collision = True + elif candidate_x > corners[1][0] and candidate_y < corners[1][1]: + if ( + np.sqrt( + (candidate_x - corners[1][0]) ** 2 + + (candidate_y - corners[1][1]) ** 2 + ) + > corner_radius - radius + ): + corner_collision = True + elif candidate_x > corners[2][0] and candidate_y > corners[2][1]: + if ( + np.sqrt( + (candidate_x - corners[2][0]) ** 2 + + (candidate_y - corners[2][1]) ** 2 + ) + > corner_radius - radius + ): + corner_collision = True + elif candidate_x < corners[3][0] and candidate_y > corners[3][1]: + if ( + np.sqrt( + (candidate_x - corners[3][0]) ** 2 + + (candidate_y - corners[3][1]) ** 2 + ) + > corner_radius - radius + ): + corner_collision = True + if corner_collision: + continue + + # Check collision with existing strands + collision = False + for existing_x, existing_y in placed_strands: + distance = np.sqrt( + (candidate_x - existing_x) ** 2 + + (candidate_y - existing_y) ** 2 + ) + if distance < strand_diameter: + collision = True + break + + if not collision: + placed_strands.append((candidate_x, candidate_y)) + # Plot the strand + circle_copper_surrounding = Circle( + (candidate_x, candidate_y), + radius, + facecolor="#b87333", # copper color + edgecolor="#8B4000", # darker copper edge + linewidth=0.1, + alpha=0.8, + ) + axis.add_patch(circle_copper_surrounding) + + circle_central_conductor = Circle( + (candidate_x, candidate_y), + radius / 2, + facecolor="black", + linewidth=0.3, + alpha=0.5, + ) + axis.add_patch(circle_central_conductor) + + if len(placed_strands) >= n_strands: + break + if len(placed_strands) >= n_strands: + break + + attempts = n_rows * n_cols + print(f"{len(placed_strands)}/ {n_strands}, Attempts:{attempts}") + + return len(placed_strands), attempts + + # # Check collision with existing strands + # collision = False + # for existing_x, existing_y in placed_strands: + # distance = np.sqrt( + # (candidate_x - existing_x) ** 2 + (candidate_y - existing_y) ** 2 + # ) + # if distance < strand_diameter: + # collision = True + # break + + # if not collision: + # placed_strands.append((candidate_x, candidate_y)) + # # Plot the strand + # circle_copper_surrounding = Circle( + # (candidate_x, candidate_y), + # radius, + # facecolor="#b87333", # copper color + # edgecolor="#8B4000", # darker copper edge + # linewidth=0.3, + # alpha=0.8, + # ) + # axis.add_patch(circle_copper_surrounding) + + # circle_central_conductor = Circle( + # (candidate_x, candidate_y), + # radius/2, + # facecolor="black", + # linewidth=0.3, + # alpha=0.5, + # ) + # axis.add_patch(circle_central_conductor) + + # attempts += 1 + # print(f"{len(placed_strands)}/ {n_strands}, Attempts:{attempts}") + + # return len(placed_strands), attempts + # Import the TF turn variables then multiply into mm i_tf_turns_integer = mfile_data.data["i_tf_turns_integer"].get_scan(scan) # If integer turns switch is on then the turns can have non square dimensions @@ -6505,7 +6494,7 @@ def plot_tf_turn(axis, fig, mfile_data, scan: int) -> None: # Pack strands if significant void fraction if void_fraction > 0.001: - n_strands, attempts = pack_strands_rectangular_with_obstacles( + n_strands, attempts = _pack_strands_rectangular_with_obstacles( cable_space_bounds=cable_bounds, pipe_center=( turn_width / 2, @@ -6516,6 +6505,7 @@ def plot_tf_turn(axis, fig, mfile_data, scan: int) -> None: void_fraction=void_fraction, axis=axis, corner_radius=radius_tf_turn_cable_space_corners, + n_strands=mfile_data.data["n_tf_turn_superconducting_cables"].get_scan(scan), ) # Calculate packing efficiency @@ -6612,7 +6602,7 @@ def plot_tf_turn(axis, fig, mfile_data, scan: int) -> None: # Pack strands if significant void fraction if void_fraction > 0.001: - n_strands, attempts = pack_strands_rectangular_with_obstacles( + n_strands, attempts = _pack_strands_rectangular_with_obstacles( cable_bounds, pipe_center, he_pipe_diameter / 2, @@ -6620,6 +6610,7 @@ def plot_tf_turn(axis, fig, mfile_data, scan: int) -> None: void_fraction, axis, radius_tf_turn_cable_space_corners, + n_strands=mfile_data.data["n_tf_turn_superconducting_cables"].get_scan(scan), ) # Calculate packing efficiency @@ -11683,7 +11674,7 @@ def main_plot( # TF coil turn structure ax20 = fig12.add_subplot(325, aspect="equal") ax20.set_position([0.025, 0.1, 0.3, 0.3]) - plot_tf_turn(ax20, fig12, m_file_data, scan) + plot_tf_cable_in_conduit_turn(ax20, fig12, m_file_data, scan) else: ax19 = fig12.add_subplot(211, aspect="equal") ax19.set_position([0.06, 0.55, 0.675, 0.4]) From d11bfe6ad3b47a8affba0c6a01cd939199113d0c Mon Sep 17 00:00:00 2001 From: mn3981 Date: Fri, 3 Oct 2025 19:50:07 +0100 Subject: [PATCH 08/17] =?UTF-8?q?=E2=9C=A8=20Update=20TF=20cable-in-condui?= =?UTF-8?q?t=20turn=20plotting=20to=20use=20dynamic=20strand=20diameter=20?= =?UTF-8?q?and=20void=20fraction,=20and=20calculate=20inner=20superconduct?= =?UTF-8?q?or=20radius=20based=20on=20copper=20area=20fraction?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- process/io/plot_proc.py | 33 +++++++++++++++++++++++++++------ 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/process/io/plot_proc.py b/process/io/plot_proc.py index eca1dd27a7..5a09fe66d1 100644 --- a/process/io/plot_proc.py +++ b/process/io/plot_proc.py @@ -6183,7 +6183,8 @@ def _pack_strands_rectangular_with_obstacles( void_fraction, n_strands, axis, - corner_radius=0, + corner_radius, + f_a_tf_turn_cable_copper, ): """Pack circular strands in rectangular space with cooling pipe obstacle""" @@ -6219,6 +6220,12 @@ def _pack_strands_rectangular_with_obstacles( n_rows = int((height - 2 * radius) // (strand_spacing * np.sqrt(3) / 2)) n_cols = int((width - 2 * radius) // strand_spacing) + # Calculate the radius of the inner superconductor circle based on the copper area fraction + # Area_superconductor = f_a_tf_turn_cable_copper * Area_strand + # Area_strand = pi * radius^2 + # So, radius_superconductor = sqrt(f_a_tf_turn_cable_copper) * radius + radius_superconductor = np.sqrt(f_a_tf_turn_cable_copper) * radius + # Generate hexagonal grid positions for row in range(n_rows): y_pos = (y + radius + row * strand_spacing * np.sqrt(3) / 2) * 1.07 @@ -6319,7 +6326,7 @@ def _pack_strands_rectangular_with_obstacles( circle_central_conductor = Circle( (candidate_x, candidate_y), - radius / 2, + radius_superconductor, facecolor="black", linewidth=0.3, alpha=0.5, @@ -6481,8 +6488,12 @@ def _pack_strands_rectangular_with_obstacles( ) # Cable strand packing parameters - strand_diameter = 0.0008 # 2mm diameter strands - void_fraction = 0.5 + strand_diameter = mfile_data.data["dia_tf_turn_superconducting_cable"].get_scan( + scan + ) + void_fraction = mfile_data.data["f_a_tf_turn_cable_space_extra_void"].get_scan( + scan + ) # Cable space bounds cable_bounds = [ @@ -6505,7 +6516,12 @@ def _pack_strands_rectangular_with_obstacles( void_fraction=void_fraction, axis=axis, corner_radius=radius_tf_turn_cable_space_corners, - n_strands=mfile_data.data["n_tf_turn_superconducting_cables"].get_scan(scan), + n_strands=mfile_data.data["n_tf_turn_superconducting_cables"].get_scan( + scan + ), + f_a_tf_turn_cable_copper=mfile_data.data[ + "f_a_tf_turn_cable_copper" + ].get_scan(scan), ) # Calculate packing efficiency @@ -6610,7 +6626,12 @@ def _pack_strands_rectangular_with_obstacles( void_fraction, axis, radius_tf_turn_cable_space_corners, - n_strands=mfile_data.data["n_tf_turn_superconducting_cables"].get_scan(scan), + n_strands=mfile_data.data["n_tf_turn_superconducting_cables"].get_scan( + scan + ), + f_a_tf_turn_cable_copper=mfile_data.data[ + "f_a_tf_turn_cable_copper" + ].get_scan(scan), ) # Calculate packing efficiency From b47fb89b6c5fb5ed47cf25f683db12cb177836b9 Mon Sep 17 00:00:00 2001 From: mn3981 Date: Mon, 6 Oct 2025 13:36:40 +0100 Subject: [PATCH 09/17] =?UTF-8?q?=E2=9C=A8=20Adjust=20TF=20cable-in-condui?= =?UTF-8?q?t=20turn=20plot=20positions=20and=20update=20subplot=20configur?= =?UTF-8?q?ations=20for=20improved=20visualization?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- process/io/plot_proc.py | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/process/io/plot_proc.py b/process/io/plot_proc.py index 5a09fe66d1..29183f153f 100644 --- a/process/io/plot_proc.py +++ b/process/io/plot_proc.py @@ -6662,8 +6662,8 @@ def _pack_strands_rectangular_with_obstacles( ) axis.text( - 0.3, - 0.425, + 0.4, + 0.9, textstr_turn_insulation, fontsize=9, verticalalignment="top", @@ -6684,8 +6684,8 @@ def _pack_strands_rectangular_with_obstacles( ) axis.text( - 0.3, - 0.36, + 0.55, + 0.9, textstr_turn_steel, fontsize=9, verticalalignment="top", @@ -6723,8 +6723,8 @@ def _pack_strands_rectangular_with_obstacles( ) axis.text( - 0.3, - 0.28, + 0.5, + 0.7, textstr_turn_cable_space, fontsize=9, verticalalignment="top", @@ -6746,8 +6746,8 @@ def _pack_strands_rectangular_with_obstacles( ) axis.text( - 0.3, - 0.125, + 0.45, + 0.8, textstr_turn_cooling, fontsize=9, verticalalignment="top", @@ -6778,8 +6778,8 @@ def _pack_strands_rectangular_with_obstacles( f"Required maxium WP current \ndensity for heat protection:\n{mfile_data.data['j_tf_wp_quench_heat_max'].get_scan(scan):.2e} A/m$^2$\n" ) axis.text( - 0.55, - 0.425, + 0.75, + 0.9, textstr_superconductor, fontsize=9, verticalalignment="top", @@ -7171,7 +7171,7 @@ def plot_physics_info(axis, mfile_data, scan): ("beta_norm_toroidal", r"$\beta_N$, toroidal", "% m T MA$^{-1}$"), ("beta_thermal_poloidal_vol_avg", r"$\beta_P$, thermal", ""), ("beta_poloidal", r"$\beta_P$, total", ""), - ("te", r"$\langle T_e \rangle$", "keV"), + ("temp_plasma_electron_vol_avg_kev", r"$\langle T_e \rangle$", "keV"), ("nd_plasma_electrons_vol_avg", r"$\langle n_e \rangle$", "m$^{-3}$"), (nong, r"$\langle n_{\mathrm{e,line}} \rangle \ / \ n_G$", ""), (tepeak, r"$T_{e0} \ / \ \langle T_e \rangle$", ""), @@ -11688,13 +11688,18 @@ def main_plot( # Can only plot WP and turn structure if superconducting coil at the moment if m_file_data.data["i_tf_sup"].get_scan(scan) == 1: # TF coil with WP - ax19 = fig12.add_subplot(231, aspect="equal") - ax19.set_position([0.025, 0.5, 0.45, 0.45]) + ax19 = fig12.add_subplot(221, aspect="equal") + ax19.set_position([ + 0.025, + 0.45, + 0.5, + 0.5, + ]) # Half height, a bit wider, top left plot_superconducting_tf_wp(ax19, m_file_data, scan, fig12) # TF coil turn structure ax20 = fig12.add_subplot(325, aspect="equal") - ax20.set_position([0.025, 0.1, 0.3, 0.3]) + ax20.set_position([0.025, 0.5, 0.4, 0.4]) plot_tf_cable_in_conduit_turn(ax20, fig12, m_file_data, scan) else: ax19 = fig12.add_subplot(211, aspect="equal") From ae61c499106eb4037212437c661b62b697568b3e Mon Sep 17 00:00:00 2001 From: mn3981 Date: Mon, 6 Oct 2025 14:14:46 +0100 Subject: [PATCH 10/17] =?UTF-8?q?=E2=9C=A8=20Add=20function=20to=20plot=20?= =?UTF-8?q?TF=20coil=20CICC=20cable=20cross-section=20and=20integrate=20in?= =?UTF-8?q?to=20main=20plotting=20routine?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- process/io/plot_proc.py | 80 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) diff --git a/process/io/plot_proc.py b/process/io/plot_proc.py index 29183f153f..80419d4c20 100644 --- a/process/io/plot_proc.py +++ b/process/io/plot_proc.py @@ -6794,6 +6794,83 @@ def _pack_strands_rectangular_with_obstacles( ) +def plot_cable_in_conduit_cable(axis, fig, mfile_data, scan: int) -> None: + """ + Plots TF coil CICC cable cross-section. + """ + + dia_tf_turn_superconducting_cable = mfile_data.data[ + "dia_tf_turn_superconducting_cable" + ].get_scan(scan) + f_a_tf_turn_cable_copper = mfile_data.data["f_a_tf_turn_cable_copper"].get_scan( + scan + ) + + # Convert to mm + dia_mm = dia_tf_turn_superconducting_cable * 1000 + radius_superconductor_mm = np.sqrt(f_a_tf_turn_cable_copper) * (dia_mm / 2) + + # Draw the outer copper circle + circle_copper_surrounding = patches.Circle( + (0, 0), + dia_mm / 2, + facecolor="#b87333", # copper color + edgecolor="#8B4000", # darker copper edge + linewidth=0.1, + alpha=0.8, + label="Copper", + zorder=1, + ) + axis.add_patch(circle_copper_surrounding) + + # Draw the inner superconductor circle + circle_central_conductor = patches.Circle( + (0, 0), + radius_superconductor_mm, + facecolor="black", + linewidth=0.3, + alpha=0.7, + label="Superconductor", + zorder=2, + ) + axis.add_patch(circle_central_conductor) + + # Convert cable diameter to mm + cable_diameter_mm = ( + mfile_data.data["dia_tf_turn_superconducting_cable"].get_scan(scan) * 1000 + ) + textstr_cable = ( + f"$\\mathbf{{Cable:}}$\n \n" + f"Cable diameter: {cable_diameter_mm:.4f} mm\n" + f"Copper area fraction: {mfile_data.data['f_a_tf_turn_cable_copper'].get_scan(scan):.4f}\n" + ) + axis.text( + 0.4, + 0.3, + textstr_cable, + fontsize=9, + verticalalignment="top", + horizontalalignment="left", + transform=fig.transFigure, + bbox={ + "boxstyle": "round", + "facecolor": "#6dd3f7", # light blue for superconductors + "alpha": 1.0, + "linewidth": 2, + }, + ) + + axis.set_aspect("equal") + axis.set_xlim(-dia_mm / 1.5, dia_mm / 1.5) + axis.set_ylim(-dia_mm / 1.5, dia_mm / 1.5) + axis.set_title("TF CICC Cable Cross-Section") + axis.minorticks_on() + axis.legend(loc="upper right") + axis.grid(True, which="both", linestyle="--", linewidth=0.5, alpha=0.2) + axis.set_xlabel("X [mm]") + axis.set_ylabel("Y [mm]") + + def plot_pf_coils(axis, mfile_data, scan, colour_scheme): """Function to plot PF coils @@ -11701,6 +11778,9 @@ def main_plot( ax20 = fig12.add_subplot(325, aspect="equal") ax20.set_position([0.025, 0.5, 0.4, 0.4]) plot_tf_cable_in_conduit_turn(ax20, fig12, m_file_data, scan) + plot_205 = fig10.add_subplot(223, aspect="equal") + plot_205.set_position([0.075, 0.1, 0.3, 0.3]) + plot_cable_in_conduit_cable(plot_205, fig10, m_file_data, scan) else: ax19 = fig12.add_subplot(211, aspect="equal") ax19.set_position([0.06, 0.55, 0.675, 0.4]) From 4d90b0e1898b90924aaa4bf4bbb581141fbf26bc Mon Sep 17 00:00:00 2001 From: mn3981 Date: Mon, 6 Oct 2025 14:37:39 +0100 Subject: [PATCH 11/17] =?UTF-8?q?=E2=9C=A8=20Add=20method=20to=20calculate?= =?UTF-8?q?=20total=20length=20of=20superconducting=20material=20for=20TF?= =?UTF-8?q?=20coils=20and=20update=20cable-in-conduit=20parameters?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- process/io/plot_proc.py | 5 +---- process/superconducting_tf_coil.py | 29 +++++++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/process/io/plot_proc.py b/process/io/plot_proc.py index 80419d4c20..32130e7d23 100644 --- a/process/io/plot_proc.py +++ b/process/io/plot_proc.py @@ -6604,10 +6604,6 @@ def _pack_strands_rectangular_with_obstacles( ), ) - # Cable strand packing parameters - strand_diameter = 0.002 # 2mm diameter strands - void_fraction = 0.9 - # Cable space bounds cable_bounds = [ insulation_thickness + steel_thickness, @@ -6843,6 +6839,7 @@ def plot_cable_in_conduit_cable(axis, fig, mfile_data, scan: int) -> None: f"$\\mathbf{{Cable:}}$\n \n" f"Cable diameter: {cable_diameter_mm:.4f} mm\n" f"Copper area fraction: {mfile_data.data['f_a_tf_turn_cable_copper'].get_scan(scan):.4f}\n" + f"Number of strands per turn: {int(mfile_data.data['n_tf_turn_superconducting_cables'].get_scan(scan))}\n" ) axis.text( 0.4, diff --git a/process/superconducting_tf_coil.py b/process/superconducting_tf_coil.py index 81bf777ad5..f5040c5264 100644 --- a/process/superconducting_tf_coil.py +++ b/process/superconducting_tf_coil.py @@ -1342,6 +1342,35 @@ def calculate_cable_in_conduit_strand_count( # Number of strands that fit return int(effective_area / strand_area) + def calculate_cable_in_conduit_superconductor_length( + self, + n_tf_coils: int, + n_tf_coil_turns: int, + len_tf_coil: float, + n_tf_turn_superconducting_cables: int, + ) -> float: + """ + Calculates the total length of superconducting material required for the TF coils. + + :param int n_tf_coils: Number of TF coils. + :param int n_tf_coil_turns: Total number of turns in the TF coil winding pack. + :param float len_tf_coil: Length of a single TF coil (in meters). + :param int n_tf_turn_superconducting_cables: Number of superconducting cables per turn in the TF coil. + + :returns: Total length of superconducting material required (in meters). + :rtype: float + """ + + # Length of superconductor in one TF coil + len_tf_coil_superconductor = ( + n_tf_coil_turns * len_tf_coil * n_tf_turn_superconducting_cables + ) + + # Total length of superconductor in all TF coils + len_tf_superconductor_total = len_tf_coil_superconductor * n_tf_coils + + return len_tf_coil_superconductor, len_tf_superconductor_total + def output_tf_superconductor_info(self): """Output TF superconductor information""" From 8259e1b3be0e9cf3dce85cde5952844a72bf5a21 Mon Sep 17 00:00:00 2001 From: mn3981 Date: Mon, 6 Oct 2025 14:38:48 +0100 Subject: [PATCH 12/17] =?UTF-8?q?=E2=9C=A8=20Add=20variable=20to=20store?= =?UTF-8?q?=20length=20of=20superconducting=20cable=20in=20TF=20coil=20and?= =?UTF-8?q?=20update=20initialization=20function?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- process/data_structure/superconducting_tf_coil_variables.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/process/data_structure/superconducting_tf_coil_variables.py b/process/data_structure/superconducting_tf_coil_variables.py index 1e693253ea..3e7f53f77e 100644 --- a/process/data_structure/superconducting_tf_coil_variables.py +++ b/process/data_structure/superconducting_tf_coil_variables.py @@ -186,6 +186,9 @@ n_tf_turn_superconducting_cables: int = None """Number of superconducting cables in the TF turn""" +len_tf_coil_superconductor: float = None +"""Length of superconducting cable in one TF coil [m]""" + j_tf_superconductor_critical: float = None """Critical current density of the superconducting cable [A/m^2]""" @@ -304,6 +307,7 @@ def init_superconducting_tf_coil_variables(): global dr_tf_wp_no_insulation global dia_tf_turn_superconducting_cable global n_tf_turn_superconducting_cables + global len_tf_coil_superconductor global j_tf_superconductor_critical global f_c_tf_turn_operating_critical global j_tf_coil_turn @@ -362,6 +366,7 @@ def init_superconducting_tf_coil_variables(): dr_tf_wp_no_insulation = 0.0 dia_tf_turn_superconducting_cable = 0.00073 n_tf_turn_superconducting_cables = 0 + len_tf_coil_superconductor = 0.0 j_tf_superconductor_critical = 0.0 f_c_tf_turn_operating_critical = 0.0 j_tf_coil_turn = 0.0 From 108077b412dd042b8bfae4c28440e5b14b91d476 Mon Sep 17 00:00:00 2001 From: mn3981 Date: Mon, 6 Oct 2025 14:39:43 +0100 Subject: [PATCH 13/17] =?UTF-8?q?=E2=9C=A8=20Add=20variable=20for=20total?= =?UTF-8?q?=20length=20of=20superconducting=20cable=20in=20all=20TF=20coil?= =?UTF-8?q?s=20and=20update=20initialization=20function?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- process/data_structure/superconducting_tf_coil_variables.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/process/data_structure/superconducting_tf_coil_variables.py b/process/data_structure/superconducting_tf_coil_variables.py index 3e7f53f77e..af280c4243 100644 --- a/process/data_structure/superconducting_tf_coil_variables.py +++ b/process/data_structure/superconducting_tf_coil_variables.py @@ -189,6 +189,9 @@ len_tf_coil_superconductor: float = None """Length of superconducting cable in one TF coil [m]""" +len_tf_superconductor_total: float = None +"""Total length of superconducting cable in all TF coils [m]""" + j_tf_superconductor_critical: float = None """Critical current density of the superconducting cable [A/m^2]""" @@ -308,6 +311,7 @@ def init_superconducting_tf_coil_variables(): global dia_tf_turn_superconducting_cable global n_tf_turn_superconducting_cables global len_tf_coil_superconductor + global len_tf_superconductor_total global j_tf_superconductor_critical global f_c_tf_turn_operating_critical global j_tf_coil_turn @@ -367,6 +371,7 @@ def init_superconducting_tf_coil_variables(): dia_tf_turn_superconducting_cable = 0.00073 n_tf_turn_superconducting_cables = 0 len_tf_coil_superconductor = 0.0 + len_tf_superconductor_total = 0.0 j_tf_superconductor_critical = 0.0 f_c_tf_turn_operating_critical = 0.0 j_tf_coil_turn = 0.0 From 4bb2a2738e9ee5398f20b756934197a24c5c66b5 Mon Sep 17 00:00:00 2001 From: mn3981 Date: Mon, 6 Oct 2025 15:09:20 +0100 Subject: [PATCH 14/17] =?UTF-8?q?=E2=9C=A8=20Add=20calculations=20for=20le?= =?UTF-8?q?ngths=20of=20superconductors=20in=20TF=20coils=20and=20log=20ou?= =?UTF-8?q?tputs=20for=20superconductor=20lengths?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- process/io/plot_proc.py | 114 ++++++++++++++++------------- process/superconducting_tf_coil.py | 10 +++ process/tf_coil.py | 12 +++ 3 files changed, 84 insertions(+), 52 deletions(-) diff --git a/process/io/plot_proc.py b/process/io/plot_proc.py index 32130e7d23..cebd7394db 100644 --- a/process/io/plot_proc.py +++ b/process/io/plot_proc.py @@ -433,7 +433,7 @@ def plot_main_power_flow( 0.22, 0.81, "$P_{\\alpha,{loss}}$\n" - f"{mfile_data.data['p_fw_alpha_mw'].get_scan(scan):.2f} MW", + f"{mfile_data.data['p_fw_alpha_mw'].get_scan(scan):,.2f} MW", transform=fig.transFigure, horizontalalignment="left", verticalalignment="bottom", @@ -445,7 +445,7 @@ def plot_main_power_flow( axis.text( 0.22, 0.69, - f"$P_{{{{rad}}}}$\n{mfile_data.data['p_plasma_rad_mw'].get_scan(scan):.2f} MW", + f"$P_{{{{rad}}}}$\n{mfile_data.data['p_plasma_rad_mw'].get_scan(scan):,.2f} MW", transform=fig.transFigure, horizontalalignment="left", verticalalignment="bottom", @@ -484,7 +484,7 @@ def plot_main_power_flow( axis.text( 0.22, 0.63, - f"$P_{{{{sep}}}}$\n{mfile_data.data['p_plasma_separatrix_mw'].get_scan(scan):.2f} MW", + f"$P_{{{{sep}}}}$\n{mfile_data.data['p_plasma_separatrix_mw'].get_scan(scan):,.2f} MW", transform=fig.transFigure, horizontalalignment="left", verticalalignment="bottom", @@ -599,7 +599,7 @@ def plot_main_power_flow( axis.text( 0.37, 0.775, - f"$P_{{\\text{{neutron}}}}$:\n{mfile_data.data['p_neutron_total_mw'].get_scan(scan):.2f} MW", + f"$P_{{\\text{{neutron}}}}$:\n{mfile_data.data['p_neutron_total_mw'].get_scan(scan):,.2f} MW", fontsize=9, verticalalignment="bottom", horizontalalignment="left", @@ -902,7 +902,7 @@ def plot_main_power_flow( axis.text( 0.325, 0.225, - f"\n\nTotal Low Grade Secondary Heat\n\n {mfile_data.data['p_plant_secondary_heat_mw'].get_scan(scan):.2f} MWth", + f"\n\nTotal Low Grade Secondary Heat\n\n {mfile_data.data['p_plant_secondary_heat_mw'].get_scan(scan):,.2f} MWth", fontsize=9, verticalalignment="bottom", horizontalalignment="center", @@ -939,7 +939,7 @@ def plot_main_power_flow( axis.text( 0.9, 0.25, - f"$P_{{\\text{{primary,thermal}}}}$:\n{mfile_data.data['p_plant_primary_heat_mw'].get_scan(scan):.2f} MW \n$\\eta_{{\\text{{turbine}}}}$: {mfile_data.data['eta_turbine'].get_scan(scan):.3f}", + f"$P_{{\\text{{primary,thermal}}}}$:\n{mfile_data.data['p_plant_primary_heat_mw'].get_scan(scan):,.2f} MW \n$\\eta_{{\\text{{turbine}}}}$: {mfile_data.data['eta_turbine'].get_scan(scan):.3f}", fontsize=9, verticalalignment="bottom", horizontalalignment="left", @@ -1068,7 +1068,7 @@ def plot_main_power_flow( axis.text( 0.68, 0.15, - f"$P_{{\\text{{gross}}}}$:\n{mfile_data.data['p_plant_electric_gross_mw'].get_scan(scan):.2f} MWe", + f"$P_{{\\text{{gross}}}}$:\n{mfile_data.data['p_plant_electric_gross_mw'].get_scan(scan):,.2f} MWe", fontsize=9, verticalalignment="bottom", horizontalalignment="left", @@ -1100,7 +1100,7 @@ def plot_main_power_flow( axis.text( 0.875, 0.05, - f"$P_{{\\text{{loss}}}}$:\n{mfile_data.data['p_turbine_loss_mw'].get_scan(scan):.2f} MWth", + f"$P_{{\\text{{loss}}}}$:\n{mfile_data.data['p_turbine_loss_mw'].get_scan(scan):,.2f} MWth", fontsize=9, verticalalignment="bottom", horizontalalignment="left", @@ -1133,7 +1133,7 @@ def plot_main_power_flow( axis.text( 0.68, 0.05, - f"$P_{{\\text{{net,electric}}}}$:\n{mfile_data.data['p_plant_electric_net_mw'].get_scan(scan):.2f} MWe", + f"$P_{{\\text{{net,electric}}}}$:\n{mfile_data.data['p_plant_electric_net_mw'].get_scan(scan):,.2f} MWe", fontsize=9, verticalalignment="bottom", horizontalalignment="left", @@ -1151,8 +1151,8 @@ def plot_main_power_flow( 0.575, 0.14, ( - f"$P_{{\\text{{recirc,electric}}}}$:\n{mfile_data.data['p_plant_electric_recirc_mw'].get_scan(scan):.2f} MWe\n" - f"$f_{{\\text{{recirc}}}}$:\n{mfile_data.data['f_p_plant_electric_recirc'].get_scan(scan):.2f}" + f"$P_{{\\text{{recirc,electric}}}}$:\n{mfile_data.data['p_plant_electric_recirc_mw'].get_scan(scan):,.2f} MWe\n" + f"$f_{{\\text{{recirc}}}}$:\n{mfile_data.data['f_p_plant_electric_recirc'].get_scan(scan):,.2f}" ), fontsize=9, verticalalignment="bottom", @@ -1306,7 +1306,7 @@ def plot_main_power_flow( axis.text( 0.46, 0.775, - f"$P_{{\\text{{FW,nuclear}}}}$:\n{mfile_data.data['p_fw_nuclear_heat_total_mw'].get_scan(scan):.2f} MW", + f"$P_{{\\text{{FW,nuclear}}}}$:\n{mfile_data.data['p_fw_nuclear_heat_total_mw'].get_scan(scan):,.2f} MW", fontsize=9, verticalalignment="bottom", horizontalalignment="left", @@ -1323,7 +1323,7 @@ def plot_main_power_flow( axis.text( 0.46, 0.71, - f"$P_{{\\text{{FW,rad}}}}$:\n{mfile_data.data['p_fw_rad_total_mw'].get_scan(scan):.2f} MW", + f"$P_{{\\text{{FW,rad}}}}$:\n{mfile_data.data['p_fw_rad_total_mw'].get_scan(scan):,.2f} MW", fontsize=9, verticalalignment="bottom", horizontalalignment="left", @@ -1385,7 +1385,7 @@ def plot_main_power_flow( axis.text( 0.5, 0.555, - f"Primary thermal\n(inc pump): {mfile_data.data['p_fw_heat_deposited_mw'].get_scan(scan):.2f} MWth", + f"Primary thermal\n(inc pump): {mfile_data.data['p_fw_heat_deposited_mw'].get_scan(scan):,.2f} MWth", fontsize=9, verticalalignment="bottom", horizontalalignment="left", @@ -1401,7 +1401,7 @@ def plot_main_power_flow( axis.text( 0.7, 0.555, - f"Primary thermal\n(inc pump): {mfile_data.data['p_blkt_heat_deposited_mw'].get_scan(scan):.2f} MWth", + f"Primary thermal\n(inc pump): {mfile_data.data['p_blkt_heat_deposited_mw'].get_scan(scan):,.2f} MWth", fontsize=9, verticalalignment="bottom", horizontalalignment="left", @@ -1508,7 +1508,7 @@ def plot_main_power_flow( axis.text( 0.6, 0.49, - f"Primary thermal (inc pump): {mfile_data.data['p_fw_blkt_heat_deposited_mw'].get_scan(scan):.2f} MWth\n", + f"Primary thermal (inc pump): {mfile_data.data['p_fw_blkt_heat_deposited_mw'].get_scan(scan):,.2f} MWth\n", fontsize=9, verticalalignment="bottom", horizontalalignment="left", @@ -1549,9 +1549,9 @@ def plot_main_power_flow( 0.625, 0.775, ( - f"$P_{{\\text{{Blkt,nuclear}}}}$:\n{mfile_data.data['p_blkt_nuclear_heat_total_mw'].get_scan(scan):.2f} MW \n" - f"$P_{{\\text{{Blkt,multiplication}}}}$:\n{mfile_data.data['p_blkt_multiplication_mw'].get_scan(scan):.2f} MW\n" - f"$f_{{\\text{{multiplication}}}}$:\n{mfile_data.data['f_p_blkt_multiplication'].get_scan(scan):.2f}" + f"$P_{{\\text{{Blkt,nuclear}}}}$:\n{mfile_data.data['p_blkt_nuclear_heat_total_mw'].get_scan(scan):,.2f} MW \n" + f"$P_{{\\text{{Blkt,multiplication}}}}$:\n{mfile_data.data['p_blkt_multiplication_mw'].get_scan(scan):,.2f} MW\n" + f"$f_{{\\text{{multiplication}}}}$:\n{mfile_data.data['f_p_blkt_multiplication'].get_scan(scan):,.2f}" ), fontsize=9, verticalalignment="bottom", @@ -1592,7 +1592,7 @@ def plot_main_power_flow( axis.text( 0.38, 0.375, - f"$P_{{\\text{{shld,secondary}}}}$:\n{mfile_data.data['p_shld_secondary_heat_mw'].get_scan(scan):.2f} MWth", + f"$P_{{\\text{{shld,secondary}}}}$:\n{mfile_data.data['p_shld_secondary_heat_mw'].get_scan(scan):,.2f} MWth", fontsize=9, verticalalignment="bottom", horizontalalignment="left", @@ -1684,7 +1684,7 @@ def plot_main_power_flow( axis.text( 0.29, 0.57, - f"$P_{{\\text{{div,rad}}}}$:\n{mfile_data.data['p_div_rad_total_mw'].get_scan(scan):.2f} MW", + f"$P_{{\\text{{div,rad}}}}$:\n{mfile_data.data['p_div_rad_total_mw'].get_scan(scan):,.2f} MW", fontsize=9, verticalalignment="bottom", horizontalalignment="left", @@ -1701,7 +1701,7 @@ def plot_main_power_flow( axis.text( 0.4, 0.58, - f"$P_{{\\text{{div,nuclear}}}}$:\n{mfile_data.data['p_div_nuclear_heat_total_mw'].get_scan(scan):.2f} MW", + f"$P_{{\\text{{div,nuclear}}}}$:\n{mfile_data.data['p_div_nuclear_heat_total_mw'].get_scan(scan):,.2f} MW", fontsize=9, verticalalignment="bottom", horizontalalignment="left", @@ -2510,9 +2510,9 @@ def plot_main_plasma_information( f"$\\kappa_{{95}}$: {mfile_data.data['kappa95'].get_scan(scan):.2f} | $\\delta_{{95}}$: {mfile_data.data['triang95'].get_scan(scan):.2f} | $\\zeta$: {mfile_data.data['plasma_square'].get_scan(scan):.2f}\n" f"A: {mfile_data.data['aspect'].get_scan(scan):.2f}\n" f"$ V_{{\\text{{p}}}}:$ {mfile_data.data['vol_plasma'].get_scan(scan):.2f}$ \\ \\text{{m}}^3$\n" - f"$ A_{{\\text{{p,surface}}}}:$ {mfile_data.data['a_plasma_surface'].get_scan(scan):.2f}$ \\ \\text{{m}}^2$\n" - f"$ A_{{\\text{{p_poloidal}}}}:$ {mfile_data.data['a_plasma_poloidal'].get_scan(scan):.2f}$ \\ \\text{{m}}^2$\n" - f"$ L_{{\\text{{p_poloidal}}}}:$ {mfile_data.data['len_plasma_poloidal'].get_scan(scan):.2f}$ \\ \\text{{m}}$" + f"$ A_{{\\text{{p,surface}}}}:$ {mfile_data.data['a_plasma_surface'].get_scan(scan):,.2f}$ \\ \\text{{m}}^2$\n" + f"$ A_{{\\text{{p_poloidal}}}}:$ {mfile_data.data['a_plasma_poloidal'].get_scan(scan):,.2f}$ \\ \\text{{m}}^2$\n" + f"$ L_{{\\text{{p_poloidal}}}}:$ {mfile_data.data['len_plasma_poloidal'].get_scan(scan):,.2f}$ \\ \\text{{m}}$" ) axis.text( @@ -2679,7 +2679,7 @@ def plot_main_plasma_information( f" - Volt-seconds needed for burn: {mfile_data.data['vs_plasma_burn_required'].get_scan(scan):.4f} Vs\n" f"$V_{{\\text{{loop}}}}$: {mfile_data.data['v_plasma_loop_burn'].get_scan(scan):.4f} V\n" f"$\\Omega_{{\\text{{p}}}}$: {mfile_data.data['res_plasma'].get_scan(scan):.4e} $\\Omega$\n" - f"Plasma resistive diffusion time: {mfile_data.data['t_plasma_res_diffusion'].get_scan(scan):.4f} s\n" + f"Plasma resistive diffusion time: {mfile_data.data['t_plasma_res_diffusion'].get_scan(scan):,.4f} s\n" f"Plasma inductance: {mfile_data.data['ind_plasma'].get_scan(scan):.4e} H\n" f"Plasma stored magnetic energy: {mfile_data.data['e_plasma_magnetic_stored'].get_scan(scan) / 1e9:.4f} GJ\n" f"Plasma normalised internal inductance: {mfile_data.data['ind_plasma_internal_norm'].get_scan(scan):.4f}" @@ -2875,11 +2875,11 @@ def plot_main_plasma_information( f"$\\mathbf{{Fusion \\ Reactions:}}$\n \n" f"Fuel mixture: \n" f"| D: {mfile_data.data['f_plasma_fuel_deuterium'].get_scan(scan):.2f} | T: {mfile_data.data['f_plasma_fuel_tritium'].get_scan(scan):.2f} | 3He: {mfile_data.data['f_plasma_fuel_helium3'].get_scan(scan):.2f} |\n\n" - f"Fusion Power, $P_{{\\text{{fus}}}}:$ {mfile_data.data['p_fusion_total_mw'].get_scan(scan):.2f} MW\n" - f"D-T Power, $P_{{\\text{{fus,DT}}}}:$ {mfile_data.data['p_dt_total_mw'].get_scan(scan):.2f} MW\n" - f"D-D Power, $P_{{\\text{{fus,DD}}}}:$ {mfile_data.data['p_dd_total_mw'].get_scan(scan):.2f} MW\n" - f"D-3He Power, $P_{{\\text{{fus,D3He}}}}:$ {mfile_data.data['p_dhe3_total_mw'].get_scan(scan):.2f} MW\n" - f"Alpha Power, $P_{{\\alpha}}:$ {mfile_data.data['p_alpha_total_mw'].get_scan(scan):.2f} MW" + f"Fusion Power, $P_{{\\text{{fus}}}}:$ {mfile_data.data['p_fusion_total_mw'].get_scan(scan):,.2f} MW\n" + f"D-T Power, $P_{{\\text{{fus,DT}}}}:$ {mfile_data.data['p_dt_total_mw'].get_scan(scan):,.2f} MW\n" + f"D-D Power, $P_{{\\text{{fus,DD}}}}:$ {mfile_data.data['p_dd_total_mw'].get_scan(scan):,.2f} MW\n" + f"D-3He Power, $P_{{\\text{{fus,D3He}}}}:$ {mfile_data.data['p_dhe3_total_mw'].get_scan(scan):,.2f} MW\n" + f"Alpha Power, $P_{{\\alpha}}:$ {mfile_data.data['p_alpha_total_mw'].get_scan(scan):,.2f} MW" ) axis.text( @@ -4702,9 +4702,9 @@ def plot_first_wall_poloidal_cross_section(axis, mfile_data, scan): textstr_fw = "\n".join(( rf"Coolant type: {i_fw_coolant_type}", - rf"$T_{{FW,peak}}$: {temp_fw_peak:.3f} K", - rf"$P_{{FW}}$: {pres_fw_coolant / 1e3:.3f} kPa", - rf"$P_{{FW}}$: {pres_fw_coolant / 1e5:.3f} bar", + rf"$T_{{FW,peak}}$: {temp_fw_peak:,.3f} K", + rf"$P_{{FW}}$: {pres_fw_coolant / 1e3:,.3f} kPa", + rf"$P_{{FW}}$: {pres_fw_coolant / 1e5:,.3f} bar", rf"$N_{{outboard}}$: {n_fw_outboard_channels}", rf"$N_{{inboard}}$: {n_fw_inboard_channels}", )) @@ -6763,10 +6763,10 @@ def _pack_strands_rectangular_with_obstacles( f"Critical field at zero \ntemperature and strain: {mfile_data.data['b_tf_superconductor_critical_zero_temp_strain'].get_scan(scan):.4f} T\n" f"Critical temperature at \nzero field and strain: {mfile_data.data['temp_tf_superconductor_critical_zero_field_strain'].get_scan(scan):.4f} K\n" f"Temperature at conductor: {mfile_data.data['tftmp'].get_scan(scan):.4f} K\n" - f"$I_{{\\text{{TF,turn critical}}}}$: {mfile_data.data['c_turn_cables_critical'].get_scan(scan):.2f} A\n" - f"$I_{{\\text{{TF,turn}}}}$: {mfile_data.data['c_tf_turn'].get_scan(scan):.2f} A\n" - f"Critcal current ratio: {mfile_data.data['f_c_tf_turn_operating_critical'].get_scan(scan):.4f}\n" - f"Superconductor temperature \nmargin: {mfile_data.data['temp_tf_superconductor_margin'].get_scan(scan):.4f} K\n" + f"$I_{{\\text{{TF,turn critical}}}}$: {mfile_data.data['c_turn_cables_critical'].get_scan(scan):,.2f} A\n" + f"$I_{{\\text{{TF,turn}}}}$: {mfile_data.data['c_tf_turn'].get_scan(scan):,.2f} A\n" + f"Critcal current ratio: {mfile_data.data['f_c_tf_turn_operating_critical'].get_scan(scan):,.4f}\n" + f"Superconductor temperature \nmargin: {mfile_data.data['temp_tf_superconductor_margin'].get_scan(scan):,.4f} K\n" f"\n$\\mathbf{{Quench:}}$\n \n" f"Quench dump time: {mfile_data.data['t_tf_superconductor_quench'].get_scan(scan):.4e} s\n" f"Quench detection time: {mfile_data.data['t_tf_quench_detection'].get_scan(scan):.4e} s\n" @@ -6835,11 +6835,21 @@ def plot_cable_in_conduit_cable(axis, fig, mfile_data, scan: int) -> None: cable_diameter_mm = ( mfile_data.data["dia_tf_turn_superconducting_cable"].get_scan(scan) * 1000 ) + # Convert lengths from meters to kilometers for display + len_tf_coil_superconductor_km = ( + mfile_data.data["len_tf_coil_superconductor"].get_scan(scan) / 1000.0 + ) + len_tf_superconductor_total_km = ( + mfile_data.data["len_tf_superconductor_total"].get_scan(scan) / 1000.0 + ) + textstr_cable = ( f"$\\mathbf{{Cable:}}$\n \n" - f"Cable diameter: {cable_diameter_mm:.4f} mm\n" + f"Cable diameter: {cable_diameter_mm:,.4f} mm\n" f"Copper area fraction: {mfile_data.data['f_a_tf_turn_cable_copper'].get_scan(scan):.4f}\n" - f"Number of strands per turn: {int(mfile_data.data['n_tf_turn_superconducting_cables'].get_scan(scan))}\n" + f"Number of strands per turn: {int(mfile_data.data['n_tf_turn_superconducting_cables'].get_scan(scan)):,}\n" + f"Length of superconductor per coil: {len_tf_coil_superconductor_km:,.2f} km\n" + f"Total length of superconductor in all coils: {len_tf_superconductor_total_km:,.2f} km\n" ) axis.text( 0.4, @@ -6851,7 +6861,7 @@ def plot_cable_in_conduit_cable(axis, fig, mfile_data, scan: int) -> None: transform=fig.transFigure, bbox={ "boxstyle": "round", - "facecolor": "#6dd3f7", # light blue for superconductors + "facecolor": "#cccccc", # grayish color "alpha": 1.0, "linewidth": 2, }, @@ -9069,7 +9079,7 @@ def plot_cs_coil_structure(axis, fig, mfile_data, scan, colour_scheme=1): f"CS full height: {mfile_data.data['dz_cs_full'].get_scan(scan):.4f} m\n" f"CS full width: {mfile_data.data['dr_cs_full'].get_scan(scan):.4f} m\n" f"CS poloidal area: {mfile_data.data['a_cs_poloidal'].get_scan(scan):.4f} m$^2$\n" - f"$N_{{\\text{{turns}}}}:$ {mfile_data.data['n_pf_coil_turns[n_cs_pf_coils-1]'].get_scan(scan):.2f}\n" + f"$N_{{\\text{{turns}}}}:$ {mfile_data.data['n_pf_coil_turns[n_cs_pf_coils-1]'].get_scan(scan):,.2f}\n" f"$I_{{\\text{{peak}}}}:$ {mfile_data.data['c_pf_cs_coils_peak_ma[n_cs_pf_coils-1]'].get_scan(scan):.3f}$ \\ MA$\n" f"$B_{{\\text{{peak}}}}:$ {mfile_data.data['b_pf_coil_peak[n_cs_pf_coils-1]'].get_scan(scan):.3f}$ \\ T$\n\n" ) @@ -10578,9 +10588,9 @@ def plot_fusion_rate_profiles(axis, fig, mfile_data, scan): # ============================================================================ textstr_dt = ( - f"Total fusion power: {mfile_data.data['p_dt_total_mw'].get_scan(scan):.2f} MW\n" - f"Plasma fusion power: {mfile_data.data['p_plasma_dt_mw'].get_scan(scan):.2f} MW \n" - f"Beam fusion power: {mfile_data.data['p_beam_dt_mw'].get_scan(scan):.2f} MW\n" + f"Total fusion power: {mfile_data.data['p_dt_total_mw'].get_scan(scan):,.2f} MW\n" + f"Plasma fusion power: {mfile_data.data['p_plasma_dt_mw'].get_scan(scan):,.2f} MW \n" + f"Beam fusion power: {mfile_data.data['p_beam_dt_mw'].get_scan(scan):,.2f} MW\n" ) axis.text( @@ -10611,7 +10621,7 @@ def plot_fusion_rate_profiles(axis, fig, mfile_data, scan): # ================================================= textstr_dd = ( - f"Total fusion power: {mfile_data.data['p_dd_total_mw'].get_scan(scan):.2f} MW\n" + f"Total fusion power: {mfile_data.data['p_dd_total_mw'].get_scan(scan):,.2f} MW\n" f"Tritium branching ratio: {mfile_data.data['f_dd_branching_trit'].get_scan(scan):.4f} \n" ) @@ -10642,7 +10652,7 @@ def plot_fusion_rate_profiles(axis, fig, mfile_data, scan): # ================================================= - textstr_dhe3 = f"Total fusion power: {mfile_data.data['p_dhe3_total_mw'].get_scan(scan):.2f} MW \n\n" + textstr_dhe3 = f"Total fusion power: {mfile_data.data['p_dhe3_total_mw'].get_scan(scan):,.2f} MW \n\n" axis.text( 0.05, @@ -10711,11 +10721,11 @@ def plot_fusion_rate_profiles(axis, fig, mfile_data, scan): # ================================================= textstr_neutron = ( - f"Total power: {mfile_data.data['p_neutron_total_mw'].get_scan(scan):.2f} MW\n" - f"Plasma power: {mfile_data.data['p_plasma_neutron_mw'].get_scan(scan):.2f} MW\n" - f"Beam power: {mfile_data.data['p_beam_neutron_mw'].get_scan(scan):.2f} MW\n\n" - f"Total power density: {mfile_data.data['pden_neutron_total_mw'].get_scan(scan):.4e} MW/m3\n" - f"Plasma power density: {mfile_data.data['pden_plasma_neutron_mw'].get_scan(scan):.4e} MW/m3\n" + f"Total power: {mfile_data.data['p_neutron_total_mw'].get_scan(scan):,.2f} MW\n" + f"Plasma power: {mfile_data.data['p_plasma_neutron_mw'].get_scan(scan):,.2f} MW\n" + f"Beam power: {mfile_data.data['p_beam_neutron_mw'].get_scan(scan):,.2f} MW\n\n" + f"Total power density: {mfile_data.data['pden_neutron_total_mw'].get_scan(scan):,.4e} MW/m3\n" + f"Plasma power density: {mfile_data.data['pden_plasma_neutron_mw'].get_scan(scan):,.4e} MW/m3\n" ) axis.text( diff --git a/process/superconducting_tf_coil.py b/process/superconducting_tf_coil.py index f5040c5264..d89b2a16b5 100644 --- a/process/superconducting_tf_coil.py +++ b/process/superconducting_tf_coil.py @@ -2041,6 +2041,16 @@ def sc_tf_internal_geom(self, i_tf_wp_geom, i_tf_case_geom, i_tf_turns_integer): f_a_void=tfcoil_variables.f_a_tf_turn_cable_space_extra_void, ) + ( + superconducting_tf_coil_variables.len_tf_coil_superconductor, + superconducting_tf_coil_variables.len_tf_superconductor_total, + ) = self.calculate_cable_in_conduit_superconductor_length( + n_tf_coils=tfcoil_variables.n_tf_coils, + n_tf_coil_turns=tfcoil_variables.n_tf_coil_turns, + len_tf_coil=tfcoil_variables.len_tf_coil, + n_tf_turn_superconducting_cables=superconducting_tf_coil_variables.n_tf_turn_superconducting_cables, + ) + # Areas and fractions # ------------------- # Central helium channel down the conductor core [m2] diff --git a/process/tf_coil.py b/process/tf_coil.py index 9432fca46d..5fc89ae424 100644 --- a/process/tf_coil.py +++ b/process/tf_coil.py @@ -1207,6 +1207,18 @@ def outtf(self): "(n_tf_turn_superconducting_cables)", superconducting_tf_coil_variables.n_tf_turn_superconducting_cables, ) + po.ovarre( + self.outfile, + "Length of superconductor in TF coil (m)", + "(len_tf_coil_superconductor)", + superconducting_tf_coil_variables.len_tf_coil_superconductor, + ) + po.ovarre( + self.outfile, + "Total length of superconductor in all TF coils (m)", + "(len_tf_superconductor_total)", + superconducting_tf_coil_variables.len_tf_superconductor_total, + ) po.ocmmnt(self.outfile, "Fractions by area") po.ovarre( self.outfile, From 0934340c678a28ba87e58e4decfbea58aeb072d0 Mon Sep 17 00:00:00 2001 From: mn3981 Date: Tue, 7 Oct 2025 14:18:44 +0100 Subject: [PATCH 15/17] =?UTF-8?q?=E2=9C=A8=20Refactor=20cable-in-conduit?= =?UTF-8?q?=20strand=20count=20calculation=20to=20exclude=20void=20fractio?= =?UTF-8?q?n=20and=20update=20related=20parameters?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- process/io/plot_proc.py | 134 +++++++---------------------- process/superconducting_tf_coil.py | 17 ++-- 2 files changed, 39 insertions(+), 112 deletions(-) diff --git a/process/io/plot_proc.py b/process/io/plot_proc.py index cebd7394db..f37849ce8d 100644 --- a/process/io/plot_proc.py +++ b/process/io/plot_proc.py @@ -6189,21 +6189,10 @@ def _pack_strands_rectangular_with_obstacles( """Pack circular strands in rectangular space with cooling pipe obstacle""" x, y, width, height = cable_space_bounds - shape_area = width * height - - # Subtract pipe area from total - pipe_area = np.pi * pipe_radius**2 - # Approximate corner area loss - corner_area = ( - 4 * (corner_radius**2 * (1 - np.pi / 4)) if corner_radius > 0 else 0 - ) - - usable_area = shape_area - pipe_area - corner_area radius = strand_diameter / 2 placed_strands = [] attempts = 0 - max_attempts = 1e5 pipe_x, pipe_y = pipe_center @@ -6259,45 +6248,44 @@ def _pack_strands_rectangular_with_obstacles( ), # top-right (x + corner_radius, y + height - corner_radius), # top-left ] - # Check each corner - corner_collision = False - if candidate_x < corners[0][0] and candidate_y < corners[0][1]: - if ( - np.sqrt( + if ( + ( + candidate_x < corners[0][0] + and candidate_y < corners[0][1] + and np.sqrt( (candidate_x - corners[0][0]) ** 2 + (candidate_y - corners[0][1]) ** 2 ) > corner_radius - radius - ): - corner_collision = True - elif candidate_x > corners[1][0] and candidate_y < corners[1][1]: - if ( - np.sqrt( + ) + or ( + candidate_x > corners[1][0] + and candidate_y < corners[1][1] + and np.sqrt( (candidate_x - corners[1][0]) ** 2 + (candidate_y - corners[1][1]) ** 2 ) > corner_radius - radius - ): - corner_collision = True - elif candidate_x > corners[2][0] and candidate_y > corners[2][1]: - if ( - np.sqrt( + ) + or ( + candidate_x > corners[2][0] + and candidate_y > corners[2][1] + and np.sqrt( (candidate_x - corners[2][0]) ** 2 + (candidate_y - corners[2][1]) ** 2 ) > corner_radius - radius - ): - corner_collision = True - elif candidate_x < corners[3][0] and candidate_y > corners[3][1]: - if ( - np.sqrt( + ) + or ( + candidate_x < corners[3][0] + and candidate_y > corners[3][1] + and np.sqrt( (candidate_x - corners[3][0]) ** 2 + (candidate_y - corners[3][1]) ** 2 ) > corner_radius - radius - ): - corner_collision = True - if corner_collision: + ) + ): continue # Check collision with existing strands @@ -6339,47 +6327,9 @@ def _pack_strands_rectangular_with_obstacles( break attempts = n_rows * n_cols - print(f"{len(placed_strands)}/ {n_strands}, Attempts:{attempts}") return len(placed_strands), attempts - # # Check collision with existing strands - # collision = False - # for existing_x, existing_y in placed_strands: - # distance = np.sqrt( - # (candidate_x - existing_x) ** 2 + (candidate_y - existing_y) ** 2 - # ) - # if distance < strand_diameter: - # collision = True - # break - - # if not collision: - # placed_strands.append((candidate_x, candidate_y)) - # # Plot the strand - # circle_copper_surrounding = Circle( - # (candidate_x, candidate_y), - # radius, - # facecolor="#b87333", # copper color - # edgecolor="#8B4000", # darker copper edge - # linewidth=0.3, - # alpha=0.8, - # ) - # axis.add_patch(circle_copper_surrounding) - - # circle_central_conductor = Circle( - # (candidate_x, candidate_y), - # radius/2, - # facecolor="black", - # linewidth=0.3, - # alpha=0.5, - # ) - # axis.add_patch(circle_central_conductor) - - # attempts += 1 - # print(f"{len(placed_strands)}/ {n_strands}, Attempts:{attempts}") - - # return len(placed_strands), attempts - # Import the TF turn variables then multiply into mm i_tf_turns_integer = mfile_data.data["i_tf_turns_integer"].get_scan(scan) # If integer turns switch is on then the turns can have non square dimensions @@ -6509,8 +6459,8 @@ def _pack_strands_rectangular_with_obstacles( cable_space_bounds=cable_bounds, pipe_center=( turn_width / 2, - turn_width / 2, - ), # pipe_center is the center of the turn + (turn_width if i_tf_turns_integer == 0 else turn_height) / 2, + ), pipe_radius=he_pipe_diameter / 2, strand_diameter=strand_diameter, void_fraction=void_fraction, @@ -6524,19 +6474,6 @@ def _pack_strands_rectangular_with_obstacles( ].get_scan(scan), ) - # Calculate packing efficiency - total_cable_space = cable_bounds[2] * cable_bounds[3] - pipe_area = np.pi * (he_pipe_diameter / 2) ** 2 - corner_area = 4 * (radius_tf_turn_cable_space_corners**2 * (1 - np.pi / 4)) - usable_area = total_cable_space - pipe_area - corner_area - strand_area = np.pi * (strand_diameter / 2) ** 2 - achieved_packing = ( - (n_strands * strand_area) / usable_area if usable_area > 0 else 0 - ) - else: - n_strands = 0 - attempts = 0 - achieved_packing = 0 axis.set_xlim(-turn_width * 0.05, turn_width * 1.05) axis.set_ylim(-turn_width * 0.05, turn_width * 1.05) @@ -6614,9 +6551,12 @@ def _pack_strands_rectangular_with_obstacles( # Pack strands if significant void fraction if void_fraction > 0.001: - n_strands, attempts = _pack_strands_rectangular_with_obstacles( + _, _ = _pack_strands_rectangular_with_obstacles( cable_bounds, - pipe_center, + ( + turn_width / 2, + turn_height / 2, + ), he_pipe_diameter / 2, strand_diameter, void_fraction, @@ -6630,20 +6570,6 @@ def _pack_strands_rectangular_with_obstacles( ].get_scan(scan), ) - # Calculate packing efficiency - total_cable_space = cable_bounds[2] * cable_bounds[3] - pipe_area = np.pi * (he_pipe_diameter / 2) ** 2 - corner_area = 4 * (radius_tf_turn_cable_space_corners**2 * (1 - np.pi / 4)) - usable_area = total_cable_space - pipe_area - corner_area - strand_area = np.pi * (strand_diameter / 2) ** 2 - achieved_packing = ( - (n_strands * strand_area) / usable_area if usable_area > 0 else 0 - ) - else: - n_strands = 0 - attempts = 0 - achieved_packing = 0 - axis.set_xlim(-turn_width * 0.05, turn_width * 1.05) axis.set_ylim(-turn_height * 0.05, turn_height * 1.05) @@ -11787,7 +11713,7 @@ def main_plot( plot_tf_cable_in_conduit_turn(ax20, fig12, m_file_data, scan) plot_205 = fig10.add_subplot(223, aspect="equal") plot_205.set_position([0.075, 0.1, 0.3, 0.3]) - plot_cable_in_conduit_cable(plot_205, fig10, m_file_data, scan) + plot_cable_in_conduit_cable(plot_205, fig11, m_file_data, scan) else: ax19 = fig12.add_subplot(211, aspect="equal") ax19.set_position([0.06, 0.55, 0.675, 0.4]) diff --git a/process/superconducting_tf_coil.py b/process/superconducting_tf_coil.py index d89b2a16b5..2eb6c4cf0c 100644 --- a/process/superconducting_tf_coil.py +++ b/process/superconducting_tf_coil.py @@ -1316,7 +1316,9 @@ def tf_cable_in_conduit_superconductor_properties( ) def calculate_cable_in_conduit_strand_count( - self, a_cable_space: float, dia_superconductor_strand: float, f_a_void: float + self, + a_cable_space: float, + dia_superconductor_strand: float, ) -> int: """ Calculates the maximum number of superconducting strands that can fit into a cable-in-conduit conductor, @@ -1326,15 +1328,13 @@ def calculate_cable_in_conduit_strand_count( :type a_cable_space: float :param dia_superconductor_strand: Diameter of a single superconducting strand (in meters). :type dia_superconductor_strand: float - :param f_a_void: Fraction of the cable area to remain void (between 0 and 1). - :type f_a_void: float :returns: The maximum number of strands that can fit in the available space, accounting for the void fraction. :rtype: int """ # Effective area available for strands (excluding voids) - effective_area = a_cable_space * (1 - f_a_void) + effective_area = a_cable_space # Area per strand (circular) strand_area = np.pi * (dia_superconductor_strand / 2) ** 2 @@ -1357,8 +1357,10 @@ def calculate_cable_in_conduit_superconductor_length( :param float len_tf_coil: Length of a single TF coil (in meters). :param int n_tf_turn_superconducting_cables: Number of superconducting cables per turn in the TF coil. - :returns: Total length of superconducting material required (in meters). - :rtype: float + :returns: Tuple containing: + - Length of superconductor in one TF coil (in meters). + - Total length of superconductor in all TF coils (in meters). + :rtype: tuple[float, float] """ # Length of superconductor in one TF coil @@ -2036,9 +2038,8 @@ def sc_tf_internal_geom(self, i_tf_wp_geom, i_tf_case_geom, i_tf_turns_integer): # --------------------------------------------------- if tfcoil_variables.i_tf_sc_mat != 6: superconducting_tf_coil_variables.n_tf_turn_superconducting_cables = self.calculate_cable_in_conduit_strand_count( - a_cable_space=tfcoil_variables.a_tf_turn_cable_space_no_void, + a_cable_space=superconducting_tf_coil_variables.a_tf_turn_cable_space_effective, dia_superconductor_strand=superconducting_tf_coil_variables.dia_tf_turn_superconducting_cable, - f_a_void=tfcoil_variables.f_a_tf_turn_cable_space_extra_void, ) ( From e79f06652b91a93ea08732ba88a1d441ba07c544 Mon Sep 17 00:00:00 2001 From: mn3981 Date: Fri, 17 Oct 2025 17:36:15 +0100 Subject: [PATCH 16/17] Add diameter of superconducting cable to integration mfile for tests --- tests/integration/data/large_tokamak_1_MFILE.DAT | 1 + tests/integration/data/large_tokamak_2_MFILE.DAT | 1 + tests/integration/data/large_tokamak_3_MFILE.DAT | 1 + tests/integration/data/large_tokamak_4_MFILE.DAT | 1 + tests/integration/data/large_tokamak_MFILE.DAT | 1 + 5 files changed, 5 insertions(+) diff --git a/tests/integration/data/large_tokamak_1_MFILE.DAT b/tests/integration/data/large_tokamak_1_MFILE.DAT index e50a5b7e96..026628aeb4 100644 --- a/tests/integration/data/large_tokamak_1_MFILE.DAT +++ b/tests/integration/data/large_tokamak_1_MFILE.DAT @@ -713,6 +713,7 @@ Steel_conduit_thickness_(m)_____________________________________________ (dx_tf_turn_steel)____________________ 8.0099E-03 ITV Inter-turn_insulation_thickness_(m)_____________________________________ (dx_tf_turn_insulation)____________________ 8.0000E-04 Diameter_of_central_helium_channel_in_cable_____________________________ (dia_tf_turn_coolant_channel)_____________________ 1.0000E-02 + Diameter_of_superconducting_cable_______________________________________ (dia_tf_turn_superconducting_cable)_ 7.29999999999999963e-04 internal_area_of_the_cable_space________________________________________ (a_tf_turn_cable_space_no_void)_______________________ 1.6999E-03 Coolant_fraction_in_conductor_excluding_central_channel_________________ (f_a_tf_turn_cable_space_extra_void)________________________ 3.0000E-01 Copper_fraction_of_conductor____________________________________________ (f_a_tf_turn_cable_copper)_____________________ 8.4212E-01 ITV diff --git a/tests/integration/data/large_tokamak_2_MFILE.DAT b/tests/integration/data/large_tokamak_2_MFILE.DAT index e675965e78..7ccd76e0d1 100644 --- a/tests/integration/data/large_tokamak_2_MFILE.DAT +++ b/tests/integration/data/large_tokamak_2_MFILE.DAT @@ -714,6 +714,7 @@ Steel_conduit_thickness_(m)_____________________________________________ (dx_tf_turn_steel)____________________ 8.0099E-03 ITV Inter-turn_insulation_thickness_(m)_____________________________________ (dx_tf_turn_insulation)____________________ 8.0000E-04 Diameter_of_central_helium_channel_in_cable_____________________________ (dia_tf_turn_coolant_channel)_____________________ 1.0000E-02 + Diameter_of_superconducting_cable________________________________________ (dia_tf_turn_superconducting_cable)_ 7.29999999999999963e-04 internal_area_of_the_cable_space________________________________________ (a_tf_turn_cable_space_no_void)_______________________ 1.6999E-03 Coolant_fraction_in_conductor_excluding_central_channel_________________ (f_a_tf_turn_cable_space_extra_void)________________________ 3.0000E-01 Copper_fraction_of_conductor____________________________________________ (f_a_tf_turn_cable_copper)_____________________ 8.4212E-01 ITV diff --git a/tests/integration/data/large_tokamak_3_MFILE.DAT b/tests/integration/data/large_tokamak_3_MFILE.DAT index 7ff496e209..d0969e3504 100644 --- a/tests/integration/data/large_tokamak_3_MFILE.DAT +++ b/tests/integration/data/large_tokamak_3_MFILE.DAT @@ -714,6 +714,7 @@ Steel_conduit_thickness_(m)_____________________________________________ (dx_tf_turn_steel)____________________ 8.0099E-03 ITV Inter-turn_insulation_thickness_(m)_____________________________________ (dx_tf_turn_insulation)____________________ 8.0000E-04 Diameter_of_central_helium_channel_in_cable_____________________________ (dia_tf_turn_coolant_channel)_____________________ 1.0000E-02 + Diameter_of_superconducting_cable________________________________________ (dia_tf_turn_superconducting_cable)_ 7.29999999999999963e-04 internal_area_of_the_cable_space________________________________________ (a_tf_turn_cable_space_no_void)_______________________ 1.6999E-03 Coolant_fraction_in_conductor_excluding_central_channel_________________ (f_a_tf_turn_cable_space_extra_void)________________________ 3.0000E-01 Copper_fraction_of_conductor____________________________________________ (f_a_tf_turn_cable_copper)_____________________ 8.4212E-01 ITV diff --git a/tests/integration/data/large_tokamak_4_MFILE.DAT b/tests/integration/data/large_tokamak_4_MFILE.DAT index d4752f7482..4363e65877 100644 --- a/tests/integration/data/large_tokamak_4_MFILE.DAT +++ b/tests/integration/data/large_tokamak_4_MFILE.DAT @@ -714,6 +714,7 @@ Steel_conduit_thickness_(m)_____________________________________________ (dx_tf_turn_steel)____________________ 8.0099E-03 ITV Inter-turn_insulation_thickness_(m)_____________________________________ (dx_tf_turn_insulation)____________________ 8.0000E-04 Diameter_of_central_helium_channel_in_cable_____________________________ (dia_tf_turn_coolant_channel)_____________________ 1.0000E-02 + Diameter_of_superconducting_cable________________________________________ (dia_tf_turn_superconducting_cable)_ 7.29999999999999963e-04 internal_area_of_the_cable_space________________________________________ (a_tf_turn_cable_space_no_void)_______________________ 1.6999E-03 Coolant_fraction_in_conductor_excluding_central_channel_________________ (f_a_tf_turn_cable_space_extra_void)________________________ 3.0000E-01 Copper_fraction_of_conductor____________________________________________ (f_a_tf_turn_cable_copper)_____________________ 8.4212E-01 ITV diff --git a/tests/integration/data/large_tokamak_MFILE.DAT b/tests/integration/data/large_tokamak_MFILE.DAT index 092bb0ba4a..6aaca41b71 100644 --- a/tests/integration/data/large_tokamak_MFILE.DAT +++ b/tests/integration/data/large_tokamak_MFILE.DAT @@ -958,6 +958,7 @@ Steel_conduit_thickness_(m)______________________________________________ (dx_tf_turn_steel)_____________ 8.00215764239549203e-03 Inter-turn_insulation_thickness_(m)______________________________________ (dx_tf_turn_insulation)________ 8.00000000000000038e-04 Diameter_of_central_helium_channel_in_cable______________________________ (dia_tf_turn_coolant_channel)__ 1.00000000000000002e-02 + Diameter_of_superconducting_cable________________________________________ (dia_tf_turn_superconducting_cable)_ 7.29999999999999963e-04 internal_area_of_the_cable_space_________________________________________ (a_tf_turn_cable_space_no_void)_ 1.72972548472813317e-03 Coolant_fraction_in_conductor_excluding_central_channel__________________ (f_a_tf_turn_cable_space_extra_void)_ 2.99999999999999989e-01 Copper_fraction_of_conductor_____________________________________________ (f_a_tf_turn_cable_copper)______________________ 8.31847298851364769e-01 From 04fa6a2cf1a31ac6035c9458a58994d004c4e5ef Mon Sep 17 00:00:00 2001 From: mn3981 Date: Tue, 21 Oct 2025 09:50:15 +0100 Subject: [PATCH 17/17] =?UTF-8?q?=E2=9C=A8=20Update=20plotting=20functions?= =?UTF-8?q?=20to=20include=20additional=20figures=20and=20adjust=20subplot?= =?UTF-8?q?=20assignments?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- process/io/plot_proc.py | 41 +++++++++++++++++++++++------------------ 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/process/io/plot_proc.py b/process/io/plot_proc.py index f37849ce8d..54a18014df 100644 --- a/process/io/plot_proc.py +++ b/process/io/plot_proc.py @@ -11523,6 +11523,7 @@ def main_plot( fig19, fig20, fig21, + fig22, m_file_data, scan, imp="../data/lz_non_corona_14_elements/", @@ -11708,47 +11709,47 @@ def main_plot( plot_superconducting_tf_wp(ax19, m_file_data, scan, fig12) # TF coil turn structure - ax20 = fig12.add_subplot(325, aspect="equal") + ax20 = fig13.add_subplot(325, aspect="equal") ax20.set_position([0.025, 0.5, 0.4, 0.4]) - plot_tf_cable_in_conduit_turn(ax20, fig12, m_file_data, scan) - plot_205 = fig10.add_subplot(223, aspect="equal") + plot_tf_cable_in_conduit_turn(ax20, fig13, m_file_data, scan) + plot_205 = fig13.add_subplot(223, aspect="equal") plot_205.set_position([0.075, 0.1, 0.3, 0.3]) - plot_cable_in_conduit_cable(plot_205, fig11, m_file_data, scan) + plot_cable_in_conduit_cable(plot_205, fig16, m_file_data, scan) else: ax19 = fig12.add_subplot(211, aspect="equal") ax19.set_position([0.06, 0.55, 0.675, 0.4]) plot_resistive_tf_wp(ax19, m_file_data, scan, fig12) plot_tf_coil_structure( - fig13.add_subplot(111, aspect="equal"), m_file_data, scan, colour_scheme + fig14.add_subplot(111, aspect="equal"), m_file_data, scan, colour_scheme ) - axes = fig14.subplots(nrows=3, ncols=1, sharex=True).flatten() + axes = fig15.subplots(nrows=3, ncols=1, sharex=True).flatten() plot_tf_stress(axes) - plot_bootstrap_comparison(fig15.add_subplot(221), m_file_data, scan) - plot_h_threshold_comparison(fig15.add_subplot(224), m_file_data, scan) - plot_density_limit_comparison(fig16.add_subplot(221), m_file_data, scan) - plot_confinement_time_comparison(fig16.add_subplot(224), m_file_data, scan) - plot_current_profiles_over_time(fig17.add_subplot(111), m_file_data, scan) + plot_bootstrap_comparison(fig16.add_subplot(221), m_file_data, scan) + plot_h_threshold_comparison(fig16.add_subplot(224), m_file_data, scan) + plot_density_limit_comparison(fig17.add_subplot(221), m_file_data, scan) + plot_confinement_time_comparison(fig17.add_subplot(224), m_file_data, scan) + plot_current_profiles_over_time(fig18.add_subplot(111), m_file_data, scan) plot_cs_coil_structure( - fig18.add_subplot(121, aspect="equal"), fig18, m_file_data, scan + fig19.add_subplot(121, aspect="equal"), fig19, m_file_data, scan ) plot_cs_turn_structure( - fig18.add_subplot(224, aspect="equal"), fig18, m_file_data, scan + fig19.add_subplot(224, aspect="equal"), fig19, m_file_data, scan ) plot_first_wall_top_down_cross_section( - fig19.add_subplot(221, aspect="equal"), m_file_data, scan + fig20.add_subplot(221, aspect="equal"), m_file_data, scan ) - plot_first_wall_poloidal_cross_section(fig19.add_subplot(122), m_file_data, scan) - plot_fw_90_deg_pipe_bend(fig19.add_subplot(337), m_file_data, scan) + plot_first_wall_poloidal_cross_section(fig20.add_subplot(122), m_file_data, scan) + plot_fw_90_deg_pipe_bend(fig20.add_subplot(337), m_file_data, scan) - plot_blkt_pipe_bends(fig20, m_file_data, scan) + plot_blkt_pipe_bends(fig21, m_file_data, scan) plot_main_power_flow( - fig21.add_subplot(111, aspect="equal"), m_file_data, scan, fig21 + fig22.add_subplot(111, aspect="equal"), m_file_data, scan, fig22 ) @@ -12064,6 +12065,7 @@ def main(args=None): page19 = plt.figure(figsize=(12, 9), dpi=80) page20 = plt.figure(figsize=(12, 9), dpi=80) page21 = plt.figure(figsize=(12, 9), dpi=80) + page22 = plt.figure(figsize=(12, 9), dpi=80) # run main_plot main_plot( @@ -12089,6 +12091,7 @@ def main(args=None): page19, page20, page21, + page22, m_file, scan=scan, demo_ranges=demo_ranges, @@ -12119,6 +12122,7 @@ def main(args=None): pdf.savefig(page19) pdf.savefig(page20) pdf.savefig(page21) + pdf.savefig(page22) # show fig if option used if args.show: @@ -12146,6 +12150,7 @@ def main(args=None): plt.close(page19) plt.close(page20) plt.close(page21) + plt.close(page22) if __name__ == "__main__":