Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
bd53d42
add variables and modify constraint
CoronelBuendia Oct 15, 2026
090b2f6
remove Tmargin calculation in superconducting.py
CoronelBuendia Oct 15, 2026
ecf7c67
calculate T margin in post-processing
CoronelBuendia Oct 15, 2026
8f2c9de
correct issue number
CoronelBuendia Oct 15, 2026
025d043
correct issue number
CoronelBuendia Oct 15, 2026
b62c307
remove local variable in availability T margin stuff cos it's annoyin…
CoronelBuendia Oct 15, 2026
577dd18
pause for breath
CoronelBuendia Oct 15, 2026
7fad54b
purge t margin calc for now
CoronelBuendia Oct 15, 2026
6f8848b
remove variables
CoronelBuendia Oct 15, 2026
7a05478
remove variables
CoronelBuendia Oct 15, 2026
45d41b5
remove variables
CoronelBuendia Oct 15, 2026
e4de6c3
purge tmargin in IN.DATs
CoronelBuendia Oct 16, 2026
7289cc1
purge more tmargmin from IN.DATs
CoronelBuendia Oct 16, 2026
cca3e56
purge more tmargmin from IN.DATs
CoronelBuendia Oct 16, 2026
c09fcd9
purge more tmargmin from docs
CoronelBuendia Oct 16, 2026
3a1b3e2
fix tests
CoronelBuendia Oct 16, 2026
de3d9b5
improve T margin calculation
CoronelBuendia Oct 16, 2026
0b4ad62
clean up
CoronelBuendia Oct 16, 2026
b4783a9
clean up
CoronelBuendia Oct 16, 2026
479455a
obslete var and temp
CoronelBuendia Oct 16, 2026
75119bf
refactor mess
CoronelBuendia Oct 16, 2026
7af2676
add comment
CoronelBuendia Oct 16, 2026
5c59623
more consistent constraint implementation on j_tf_wp
CoronelBuendia Oct 16, 2026
2c7f632
use in CROCO too
CoronelBuendia Oct 16, 2026
434d2b5
remove old function
CoronelBuendia Oct 16, 2026
a37ddd4
better error message
CoronelBuendia Oct 16, 2026
d3c60c3
remove spurious call in CROCO
CoronelBuendia Oct 16, 2026
7756860
add log
CoronelBuendia Oct 16, 2026
9136245
improve warnings
CoronelBuendia Oct 16, 2026
6bd9d0c
remove current sharing REBCO
CoronelBuendia Oct 20, 2026
630444e
fix stellerator duh
CoronelBuendia Oct 20, 2026
101384d
trigger CI
CoronelBuendia Oct 20, 2026
fde4482
change sign in constraint
CoronelBuendia Oct 21, 2026
efb9acb
format constraint labels
CoronelBuendia Oct 21, 2026
c9535a7
update string
CoronelBuendia Oct 21, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion documentation/source/eng-models/central-solenoid.md
Original file line number Diff line number Diff line change
Expand Up @@ -464,7 +464,7 @@ The critical current density *J*<sub>crit</sub> 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*<sub>crit</sub>
is equal to the operating current density *J*<sub>op</sub>. 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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.

---------

Expand Down
2 changes: 1 addition & 1 deletion examples/data/large_tokamak_IN.DAT
Original file line number Diff line number Diff line change
Expand Up @@ -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 *
*---------------------------------*
Expand Down
2 changes: 1 addition & 1 deletion examples/data/large_tokamak_eval_IN.DAT
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 *
*---------------------------------*
Expand Down
2 changes: 1 addition & 1 deletion examples/data/large_tokamak_varyrun_IN.DAT
Original file line number Diff line number Diff line change
Expand Up @@ -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 *
*---------------------------------*
Expand Down
2 changes: 1 addition & 1 deletion examples/data/scan_example_file_IN.DAT
Original file line number Diff line number Diff line change
Expand Up @@ -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 *
*---------------------------------*
Expand Down
4 changes: 4 additions & 0 deletions process/core/data_structure/obsolete_vars.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand Down
21 changes: 0 additions & 21 deletions process/core/init.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 0 additions & 1 deletion process/core/input.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
),
Expand Down
45 changes: 32 additions & 13 deletions process/core/io/plot/summary/reporting/constraints.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from __future__ import annotations

import re
from typing import TYPE_CHECKING

import numpy as np
Expand All @@ -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

Expand Down Expand Up @@ -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)
Expand All @@ -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,
Expand Down Expand Up @@ -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)
Expand All @@ -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
Expand Down
17 changes: 9 additions & 8 deletions process/core/solver/constraints.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the description is a bit back to front now as we're limiting current density primarily maybe something like this is more accurate

Equation for TF coil superconducting current density limit at the minimum temperature margin


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:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't a temperature margin right? It's a current density, so we should change this variable name to reflect that.

@CoronelBuendia CoronelBuendia Oct 26, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it's a current density. I named it to be in line with the other similar variables:

j_tf_wp_temp_margin : allowable TF coil winding pack current density, for specified temperature margin (A/m²), in line with j_tf_wp_critical (eqcon 33) and j_tf_wp_quench_heat_max (eqcon 35)

Happy to rename

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,
)

Expand Down
7 changes: 4 additions & 3 deletions process/data_structure/numerics.py
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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)
Expand Down
21 changes: 9 additions & 12 deletions process/data_structure/tfcoil_variables.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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²)
Expand Down Expand Up @@ -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)"""

Expand Down
17 changes: 8 additions & 9 deletions process/models/availability.py
Original file line number Diff line number Diff line change
Expand Up @@ -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, &quot;Availability in PROCESS&quot;
2014 EUROfusion RAMI report, "Availability in PROCESS";

Parameters
----------
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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)

Expand All @@ -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(
Expand All @@ -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(
Expand Down
4 changes: 2 additions & 2 deletions process/models/stellarator/coils/calculate.py
Original file line number Diff line number Diff line change
Expand Up @@ -417,15 +417,15 @@ 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,
data.tfcoil.fhts,
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
Expand Down
Loading
Loading