sodetlib 0.6.1rc1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,406 @@
1
+ import time
2
+ import numpy as np
3
+ import sodetlib as sdl
4
+ from sodetlib.operations.tracking import relock_tracking_setup
5
+ from sodetlib.operations import uxm_setup, bias_steps
6
+
7
+ import matplotlib.pyplot as plt
8
+ import os
9
+ if not os.environ.get('NO_PYSMURF', False):
10
+ from pysmurf.client.base.smurf_control import SmurfControl
11
+
12
+
13
+
14
+ @sdl.set_action()
15
+ def reload_tune(S, cfg, bands, setup_notches=False,
16
+ new_master_assignment=False, tunefile=None, update_cfg=True):
17
+ """
18
+ Reloads an existing tune, runs setup-notches and serial grad descent
19
+ and eta scan.
20
+
21
+ Args
22
+ -----
23
+ S : SmurfControl
24
+ Pysmurf instance
25
+ cfg : DetConfig
26
+ Det config instance
27
+ bands : list, optional
28
+ List of bands to run
29
+ setup_notches : bool
30
+ Whether to run setup_notches
31
+ new_master_assignment : bool
32
+ Whether to create a new master assignment
33
+ tunefile : str
34
+ Tunefile to load. Defaults to the tunefile in the device cfg.
35
+ update_cfg : bool
36
+ If true, will update the device cfg with the tunefile created by
37
+ setup notches.
38
+ """
39
+
40
+ if tunefile is None:
41
+ tunefile = cfg.dev.exp['tunefile']
42
+
43
+ S.load_tune(tunefile)
44
+
45
+ for band in bands:
46
+ sdl.stop_point(S)
47
+ bcfg = cfg.dev.bands[band]
48
+ S.set_att_uc(band, bcfg['uc_att'])
49
+ S.set_att_dc(band, bcfg['dc_att'])
50
+ sdl.pub_ocs_log(S, f"Relocking tune: Band {band}")
51
+ if setup_notches:
52
+ S.log(f"Setup notches, new_master_assignment={new_master_assignment}")
53
+ S.setup_notches(band, new_master_assignment=new_master_assignment)
54
+ else:
55
+ S.relock(band)
56
+
57
+ if setup_notches and update_cfg:
58
+ # Update tunefile
59
+ cfg.dev.update_experiment({'tunefile': S.tune_file}, update_file=True)
60
+
61
+ run_grad_descent_and_eta_scan(S, cfg, bands=bands, update_tune=False)
62
+
63
+ return True, None
64
+
65
+
66
+ @sdl.set_action()
67
+ def run_grad_descent_and_eta_scan(
68
+ S, cfg, bands=None, update_tune=False, force_run=False, max_iters=None,
69
+ gain=None):
70
+ """
71
+ This function runs serial gradient and eta scan for each band.
72
+ Critically, it pulls in gradient descent tune parameters from the device
73
+ config and uses them to setup the grad descent operation before running.
74
+
75
+ Args
76
+ ----
77
+ S : SmurfControl
78
+ Pysmurf instance
79
+ cfg : DetConfig
80
+ Det config instance
81
+ bands : list, optional
82
+ List of bands to run on. Defaults to all 8
83
+ update_tune : bool
84
+ If this is set to True, the new resonance frequency and eta parameters
85
+ will be loaded into the smurf tune, and a new tunefile will be written
86
+ based on the new measurements.
87
+ force_run : bool
88
+ If True, will reset the ``etaScanInProgress`` variable to force it
89
+ to re-run. This might be necessary if serial gradient descent failed out.
90
+ """
91
+
92
+ if bands is None:
93
+ bands = cfg.dev.exp['active_bands']
94
+ bands = np.atleast_1d(bands)
95
+
96
+ for b in bands:
97
+ sdl.stop_point(S)
98
+ in_progress_reg = S._cryo_root(b) + 'etaScanInProgress'
99
+ if S._caget(in_progress_reg):
100
+ if force_run:
101
+ S._caput(in_progress_reg, 0)
102
+ else:
103
+ raise RuntimeError(
104
+ "Failed during grad descent -- scan already in progress"
105
+ )
106
+
107
+ bcfg = cfg.dev.bands[b]
108
+
109
+ if max_iters is None:
110
+ max_iters = bcfg['gradientDescentMaxIters']
111
+ if gain is None:
112
+ gain = bcfg['gradientDescentGain']
113
+
114
+ S.set_gradient_descent_step_hz(b, bcfg['gradientDescentStepHz'])
115
+ S.set_gradient_descent_max_iters(b, max_iters)
116
+ S.set_gradient_descent_gain(b, gain)
117
+ S.set_gradient_descent_converge_hz(b, bcfg['gradientDescentConvergeHz'])
118
+ S.set_gradient_descent_beta(b, bcfg['gradientDescentBeta'])
119
+
120
+ S.log(f"Running grad descent and eta scan on band {b}")
121
+
122
+ init_scale_array = S.get_amplitude_scale_array(b)
123
+ init_center_freq_array = S.get_center_frequency_array(b)
124
+ S.run_serial_gradient_descent(b)
125
+
126
+ if (S.get_amplitude_scale_array(b) != init_scale_array).any():
127
+ S.log("Grad descent errored out! Resetting state")
128
+ S.set_amplitude_scale_array(b, init_scale_array)
129
+ S.set_center_frequency_array(b, init_center_freq_array)
130
+ S._caput(in_progress_reg, 0)
131
+ raise RuntimeError(
132
+ "Failed during grad descent -- check smurf-streamer logs"
133
+ )
134
+
135
+ S.run_serial_eta_scan(b)
136
+
137
+ if update_tune:
138
+ # This basically does the inverse of what's in the relock function
139
+ band_center_mhz = S.get_band_center_mhz(b)
140
+ subband_offset = S.get_tone_frequency_offset_mhz(b)
141
+ center_freq_array = S.get_center_frequency_array(b)
142
+ eta_phase_array = S.get_eta_phase_array(b)
143
+ eta_mag_array = S.get_eta_mag_array(b)
144
+ res_freqs = band_center_mhz + subband_offset + center_freq_array
145
+
146
+ for res in S.freq_resp[b]['resonances'].values():
147
+ ch = res['channel']
148
+ f0 = res['freq']
149
+ # Pysmurf does this to ignore channels with bad freqs so I
150
+ # guess we will too
151
+ for ll, hh in S._bad_mask:
152
+ if ll < f0 < hh:
153
+ ch = -1
154
+
155
+ res['freq'] = res_freqs[ch]
156
+ res['offset'] = center_freq_array[ch]
157
+ res['eta_phase'] = eta_phase_array[ch]
158
+ res['eta_scaled'] = eta_mag_array[ch]
159
+
160
+ if update_tune:
161
+ S.log("Saving tune!")
162
+ S.save_tune()
163
+ cfg.dev.exp['tunefile'] = S.tune_file
164
+ cfg.dev.update_file()
165
+
166
+
167
+ def get_full_band_sweep(S, cfg, band, chan):
168
+ """
169
+ This runs full_band_ampl_sweep to get the resonator response around a single
170
+ channel. This is useful for debugging bad channels.
171
+
172
+ Args
173
+ -----
174
+ S : SmurfControl
175
+ Pysmurf instance
176
+ cfg : DetConfig
177
+ Det Config instance
178
+ band : int
179
+ smurf-band of channel
180
+ channel : int
181
+ smurf-channel of channel
182
+
183
+ Returns
184
+ ----------
185
+ fs : np.ndarray
186
+ Array of frequencies that were swept over
187
+ resp : np.ndarray
188
+ Complex transmission across the frequency range
189
+ """
190
+ sb = S.get_subband_from_channel(band, chan)
191
+ tone_power = cfg.dev.bands[band]['tone_power']
192
+ center_freq_array = S.get_center_frequency_array(band)
193
+ amp_scale_array = S.get_amplitude_scale_array(band)
194
+ eta_phase_array = S.get_eta_phase_array(band)
195
+ eta_mag_array = S.get_eta_mag_array(band)
196
+ fb_enable = S.get_feedback_enable(band)
197
+ fb_enable_arr = S.get_feedback_enable_array(band)
198
+
199
+ try:
200
+ fs, resp = S.full_band_ampl_sweep(band, [sb], tone_power, 2, n_step=256)
201
+ finally:
202
+ S.set_center_frequency_array(band, center_freq_array)
203
+ S.set_amplitude_scale_array(band, amp_scale_array)
204
+ S.set_eta_phase_array(band, eta_phase_array)
205
+ S.set_eta_mag_array(band, eta_mag_array)
206
+ S.set_feedback_enable(band, fb_enable)
207
+ S.set_feedback_enable_array(band, fb_enable_arr)
208
+ return fs, resp
209
+
210
+
211
+ def plot_channel_resonance(S, cfg, band, chan):
212
+ """
213
+ Measures and plots resonator properties for a single smurf channel.
214
+ This will perform a scan around the specified channel, and will plot the
215
+ resonance dip along with where smurf thinks the resonance frequency is.
216
+ This is very useful for debugging why a channel isn't reading out properly.
217
+
218
+ Args
219
+ -----
220
+ S : (SmurfControl)
221
+ pysmurf instance
222
+ cfg : (DetConfig)
223
+ DetConfig instance
224
+ band : (int)
225
+ smurf band number
226
+ chan : (int)
227
+ smurf channel number
228
+ """
229
+ res_freq = S.channel_to_freq(band, chan)
230
+ fs, resp = get_full_band_sweep(S, cfg, band, chan)
231
+
232
+ fs = fs.ravel() + S.get_band_center_mhz(band)
233
+ resp = resp.ravel()
234
+ fs = fs[np.abs(resp) > 0]
235
+ resp = resp[np.abs(resp) > 0]
236
+
237
+ fig, axes = plt.subplots(2, 1, figsize=(10, 10))
238
+
239
+ # Res-dip plot
240
+ ax = axes[0]
241
+ ax.plot(fs, np.abs(resp))
242
+ ax = ax.twinx()
243
+ ax.plot(fs, np.unwrap(np.angle(resp)), color='C1')
244
+ ax.axvline(res_freq, color='grey', ls='--')
245
+
246
+ # Circ plot
247
+ eta_phase = S.get_eta_phase_array(band)[chan]
248
+ eta = np.exp(1.0j * eta_phase * (2*np.pi) / 360)
249
+ Q = np.real(resp * eta)
250
+ I = -np.imag(resp * eta)
251
+
252
+ ax = axes[1]
253
+ ax.axvline(0, color='grey')
254
+ ax.axhline(0, color='grey')
255
+ ax.scatter(I, Q, c=np.abs(resp))
256
+ ax.scatter(np.real(resp), np.imag(resp), c=np.abs(resp), alpha=0.3)
257
+
258
+ return fig, axes
259
+
260
+
261
+ @sdl.set_action()
262
+ def uxm_relock(
263
+ S, cfg, bands=None, show_plots=False,
264
+ setup_notches=False, new_master_assignment=False, reset_rate_khz=None,
265
+ nphi0=None, skip_setup_amps=False):
266
+ """
267
+ Relocks resonators by running the following steps:
268
+
269
+ 1. Reset state (all off, disable waveform, etc.)
270
+ 2. Set amps and check tolerance
271
+ 3. load tune, setup_notches, serial grad descent and eta scan
272
+ 4. Tracking setup
273
+ 5. Measure noise
274
+
275
+ Args
276
+ -----
277
+ S : SmurfControl
278
+ Pysmurf instance
279
+ cfg : DetConfig
280
+ Det config instance
281
+ bands : list, optional
282
+ List of bands to run on. Defaults to all 8
283
+ setup_notches : bool
284
+ If True, will run setup notches instead of relocking
285
+ new_master_assignment : bool
286
+ Whether to run setup_notches with new_master_assignment.
287
+ Defaults to False
288
+ reset_rate_khz : float, optional
289
+ Flux Ramp Reset Rate to set (kHz), defaults to the value in the dev cfg
290
+ nphi0 : int, optional
291
+ Number of phi0's to ramp through. Defaults to the value that was used
292
+ during setup.
293
+ show_plots : bool
294
+ If True will show plots
295
+ skip_setup_amps : bool
296
+ If True will skip amplifier setup.
297
+
298
+ Returns
299
+ --------
300
+ summary : dict
301
+ Dictionary containing a summary of all run operations.
302
+ """
303
+ if bands is None:
304
+ bands = cfg.dev.exp['active_bands']
305
+ bands = np.atleast_1d(bands)
306
+
307
+ exp = cfg.dev.exp
308
+
309
+ #############################################################
310
+ # 1. Reset to known state
311
+ #############################################################
312
+ S.all_off() # Turn off Flux ramp, tones, and biases
313
+ S.set_rtm_arb_waveform_enable(0)
314
+ S.set_filter_disable(0)
315
+ S.set_downsample_factor(exp['downsample_factor'])
316
+ if exp['coupling_mode'] == 'dc':
317
+ S.set_mode_dc()
318
+ else:
319
+ S.set_mode_ac()
320
+
321
+ for band in bands:
322
+ band_cfg = cfg.dev.bands[band]
323
+ S.set_synthesis_scale(band, exp['synthesis_scale'])
324
+ S.set_att_uc(band, band_cfg['uc_att'])
325
+ S.set_att_dc(band, band_cfg['dc_att'])
326
+ S.set_band_delay_us(band, band_cfg['band_delay_us'])
327
+ S.amplitude_scale[band] = band_cfg['tone_power']
328
+
329
+ summary = {}
330
+ summary['timestamps'] = []
331
+ #############################################################
332
+ # 2. Setup amps
333
+ #############################################################
334
+ sdl.stop_point(S)
335
+ if not skip_setup_amps:
336
+ summary['timestamps'].append(('setup_amps', time.time()))
337
+ sdl.set_session_data(S, 'timestamps', summary['timestamps'])
338
+ success, summary['amps'] = uxm_setup.setup_amps(S, cfg)
339
+
340
+ if not success:
341
+ return False, summary
342
+ else:
343
+ print("Skipping amp setup")
344
+
345
+ #############################################################
346
+ # 3. Load tune
347
+ #############################################################
348
+ sdl.stop_point(S)
349
+ summary['timestamps'].append(('load_tune', time.time()))
350
+ sdl.set_session_data(S, 'timestamps', summary['timestamps'])
351
+ success, summary['reload_tune'] = reload_tune(
352
+ S, cfg, bands, setup_notches=setup_notches,
353
+ new_master_assignment=new_master_assignment)
354
+
355
+ sdl.set_session_data(S, 'reload_tune', summary['reload_tune'])
356
+ if not success:
357
+ return False, summary
358
+
359
+ #############################################################
360
+ # 4. Tracking Setup
361
+ #############################################################
362
+ sdl.stop_point(S)
363
+ summary['timestamps'].append(('tracking_setup', time.time()))
364
+ sdl.set_session_data(S, 'timestamps', summary['timestamps'])
365
+
366
+ # Ensure feedback is enabled
367
+ for b in bands:
368
+ S.set_feedback_enable(b, 1)
369
+
370
+ tr = relock_tracking_setup(
371
+ S, cfg, bands, show_plots=show_plots,
372
+ reset_rate_khz=reset_rate_khz, nphi0=nphi0
373
+ )
374
+ summary['tracking_setup_results'] = tr
375
+
376
+ #############################################################
377
+ # 5. Noise
378
+ #############################################################
379
+ sdl.stop_point(S)
380
+ summary['timestamps'].append(('noise', time.time()))
381
+ sdl.set_session_data(S, 'timestamps', summary['timestamps'])
382
+ am, summary['noise'] = sdl.noise.take_noise(S, cfg, 30,
383
+ show_plot=show_plots,
384
+ save_plot=True,
385
+ g3_tag='post_relock')
386
+ summary['noise']['am'] = am
387
+
388
+ sdl.set_session_data(S, 'noise', {
389
+ 'band_medians': summary['noise']['band_medians']
390
+ })
391
+
392
+ #############################################################
393
+ # 6. Bias group mapping
394
+ #############################################################
395
+ sdl.stop_point(S)
396
+ summary['timestamps'].append(('bg_map', time.time()))
397
+ sdl.set_session_data(S, 'timestamps', summary['timestamps'])
398
+
399
+ bsa = bias_steps.take_bgmap(S, cfg,
400
+ show_plots=show_plots)
401
+ summary['bg_map'] = bsa
402
+
403
+ summary['timestamps'].append(('end', time.time()))
404
+ sdl.set_session_data(S, 'timestamps', summary['timestamps'])
405
+
406
+ return True, summary