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,189 @@
1
+ from sodetlib.operations import uxm_relock, tracking, uxm_setup
2
+ from sodetlib import noise
3
+ import sodetlib as sdl
4
+ import numpy as np
5
+ import time
6
+ from tqdm.auto import tqdm
7
+ import matplotlib.pyplot as plt
8
+ from pysmurf.client.base.smurf_control import SmurfControl
9
+
10
+
11
+ def find_min_total_atten(S, band, atten_offset=2):
12
+ """
13
+ Finds the minimum total atten level (UC + DC) required for the ADCs not to
14
+ be saturated. This assumes tones are already enabled.
15
+
16
+ Args
17
+ -----
18
+ S : SmurfControl
19
+ Pysmurf instance
20
+ band : int
21
+ Band number
22
+ atten_offset : int
23
+ Int to add to total attens to try to avoid ADC saturation.
24
+ """
25
+ uc, dc = 0, 0
26
+ S.set_att_uc(band, 0)
27
+ S.set_att_dc(band, 0)
28
+
29
+ for uc in range(0, 31, 2):
30
+ S.set_att_uc(band, uc)
31
+ if not S.check_adc_saturation(band):
32
+ return min(uc + dc + atten_offset, 60)
33
+ time.sleep(0.05)
34
+
35
+ for dc in range(0, 31, 2):
36
+ S.set_att_dc(band, dc)
37
+ if not S.check_adc_saturation(band):
38
+ return min(uc + dc + atten_offset, 60)
39
+ time.sleep(0.05)
40
+
41
+ return False
42
+
43
+ @sdl.set_action()
44
+ def optimize_band_atten(S: SmurfControl, cfg, band, meas_time=30,
45
+ total_att=None, ucs=None, show_pb=False, bgs=None, full_tune=False):
46
+ """
47
+ This function will optimize values for uc and dc attenuators. First, it
48
+ will find the minimum total attenuation required to avoid saturating ADCs.
49
+ Then it loops through values of UC attenuators (keeping the total atten
50
+ constant), runs gradient descent, tracking setup, and the takes noise.
51
+ It will chose the optimal attenuation to minimize the white noise level.
52
+ After this is run, you should re-run grad-descent and tracking functions
53
+ before taking data.
54
+
55
+ Args
56
+ -----
57
+ S : SmurfControl
58
+ Pysmurf instance
59
+ cfg : DetConfig
60
+ DetConfig instance
61
+ meas_time : float
62
+ Duration (sec) for noise measurement
63
+ total_atts : list
64
+ Total attenuations for each specified band. If this is not passed,
65
+ it will calculate this automatically
66
+ ucs : list
67
+ List of UC attenuations to loop over
68
+ show_pb : bool
69
+ If True, will display a progress bar.
70
+ bgs : list
71
+ List of bias lines to overbias. This must at least contain detectors on
72
+ the band that is being optimized. If None is specified, will just
73
+ overbias all detectors at each step.
74
+ full_tune : bool
75
+ If True, will run ``setup_tune``, which performs find freq and setup
76
+ otches, at each step. This is the way to get the most accurate
77
+ optimization, as eta and the resonance frequency change as you adjust
78
+ the uc attenuation. If your uc step is small enough (1 or maybe 2), then
79
+ the gradient descent and eta scan functions may be sufficient and you
80
+ might not need to do a full tune at each step.
81
+ """
82
+
83
+ if total_att is None:
84
+ total_att = find_min_total_atten(S, band)
85
+ S.log(f"Total att for band {band}: {total_att}")
86
+
87
+ if bgs is None:
88
+ bgs = cfg.dev.exp['active_bgs']
89
+ bgs = np.atleast_1d(bgs)
90
+
91
+ if ucs is None:
92
+ ucs = np.arange(0, 31, 3)
93
+
94
+ nsteps = len(ucs)
95
+ sids = np.zeros(nsteps)
96
+ wls = np.full(nsteps, np.nan)
97
+
98
+ wls_full = [None for _ in range(nsteps)]
99
+ tunefiles = [None for _ in range(nsteps)]
100
+
101
+ for i, uc in enumerate(tqdm(ucs, disable=not show_pb)):
102
+ dc = max(total_att - uc, 0)
103
+ if dc <= 30:
104
+ S.set_att_uc(band, uc)
105
+ S.set_att_dc(band, dc)
106
+ else:
107
+ continue
108
+
109
+ S.log(f"Running UC={uc}")
110
+
111
+ S.flux_ramp_off()
112
+ S.set_flux_ramp_dac(0)
113
+ for bg in bgs:
114
+ S.set_tes_bias_bipolar(bg, 0)
115
+
116
+ if full_tune:
117
+ uxm_setup.setup_tune(S, cfg, bands=band, update_cfg=False)
118
+ else:
119
+ uxm_relock.run_grad_descent_and_eta_scan(S, cfg, bands=[band])
120
+
121
+ tunefiles[i] = S.tune_file
122
+ tracking.relock_tracking_setup(S, cfg, band)
123
+ S.overbias_tes_all(bgs)
124
+ _, res = noise.take_noise(S, cfg, meas_time, plot_band_summary=False, show_plot=False, save_plot=False)
125
+ sids[i] = res['sid']
126
+ wls[i] = res['band_medians'][band]
127
+ wls_full[i] = res['noise_pars'][:, 0]
128
+
129
+ fname = sdl.make_filename(S, f'atten_optimization_b{band}.npy')
130
+ try:
131
+ wls_full = np.array(wls_full)
132
+ except:
133
+ print('Error: not all wls_full entries are of same shape. Could be due to different number of resonators on each step')
134
+ wls_full = None
135
+ data = dict(
136
+ band=band, total_att=total_att, ucs=ucs, sids=sids, wls=wls,
137
+ wls_full=wls_full, path=fname, tunefiles=tunefiles, meta=sdl.get_metadata(S, cfg)
138
+ )
139
+
140
+ np.save(fname, data, allow_pickle=True)
141
+ S.log(f"Saving {fname}")
142
+ S.pub.register_file(fname, 'atten_optimzation', format='npy')
143
+
144
+ fig, _ = plot_atten_optimization(data)
145
+ fname = sdl.make_filename(S, 'atten_optimization.png', plot=True)
146
+ S.log(f"Saving {fname}")
147
+ fig.savefig(fname)
148
+ S.pub.register_file(fname, 'atten_optimzation', format='png')
149
+
150
+ return data
151
+
152
+
153
+ def plot_atten_optimization(data, ax=None, ylim=None, text_loc=(0.02, 0.03)):
154
+ """
155
+ Plots the results of the optimize_attens function.
156
+
157
+ Args
158
+ -----
159
+ data : dict
160
+ Results from the ``optimize_band_atten`` function
161
+ ax : Matplotlib axis
162
+ Axis to plot on. If None this will create a new figure.
163
+ ylim : tuple
164
+ ylim to set
165
+ text_loc : tuple
166
+ Location of the textbox
167
+ """
168
+ if ax is None:
169
+ fig, ax = plt.subplots()
170
+ else:
171
+ fig = ax.figure
172
+
173
+ ax.plot(data['ucs'], data['wls'])
174
+
175
+ imin = np.nanargmin(data['wls'])
176
+ opt_uc = data['ucs'][imin]
177
+ opt_dc = max(data['total_att'] - opt_uc, 0)
178
+ ax.plot([opt_uc], [data['wls'][imin]], marker='x', color='red')
179
+ ax.set_title(f"Band {data['band']}, total_att={data['total_att']}")
180
+ txt = '\n'.join([
181
+ f"Min = {data['wls'][imin]:.1f} pA/rt(Hz)",
182
+ f"Opt (UC, DC) = ({opt_uc}, {opt_dc})"
183
+ ])
184
+ ax.text(*text_loc, txt, transform=ax.transAxes,
185
+ bbox=dict(facecolor='wheat', alpha=0.2))
186
+ if ylim is not None:
187
+ ax.set_ylim(*ylim)
188
+
189
+ return fig, ax