Skip to content

Commit 8eb3e30

Browse files
authored
Merge pull request #26 from catheybl/main
Updated Wire Scanners
2 parents 7bf0d9e + d8867ac commit 8eb3e30

3 files changed

Lines changed: 81 additions & 27 deletions

File tree

virtaccl/PyORBIT_Model/pyorbit_va_nodes.py

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -146,17 +146,17 @@ def getYAvg(self):
146146
# Class for wire scanners. This class simply returns histograms of the vertical and horizontal positions.
147147
class WSclass(BaseLinacNode):
148148
node_type = "WireScanner"
149-
parameter_list = ['x_histogram', 'y_histogram', 'x_avg', 'y_avg', 'x_sigma', 'y_sigma']
149+
parameter_list = ['x_histogram', 'y_histogram', 'x_avg', 'y_avg', 'x_sigma', 'y_sigma', 'bin_number']
150150

151151
def __init__(self, node_name: str, bin_number: int = 50):
152-
parameters = {'x_histogram': np.array([[-10, 0], [10, 0]]), 'y_histogram': np.array([[-10, 0], [10, 0]]),
153-
'x_avg': 0.0, 'y_avg': 0.0, 'x_sigma': 0.0, 'y_sigma': 0.0}
152+
default_histogram = np.column_stack((np.linspace(-10, 10, bin_number), np.zeros(bin_number)))
153+
parameters = {'x_histogram': default_histogram, 'y_histogram': default_histogram,
154+
'x_avg': 0.0, 'y_avg': 0.0, 'x_sigma': 0.0, 'y_sigma': 0.0, 'bin_number': bin_number}
154155
BaseLinacNode.__init__(self, node_name)
155156
for key, value in parameters.items():
156157
self.addParam(key, value)
157158
self.node_name = node_name
158159
self.setType(WSclass.node_type)
159-
self.bin_number = bin_number
160160

161161
def track(self, paramsDict):
162162
if "bunch" not in paramsDict:
@@ -165,6 +165,7 @@ def track(self, paramsDict):
165165
part_num = bunch.getSizeGlobal()
166166
x_array = np.zeros(part_num)
167167
y_array = np.zeros(part_num)
168+
bin_number = self.getParam('bin_number')
168169
if part_num > 0:
169170
sync_part = bunch.getSyncParticle()
170171
sync_beta = sync_part.beta()
@@ -179,13 +180,13 @@ def track(self, paramsDict):
179180
y_avg += y
180181

181182
x_limits = np.array([np.min(x_array), np.max(x_array)]) * 1.1
182-
x_bin_edges = np.linspace(x_limits[0], x_limits[1], self.bin_number + 1)
183+
x_bin_edges = np.linspace(x_limits[0], x_limits[1], bin_number + 1)
183184
x_hist, x_bins = np.histogram(x_array, bins=x_bin_edges)
184185
x_positions = (x_bins[:-1] + x_bins[1:]) / 2
185186
x_out = np.column_stack((x_positions, x_hist))
186187

187188
y_limits = np.array([np.min(y_array), np.max(y_array)]) * 1.1
188-
y_bin_edges = np.linspace(y_limits[0], y_limits[1], self.bin_number + 1)
189+
y_bin_edges = np.linspace(y_limits[0], y_limits[1], bin_number + 1)
189190
y_hist, y_bins = np.histogram(y_array, bins=y_bin_edges)
190191
y_positions = (y_bins[:-1] + y_bins[1:]) / 2
191192
y_out = np.column_stack((y_positions, y_hist))
@@ -204,8 +205,9 @@ def track(self, paramsDict):
204205
self.setParam('y_sigma', y_sigma)
205206

206207
else:
207-
self.setParam('x_histogram', np.array([[-10, 0], [10, 0]]))
208-
self.setParam('y_histogram', np.array([[-10, 0], [10, 0]]))
208+
default_histogram = np.column_stack((np.linspace(-10, 10, bin_number), np.zeros(bin_number)))
209+
self.setParam('x_histogram', default_histogram)
210+
self.setParam('y_histogram', default_histogram)
209211
self.setParam('x_avg', 0)
210212
self.setParam('y_avg', 0)
211213
self.setParam('x_sigma', 0)
@@ -223,6 +225,18 @@ def getXAvg(self):
223225
def getYAvg(self):
224226
return self.getParam('y_avg')
225227

228+
def getXSigma(self):
229+
return self.getParam('x_sigma')
230+
231+
def getYSigma(self):
232+
return self.getParam('y_sigma')
233+
234+
def getBinNumber(self):
235+
return self.getParam('bin_number')
236+
237+
def setBinNumber(self, new_bin_number):
238+
self.setParam('bin_number', new_bin_number)
239+
226240

227241
# Class for wire scanners. This class simply returns histograms of the vertical and horizontal positions.
228242
class ScreenClass(BaseLinacNode):

virtaccl/site/SNS_Linac/virtual_SNS_linac.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -209,9 +209,11 @@ def build_sns(**kwargs):
209209
beam_line.add_device(bend_device)
210210

211211
wire_scanners = devices_dict["Wire_Scanner"]
212+
bin_number = 50
212213
for name, model_name in wire_scanners.items():
213214
if model_name in element_list:
214-
ws_device = WireScanner(name, model_name)
215+
model.get_element_controller(model_name).get_element().setBinNumber(bin_number)
216+
ws_device = WireScanner(name, model_name, {'bin_number': bin_number})
215217
beam_line.add_device(ws_device)
216218

217219
bpms = devices_dict["BPM"]

virtaccl/site/SNS_Linac/virtual_devices.py

Lines changed: 56 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -164,21 +164,28 @@ class Cavity(Device):
164164
phase_key = 'phase' # [radians]
165165
amp_key = 'amp' # [arb. units]
166166

167+
# Device Defaults
168+
default_initial_phase = 0 # [radians]
169+
default_initial_amp = 1.0 # [arb. units]
170+
167171
def __init__(self, name: str, model_name: str = None, initial_dict: Dict[str, Any] = None, phase_offset=0,
168172
design_amp=15):
169173
if model_name is None:
170174
self.model_name = name
171175
else:
172176
self.model_name = model_name
173177
super().__init__(name, self.model_name)
178+
initial_dict = {} if initial_dict is None else initial_dict
174179

175180
# Sets initial values for parameters.
176-
if initial_dict is not None:
181+
if Cavity.phase_key in initial_dict:
177182
initial_phase = initial_dict[Cavity.phase_key]
183+
else:
184+
initial_phase = Cavity.default_initial_phase
185+
if Cavity.amp_key in initial_dict:
178186
initial_amp = initial_dict[Cavity.amp_key]
179187
else:
180-
initial_phase = 0
181-
initial_amp = 1.0
188+
initial_amp = Cavity.default_initial_amp
182189

183190
self.design_amp = design_amp # [MV]
184191

@@ -297,42 +304,60 @@ class WireScanner(Device):
297304
speed_pv = 'Speed_Set' # [mm/s]
298305
x_avg_pv = 'Hor_Mean_gs' # [mm]
299306
y_avg_pv = 'Ver_Mean_gs' # [mm]
300-
x_sigma_pv = 'Hor_Sigma_gs'
301-
y_sigma_pv = 'Ver_Sigma_gs'
307+
x_sigma_pv = 'Hor_Sigma_gs' # [mm]
308+
y_sigma_pv = 'Ver_Sigma_gs' # [mm]
309+
x_profile_pv = 'Hor_Profile' # [arb. units]
310+
x_axis_pv = 'Hor_Axis' # [mm]
311+
y_profile_pv = 'Ver_Profile' # [arb. units]
312+
y_axis_pv = 'Ver_Axis' # [mm]
302313

303314
# PyORBIT parameter keys
304-
x_hist_key = 'x_histogram' # [arb. units]
305-
y_hist_key = 'y_histogram' # [arb. units]
315+
x_hist_key = 'x_histogram' # [m, arb. units]
316+
y_hist_key = 'y_histogram' # [m, arb. units]
306317
x_avg_key = 'x_avg' # [m]
307318
y_avg_key = 'y_avg' # [m]
308-
x_sigma_key = 'x_sigma'
309-
y_sigma_key = 'y_sigma'
319+
x_sigma_key = 'x_sigma' # [m]
320+
y_sigma_key = 'y_sigma' # [m]
321+
bin_number_key = 'bin_number' # [number]
310322

311323
# Device keys
312324
position_key = 'wire_position' # [m]
313-
speed_key = 'wire_speed' # [m]
325+
speed_key = 'wire_speed' # [m/s]
314326

327+
# Device Constants
315328
x_offset = -0.01 # [m]
316329
y_offset = 0.01 # [m]
317330
wire_coeff = 1 / math.sqrt(2)
318331

332+
# Device Defaults
333+
default_initial_position = -0.05 # [m]
334+
default_initial_speed = 1 # [m/s]
335+
default_bin_number = 50 # number
336+
319337
def __init__(self, name: str, model_name: str = None, initial_dict: Dict[str, Any] = None):
320338
if model_name is None:
321339
self.model_name = name
322340
else:
323341
self.model_name = model_name
324342
super().__init__(name, self.model_name)
343+
initial_dict = {} if initial_dict is None else initial_dict
325344

326345
# Changes the units from meters to millimeters for associated PVs.
327346
self.milli_units = LinearTInv(scaler=1e3)
328347

329-
# Sets initial values for parameters.
330-
if initial_dict is not None:
348+
# Use defaults for any unspecified parameters
349+
if WireScanner.position_key in initial_dict:
331350
initial_position = initial_dict[WireScanner.position_key]
351+
else:
352+
initial_position = WireScanner.default_initial_position
353+
if WireScanner.speed_key in initial_dict:
332354
initial_speed = initial_dict[WireScanner.speed_key]
333355
else:
334-
initial_position = -0.05 # [mm]
335-
initial_speed = 1 # [mm/s]
356+
initial_speed = WireScanner.default_initial_speed
357+
if WireScanner.bin_number_key in initial_dict:
358+
bin_number = initial_dict[WireScanner.bin_number_key]
359+
else:
360+
bin_number = WireScanner.default_bin_number
336361

337362
# Defines internal parameters to keep track of the wire position.
338363
self.last_wire_pos = initial_position
@@ -348,8 +373,12 @@ def __init__(self, name: str, model_name: str = None, initial_dict: Dict[str, An
348373
self.register_measurement(WireScanner.y_charge_pv, noise=xy_noise)
349374
self.register_measurement(WireScanner.x_avg_pv, noise=xy_noise, transform=self.milli_units)
350375
self.register_measurement(WireScanner.y_avg_pv, noise=xy_noise, transform=self.milli_units)
351-
self.register_measurement(WireScanner.x_sigma_pv, transform=self.milli_units)
352-
self.register_measurement(WireScanner.y_sigma_pv, transform=self.milli_units)
376+
self.register_measurement(WireScanner.x_sigma_pv, noise=xy_noise, transform=self.milli_units)
377+
self.register_measurement(WireScanner.y_sigma_pv, noise=xy_noise, transform=self.milli_units)
378+
self.register_measurement(WireScanner.x_profile_pv, definition={'count': bin_number})
379+
self.register_measurement(WireScanner.x_axis_pv, transform=self.milli_units, definition={'count': bin_number})
380+
self.register_measurement(WireScanner.y_profile_pv, definition={'count': bin_number})
381+
self.register_measurement(WireScanner.y_axis_pv, transform=self.milli_units, definition={'count': bin_number})
353382

354383
self.register_setting(WireScanner.speed_pv, default=initial_speed, transform=self.milli_units)
355384
self.register_setting(WireScanner.position_pv, default=initial_position, transform=self.milli_units)
@@ -394,17 +423,26 @@ def update_measurements(self, new_params: Dict[str, Dict[str, Any]] = None):
394423

395424
ws_params = new_params[self.model_name]
396425
x_hist = ws_params[WireScanner.x_hist_key]
426+
x_axis = x_hist[:, 0]
427+
x_profile = x_hist[:, 1]
397428
y_hist = ws_params[WireScanner.y_hist_key]
429+
y_axis = y_hist[:, 0]
430+
y_profile = y_hist[:, 1]
398431

399432
# Find the location of the vertical wire. Then interpolate the histogram from the model at that value.
400433
x_pos = WireScanner.wire_coeff * wire_pos + WireScanner.x_offset
401-
x_value = np.interp(x_pos, x_hist[:, 0], x_hist[:, 1], left=0, right=0)
434+
x_value = np.interp(x_pos, x_axis, x_profile, left=0, right=0)
402435
self.update_measurement(WireScanner.x_charge_pv, x_value)
403436

404437
y_pos = WireScanner.wire_coeff * wire_pos + WireScanner.y_offset
405-
y_value = np.interp(y_pos, y_hist[:, 0], y_hist[:, 1], left=0, right=0)
438+
y_value = np.interp(y_pos, y_axis, y_profile, left=0, right=0)
406439
self.update_measurement(WireScanner.y_charge_pv, y_value)
407440

441+
self.update_measurement(WireScanner.x_profile_pv, x_profile)
442+
self.update_measurement(WireScanner.x_axis_pv, x_axis)
443+
self.update_measurement(WireScanner.y_profile_pv, y_profile)
444+
self.update_measurement(WireScanner.y_axis_pv, y_axis)
445+
408446
self.update_measurement(WireScanner.x_avg_pv, ws_params[WireScanner.x_avg_key])
409447
self.update_measurement(WireScanner.y_avg_pv, ws_params[WireScanner.y_avg_key])
410448
self.update_measurement(WireScanner.x_sigma_pv, ws_params[WireScanner.x_sigma_key])

0 commit comments

Comments
 (0)