66from panel .viewable import Viewer
77
88from waveform_editor .derived_waveform import DerivedWaveform
9- from waveform_editor .tendencies .linear import LinearTendency
109from waveform_editor .tendencies .piecewise import PiecewiseLinearTendency
11- from waveform_editor .waveform import Waveform
1210
1311
1412class CoilCurrents (Viewer ):
1513 coil_ui = param .List (
1614 doc = "List of tuples containing the checkboxes and sliders for the coil currents"
1715 )
18- export_time = param .Number (doc = "Time to export coil currents to" )
16+ export_time = param .Number (
17+ doc = "Select a time at which coil currents will be saved to waveforms"
18+ )
1919
2020 def __init__ (self , main_gui ):
2121 super ().__init__ ()
@@ -36,10 +36,13 @@ def __init__(self, main_gui):
3636 confirm_button = pn .widgets .Button (
3737 on_click = lambda event : self ._store_coil_currents (),
3838 name = "Save Currents as Waveforms" ,
39- margin = 30 ,
39+ margin = ( 30 , 0 , 0 , 0 ) ,
4040 )
4141 self .panel = pn .Column (
42- pn .Row (export_time_input , confirm_button , no_ids_message ),
42+ pn .Row (
43+ export_time_input , confirm_button , visible = self .param .coil_ui .rx .bool ()
44+ ),
45+ no_ids_message ,
4346 guide_message ,
4447 self .grid_box ,
4548 )
@@ -78,65 +81,119 @@ def create_ui(self, pf_active):
7881 self .coil_ui = new_coil_ui
7982
8083 def _store_coil_currents (self ):
84+ """Store the current values from the coil UI sliders into the waveform
85+ configuration.
86+ """
8187 coil_currents = self ._get_currents ()
8288 config = self .main_gui .config
8389 new_waveforms_created = False
90+ group_name = "Coil Currents"
8491
85- for i in range (len (self .coil_ui )):
86- name = f"pf_active/coil({ i + 1 } )/current/data"
87- if name in self .main_gui .config .waveform_map :
88- tendencies = self .main_gui .config [name ].tendencies
89- if tendencies :
90- end_time = tendencies [- 1 ].end
91- if end_time >= self .export_time :
92- pn .state .notifications .error (
93- "Export time must be later than the end of any existing waveforms"
94- )
95- return
92+ if not self ._has_valid_export_time ():
93+ return
9694
9795 for i , current in enumerate (coil_currents ):
9896 name = f"pf_active/coil({ i + 1 } )/current/data"
99- eps = 1e-100
100- # Piecewise tendencies must contain at least two points
101- new_piecewise = f"- {{type: piecewise, time: [{ self .export_time } , { self .export_time + eps } ], value: [{ current } , { current } ]}}"
102- if not name in config .waveform_map :
103- group_name = "Coil Currents"
97+ if name not in config .waveform_map :
10498 if group_name not in config .groups :
10599 config .add_group (group_name , [])
106- waveform = config .parser .parse_waveform (f"{ name } :\n { new_piecewise } " )
107- config .add_waveform (waveform , [group_name ])
100+ self ._create_new_waveform (config , name , current , group_name )
108101 new_waveforms_created = True
109102 else :
110103 waveform = config [name ]
111104 if isinstance (waveform , DerivedWaveform ):
112- pn .state .error (
113- f"Could not store coil current in { name } , because it is a derived waveform"
105+ pn .state .notifications .error (
106+ f"Could not store coil current in waveform { name !r} , "
107+ "because it is a derived waveform"
114108 )
115109 continue
116-
117- last_tendency = waveform .tendencies [- 1 ]
118- if isinstance (last_tendency , PiecewiseLinearTendency ):
119- waveform .yaml [- 1 ]["time" ].append (float (self .export_time ))
120- waveform .yaml [- 1 ]["value" ].append (float (current ))
121- yaml_str = f"{ name } :\n { waveform .get_yaml_string ()} "
122- else :
123- end = waveform .tendencies [- 1 ].end
124- append_new_piecewise = f"- {{type: piecewise, time: [{ end } , { self .export_time } ], value: [{ current } , { current } ]}}"
125- yaml_str = (
126- f"{ name } :\n { waveform .get_yaml_string ()} { append_new_piecewise } "
127- )
128- new_waveform = config .parse_waveform (yaml_str )
129- config .replace_waveform (new_waveform )
110+ self ._append_to_existing_waveform (config , name , current )
130111
131112 if new_waveforms_created :
132113 self .main_gui .selector .refresh ()
133114 pn .state .notifications .warning (
134- f"Could not find an existing waveform to store the coil current. A new waveform is created in the { group_name !r} group"
115+ "Could not find an existing waveform to store the coil current. "
116+ f"New waveform(s) are created in the { group_name !r} group"
135117 )
136118 else :
137119 pn .state .notifications .success (
138- "The coil currents were appended to their respective waveforms."
120+ "The values of the coil currents were appended to their respective "
121+ "waveforms."
122+ )
123+
124+ def _append_to_existing_waveform (self , config , name , current ):
125+ """Append coil current value to an existing waveform. If the last tendency is a
126+ piecewise tendency, it is extended, otherwise a new piecewise tendency
127+ is added.
128+
129+ Args:
130+ config: The waveform configuration.
131+ name: Name of the waveform.
132+ current: Coil current value to append.
133+ """
134+ waveform = config [name ]
135+ last_tendency = waveform .tendencies [- 1 ]
136+
137+ # Either append to existing piecewise linear tendency, or create new
138+ # piecewise linear tendency
139+ if isinstance (last_tendency , PiecewiseLinearTendency ):
140+ waveform .yaml [- 1 ]["time" ].append (float (self .export_time ))
141+ waveform .yaml [- 1 ]["value" ].append (float (current ))
142+ yaml_str = f"{ name } :\n { waveform .get_yaml_string ()} "
143+ else :
144+ end = waveform .tendencies [- 1 ].end
145+ new_piecewise = (
146+ f"- {{type: piecewise, time: [{ end } , { self .export_time } ], "
147+ f"value: [{ current } , { current } ]}}"
148+ )
149+ yaml_str = f"{ name } :\n { waveform .get_yaml_string ()} { new_piecewise } "
150+
151+ new_waveform = config .parse_waveform (yaml_str )
152+ config .replace_waveform (new_waveform )
153+
154+ def _create_new_waveform (self , config , name , current , group_name ):
155+ """Create a new waveform for a coil current when none exists.
156+
157+ Args:
158+ config: The waveform configuration.
159+ name: Name of the waveform.
160+ current: Coil current value to append.
161+ group_name: Name of the group to place the new waveform in.
162+ """
163+ # Piecewise tendencies must contain at least two points
164+ eps = 1e-9
165+ new_piecewise = (
166+ f"- {{type: piecewise, time: [{ self .export_time - eps } , "
167+ f"{ self .export_time } ], value: [{ current } , { current } ]}}"
168+ )
169+ waveform = config .parser .parse_waveform (f"{ name } :\n { new_piecewise } " )
170+ config .add_waveform (waveform , [group_name ])
171+
172+ def _has_valid_export_time (self ):
173+ """Check whether the export time is later than the last tendency endpoint
174+ in all existing coil current waveforms.
175+
176+ Returns:
177+ True if export time is valid, False otherwise.
178+ """
179+ latest_time = None
180+ for i in range (len (self .coil_ui )):
181+ name = f"pf_active/coil({ i + 1 } )/current/data"
182+ if name in self .main_gui .config .waveform_map :
183+ tendencies = self .main_gui .config [name ].tendencies
184+ if tendencies :
185+ end_time = tendencies [- 1 ].end
186+ if latest_time is None or end_time > latest_time :
187+ latest_time = end_time
188+
189+ if latest_time is not None and latest_time >= self .export_time :
190+ pn .state .notifications .error (
191+ f"Invalid export time: { self .export_time } . It must be greater than the "
192+ f"last endpoint of existing coil current waveforms ({ latest_time } )."
139193 )
194+ return False
195+
196+ return True
140197
141198 def fill_pf_active (self , pf_active ):
142199 """Update the coil currents of the provided pf_active IDS. Only coils with
@@ -151,11 +208,11 @@ def fill_pf_active(self, pf_active):
151208 pf_active .coil [i ].current .data = np .array ([slider .value ])
152209
153210 def _get_currents (self ):
211+ """Returns the coil current values in a list."""
154212 coil_currents = []
155213 for coil_ui in self .coil_ui :
156214 _ , slider = coil_ui .objects
157215 coil_currents .append (slider .value )
158-
159216 return coil_currents
160217
161218 def sync_ui_with_pf_active (self , pf_active ):
0 commit comments