From bd53d4286afa01ea1edf40fef2b744bf3e66cd79 Mon Sep 17 00:00:00 2001 From: Matti Coleman Date: Thu, 15 Oct 2026 14:29:17 +0200 Subject: [PATCH 01/35] add variables and modify constraint --- process/core/solver/constraints.py | 16 +++++++++------- process/data_structure/tfcoil_variables.py | 15 +++++++++------ 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/process/core/solver/constraints.py b/process/core/solver/constraints.py index d8013775bf..d13eac9cb9 100644 --- a/process/core/solver/constraints.py +++ b/process/core/solver/constraints.py @@ -997,18 +997,20 @@ def constraint_equation_35(constraint_registration, data): ) -@ConstraintManager.register_constraint(36, "K", ">=") +@ConstraintManager.register_constraint(36, "A/m²", ">=") def constraint_equation_36(constraint_registration, data): """Equation for TF coil s/c temperature margin lower limit (SCTF) + by means of the actual to critical current density - temp_tf_superconductor_margin: - TF coil temperature margin (K) - temp_tf_superconductor_margin_min: - minimum allowable temperature margin : TF coils (K) + j_tf_str: + actual superconductor strand current density (A/m²) + j_crit_str_tf: + superconductor strand critical current density under operating + conditions (A/m²) """ return geq( - data.tfcoil.temp_tf_superconductor_margin, - data.tfcoil.temp_tf_superconductor_margin_min, + data.tfcoil.j_tf_str, + data.tfcoil.j_crit_str_tf, constraint_registration, ) diff --git a/process/data_structure/tfcoil_variables.py b/process/data_structure/tfcoil_variables.py index 1e9bdb5a55..3f9b072573 100644 --- a/process/data_structure/tfcoil_variables.py +++ b/process/data_structure/tfcoil_variables.py @@ -337,13 +337,16 @@ class TFData: """ j_tf_bus: float = 1.25e6 - """bussing current density (A/m2)""" + """bussing current density (A/m²)""" j_crit_str_tf: float = 0.0 """j_crit_str : superconductor strand critical current density under operating - conditions (A/m2). Necessary for the cost calculation in $/kAm + conditions (A/m²). """ + j_tf_str: float = 0.0 + """j_tf_str : actual superconductor strand current density (A/m²)""" + j_crit_str_0: list[float] = field( default_factory=lambda: np.array([ 596905475.80390120, @@ -357,18 +360,18 @@ class TFData: 865652122.9071957, ]) ) - """j_crit_str_pf_0 : superconductor strand critical current density at 6 T and 4.2 K (A/m2) + """j_crit_str_pf_0 : superconductor strand critical current density at 6 T and 4.2 K (A/m²) Necessary for the cost calculation in $/kAm """ j_tf_wp_critical: float = 0.0 - """critical current density for winding pack (A/m2)""" + """critical current density for winding pack (A/m²)""" j_tf_wp_quench_heat_max: float = 0.0 - """allowable TF coil winding pack current density, for dump temperature rise protection (A/m2)""" + """allowable TF coil winding pack current density, for dump temperature rise protection (A/m²)""" j_tf_wp: float = 0.0 - """winding pack engineering current density (A/m2)""" + """winding pack engineering current density (A/m²)""" j_tf_coil_full_area: float = 0.0 """Inboard leg mid-plane full coil area current density (A/m²) From 090b2f686452e843fc4fba546fc39cff765b65f3 Mon Sep 17 00:00:00 2001 From: Matti Coleman Date: Thu, 15 Oct 2026 14:36:10 +0200 Subject: [PATCH 02/35] remove Tmargin calculation in superconducting.py --- process/models/tfcoil/superconducting.py | 160 +---------------------- 1 file changed, 2 insertions(+), 158 deletions(-) diff --git a/process/models/tfcoil/superconducting.py b/process/models/tfcoil/superconducting.py index 34fcfa3d97..8f71d5e096 100644 --- a/process/models/tfcoil/superconducting.py +++ b/process/models/tfcoil/superconducting.py @@ -6,7 +6,6 @@ from types import DynamicClassAttribute import numpy as np -from scipy import optimize from process.core import constants from process.core import process_output as po @@ -1174,144 +1173,6 @@ def output_tf_superconductor_info(self): "OP ", ) - @staticmethod - def calculate_superconductor_temperature_margin( - i_tf_superconductor: int, - j_superconductor: float, - b_tf_inboard_peak: float, - strain: float, - bc20m: float, - tc0m: float, - c0: float, - temp_tf_coolant_peak_field: float, - data: DataStructure, - ): - """Calculate the temperature margin of the TF superconductor. - - Parameters - ---------- - i_tf_superconductor: - Switch for conductor type: - - 1: ITER Nb3Sn, standard parameters - - 2: Bi-2212 High Temperature Superconductor - - 3: NbTi - - 4: ITER Nb3Sn, user-defined parameters - - 5: WST Nb3Sn parameterisation - - 7: Durham Ginzburg-Landau Nb-Ti parameterisation - - 8: Durham Ginzburg-Landau critical surface model for REBCO - - 9: Hazelton experimental data + Zhai conceptual model for REBCO - j_superconductor: - Current density in superconductor (A/m²). - b_tf_inboard_peak: - Peak field at conductor (T). - strain: - Strain on superconductor. - bc20m: - Critical field at zero temperature and strain (T). - tc0m: - Critical temperature at zero field and strain (K). - c0: - Constant used in NbTi critical current density calculation (A/m²). - temp_tf_coolant_peak_field: - He temperature at peak field point (K). - - Returns - ------- - type - temp_tf_superconductor_margin. - - - Raises - ------ - ProcessValueError - If an unknown superconductor type is specified. - """ - # ================================================================= - # Calculate temperature margin of superconductor - - # Temperature margin (already calculated in superconductors.bi2212 for - # i_tf_superconductor=2) - match SuperconductorModel(i_tf_superconductor): - case SuperconductorModel.BI2212: - # Bi-2212: temperature margin already calculated elsewhere - temp_tf_superconductor_margin = 0.0 - # Find temperature at which current density margin = 0 - case ( - SuperconductorModel.ITER_NB3SN - | SuperconductorModel.OLD_LUBELL_NBTI - | SuperconductorModel.USER_DEFINED_NB3SN - | SuperconductorModel.WST_NB3SN - | SuperconductorModel.DURHAM_NBTI - | SuperconductorModel.DURHAM_REBCO - | SuperconductorModel.HAZELTON_ZHAI_REBCO - ): - if ( - SuperconductorModel(i_tf_superconductor) - == SuperconductorModel.OLD_LUBELL_NBTI - ): - arguments = ( - i_tf_superconductor, - j_superconductor, - b_tf_inboard_peak, - strain, - bc20m, - tc0m, - data.superconducting_tfcoil.dr_tf_hts_tape, - data.superconducting_tfcoil.dx_tf_hts_tape_rebco, - data.superconducting_tfcoil.dx_tf_hts_tape_total, - c0, - ) - else: - arguments = ( - i_tf_superconductor, - j_superconductor, - b_tf_inboard_peak, - strain, - bc20m, - tc0m, - data.superconducting_tfcoil.dr_tf_hts_tape, - data.superconducting_tfcoil.dx_tf_hts_tape_rebco, - data.superconducting_tfcoil.dx_tf_hts_tape_total, - ) - - another_estimate = 2 * temp_tf_coolant_peak_field - ( - t_zero_margin, - _, - ) = optimize.newton( - superconductors.superconductor_current_density_margin, - temp_tf_coolant_peak_field, - fprime=None, - args=arguments, - tol=1.0e-06, - maxiter=50, - fprime2=None, - x1=another_estimate, - rtol=1.0e-6, - full_output=True, - disp=True, - ) - temp_tf_superconductor_margin = ( - t_zero_margin - temp_tf_coolant_peak_field - ) - data.tfcoil.temp_margin = temp_tf_superconductor_margin - - if temp_tf_superconductor_margin <= 0.0e0: - logger.error( - f"""Negative TFC temperature margin - temp_tf_superconductor_margin: {temp_tf_superconductor_margin} - b_tf_inboard_peak: {b_tf_inboard_peak} - j_superconductor: {j_superconductor} - """ - ) - case _: - raise ProcessValueError( - f"Unknown superconductor type: " - f"i_tf_superconductor={i_tf_superconductor}" - ) - - return temp_tf_superconductor_margin - @staticmethod def quench_heat_protection_current_density( c_tf_turn: float, @@ -2727,6 +2588,7 @@ def run(self, output: bool = False): ) ) + self.data.tfcoil.j_tf_str = critical_superconductor_info.j_superconductor self.data.tfcoil.j_tf_wp_critical = critical_superconductor_info.j_tf_wp_critical d_sc_tf.j_tf_superconductor_critical = ( critical_superconductor_info.j_superconductor_critical @@ -3999,6 +3861,7 @@ def run(self, output: bool = False): a_tf_croco_strand=d_sc_tf.a_tf_croco_strand, ) + self.data.tfcoil.j_tf_str = superconductor_critical_properties.j_superconductor self.data.tfcoil.j_tf_wp_critical = ( superconductor_critical_properties.j_tf_wp_critical ) @@ -4021,25 +3884,6 @@ def run(self, output: bool = False): superconductor_critical_properties.c_turn_cables_critical ) - if self.data.tfcoil.i_str_wp == 0: - strain = self.data.tfcoil.str_tf_con_res - else: - strain = self.data.tfcoil.str_wp - - self.data.tfcoil.temp_tf_superconductor_margin = ( - self.calculate_superconductor_temperature_margin( - i_tf_superconductor=self.data.tfcoil.i_tf_sc_mat, - j_superconductor=d_sc_tf.j_tf_superconductor, - b_tf_inboard_peak=self.data.tfcoil.b_tf_inboard_peak_with_ripple, - strain=strain, - bc20m=d_sc_tf.b_tf_superconductor_critical_zero_temp_strain, - tc0m=d_sc_tf.temp_tf_superconductor_critical_zero_field_strain, - c0=1.0e10, - temp_tf_coolant_peak_field=self.data.tfcoil.tftmp, - data=self.data, - ) - ) - self.data.tfcoil.v_tf_coil_dump_quench_kv = ( self.croco_voltage() / 1.0e3 ) # TFC Quench voltage in kV From ecf7c67813c1e62ec16d9776f3e0665e0d4def59 Mon Sep 17 00:00:00 2001 From: Matti Coleman Date: Thu, 15 Oct 2026 15:28:44 +0200 Subject: [PATCH 03/35] calculate T margin in post-processing --- process/models/tfcoil/superconducting.py | 148 +++++++++++++++++++++++ 1 file changed, 148 insertions(+) diff --git a/process/models/tfcoil/superconducting.py b/process/models/tfcoil/superconducting.py index 8f71d5e096..9f6f6388a8 100644 --- a/process/models/tfcoil/superconducting.py +++ b/process/models/tfcoil/superconducting.py @@ -6,6 +6,7 @@ from types import DynamicClassAttribute import numpy as np +from scipy import optimize from process.core import constants from process.core import process_output as po @@ -941,6 +942,24 @@ def output_tf_superconductor_info(self): """Output TF superconductor information""" d_sc_tf = self.data.superconducting_tfcoil + if self.data.tfcoil.i_str_wp == 0: + strain = self.data.tfcoil.str_tf_con_res + else: + strain = self.data.tfcoil.str_wp + + self.data.tfcoil.temp_tf_superconductor_margin = ( + self.calculate_superconductor_temperature_margin( + i_tf_superconductor=self.data.tfcoil.i_tf_sc_mat, + j_superconductor=d_sc_tf.j_tf_superconductor, + b_tf_inboard_peak=self.data.tfcoil.b_tf_inboard_peak_with_ripple, + strain=strain, + bc20m=d_sc_tf.b_tf_superconductor_critical_zero_temp_strain, + tc0m=d_sc_tf.temp_tf_superconductor_critical_zero_field_strain, + c0=1.0e10, + temp_tf_coolant_peak_field=self.data.tfcoil.tftmp, + data=self.data, + ) + ) po.oheadr(self.outfile, "TF Coils Superconductor Information") po.ovarre( self.outfile, @@ -2129,6 +2148,135 @@ def run_and_output_stress(self) -> None: self.data.tfcoil.sig_tf_case = 0.0e0 self.data.tfcoil.sig_tf_wp = 0.0e0 + @staticmethod + def calculate_superconductor_temperature_margin( + i_tf_superconductor: int, + j_superconductor: float, + b_tf_inboard_peak: float, + strain: float, + bc20m: float, + tc0m: float, + c0: float, + temp_tf_coolant_peak_field: float, + data: DataStructure, + ) -> float: + """Calculate the temperature margin of the TF superconductor. + + Parameters + ---------- + i_tf_superconductor: + Switch for conductor type: + - 1: ITER Nb3Sn, standard parameters + - 2: Bi-2212 High Temperature Superconductor + - 3: NbTi + - 4: ITER Nb3Sn, user-defined parameters + - 5: WST Nb3Sn parameterisation + - 7: Durham Ginzburg-Landau Nb-Ti parameterisation + - 8: Durham Ginzburg-Landau critical surface model for REBCO + - 9: Hazelton experimental data + Zhai conceptual model for REBCO + j_superconductor: + Current density in superconductor (A/m²). + b_tf_inboard_peak: + Peak field at conductor (T). + strain: + Strain on superconductor. + bc20m: + Critical field at zero temperature and strain (T). + tc0m: + Critical temperature at zero field and strain (K). + c0: + Constant used in NbTi critical current density calculation (A/m²). + temp_tf_coolant_peak_field: + He temperature at peak field point (K). + + Returns + ------- + type + temp_tf_superconductor_margin. + + + Raises + ------ + ProcessValueError + If an unknown superconductor type is specified. + """ + model = SuperconductorModel(i_tf_superconductor) + + args = ( + i_tf_superconductor, + j_superconductor, + b_tf_inboard_peak, + strain, + bc20m, + tc0m, + data.superconducting_tfcoil.dr_tf_hts_tape, + data.superconducting_tfcoil.dx_tf_hts_tape_rebco, + data.superconducting_tfcoil.dx_tf_hts_tape_total, + ) + + match model: + case SuperconductorModel.BI2212: + return 0.0 + + case SuperconductorModel.OLD_LUBELL_NBTI: + args += (c0,) + + case ( + SuperconductorModel.ITER_NB3SN + | SuperconductorModel.USER_DEFINED_NB3SN + | SuperconductorModel.WST_NB3SN + | SuperconductorModel.DURHAM_NBTI + | SuperconductorModel.DURHAM_REBCO + | SuperconductorModel.HAZELTON_ZHAI_REBCO + ): + pass + + case _: + raise ProcessValueError( + f"Unknown superconductor type: " + f"i_tf_superconductor={i_tf_superconductor}" + ) + + j_scale = max(abs(j_superconductor), 1.0) + + def objective(t): + residual = superconductors.superconductor_current_density_margin(t[0], *args) + return (residual / j_scale) ** 2 + + result = optimize.minimize( + objective, + x0=[temp_tf_coolant_peak_field], + bounds=[(1e-9, tc0m)], + method="Powell", + options={"xtol": 1e-6, "ftol": 1e-10, "maxiter": 100}, + ) + + if not result.success: + raise ProcessValueError(result.message) + + if not result.fun <= 1e-12: + raise ProcessValueError( + "Superconductor temperature margin minimum is not sufficiently" + f" close to zero: {result.fun}" + ) + + t_zero_margin = float(result.x[0]) + + margin = t_zero_margin - temp_tf_coolant_peak_field + # MC: wtf is this? kill it with fire + data.tfcoil.temp_margin = margin + + if margin <= 0: + logger.error( + "Non-positive TFC temperature margin: %s; " + "peak field: %s; superconductor current density: %s", + margin, + b_tf_inboard_peak, + j_superconductor, + ) + + return margin + @dataclass(slots=True) class TFGeneralTurnGeometry: From 8f2c9dec1b3544a0aa0b2a48d8384acbe71f57e0 Mon Sep 17 00:00:00 2001 From: Matti Coleman Date: Thu, 15 Oct 2026 15:37:39 +0200 Subject: [PATCH 04/35] correct issue number --- process/models/tfcoil/superconducting.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/process/models/tfcoil/superconducting.py b/process/models/tfcoil/superconducting.py index 9f6f6388a8..3997e3e7ea 100644 --- a/process/models/tfcoil/superconducting.py +++ b/process/models/tfcoil/superconducting.py @@ -4552,7 +4552,7 @@ def tf_croco_superconductor_properties( ) tmarg = current_sharing_t - temp_tf_peak self.data.tfcoil.temp_margin = ( - tmarg # Only used in the availabilty routine - see comment to Issue #526 + tmarg # Only used in the availability routine - see comment to Issue #527 ) return TFSuperconductorLimits( From 025d0434c52b9a88be62e8ba3fb0fc2effbdc594 Mon Sep 17 00:00:00 2001 From: Matti Coleman Date: Thu, 15 Oct 2026 15:39:01 +0200 Subject: [PATCH 05/35] correct issue number --- process/models/availability.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/process/models/availability.py b/process/models/availability.py index f2014bd22a..564c2b51ad 100644 --- a/process/models/availability.py +++ b/process/models/availability.py @@ -750,7 +750,7 @@ def calc_u_unplanned_magnets(self, output: bool) -> float: unplanned unavailability of magnets """ # Magnet temperature margin limit (K) - # Use the lower of the two values. Issue #526 + # Use the lower of the two values. Issue #527 tmargmin = min( self.data.tfcoil.temp_tf_superconductor_margin_min, self.data.tfcoil.temp_cs_superconductor_margin_min, From b62c307348a9fe483ac6b8d18ebea819e269ea82 Mon Sep 17 00:00:00 2001 From: Matti Coleman Date: Thu, 15 Oct 2026 15:41:50 +0200 Subject: [PATCH 06/35] remove local variable in availability T margin stuff cos it's annoying me --- process/models/availability.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/process/models/availability.py b/process/models/availability.py index 564c2b51ad..45b531fa63 100644 --- a/process/models/availability.py +++ b/process/models/availability.py @@ -755,7 +755,6 @@ def calc_u_unplanned_magnets(self, output: bool) -> float: self.data.tfcoil.temp_tf_superconductor_margin_min, self.data.tfcoil.temp_cs_superconductor_margin_min, ) - mag_temp_marg_limit = tmargmin # Magnet maintenance time (years) mag_main_time = 0.5e0 @@ -768,7 +767,7 @@ def calc_u_unplanned_magnets(self, output: bool) -> float: # Point at which risk of unplanned unavailability increases # conf_mag is the c factor, which determines the temperature margin at which # lifetime starts to decline. - start_of_risk = mag_temp_marg_limit / self.data.costs.conf_mag + start_of_risk = tmargmin / self.data.costs.conf_mag # Determine if temperature margin is in region with risk of # unplanned unavailability From 577dd18cd299596439573a55653cf07c723913f0 Mon Sep 17 00:00:00 2001 From: Matti Coleman Date: Thu, 15 Oct 2026 16:24:22 +0200 Subject: [PATCH 07/35] pause for breath --- process/models/availability.py | 2 +- process/models/superconductors.py | 88 +++++++++++++++---------------- 2 files changed, 45 insertions(+), 45 deletions(-) diff --git a/process/models/availability.py b/process/models/availability.py index 45b531fa63..1a6d1f067a 100644 --- a/process/models/availability.py +++ b/process/models/availability.py @@ -737,7 +737,7 @@ def calc_u_unplanned_magnets(self, output: bool) -> float: This routine calculates the unplanned unavailability of the magnets, using the methodology outlined in the 2014 EUROfusion RAMI report. - 2014 EUROfusion RAMI report, "Availability in PROCESS" + 2014 EUROfusion RAMI report, "Availability in PROCESS"; Parameters ---------- diff --git a/process/models/superconductors.py b/process/models/superconductors.py index ff435bdd8a..3483f1beeb 100644 --- a/process/models/superconductors.py +++ b/process/models/superconductors.py @@ -298,50 +298,6 @@ def jcrit_rebco( return j_critical, validity, b_c20_max, temp_c0_max -def current_sharing_rebco(bfield, j): - """Current sharing temperature for "REBCO" 2nd generation HTS superconductor - - Parameters - ---------- - bfield : - Magnetic field at superconductor (T) - j : - Current density in superconductor (A/m2) - - Returns - ------- - current_sharing_t : - Current sharing temperature (K) - """ - - def deltaj_rebco(temperature): - jcritical, _, _, _ = jcrit_rebco( - temp_conductor=temperature, - b_conductor=bfield, - temp_c0_max=SuperconductorModel.CROCO_REBCO.temp_crit_zero_field_strain, - b_c20_max=SuperconductorModel.CROCO_REBCO.b_crit_zero_field_strain, - ) - return jcritical - j - - # No additional arguments are required for deltaj_rebco since it only has one - # argument. - - estimate = 10.0 - another_estimate = 20.0 - current_sharing_t, _root_result = optimize.newton( - deltaj_rebco, - estimate, - tol=1e-6, - rtol=1e-6, - maxiter=50, - x1=another_estimate, - full_output=True, - disp=True, - ) - - return current_sharing_t - - def itersc( temp_conductor: float, b_conductor: float, @@ -1337,3 +1293,47 @@ def superconductor_current_density_margin( ) from err return j_superconductor_critical - j_superconductor + + +def current_sharing_rebco(bfield, j): + """Current sharing temperature for "REBCO" 2nd generation HTS superconductor + + Parameters + ---------- + bfield : + Magnetic field at superconductor (T) + j : + Current density in superconductor (A/m2) + + Returns + ------- + current_sharing_t : + Current sharing temperature (K) + """ + + def deltaj_rebco(temperature): + jcritical, _, _, _ = jcrit_rebco( + temp_conductor=temperature, + b_conductor=bfield, + temp_c0_max=SuperconductorModel.CROCO_REBCO.temp_crit_zero_field_strain, + b_c20_max=SuperconductorModel.CROCO_REBCO.b_crit_zero_field_strain, + ) + return jcritical - j + + # No additional arguments are required for deltaj_rebco since it only has one + # argument. + + estimate = 10.0 + another_estimate = 20.0 + current_sharing_t, _root_result = optimize.newton( + deltaj_rebco, + estimate, + tol=1e-6, + rtol=1e-6, + maxiter=50, + x1=another_estimate, + full_output=True, + disp=True, + ) + + return current_sharing_t From 7fad54bead22a229589bef600616a2cae516b694 Mon Sep 17 00:00:00 2001 From: Matti Coleman Date: Thu, 15 Oct 2026 17:01:28 +0200 Subject: [PATCH 08/35] purge t margin calc for now --- process/models/tfcoil/superconducting.py | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/process/models/tfcoil/superconducting.py b/process/models/tfcoil/superconducting.py index 3997e3e7ea..92df66b4a1 100644 --- a/process/models/tfcoil/superconducting.py +++ b/process/models/tfcoil/superconducting.py @@ -2756,25 +2756,6 @@ def run(self, output: bool = False): critical_superconductor_info.c_turn_cables_critical ) - if self.data.tfcoil.i_str_wp == 0: - strain = self.data.tfcoil.str_tf_con_res - else: - strain = self.data.tfcoil.str_wp - - self.data.tfcoil.temp_tf_superconductor_margin = ( - self.calculate_superconductor_temperature_margin( - i_tf_superconductor=self.data.tfcoil.i_tf_sc_mat, - j_superconductor=d_sc_tf.j_tf_superconductor, - b_tf_inboard_peak=self.data.tfcoil.b_tf_inboard_peak_with_ripple, - strain=strain, - bc20m=d_sc_tf.b_tf_superconductor_critical_zero_temp_strain, - tc0m=d_sc_tf.temp_tf_superconductor_critical_zero_field_strain, - c0=1.0e10, - temp_tf_coolant_peak_field=self.data.tfcoil.tftmp, - data=self.data, - ) - ) - # Do current density protection calculation # Only setup for Nb3Sn at present. if ( From 6f8848be97d482f0f605ba316393633c283ea8b5 Mon Sep 17 00:00:00 2001 From: Matti Coleman Date: Thu, 15 Oct 2026 17:15:30 +0200 Subject: [PATCH 09/35] remove variables --- process/data_structure/tfcoil_variables.py | 6 ------ process/models/tfcoil/superconducting.py | 10 +--------- 2 files changed, 1 insertion(+), 15 deletions(-) diff --git a/process/data_structure/tfcoil_variables.py b/process/data_structure/tfcoil_variables.py index 3f9b072573..50fbe0d6c8 100644 --- a/process/data_structure/tfcoil_variables.py +++ b/process/data_structure/tfcoil_variables.py @@ -673,12 +673,6 @@ class TFData: temp_cs_superconductor_margin_min: float = 0.0 """minimum allowable temperature margin : CS (K)""" - tmargmin: float = 0.0 - """minimum allowable temperature margin : TFC AND CS (K)""" - - temp_margin: float = 0.0 - """temperature margin (K)""" - temp_tf_superconductor_margin: float = 0.0 """TF coil superconductor temperature margin (K)""" diff --git a/process/models/tfcoil/superconducting.py b/process/models/tfcoil/superconducting.py index 92df66b4a1..08021477b9 100644 --- a/process/models/tfcoil/superconducting.py +++ b/process/models/tfcoil/superconducting.py @@ -2738,6 +2738,7 @@ def run(self, output: bool = False): self.data.tfcoil.j_tf_str = critical_superconductor_info.j_superconductor self.data.tfcoil.j_tf_wp_critical = critical_superconductor_info.j_tf_wp_critical + d_sc_tf.j_tf_superconductor_critical = ( critical_superconductor_info.j_superconductor_critical ) @@ -4527,15 +4528,6 @@ def tf_croco_superconductor_properties( # when we have found the desired value of tmarg j_superconductor = f_c_tf_turn_operating_critical * j_superconductor_critical - # Temperature margin - current_sharing_t = superconductors.current_sharing_rebco( - b_tf_inboard_peak, j_superconductor - ) - tmarg = current_sharing_t - temp_tf_peak - self.data.tfcoil.temp_margin = ( - tmarg # Only used in the availability routine - see comment to Issue #527 - ) - return TFSuperconductorLimits( j_tf_wp_critical=j_tf_wp_critical, j_superconductor_critical=j_superconductor_critical, From 7a0547838298ce7790ae9b88c10e99d2919f3967 Mon Sep 17 00:00:00 2001 From: Matti Coleman Date: Thu, 15 Oct 2026 17:18:37 +0200 Subject: [PATCH 10/35] remove variables --- process/models/availability.py | 4 ++-- tests/unit/models/test_availability.py | 12 ++++++++---- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/process/models/availability.py b/process/models/availability.py index 1a6d1f067a..539e1033e2 100644 --- a/process/models/availability.py +++ b/process/models/availability.py @@ -771,7 +771,7 @@ def calc_u_unplanned_magnets(self, output: bool) -> float: # Determine if temperature margin is in region with risk of # unplanned unavailability - if self.data.tfcoil.temp_margin >= start_of_risk: + if self.data.tfcoil.temp_tf_superconductor_margin >= start_of_risk: u_unplanned_magnets = mag_min_u_unplanned else: # Linear decrease in expected lifetime when approaching the limit @@ -781,7 +781,7 @@ def calc_u_unplanned_magnets(self, output: bool) -> float: self.data.costs.t_plant_operational_total_yrs / (start_of_risk - tmargmin) ) - * (self.data.tfcoil.temp_margin - tmargmin), + * (self.data.tfcoil.temp_tf_superconductor_margin - tmargmin), ) u_unplanned_magnets = mag_main_time / (t_life + mag_main_time) diff --git a/tests/unit/models/test_availability.py b/tests/unit/models/test_availability.py index d21009b306..2cac59f934 100644 --- a/tests/unit/models/test_availability.py +++ b/tests/unit/models/test_availability.py @@ -238,7 +238,7 @@ def calc_u_unplanned_magnets_param(**kwargs): :rtype: dict """ defaults = { - "temp_margin": 1.5, + "temp_tf_superconductor_margin": 1.5, "temp_tf_superconductor_margin_min": 1.5, "temp_cs_superconductor_margin_min": 1.5, "t_plant_operational_total_yrs": 30, @@ -264,13 +264,13 @@ def calc_u_unplanned_magnets_params(): return [ calc_u_unplanned_magnets_param(), calc_u_unplanned_magnets_param( - temp_margin=2.0, + temp_tf_superconductor_margin=2.0, temp_tf_superconductor_margin_min=1.6, temp_cs_superconductor_margin_min=1.6, conf_mag=0.8, ), calc_u_unplanned_magnets_param( - temp_margin=1.8, + temp_tf_superconductor_margin=1.8, temp_tf_superconductor_margin_min=1.6, temp_cs_superconductor_margin_min=1.6, conf_mag=0.8, @@ -314,7 +314,11 @@ def calc_u_unplanned_magnets_fix(availability, request, monkeypatch): "temp_tf_superconductor_margin_min", param["temp_tf_superconductor_margin_min"], ) - monkeypatch.setattr(availability.data.tfcoil, "temp_margin", param["temp_margin"]) + monkeypatch.setattr( + availability.data.tfcoil, + "temp_tf_superconductor_margin", + param["temp_tf_superconductor_margin"], + ) return param["expected"] From 45d41b53f2945402426c4d2d17bf1aef76b4fa7f Mon Sep 17 00:00:00 2001 From: Matti Coleman Date: Thu, 15 Oct 2026 17:22:10 +0200 Subject: [PATCH 11/35] remove variables --- process/core/init.py | 21 ------------------- process/core/input.py | 1 - process/models/stellarator/coils/calculate.py | 2 +- 3 files changed, 1 insertion(+), 23 deletions(-) diff --git a/process/core/init.py b/process/core/init.py index 4b5b537029..589cf67970 100644 --- a/process/core/init.py +++ b/process/core/init.py @@ -1218,27 +1218,6 @@ def check_process(inputs, data): # noqa: ARG001 fsum=fsum, ) - # Check that the temperature margins are not overdetermined - if data.tfcoil.tmargmin > 0.0001: - # This limit has been input and will be applied to both TFC and CS - if data.tfcoil.temp_tf_superconductor_margin_min > 0.0001: - logger.error( - "temp_tf_superconductor_margin_min and tmargmin should not both" - " be specified in IN.DAT" - " temp_tf_superconductor_margin_min has been ignored", - stacklevel=2, - ) - if data.tfcoil.temp_cs_superconductor_margin_min > 0.0001: - logger.error( - "temp_cs_superconductor_margin_min and tmargmin should not both" - " be specified in IN.DAT" - " temp_cs_superconductor_margin_min has been ignored", - stacklevel=2, - ) - - data.tfcoil.temp_tf_superconductor_margin_min = data.tfcoil.tmargmin - data.tfcoil.temp_cs_superconductor_margin_min = data.tfcoil.tmargmin - if ( data.physics.tauee_in > 1e-10 and data.physics.i_confinement_time != ConfinementTimeModel.NSTX_GYRO_BOHM diff --git a/process/core/input.py b/process/core/input.py index c288a0fc58..8af3564ff3 100644 --- a/process/core/input.py +++ b/process/core/input.py @@ -849,7 +849,6 @@ def bounds(self) -> tuple[NumberType | None, NumberType | None]: "dx_tf_wp_insulation": InputVariable("tfcoil", float, range=(0.0, 0.1)), "life_plant": InputVariable("costs", float, range=(1.0, 100.0)), "tmain": InputVariable("costs", float, range=(0.0, 100.0)), - "tmargmin": InputVariable("tfcoil", float, range=(0.0, 20.0)), "temp_cs_superconductor_margin_min": InputVariable( "tfcoil", float, range=(0.0, 20.0) ), diff --git a/process/models/stellarator/coils/calculate.py b/process/models/stellarator/coils/calculate.py index 151e34abc5..b51d7c66c5 100644 --- a/process/models/stellarator/coils/calculate.py +++ b/process/models/stellarator/coils/calculate.py @@ -417,7 +417,7 @@ def winding_pack_total_size( # jcrit for this bmax: jcrit_vector[k] = jcrit_from_material( b_max_k[k], - data.tfcoil.tftmp + data.tfcoil.tmargmin, + data.tfcoil.tftmp + data.tfcoil.temp_tf_superconductor_margin, data.tfcoil.i_tf_sc_mat, data.tfcoil.bcritsc, data.tfcoil.f_a_tf_turn_cable_copper, From e4de6c38d5d30335051eaba7623450278624a7a8 Mon Sep 17 00:00:00 2001 From: Matti Coleman Date: Fri, 16 Oct 2026 08:23:12 +0200 Subject: [PATCH 12/35] purge tmargin in IN.DATs --- tests/regression/input_files/large_tokamak_eval.IN.DAT | 2 +- tests/regression/input_files/large_tokamak_nof.IN.DAT | 2 +- tests/regression/input_files/low_aspect_ratio_DEMO.IN.DAT | 8 ++++---- tests/regression/input_files/stellarator_helias.IN.DAT | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/regression/input_files/large_tokamak_eval.IN.DAT b/tests/regression/input_files/large_tokamak_eval.IN.DAT index d9128b7218..00e4eadbe8 100644 --- a/tests/regression/input_files/large_tokamak_eval.IN.DAT +++ b/tests/regression/input_files/large_tokamak_eval.IN.DAT @@ -382,7 +382,7 @@ dr_tf_wp_with_insulation = 0.5153787768966674 * radial thickness of winding pack dx_tf_turn_steel = 0.008012110032981922 * TF coil conduit case thickness (m) (`iteration variable 58`) dx_tf_wp_insulation = 0.008 * Thickness of the ground insulation layer surrounding (m) temp_cs_superconductor_margin_min = 1.5 * minimum allowable temperature margin ; CS (K) -tmargmin = 1.5 * minimum allowable temperature margin ; TFC AND CS (K) +temp_tf_superconductor_margin_min = 1.5 * minimum allowable temperature margin ; TF (K) v_tf_coil_dump_quench_max_kv = 10.0 * max voltage across TF coil during quench (kV) (`iteration variable 52`) f_a_tf_turn_cable_space_extra_void = 0.3 * coolant fraction of TFC 'cable' (`i_tf_sup=1`); or of TFC leg (`i_tf_sup=0`) flu_tf_neutron_fast_max = 1e22 * max fluence in the TF coil diff --git a/tests/regression/input_files/large_tokamak_nof.IN.DAT b/tests/regression/input_files/large_tokamak_nof.IN.DAT index 203363dad0..66cfa56193 100644 --- a/tests/regression/input_files/large_tokamak_nof.IN.DAT +++ b/tests/regression/input_files/large_tokamak_nof.IN.DAT @@ -103,7 +103,7 @@ icc = 35 *--------------------------* icc = 36 * Minimum allowable temperature margin [K] -tmargmin = 1.5 +temp_tf_superconductor_margin_min = 1.5 * minimum allowable temperature margin ; TF (K) * CS coil temp margin lower limit * *---------------------------------* diff --git a/tests/regression/input_files/low_aspect_ratio_DEMO.IN.DAT b/tests/regression/input_files/low_aspect_ratio_DEMO.IN.DAT index af4872e2a9..163f9ed82e 100644 --- a/tests/regression/input_files/low_aspect_ratio_DEMO.IN.DAT +++ b/tests/regression/input_files/low_aspect_ratio_DEMO.IN.DAT @@ -940,12 +940,12 @@ dx_tf_wp_insulation = 0.008 * DESCRIPTION: Ground insulation thickness surrounding winding pack (m) * JUSTIFICATION: -tmargmin = 1.500 -* DESCRIPTION: Minimum allowable temperature margin (cs and tf coils) (k) +temp_tf_superconductor_margin_min = 1.5 +* DESCRIPTION: Minimum allowable temperature margin (TF coils) (k) * JUSTIFICATION: -*t_margmin_cs = 1.500 -* DESCRIPTION: Minimum allowable temperature margin (cs and tf coils) (k) +temp_cs_superconductor_margin_min = 1.5 +* DESCRIPTION: Minimum allowable temperature margin (CS) (k) * JUSTIFICATION: f_a_tf_turn_cable_space_extra_void = 0.300 diff --git a/tests/regression/input_files/stellarator_helias.IN.DAT b/tests/regression/input_files/stellarator_helias.IN.DAT index 73480c5d80..d97f5c091d 100644 --- a/tests/regression/input_files/stellarator_helias.IN.DAT +++ b/tests/regression/input_files/stellarator_helias.IN.DAT @@ -233,8 +233,8 @@ runtitle = SQuID *-----------------Tfcoil Variables-----------------* i_tf_sc_mat = 1 * Switch for superconductor material in tf coils (1: ITER Nb3Sn) -tftmp = 4.5 * Peak helium coolant temperature in TF coils and PF coils (K) -tmargmin = 1.5 * Temperature margin for superconductor current density calculation (K) +tftmp = 4.5 * Peak helium coolant temperature in TF coils and PF coils (K) +temp_tf_superconductor_margin_min = 1.5 * Temperature margin for superconductor current density calculation (K) temp_tf_cryo = 4.5 * Coil temperature for cryogenic plant power calculation (K) dx_tf_turn_general = 0.056 * Dimension conductor area including steel and insulation. dx_tf_turn_insulation = 0.002 * Conduit insulation thickness (m) From 7289cc1fe26ae039f182813407a6b9c73fd255cc Mon Sep 17 00:00:00 2001 From: Matti Coleman Date: Fri, 16 Oct 2026 08:28:29 +0200 Subject: [PATCH 13/35] purge more tmargmin from IN.DATs --- examples/data/large_tokamak_IN.DAT | 2 +- examples/data/large_tokamak_eval_IN.DAT | 2 +- examples/data/large_tokamak_varied_min_net_electric_IN.DAT | 2 +- examples/data/large_tokamak_varyrun_IN.DAT | 2 +- examples/data/scan_example_file_IN.DAT | 2 +- process/models/availability.py | 6 +++--- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/examples/data/large_tokamak_IN.DAT b/examples/data/large_tokamak_IN.DAT index cc19dd2588..fd176e85aa 100644 --- a/examples/data/large_tokamak_IN.DAT +++ b/examples/data/large_tokamak_IN.DAT @@ -91,7 +91,7 @@ icc = 35 *--------------------------* icc = 36 * Minimum allowable temperature margin [K] -tmargmin = 1.5 +temp_tf_superconductor_margin_min = 1.5 * CS coil temp margin lower limit * *---------------------------------* diff --git a/examples/data/large_tokamak_eval_IN.DAT b/examples/data/large_tokamak_eval_IN.DAT index 4d1db75a9f..d9b6e22249 100644 --- a/examples/data/large_tokamak_eval_IN.DAT +++ b/examples/data/large_tokamak_eval_IN.DAT @@ -383,7 +383,7 @@ dr_tf_wp_with_insulation = 0.5153787768966674 * radial thickness of winding pack dx_tf_turn_steel = 0.008012110032981922 * TF coil conduit case thickness (m) (`iteration variable 58`) dx_tf_wp_insulation = 0.008 * Thickness of the ground insulation layer surrounding (m) temp_cs_superconductor_margin_min = 1.5 * minimum allowable temperature margin ; CS (K) -tmargmin = 1.5 * minimum allowable temperature margin ; TFC AND CS (K) +temp_tf_superconductor_margin_min = 1.5 * minimum allowable temperature margin ; TF (K) v_tf_coil_dump_quench_max_kv = 10.0 * max voltage across TF coil during quench (kV) (`iteration variable 52`) f_a_tf_turn_cable_space_extra_void = 0.3 * coolant fraction of TFC 'cable' (`i_tf_sup=1`); or of TFC leg (`i_tf_sup=0`) flu_tf_neutron_fast_max = 1e22 * max fluence in the TF coil diff --git a/examples/data/large_tokamak_varied_min_net_electric_IN.DAT b/examples/data/large_tokamak_varied_min_net_electric_IN.DAT index 432d68ae6e..954000e6c3 100644 --- a/examples/data/large_tokamak_varied_min_net_electric_IN.DAT +++ b/examples/data/large_tokamak_varied_min_net_electric_IN.DAT @@ -95,7 +95,7 @@ icc = 35 *--------------------------* icc = 36 * Minimum allowable temperature margin [K] -tmargmin = 1.5 +temp_tf_superconductor_margin_min = 1.5 * CS coil temp margin lower limit * *---------------------------------* diff --git a/examples/data/large_tokamak_varyrun_IN.DAT b/examples/data/large_tokamak_varyrun_IN.DAT index 75e2a2759f..d255af115b 100644 --- a/examples/data/large_tokamak_varyrun_IN.DAT +++ b/examples/data/large_tokamak_varyrun_IN.DAT @@ -96,7 +96,7 @@ icc = 35 *--------------------------* icc = 36 * Minimum allowable temperature margin [K] -tmargmin = 1.5 +temp_tf_superconductor_margin_min = 1.5 * CS coil temp margin lower limit * *---------------------------------* diff --git a/examples/data/scan_example_file_IN.DAT b/examples/data/scan_example_file_IN.DAT index 323c0321ca..325e75bb0e 100644 --- a/examples/data/scan_example_file_IN.DAT +++ b/examples/data/scan_example_file_IN.DAT @@ -102,7 +102,7 @@ icc = 35 *--------------------------* icc = 36 * Minimum allowable temperature margin [K] -tmargmin = 1.5 +temp_tf_superconductor_margin_min = 1.5 * CS coil temp margin lower limit * *---------------------------------* diff --git a/process/models/availability.py b/process/models/availability.py index 539e1033e2..e167943abf 100644 --- a/process/models/availability.py +++ b/process/models/availability.py @@ -791,7 +791,7 @@ def calc_u_unplanned_magnets(self, output: bool) -> float: if output: po.ocmmnt(self.outfile, "Magnets:") po.oblnkl(self.outfile) - po.ovarre( + po.ovarre( # This is of questionable interest... self.outfile, "Minimum temperature margin (K)", "(tmargmin)", tmargmin ) po.ovarre( @@ -803,8 +803,8 @@ def calc_u_unplanned_magnets(self, output: bool) -> float: po.ovarre( self.outfile, "Temperature Margin (K)", - "(temp_margin)", - self.data.tfcoil.temp_margin, + "(temp_tf_superconductor_margin_min)", + self.data.tfcoil.temp_tf_superconductor_margin, "OP ", ) po.ovarre( From cca3e56d2068541339009943bba883637d4b6289 Mon Sep 17 00:00:00 2001 From: Matti Coleman Date: Fri, 16 Oct 2026 08:31:08 +0200 Subject: [PATCH 14/35] purge more tmargmin from IN.DATs --- tests/integration/data/large_tokamak_1_MFILE.DAT | 2 +- tests/integration/data/large_tokamak_2_MFILE.DAT | 2 +- tests/integration/data/large_tokamak_3_MFILE.DAT | 2 +- tests/integration/data/large_tokamak_4_MFILE.DAT | 2 +- tests/integration/data/large_tokamak_IN.DAT | 2 +- tests/integration/data/large_tokamak_MFILE.DAT | 2 +- tests/integration/data/large_tokamak_eval.IN.DAT | 2 +- tests/integration/data/scan_2D_MFILE.DAT | 2 +- tests/integration/data/scan_MFILE.DAT | 2 +- tests/unit/data/large_tokamak_IN.DAT | 2 +- tests/unit/data/large_tokamak_MFILE.DAT | 2 +- 11 files changed, 11 insertions(+), 11 deletions(-) diff --git a/tests/integration/data/large_tokamak_1_MFILE.DAT b/tests/integration/data/large_tokamak_1_MFILE.DAT index 0ba36d9cd5..2931ed4f94 100644 --- a/tests/integration/data/large_tokamak_1_MFILE.DAT +++ b/tests/integration/data/large_tokamak_1_MFILE.DAT @@ -1334,7 +1334,7 @@ fjprot = 1.0 icc = 36 ixc = 54 * ftmargtf * Minimum allowable temperature margin [K] -tmargmin = 1.5 +temp_tf_superconductor_margin_min = 1.5 * CS coil temp margin lower limit * *---------------------------------* diff --git a/tests/integration/data/large_tokamak_2_MFILE.DAT b/tests/integration/data/large_tokamak_2_MFILE.DAT index 640640c6b0..cfc40b0c81 100644 --- a/tests/integration/data/large_tokamak_2_MFILE.DAT +++ b/tests/integration/data/large_tokamak_2_MFILE.DAT @@ -1335,7 +1335,7 @@ fjprot = 1.0 icc = 36 ixc = 54 * ftmargtf * Minimum allowable temperature margin [K] -tmargmin = 1.5 +temp_tf_superconductor_margin_min = 1.5 * CS coil temp margin lower limit * *---------------------------------* diff --git a/tests/integration/data/large_tokamak_3_MFILE.DAT b/tests/integration/data/large_tokamak_3_MFILE.DAT index 2c49f31188..67adf75c57 100644 --- a/tests/integration/data/large_tokamak_3_MFILE.DAT +++ b/tests/integration/data/large_tokamak_3_MFILE.DAT @@ -1335,7 +1335,7 @@ fjprot = 1.0 icc = 36 ixc = 54 * ftmargtf * Minimum allowable temperature margin [K] -tmargmin = 1.5 +temp_tf_superconductor_margin_min = 1.5 * CS coil temp margin lower limit * *---------------------------------* diff --git a/tests/integration/data/large_tokamak_4_MFILE.DAT b/tests/integration/data/large_tokamak_4_MFILE.DAT index 9748b8980c..af00e406a9 100644 --- a/tests/integration/data/large_tokamak_4_MFILE.DAT +++ b/tests/integration/data/large_tokamak_4_MFILE.DAT @@ -1335,7 +1335,7 @@ fjprot = 1.0 icc = 36 ixc = 54 * ftmargtf * Minimum allowable temperature margin [K] -tmargmin = 1.5 +temp_tf_superconductor_margin_min = 1.5 * CS coil temp margin lower limit * *---------------------------------* diff --git a/tests/integration/data/large_tokamak_IN.DAT b/tests/integration/data/large_tokamak_IN.DAT index 16230592e5..6c9514ac9d 100644 --- a/tests/integration/data/large_tokamak_IN.DAT +++ b/tests/integration/data/large_tokamak_IN.DAT @@ -94,7 +94,7 @@ icc = 35 *--------------------------* icc = 36 * Minimum allowable temperature margin [K] -tmargmin = 1.5 +temp_tf_superconductor_margin_min = 1.5 * CS coil temp margin lower limit * *---------------------------------* diff --git a/tests/integration/data/large_tokamak_MFILE.DAT b/tests/integration/data/large_tokamak_MFILE.DAT index 2b6c6ef64b..f221e52c6c 100644 --- a/tests/integration/data/large_tokamak_MFILE.DAT +++ b/tests/integration/data/large_tokamak_MFILE.DAT @@ -16913,7 +16913,7 @@ icc = 35 *--------------------------* icc = 36 * Minimum allowable temperature margin [K] -tmargmin = 1.5 +temp_tf_superconductor_margin_min = 1.5 * CS coil temp margin lower limit * *---------------------------------* diff --git a/tests/integration/data/large_tokamak_eval.IN.DAT b/tests/integration/data/large_tokamak_eval.IN.DAT index 29ccd473a4..42a25e3fb1 100644 --- a/tests/integration/data/large_tokamak_eval.IN.DAT +++ b/tests/integration/data/large_tokamak_eval.IN.DAT @@ -379,7 +379,7 @@ dr_tf_wp_with_insulation = 0.5153787768966674 * radial thickness of winding pack dx_tf_turn_steel = 0.008012110032981922 * TF coil conduit case thickness (m) (`iteration variable 58`) dx_tf_wp_insulation = 0.008 * Thickness of the ground insulation layer surrounding (m) temp_cs_superconductor_margin_min = 1.5 * minimum allowable temperature margin ; CS (K) -tmargmin = 1.5 * minimum allowable temperature margin ; TFC AND CS (K) +temp_tf_superconductor_margin_min = 1.5 * minimum allowable temperature margin ; TF (K) v_tf_coil_dump_quench_max_kv = 10.0 * max voltage across TF coil during quench (kV) (`iteration variable 52`) f_a_tf_turn_cable_space_extra_void = 0.3 * coolant fraction of TFC 'cable' (`i_tf_sup=1`); or of TFC leg (`i_tf_sup=0`) flu_tf_neutron_fast_max = 1e22 * max fluence in the TF coil diff --git a/tests/integration/data/scan_2D_MFILE.DAT b/tests/integration/data/scan_2D_MFILE.DAT index 6ebf98ee83..c993437740 100644 --- a/tests/integration/data/scan_2D_MFILE.DAT +++ b/tests/integration/data/scan_2D_MFILE.DAT @@ -17628,7 +17628,7 @@ fjprot = 1.0 icc = 36 ixc = 54 * ftmargtf * Minimum allowable temperature margin [K] -tmargmin = 1.5 +temp_tf_superconductor_margin_min = 1.5 * CS coil temp margin lower limit * *---------------------------------* diff --git a/tests/integration/data/scan_MFILE.DAT b/tests/integration/data/scan_MFILE.DAT index 0258654bd3..dad7ddace2 100644 --- a/tests/integration/data/scan_MFILE.DAT +++ b/tests/integration/data/scan_MFILE.DAT @@ -9292,7 +9292,7 @@ dx_tf_turn_insulation = 2.0d-3 * Conduit insulation thickness (m) dr_tf_nose_case = 0.495 * Inboard tf coil case outer (non-plasma side) thickness (m) dx_tf_turn_steel = 0.008 * Tf coil conduit case thickness (m) dx_tf_wp_insulation = 0.008 * Ground insulation thickness surrounding winding pack (m) -tmargmin = 1.500 * Minimum allowable temperature margin (cs and tf coils) (k) +temp_tf_superconductor_margin_min = 1.5 * Minimum allowable temperature margin (TF coils) (k) v_tf_coil_dump_quench_max_kv = 10.00 * Max voltage across tf coil during quench (kv) f_a_tf_turn_cable_space_extra_void = 0.300 * Coolant fraction of tfc 'cable' (i_tf_sup=1); or of tfc leg (i_tf_sup=0) i_tf_sc_mat = 5 diff --git a/tests/unit/data/large_tokamak_IN.DAT b/tests/unit/data/large_tokamak_IN.DAT index 16230592e5..6c9514ac9d 100644 --- a/tests/unit/data/large_tokamak_IN.DAT +++ b/tests/unit/data/large_tokamak_IN.DAT @@ -94,7 +94,7 @@ icc = 35 *--------------------------* icc = 36 * Minimum allowable temperature margin [K] -tmargmin = 1.5 +temp_tf_superconductor_margin_min = 1.5 * CS coil temp margin lower limit * *---------------------------------* diff --git a/tests/unit/data/large_tokamak_MFILE.DAT b/tests/unit/data/large_tokamak_MFILE.DAT index 4fbc61943a..67fa4c827b 100644 --- a/tests/unit/data/large_tokamak_MFILE.DAT +++ b/tests/unit/data/large_tokamak_MFILE.DAT @@ -7752,7 +7752,7 @@ fjprot = 1.0 icc = 36 ixc = 54 * ftmargtf * Minimum allowable temperature margin [K] -tmargmin = 1.5 +temp_tf_superconductor_margin_min = 1.5 * CS coil temp margin lower limit * *---------------------------------* From c09fcd949c512f215905866718e6eeda96c91223 Mon Sep 17 00:00:00 2001 From: Matti Coleman Date: Fri, 16 Oct 2026 08:32:51 +0200 Subject: [PATCH 15/35] purge more tmargmin from docs --- documentation/source/eng-models/central-solenoid.md | 2 +- documentation/source/eng-models/tf-coil-superconducting.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/documentation/source/eng-models/central-solenoid.md b/documentation/source/eng-models/central-solenoid.md index d489224950..e26e92a190 100644 --- a/documentation/source/eng-models/central-solenoid.md +++ b/documentation/source/eng-models/central-solenoid.md @@ -464,7 +464,7 @@ The critical current density *J*crit is a function of the temperature The temperature margin $\Delta$*T* is the difference between the current sharing temperature and the operating temperature. The current sharing temperature is the temperature at which *J*crit is equal to the operating current density *J*op. The minimum allowed $\Delta$*T* can be -set using input parameter `tmargmin` together with constraint equation no. 60. +set using input parameter `temp_cs_superconductor_margin_min` together with constraint equation no. 60. It is recommended that EITHER the temperature margin constraint (60), OR the current density constraints (26 and 27) are activated. diff --git a/documentation/source/eng-models/tf-coil-superconducting.md b/documentation/source/eng-models/tf-coil-superconducting.md index aefd02bda4..e16dcd5a45 100644 --- a/documentation/source/eng-models/tf-coil-superconducting.md +++ b/documentation/source/eng-models/tf-coil-superconducting.md @@ -241,7 +241,7 @@ Three constraints are relevant to the operating current density $J_{\mbox{op}}$ - Temperature margin (`constraint 36`) -- The critical current density $J_{\mbox{crit}}$ falls with the temperature of the superconductor. The temperature margin $\Delta T$ is the difference between the current sharing temperature (at which $J_{\mbox{crit}}$ would be equal to $J_{\mbox{op}}$) and the operating temperature. The minimum allowed $\Delta T$ -can be set using `tmargmin` together with constraint equation 36. Note that if the temperature margin is positive, $J_{\mbox{op}}$ is guaranteed to be lower than \jcrit, and so constraints 33 and 36 need not both be turned on. It is recommended that only one of these two constraints is activated. +can be set using `temp_tf_superconductor_margin_min` together with constraint equation 36. Note that if the temperature margin is positive, $J_{\mbox{op}}$ is guaranteed to be lower than \jcrit, and so constraints 33 and 36 need not both be turned on. It is recommended that only one of these two constraints is activated. --------- From 3a1b3e281c1f6395d0434cc2f6d1a76177b89af4 Mon Sep 17 00:00:00 2001 From: Matti Coleman Date: Fri, 16 Oct 2026 08:49:59 +0200 Subject: [PATCH 16/35] fix tests --- process/models/tfcoil/superconducting.py | 7 ++++--- tests/unit/models/tfcoil/test_sctfcoil.py | 18 ++++++++++-------- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/process/models/tfcoil/superconducting.py b/process/models/tfcoil/superconducting.py index 08021477b9..958f4bd61d 100644 --- a/process/models/tfcoil/superconducting.py +++ b/process/models/tfcoil/superconducting.py @@ -2246,7 +2246,10 @@ def objective(t): result = optimize.minimize( objective, x0=[temp_tf_coolant_peak_field], - bounds=[(1e-9, tc0m)], + # These are reasonable bounds (near absolute zero, and 80% of crit T at 0 + # field). They are used to protect against unkludged and NaN results from + # some of the superconducting parameterisations + bounds=[(1e-3, 0.8 * tc0m)], method="Powell", options={"xtol": 1e-6, "ftol": 1e-10, "maxiter": 100}, ) @@ -2263,8 +2266,6 @@ def objective(t): t_zero_margin = float(result.x[0]) margin = t_zero_margin - temp_tf_coolant_peak_field - # MC: wtf is this? kill it with fire - data.tfcoil.temp_margin = margin if margin <= 0: logger.error( diff --git a/tests/unit/models/tfcoil/test_sctfcoil.py b/tests/unit/models/tfcoil/test_sctfcoil.py index efcc160e77..ca94fc92cc 100644 --- a/tests/unit/models/tfcoil/test_sctfcoil.py +++ b/tests/unit/models/tfcoil/test_sctfcoil.py @@ -149,9 +149,9 @@ def test_protect(protectparam, sctfcoil): class SuperconParam(NamedTuple): temp_tf_superconductor_margin_min: Any = None - n_tf_coils: Any = None + temp_tf_superconductor_margin: Any = None - temp_margin: Any = None + n_tf_coils: Any = None dia_tf_turn_coolant_channel: Any = None @@ -213,7 +213,7 @@ class SuperconParam(NamedTuple): tcritsc: Any = None - expected_temp_margin: Any = None + expected_temp_tf_superconductor_margin: Any = None expected_j_tf_wp_quench_heat_max: Any = None @@ -242,7 +242,7 @@ class SuperconParam(NamedTuple): SuperconParam( temp_tf_superconductor_margin_min=1.5, n_tf_coils=16, - temp_margin=0, + temp_tf_superconductor_margin=0, dia_tf_turn_coolant_channel=0.010000000000000002, c_tf_turn=74026.751437500003, b_tf_inboard_peak_with_ripple=12.48976756562082, @@ -275,7 +275,7 @@ class SuperconParam(NamedTuple): detection_time=0.0, bcritsc=24, tcritsc=16, - expected_temp_margin=2.864553846654988, + expected_temp_tf_superconductor_margin=2.864553846654988, expected_j_tf_wp_quench_heat_max=17213147.288375787, expected_j_tf_wp_critical=49719296.722920775, expected_vd=9988.2637896807955, @@ -290,7 +290,7 @@ class SuperconParam(NamedTuple): SuperconParam( temp_tf_superconductor_margin_min=1.5, n_tf_coils=16, - temp_margin=2.3431632224075836, + temp_tf_superconductor_margin=2.3431632224075836, dia_tf_turn_coolant_channel=0.010000000000000002, c_tf_turn=74026.751437500003, b_tf_inboard_peak_with_ripple=12.48976756562082, @@ -321,7 +321,7 @@ class SuperconParam(NamedTuple): detection_time=0.0, bcritsc=24, tcritsc=16, - expected_temp_margin=2.864553846654988, + expected_temp_tf_superconductor_margin=2.864553846654988, expected_j_tf_wp_quench_heat_max=17213147.288375787, expected_j_tf_wp_critical=49719296.722920775, expected_vd=10001.287165953383, @@ -363,7 +363,9 @@ def test_supercon(superconparam, monkeypatch, cicc_sctfcoil): ) monkeypatch.setattr( - cicc_sctfcoil.data.tfcoil, "temp_margin", superconparam.temp_margin + cicc_sctfcoil.data.tfcoil, + "temp_tf_superconductor_margin", + superconparam.temp_tf_superconductor_margin, ) monkeypatch.setattr(cicc_sctfcoil.data.tfcoil, "rrr_tf_cu", superconparam.cu_rrr) From de3d9b556f72c3b9cacf63d8c90a6887675ba31c Mon Sep 17 00:00:00 2001 From: Matti Coleman Date: Fri, 16 Oct 2026 10:18:37 +0200 Subject: [PATCH 17/35] improve T margin calculation --- process/models/tfcoil/superconducting.py | 38 ++++++++++++++---------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/process/models/tfcoil/superconducting.py b/process/models/tfcoil/superconducting.py index 958f4bd61d..4b7acbbde7 100644 --- a/process/models/tfcoil/superconducting.py +++ b/process/models/tfcoil/superconducting.py @@ -2239,28 +2239,36 @@ def calculate_superconductor_temperature_margin( j_scale = max(abs(j_superconductor), 1.0) - def objective(t): - residual = superconductors.superconductor_current_density_margin(t[0], *args) - return (residual / j_scale) ** 2 + def residual(t): + return superconductors.superconductor_current_density_margin(t, *args) - result = optimize.minimize( - objective, + result = optimize.least_squares( + lambda t: residual(t[0]) / j_scale, x0=[temp_tf_coolant_peak_field], - # These are reasonable bounds (near absolute zero, and 80% of crit T at 0 - # field). They are used to protect against unkludged and NaN results from - # some of the superconducting parameterisations - bounds=[(1e-3, 0.8 * tc0m)], - method="Powell", - options={"xtol": 1e-6, "ftol": 1e-10, "maxiter": 100}, + bounds=([1e-3], [0.999 * tc0m]), + method="trf", + xtol=1e-9, + ftol=None, + gtol=None, + max_nfev=50, ) if not result.success: - raise ProcessValueError(result.message) + raise ProcessValueError( + f"Error in TF temperature margin calculation: {result.message}" + ) + + normalised_residual = float(result.fun[0]) + residual_tol = 1e-10 - if not result.fun <= 1e-12: + if ( + not np.isfinite(normalised_residual) + or abs(normalised_residual) > residual_tol + ): raise ProcessValueError( - "Superconductor temperature margin minimum is not sufficiently" - f" close to zero: {result.fun}" + "Superconductor temperature margin residual is not sufficiently " + f"close to zero: {normalised_residual:.6g} " + f"(normalised tolerance: {residual_tol:.6g})" ) t_zero_margin = float(result.x[0]) From 0b4ad621f1bb096bac04247a9a753f2402d5cf66 Mon Sep 17 00:00:00 2001 From: Matti Coleman Date: Fri, 16 Oct 2026 10:19:25 +0200 Subject: [PATCH 18/35] clean up --- process/models/tfcoil/superconducting.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/process/models/tfcoil/superconducting.py b/process/models/tfcoil/superconducting.py index 4b7acbbde7..335d1918eb 100644 --- a/process/models/tfcoil/superconducting.py +++ b/process/models/tfcoil/superconducting.py @@ -2239,11 +2239,11 @@ def calculate_superconductor_temperature_margin( j_scale = max(abs(j_superconductor), 1.0) - def residual(t): - return superconductors.superconductor_current_density_margin(t, *args) - result = optimize.least_squares( - lambda t: residual(t[0]) / j_scale, + lambda t: ( + superconductors.superconductor_current_density_margin(t[0], *args) + / j_scale + ), x0=[temp_tf_coolant_peak_field], bounds=([1e-3], [0.999 * tc0m]), method="trf", From b4783a92169d8691b80be7f13f3073b0777ca546 Mon Sep 17 00:00:00 2001 From: Matti Coleman Date: Fri, 16 Oct 2026 10:19:52 +0200 Subject: [PATCH 19/35] clean up --- process/models/tfcoil/superconducting.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/process/models/tfcoil/superconducting.py b/process/models/tfcoil/superconducting.py index 335d1918eb..9120b30db8 100644 --- a/process/models/tfcoil/superconducting.py +++ b/process/models/tfcoil/superconducting.py @@ -2237,12 +2237,12 @@ def calculate_superconductor_temperature_margin( f"i_tf_superconductor={i_tf_superconductor}" ) - j_scale = max(abs(j_superconductor), 1.0) + j_scale = 1.0 / max(abs(j_superconductor), 1.0) result = optimize.least_squares( lambda t: ( - superconductors.superconductor_current_density_margin(t[0], *args) - / j_scale + j_scale + * superconductors.superconductor_current_density_margin(t[0], *args) ), x0=[temp_tf_coolant_peak_field], bounds=([1e-3], [0.999 * tc0m]), From 479455ab38da71beab9d737e7a275fe9aaeebdb7 Mon Sep 17 00:00:00 2001 From: Matti Coleman Date: Fri, 16 Oct 2026 12:52:58 +0200 Subject: [PATCH 20/35] obslete var and temp --- process/core/data_structure/obsolete_vars.py | 4 + process/core/solver/constraints.py | 14 +- process/models/tfcoil/superconducting.py | 160 ++++++++++++++++++- 3 files changed, 168 insertions(+), 10 deletions(-) diff --git a/process/core/data_structure/obsolete_vars.py b/process/core/data_structure/obsolete_vars.py index fa3bb1c77f..b8a21ece37 100644 --- a/process/core/data_structure/obsolete_vars.py +++ b/process/core/data_structure/obsolete_vars.py @@ -483,6 +483,10 @@ "temp_plasma_separatrix_kev": "temp_plasma_separatrix_electron_kev", "iohcl": "i_cs_use", "beta_total_vol_avg": None, + "tmargmin": [ + "temp_tf_superconductor_margin_min", + "temp_cs_superconductor_margin_min", + ], } OBS_VARS_HELP = { diff --git a/process/core/solver/constraints.py b/process/core/solver/constraints.py index d13eac9cb9..0bb4c7500b 100644 --- a/process/core/solver/constraints.py +++ b/process/core/solver/constraints.py @@ -1002,15 +1002,15 @@ def constraint_equation_36(constraint_registration, data): """Equation for TF coil s/c temperature margin lower limit (SCTF) by means of the actual to critical current density - j_tf_str: - actual superconductor strand current density (A/m²) - j_crit_str_tf: - superconductor strand critical current density under operating + j_tf_wp_critical: + winding pack critical current density under operating conditions (A/m²) + j_tf_wp: + winding pack current density (A/m²) """ - return geq( - data.tfcoil.j_tf_str, - data.tfcoil.j_crit_str_tf, + return leq( + data.tfcoil.j_tf_wp, + data.tfcoil.j_tf_wp_critical, constraint_registration, ) diff --git a/process/models/tfcoil/superconducting.py b/process/models/tfcoil/superconducting.py index 9120b30db8..b93c03e02a 100644 --- a/process/models/tfcoil/superconducting.py +++ b/process/models/tfcoil/superconducting.py @@ -2149,7 +2149,7 @@ def run_and_output_stress(self) -> None: self.data.tfcoil.sig_tf_wp = 0.0e0 @staticmethod - def calculate_superconductor_temperature_margin( + def calculate_superconductor_temperature_margin2( i_tf_superconductor: int, j_superconductor: float, b_tf_inboard_peak: float, @@ -2286,6 +2286,143 @@ def calculate_superconductor_temperature_margin( return margin + @staticmethod + def calculate_superconductor_temperature_margin( + i_tf_superconductor: int, + j_superconductor: float, + b_tf_inboard_peak: float, + strain: float, + bc20m: float, + tc0m: float, + c0: float, + temp_tf_coolant_peak_field: float, + data: DataStructure, + ): + """Calculate the temperature margin of the TF superconductor. + + Parameters + ---------- + i_tf_superconductor: + Switch for conductor type: + - 1: ITER Nb3Sn, standard parameters + - 2: Bi-2212 High Temperature Superconductor + - 3: NbTi + - 4: ITER Nb3Sn, user-defined parameters + - 5: WST Nb3Sn parameterisation + - 7: Durham Ginzburg-Landau Nb-Ti parameterisation + - 8: Durham Ginzburg-Landau critical surface model for REBCO + - 9: Hazelton experimental data + Zhai conceptual model for REBCO + j_superconductor: + Current density in superconductor (A/m²). + b_tf_inboard_peak: + Peak field at conductor (T). + strain: + Strain on superconductor. + bc20m: + Critical field at zero temperature and strain (T). + tc0m: + Critical temperature at zero field and strain (K). + c0: + Constant used in NbTi critical current density calculation (A/m²). + temp_tf_coolant_peak_field: + He temperature at peak field point (K). + + Returns + ------- + type + temp_tf_superconductor_margin. + + + Raises + ------ + ProcessValueError + If an unknown superconductor type is specified. + """ + # ================================================================= + # Calculate temperature margin of superconductor + + # Temperature margin (already calculated in superconductors.bi2212 for + # i_tf_superconductor=2) + match SuperconductorModel(i_tf_superconductor): + case SuperconductorModel.BI2212: + # Bi-2212: temperature margin already calculated elsewhere + temp_tf_superconductor_margin = 0.0 + # Find temperature at which current density margin = 0 + case ( + SuperconductorModel.ITER_NB3SN + | SuperconductorModel.OLD_LUBELL_NBTI + | SuperconductorModel.USER_DEFINED_NB3SN + | SuperconductorModel.WST_NB3SN + | SuperconductorModel.DURHAM_NBTI + | SuperconductorModel.DURHAM_REBCO + | SuperconductorModel.HAZELTON_ZHAI_REBCO + ): + if ( + SuperconductorModel(i_tf_superconductor) + == SuperconductorModel.OLD_LUBELL_NBTI + ): + arguments = ( + i_tf_superconductor, + j_superconductor, + b_tf_inboard_peak, + strain, + bc20m, + tc0m, + data.superconducting_tfcoil.dr_tf_hts_tape, + data.superconducting_tfcoil.dx_tf_hts_tape_rebco, + data.superconducting_tfcoil.dx_tf_hts_tape_total, + c0, + ) + else: + arguments = ( + i_tf_superconductor, + j_superconductor, + b_tf_inboard_peak, + strain, + bc20m, + tc0m, + data.superconducting_tfcoil.dr_tf_hts_tape, + data.superconducting_tfcoil.dx_tf_hts_tape_rebco, + data.superconducting_tfcoil.dx_tf_hts_tape_total, + ) + + another_estimate = 2 * temp_tf_coolant_peak_field + ( + t_zero_margin, + _, + ) = optimize.newton( + superconductors.superconductor_current_density_margin, + temp_tf_coolant_peak_field, + fprime=None, + args=arguments, + tol=1.0e-06, + maxiter=50, + fprime2=None, + x1=another_estimate, + rtol=1.0e-6, + full_output=True, + disp=True, + ) + temp_tf_superconductor_margin = ( + t_zero_margin - temp_tf_coolant_peak_field + ) + + if temp_tf_superconductor_margin <= 0.0e0: + logger.error( + f"""Negative TFC temperature margin + temp_tf_superconductor_margin: {temp_tf_superconductor_margin} + b_tf_inboard_peak: {b_tf_inboard_peak} + j_superconductor: {j_superconductor} + """ + ) + case _: + raise ProcessValueError( + f"Unknown superconductor type: " + f"i_tf_superconductor={i_tf_superconductor}" + ) + + return temp_tf_superconductor_margin + @dataclass(slots=True) class TFGeneralTurnGeometry: @@ -2745,7 +2882,6 @@ def run(self, output: bool = False): ) ) - self.data.tfcoil.j_tf_str = critical_superconductor_info.j_superconductor self.data.tfcoil.j_tf_wp_critical = critical_superconductor_info.j_tf_wp_critical d_sc_tf.j_tf_superconductor_critical = ( @@ -2766,6 +2902,25 @@ def run(self, output: bool = False): critical_superconductor_info.c_turn_cables_critical ) + # if self.data.tfcoil.i_str_wp == 0: + # strain = self.data.tfcoil.str_tf_con_res + # else: + # strain = self.data.tfcoil.str_wp + + # self.data.tfcoil.temp_tf_superconductor_margin = ( + # self.calculate_superconductor_temperature_margin( + # i_tf_superconductor=self.data.tfcoil.i_tf_sc_mat, + # j_superconductor=d_sc_tf.j_tf_superconductor, + # b_tf_inboard_peak=self.data.tfcoil.b_tf_inboard_peak_with_ripple, + # strain=strain, + # bc20m=d_sc_tf.b_tf_superconductor_critical_zero_temp_strain, + # tc0m=d_sc_tf.temp_tf_superconductor_critical_zero_field_strain, + # c0=1.0e10, + # temp_tf_coolant_peak_field=self.data.tfcoil.tftmp, + # data=self.data, + # ) + # ) + # Do current density protection calculation # Only setup for Nb3Sn at present. if ( @@ -4000,7 +4155,6 @@ def run(self, output: bool = False): a_tf_croco_strand=d_sc_tf.a_tf_croco_strand, ) - self.data.tfcoil.j_tf_str = superconductor_critical_properties.j_superconductor self.data.tfcoil.j_tf_wp_critical = ( superconductor_critical_properties.j_tf_wp_critical ) From 75119bf4228a826c1f4a15d4345a479920d9fb2f Mon Sep 17 00:00:00 2001 From: Matti Coleman Date: Fri, 16 Oct 2026 13:32:49 +0200 Subject: [PATCH 21/35] refactor mess --- process/models/tfcoil/superconducting.py | 322 ++++++----------------- 1 file changed, 76 insertions(+), 246 deletions(-) diff --git a/process/models/tfcoil/superconducting.py b/process/models/tfcoil/superconducting.py index b93c03e02a..d051553b00 100644 --- a/process/models/tfcoil/superconducting.py +++ b/process/models/tfcoil/superconducting.py @@ -2149,7 +2149,7 @@ def run_and_output_stress(self) -> None: self.data.tfcoil.sig_tf_wp = 0.0e0 @staticmethod - def calculate_superconductor_temperature_margin2( + def calculate_superconductor_temperature_margin( i_tf_superconductor: int, j_superconductor: float, b_tf_inboard_peak: float, @@ -2287,7 +2287,7 @@ def calculate_superconductor_temperature_margin2( return margin @staticmethod - def calculate_superconductor_temperature_margin( + def calculate_superconductor_temperature_margin_old( i_tf_superconductor: int, j_superconductor: float, b_tf_inboard_peak: float, @@ -3041,10 +3041,10 @@ def tf_cable_in_conduit_superconductor_properties( Not used for the CroCo conductor. """ - if ( - SuperconductorModel(i_tf_superconductor).sc_shape - != SuperconductorShape.CABLE - ): + model = SuperconductorModel(i_tf_superconductor) + tfcoil = data.tfcoil + + if model.sc_shape != SuperconductorShape.CABLE: raise ProcessValueError( "Cannot calculate cable in conduit superconductor properties for " "non-cable superconductors. Change `i_tf_sc_mat` to a cable " @@ -3052,245 +3052,83 @@ def tf_cable_in_conduit_superconductor_properties( "superconductors." ) - # Guard against negative conductor fraction f_a_tf_turn_cable_space_conductor - # Kludge to allow solver to continue and hopefully be constrained away - # from this point - f_a_tf_turn_cable_space_cooling = min(f_a_tf_turn_cable_space_cooling, 0.99) + strain = tfcoil.str_tf_con_res if tfcoil.i_str_wp == 0 else tfcoil.str_wp + non_copper_fraction = 1.0 - f_a_tf_turn_cable_copper + conductor_fraction = 1.0 - min(f_a_tf_turn_cable_space_cooling, 0.99) - # Conductor fraction (including central helium channel) - f_a_tf_turn_cable_space_conductor = 1.0e0 - f_a_tf_turn_cable_space_cooling + conditions = { + "temp_conductor": temp_tf_coolant_peak_field, + "b_conductor": b_tf_inboard_peak, + } - if data.tfcoil.i_str_wp == 0: - strain = data.tfcoil.str_tf_con_res - else: - strain = data.tfcoil.str_wp + # Defaults for models whose current density excludes copper. + cable_area = a_tf_turn_cable_space_effective + bc20m = tc0m = None - # ================================================================= - match SuperconductorModel(i_tf_superconductor): - # ITER Nb3Sn critical surface parameterization - case SuperconductorModel.ITER_NB3SN: - # Peak field and temperature at zero strain - bc20m = SuperconductorModel.ITER_NB3SN.b_crit_zero_field_strain # [T] - tc0m = SuperconductorModel.ITER_NB3SN.temp_crit_zero_field_strain # [K] + match model: + case ( + SuperconductorModel.ITER_NB3SN + | SuperconductorModel.USER_DEFINED_NB3SN + | SuperconductorModel.WST_NB3SN + ): + if model == SuperconductorModel.USER_DEFINED_NB3SN: + bc20m, tc0m = bcritsc, tcritsc + else: + bc20m = model.b_crit_zero_field_strain + tc0m = model.temp_crit_zero_field_strain - # If strain limit achieved, throw a warning and use the lower strain - if abs(strain) > 0.5e-2: + if abs(strain) > 0.005: logger.error( f"TF strain={strain} was outside the region of applicability. " - f"Used lower strain." + "Used lower strain." ) - strain = np.sign(strain) * 0.5e-2 + strain = np.sign(strain) * 0.005 - # j_superconductor_critical returned by superconductors.itersc is the - # critical current density in the superconductor - not the whole strand, - # which contains copper - j_superconductor_critical, _, _ = superconductors.itersc( - temp_conductor=temp_tf_coolant_peak_field, - b_conductor=b_tf_inboard_peak, + critical_surface = ( + superconductors.western_superconducting_nb3sn + if model == SuperconductorModel.WST_NB3SN + else superconductors.itersc + ) + j_superconductor_critical, _, _ = critical_surface( + **conditions, strain=strain, b_c20max=bc20m, temp_c0max=tc0m, ) - # Scale for the copper area fraction of the cable - j_cables_critical = j_superconductor_critical * ( - 1.0e0 - f_a_tf_turn_cable_copper - ) - - # Critical current in turn all turn cables - c_turn_cables_critical = ( - j_cables_critical * a_tf_turn_cable_space_effective - ) - - # Strand critical current calculation for costing in $/kAm - # = Superconducting filaments jc * (1 - strand copper fraction) - data.tfcoil.j_crit_str_tf = j_superconductor_critical * ( - 1.0e0 - f_a_tf_turn_cable_copper - ) - - # ================================================================= - - # Bi-2212 high temperature superconductor parameterization case SuperconductorModel.BI2212: - # Current density in a strand of Bi-2212 conductor - # N.B. jcrit returned by superconductors.bi2212 is the critical - # current density in the strand, not just the superconducting portion. - # The parameterization for j_crit_cable assumes a particular strand - # composition that does not require a user-defined copper fraction, - # so this is irrelevant in this model - j_strand = ( - j_tf_wp - * a_tf_turn - / (a_tf_turn_cable_space * f_a_tf_turn_cable_space_conductor) - ) + # This model already accounts for the strand composition. + cable_area = a_tf_turn_cable_space * conductor_fraction + j_strand = j_tf_wp * a_tf_turn / cable_area - j_crit_cable, _ = superconductors.bi2212( - b_conductor=b_tf_inboard_peak, + j_cables_critical, _ = superconductors.bi2212( + **conditions, jstrand=j_strand, - temp_conductor=temp_tf_coolant_peak_field, f_strain=f_strain_scale, ) - j_superconductor_critical = j_crit_cable / ( - 1.0e0 - f_a_tf_turn_cable_copper - ) - # Critical current in cable - c_turn_cables_critical = ( - j_crit_cable - * a_tf_turn_cable_space - * f_a_tf_turn_cable_space_conductor - ) - - # Strand critical current calulation for costing in $ / kAm - # Copper in the strand is already accounted for - data.tfcoil.j_crit_str_tf = j_superconductor_critical - # ================================================================= + j_superconductor_critical = j_cables_critical / non_copper_fraction - # NbTi data case SuperconductorModel.OLD_LUBELL_NBTI: - bc20m = ( - SuperconductorModel.OLD_LUBELL_NBTI.b_crit_zero_field_strain - ) # [T] - tc0m = ( - SuperconductorModel.OLD_LUBELL_NBTI.temp_crit_zero_field_strain - ) # [K] - c0 = 1.0e10 # [A/m²] + bc20m = model.b_crit_zero_field_strain + tc0m = model.temp_crit_zero_field_strain j_superconductor_critical, _ = superconductors.jcrit_nbti( - temp_conductor=temp_tf_coolant_peak_field, - b_conductor=b_tf_inboard_peak, - c0=c0, - b_c20max=bc20m, - temp_c0max=tc0m, - ) - - # Scale for the copper area fraction of the cable - j_cables_critical = j_superconductor_critical * ( - 1.0e0 - f_a_tf_turn_cable_copper - ) - - # Critical current in turn all turn cables - c_turn_cables_critical = ( - j_cables_critical * a_tf_turn_cable_space_effective - ) - - # Strand critical current calulation for costing in $ / kAm - # = superconducting filaments jc * (1 -strand copper fraction) - data.tfcoil.j_crit_str_tf = j_superconductor_critical * ( - 1.0e0 - f_a_tf_turn_cable_copper - ) - - # ================================================================= - - # ITER Nb3Sn parameterization, but user-defined parameters - case SuperconductorModel.USER_DEFINED_NB3SN: - bc20m = bcritsc # [T] - tc0m = tcritsc # [K] - - # If strain limit achieved, throw a warning and use the lower strain - if abs(strain) > 0.5e-2: - logger.error( - f"TF strain={strain} was outside the region of applicability. " - f"Used lower strain." - ) - strain = np.sign(strain) * 0.5e-2 - - j_superconductor_critical, _, _ = superconductors.itersc( - temp_conductor=temp_tf_coolant_peak_field, - b_conductor=b_tf_inboard_peak, - strain=strain, + **conditions, + c0=1.0e10, b_c20max=bc20m, temp_c0max=tc0m, ) - # Scale for the copper area fraction of the cable - j_cables_critical = j_superconductor_critical * ( - 1.0e0 - f_a_tf_turn_cable_copper - ) - - # Critical current in turn all turn cables - c_turn_cables_critical = ( - j_cables_critical * a_tf_turn_cable_space_effective - ) - - # Strand critical current calulation for costing in $ / kAm - # = superconducting filaments jc * (1 -strand copper fraction) - data.tfcoil.j_crit_str_tf = j_superconductor_critical * ( - 1.0e0 - f_a_tf_turn_cable_copper - ) - - # ================================================================= - # WST Nb3Sn parameterisation - case SuperconductorModel.WST_NB3SN: - bc20m = SuperconductorModel.WST_NB3SN.b_crit_zero_field_strain # [T] - tc0m = SuperconductorModel.WST_NB3SN.temp_crit_zero_field_strain # [K] - - # If strain limit achieved, throw a warning and use the lower strain - if abs(strain) > 0.5e-2: - logger.error( - f"TF strain={strain} was outside the region of applicability. " - f"Used lower strain." - ) - strain = np.sign(strain) * 0.5e-2 - - # j_superconductor_critical returned by superconductors.itersc is the - # critical current density in the superconductor - not the whole strand, - # which contains copper - j_superconductor_critical, _, _ = ( - superconductors.western_superconducting_nb3sn( - temp_conductor=temp_tf_coolant_peak_field, - b_conductor=b_tf_inboard_peak, - strain=strain, - b_c20max=bc20m, - temp_c0max=tc0m, - ) - ) - # Scale for the copper area fraction of the cable - j_cables_critical = j_superconductor_critical * ( - 1.0e0 - f_a_tf_turn_cable_copper - ) - - # Critical current in turn all turn cables - c_turn_cables_critical = ( - j_cables_critical * a_tf_turn_cable_space_effective - ) - - # Strand critical current calulation for costing in $ / kAm - # = superconducting filaments jc * (1 -strand copper fraction) - data.tfcoil.j_crit_str_tf = j_superconductor_critical * ( - 1.0e0 - f_a_tf_turn_cable_copper - ) - - # ================================================================= - - # Durham Ginzburg-Landau Nb-Ti parameterisation case SuperconductorModel.DURHAM_NBTI: - bc20m = SuperconductorModel.DURHAM_NBTI.b_crit_zero_field_strain # [T] - tc0m = SuperconductorModel.DURHAM_NBTI.temp_crit_zero_field_strain # [K] + bc20m = model.b_crit_zero_field_strain + tc0m = model.temp_crit_zero_field_strain j_superconductor_critical, _, _ = superconductors.gl_nbti( - temp_conductor=temp_tf_coolant_peak_field, - b_conductor=b_tf_inboard_peak, + **conditions, strain=strain, b_c20max=bc20m, t_c0=tc0m, ) - # Scale for the copper area fraction of the cable - j_cables_critical = j_superconductor_critical * ( - 1.0e0 - f_a_tf_turn_cable_copper - ) - - # Critical current in turn all turn cables - c_turn_cables_critical = ( - j_cables_critical * a_tf_turn_cable_space_effective - ) - - # Strand critical current calulation for costing in $ / kAm - # = superconducting filaments jc * (1 -strand copper fraction) - data.tfcoil.j_crit_str_tf = j_superconductor_critical * ( - 1.0e0 - f_a_tf_turn_cable_copper - ) - - # ================================================================= case _: raise ProcessValueError( @@ -3298,48 +3136,40 @@ def tf_cable_in_conduit_superconductor_properties( i_tf_superconductor=i_tf_superconductor, ) - # ================================================================= - - # Critical current density in winding pack - # a_tf_turn : Area per turn - # (i.e. entire jacketed conductor with insulation) (m2) - j_tf_wp_critical = c_turn_cables_critical / a_tf_turn - - # Ratio of operating / critical current - f_c_tf_turn_operating_critical = c_tf_turn / c_turn_cables_critical - - # Operating current density - j_tf_coil_turn = c_tf_turn / a_tf_turn - - # Actual current density in superconductor, not including copper - - j_superconductor = f_c_tf_turn_operating_critical * j_superconductor_critical + if model == SuperconductorModel.BI2212: + # Preserve the original Bi-2212 costing convention. + tfcoil.j_crit_str_tf = j_superconductor_critical + else: + j_cables_critical = j_superconductor_critical * non_copper_fraction + tfcoil.j_crit_str_tf = j_cables_critical - # ================================================================= + c_turn_cables_critical = j_cables_critical * cable_area + operating_fraction = c_tf_turn / c_turn_cables_critical + j_superconductor = operating_fraction * j_superconductor_critical - if f_c_tf_turn_operating_critical <= 0e0: + if operating_fraction <= 0.0: logger.error( - f"""Negative Iop/Icrit for TF coil - jsc: {j_superconductor} - f_c_tf_turn_operating_critical: {f_c_tf_turn_operating_critical} - j_superconductor_critical: {j_superconductor_critical} - Check conductor dimensions. Cable space area a_tf_turn_cable_space - likely gone negative. a_tf_turn_cable_space: {a_tf_turn_cable_space} - This is likely because dr_tf_turn_cable_space or - dx_tf_turn_cable_space has gone negative: - dr_tf_turn_cable_space: - {data.superconducting_tfcoil.dr_tf_turn_cable_space} - dx_tf_turn_cable_space: - {data.superconducting_tfcoil.dx_tf_turn_cable_space} - """ + f"""Non-positive Iop/Icrit for TF coil + jsc: {j_superconductor} + f_c_tf_turn_operating_critical: {operating_fraction} + j_superconductor_critical: {j_superconductor_critical} + Check conductor dimensions. Cable space area a_tf_turn_cable_space + likely gone negative. a_tf_turn_cable_space: {a_tf_turn_cable_space} + This is likely because dr_tf_turn_cable_space or + dx_tf_turn_cable_space has gone negative: + dr_tf_turn_cable_space: + {data.superconducting_tfcoil.dr_tf_turn_cable_space} + dx_tf_turn_cable_space: + {data.superconducting_tfcoil.dx_tf_turn_cable_space} + """ ) return TFSuperconductorLimits( - j_tf_wp_critical=j_tf_wp_critical, + j_tf_wp_critical=c_turn_cables_critical / a_tf_turn, j_superconductor_critical=j_superconductor_critical, - f_c_tf_turn_operating_critical=f_c_tf_turn_operating_critical, + f_c_tf_turn_operating_critical=operating_fraction, j_superconductor=j_superconductor, - j_tf_coil_turn=j_tf_coil_turn, + j_tf_coil_turn=c_tf_turn / a_tf_turn, bc20m=bc20m, tc0m=tc0m, c_turn_cables_critical=c_turn_cables_critical, From 7af2676cd519d037ece13812e44d8654839aeb2f Mon Sep 17 00:00:00 2001 From: Matti Coleman Date: Fri, 16 Oct 2026 13:34:07 +0200 Subject: [PATCH 22/35] add comment --- process/models/tfcoil/superconducting.py | 1 + 1 file changed, 1 insertion(+) diff --git a/process/models/tfcoil/superconducting.py b/process/models/tfcoil/superconducting.py index d051553b00..ccdd8140ff 100644 --- a/process/models/tfcoil/superconducting.py +++ b/process/models/tfcoil/superconducting.py @@ -3098,6 +3098,7 @@ def tf_cable_in_conduit_superconductor_properties( case SuperconductorModel.BI2212: # This model already accounts for the strand composition. + # MC: returns bc20m = None, tc0m = None explicitly now cable_area = a_tf_turn_cable_space * conductor_fraction j_strand = j_tf_wp * a_tf_turn / cable_area From 5c59623b5353242ddced8af46f65acf15ef3352b Mon Sep 17 00:00:00 2001 From: Matti Coleman Date: Fri, 16 Oct 2026 13:59:32 +0200 Subject: [PATCH 23/35] more consistent constraint implementation on j_tf_wp --- process/core/solver/constraints.py | 7 ++--- process/data_structure/tfcoil_variables.py | 4 +-- process/models/tfcoil/superconducting.py | 36 +++++++++++++++++----- 3 files changed, 34 insertions(+), 13 deletions(-) diff --git a/process/core/solver/constraints.py b/process/core/solver/constraints.py index 0bb4c7500b..429f885869 100644 --- a/process/core/solver/constraints.py +++ b/process/core/solver/constraints.py @@ -1002,15 +1002,14 @@ def constraint_equation_36(constraint_registration, data): """Equation for TF coil s/c temperature margin lower limit (SCTF) by means of the actual to critical current density - j_tf_wp_critical: - winding pack critical current density under operating - conditions (A/m²) j_tf_wp: winding pack current density (A/m²) + j_tf_wp_temp_margin: + superconducting strand critical current density (A/m²) """ return leq( data.tfcoil.j_tf_wp, - data.tfcoil.j_tf_wp_critical, + data.tfcoil.j_tf_wp_temp_margin, constraint_registration, ) diff --git a/process/data_structure/tfcoil_variables.py b/process/data_structure/tfcoil_variables.py index 50fbe0d6c8..a81016dbc0 100644 --- a/process/data_structure/tfcoil_variables.py +++ b/process/data_structure/tfcoil_variables.py @@ -344,8 +344,8 @@ class TFData: conditions (A/m²). """ - j_tf_str: float = 0.0 - """j_tf_str : actual superconductor strand current density (A/m²)""" + j_tf_wp_temp_margin: float = 0.0 + """j_tf_wp_temp_margin : allowable TF coil winding pack current density, for specified temperature margin (A/m²)""" j_crit_str_0: list[float] = field( default_factory=lambda: np.array([ diff --git a/process/models/tfcoil/superconducting.py b/process/models/tfcoil/superconducting.py index ccdd8140ff..8525044658 100644 --- a/process/models/tfcoil/superconducting.py +++ b/process/models/tfcoil/superconducting.py @@ -2863,6 +2863,27 @@ def run(self, output: bool = False): * self.data.tfcoil.n_tf_coil_turns ) + margin_superconductor_info = self.tf_cable_in_conduit_superconductor_properties( + a_tf_turn_cable_space=self.data.tfcoil.a_tf_turn_cable_space_no_void, + a_tf_turn=self.data.tfcoil.a_tf_turn, + a_tf_turn_cable_space_effective=d_sc_tf.a_tf_turn_cable_space_effective, + f_a_tf_turn_cable_space_cooling=d_sc_tf.f_a_tf_turn_cable_space_cooling, + b_tf_inboard_peak=self.data.tfcoil.b_tf_inboard_peak_with_ripple, + f_a_tf_turn_cable_copper=self.data.tfcoil.f_a_tf_turn_cable_copper, + c_tf_turn=self.data.tfcoil.c_tf_turn, + j_tf_wp=self.data.tfcoil.j_tf_wp, + i_tf_superconductor=self.data.tfcoil.i_tf_sc_mat, + f_strain_scale=self.data.tfcoil.fhts, + temp_tf_coolant_peak_field=self.data.tfcoil.tftmp + + self.data.tfcoil.temp_tf_superconductor_margin_min, + bcritsc=self.data.tfcoil.bcritsc, + tcritsc=self.data.tfcoil.tcritsc, + data=self.data, + ) + self.data.tfcoil.j_tf_wp_temp_margin = ( + margin_superconductor_info.j_tf_wp_critical + ) + critical_superconductor_info = ( self.tf_cable_in_conduit_superconductor_properties( a_tf_turn_cable_space=self.data.tfcoil.a_tf_turn_cable_space_no_void, @@ -3063,7 +3084,6 @@ def tf_cable_in_conduit_superconductor_properties( # Defaults for models whose current density excludes copper. cable_area = a_tf_turn_cable_space_effective - bc20m = tc0m = None match model: case ( @@ -3098,7 +3118,9 @@ def tf_cable_in_conduit_superconductor_properties( case SuperconductorModel.BI2212: # This model already accounts for the strand composition. - # MC: returns bc20m = None, tc0m = None explicitly now + # MC: Comment for reviewers: in this case I imagine it would raise + # UnboundLocalError + bc20m = tc0m = 0.0 cable_area = a_tf_turn_cable_space * conductor_fraction j_strand = j_tf_wp * a_tf_turn / cable_area @@ -3145,14 +3167,14 @@ def tf_cable_in_conduit_superconductor_properties( tfcoil.j_crit_str_tf = j_cables_critical c_turn_cables_critical = j_cables_critical * cable_area - operating_fraction = c_tf_turn / c_turn_cables_critical - j_superconductor = operating_fraction * j_superconductor_critical + f_c_tf_turn_operating_critical = c_tf_turn / c_turn_cables_critical + j_superconductor = f_c_tf_turn_operating_critical * j_superconductor_critical - if operating_fraction <= 0.0: + if f_c_tf_turn_operating_critical <= 0.0: logger.error( f"""Non-positive Iop/Icrit for TF coil jsc: {j_superconductor} - f_c_tf_turn_operating_critical: {operating_fraction} + f_c_tf_turn_operating_critical: {f_c_tf_turn_operating_critical} j_superconductor_critical: {j_superconductor_critical} Check conductor dimensions. Cable space area a_tf_turn_cable_space likely gone negative. a_tf_turn_cable_space: {a_tf_turn_cable_space} @@ -3168,7 +3190,7 @@ def tf_cable_in_conduit_superconductor_properties( return TFSuperconductorLimits( j_tf_wp_critical=c_turn_cables_critical / a_tf_turn, j_superconductor_critical=j_superconductor_critical, - f_c_tf_turn_operating_critical=operating_fraction, + f_c_tf_turn_operating_critical=f_c_tf_turn_operating_critical, j_superconductor=j_superconductor, j_tf_coil_turn=c_tf_turn / a_tf_turn, bc20m=bc20m, From 2c7f632e7dbb27eb73622b95235004e88ac952a5 Mon Sep 17 00:00:00 2001 From: Matti Coleman Date: Fri, 16 Oct 2026 14:19:22 +0200 Subject: [PATCH 24/35] use in CROCO too --- process/models/tfcoil/superconducting.py | 103 +++++++++++++++++++---- 1 file changed, 85 insertions(+), 18 deletions(-) diff --git a/process/models/tfcoil/superconducting.py b/process/models/tfcoil/superconducting.py index 8525044658..12b8ddffaf 100644 --- a/process/models/tfcoil/superconducting.py +++ b/process/models/tfcoil/superconducting.py @@ -14,6 +14,7 @@ from process.core.exceptions import ProcessValueError from process.data_structure.superconducting_tf_coil_variables import TFWPIntegerTurnType from process.models import superconductors +from process.models.availability import AvailabilityModel from process.models.superconductors import ( N_CROCO_STRANDS_TURN, SuperconductorMaterial, @@ -2863,6 +2864,7 @@ def run(self, output: bool = False): * self.data.tfcoil.n_tf_coil_turns ) + # Calculate allowable current density at T = T_peak + T_margin_min margin_superconductor_info = self.tf_cable_in_conduit_superconductor_properties( a_tf_turn_cable_space=self.data.tfcoil.a_tf_turn_cable_space_no_void, a_tf_turn=self.data.tfcoil.a_tf_turn, @@ -2884,6 +2886,7 @@ def run(self, output: bool = False): margin_superconductor_info.j_tf_wp_critical ) + # Calculate critical current density at T = T_peak critical_superconductor_info = ( self.tf_cable_in_conduit_superconductor_properties( a_tf_turn_cable_space=self.data.tfcoil.a_tf_turn_cable_space_no_void, @@ -2923,24 +2926,29 @@ def run(self, output: bool = False): critical_superconductor_info.c_turn_cables_critical ) - # if self.data.tfcoil.i_str_wp == 0: - # strain = self.data.tfcoil.str_tf_con_res - # else: - # strain = self.data.tfcoil.str_wp - - # self.data.tfcoil.temp_tf_superconductor_margin = ( - # self.calculate_superconductor_temperature_margin( - # i_tf_superconductor=self.data.tfcoil.i_tf_sc_mat, - # j_superconductor=d_sc_tf.j_tf_superconductor, - # b_tf_inboard_peak=self.data.tfcoil.b_tf_inboard_peak_with_ripple, - # strain=strain, - # bc20m=d_sc_tf.b_tf_superconductor_critical_zero_temp_strain, - # tc0m=d_sc_tf.temp_tf_superconductor_critical_zero_field_strain, - # c0=1.0e10, - # temp_tf_coolant_peak_field=self.data.tfcoil.tftmp, - # data=self.data, - # ) - # ) + match AvailabilityModel(self.data.costs.i_plant_availability): + # Only calculate temperature margin if required + case AvailabilityModel.MORRIS | AvailabilityModel.ST: + if self.data.tfcoil.i_str_wp == 0: + strain = self.data.tfcoil.str_tf_con_res + else: + strain = self.data.tfcoil.str_wp + + temp_margin = self.calculate_superconductor_temperature_margin( + i_tf_superconductor=self.data.tfcoil.i_tf_sc_mat, + j_superconductor=d_sc_tf.j_tf_superconductor, + b_tf_inboard_peak=self.data.tfcoil.b_tf_inboard_peak_with_ripple, + strain=strain, + bc20m=d_sc_tf.b_tf_superconductor_critical_zero_temp_strain, + tc0m=d_sc_tf.temp_tf_superconductor_critical_zero_field_strain, + c0=1.0e10, + temp_tf_coolant_peak_field=self.data.tfcoil.tftmp, + data=self.data, + ) + case AvailabilityModel.USER_INPUT | AvailabilityModel.WARD_TAYLOR: + temp_margin = self.data.tfcoil.temp_tf_superconductor_margin_min + + self.data.tfcoil.temp_tf_superconductor_margin = temp_margin # Do current density protection calculation # Only setup for Nb3Sn at present. @@ -3996,6 +4004,22 @@ def run(self, output: bool = False): * self.data.tfcoil.n_tf_coil_turns ) + superconductor_margin_properties = self.tf_croco_superconductor_properties( + a_tf_turn=self.data.tfcoil.a_tf_turn, + b_tf_inboard_peak=self.data.tfcoil.b_tf_inboard_peak_with_ripple, + cur_tf_turn=self.data.tfcoil.c_tf_turn, + temp_tf_peak=self.data.tfcoil.tftmp + + self.data.tfcoil.temp_tf_superconductor_margin_min, + i_tf_superconductor=self.data.tfcoil.i_tf_sc_mat, + dr_tf_hts_tape=d_sc_tf.dr_tf_hts_tape, + dx_tf_hts_tape_rebco=d_sc_tf.dx_tf_hts_tape_rebco, + dx_tf_hts_tape_total=d_sc_tf.dx_tf_hts_tape_total, + a_tf_croco_strand=d_sc_tf.a_tf_croco_strand, + ) + self.data.tfcoil.j_tf_wp_temp_margin = ( + superconductor_margin_properties.j_tf_wp_critical + ) + superconductor_critical_properties = self.tf_croco_superconductor_properties( a_tf_turn=self.data.tfcoil.a_tf_turn, b_tf_inboard_peak=self.data.tfcoil.b_tf_inboard_peak_with_ripple, @@ -4030,6 +4054,49 @@ def run(self, output: bool = False): superconductor_critical_properties.c_turn_cables_critical ) + if self.data.tfcoil.i_str_wp == 0: + strain = self.data.tfcoil.str_tf_con_res + else: + strain = self.data.tfcoil.str_wp + + self.data.tfcoil.temp_tf_superconductor_margin = ( + self.calculate_superconductor_temperature_margin( + i_tf_superconductor=self.data.tfcoil.i_tf_sc_mat, + j_superconductor=d_sc_tf.j_tf_superconductor, + b_tf_inboard_peak=self.data.tfcoil.b_tf_inboard_peak_with_ripple, + strain=strain, + bc20m=d_sc_tf.b_tf_superconductor_critical_zero_temp_strain, + tc0m=d_sc_tf.temp_tf_superconductor_critical_zero_field_strain, + c0=1.0e10, + temp_tf_coolant_peak_field=self.data.tfcoil.tftmp, + data=self.data, + ) + ) + + match AvailabilityModel(self.data.costs.i_plant_availability): + # Only calculate temperature margin if required + case AvailabilityModel.MORRIS | AvailabilityModel.ST: + if self.data.tfcoil.i_str_wp == 0: + strain = self.data.tfcoil.str_tf_con_res + else: + strain = self.data.tfcoil.str_wp + + temp_margin = self.calculate_superconductor_temperature_margin( + i_tf_superconductor=self.data.tfcoil.i_tf_sc_mat, + j_superconductor=d_sc_tf.j_tf_superconductor, + b_tf_inboard_peak=self.data.tfcoil.b_tf_inboard_peak_with_ripple, + strain=strain, + bc20m=d_sc_tf.b_tf_superconductor_critical_zero_temp_strain, + tc0m=d_sc_tf.temp_tf_superconductor_critical_zero_field_strain, + c0=1.0e10, + temp_tf_coolant_peak_field=self.data.tfcoil.tftmp, + data=self.data, + ) + case AvailabilityModel.USER_INPUT | AvailabilityModel.WARD_TAYLOR: + temp_margin = self.data.tfcoil.temp_tf_superconductor_margin_min + + self.data.tfcoil.temp_tf_superconductor_margin = temp_margin + self.data.tfcoil.v_tf_coil_dump_quench_kv = ( self.croco_voltage() / 1.0e3 ) # TFC Quench voltage in kV From 434d2b5ac1b4c449f30b117b4fa95e0f8145d6d9 Mon Sep 17 00:00:00 2001 From: Matti Coleman Date: Fri, 16 Oct 2026 14:23:03 +0200 Subject: [PATCH 25/35] remove old function --- process/models/tfcoil/superconducting.py | 137 ----------------------- 1 file changed, 137 deletions(-) diff --git a/process/models/tfcoil/superconducting.py b/process/models/tfcoil/superconducting.py index 12b8ddffaf..cd51432377 100644 --- a/process/models/tfcoil/superconducting.py +++ b/process/models/tfcoil/superconducting.py @@ -2287,143 +2287,6 @@ def calculate_superconductor_temperature_margin( return margin - @staticmethod - def calculate_superconductor_temperature_margin_old( - i_tf_superconductor: int, - j_superconductor: float, - b_tf_inboard_peak: float, - strain: float, - bc20m: float, - tc0m: float, - c0: float, - temp_tf_coolant_peak_field: float, - data: DataStructure, - ): - """Calculate the temperature margin of the TF superconductor. - - Parameters - ---------- - i_tf_superconductor: - Switch for conductor type: - - 1: ITER Nb3Sn, standard parameters - - 2: Bi-2212 High Temperature Superconductor - - 3: NbTi - - 4: ITER Nb3Sn, user-defined parameters - - 5: WST Nb3Sn parameterisation - - 7: Durham Ginzburg-Landau Nb-Ti parameterisation - - 8: Durham Ginzburg-Landau critical surface model for REBCO - - 9: Hazelton experimental data + Zhai conceptual model for REBCO - j_superconductor: - Current density in superconductor (A/m²). - b_tf_inboard_peak: - Peak field at conductor (T). - strain: - Strain on superconductor. - bc20m: - Critical field at zero temperature and strain (T). - tc0m: - Critical temperature at zero field and strain (K). - c0: - Constant used in NbTi critical current density calculation (A/m²). - temp_tf_coolant_peak_field: - He temperature at peak field point (K). - - Returns - ------- - type - temp_tf_superconductor_margin. - - - Raises - ------ - ProcessValueError - If an unknown superconductor type is specified. - """ - # ================================================================= - # Calculate temperature margin of superconductor - - # Temperature margin (already calculated in superconductors.bi2212 for - # i_tf_superconductor=2) - match SuperconductorModel(i_tf_superconductor): - case SuperconductorModel.BI2212: - # Bi-2212: temperature margin already calculated elsewhere - temp_tf_superconductor_margin = 0.0 - # Find temperature at which current density margin = 0 - case ( - SuperconductorModel.ITER_NB3SN - | SuperconductorModel.OLD_LUBELL_NBTI - | SuperconductorModel.USER_DEFINED_NB3SN - | SuperconductorModel.WST_NB3SN - | SuperconductorModel.DURHAM_NBTI - | SuperconductorModel.DURHAM_REBCO - | SuperconductorModel.HAZELTON_ZHAI_REBCO - ): - if ( - SuperconductorModel(i_tf_superconductor) - == SuperconductorModel.OLD_LUBELL_NBTI - ): - arguments = ( - i_tf_superconductor, - j_superconductor, - b_tf_inboard_peak, - strain, - bc20m, - tc0m, - data.superconducting_tfcoil.dr_tf_hts_tape, - data.superconducting_tfcoil.dx_tf_hts_tape_rebco, - data.superconducting_tfcoil.dx_tf_hts_tape_total, - c0, - ) - else: - arguments = ( - i_tf_superconductor, - j_superconductor, - b_tf_inboard_peak, - strain, - bc20m, - tc0m, - data.superconducting_tfcoil.dr_tf_hts_tape, - data.superconducting_tfcoil.dx_tf_hts_tape_rebco, - data.superconducting_tfcoil.dx_tf_hts_tape_total, - ) - - another_estimate = 2 * temp_tf_coolant_peak_field - ( - t_zero_margin, - _, - ) = optimize.newton( - superconductors.superconductor_current_density_margin, - temp_tf_coolant_peak_field, - fprime=None, - args=arguments, - tol=1.0e-06, - maxiter=50, - fprime2=None, - x1=another_estimate, - rtol=1.0e-6, - full_output=True, - disp=True, - ) - temp_tf_superconductor_margin = ( - t_zero_margin - temp_tf_coolant_peak_field - ) - - if temp_tf_superconductor_margin <= 0.0e0: - logger.error( - f"""Negative TFC temperature margin - temp_tf_superconductor_margin: {temp_tf_superconductor_margin} - b_tf_inboard_peak: {b_tf_inboard_peak} - j_superconductor: {j_superconductor} - """ - ) - case _: - raise ProcessValueError( - f"Unknown superconductor type: " - f"i_tf_superconductor={i_tf_superconductor}" - ) - - return temp_tf_superconductor_margin - @dataclass(slots=True) class TFGeneralTurnGeometry: From a37ddd48204c93f6676afe7a616eb45e93c071e8 Mon Sep 17 00:00:00 2001 From: Matti Coleman Date: Fri, 16 Oct 2026 16:00:27 +0200 Subject: [PATCH 26/35] better error message --- process/models/superconductors.py | 3 +++ process/models/tfcoil/superconducting.py | 5 +++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/process/models/superconductors.py b/process/models/superconductors.py index 3483f1beeb..b82870af35 100644 --- a/process/models/superconductors.py +++ b/process/models/superconductors.py @@ -1292,6 +1292,9 @@ def superconductor_current_density_margin( f"Unknown superconductor material code: {i_superconductor_type}" ) from err + print(f"{j_superconductor_critical=}") + print(f"{j_superconductor=}") + print(f"{(j_superconductor_critical - j_superconductor)=}") return j_superconductor_critical - j_superconductor diff --git a/process/models/tfcoil/superconducting.py b/process/models/tfcoil/superconducting.py index cd51432377..b85dc03984 100644 --- a/process/models/tfcoil/superconducting.py +++ b/process/models/tfcoil/superconducting.py @@ -2248,7 +2248,7 @@ def calculate_superconductor_temperature_margin( x0=[temp_tf_coolant_peak_field], bounds=([1e-3], [0.999 * tc0m]), method="trf", - xtol=1e-9, + xtol=1e-11, ftol=None, gtol=None, max_nfev=50, @@ -2267,7 +2267,8 @@ def calculate_superconductor_temperature_margin( or abs(normalised_residual) > residual_tol ): raise ProcessValueError( - "Superconductor temperature margin residual is not sufficiently " + "Superconductor current density residual for temperature margin " + "calculation is not sufficiently " f"close to zero: {normalised_residual:.6g} " f"(normalised tolerance: {residual_tol:.6g})" ) From d3c60c31807bd87134de869b503a4a19908faa5a Mon Sep 17 00:00:00 2001 From: Matti Coleman Date: Fri, 16 Oct 2026 16:03:27 +0200 Subject: [PATCH 27/35] remove spurious call in CROCO --- process/models/tfcoil/superconducting.py | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/process/models/tfcoil/superconducting.py b/process/models/tfcoil/superconducting.py index b85dc03984..ac2289f57c 100644 --- a/process/models/tfcoil/superconducting.py +++ b/process/models/tfcoil/superconducting.py @@ -3918,25 +3918,6 @@ def run(self, output: bool = False): superconductor_critical_properties.c_turn_cables_critical ) - if self.data.tfcoil.i_str_wp == 0: - strain = self.data.tfcoil.str_tf_con_res - else: - strain = self.data.tfcoil.str_wp - - self.data.tfcoil.temp_tf_superconductor_margin = ( - self.calculate_superconductor_temperature_margin( - i_tf_superconductor=self.data.tfcoil.i_tf_sc_mat, - j_superconductor=d_sc_tf.j_tf_superconductor, - b_tf_inboard_peak=self.data.tfcoil.b_tf_inboard_peak_with_ripple, - strain=strain, - bc20m=d_sc_tf.b_tf_superconductor_critical_zero_temp_strain, - tc0m=d_sc_tf.temp_tf_superconductor_critical_zero_field_strain, - c0=1.0e10, - temp_tf_coolant_peak_field=self.data.tfcoil.tftmp, - data=self.data, - ) - ) - match AvailabilityModel(self.data.costs.i_plant_availability): # Only calculate temperature margin if required case AvailabilityModel.MORRIS | AvailabilityModel.ST: From 77568605cbe3c31c2b292bae0f7970fbe6d62811 Mon Sep 17 00:00:00 2001 From: Matti Coleman Date: Fri, 16 Oct 2026 16:17:51 +0200 Subject: [PATCH 28/35] add log --- process/models/superconductors.py | 3 --- process/models/tfcoil/superconducting.py | 17 ++++++++++++++--- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/process/models/superconductors.py b/process/models/superconductors.py index b82870af35..3483f1beeb 100644 --- a/process/models/superconductors.py +++ b/process/models/superconductors.py @@ -1292,9 +1292,6 @@ def superconductor_current_density_margin( f"Unknown superconductor material code: {i_superconductor_type}" ) from err - print(f"{j_superconductor_critical=}") - print(f"{j_superconductor=}") - print(f"{(j_superconductor_critical - j_superconductor)=}") return j_superconductor_critical - j_superconductor diff --git a/process/models/tfcoil/superconducting.py b/process/models/tfcoil/superconducting.py index ac2289f57c..43c8a00d2e 100644 --- a/process/models/tfcoil/superconducting.py +++ b/process/models/tfcoil/superconducting.py @@ -948,8 +948,8 @@ def output_tf_superconductor_info(self): else: strain = self.data.tfcoil.str_wp - self.data.tfcoil.temp_tf_superconductor_margin = ( - self.calculate_superconductor_temperature_margin( + try: + temp_margin = self.calculate_superconductor_temperature_margin( i_tf_superconductor=self.data.tfcoil.i_tf_sc_mat, j_superconductor=d_sc_tf.j_tf_superconductor, b_tf_inboard_peak=self.data.tfcoil.b_tf_inboard_peak_with_ripple, @@ -960,7 +960,18 @@ def output_tf_superconductor_info(self): temp_tf_coolant_peak_field=self.data.tfcoil.tftmp, data=self.data, ) - ) + except ProcessValueError: + # Non-positive + logger.error( + "TF coil temperature margin calculation failed. This is either due " + "to required current density being above the critical current density," + " or because the superconducting parameterisation returned a nan or " + "negative current density.", + stacklevel=2, + ) + temp_margin = np.nan + + self.data.tfcoil.temp_tf_superconductor_margin = temp_margin po.oheadr(self.outfile, "TF Coils Superconductor Information") po.ovarre( self.outfile, From 9136245b9f9d86640a620663930285a08a52645b Mon Sep 17 00:00:00 2001 From: Matti Coleman Date: Fri, 16 Oct 2026 16:27:18 +0200 Subject: [PATCH 29/35] improve warnings --- process/models/tfcoil/superconducting.py | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/process/models/tfcoil/superconducting.py b/process/models/tfcoil/superconducting.py index 43c8a00d2e..01bb5c25dd 100644 --- a/process/models/tfcoil/superconducting.py +++ b/process/models/tfcoil/superconducting.py @@ -961,14 +961,7 @@ def output_tf_superconductor_info(self): data=self.data, ) except ProcessValueError: - # Non-positive - logger.error( - "TF coil temperature margin calculation failed. This is either due " - "to required current density being above the critical current density," - " or because the superconducting parameterisation returned a nan or " - "negative current density.", - stacklevel=2, - ) + # Non-positive or failed optimisation / j_crit calculation temp_margin = np.nan self.data.tfcoil.temp_tf_superconductor_margin = temp_margin @@ -2266,9 +2259,9 @@ def calculate_superconductor_temperature_margin( ) if not result.success: - raise ProcessValueError( - f"Error in TF temperature margin calculation: {result.message}" - ) + txt = f"Error in TF temperature margin calculation: {result.message}" + logger.error(txt) + raise ProcessValueError(txt) normalised_residual = float(result.fun[0]) residual_tol = 1e-10 @@ -2277,13 +2270,16 @@ def calculate_superconductor_temperature_margin( not np.isfinite(normalised_residual) or abs(normalised_residual) > residual_tol ): - raise ProcessValueError( + txt = ( "Superconductor current density residual for temperature margin " - "calculation is not sufficiently " - f"close to zero: {normalised_residual:.6g} " + "calculation is not sufficiently close to zero: " + f"{normalised_residual:.6g} " f"(normalised tolerance: {residual_tol:.6g})" ) + logger.error(txt) + raise ProcessValueError(txt) + t_zero_margin = float(result.x[0]) margin = t_zero_margin - temp_tf_coolant_peak_field From 6bd9d0c233af70c6d311fd548e6e020fbd463d11 Mon Sep 17 00:00:00 2001 From: Matti Coleman Date: Tue, 20 Oct 2026 16:41:32 +0200 Subject: [PATCH 30/35] remove current sharing REBCO --- process/models/superconductors.py | 45 ----------------------- process/models/tfcoil/superconducting.py | 2 +- tests/unit/models/test_superconductors.py | 6 --- 3 files changed, 1 insertion(+), 52 deletions(-) diff --git a/process/models/superconductors.py b/process/models/superconductors.py index 3483f1beeb..dd83dfc641 100644 --- a/process/models/superconductors.py +++ b/process/models/superconductors.py @@ -6,7 +6,6 @@ from types import DynamicClassAttribute import numpy as np -from scipy import optimize from process.core.exceptions import ProcessValueError @@ -1293,47 +1292,3 @@ def superconductor_current_density_margin( ) from err return j_superconductor_critical - j_superconductor - - -def current_sharing_rebco(bfield, j): - """Current sharing temperature for "REBCO" 2nd generation HTS superconductor - - Parameters - ---------- - bfield : - Magnetic field at superconductor (T) - j : - Current density in superconductor (A/m2) - - Returns - ------- - current_sharing_t : - Current sharing temperature (K) - """ - - def deltaj_rebco(temperature): - jcritical, _, _, _ = jcrit_rebco( - temp_conductor=temperature, - b_conductor=bfield, - temp_c0_max=SuperconductorModel.CROCO_REBCO.temp_crit_zero_field_strain, - b_c20_max=SuperconductorModel.CROCO_REBCO.b_crit_zero_field_strain, - ) - return jcritical - j - - # No additional arguments are required for deltaj_rebco since it only has one - # argument. - - estimate = 10.0 - another_estimate = 20.0 - current_sharing_t, _root_result = optimize.newton( - deltaj_rebco, - estimate, - tol=1e-6, - rtol=1e-6, - maxiter=50, - x1=another_estimate, - full_output=True, - disp=True, - ) - - return current_sharing_t diff --git a/process/models/tfcoil/superconducting.py b/process/models/tfcoil/superconducting.py index 01bb5c25dd..122aeadffa 100644 --- a/process/models/tfcoil/superconducting.py +++ b/process/models/tfcoil/superconducting.py @@ -2380,7 +2380,7 @@ class CICCSuperconductingTFCoil(SuperconductingTFCoil): """Cable in Conduit Superconducting TF Coil class.""" def run(self, output: bool = False): - """Run the superconducting TF coil model for a CroCo conductor with REBCO tape. + """Run the superconducting TF coil model for a CICC conductor. Parameters ---------- diff --git a/tests/unit/models/test_superconductors.py b/tests/unit/models/test_superconductors.py index 393ec37e98..3321d31838 100644 --- a/tests/unit/models/test_superconductors.py +++ b/tests/unit/models/test_superconductors.py @@ -154,12 +154,6 @@ def test_jcrit_rebco(): assert validity -def test_current_sharing_rebco(): - assert superconductors.current_sharing_rebco(11.0, 2e7) == pytest.approx( - 71.28702697627514 - ) - - def test_bi2212(): jcrit, tmarg = superconductors.bi2212(7.0, 2e7, 4.75, 0.2) From 630444e2e9bf790bd8920e91027db967c5ee0a5b Mon Sep 17 00:00:00 2001 From: Matti Coleman Date: Tue, 20 Oct 2026 17:06:04 +0200 Subject: [PATCH 31/35] fix stellerator duh --- process/models/stellarator/coils/calculate.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/process/models/stellarator/coils/calculate.py b/process/models/stellarator/coils/calculate.py index b51d7c66c5..f6010ed8eb 100644 --- a/process/models/stellarator/coils/calculate.py +++ b/process/models/stellarator/coils/calculate.py @@ -417,7 +417,7 @@ def winding_pack_total_size( # jcrit for this bmax: jcrit_vector[k] = jcrit_from_material( b_max_k[k], - data.tfcoil.tftmp + data.tfcoil.temp_tf_superconductor_margin, + data.tfcoil.tftmp + data.tfcoil.temp_tf_superconductor_margin_min, data.tfcoil.i_tf_sc_mat, data.tfcoil.bcritsc, data.tfcoil.f_a_tf_turn_cable_copper, @@ -425,7 +425,7 @@ def winding_pack_total_size( data.tfcoil.tcritsc, data.tfcoil.f_a_tf_turn_cable_space_extra_void, data.tfcoil.j_tf_wp, - ) # Get here a temperature margin from data.tfcoil.tmargtf. + ) # The operation current density weighted with the global iop/icrit fraction lhs[:] = data.constraints.f_j_tf_wp_critical_max * jcrit_vector From 101384df5535c99fee02aeacc0d5604f9802ea65 Mon Sep 17 00:00:00 2001 From: Matti Coleman Date: Tue, 20 Oct 2026 17:11:28 +0200 Subject: [PATCH 32/35] trigger CI From fde4482c3c9af05f85e88ceebc11faae8af0b8df Mon Sep 17 00:00:00 2001 From: Matti Coleman Date: Wed, 21 Oct 2026 08:54:32 +0200 Subject: [PATCH 33/35] change sign in constraint --- process/core/solver/constraints.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/process/core/solver/constraints.py b/process/core/solver/constraints.py index 429f885869..486b207df0 100644 --- a/process/core/solver/constraints.py +++ b/process/core/solver/constraints.py @@ -997,7 +997,7 @@ def constraint_equation_35(constraint_registration, data): ) -@ConstraintManager.register_constraint(36, "A/m²", ">=") +@ConstraintManager.register_constraint(36, "A/m²", "<=") def constraint_equation_36(constraint_registration, data): """Equation for TF coil s/c temperature margin lower limit (SCTF) by means of the actual to critical current density From efb9acb6e8224356160ccac6b8e6151c42902919 Mon Sep 17 00:00:00 2001 From: Matti Coleman Date: Wed, 21 Oct 2026 10:56:47 +0200 Subject: [PATCH 34/35] format constraint labels --- .../io/plot/summary/reporting/constraints.py | 45 +++++++++++++------ process/data_structure/numerics.py | 4 +- 2 files changed, 34 insertions(+), 15 deletions(-) diff --git a/process/core/io/plot/summary/reporting/constraints.py b/process/core/io/plot/summary/reporting/constraints.py index 0d70177b13..dad262bc15 100644 --- a/process/core/io/plot/summary/reporting/constraints.py +++ b/process/core/io/plot/summary/reporting/constraints.py @@ -2,6 +2,7 @@ from __future__ import annotations +import re from typing import TYPE_CHECKING import numpy as np @@ -15,6 +16,35 @@ __all__ = ["plot_equality_constraint_equations", "plot_inequality_constraint_equations"] +def _var_label_formatting(var_label: str) -> str: + """Format normalised-residue variable labels for display. + + If ``var_label`` is a string ending with ``"_normalised_residue"``, the + suffix is removed, trailing underscores are stripped, and remaining + underscores are replaced with spaces. Underscores appearing within LaTeX + expressions delimited by ``$...$`` or ``$$...$$`` are preserved. + + Parameters + ---------- + var_label : str + Variable label to format. Non-string values, or strings that do not end + with ``"_normalised_residue"``, are returned unchanged. + + Returns + ------- + str + The formatted label if applicable, otherwise the original input value. + """ + if isinstance(var_label, str) and var_label.endswith("_normalised_residue"): + var_label = var_label.removesuffix("_normalised_residue").rstrip("_") + parts = re.split(r"(\$\$.*?\$\$|\$[^$]*\$)", var_label) + var_label = "".join( + part.replace("_", " ") if i % 2 == 0 else part + for i, part in enumerate(parts) + ) + return var_label + + def plot_equality_constraint_equations(axis: plt.Axes, m_file_data: MFile, scan: int): """Plot the equality constraints for a solution and their normalised residuals @@ -45,6 +75,7 @@ def plot_equality_constraint_equations(axis: plt.Axes, m_file_data: MFile, scan: # Use the variable name if available, else fallback to "eq_conXXX" var_label = con_names.get(n, f"eq_con{n:03d}") + var_label = _var_label_formatting(var_label) # Normalized residual of the constraint con_norm_residual = m_file_data.data[f"eq_con{n:03d}"].get_scan(scan) @@ -53,14 +84,6 @@ def plot_equality_constraint_equations(axis: plt.Axes, m_file_data: MFile, scan: con_units_raw = m_file_data.data[f"eq_units_con{n:03d}"].get_scan(scan) con_units = str(con_units_raw).strip("'`") - # Remove '_normalised_residue' from the label if present - if isinstance(var_label, str) and var_label.endswith("_normalised_residue"): - var_label = var_label.replace("_normalised_residue", "") - - # Remove trailing underscores and replace underscores between words with - # spaces - var_label = var_label.rstrip("_").replace("_", " ") - # Plot the normalised residual as a bar axis.barh( n_plot, @@ -143,6 +166,7 @@ def plot_inequality_constraint_equations(axis: plt.Axes, m_file: MFile, scan: in # Use the variable name if available, else fallback to "ineq_conXXX" var_label = con_names.get(n, f"ineq_con{n:03d}") + var_label = _var_label_formatting(var_label) # Normalized residual of the constraint con_residual_norm = m_file.data[f"ineq_con{n:03d}"].get_scan(scan) @@ -167,11 +191,6 @@ def plot_inequality_constraint_equations(axis: plt.Axes, m_file: MFile, scan: in zorder=0, ) - # Remove '_normalised_residue' from the label if present - if isinstance(var_label, str) and var_label.endswith("_normalised_residue"): - var_label = var_label.replace("_normalised_residue", "") - var_label = var_label.rstrip("_").replace("_", " ") - # Calculate the normalised constraint threshold depending if the constraint is an # upper # or lower limit diff --git a/process/data_structure/numerics.py b/process/data_structure/numerics.py index b34af3cf6b..4fd76ae2db 100644 --- a/process/data_structure/numerics.py +++ b/process/data_structure/numerics.py @@ -232,10 +232,10 @@ class NumericsData: "Plasma injected power (Pₐᵤₓ) upper limit ", "TF coil case stress upper limit ", "TF coil conduit stress upper limit ", - "TF coil superconductor critical current density upper limit ", + r"TF coil superconductor $j_{\mathrm{crit}}$ upper limit (ratio) ", "TF quench dump voltage upper limit ", "TF quench hotspot current density upper limit ", - "TF coil superconductor temperature margin lower limit ", + r"TF coil superconductor $j_{\mathrm{crit}}$ upper limit (T margin) ", "HCD normalised current drive efficiency (γ) upper limit ", # noqa: RUF001 "FW coolant temperature rise upper limit ", "FW peak temperature limit", From c9535a77f90c44d89b0cd4a33bdd607d44636e81 Mon Sep 17 00:00:00 2001 From: Matti Coleman Date: Wed, 21 Oct 2026 10:57:53 +0200 Subject: [PATCH 35/35] update string --- process/data_structure/numerics.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/process/data_structure/numerics.py b/process/data_structure/numerics.py index 4fd76ae2db..700320f4ab 100644 --- a/process/data_structure/numerics.py +++ b/process/data_structure/numerics.py @@ -335,7 +335,8 @@ class NumericsData: * (33) I_op / I_critical (TF coil) (SCTF) (itv 50,56,57,58,59,60,24) * (34) Dump voltage upper limit (SCTF) (itv 51,52,56,57,58,59,60,24) * (35) TF Quench Hotspot J limit (SCTF) (itv 53,56,57,58,59,60,24) - * (36) TF coil temperature margin lower limit (SCTF) (itv 54,55,56,57,58,59,60,24) + * (36) TF coil superconductor critical current density upper limit (T margin) (SCTF) + (itv 54,55,56,57,58,59,60,24) * (37) Current drive gamma upper limit (itv 40,47) * (38) First wall coolant temperature rise upper limit (itv 62) * (39) First wall peak temperature upper limit (itv 63)