Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
23 changes: 23 additions & 0 deletions process/core/init.py
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,29 @@ def check_process(inputs, data): # noqa: ARG001
"Iteration variables 13 and 140 cannot be used simultaneously",
)

# A superconducting TF coil must have a non-zero inboard thickness.
# dr_tf_inboard is only derived from the winding pack and case thicknesses
# when dr_tf_wp_with_insulation (ixc = 140) is an iteration variable (see
# Build.calculate_radial_build); with neither ixc = 13 nor ixc = 140 active
# it stays at its input value, and the default of 0 silently removes the
# TF coil from the radial build. Stellarators calculate dr_tf_inboard
# during the model run, so are excluded from this check.
if (
data.stellarator.istell == 0
and data.ife.ife == 0
and data.tfcoil.i_tf_sup == TFConductorModel.SUPERCONDUCTING
and not (data.numerics.ixc[: data.numerics.n_iteration_variables] == 13).any()
and not (data.numerics.ixc[: data.numerics.n_iteration_variables] == 140).any()
and data.build.dr_tf_inboard <= 0.0
):
raise ProcessValidationError(
"dr_tf_inboard is not positive: the superconducting inboard TF coil"
" has no thickness. Set dr_tf_inboard (or use ixc = 13), or make"
" dr_tf_wp_with_insulation an iteration variable (ixc = 140) so that"
" dr_tf_inboard is derived from the winding pack and case thicknesses",
dr_tf_inboard=data.build.dr_tf_inboard,
)

# Can't use c_tf_turn as iteration var, constraint or
# input if i_tf_turns_integer == 1
if (
Expand Down
74 changes: 74 additions & 0 deletions tests/unit/core/test_init.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
"""Unit tests for input sanity checks in process.core.init.check_process."""

import pytest

from process.core.exceptions import ProcessValidationError
from process.core.init import check_process
from process.core.model import DataStructure
from process.models.tfcoil.base import TFConductorModel


def _validation_error_message(data):
"""Run check_process and return any validation error message.

Later, unrelated checks may still fire on an otherwise-default
DataStructure, so callers assert on the message content rather than
on whether an error was raised.
"""
try:
check_process(None, data)
except ProcessValidationError as error:
return str(error)
return ""


def test_zero_thickness_superconducting_tf_is_rejected():
"""SC TF with dr_tf_inboard left at 0 and neither ixc=13 nor ixc=140
active must fail validation instead of silently building a machine
with no inboard TF coil.
"""
data = DataStructure()
data.tfcoil.i_tf_sup = TFConductorModel.SUPERCONDUCTING
data.build.dr_tf_inboard = 0.0

with pytest.raises(ProcessValidationError, match="dr_tf_inboard"):
check_process(None, data)


def test_explicit_tf_thickness_is_accepted():
data = DataStructure()
data.tfcoil.i_tf_sup = TFConductorModel.SUPERCONDUCTING
data.build.dr_tf_inboard = 0.5

assert "dr_tf_inboard" not in _validation_error_message(data)


def test_wp_thickness_iteration_variable_is_accepted():
"""With ixc = 140 active, dr_tf_inboard is derived in the build model,
so a zero input value is legitimate.
"""
data = DataStructure()
data.tfcoil.i_tf_sup = TFConductorModel.SUPERCONDUCTING
data.build.dr_tf_inboard = 0.0
data.numerics.n_iteration_variables = 1
data.numerics.ixc[0] = 140

assert "dr_tf_inboard" not in _validation_error_message(data)


def test_resistive_tf_is_not_checked():
data = DataStructure()
data.tfcoil.i_tf_sup = TFConductorModel.WATER_COOLED_COPPER
data.build.dr_tf_inboard = 0.0

assert "dr_tf_inboard" not in _validation_error_message(data)


def test_stellarator_is_not_checked():
"""Stellarators calculate dr_tf_inboard during the model run."""
data = DataStructure()
data.stellarator.istell = 1
data.tfcoil.i_tf_sup = TFConductorModel.SUPERCONDUCTING
data.build.dr_tf_inboard = 0.0

assert "dr_tf_inboard" not in _validation_error_message(data)
7 changes: 6 additions & 1 deletion tests/unit/core/test_input.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,12 @@

@pytest.fixture
def data_structure_obj():
return DataStructure()
data = DataStructure()
# These parser tests run init_process on minimal input snippets; give the
# scaffold a valid TF thickness so configuration validation (which rejects
# a zero-thickness superconducting TF) does not reject the scaffold.
data.build.dr_tf_inboard = 1.0
return data


def _create_input_file(directory, content: str):
Expand Down
Loading