pytesprocess 0.1.1__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.
- pytesprocess/__init__.py +9 -0
- pytesprocess/_version.py +2 -0
- pytesprocess/cli/__init__.py +1 -0
- pytesprocess/cli/commands/__init__.py +5 -0
- pytesprocess/cli/commands/event.py +66 -0
- pytesprocess/cli/commands/filter.py +17 -0
- pytesprocess/cli/commands/ivsweep.py +29 -0
- pytesprocess/cli/common.py +86 -0
- pytesprocess/cli/main.py +81 -0
- pytesprocess/config/__init__.py +4 -0
- pytesprocess/config/loader.py +94 -0
- pytesprocess/config/manager.py +297 -0
- pytesprocess/config/resolvers/__init__.py +5 -0
- pytesprocess/config/resolvers/common.py +56 -0
- pytesprocess/config/resolvers/feature.py +293 -0
- pytesprocess/config/resolvers/salting.py +86 -0
- pytesprocess/config/resolvers/trigger.py +84 -0
- pytesprocess/config/selectors.py +108 -0
- pytesprocess/config/validation.py +314 -0
- pytesprocess/config/warnings.py +2 -0
- pytesprocess/core/__init__.py +10 -0
- pytesprocess/core/algorithms.py +1455 -0
- pytesprocess/core/didv.py +1648 -0
- pytesprocess/core/eventbuilder.py +495 -0
- pytesprocess/core/filterbuilder.py +81 -0
- pytesprocess/core/filterdata.py +1849 -0
- pytesprocess/core/ivsweep.py +2072 -0
- pytesprocess/core/noise.py +923 -0
- pytesprocess/core/noisemodel.py +1408 -0
- pytesprocess/core/oftrigger.py +1035 -0
- pytesprocess/core/template.py +450 -0
- pytesprocess/process/__init__.py +6 -0
- pytesprocess/process/data_source.py +185 -0
- pytesprocess/process/event_context.py +35 -0
- pytesprocess/process/feature_plan.py +186 -0
- pytesprocess/process/feature_resources.py +267 -0
- pytesprocess/process/features.py +1024 -0
- pytesprocess/process/filterprocess.py +1176 -0
- pytesprocess/process/ivprocess.py +1380 -0
- pytesprocess/process/processing_data.py +967 -0
- pytesprocess/process/randoms.py +921 -0
- pytesprocess/process/triggers.py +1011 -0
- pytesprocess/salting/__init__.py +7 -0
- pytesprocess/salting/generator.py +364 -0
- pytesprocess/salting/injector.py +329 -0
- pytesprocess/salting/sampling.py +84 -0
- pytesprocess/utils/__init__.py +5 -0
- pytesprocess/utils/arg_utils.py +122 -0
- pytesprocess/utils/dataframe_output.py +120 -0
- pytesprocess/utils/filter_hdf5.py +594 -0
- pytesprocess/utils/utils.py +701 -0
- pytesprocess/workflows/__init__.py +3 -0
- pytesprocess/workflows/processing.py +317 -0
- pytesprocess/workflows/salting.py +133 -0
- pytesprocess-0.1.1.dist-info/METADATA +211 -0
- pytesprocess-0.1.1.dist-info/RECORD +60 -0
- pytesprocess-0.1.1.dist-info/WHEEL +5 -0
- pytesprocess-0.1.1.dist-info/entry_points.txt +2 -0
- pytesprocess-0.1.1.dist-info/licenses/LICENSE +21 -0
- pytesprocess-0.1.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,1408 @@
|
|
|
1
|
+
import matplotlib.pyplot as plt
|
|
2
|
+
from lmfit import Model
|
|
3
|
+
import numpy as np
|
|
4
|
+
import qetpy as qp
|
|
5
|
+
from scipy import constants
|
|
6
|
+
from scipy.signal import savgol_filter
|
|
7
|
+
|
|
8
|
+
from pytesprocess.core.filterdata import FilterData
|
|
9
|
+
from pytesprocess.utils import utils
|
|
10
|
+
|
|
11
|
+
__all__ = ['NoiseModel']
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class NoiseModel(FilterData):
|
|
15
|
+
"""
|
|
16
|
+
Class to manage noise calculation from
|
|
17
|
+
randoms for multiple channels
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
def __init__(self, verbose=True):
|
|
21
|
+
"""
|
|
22
|
+
Initialize class
|
|
23
|
+
|
|
24
|
+
Parameters:
|
|
25
|
+
----------
|
|
26
|
+
|
|
27
|
+
verbose : bool, optional
|
|
28
|
+
display information
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
# instantiate base class
|
|
32
|
+
super().__init__(verbose=verbose)
|
|
33
|
+
|
|
34
|
+
# data
|
|
35
|
+
self._noise_data = dict()
|
|
36
|
+
self._tbath = None
|
|
37
|
+
self._tload_guess = None
|
|
38
|
+
self._tc = dict()
|
|
39
|
+
self._gta = dict()
|
|
40
|
+
self._inductance = dict()
|
|
41
|
+
self._poles = None
|
|
42
|
+
|
|
43
|
+
# internal data for SC fit
|
|
44
|
+
self._s_isquid_for_fit = None
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def set_iv_didv_results_from_file(self, file_name,
|
|
48
|
+
poles=2,
|
|
49
|
+
channels=None):
|
|
50
|
+
"""
|
|
51
|
+
set dIdV and/or IV sweep results from an HDF5 file
|
|
52
|
+
"""
|
|
53
|
+
|
|
54
|
+
self.load_hdf5(file_name)
|
|
55
|
+
|
|
56
|
+
if channels is None:
|
|
57
|
+
channels = self._filter_data.keys()
|
|
58
|
+
if not channels:
|
|
59
|
+
raise ValueError(f'ERROR: No data loaded... '
|
|
60
|
+
f'Check file {file_name}')
|
|
61
|
+
elif isinstance(channels, str):
|
|
62
|
+
channels = [channels]
|
|
63
|
+
|
|
64
|
+
for chan in channels:
|
|
65
|
+
|
|
66
|
+
# check if filter data
|
|
67
|
+
if chan not in self._filter_data.keys():
|
|
68
|
+
raise ValueError(f'ERROR: No data loaded for channel {chan}. '
|
|
69
|
+
f'Check file {file_name} !')
|
|
70
|
+
# dIdV results
|
|
71
|
+
didv_results = None
|
|
72
|
+
try:
|
|
73
|
+
didv_results = self.get_didv_results(chan, poles=poles)
|
|
74
|
+
except:
|
|
75
|
+
print(f'WARNING: No {poles}-poles dIdV results found for '
|
|
76
|
+
f'channel {chan}!')
|
|
77
|
+
|
|
78
|
+
# IV results
|
|
79
|
+
ivsweep_results = None
|
|
80
|
+
try:
|
|
81
|
+
ivsweep_results = self.get_ivsweep_results(chan)
|
|
82
|
+
except:
|
|
83
|
+
pass
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
self.set_iv_didv_results_from_dict(
|
|
87
|
+
chan,
|
|
88
|
+
didv_results=didv_results,
|
|
89
|
+
poles=poles,
|
|
90
|
+
ivsweep_results=ivsweep_results
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
def set_iv_didv_results_from_dict(self, channel,
|
|
94
|
+
didv_results=None,
|
|
95
|
+
poles=2,
|
|
96
|
+
ivsweep_results=None):
|
|
97
|
+
"""
|
|
98
|
+
Set didv from dictionary for specified channel
|
|
99
|
+
"""
|
|
100
|
+
|
|
101
|
+
# check if channel exist
|
|
102
|
+
if channel not in self._noise_data.keys():
|
|
103
|
+
self._noise_data[channel] = dict()
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
# save poles
|
|
107
|
+
self._poles = poles
|
|
108
|
+
|
|
109
|
+
# dIdV results
|
|
110
|
+
if didv_results is not None:
|
|
111
|
+
|
|
112
|
+
if poles is None:
|
|
113
|
+
raise ValueError('ERROR: dIdV poles (2 or 3) required!')
|
|
114
|
+
|
|
115
|
+
# add to filter data
|
|
116
|
+
metadata = None
|
|
117
|
+
if 'metadata' in didv_results:
|
|
118
|
+
metadata = didv_results['metadata']
|
|
119
|
+
self.set_didv_results(
|
|
120
|
+
channel,
|
|
121
|
+
didv_results,
|
|
122
|
+
poles=poles,
|
|
123
|
+
metadata=metadata
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
# Add small signal parameters
|
|
127
|
+
if 'smallsignalparams' in didv_results.keys():
|
|
128
|
+
self._noise_data[channel]['smallsignalparams'] = (
|
|
129
|
+
didv_results['smallsignalparams'].copy()
|
|
130
|
+
)
|
|
131
|
+
else:
|
|
132
|
+
raise ValueError(f'ERROR: dIdV fit results '
|
|
133
|
+
f'does not contain "smallsignalparams" '
|
|
134
|
+
f'for channel {channel}!')
|
|
135
|
+
|
|
136
|
+
# add "biasparams"
|
|
137
|
+
if ('biasparams' in didv_results.keys()
|
|
138
|
+
and didv_results['biasparams'] is not None):
|
|
139
|
+
self._noise_data[channel]['biasparams'] = didv_results['biasparams']
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
# IV results
|
|
143
|
+
if ivsweep_results is not None:
|
|
144
|
+
|
|
145
|
+
# add to filter data
|
|
146
|
+
self.set_ivsweep_results(
|
|
147
|
+
channel,
|
|
148
|
+
ivsweep_results,
|
|
149
|
+
'noise')
|
|
150
|
+
|
|
151
|
+
# add to noise data
|
|
152
|
+
if 'biasparams' not in self._noise_data[channel]:
|
|
153
|
+
self._noise_data[channel]['biasparams'] = ivsweep_results
|
|
154
|
+
else:
|
|
155
|
+
self._noise_data[channel]['biasparams'].update(ivsweep_results)
|
|
156
|
+
|
|
157
|
+
# add more quantities
|
|
158
|
+
if 'rn' not in self._noise_data[channel]['biasparams']:
|
|
159
|
+
self._noise_data[chan]['biasparams']['rn'] = (
|
|
160
|
+
ivsweep_results['rn']
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
# add more quantities
|
|
164
|
+
if 'rp' not in self._noise_data[channel]['biasparams']:
|
|
165
|
+
self._noise_data[chan]['biasparams']['rp'] = (
|
|
166
|
+
ivsweep_results['rp']
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
if 'rshunt' not in self._noise_data[channel]['biasparams']:
|
|
171
|
+
self._noise_data[channel]['biasparams']['rshunt'] = (
|
|
172
|
+
ivsweep_results['rshunt']
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
# inductance
|
|
176
|
+
if 'normal_didv_fit_L' in ivsweep_results.keys():
|
|
177
|
+
self.set_inductance(channel,
|
|
178
|
+
ivsweep_results['normal_didv_fit_L'],
|
|
179
|
+
'normal')
|
|
180
|
+
|
|
181
|
+
if 'sc_didv_fit_L' in ivsweep_results.keys():
|
|
182
|
+
self.set_inductance(channel,
|
|
183
|
+
ivsweep_results['sc_didv_fit_L'],
|
|
184
|
+
'sc')
|
|
185
|
+
|
|
186
|
+
# check Rn
|
|
187
|
+
if 'rn' not in self._noise_data[channel]['biasparams']:
|
|
188
|
+
raise ValueError(f'ERROR: No Rn found for channel {channel}! '
|
|
189
|
+
f'Add "rn" key/value in "ivsweep_results" '
|
|
190
|
+
f'argument.')
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
def set_inductance(self, channel, L, state):
|
|
194
|
+
"""
|
|
195
|
+
Set inductance when normal/SC state
|
|
196
|
+
"""
|
|
197
|
+
if (state != 'normal' and state != 'sc'):
|
|
198
|
+
raise ValueError('ERROR: "state" argument should be '
|
|
199
|
+
'"normal" or "sc"!')
|
|
200
|
+
|
|
201
|
+
if channel not in self._inductance.keys():
|
|
202
|
+
self._inductance[channel] = dict()
|
|
203
|
+
|
|
204
|
+
self._inductance[channel][state] = float(L)
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
def set_tbath(self, tbath):
|
|
208
|
+
"""
|
|
209
|
+
Set bath temperature
|
|
210
|
+
"""
|
|
211
|
+
|
|
212
|
+
self._tbath = float(tbath)
|
|
213
|
+
|
|
214
|
+
def set_tload_guess(self, tload):
|
|
215
|
+
"""
|
|
216
|
+
Set bath temperature
|
|
217
|
+
"""
|
|
218
|
+
|
|
219
|
+
self._tload_guess = float(tload)
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
def set_tc(self, channel, tc):
|
|
223
|
+
"""
|
|
224
|
+
Set Tc
|
|
225
|
+
"""
|
|
226
|
+
|
|
227
|
+
self._tc[channel] = float(tc)
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
def set_gta(self, channel, gta):
|
|
231
|
+
"""
|
|
232
|
+
Set Gta for specified channel
|
|
233
|
+
"""
|
|
234
|
+
|
|
235
|
+
self._gta[channel] = float(gta)
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
def set_psd_from_file(self, file_name):
|
|
239
|
+
"""
|
|
240
|
+
Set TES data from file
|
|
241
|
+
"""
|
|
242
|
+
|
|
243
|
+
raise ValueError('ERROR: Not implemented yet!')
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
# load hdf5 file
|
|
247
|
+
self.load_hdf5(file_name)
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
def set_psd(self, channel, psd, psd_freqs, state):
|
|
251
|
+
"""
|
|
252
|
+
Set two-sided PSD for "normal", "sc", "transition"
|
|
253
|
+
state, The TES bias used when taking PSD data
|
|
254
|
+
needs also to be set.
|
|
255
|
+
"""
|
|
256
|
+
|
|
257
|
+
state_list = ['normal', 'sc', 'transition']
|
|
258
|
+
if state not in state_list:
|
|
259
|
+
raise ValueError(
|
|
260
|
+
'ERROR: "state" argument should be '
|
|
261
|
+
'"normal", "sc", or "transition"'
|
|
262
|
+
)
|
|
263
|
+
|
|
264
|
+
# initialized
|
|
265
|
+
if channel not in self._noise_data.keys():
|
|
266
|
+
self._noise_data[channel] = dict()
|
|
267
|
+
|
|
268
|
+
if state not in self._noise_data[channel]:
|
|
269
|
+
self._noise_data[channel][state] = dict()
|
|
270
|
+
|
|
271
|
+
# check if folded
|
|
272
|
+
is_folded = not np.any(psd_freqs<0)
|
|
273
|
+
if is_folded:
|
|
274
|
+
raise ValueError(
|
|
275
|
+
'ERROR: Two-sides PSD needs '
|
|
276
|
+
'to be provided, not folded PSD!')
|
|
277
|
+
|
|
278
|
+
self._noise_data[channel][state]['psd'] = psd
|
|
279
|
+
self._noise_data[channel][state]['psd_freqs'] = psd_freqs
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
def set_normal_fit_results(self, channel,
|
|
283
|
+
squiddc=None,
|
|
284
|
+
squidpole=None,
|
|
285
|
+
squidn=None):
|
|
286
|
+
"""
|
|
287
|
+
Set normal noise fit results
|
|
288
|
+
"""
|
|
289
|
+
|
|
290
|
+
if (squiddc is None
|
|
291
|
+
or squidpole is None
|
|
292
|
+
or squidn is None):
|
|
293
|
+
raise ValueError('ERROR: "squiddc", "squidpole", and '
|
|
294
|
+
'"squidn" are required!')
|
|
295
|
+
|
|
296
|
+
if channel not in self._noise_data.keys():
|
|
297
|
+
self._noise_data[channel] = dict()
|
|
298
|
+
|
|
299
|
+
if 'normal' not in self._noise_data[channel]:
|
|
300
|
+
self._noise_data[channel]['normal'] = dict()
|
|
301
|
+
|
|
302
|
+
# add data
|
|
303
|
+
self._noise_data[channel]['normal']['fit'] = {
|
|
304
|
+
'squiddc': float(squiddc),
|
|
305
|
+
'squidpole': float(squidpole),
|
|
306
|
+
'squidn': float(squidn)
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
|
|
310
|
+
def set_sc_fit_results(self, channel, tload=None):
|
|
311
|
+
|
|
312
|
+
"""
|
|
313
|
+
Set SC noise fit results
|
|
314
|
+
"""
|
|
315
|
+
|
|
316
|
+
if tload is None:
|
|
317
|
+
raise ValueError('ERROR: "tload" is required!')
|
|
318
|
+
|
|
319
|
+
if channel not in self._noise_data.keys():
|
|
320
|
+
self._noise_data[channel] = dict()
|
|
321
|
+
|
|
322
|
+
if 'sc' not in self._noise_data[channel]:
|
|
323
|
+
self._noise_data[channel]['sc'] = dict()
|
|
324
|
+
|
|
325
|
+
# add data
|
|
326
|
+
self._noise_data[channel]['sc']['fit'] = {'tload': float(tload)}
|
|
327
|
+
|
|
328
|
+
|
|
329
|
+
def set_squid_noise(self, channel, squid_noise, squid_noise_freqs):
|
|
330
|
+
|
|
331
|
+
"""
|
|
332
|
+
Set SQUID noise
|
|
333
|
+
"""
|
|
334
|
+
|
|
335
|
+
if channel not in self._noise_data.keys():
|
|
336
|
+
self._noise_data[channel] = dict()
|
|
337
|
+
|
|
338
|
+
if 'sim' not in self._noise_data[channel]:
|
|
339
|
+
self._noise_data[channel]['sim'] = {'normal': {},
|
|
340
|
+
'sc' :{},
|
|
341
|
+
'transition': {}}
|
|
342
|
+
|
|
343
|
+
self._noise_data[channel]['sim']['normal']['s_isquid'] = (
|
|
344
|
+
squid_noise
|
|
345
|
+
)
|
|
346
|
+
self._noise_data[channel]['sim']['normal']['freqs'] = (
|
|
347
|
+
squid_noise_freqs
|
|
348
|
+
)
|
|
349
|
+
|
|
350
|
+
|
|
351
|
+
def calc_squid_noise(self,
|
|
352
|
+
channels=None,
|
|
353
|
+
do_fit_normal_noise=False,
|
|
354
|
+
fit_range=(100, 1e5),
|
|
355
|
+
squiddc0=6e-12, squidpole0=200, squidn0=0.7,
|
|
356
|
+
lgc_plot=False, xlims=None, ylims=None,
|
|
357
|
+
lgc_save_fig=False, save_path=None):
|
|
358
|
+
"""
|
|
359
|
+
Calculate SQUID+Electronics noise from normal noise
|
|
360
|
+
either by using normal noise directly (subtracting johnson noise)
|
|
361
|
+
or fitting PSD
|
|
362
|
+
|
|
363
|
+
Parameters
|
|
364
|
+
----------
|
|
365
|
+
fit_range : tuple, optional
|
|
366
|
+
The frequency range over which to do the fit.
|
|
367
|
+
squiddc0 : float, optional
|
|
368
|
+
Initial guess for the squiddc parameter.
|
|
369
|
+
squidpole0 : float, optional
|
|
370
|
+
Initial guess for the squidpole parameter.
|
|
371
|
+
squidn0 : float, optional
|
|
372
|
+
Initial guess for the squidn parameter.
|
|
373
|
+
lgc_plot : bool, optional
|
|
374
|
+
If True, a plot of the fit is shown.
|
|
375
|
+
xlims : NoneType, tuple, optional
|
|
376
|
+
Limits to be passed to ax.set_xlim().
|
|
377
|
+
ylims : NoneType, tuple, optional
|
|
378
|
+
Limits to be passed to ax.set_ylim().
|
|
379
|
+
lgc_save_fig : bool, optional
|
|
380
|
+
If True, the figure is saved.
|
|
381
|
+
save_path : str
|
|
382
|
+
Directory to save data
|
|
383
|
+
|
|
384
|
+
Returns
|
|
385
|
+
-------
|
|
386
|
+
None
|
|
387
|
+
|
|
388
|
+
|
|
389
|
+
"""
|
|
390
|
+
|
|
391
|
+
# check if fit normal noise
|
|
392
|
+
if do_fit_normal_noise:
|
|
393
|
+
self.fit_normal_noise(
|
|
394
|
+
channels=channels,
|
|
395
|
+
fit_range=fit_range,
|
|
396
|
+
squiddc0=squiddc0,
|
|
397
|
+
squidpole0=squidpole0,
|
|
398
|
+
squidn0=squidn0,
|
|
399
|
+
lgc_plot=lgc_plot,
|
|
400
|
+
xlims=xlims, ylims=ylims,
|
|
401
|
+
lgc_save_fig=lgc_save_fig,
|
|
402
|
+
save_path=save_path)
|
|
403
|
+
return
|
|
404
|
+
|
|
405
|
+
# check channels
|
|
406
|
+
if channels is None:
|
|
407
|
+
channels = self._noise_data.keys()
|
|
408
|
+
elif isinstance(channels, str):
|
|
409
|
+
channels = [channels]
|
|
410
|
+
|
|
411
|
+
|
|
412
|
+
for chan in channels:
|
|
413
|
+
|
|
414
|
+
# check psd
|
|
415
|
+
if chan not in self._noise_data.keys():
|
|
416
|
+
raise ValueError(f'ERROR: No data for channel {chan} '
|
|
417
|
+
'available. Set psd and didv data first!')
|
|
418
|
+
|
|
419
|
+
|
|
420
|
+
# check normal parameters exists
|
|
421
|
+
if ('normal' not in self._noise_data[chan]
|
|
422
|
+
or 'psd' not in self._noise_data[chan]['normal']):
|
|
423
|
+
raise ValueError(f'ERROR: No normal psd for channel {chan} '
|
|
424
|
+
'available. Set psd first!')
|
|
425
|
+
|
|
426
|
+
# check tload
|
|
427
|
+
if self._tload_guess is None:
|
|
428
|
+
raise ValueError(f'ERROR: Normal fit requires Tload '
|
|
429
|
+
f'Set Tload first using function '
|
|
430
|
+
f'"set_tload_guess(tload)"!')
|
|
431
|
+
|
|
432
|
+
|
|
433
|
+
# check tload
|
|
434
|
+
if self._tbath is None:
|
|
435
|
+
raise ValueError(f'ERROR: Bath temperature is needed! '
|
|
436
|
+
f'Set Tbath first using function '
|
|
437
|
+
f'"set_tbath(tbath)"!')
|
|
438
|
+
|
|
439
|
+
# didv
|
|
440
|
+
if 'biasparams' not in self._noise_data[chan]:
|
|
441
|
+
raise ValueError(f'ERROR: No iv/didv data for channel {chan} '
|
|
442
|
+
'available. Set didv data first!')
|
|
443
|
+
# inductance
|
|
444
|
+
if (chan not in self._inductance
|
|
445
|
+
or 'normal' not in self._inductance[chan]):
|
|
446
|
+
raise ValueError(f'ERROR: No inductance value for channel {chan} '
|
|
447
|
+
f'available. Set inductance first using function '
|
|
448
|
+
f'set_inductance("{chan}", L, "normal")')
|
|
449
|
+
|
|
450
|
+
# Loop channels and fit data
|
|
451
|
+
for chan in channels:
|
|
452
|
+
|
|
453
|
+
# psd array
|
|
454
|
+
psd = self._noise_data[chan]['normal']['psd']
|
|
455
|
+
psd_freqs = self._noise_data[chan]['normal']['psd_freqs']
|
|
456
|
+
|
|
457
|
+
# tc
|
|
458
|
+
tc = self._tc[chan]
|
|
459
|
+
|
|
460
|
+
# rn/rload
|
|
461
|
+
rn = float(self._noise_data[chan]['biasparams']['rn'])
|
|
462
|
+
rp = float(self._noise_data[chan]['biasparams']['rp'])
|
|
463
|
+
rshunt = None
|
|
464
|
+
if 'rsh' in self._noise_data[chan]['biasparams']:
|
|
465
|
+
rshunt = self._noise_data[chan]['biasparams']['rsh']
|
|
466
|
+
else:
|
|
467
|
+
rshunt = self._noise_data[chan]['biasparams']['rshunt']
|
|
468
|
+
rload = rp + rshunt
|
|
469
|
+
|
|
470
|
+
# inductance
|
|
471
|
+
L = float(self._inductance[chan]['normal'])
|
|
472
|
+
|
|
473
|
+
# calc squid noise
|
|
474
|
+
squid_noise = qp.sim.get_squid_noise_from_normal_noise(
|
|
475
|
+
freqs=psd_freqs,
|
|
476
|
+
normal_noise=psd,
|
|
477
|
+
tload=self._tload_guess,
|
|
478
|
+
tc=tc, rload=rload, rn=rn,
|
|
479
|
+
inductance=L)
|
|
480
|
+
|
|
481
|
+
if 'sim' not in self._noise_data[chan]:
|
|
482
|
+
self._noise_data[chan]['sim'] = {'normal': {},
|
|
483
|
+
'sc' :{},
|
|
484
|
+
'transition': {}}
|
|
485
|
+
|
|
486
|
+
self._noise_data[chan]['sim']['normal']['s_isquid'] = squid_noise
|
|
487
|
+
self._noise_data[chan]['sim']['normal']['freqs'] = psd_freqs
|
|
488
|
+
|
|
489
|
+
|
|
490
|
+
def fit_normal_noise(self, channels=None,
|
|
491
|
+
fit_range=(100, 1e5),
|
|
492
|
+
squiddc0=6e-12, squidpole0=200, squidn0=0.7,
|
|
493
|
+
lgc_plot=False, xlims=None, ylims=None,
|
|
494
|
+
lgc_save_fig=False, save_path=None):
|
|
495
|
+
"""
|
|
496
|
+
Function to fit the noise components of the SQUID+Electronics.
|
|
497
|
+
Fits all normal noise PSDs and stores the average value for
|
|
498
|
+
squiddc, squidpole, and squidn as attributes of the class.
|
|
499
|
+
|
|
500
|
+
Parameters
|
|
501
|
+
----------
|
|
502
|
+
fit_range : tuple, optional
|
|
503
|
+
The frequency range over which to do the fit.
|
|
504
|
+
squiddc0 : float, optional
|
|
505
|
+
Initial guess for the squiddc parameter.
|
|
506
|
+
squidpole0 : float, optional
|
|
507
|
+
Initial guess for the squidpole parameter.
|
|
508
|
+
squidn0 : float, optional
|
|
509
|
+
Initial guess for the squidn parameter.
|
|
510
|
+
lgc_plot : bool, optional
|
|
511
|
+
If True, a plot of the fit is shown.
|
|
512
|
+
xlims : NoneType, tuple, optional
|
|
513
|
+
Limits to be passed to ax.set_xlim().
|
|
514
|
+
ylims : NoneType, tuple, optional
|
|
515
|
+
Limits to be passed to ax.set_ylim().
|
|
516
|
+
lgc_save_fig : bool, optional
|
|
517
|
+
If True, the figure is saved.
|
|
518
|
+
save_path : str
|
|
519
|
+
Directory to save data
|
|
520
|
+
|
|
521
|
+
Returns
|
|
522
|
+
-------
|
|
523
|
+
None
|
|
524
|
+
|
|
525
|
+
"""
|
|
526
|
+
|
|
527
|
+
# check channels
|
|
528
|
+
if channels is None:
|
|
529
|
+
channels = self._noise_data.keys()
|
|
530
|
+
elif isinstance(channels, str):
|
|
531
|
+
channels = [channels]
|
|
532
|
+
|
|
533
|
+
|
|
534
|
+
for chan in channels:
|
|
535
|
+
|
|
536
|
+
# check psd
|
|
537
|
+
if chan not in self._noise_data.keys():
|
|
538
|
+
raise ValueError(f'ERROR: No data for channel {chan} '
|
|
539
|
+
'available. Set psd and didv data first!')
|
|
540
|
+
|
|
541
|
+
|
|
542
|
+
# check normal parameters exists
|
|
543
|
+
if ('normal' not in self._noise_data[chan]
|
|
544
|
+
or 'psd' not in self._noise_data[chan]['normal']):
|
|
545
|
+
raise ValueError(f'ERROR: No normal psd for channel {chan} '
|
|
546
|
+
'available. Set psd first!')
|
|
547
|
+
|
|
548
|
+
|
|
549
|
+
# check tc
|
|
550
|
+
if chan not in self._tc.keys():
|
|
551
|
+
raise ValueError(f'ERROR: No Tc for channel {chan} '
|
|
552
|
+
f'available. Set Tc first using function '
|
|
553
|
+
f'set_tc("{chan}", tc)"!')
|
|
554
|
+
|
|
555
|
+
|
|
556
|
+
# check tload
|
|
557
|
+
if self._tload_guess is None:
|
|
558
|
+
raise ValueError(f'ERROR: Normal fit requires Tload '
|
|
559
|
+
f'Set Tload first using function '
|
|
560
|
+
f'"set_tload_guess(tload)"!')
|
|
561
|
+
|
|
562
|
+
|
|
563
|
+
# check tload
|
|
564
|
+
if self._tbath is None:
|
|
565
|
+
raise ValueError(f'ERROR: Bath temperature is needed! '
|
|
566
|
+
f'Set Tbath first using function '
|
|
567
|
+
f'"set_tbath(tbath)"!')
|
|
568
|
+
|
|
569
|
+
# didv
|
|
570
|
+
if 'biasparams' not in self._noise_data[chan]:
|
|
571
|
+
raise ValueError(f'ERROR: No iv/didv data for channel {chan} '
|
|
572
|
+
'available. Set didv data first!')
|
|
573
|
+
# inductance
|
|
574
|
+
if (chan not in self._inductance
|
|
575
|
+
or 'normal' not in self._inductance[chan]):
|
|
576
|
+
raise ValueError(f'ERROR: No inductance value for channel {chan} '
|
|
577
|
+
f'available. Set inductance first using function '
|
|
578
|
+
f'set_inductance("{chan}", L, "normal")')
|
|
579
|
+
|
|
580
|
+
# Loop channels and fit data
|
|
581
|
+
for chan in channels:
|
|
582
|
+
|
|
583
|
+
# psd array
|
|
584
|
+
psd = self._noise_data[chan]['normal']['psd']
|
|
585
|
+
psd_freqs = self._noise_data[chan]['normal']['psd_freqs']
|
|
586
|
+
|
|
587
|
+
# didv
|
|
588
|
+
didv_results = self.get_didv_results(chan, self._poles)
|
|
589
|
+
|
|
590
|
+
# tc
|
|
591
|
+
tc = self._tc[chan]
|
|
592
|
+
|
|
593
|
+
# rn/rload
|
|
594
|
+
rn = float(self._noise_data[chan]['biasparams']['rn'])
|
|
595
|
+
rp = float(self._noise_data[chan]['biasparams']['rp'])
|
|
596
|
+
rshunt = None
|
|
597
|
+
if 'rsh' in self._noise_data[chan]['biasparams']:
|
|
598
|
+
rshunt = self._noise_data[chan]['biasparams']['rsh']
|
|
599
|
+
else:
|
|
600
|
+
rshunt = self._noise_data[chan]['biasparams']['rshunt']
|
|
601
|
+
rload = rp + rshunt
|
|
602
|
+
|
|
603
|
+
# inductance
|
|
604
|
+
L = float(self._inductance[chan]['normal'])
|
|
605
|
+
|
|
606
|
+
# fit range
|
|
607
|
+
ind_lower = (np.abs(psd_freqs - fit_range[0])).argmin()
|
|
608
|
+
ind_upper = (np.abs(psd_freqs - fit_range[1])).argmin()
|
|
609
|
+
|
|
610
|
+
xdata = psd_freqs[ind_lower:ind_upper]
|
|
611
|
+
ydata = self._flatten_psd(psd_freqs, psd)[ind_lower:ind_upper]
|
|
612
|
+
|
|
613
|
+
# build model and fit
|
|
614
|
+
model = Model(_normal_noise, independent_vars=['freqs'])
|
|
615
|
+
|
|
616
|
+
params = model.make_params(
|
|
617
|
+
squiddc=squiddc0,
|
|
618
|
+
squidpole=squidpole0,
|
|
619
|
+
squidn=squidn0,
|
|
620
|
+
rload=rload,
|
|
621
|
+
tload=self._tload_guess,
|
|
622
|
+
rn=rn,
|
|
623
|
+
tc=tc,
|
|
624
|
+
inductance=L,
|
|
625
|
+
)
|
|
626
|
+
params['tc'].vary = False
|
|
627
|
+
params['tload'].vary = False
|
|
628
|
+
params['rload'].vary = False
|
|
629
|
+
params['rn'].vary = False
|
|
630
|
+
params['inductance'].vary = False
|
|
631
|
+
result = model.fit(ydata, params, freqs=xdata)
|
|
632
|
+
|
|
633
|
+
# fit result
|
|
634
|
+
fitvals = result.values
|
|
635
|
+
|
|
636
|
+
# store values
|
|
637
|
+
self._noise_data[chan]['normal']['fit'] = fitvals
|
|
638
|
+
|
|
639
|
+
# calculate sQUID noise
|
|
640
|
+
squiddc = fitvals['squiddc'],
|
|
641
|
+
squidpole = fitvals['squidpole'],
|
|
642
|
+
squidn = itvals['squidn'],
|
|
643
|
+
squid_noise = self.get_squid_noise_from_fit(
|
|
644
|
+
psd_freqs, squiddc, squidpole, squidn
|
|
645
|
+
)
|
|
646
|
+
|
|
647
|
+
|
|
648
|
+
if 'sim' not in self._noise_data[chan]:
|
|
649
|
+
self._noise_data[chan]['sim'] = {'normal': {},
|
|
650
|
+
'sc' :{},
|
|
651
|
+
'transition': {}}
|
|
652
|
+
# FIXME: Need to unfold
|
|
653
|
+
self._noise_data[chan]['sim']['normal']['s_isquid'] = squid_noise
|
|
654
|
+
self._noise_data[chan]['sim']['normal']['freqs'] = psd_freqs
|
|
655
|
+
|
|
656
|
+
# Instantiate Noise sim
|
|
657
|
+
"""
|
|
658
|
+
noise_sim = qp.sim.TESnoise(
|
|
659
|
+
rload=float(rload),
|
|
660
|
+
r0=rn,
|
|
661
|
+
rshunt=rshunt,
|
|
662
|
+
inductance=L,
|
|
663
|
+
beta=0,
|
|
664
|
+
loopgain=0,
|
|
665
|
+
tau0=0,
|
|
666
|
+
G=0,
|
|
667
|
+
qetbias=tes_bias,
|
|
668
|
+
tc=tc,
|
|
669
|
+
tload=self._tload_guess,
|
|
670
|
+
tbath=self._tbath,
|
|
671
|
+
squiddc=fitvals['squiddc'],
|
|
672
|
+
squidpole=fitvals['squidpole'],
|
|
673
|
+
squidn=fitvals['squidn'],
|
|
674
|
+
)
|
|
675
|
+
|
|
676
|
+
|
|
677
|
+
if lgc_plot:
|
|
678
|
+
qp.plotting.plot_noise_sim(
|
|
679
|
+
f=psd_freqs,
|
|
680
|
+
psd=psd,
|
|
681
|
+
noise_sim=noise_sim,
|
|
682
|
+
istype='normal',
|
|
683
|
+
qetbias=round(tes_bias*1e9)/1e9,
|
|
684
|
+
lgcsave=lgc_save_fig,
|
|
685
|
+
figsavepath=save_path,
|
|
686
|
+
xlims=xlims,
|
|
687
|
+
ylims=ylims,
|
|
688
|
+
)
|
|
689
|
+
"""
|
|
690
|
+
|
|
691
|
+
|
|
692
|
+
def get_squid_noise_from_fit(self, freqs, squiddc, squidpole, squidn):
|
|
693
|
+
"""
|
|
694
|
+
Calculate SQUID noise
|
|
695
|
+
"""
|
|
696
|
+
|
|
697
|
+
s_isquid = (squiddc * (1.0 + (squidpole / freqs)**squidn))**2.0
|
|
698
|
+
return s_isquid
|
|
699
|
+
|
|
700
|
+
|
|
701
|
+
def get_noise_data(self, channel=None, mode='transition'):
|
|
702
|
+
'''
|
|
703
|
+
Gets a dictionary with all relevant noise parameters fit at
|
|
704
|
+
the bias point(s).
|
|
705
|
+
|
|
706
|
+
Parameters
|
|
707
|
+
----------
|
|
708
|
+
channel : str, optional
|
|
709
|
+
The channel name of the data to be fetched.
|
|
710
|
+
|
|
711
|
+
mode : str, optional, default='transition'
|
|
712
|
+
The bias mode type to indicate the range of bias
|
|
713
|
+
indices to return noise fit arrays for.
|
|
714
|
+
Options are: 'transition', 'normal', or 'sc'.
|
|
715
|
+
|
|
716
|
+
Returns
|
|
717
|
+
-------
|
|
718
|
+
noise_dict : dict
|
|
719
|
+
A dictionary with the following keys:
|
|
720
|
+
's_ites', 's_iload', 's_itfn', 's_isquid', 's_itot',
|
|
721
|
+
's_ptes', 's_pload', 's_ptfn', 's_psquid', 's_ptot',
|
|
722
|
+
'freqs'
|
|
723
|
+
'''
|
|
724
|
+
if channel is None:
|
|
725
|
+
raise ValueError("You must specify a channel.")
|
|
726
|
+
|
|
727
|
+
valid_modes = ['transition', 'normal', 'sc']
|
|
728
|
+
if mode not in valid_modes:
|
|
729
|
+
raise ValueError(
|
|
730
|
+
f"'{mode}' is not a valid mode. "
|
|
731
|
+
f"Valid options are: {'transition',' normal',' sc'}.")
|
|
732
|
+
|
|
733
|
+
if channel not in self._noise_data:
|
|
734
|
+
raise ValueError(f"Channel '{channel}' not found in noise data.")
|
|
735
|
+
|
|
736
|
+
if 'sim' not in self._noise_data[channel] or mode not in self._noise_data[channel]['sim']:
|
|
737
|
+
raise ValueError(
|
|
738
|
+
f"Noise data for mode '{mode}' not available for channel '{channel}'. "
|
|
739
|
+
f"Have you run analyze_noise()?")
|
|
740
|
+
|
|
741
|
+
noise_dict = self._noise_data[channel]['sim'][mode]
|
|
742
|
+
return noise_dict
|
|
743
|
+
|
|
744
|
+
|
|
745
|
+
def fit_sc_noise(self, channels=None,
|
|
746
|
+
fit_range=(100, 1e5),
|
|
747
|
+
lgc_plot=False, xlims=None, ylims=None,
|
|
748
|
+
lgc_save_fig=False, save_path=None):
|
|
749
|
+
"""
|
|
750
|
+
Function to fit the components of the SC Noise. Fits all SC
|
|
751
|
+
noise PSDs and stores tload
|
|
752
|
+
|
|
753
|
+
Parameters
|
|
754
|
+
----------
|
|
755
|
+
fit_range : tuple, optional
|
|
756
|
+
The frequency range over which to do the fit.
|
|
757
|
+
|
|
758
|
+
Parameters
|
|
759
|
+
----------
|
|
760
|
+
fit_range : tuple, optional
|
|
761
|
+
The frequency range over which to do the fit.
|
|
762
|
+
lgc_plot : bool, optional
|
|
763
|
+
If True, a plot of the fit is shown.
|
|
764
|
+
xlims : NoneType, tuple, optional
|
|
765
|
+
Limits to be passed to ax.set_xlim().
|
|
766
|
+
ylims : NoneType, tuple, optional
|
|
767
|
+
Limits to be passed to ax.set_ylim().
|
|
768
|
+
lgc_save_fig : bool, optional
|
|
769
|
+
If True, the figure is saved.
|
|
770
|
+
save_path : str
|
|
771
|
+
Directory to save data
|
|
772
|
+
|
|
773
|
+
Returns
|
|
774
|
+
-------
|
|
775
|
+
None
|
|
776
|
+
|
|
777
|
+
"""
|
|
778
|
+
|
|
779
|
+
# check channels
|
|
780
|
+
if channels is None:
|
|
781
|
+
channels = self._noise_data.keys()
|
|
782
|
+
elif isinstance(channels, str):
|
|
783
|
+
channels = [channels]
|
|
784
|
+
|
|
785
|
+
|
|
786
|
+
for chan in channels:
|
|
787
|
+
if chan not in self._noise_data.keys():
|
|
788
|
+
raise ValueError(f'ERROR: No data for channel {chan} '
|
|
789
|
+
'available. Set data first!')
|
|
790
|
+
|
|
791
|
+
|
|
792
|
+
# check normal parameters exists
|
|
793
|
+
if ('sc' not in self._noise_data[chan]
|
|
794
|
+
or 'psd' not in self._noise_data[chan]['sc']):
|
|
795
|
+
raise ValueError(f'ERROR: No SC psd for channel {chan} '
|
|
796
|
+
'available. Set psd first!')
|
|
797
|
+
|
|
798
|
+
|
|
799
|
+
if ('sim' not in self._noise_data[chan]
|
|
800
|
+
or 'normal' not in self._noise_data[chan]['sim']):
|
|
801
|
+
raise ValueError(f'ERROR: SQUID noise should be first calculated '
|
|
802
|
+
f'from normal noise for channel {chan}')
|
|
803
|
+
|
|
804
|
+
|
|
805
|
+
if chan not in self._tc.keys():
|
|
806
|
+
raise ValueError(f'ERROR: No Tc for channel {chan} '
|
|
807
|
+
f'available. Set Tc first using function '
|
|
808
|
+
f'"set_tc({chan}, tc)"!')
|
|
809
|
+
|
|
810
|
+
# didv
|
|
811
|
+
if 'biasparams' not in self._noise_data[chan]:
|
|
812
|
+
raise ValueError(f'ERROR: No iv/didv data for channel {chan} '
|
|
813
|
+
'available. Set didv data first!')
|
|
814
|
+
|
|
815
|
+
# inductance
|
|
816
|
+
if (chan not in self._inductance
|
|
817
|
+
or 'sc' not in self._inductance[chan]):
|
|
818
|
+
raise ValueError(f'ERROR: No inductance value for channel {chan} '
|
|
819
|
+
f'available. Set inductance first using function '
|
|
820
|
+
f'set_inductance("{chan}", L, "sc")')
|
|
821
|
+
|
|
822
|
+
if (lgc_plot
|
|
823
|
+
and (self._poles != 2 or self._poles != 3)):
|
|
824
|
+
print('WARNING: Unable to display SC noise model. Fit results (2 or 3-poles) '
|
|
825
|
+
'required!')
|
|
826
|
+
|
|
827
|
+
|
|
828
|
+
# Loop channels and fit data
|
|
829
|
+
for chan in channels:
|
|
830
|
+
|
|
831
|
+
# psd array
|
|
832
|
+
psd = self._noise_data[chan]['sc']['psd']
|
|
833
|
+
psd_freqs = self._noise_data[chan]['sc']['psd_freqs']
|
|
834
|
+
is_folded = not np.any(psd_freqs<0)
|
|
835
|
+
if is_folded:
|
|
836
|
+
raise ValueError('ERROR: SC PSD should be two-sided')
|
|
837
|
+
|
|
838
|
+
# fold
|
|
839
|
+
fs = utils.estimate_sampling_rate(psd_freqs)
|
|
840
|
+
psd_fold_freqs, psd_fold = qp.foldpsd(psd, fs)
|
|
841
|
+
|
|
842
|
+
# Tc
|
|
843
|
+
tc = self._tc[chan]
|
|
844
|
+
|
|
845
|
+
# rn/rload
|
|
846
|
+
rn = float(self._noise_data[chan]['biasparams']['rn'])
|
|
847
|
+
rp = float(self._noise_data[chan]['biasparams']['rp'])
|
|
848
|
+
rshunt = None
|
|
849
|
+
if 'rsh' in self._noise_data[chan]['biasparams']:
|
|
850
|
+
rshunt = self._noise_data[chan]['biasparams']['rsh']
|
|
851
|
+
else:
|
|
852
|
+
rshunt = self._noise_data[chan]['biasparams']['rshunt']
|
|
853
|
+
rload = rp+rshunt
|
|
854
|
+
|
|
855
|
+
# inductance
|
|
856
|
+
L = self._inductance[chan]['sc']
|
|
857
|
+
|
|
858
|
+
# SQUID noise
|
|
859
|
+
squid_noise = self._noise_data[chan]['sim']['normal']['s_isquid']
|
|
860
|
+
squid_noise_freqs = self._noise_data[chan]['sim']['normal']['freqs']
|
|
861
|
+
|
|
862
|
+
# fold
|
|
863
|
+
fs_squid = utils.estimate_sampling_rate(squid_noise_freqs)
|
|
864
|
+
squid_noise_fold_freqs, squid_noise_fold = (
|
|
865
|
+
qp.foldpsd(squid_noise, fs_squid)
|
|
866
|
+
)
|
|
867
|
+
|
|
868
|
+
# fit range
|
|
869
|
+
ind_lower = (np.abs(psd_freqs - fit_range[0])).argmin()
|
|
870
|
+
ind_upper = (np.abs(psd_freqs - fit_range[1])).argmin()
|
|
871
|
+
|
|
872
|
+
xdata = psd_fold_freqs[ind_lower:ind_upper]
|
|
873
|
+
ydata = self._flatten_psd(psd_fold_freqs, psd_fold)[ind_lower:ind_upper]
|
|
874
|
+
squid_noise_fold_reduced = squid_noise_fold[ind_lower:ind_upper]
|
|
875
|
+
|
|
876
|
+
# save internal data to pass to fit function
|
|
877
|
+
# (no extra array argument allowed with lmfit)
|
|
878
|
+
self._s_isquid_for_fit = squid_noise_fold_reduced
|
|
879
|
+
|
|
880
|
+
# build model and fit
|
|
881
|
+
model = Model(self._sc_noise, independent_vars=['freqs'])
|
|
882
|
+
params = model.make_params(
|
|
883
|
+
tload=self._tload_guess,
|
|
884
|
+
rload=rload,
|
|
885
|
+
inductance=L
|
|
886
|
+
)
|
|
887
|
+
|
|
888
|
+
params['rload'].vary = False
|
|
889
|
+
params['inductance'].vary = False
|
|
890
|
+
result = model.fit(ydata, params, freqs=xdata)
|
|
891
|
+
|
|
892
|
+
# fit result
|
|
893
|
+
fitvals = result.values
|
|
894
|
+
|
|
895
|
+
# store values
|
|
896
|
+
self._noise_data[chan]['sc']['fit'] = fitvals
|
|
897
|
+
|
|
898
|
+
if self._verbose:
|
|
899
|
+
tload = format(fitvals['tload']*1e3, '.2f')
|
|
900
|
+
print(f'INFO: Fitted Tload from SC noise for '
|
|
901
|
+
f'channel {chan} = {tload} mK')
|
|
902
|
+
|
|
903
|
+
|
|
904
|
+
# if 2 or 3-poles fit not availble:
|
|
905
|
+
# unable to instantiate QETpy TESnoise
|
|
906
|
+
if (self._poles != 2 and self._poles != 3):
|
|
907
|
+
continue
|
|
908
|
+
|
|
909
|
+
# get didv results
|
|
910
|
+
didv_results = self.get_didv_results(chan, poles=self._poles)
|
|
911
|
+
|
|
912
|
+
|
|
913
|
+
# Instantiate Noise sim
|
|
914
|
+
noise_sim = qp.sim.TESnoise(
|
|
915
|
+
freqs=psd_freqs,
|
|
916
|
+
didv_result=didv_results,
|
|
917
|
+
tc=tc,
|
|
918
|
+
tload=fitvals['tload'],
|
|
919
|
+
tbath=self._tbath,
|
|
920
|
+
p0_manual=None,
|
|
921
|
+
n=5.0,
|
|
922
|
+
lgc_ballistic=True,
|
|
923
|
+
squid_noise_current=squid_noise,
|
|
924
|
+
squid_noise_current_freqs=squid_noise_freqs,
|
|
925
|
+
lgc_diagnostics=True)
|
|
926
|
+
|
|
927
|
+
# save
|
|
928
|
+
s_isquid = noise_sim.s_isquid(psd_freqs)
|
|
929
|
+
s_iloadsc = noise_sim.s_iloadsc(psd_freqs)
|
|
930
|
+
s_itotsc = noise_sim.s_itotsc(psd_freqs)
|
|
931
|
+
|
|
932
|
+
self._noise_data[chan]['sim']['sc']['s_itotsc'] = s_itotsc
|
|
933
|
+
self._noise_data[chan]['sim']['sc']['s_isquid'] = s_itotsc
|
|
934
|
+
self._noise_data[chan]['sim']['sc']['freqs'] = psd_freqs
|
|
935
|
+
|
|
936
|
+
|
|
937
|
+
# fold
|
|
938
|
+
_,s_isquid_fold = qp.foldpsd(s_isquid, fs)
|
|
939
|
+
_,s_iloadsc_fold = qp.foldpsd(s_iloadsc, fs)
|
|
940
|
+
_,s_itotsc_fold = qp.foldpsd(s_itotsc, fs)
|
|
941
|
+
|
|
942
|
+
# plot
|
|
943
|
+
if lgc_plot:
|
|
944
|
+
|
|
945
|
+
fig, ax = plt.subplots(figsize=(11, 6))
|
|
946
|
+
f = psd_fold_freqs
|
|
947
|
+
|
|
948
|
+
ax.grid(which="minor", linestyle="dotted", alpha=0.5)
|
|
949
|
+
ax.loglog(
|
|
950
|
+
f[1:], np.sqrt(psd_fold[1:]), alpha=0.5,
|
|
951
|
+
color='#8c564b', label='Raw Data',
|
|
952
|
+
)
|
|
953
|
+
ax.loglog(
|
|
954
|
+
f[1:], np.sqrt(s_isquid_fold[1:]),
|
|
955
|
+
color='#9467bd', label='Squid+Electronics Noise',
|
|
956
|
+
)
|
|
957
|
+
ax.loglog(
|
|
958
|
+
f[1:], np.sqrt(s_iloadsc_fold[1:]),
|
|
959
|
+
color='#ff7f0e', label='Load Noise',
|
|
960
|
+
)
|
|
961
|
+
ax.loglog(
|
|
962
|
+
f[1:], np.sqrt(s_itotsc_fold[1:]),
|
|
963
|
+
color='#d62728', label='Total Noise',
|
|
964
|
+
)
|
|
965
|
+
ax.legend()
|
|
966
|
+
ax.set_xlabel('Frequency [Hz]')
|
|
967
|
+
ax.set_ylabel('Input Referenced Current Noise '
|
|
968
|
+
r'[A/$\sqrt{\mathrm{Hz}}$]')
|
|
969
|
+
ax.set_title(f'{chan} Superconducting State noise')
|
|
970
|
+
ax.tick_params(which="both", direction="in",
|
|
971
|
+
right=True, top=True)
|
|
972
|
+
|
|
973
|
+
plt.show()
|
|
974
|
+
|
|
975
|
+
|
|
976
|
+
def analyze_noise(self, channels=None,
|
|
977
|
+
fit_range=(100, 1e5),
|
|
978
|
+
do_fit_normal_noise=False,
|
|
979
|
+
squiddc0=6e-12, squidpole0=200, squidn0=0.7,
|
|
980
|
+
lgc_plot=False,
|
|
981
|
+
xlims=None, ylims_current=None, ylims_power=None,
|
|
982
|
+
lgc_save_fig=False, save_path=None):
|
|
983
|
+
"""
|
|
984
|
+
Function to analyze and plot noise PSD with all the theoretical noise
|
|
985
|
+
components (calculated from the didv fits).
|
|
986
|
+
|
|
987
|
+
lgc_plot : bool, optional
|
|
988
|
+
If True, a plot of the fit is shown.
|
|
989
|
+
xlims : NoneType, tuple, optional
|
|
990
|
+
Limits to be passed to ax.set_xlim().
|
|
991
|
+
ylims_current : NoneType, tuple, optional
|
|
992
|
+
Limits to be passed to ax.set_ylim() for the current noise
|
|
993
|
+
plots.
|
|
994
|
+
ylims_power : NoneType, tuple, optional
|
|
995
|
+
Limits to be passed to ax.set_ylim() for the power noise
|
|
996
|
+
plots.
|
|
997
|
+
lgc_save : bool, optional
|
|
998
|
+
If True, the figure is saved.
|
|
999
|
+
save_path : str
|
|
1000
|
+
Directory to save data
|
|
1001
|
+
|
|
1002
|
+
Returns
|
|
1003
|
+
-------
|
|
1004
|
+
None
|
|
1005
|
+
"""
|
|
1006
|
+
|
|
1007
|
+
# check channels
|
|
1008
|
+
if channels is None:
|
|
1009
|
+
channels = self._noise_data.keys()
|
|
1010
|
+
elif isinstance(channels, str):
|
|
1011
|
+
channels = [channels]
|
|
1012
|
+
|
|
1013
|
+
|
|
1014
|
+
for chan in channels:
|
|
1015
|
+
|
|
1016
|
+
if chan not in self._noise_data.keys():
|
|
1017
|
+
raise ValueError(f'ERROR: No data for channel {chan} '
|
|
1018
|
+
'available. Set data first!')
|
|
1019
|
+
|
|
1020
|
+
# check if either sc psd or fit available
|
|
1021
|
+
state = 'sc'
|
|
1022
|
+
if (state not in self._noise_data[chan]
|
|
1023
|
+
or ('psd' not in self._noise_data[chan][state]
|
|
1024
|
+
and 'fit' not in self._noise_data[chan][state])):
|
|
1025
|
+
raise ValueError(f'ERROR: No {state} psd or fit results for '
|
|
1026
|
+
f'channel {chan} available!')
|
|
1027
|
+
|
|
1028
|
+
# check if either normal psd or fit or squid noise available
|
|
1029
|
+
state = 'normal'
|
|
1030
|
+
if ('sim' not in self._noise_data[chan]
|
|
1031
|
+
and state not in self._noise_data[chan]):
|
|
1032
|
+
raise ValueError(f'ERROR: No {state} psd or fit results '
|
|
1033
|
+
f'or squid noise available for '
|
|
1034
|
+
f'channel {chan}!')
|
|
1035
|
+
|
|
1036
|
+
# check transition psd
|
|
1037
|
+
if ('transition' not in self._noise_data[chan]
|
|
1038
|
+
or 'psd' not in self._noise_data[chan]['transition']):
|
|
1039
|
+
raise ValueError(f'ERROR: No transition psd for '
|
|
1040
|
+
f'channel {chan} available!'
|
|
1041
|
+
f'Set PSD first!')
|
|
1042
|
+
|
|
1043
|
+
# check tc
|
|
1044
|
+
if chan not in self._tc.keys():
|
|
1045
|
+
raise ValueError(f'ERROR: No Tc for channel {chan} '
|
|
1046
|
+
f'available. Set Tc first using function '
|
|
1047
|
+
f'"set_tc({chan}, tc)"!')
|
|
1048
|
+
|
|
1049
|
+
|
|
1050
|
+
# check Gta
|
|
1051
|
+
#if chan not in self._gta.keys() and self._verbose:
|
|
1052
|
+
# print(f'WARNING: No Gta available for channel {chan}. '
|
|
1053
|
+
# f'By default Gta is calculated using 5*p0/tc.\n'
|
|
1054
|
+
# f'You can set more precise Gta using function '
|
|
1055
|
+
# f'"set_gta({chan}, gta)"!')
|
|
1056
|
+
|
|
1057
|
+
# didv
|
|
1058
|
+
#if 'biasparams' not in self._noise_data[chan]:
|
|
1059
|
+
# raise ValueError(f'ERROR: No iv/didv data for channel {chan} '
|
|
1060
|
+
# 'available. Set didv data first!')
|
|
1061
|
+
|
|
1062
|
+
|
|
1063
|
+
# loop channels and do SC/normal fit if needed
|
|
1064
|
+
for chan in channels:
|
|
1065
|
+
|
|
1066
|
+
# get parameters
|
|
1067
|
+
tc = self._tc[chan]
|
|
1068
|
+
rn = self._noise_data[chan]['biasparams']['rn']
|
|
1069
|
+
rp = self._noise_data[chan]['biasparams']['rp']
|
|
1070
|
+
rshunt = None
|
|
1071
|
+
if 'rsh' in self._noise_data[chan]['biasparams']:
|
|
1072
|
+
rshunt = self._noise_data[chan]['biasparams']['rsh']
|
|
1073
|
+
else:
|
|
1074
|
+
rshunt = self._noise_data[chan]['biasparams']['rshunt']
|
|
1075
|
+
rload = rp + rshunt
|
|
1076
|
+
L = self._noise_data[chan]['smallsignalparams']['L']
|
|
1077
|
+
p0 = self._noise_data[chan]['biasparams']['p0']
|
|
1078
|
+
|
|
1079
|
+
# SQUID noise from normal noise
|
|
1080
|
+
if ('sim' not in self._noise_data[chan]
|
|
1081
|
+
or not self._noise_data[chan]['sim']['normal']):
|
|
1082
|
+
self.calc_squid_noise(
|
|
1083
|
+
channels=chan,
|
|
1084
|
+
do_fit_normal_noise=do_fit_normal_noise,
|
|
1085
|
+
fit_range=fit_range,
|
|
1086
|
+
squiddc0=squiddc0, squidpole0=squidpole0,
|
|
1087
|
+
squidn0=squidn0,
|
|
1088
|
+
lgc_plot=lgc_plot, xlims=xlims, ylims=ylims,
|
|
1089
|
+
lgc_save_fig=lgc_save_fig, save_path=save_path
|
|
1090
|
+
)
|
|
1091
|
+
|
|
1092
|
+
# two sided SQUID noise
|
|
1093
|
+
squid_noise = self._noise_data[chan]['sim']['normal']['s_isquid']
|
|
1094
|
+
squid_noise_freqs = self._noise_data[chan]['sim']['normal']['freqs']
|
|
1095
|
+
|
|
1096
|
+
# Tload from SC fit
|
|
1097
|
+
if 'fit' not in self._noise_data[chan]['sc']:
|
|
1098
|
+
self.fit_sc_noise(channels=channels,
|
|
1099
|
+
fit_range=fit_range,
|
|
1100
|
+
lgc_plot=lgc_plot,
|
|
1101
|
+
xlims=xlims, ylims=ylims_current,
|
|
1102
|
+
lgc_save_fig=lgc_save_fig,
|
|
1103
|
+
save_path=save_path)
|
|
1104
|
+
|
|
1105
|
+
# fit results
|
|
1106
|
+
fit_sc = self._noise_data[chan]['sc']['fit']
|
|
1107
|
+
|
|
1108
|
+
# didv results
|
|
1109
|
+
didv_results = self.get_didv_results(chan, poles=self._poles)
|
|
1110
|
+
|
|
1111
|
+
# get psd in transition
|
|
1112
|
+
psd = self._noise_data[chan]['transition']['psd']
|
|
1113
|
+
psd_freqs = self._noise_data[chan]['transition']['psd_freqs']
|
|
1114
|
+
|
|
1115
|
+
# Instantiate TES Noise
|
|
1116
|
+
noise_sim = qp.sim.TESnoise(
|
|
1117
|
+
freqs=psd_freqs,
|
|
1118
|
+
didv_result=didv_results,
|
|
1119
|
+
tc=tc,
|
|
1120
|
+
tload=fit_sc['tload'],
|
|
1121
|
+
tbath=self._tbath,
|
|
1122
|
+
p0_manual=None,
|
|
1123
|
+
n=5.0,
|
|
1124
|
+
lgc_ballistic=True,
|
|
1125
|
+
squid_noise_current=squid_noise,
|
|
1126
|
+
squid_noise_current_freqs=squid_noise_freqs,
|
|
1127
|
+
lgc_diagnostics=False)
|
|
1128
|
+
|
|
1129
|
+
s_ites = noise_sim.s_ites(psd_freqs)
|
|
1130
|
+
s_iload = noise_sim.s_iload(psd_freqs)
|
|
1131
|
+
s_itfn = noise_sim.s_itfn(psd_freqs)
|
|
1132
|
+
s_isquid = noise_sim.s_isquid(psd_freqs)
|
|
1133
|
+
s_itot = noise_sim.s_itot(psd_freqs)
|
|
1134
|
+
|
|
1135
|
+
s_ptes = noise_sim.s_ptes(psd_freqs)
|
|
1136
|
+
s_pload = noise_sim.s_pload(psd_freqs)
|
|
1137
|
+
s_ptfn = noise_sim.s_ptfn(psd_freqs)
|
|
1138
|
+
s_psquid = noise_sim.s_psquid(psd_freqs)
|
|
1139
|
+
s_ptot = noise_sim.s_ptot(psd_freqs)
|
|
1140
|
+
|
|
1141
|
+
|
|
1142
|
+
# convert noise to power
|
|
1143
|
+
p_psd = psd*np.abs(noise_sim.dPdI)**2
|
|
1144
|
+
|
|
1145
|
+
|
|
1146
|
+
# save
|
|
1147
|
+
self._noise_data[chan]['sim']['transition'] = {
|
|
1148
|
+
's_ites':s_ites, 's_iload':s_iload,
|
|
1149
|
+
's_itfn':s_itfn, 's_isquid':s_isquid,
|
|
1150
|
+
's_itot':s_itot,
|
|
1151
|
+
's_ptes':s_ptes, 's_pload':s_pload,
|
|
1152
|
+
's_ptfn':s_ptfn, 's_psquid':s_psquid,
|
|
1153
|
+
's_ptot':s_ptot,
|
|
1154
|
+
'freqs': psd_freqs,
|
|
1155
|
+
's_idata': psd,
|
|
1156
|
+
's_pdata': p_psd
|
|
1157
|
+
}
|
|
1158
|
+
|
|
1159
|
+
|
|
1160
|
+
|
|
1161
|
+
# plot
|
|
1162
|
+
if lgc_plot:
|
|
1163
|
+
|
|
1164
|
+
# fold
|
|
1165
|
+
fs = utils.estimate_sampling_rate(psd_freqs)
|
|
1166
|
+
psd_fold_freqs, psd_fold = qp.foldpsd(psd, fs)
|
|
1167
|
+
_,p_psd_fold = qp.foldpsd(p_psd, fs)
|
|
1168
|
+
|
|
1169
|
+
_,s_ites_fold = qp.foldpsd(s_ites, fs)
|
|
1170
|
+
_,s_iload_fold = qp.foldpsd(s_iload, fs)
|
|
1171
|
+
_,s_itfn_fold = qp.foldpsd(s_itfn, fs)
|
|
1172
|
+
_,s_isquid_fold = qp.foldpsd(s_isquid, fs)
|
|
1173
|
+
_,s_itot_fold = qp.foldpsd(s_itot, fs)
|
|
1174
|
+
|
|
1175
|
+
_,s_ptes_fold = qp.foldpsd(s_ptes, fs)
|
|
1176
|
+
_,s_pload_fold = qp.foldpsd(s_pload, fs)
|
|
1177
|
+
_,s_ptfn_fold = qp.foldpsd(s_ptfn, fs)
|
|
1178
|
+
_,s_psquid_fold = qp.foldpsd(s_psquid, fs)
|
|
1179
|
+
_,s_ptot_fold = qp.foldpsd(s_ptot, fs)
|
|
1180
|
+
|
|
1181
|
+
f = psd_fold_freqs
|
|
1182
|
+
|
|
1183
|
+
fig, ax = plt.subplots(figsize=(11, 6))
|
|
1184
|
+
ax.grid(which="major", linestyle='--')
|
|
1185
|
+
ax.grid(which="minor", linestyle="dotted", alpha=0.5)
|
|
1186
|
+
ax.tick_params(which="both", direction="in", right=True, top=True)
|
|
1187
|
+
ax.set_xlabel(r'Frequency [Hz]')
|
|
1188
|
+
ax.set_title(f'{chan} Current Noise')
|
|
1189
|
+
|
|
1190
|
+
ax.loglog(
|
|
1191
|
+
f[1:],
|
|
1192
|
+
np.sqrt(np.abs(s_ites_fold[1:])),
|
|
1193
|
+
color='#1f77b4',
|
|
1194
|
+
linewidth=1.5,
|
|
1195
|
+
label='TES Johnson Noise',
|
|
1196
|
+
)
|
|
1197
|
+
|
|
1198
|
+
ax.loglog(
|
|
1199
|
+
f[1:],
|
|
1200
|
+
np.sqrt(np.abs(s_iload_fold[1:])),
|
|
1201
|
+
color='#ff7f0e',
|
|
1202
|
+
linewidth=1.5,
|
|
1203
|
+
label='Load Noise',
|
|
1204
|
+
)
|
|
1205
|
+
ax.loglog(
|
|
1206
|
+
f[1:],
|
|
1207
|
+
np.sqrt(np.abs(s_itfn_fold[1:])),
|
|
1208
|
+
color='#2ca02c',
|
|
1209
|
+
linewidth=1.5,
|
|
1210
|
+
label='TFN Noise',
|
|
1211
|
+
)
|
|
1212
|
+
ax.loglog(
|
|
1213
|
+
f[1:],
|
|
1214
|
+
np.sqrt(np.abs(s_itot_fold[1:])),
|
|
1215
|
+
color='#d62728',
|
|
1216
|
+
linewidth=1.5,
|
|
1217
|
+
label='Total Noise',
|
|
1218
|
+
)
|
|
1219
|
+
ax.loglog(
|
|
1220
|
+
f[1:],
|
|
1221
|
+
np.sqrt(np.abs(s_isquid_fold[1:])),
|
|
1222
|
+
color='#9467bd',
|
|
1223
|
+
linewidth=1.5,
|
|
1224
|
+
label='Squid+Electronics Noise',
|
|
1225
|
+
)
|
|
1226
|
+
|
|
1227
|
+
ax.loglog(f[1:], np.sqrt(psd_fold[1:]),
|
|
1228
|
+
color='#8c564b', alpha=0.8,
|
|
1229
|
+
label='Raw Data')
|
|
1230
|
+
|
|
1231
|
+
ax.set_ylabel(r'TES Current Noise $[A/\sqrt{\mathrm{Hz}}]$')
|
|
1232
|
+
|
|
1233
|
+
lgd = plt.legend(loc='upper right')
|
|
1234
|
+
|
|
1235
|
+
|
|
1236
|
+
# power nouise
|
|
1237
|
+
fig, ax = plt.subplots(figsize=(11, 6))
|
|
1238
|
+
ax.grid(which="major", linestyle='--')
|
|
1239
|
+
ax.grid(which="minor", linestyle="dotted", alpha=0.5)
|
|
1240
|
+
ax.tick_params(which="both", direction="in", right=True, top=True)
|
|
1241
|
+
ax.set_xlabel(r'Frequency [Hz]')
|
|
1242
|
+
ax.set_title(f'{chan} Power Noise')
|
|
1243
|
+
|
|
1244
|
+
ax.loglog(
|
|
1245
|
+
f[1:],
|
|
1246
|
+
np.sqrt(np.abs(s_ptes_fold[1:])),
|
|
1247
|
+
color='#1f77b4',
|
|
1248
|
+
linewidth=1.5,
|
|
1249
|
+
label='TES Johnson Noise',
|
|
1250
|
+
)
|
|
1251
|
+
|
|
1252
|
+
ax.loglog(
|
|
1253
|
+
f[1:],
|
|
1254
|
+
np.sqrt(np.abs(s_pload_fold[1:])),
|
|
1255
|
+
color='#ff7f0e',
|
|
1256
|
+
linewidth=1.5,
|
|
1257
|
+
label='Load Noise',
|
|
1258
|
+
)
|
|
1259
|
+
ax.loglog(
|
|
1260
|
+
f[1:],
|
|
1261
|
+
np.sqrt(np.abs(s_ptfn_fold[1:])),
|
|
1262
|
+
color='#2ca02c',
|
|
1263
|
+
linewidth=1.5,
|
|
1264
|
+
label='TFN Noise',
|
|
1265
|
+
)
|
|
1266
|
+
ax.loglog(
|
|
1267
|
+
f[1:],
|
|
1268
|
+
np.sqrt(np.abs(s_ptot_fold[1:])),
|
|
1269
|
+
color='#d62728',
|
|
1270
|
+
linewidth=1.5,
|
|
1271
|
+
label='Total Noise',
|
|
1272
|
+
)
|
|
1273
|
+
ax.loglog(
|
|
1274
|
+
f[1:],
|
|
1275
|
+
np.sqrt(np.abs(s_psquid_fold[1:])),
|
|
1276
|
+
color='#9467bd',
|
|
1277
|
+
linewidth=1.5,
|
|
1278
|
+
label='Squid+Electronics Noise',
|
|
1279
|
+
)
|
|
1280
|
+
|
|
1281
|
+
ax.loglog(f[1:], np.sqrt(p_psd_fold[1:]), color='#8c564b',
|
|
1282
|
+
alpha=0.8, label='Raw Data')
|
|
1283
|
+
|
|
1284
|
+
ax.set_ylabel(r'Input Referenced Power Noise [W/$\sqrt{\mathrm{Hz}}$]')
|
|
1285
|
+
|
|
1286
|
+
lgd = plt.legend(loc='upper right')
|
|
1287
|
+
|
|
1288
|
+
plt.show()
|
|
1289
|
+
|
|
1290
|
+
def _flatten_psd(self, f, psd):
|
|
1291
|
+
"""
|
|
1292
|
+
Helper function to smooth out all the spikes in a single-sided psd
|
|
1293
|
+
in order to more easily fit the SC and Normal state noise.
|
|
1294
|
+
|
|
1295
|
+
Parameters
|
|
1296
|
+
----------
|
|
1297
|
+
f: ndarray
|
|
1298
|
+
Array of frequency values.
|
|
1299
|
+
psd : ndarray
|
|
1300
|
+
Array of one sided psd values.
|
|
1301
|
+
|
|
1302
|
+
Returns
|
|
1303
|
+
-------
|
|
1304
|
+
flattened_psd : ndarray
|
|
1305
|
+
Array of values of smoothed psd.
|
|
1306
|
+
|
|
1307
|
+
"""
|
|
1308
|
+
|
|
1309
|
+
sav = np.zeros(psd.shape)
|
|
1310
|
+
div = int(.0025*len(psd))
|
|
1311
|
+
sav_lower = savgol_filter(psd[1:], 3, 1, mode='interp', deriv=0)
|
|
1312
|
+
sav_upper = savgol_filter(psd[1:], 45, 1, mode='interp', deriv=0)
|
|
1313
|
+
sav[1:div+1] = sav_lower[:div]
|
|
1314
|
+
sav[1+div:] = sav_upper[div:]
|
|
1315
|
+
sav[0] = psd[0]
|
|
1316
|
+
flattened_psd = qp.utils.make_decreasing(sav, x=f)
|
|
1317
|
+
|
|
1318
|
+
return flattened_psd
|
|
1319
|
+
|
|
1320
|
+
def _sc_noise(self, freqs, tload, rload, inductance):
|
|
1321
|
+
"""
|
|
1322
|
+
Functional form of the Super Conducting state noise. Including
|
|
1323
|
+
the Johnson noise for the load resistor and the SQUID + downstream
|
|
1324
|
+
electronics noise. See qetpy.sim.TESnoise class for more info.
|
|
1325
|
+
|
|
1326
|
+
Parameters
|
|
1327
|
+
----------
|
|
1328
|
+
freqs : array
|
|
1329
|
+
Array of frequencies.
|
|
1330
|
+
tload : float
|
|
1331
|
+
The temeperature of the load resistor in Kelvin.
|
|
1332
|
+
rload : float
|
|
1333
|
+
Value of the load resistor in Ohms.
|
|
1334
|
+
inductance : float
|
|
1335
|
+
The inductance of the TES line.
|
|
1336
|
+
s_isquid : array
|
|
1337
|
+
SQUID+Electronics noise array
|
|
1338
|
+
|
|
1339
|
+
Returns
|
|
1340
|
+
-------
|
|
1341
|
+
s_tot : array
|
|
1342
|
+
Array of values corresponding to the theoretical SC state noise.
|
|
1343
|
+
|
|
1344
|
+
"""
|
|
1345
|
+
|
|
1346
|
+
omega = 2.0 * np.pi * freqs
|
|
1347
|
+
dIdVsc = 1.0 / (rload + 1.0j * omega * inductance)
|
|
1348
|
+
s_vload = 4.0 * constants.k * tload * rload * np.ones_like(freqs)
|
|
1349
|
+
s_iloadsc = s_vload * np.abs(dIdVsc)**2.0
|
|
1350
|
+
return s_iloadsc + self._s_isquid_for_fit
|
|
1351
|
+
|
|
1352
|
+
|
|
1353
|
+
def _normal_noise(self, freqs, squiddc, squidpole,
|
|
1354
|
+
squidn, rload, tload, rn, tc,
|
|
1355
|
+
inductance):
|
|
1356
|
+
"""
|
|
1357
|
+
Functional form of the normal state noise. Including the Johnson
|
|
1358
|
+
noise for the load resistor, the Johnson noise for the TES, and the
|
|
1359
|
+
SQUID + downstream electronics noise. See qetpy.sim.TESnoise class
|
|
1360
|
+
for more info.
|
|
1361
|
+
|
|
1362
|
+
Parameters
|
|
1363
|
+
----------
|
|
1364
|
+
freqs : array
|
|
1365
|
+
Array of frequencies.
|
|
1366
|
+
squiddc : float
|
|
1367
|
+
The average value for the white noise from the squid (ignoring
|
|
1368
|
+
the 1/f component).
|
|
1369
|
+
squidpole : float
|
|
1370
|
+
The knee for the 1/f component of the noise.
|
|
1371
|
+
squidn : float
|
|
1372
|
+
The factor for the 1/f^n noise.
|
|
1373
|
+
rload : float
|
|
1374
|
+
Value of the load resistor in Ohms.
|
|
1375
|
+
tload : float
|
|
1376
|
+
The temeperature of the load resistor in Kelvin.
|
|
1377
|
+
rn : float
|
|
1378
|
+
The value of the resistance of the TES when normal.
|
|
1379
|
+
tc : float
|
|
1380
|
+
The SC transistion temperature of the TES.
|
|
1381
|
+
inductance : float
|
|
1382
|
+
The inductance of the TES line.
|
|
1383
|
+
|
|
1384
|
+
Returns
|
|
1385
|
+
-------
|
|
1386
|
+
s_tot : array
|
|
1387
|
+
Array of values corresponding to the theoretical normal state noise.
|
|
1388
|
+
|
|
1389
|
+
"""
|
|
1390
|
+
|
|
1391
|
+
omega = 2.0 * np.pi * freqs
|
|
1392
|
+
dIdVnormal = 1.0 / (rload + rn + 1.0j * omega * inductance)
|
|
1393
|
+
|
|
1394
|
+
# Johnson noise for the load resistor
|
|
1395
|
+
s_vload = 4.0 * constants.k * tload * rload * np.ones_like(freqs)
|
|
1396
|
+
s_iloadnormal = s_vload * np.abs(dIdVnormal)**2.0
|
|
1397
|
+
|
|
1398
|
+
# Johnson noise for the TES
|
|
1399
|
+
s_vtesnormal = 4.0 * constants.k * tc * rn * np.ones_like(freqs)
|
|
1400
|
+
s_itesnormal = s_vtesnormal * np.abs(dIdVnormal)**2.0
|
|
1401
|
+
|
|
1402
|
+
# SQUID + downstream electronics noise
|
|
1403
|
+
s_isquid = (squiddc * (1.0 + (squidpole / freqs)**squidn))**2.0
|
|
1404
|
+
|
|
1405
|
+
# total noise
|
|
1406
|
+
s_tot = s_iloadnormal + s_itesnormal + s_isquid
|
|
1407
|
+
|
|
1408
|
+
return s_tot
|