Skip to content
Merged
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
39 changes: 38 additions & 1 deletion waveform_editor/gui/shape_editor/coil_currents.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,12 @@ def exceeds_limit(self):
class CoilCurrents(Viewer):
coils = param.List(doc="List of CoilCurrentEntry for each coil")
edited = param.Event(doc="Triggered whenever the user edits a coil.")
reference_ip = param.Number(
default=None,
precedence=-1,
doc="The plasma current the coil currents were computed for, None before "
"the first NICE run.",
)

# Table column names
COIL_NAME = "coil_name"
Expand Down Expand Up @@ -153,6 +159,7 @@ def create_ui(self, pf_active):
Args:
pf_active: pf_active IDS containing coils with current values.
"""
self.reference_ip = None
if not pf_active:
self.coils = []
return
Expand Down Expand Up @@ -264,16 +271,46 @@ def fill_pf_active(self, pf_active):
if coil.current is not None:
pf_active.coil[i].current.data = np.array([coil.current])

def sync_ui_with_pf_active(self, pf_active):
def sync_ui_with_pf_active(self, pf_active, ip):
"""Synchronize UI with the current values from the pf_active IDS.

Args:
pf_active: pf_active IDS for which the coil currents are used.
ip: The plasma current the coil currents were computed for.
"""
for i, coil in enumerate(pf_active.coil):
self.coils[i].current = coil.current.data[0]
self.reference_ip = ip
self._update_table()

def scale_to_ip(self, ip):
"""Scale the coil currents from the plasma current they were computed for to
a new one. The Grad-Shafranov equation is linear in the currents, so scaling
all of them by the same factor keeps the plasma shape.

Args:
ip: The new plasma current [A].
"""
if self.reference_ip is None:
pn.state.notifications.info(
"Run NICE first: the coil currents are scaled from the plasma "
"current of the last run."
)
return
# There is no scale factor to or from a plasma current of 0 A
if ip == 0 or self.reference_ip == 0:
return
factor = ip / self.reference_ip
for coil in self.coils:
if coil.current is not None:
coil.current *= factor
self.reference_ip = ip
self._update_table()
pn.state.notifications.info(
f"Scaled the coil currents by {factor:.4g} with the plasma current."
)
self._warn_exceeded()

def update_xml(self, xml_params: ET.Element):
"""Update XML parameters based on coil current table configuration.

Expand Down
24 changes: 24 additions & 0 deletions waveform_editor/gui/shape_editor/plasma_properties.py
Original file line number Diff line number Diff line change
Expand Up @@ -370,13 +370,37 @@ class PlasmaProperties(Viewer):
label="Set toroidal field by TF coil current",
doc="Compute the toroidal field from the TF coil current and the tf IDS.",
)
ip = param.Number(default=None, precedence=-1, doc="The plasma current [A].")
scale_coil_currents = param.Boolean(
default=False,
label="Scale coil currents with the plasma current",
doc="Scale the coil currents by the change of the plasma current, which keeps "
"the plasma shape for the same plasma profiles.",
)

def __init__(self):
super().__init__()
self._ip = PropertyInput(
"Plasma current [A]",
default_value=DEFAULT_PROPERTIES["ip"],
step=1e6,
extra=[
pn.Row(
pn.widgets.Checkbox.from_param(self.param.scale_coil_currents),
pn.widgets.TooltipIcon(
value=(
"Multiply all coil currents by the ratio of the new to "
"the previous plasma current. For the same profiles, this "
"keeps the plasma shape, which is useful in direct mode. "
"The currents are scaled from the plasma current of the "
"last NICE run."
),
align="center",
margin=(-3, 0, 0, -10),
),
margin=(8, 0, 0, 0),
)
],
)
self._r0 = PropertyInput(
"Reference major radius [m]",
Expand Down
19 changes: 15 additions & 4 deletions waveform_editor/gui/shape_editor/shape_editor.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ def __init__(self, main_gui):
self.plasma_shape = PlasmaShape()
self.plasma_properties = PlasmaProperties()
self.coil_currents = CoilCurrents()
self.plasma_properties.param.watch(self._scale_coil_currents, "ip")
self.nice_plotter = NicePlotter(
communicator=self.communicator,
plasma_shape=self.plasma_shape,
Expand Down Expand Up @@ -270,14 +271,20 @@ def _create_options_tabs(self, is_inverse_mode, has_shape, has_properties):
sizing_mode="stretch_width",
stylesheets=[".bk-tab { flex: 1; text-align: center; }"],
)
tabs.active = min(self._active_tab, log_tab)
# Without the plasma shape tab, the other tabs move one to the left
tabs.active = max(self._active_tab - (not is_inverse_mode), 0)
tabs.param.watch(self._remember_active_tab, "active")
self.terminal.visible = tabs.param.active.rx() == log_tab
return tabs

def _remember_active_tab(self, event):
"""Keep the open tab open when the tabs are rebuilt."""
self._active_tab = event.new
self._active_tab = event.new + (not self.nice_settings.is_inverse_mode)

def _scale_coil_currents(self, event):
"""Scale the coil currents with the plasma current, when that is enabled."""
if self.plasma_properties.scale_coil_currents:
self.coil_currents.scale_to_ip(event.new)

def _load_slice(self, uri, ids_name, time=0):
"""Load an IDS slice and return it.
Expand Down Expand Up @@ -353,7 +360,9 @@ def _restore_run(self, event):
return
self.communicator.equilibrium = equilibrium
self.communicator.pf_active = pf_active
self.coil_currents.sync_ui_with_pf_active(pf_active)
self.coil_currents.sync_ui_with_pf_active(
pf_active, equilibrium.time_slice[0].global_quantities.ip
)
self._update_metrics()
self._list_runs()

Expand Down Expand Up @@ -557,7 +566,9 @@ async def _run_nice(self, auto):
# NICE crashed without a result
if self.communicator.equilibrium is previous_equilibrium:
return
self.coil_currents.sync_ui_with_pf_active(self.communicator.pf_active)
self.coil_currents.sync_ui_with_pf_active(
self.communicator.pf_active, self.plasma_properties.ip
)
self._update_metrics()
# Only a converged run is worth restoring
if self.communicator.converged:
Expand Down
Loading