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. --------- 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/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/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/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/core/solver/constraints.py b/process/core/solver/constraints.py index d8013775bf..486b207df0 100644 --- a/process/core/solver/constraints.py +++ b/process/core/solver/constraints.py @@ -997,18 +997,19 @@ 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_wp: + winding pack current density (A/m²) + j_tf_wp_temp_margin: + superconducting strand critical current density (A/m²) """ - return geq( - data.tfcoil.temp_tf_superconductor_margin, - data.tfcoil.temp_tf_superconductor_margin_min, + return leq( + data.tfcoil.j_tf_wp, + data.tfcoil.j_tf_wp_temp_margin, constraint_registration, ) diff --git a/process/data_structure/numerics.py b/process/data_structure/numerics.py index b34af3cf6b..700320f4ab 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", @@ -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) diff --git a/process/data_structure/tfcoil_variables.py b/process/data_structure/tfcoil_variables.py index 1e9bdb5a55..a81016dbc0 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_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([ 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²) @@ -670,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/availability.py b/process/models/availability.py index f2014bd22a..e167943abf 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 ---------- @@ -750,12 +750,11 @@ 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, ) - mag_temp_marg_limit = tmargmin # Magnet maintenance time (years) mag_main_time = 0.5e0 @@ -768,11 +767,11 @@ 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 - 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 @@ -782,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) @@ -792,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( @@ -804,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( diff --git a/process/models/stellarator/coils/calculate.py b/process/models/stellarator/coils/calculate.py index 151e34abc5..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.tmargmin, + 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 diff --git a/process/models/superconductors.py b/process/models/superconductors.py index ff435bdd8a..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 @@ -298,50 +297,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, diff --git a/process/models/tfcoil/superconducting.py b/process/models/tfcoil/superconducting.py index 34fcfa3d97..122aeadffa 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, @@ -942,6 +943,28 @@ 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 + + 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, + 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, + ) + except ProcessValueError: + # Non-positive or failed optimisation / j_crit calculation + 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, @@ -1174,144 +1197,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, @@ -2268,6 +2153,148 @@ 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 = 1.0 / max(abs(j_superconductor), 1.0) + + result = optimize.least_squares( + lambda t: ( + j_scale + * superconductors.superconductor_current_density_margin(t[0], *args) + ), + x0=[temp_tf_coolant_peak_field], + bounds=([1e-3], [0.999 * tc0m]), + method="trf", + xtol=1e-11, + ftol=None, + gtol=None, + max_nfev=50, + ) + + if not result.success: + 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 + + if ( + not np.isfinite(normalised_residual) + or abs(normalised_residual) > residual_tol + ): + txt = ( + "Superconductor current density residual for temperature margin " + "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 + + 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: @@ -2353,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 ---------- @@ -2708,6 +2735,29 @@ 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, + 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 + ) + + # 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, @@ -2728,6 +2778,7 @@ def run(self, output: bool = False): ) 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 ) @@ -2746,24 +2797,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 + 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 - 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, - ) - ) + 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. @@ -2885,10 +2941,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 " @@ -2896,245 +2952,85 @@ 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 - # ================================================================= - 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) - ) - - j_crit_cable, _ = superconductors.bi2212( - b_conductor=b_tf_inboard_peak, + # This model already accounts for the strand composition. + # 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 + + 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( @@ -3142,48 +3038,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 + 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 - # Ratio of operating / critical current + c_turn_cables_critical = j_cables_critical * cable_area 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 f_c_tf_turn_operating_critical <= 0e0: + if f_c_tf_turn_operating_critical <= 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: {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} + """ ) 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, 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, @@ -3987,6 +3875,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, @@ -4021,24 +3925,29 @@ 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 + 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 - 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, - ) - ) + 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 @@ -4554,15 +4463,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 availabilty routine - see comment to Issue #526 - ) - return TFSuperconductorLimits( j_tf_wp_critical=j_tf_wp_critical, j_superconductor_critical=j_superconductor_critical, 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/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) 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 * *---------------------------------* 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"] 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) 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)