Skip to content

Commit 0e9fcfe

Browse files
committed
clean up
1 parent abdbe8a commit 0e9fcfe

File tree

2 files changed

+100
-83
lines changed

2 files changed

+100
-83
lines changed

waveform_editor/gui/shape_editor.py

Lines changed: 0 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
import xml.etree.ElementTree as ET
44

55
import imas
6-
import numpy as np
76
import panel as pn
87
import param
98
from imas.ids_toplevel import IDSToplevel
@@ -72,10 +71,6 @@ def __init__(self, main_gui):
7271
nice_mode_radio = pn.widgets.RadioBoxGroup.from_param(
7372
self.nice_settings.param.mode, inline=True, margin=(15, 20, 0, 20)
7473
)
75-
button_store = pn.widgets.Button(
76-
name="Store Coil Currents",
77-
on_click=lambda event: self._store_coil_currents(),
78-
)
7974
buttons = pn.Row(button_start, button_stop, nice_mode_radio)
8075

8176
# Accordion does not allow dynamic titles, so use separate card for each option
@@ -111,23 +106,6 @@ def __init__(self, main_gui):
111106
),
112107
)
113108

114-
def _store_coil_currents(self):
115-
# TODO:add option to change time
116-
self.placeholder_time += 1
117-
coil_currents = self.coil_currents.get()
118-
config = self.main_gui.config
119-
120-
for idx, current in enumerate(coil_currents):
121-
name = f"pf_active/coil({idx + 1})/current/data"
122-
if name in config.waveform_map:
123-
waveform = config[name]
124-
tendency = LinearTendency(
125-
user_end=self.placeholder_time, user_to=current
126-
)
127-
waveform.append_tendency(tendency)
128-
else:
129-
pn.state.notifications.error(f"There is no waveform called: {name}")
130-
131109
def _create_card(self, panel_object, title, is_valid=None, visible=True):
132110
"""Create a collapsed card containing a panel object and a title.
133111

waveform_editor/shape_editor/coil_currents.py

Lines changed: 100 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,17 @@
66
from panel.viewable import Viewer
77

88
from waveform_editor.derived_waveform import DerivedWaveform
9-
from waveform_editor.tendencies.linear import LinearTendency
10-
from waveform_editor.tendencies.piecewise import PiecewiseLinearTendency
11-
from waveform_editor.waveform import Waveform
12-
139
from waveform_editor.settings import settings
10+
from waveform_editor.tendencies.piecewise import PiecewiseLinearTendency
1411

1512

1613
class CoilCurrents(Viewer):
1714
coil_ui = param.List(
1815
doc="List of tuples containing the checkboxes and sliders for the coil currents"
1916
)
20-
export_time = param.Number(doc="Time to export coil currents to")
17+
export_time = param.Number(
18+
doc="Select a time at which coil currents will be saved to waveforms"
19+
)
2120

2221
def __init__(self, main_gui, **params):
2322
super().__init__(**params)
@@ -39,31 +38,17 @@ def __init__(self, main_gui, **params):
3938
confirm_button = pn.widgets.Button(
4039
on_click=lambda event: self._store_coil_currents(),
4140
name="Save Currents as Waveforms",
42-
margin=30,
41+
margin=(30, 0, 0, 0),
4342
)
4443
self.panel = pn.Column(
45-
pn.Row(export_time_input, confirm_button, no_ids_message),
44+
pn.Row(
45+
export_time_input, confirm_button, visible=self.param.coil_ui.rx.bool()
46+
),
47+
no_ids_message,
4648
guide_message,
4749
self.sliders_ui,
48-
self.modal,
4950
)
5051

51-
def _open_modal(self):
52-
self._coil_currents_valid()
53-
self.modal.show()
54-
55-
@param.depends("export_time", watch=True)
56-
def _coil_currents_valid(self):
57-
self.coil_export_valid = True
58-
for i in range(len(self.coil_ui)):
59-
name = f"pf_active/coil({i + 1})/current/data"
60-
if name in self.main_gui.config.waveform_map:
61-
tendencies = self.main_gui.config[name].tendencies
62-
if tendencies:
63-
end_time = tendencies[-1].end
64-
if end_time >= self.export_time:
65-
self.coil_export_valid = False
66-
6752
@param.depends("coil_ui", watch=True)
6853
def _update_slider_grid(self):
6954
self.sliders_ui.objects = self.coil_ui
@@ -100,66 +85,120 @@ def create_ui(self, pf_active):
10085
self.coil_ui = new_coil_ui
10186

10287
def _store_coil_currents(self):
88+
"""Store the current values from the coil UI sliders into the waveform
89+
configuration.
90+
"""
10391
coil_currents = self._get_currents()
10492
config = self.main_gui.config
10593
new_waveforms_created = False
94+
group_name = "Coil Currents"
10695

107-
for i in range(len(self.coil_ui)):
108-
name = f"pf_active/coil({i + 1})/current/data"
109-
if name in self.main_gui.config.waveform_map:
110-
tendencies = self.main_gui.config[name].tendencies
111-
if tendencies:
112-
end_time = tendencies[-1].end
113-
if end_time >= self.export_time:
114-
pn.state.notifications.error(
115-
"Export time must be later than the end of any existing waveforms"
116-
)
117-
return
96+
if not self._has_valid_export_time():
97+
return
11898

11999
for i, current in enumerate(coil_currents):
120100
name = f"pf_active/coil({i + 1})/current/data"
121-
eps = 1e-100
122-
# Piecewise tendencies must contain at least two points
123-
new_piecewise = f"- {{type: piecewise, time: [{self.export_time}, {self.export_time + eps}], value: [{current}, {current}]}}"
124-
if not name in config.waveform_map:
125-
group_name = "Coil Currents"
101+
if name not in config.waveform_map:
126102
if group_name not in config.groups:
127103
config.add_group(group_name, [])
128-
waveform = config.parser.parse_waveform(f"{name}:\n{new_piecewise}")
129-
config.add_waveform(waveform, [group_name])
104+
self._create_new_waveform(config, name, current, group_name)
130105
new_waveforms_created = True
131106
else:
132107
waveform = config[name]
133108
if isinstance(waveform, DerivedWaveform):
134-
pn.state.error(
135-
f"Could not store coil current in {name}, because it is a derived waveform"
109+
pn.state.notifications.error(
110+
f"Could not store coil current in waveform {name!r}, "
111+
"because it is a derived waveform"
136112
)
137113
continue
138-
139-
last_tendency = waveform.tendencies[-1]
140-
if isinstance(last_tendency, PiecewiseLinearTendency):
141-
waveform.yaml[-1]["time"].append(float(self.export_time))
142-
waveform.yaml[-1]["value"].append(float(current))
143-
yaml_str = f"{name}:\n{waveform.get_yaml_string()}"
144-
else:
145-
end = waveform.tendencies[-1].end
146-
append_new_piecewise = f"- {{type: piecewise, time: [{end}, {self.export_time}], value: [{current}, {current}]}}"
147-
yaml_str = (
148-
f"{name}:\n{waveform.get_yaml_string()}{append_new_piecewise}"
149-
)
150-
new_waveform = config.parse_waveform(yaml_str)
151-
config.replace_waveform(new_waveform)
114+
self._append_to_existing_waveform(config, name, current)
152115

153116
if new_waveforms_created:
154117
self.main_gui.selector.refresh()
155118
pn.state.notifications.warning(
156-
f"Could not find an existing waveform to store the coil current. A new waveform is created in the {group_name!r} group"
119+
"Could not find an existing waveform to store the coil current. "
120+
f"New waveform(s) are created in the {group_name!r} group"
157121
)
158122
else:
159123
pn.state.notifications.success(
160-
"The coil currents were appended to their respective waveforms."
124+
"The values of the coil currents were appended to their respective "
125+
"waveforms."
161126
)
162127

128+
def _append_to_existing_waveform(self, config, name, current):
129+
"""Append coil current value to an existing waveform. If the last tendency is a
130+
piecewise tendency, it is extended, otherwise a new piecewise tendency
131+
is added.
132+
133+
Args:
134+
config: The waveform configuration.
135+
name: Name of the waveform.
136+
current: Coil current value to append.
137+
"""
138+
waveform = config[name]
139+
last_tendency = waveform.tendencies[-1]
140+
141+
# Either append to existing piecewise linear tendency, or create new
142+
# piecewise linear tendency
143+
if isinstance(last_tendency, PiecewiseLinearTendency):
144+
waveform.yaml[-1]["time"].append(float(self.export_time))
145+
waveform.yaml[-1]["value"].append(float(current))
146+
yaml_str = f"{name}:\n{waveform.get_yaml_string()}"
147+
else:
148+
end = waveform.tendencies[-1].end
149+
new_piecewise = (
150+
f"- {{type: piecewise, time: [{end}, {self.export_time}], "
151+
f"value: [{current}, {current}]}}"
152+
)
153+
yaml_str = f"{name}:\n{waveform.get_yaml_string()}{new_piecewise}"
154+
155+
new_waveform = config.parse_waveform(yaml_str)
156+
config.replace_waveform(new_waveform)
157+
158+
def _create_new_waveform(self, config, name, current, group_name):
159+
"""Create a new waveform for a coil current when none exists.
160+
161+
Args:
162+
config: The waveform configuration.
163+
name: Name of the waveform.
164+
current: Coil current value to append.
165+
group_name: Name of the group to place the new waveform in.
166+
"""
167+
# Piecewise tendencies must contain at least two points
168+
eps = 1e-9
169+
new_piecewise = (
170+
f"- {{type: piecewise, time: [{self.export_time - eps}, "
171+
f"{self.export_time}], value: [{current}, {current}]}}"
172+
)
173+
waveform = config.parser.parse_waveform(f"{name}:\n{new_piecewise}")
174+
config.add_waveform(waveform, [group_name])
175+
176+
def _has_valid_export_time(self):
177+
"""Check whether the export time is later than the last tendency endpoint
178+
in all existing coil current waveforms.
179+
180+
Returns:
181+
True if export time is valid, False otherwise.
182+
"""
183+
latest_time = None
184+
for i in range(len(self.coil_ui)):
185+
name = f"pf_active/coil({i + 1})/current/data"
186+
if name in self.main_gui.config.waveform_map:
187+
tendencies = self.main_gui.config[name].tendencies
188+
if tendencies:
189+
end_time = tendencies[-1].end
190+
if latest_time is None or end_time > latest_time:
191+
latest_time = end_time
192+
193+
if latest_time is not None and latest_time >= self.export_time:
194+
pn.state.notifications.error(
195+
f"Invalid export time: {self.export_time}. It must be greater than the "
196+
f"last endpoint of existing coil current waveforms ({latest_time})."
197+
)
198+
return False
199+
200+
return True
201+
163202
def fill_pf_active(self, pf_active):
164203
"""Update the coil currents of the provided pf_active IDS. Only coils with
165204
their corresponding checkbox checked are updated.
@@ -172,11 +211,11 @@ def fill_pf_active(self, pf_active):
172211
pf_active.coil[i].current.data = np.array([slider.value])
173212

174213
def _get_currents(self):
214+
"""Returns the coil current values in a list."""
175215
coil_currents = []
176216
for coil_ui in self.coil_ui:
177217
_, slider = coil_ui.objects
178218
coil_currents.append(slider.value)
179-
180219
return coil_currents
181220

182221
def sync_ui_with_pf_active(self, pf_active):

0 commit comments

Comments
 (0)