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,2072 @@
|
|
|
1
|
+
from datetime import datetime
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
import os
|
|
4
|
+
import stat
|
|
5
|
+
|
|
6
|
+
import matplotlib.pyplot as plt
|
|
7
|
+
import numpy as np
|
|
8
|
+
import pandas as pd
|
|
9
|
+
import qetpy as qp
|
|
10
|
+
|
|
11
|
+
from pytesprocess.core.didv import DIDVAnalysis
|
|
12
|
+
from pytesprocess.core.filterdata import FilterData
|
|
13
|
+
from pytesprocess.core.noisemodel import NoiseModel
|
|
14
|
+
|
|
15
|
+
__all__ = [
|
|
16
|
+
'IVSweepAnalysis'
|
|
17
|
+
]
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class IVSweepAnalysis(FilterData):
|
|
21
|
+
"""
|
|
22
|
+
Class to manage iv/sweep calculations using
|
|
23
|
+
QETpy
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
def __init__(self, verbose=True,
|
|
27
|
+
auto_save_hdf5=False,
|
|
28
|
+
save_path=None):
|
|
29
|
+
"""
|
|
30
|
+
Initialize class
|
|
31
|
+
|
|
32
|
+
Parameters:
|
|
33
|
+
----------
|
|
34
|
+
|
|
35
|
+
verbose : bool, optional
|
|
36
|
+
display information
|
|
37
|
+
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
# IV, DIDV, Noise, Template objects
|
|
41
|
+
self._ibis_objects = dict()
|
|
42
|
+
self._didv_objects = dict()
|
|
43
|
+
self._didv_summary = dict()
|
|
44
|
+
|
|
45
|
+
# rshunt/rshunt_err if not in raw data
|
|
46
|
+
# rp/rp error if no SC points
|
|
47
|
+
self._readout_params = dict()
|
|
48
|
+
|
|
49
|
+
# resolution
|
|
50
|
+
self._resolution_data = dict()
|
|
51
|
+
|
|
52
|
+
# IV sweep nb bias point
|
|
53
|
+
self._nb_sc_normal_points = dict()
|
|
54
|
+
|
|
55
|
+
# noise analysis
|
|
56
|
+
self._tbath = None
|
|
57
|
+
self._tc = dict()
|
|
58
|
+
self._gta = dict()
|
|
59
|
+
self._tload_guess = None
|
|
60
|
+
self._noisedict = dict()
|
|
61
|
+
|
|
62
|
+
# save results
|
|
63
|
+
self._save_hdf5 = auto_save_hdf5
|
|
64
|
+
self._save_path = None
|
|
65
|
+
self._file_name = self._set_file_name()
|
|
66
|
+
|
|
67
|
+
if save_path is not None:
|
|
68
|
+
path = Path(save_path)
|
|
69
|
+
if path.is_file():
|
|
70
|
+
self._save_path = str(path.parent)
|
|
71
|
+
self._file_name = path.name
|
|
72
|
+
elif path.is_dir():
|
|
73
|
+
self._save_path = str(path)
|
|
74
|
+
else:
|
|
75
|
+
raise ValueError('ERROR: "save_path" should be a file or path!')
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
if self._save_hdf5:
|
|
79
|
+
full_path_file = self._file_name
|
|
80
|
+
if self._save_path is not None:
|
|
81
|
+
full_path_file = self._save_path + '/' + self._file_name
|
|
82
|
+
print(f'INFO: Results will be automatically saved '
|
|
83
|
+
f'in {full_path_file}')
|
|
84
|
+
|
|
85
|
+
# instantiate base class
|
|
86
|
+
super().__init__(verbose=True)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def clear(self, channels=None):
|
|
90
|
+
"""
|
|
91
|
+
Clear all data
|
|
92
|
+
"""
|
|
93
|
+
# objects
|
|
94
|
+
self._ibis_objects = dict()
|
|
95
|
+
self._didv_objects = dict()
|
|
96
|
+
self._didv_summary = dict()
|
|
97
|
+
self._resolution_data = dict()
|
|
98
|
+
|
|
99
|
+
# filter data
|
|
100
|
+
self.clear_data()
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def get_didv_objects(self, channel):
|
|
104
|
+
"""
|
|
105
|
+
Get the dIdV pytesprocess objects in a dictionary
|
|
106
|
+
key: tes_bias_ua, value: DIDVAnalysis object
|
|
107
|
+
"""
|
|
108
|
+
|
|
109
|
+
output_dict = None
|
|
110
|
+
if channel in self._didv_objects:
|
|
111
|
+
output_dict = self._didv_objects[channel]
|
|
112
|
+
|
|
113
|
+
return output_dict
|
|
114
|
+
|
|
115
|
+
def get_ibis_qetpy_object(self, channel):
|
|
116
|
+
"""
|
|
117
|
+
Get QETpy
|
|
118
|
+
"""
|
|
119
|
+
|
|
120
|
+
output_dict = None
|
|
121
|
+
if channel in self._ibis_objects:
|
|
122
|
+
output_dict = self._ibis_objects[channel]
|
|
123
|
+
return output_dict
|
|
124
|
+
|
|
125
|
+
def get_didv_summary(self, channel):
|
|
126
|
+
"""
|
|
127
|
+
Get QETpy
|
|
128
|
+
"""
|
|
129
|
+
|
|
130
|
+
output_dict = None
|
|
131
|
+
if channel in self._didv_summary:
|
|
132
|
+
output_dict = self._didv_summary[channel]
|
|
133
|
+
return output_dict
|
|
134
|
+
|
|
135
|
+
def get_resolution_data(self, channel):
|
|
136
|
+
"""
|
|
137
|
+
Get QETpy
|
|
138
|
+
"""
|
|
139
|
+
|
|
140
|
+
output_dict = None
|
|
141
|
+
if channel in self._resolution_data:
|
|
142
|
+
output_dict = self._resolution_data[channel]
|
|
143
|
+
return output_dict
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def set_data_from_file(self, file_name):
|
|
147
|
+
"""
|
|
148
|
+
Load IV processed data from file
|
|
149
|
+
"""
|
|
150
|
+
|
|
151
|
+
self.load_hdf5(file_name)
|
|
152
|
+
|
|
153
|
+
# update number of SC/Normal; points
|
|
154
|
+
for chan, chan_dict in self._filter_data.items():
|
|
155
|
+
|
|
156
|
+
# loop keys
|
|
157
|
+
for data_key in chan_dict.keys():
|
|
158
|
+
if ('ivsweep_data' in data_key
|
|
159
|
+
and 'metadata' not in data_key):
|
|
160
|
+
tag = 'default'
|
|
161
|
+
if len(data_key)>13:
|
|
162
|
+
tag = data_key[13:]
|
|
163
|
+
self.set_nb_sc_normal_points(
|
|
164
|
+
chan,
|
|
165
|
+
overwrite=False,
|
|
166
|
+
tag=tag)
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
# update path
|
|
170
|
+
if self._save_path is None:
|
|
171
|
+
dir_name = os.path.dirname(file_name)
|
|
172
|
+
|
|
173
|
+
if '/processed' in dir_name:
|
|
174
|
+
dir_name = dir_name.replace('/processed',
|
|
175
|
+
'/filterdata')
|
|
176
|
+
self._save_path = dir_name
|
|
177
|
+
|
|
178
|
+
if not os.path.isdir(dir_name):
|
|
179
|
+
try:
|
|
180
|
+
os.makedirs(dir_name)
|
|
181
|
+
os.chmod(dir_name, stat.S_IRWXG | stat.S_IRWXU |
|
|
182
|
+
stat.S_IROTH | stat.S_IXOTH)
|
|
183
|
+
except OSError:
|
|
184
|
+
raise ValueError('\nERROR: Unable to create directory "'
|
|
185
|
+
+ dir_name + '"!\n')
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
def set_data_from_dict(self, data_dict, tag='default'):
|
|
189
|
+
"""
|
|
190
|
+
Set data using dictionary with
|
|
191
|
+
keys=channels, values=dataframe
|
|
192
|
+
"""
|
|
193
|
+
|
|
194
|
+
for chan, df in data_dict.items():
|
|
195
|
+
if not isinstance(df, pd.DataFrame):
|
|
196
|
+
raise ValueError(
|
|
197
|
+
'ERROR: Unrecognized format. Dictionary '
|
|
198
|
+
'item should be a pandas dataframe!'
|
|
199
|
+
)
|
|
200
|
+
self.set_data_from_dataframe(chan, df, tag=tag)
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
# set number of bias points for SC/Normal state
|
|
204
|
+
self.set_nb_sc_normal_points(chan,
|
|
205
|
+
overwrite=True,
|
|
206
|
+
tag=tag)
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
def set_data_from_dataframe(self, channel, df,
|
|
210
|
+
tag='default'):
|
|
211
|
+
"""
|
|
212
|
+
Set data from dataframe for a specific channel
|
|
213
|
+
"""
|
|
214
|
+
|
|
215
|
+
if not isinstance(df, pd.DataFrame):
|
|
216
|
+
raise ValueError(
|
|
217
|
+
'ERROR: Unrecognized format. Dictionary '
|
|
218
|
+
'item should be a pandas dataframe!'
|
|
219
|
+
)
|
|
220
|
+
|
|
221
|
+
# sort
|
|
222
|
+
df = df.sort_values('tes_bias',
|
|
223
|
+
ascending=False,
|
|
224
|
+
key=abs,
|
|
225
|
+
ignore_index=True)
|
|
226
|
+
|
|
227
|
+
# store dataframe
|
|
228
|
+
self.set_ivsweep_data(channel,
|
|
229
|
+
df,
|
|
230
|
+
metadata=None,
|
|
231
|
+
tag=tag)
|
|
232
|
+
|
|
233
|
+
# set number of bias points for SC/Normal state
|
|
234
|
+
self.set_nb_sc_normal_points(channel,
|
|
235
|
+
overwrite=True,
|
|
236
|
+
tag=tag)
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
def set_rshunt(self, channels,
|
|
240
|
+
rshunt=None, rshunt_err=None):
|
|
241
|
+
"""
|
|
242
|
+
Set rshunt and/or rshunt error for one or more channels
|
|
243
|
+
if not stored in raw data file.
|
|
244
|
+
|
|
245
|
+
If multiple channels, then all arguments should be a list.
|
|
246
|
+
"""
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
# convert to list
|
|
250
|
+
if isinstance(channels, str):
|
|
251
|
+
channels = [channels]
|
|
252
|
+
|
|
253
|
+
params = {'rshunt': rshunt,
|
|
254
|
+
'rshunt_err': rshunt_err}
|
|
255
|
+
|
|
256
|
+
for param, param_val in params.items():
|
|
257
|
+
if param_val is not None:
|
|
258
|
+
if (isinstance(param_val, int)
|
|
259
|
+
or isinstance(param_val, float)) :
|
|
260
|
+
param_val = [param_val]
|
|
261
|
+
if len(param_val) == 1:
|
|
262
|
+
param_val = param_val*len(channels)
|
|
263
|
+
elif len(param_val)!=len(channels):
|
|
264
|
+
raise ValueError(
|
|
265
|
+
f'ERROR: {param} list should be have length = '
|
|
266
|
+
' # channels')
|
|
267
|
+
else:
|
|
268
|
+
param_val = [None]*len(channels)
|
|
269
|
+
|
|
270
|
+
# replace
|
|
271
|
+
params[param] = param_val
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
for ichan, chan in enumerate(channels):
|
|
275
|
+
if chan not in self._readout_params.keys():
|
|
276
|
+
self._readout_params[chan] = dict()
|
|
277
|
+
self._readout_params[chan]['rshunt'] = params['rshunt'][ichan]
|
|
278
|
+
self._readout_params[chan]['rshunt_err'] = params['rshunt_err'][ichan]
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
def set_rp(self, channels,
|
|
282
|
+
rp=None, rp_err=None):
|
|
283
|
+
"""
|
|
284
|
+
Set rp and/or rp error for one or more channels if no SC points.
|
|
285
|
+
If multiple channels, then all arguments should be a list.
|
|
286
|
+
"""
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
# convert to list
|
|
290
|
+
if isinstance(channels, str):
|
|
291
|
+
channels = [channels]
|
|
292
|
+
|
|
293
|
+
|
|
294
|
+
params = {'rp': rp,
|
|
295
|
+
'rp_err': rp_err}
|
|
296
|
+
|
|
297
|
+
for param, param_val in params.items():
|
|
298
|
+
if param_val is not None:
|
|
299
|
+
if (isinstance(param_val, int)
|
|
300
|
+
or isinstance(param_val, float)):
|
|
301
|
+
param_val = [param_val]
|
|
302
|
+
if len(param_val) == 1:
|
|
303
|
+
param_val = param_val*len(channels)
|
|
304
|
+
elif len(param_val)!=len(channels):
|
|
305
|
+
raise ValueError(
|
|
306
|
+
f'ERROR: "{param}" list should be have length = '
|
|
307
|
+
' # channels')
|
|
308
|
+
else:
|
|
309
|
+
param_val = [None]*len(channels)
|
|
310
|
+
|
|
311
|
+
# replace
|
|
312
|
+
params[param] = param_val
|
|
313
|
+
|
|
314
|
+
for ichan,chan in enumerate(channels):
|
|
315
|
+
if chan not in self._readout_params.keys():
|
|
316
|
+
self._readout_params[chan] = dict()
|
|
317
|
+
self._readout_params[chan]['rp'] = params['rp'][ichan]
|
|
318
|
+
self._readout_params[chan]['rp_err'] = params['rp_err'][ichan]
|
|
319
|
+
|
|
320
|
+
|
|
321
|
+
|
|
322
|
+
def set_nb_sc_normal_points(self, channel,
|
|
323
|
+
nsc=None, nnorm=None,
|
|
324
|
+
overwrite=False,
|
|
325
|
+
tag='default'):
|
|
326
|
+
"""
|
|
327
|
+
Set number of bias points for SC and Normal
|
|
328
|
+
state. If None, number of bias points are decided
|
|
329
|
+
based on linearity estimate (done during the processing
|
|
330
|
+
step)
|
|
331
|
+
|
|
332
|
+
"""
|
|
333
|
+
|
|
334
|
+
# initialize
|
|
335
|
+
if channel not in self._nb_sc_normal_points.keys():
|
|
336
|
+
self._nb_sc_normal_points[channel] = {
|
|
337
|
+
'sc': None,
|
|
338
|
+
'normal': None
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
# get dataframe
|
|
342
|
+
df = self.get_ivsweep_data(channel, tag=tag)
|
|
343
|
+
df = df.sort_values('tes_bias',
|
|
344
|
+
ascending=False,
|
|
345
|
+
key=abs,
|
|
346
|
+
ignore_index=True)
|
|
347
|
+
|
|
348
|
+
# check nb sc/normal points in dataframe
|
|
349
|
+
nsc_df = 0
|
|
350
|
+
nnorm_df = 0
|
|
351
|
+
if 'state' in df.columns:
|
|
352
|
+
nsc_df = len(np.where(df['state']=='sc')[0])
|
|
353
|
+
nnorm_df = len(np.where(df['state']=='normal')[0])
|
|
354
|
+
|
|
355
|
+
# check number of points stored in dictionary
|
|
356
|
+
nsc_current = self._nb_sc_normal_points[channel]['sc']
|
|
357
|
+
nnorm_current = self._nb_sc_normal_points[channel]['normal']
|
|
358
|
+
|
|
359
|
+
# update SC
|
|
360
|
+
if (overwrite or nsc is not None or nsc_current is None):
|
|
361
|
+
if nsc is None:
|
|
362
|
+
nsc = nsc_df
|
|
363
|
+
self._nb_sc_normal_points[channel]['sc'] = nsc
|
|
364
|
+
|
|
365
|
+
# update Normal
|
|
366
|
+
if (overwrite or nnorm is not None or nnorm_current is None):
|
|
367
|
+
if nnorm is None:
|
|
368
|
+
nnorm = nnorm_df
|
|
369
|
+
self._nb_sc_normal_points[channel]['normal'] = nnorm
|
|
370
|
+
|
|
371
|
+
# update dataframe if needed
|
|
372
|
+
nsc_current = self._nb_sc_normal_points[channel]['sc']
|
|
373
|
+
nnorm_current = self._nb_sc_normal_points[channel]['normal']
|
|
374
|
+
|
|
375
|
+
do_update = False
|
|
376
|
+
if (nsc_current is not None
|
|
377
|
+
and nsc_current != nsc_df):
|
|
378
|
+
df.loc[df['state']=='sc', 'state'] = np.nan
|
|
379
|
+
if nsc_current != 0:
|
|
380
|
+
df.loc[df.index[-nsc_current:], 'state'] = 'sc'
|
|
381
|
+
do_update = True
|
|
382
|
+
|
|
383
|
+
if (nnorm_current is not None
|
|
384
|
+
and nnorm_current != nnorm_df):
|
|
385
|
+
df.loc[df['state']=='normal', 'state'] = np.nan
|
|
386
|
+
df.loc[:nnorm_current-1, 'state'] = 'normal'
|
|
387
|
+
do_update = True
|
|
388
|
+
|
|
389
|
+
if do_update:
|
|
390
|
+
self.set_ivsweep_data(channel,
|
|
391
|
+
df,
|
|
392
|
+
metadata=None,
|
|
393
|
+
tag=tag)
|
|
394
|
+
|
|
395
|
+
|
|
396
|
+
|
|
397
|
+
def analyze_sweep(self, channels=None,
|
|
398
|
+
lgc_invert_offset='auto',
|
|
399
|
+
lgc_plot=False, lgc_save_plot=False,
|
|
400
|
+
plot_save_path=None,
|
|
401
|
+
tag='default',
|
|
402
|
+
**kwargs):
|
|
403
|
+
"""
|
|
404
|
+
Analyze IV sweep (multi-channels) to calculate I0,R0,P0
|
|
405
|
+
in function of bias.
|
|
406
|
+
"""
|
|
407
|
+
|
|
408
|
+
# --------------------------------
|
|
409
|
+
# Check arguments
|
|
410
|
+
# ---------------------------------
|
|
411
|
+
|
|
412
|
+
# check data
|
|
413
|
+
available_channels = list()
|
|
414
|
+
ivdata_label = 'ivsweep_data_' + tag
|
|
415
|
+
for chan, chan_dict in self._filter_data.items():
|
|
416
|
+
if ivdata_label in chan_dict.keys():
|
|
417
|
+
available_channels.append(chan)
|
|
418
|
+
|
|
419
|
+
if not available_channels:
|
|
420
|
+
raise ValueError('ERROR: No channel available. '
|
|
421
|
+
'Set data first!')
|
|
422
|
+
if channels is None:
|
|
423
|
+
channels = available_channels
|
|
424
|
+
else:
|
|
425
|
+
if isinstance(channels, str):
|
|
426
|
+
channels = [channels]
|
|
427
|
+
for chan in channels:
|
|
428
|
+
if chan not in available_channels:
|
|
429
|
+
raise ValueError(f'ERROR: No data for channel {chan}'
|
|
430
|
+
' available. Set data first!')
|
|
431
|
+
# number of channels
|
|
432
|
+
nb_channels = len(channels)
|
|
433
|
+
|
|
434
|
+
# --------------------------------
|
|
435
|
+
# Check sweep data for all channels
|
|
436
|
+
# first
|
|
437
|
+
# ---------------------------------
|
|
438
|
+
|
|
439
|
+
# loop channel and check if sc / normal points available
|
|
440
|
+
# check Rshunt and Rp (needed if no SC points)
|
|
441
|
+
for ichan, chan in enumerate(channels):
|
|
442
|
+
|
|
443
|
+
df = self.get_ivsweep_data(chan, tag=tag)
|
|
444
|
+
df = df.sort_values('tes_bias',
|
|
445
|
+
ascending=False,
|
|
446
|
+
key=abs,
|
|
447
|
+
ignore_index=True)
|
|
448
|
+
|
|
449
|
+
|
|
450
|
+
# check processing has been done
|
|
451
|
+
if ('offset_iv' not in df.columns
|
|
452
|
+
and 'offset_didv' not in df.columns):
|
|
453
|
+
raise ValueError(f'ERROR: Unable to find an offset in'
|
|
454
|
+
' the channel {chan} dataframe! Check data!')
|
|
455
|
+
|
|
456
|
+
# Rp / Rshunt
|
|
457
|
+
rp = None
|
|
458
|
+
rp_err = None
|
|
459
|
+
rshunt = None
|
|
460
|
+
rshunt_err = None
|
|
461
|
+
|
|
462
|
+
# first check if parameters have been set manually
|
|
463
|
+
for param_chan, param_dict in self._readout_params.items():
|
|
464
|
+
if chan == param_chan:
|
|
465
|
+
if 'rp' in param_dict.keys():
|
|
466
|
+
rp = param_dict['rp']
|
|
467
|
+
if 'rp_err' in param_dict.keys():
|
|
468
|
+
rp_err = param_dict['rp_err']
|
|
469
|
+
if 'rshunt' in param_dict.keys():
|
|
470
|
+
rshunt = param_dict['rshunt']
|
|
471
|
+
if 'rshunt_err' in param_dict.keys():
|
|
472
|
+
rshunt_err = param_dict['rshunt_err']
|
|
473
|
+
|
|
474
|
+
|
|
475
|
+
# If None, use dataframe
|
|
476
|
+
if rp is None:
|
|
477
|
+
if ('rp' in df.columns
|
|
478
|
+
and not np.isnan(df['rp'].values[0])):
|
|
479
|
+
rp = df['rp'].values[0]
|
|
480
|
+
if rp_err is None:
|
|
481
|
+
if ('rp_err' in df.columns
|
|
482
|
+
and not np.isnan(df['rp_err'].values[0])):
|
|
483
|
+
rp_err = df['rp_err'].values[0]
|
|
484
|
+
elif rp is not None:
|
|
485
|
+
rp_err = rp*0.02
|
|
486
|
+
if rshunt is None:
|
|
487
|
+
if ('rshunt' in df.columns
|
|
488
|
+
and not np.isnan(df['rshunt'].values[0])):
|
|
489
|
+
rshunt = df['rshunt'].values[0]
|
|
490
|
+
if rshunt_err is None:
|
|
491
|
+
if ('rshunt_err' in df.columns
|
|
492
|
+
and not np.isnan(df['rshunt_err'].values[0])):
|
|
493
|
+
rshunt_err = df['rshunt_err'].values[0]
|
|
494
|
+
elif rshunt is not None:
|
|
495
|
+
rshunt_err = rshunt*0.02
|
|
496
|
+
|
|
497
|
+
# store parameters
|
|
498
|
+
if chan not in self._readout_params.keys():
|
|
499
|
+
self._readout_params[chan] = dict()
|
|
500
|
+
self._readout_params[chan]['rp'] = rp
|
|
501
|
+
self._readout_params[chan]['rp_err'] = rp_err
|
|
502
|
+
self._readout_params[chan]['rshunt'] = rshunt
|
|
503
|
+
self._readout_params[chan]['rshunt_err'] = rshunt_err
|
|
504
|
+
|
|
505
|
+
|
|
506
|
+
# normal/SC points
|
|
507
|
+
nnorm = self._nb_sc_normal_points[chan]['normal']
|
|
508
|
+
nsc = self._nb_sc_normal_points[chan]['sc']
|
|
509
|
+
|
|
510
|
+
if nnorm is None:
|
|
511
|
+
raise ValueError(
|
|
512
|
+
f'ERROR: Number of normal points unknown for {chan} '
|
|
513
|
+
'Use "set_nb_sc_normal_points()" function first!'
|
|
514
|
+
)
|
|
515
|
+
|
|
516
|
+
if nsc is None:
|
|
517
|
+
raise ValueError(
|
|
518
|
+
f'ERROR: Number of SC points unknown for {chan} '
|
|
519
|
+
'Use "set_nb_sc_normal_points()" function first! '
|
|
520
|
+
'Set to 0 if no SC points and set Rp using "set_rp()" '
|
|
521
|
+
'function'
|
|
522
|
+
)
|
|
523
|
+
|
|
524
|
+
if nsc == 0:
|
|
525
|
+
if rp is None or rp_err is None:
|
|
526
|
+
raise ValueError(
|
|
527
|
+
f'ERROR: Rp is required for channel {chan} when no SC points! '
|
|
528
|
+
'Use set_rp() function first!')
|
|
529
|
+
|
|
530
|
+
# check rshunt
|
|
531
|
+
if rshunt is None or rshunt_err is None:
|
|
532
|
+
raise ValueError(
|
|
533
|
+
f'ERROR: Rshunt is required for channel {chan}! '
|
|
534
|
+
'Use set_rshunt() function first!'
|
|
535
|
+
)
|
|
536
|
+
|
|
537
|
+
|
|
538
|
+
# --------------------------------
|
|
539
|
+
# Loop channel and do analysis
|
|
540
|
+
# ---------------------------------
|
|
541
|
+
# Loop channels
|
|
542
|
+
for ichan, chan in enumerate(channels):
|
|
543
|
+
|
|
544
|
+
# get dataframe
|
|
545
|
+
df = self.get_ivsweep_data(chan, tag=tag)
|
|
546
|
+
df = df.sort_values('tes_bias',
|
|
547
|
+
ascending=False,
|
|
548
|
+
key=abs,
|
|
549
|
+
ignore_index=True)
|
|
550
|
+
|
|
551
|
+
|
|
552
|
+
# normal range
|
|
553
|
+
nnorm = self._nb_sc_normal_points[chan]['normal']
|
|
554
|
+
range_normal = range(nnorm)
|
|
555
|
+
|
|
556
|
+
# sc range
|
|
557
|
+
nsc = self._nb_sc_normal_points[chan]['sc']
|
|
558
|
+
range_sc = []
|
|
559
|
+
fitsc = False
|
|
560
|
+
if nsc > 0:
|
|
561
|
+
fitsc = True
|
|
562
|
+
range_sc = range(len(df)-nsc,
|
|
563
|
+
len(df))
|
|
564
|
+
|
|
565
|
+
# parameters dictionary
|
|
566
|
+
param_dict = self._readout_params[chan]
|
|
567
|
+
|
|
568
|
+
# check noise/dIdV sweep
|
|
569
|
+
data_types = list()
|
|
570
|
+
labels = list()
|
|
571
|
+
if 'offset_iv' in df.columns:
|
|
572
|
+
data_types.append('iv')
|
|
573
|
+
labels.append(f'{chan} iv')
|
|
574
|
+
if 'offset_didv' in df.columns:
|
|
575
|
+
data_types.append('didv')
|
|
576
|
+
labels.append(f'{chan} dIdV')
|
|
577
|
+
|
|
578
|
+
# get values from dataframe
|
|
579
|
+
tes_bias = df['tes_bias'].values
|
|
580
|
+
offset = df['offset_' + data_types[0]].values
|
|
581
|
+
offset_err = df['offset_err_' + data_types[0]].values
|
|
582
|
+
if len(data_types)==2:
|
|
583
|
+
tes_bias = np.vstack([tes_bias, tes_bias])
|
|
584
|
+
tes_bias[1,:] = df['tes_bias_' + data_types[1]].values
|
|
585
|
+
offset = np.vstack([offset, offset])
|
|
586
|
+
offset[1,:] = df['offset_' + data_types[1]].values
|
|
587
|
+
offset_err = np.vstack([offset_err, offset_err])
|
|
588
|
+
offset_err[1,:] = df['offset_err_' + data_types[1]].values
|
|
589
|
+
|
|
590
|
+
|
|
591
|
+
# invert offset
|
|
592
|
+
offset = offset.copy()
|
|
593
|
+
if (lgc_invert_offset != 'auto'
|
|
594
|
+
and lgc_invert_offset):
|
|
595
|
+
offset = -1*offset
|
|
596
|
+
|
|
597
|
+
# rp guess
|
|
598
|
+
rp = param_dict['rp']
|
|
599
|
+
rp_err= param_dict['rp_err']
|
|
600
|
+
if rp is None:
|
|
601
|
+
rp = 0
|
|
602
|
+
rp_err = 0
|
|
603
|
+
|
|
604
|
+
# instantiate
|
|
605
|
+
ivobj = qp.IBIS(dites=offset,
|
|
606
|
+
dites_err=offset_err,
|
|
607
|
+
ibias=tes_bias,
|
|
608
|
+
ibias_err=np.zeros_like(tes_bias),
|
|
609
|
+
rsh=param_dict['rshunt'],
|
|
610
|
+
rsh_err=param_dict['rshunt_err'],
|
|
611
|
+
rp_guess=rp,
|
|
612
|
+
rp_err_guess=rp_err,
|
|
613
|
+
chan_names=labels,
|
|
614
|
+
fitsc=fitsc,
|
|
615
|
+
normalinds=list(range_normal),
|
|
616
|
+
scinds=list(range_sc),)
|
|
617
|
+
|
|
618
|
+
ivobj.analyze(**kwargs)
|
|
619
|
+
|
|
620
|
+
rn_check = ivobj.rnorm[0, 0]
|
|
621
|
+
if (lgc_invert_offset == 'auto' and rn_check<0):
|
|
622
|
+
lgc_invert_offset = True
|
|
623
|
+
ivobj.dites = -1*ivobj.dites
|
|
624
|
+
ivobj.analyze(**kwargs)
|
|
625
|
+
|
|
626
|
+
|
|
627
|
+
# store results in DataFrame/Series
|
|
628
|
+
df['lgc_invert_offset'] = lgc_invert_offset
|
|
629
|
+
df['vb'] = ivobj.vb[0,0,:]
|
|
630
|
+
df['vb_err'] = ivobj.vb_err[0,0,:]
|
|
631
|
+
|
|
632
|
+
for itype, data_type in enumerate(data_types):
|
|
633
|
+
|
|
634
|
+
# parameter dependend of bias point
|
|
635
|
+
df['p0_' + data_type] = ivobj.ptes[0, itype]
|
|
636
|
+
df['p0_err_' + data_type] = ivobj.ptes_err[0, itype]
|
|
637
|
+
df['r0_' + data_type] = ivobj.r0[0, itype]
|
|
638
|
+
df['r0_err_' + data_type] = ivobj.r0_err[0, itype]
|
|
639
|
+
df['i0_' + data_type] = ivobj.ites[0, itype]
|
|
640
|
+
df['i0_err_' + data_type] = ivobj.ites_err[0, itype]
|
|
641
|
+
|
|
642
|
+
# get results independent of sweep
|
|
643
|
+
rp = ivobj.rp[0, itype]
|
|
644
|
+
rp_err = ivobj.rp_err[0, itype]
|
|
645
|
+
rn = ivobj.rnorm[0, itype]
|
|
646
|
+
rn_err = ivobj.rnorm_err[0, itype]
|
|
647
|
+
i0_off = ivobj.ioff[0, itype]
|
|
648
|
+
i0_off_err = ivobj.ioff_err[0, itype]
|
|
649
|
+
ibias_off = ivobj.ibias_off[0, itype]
|
|
650
|
+
ibias_off_err = ivobj.ibias_off_err[0, itype]
|
|
651
|
+
ibias_true = ivobj.ibias_true[0, itype]
|
|
652
|
+
ibias_true_err = ivobj.ibias_true_err[0, itype]
|
|
653
|
+
|
|
654
|
+
|
|
655
|
+
# store in df and dictionary
|
|
656
|
+
df['rp_' + data_type] = rp
|
|
657
|
+
df['rp_err_' + data_type] = rp_err
|
|
658
|
+
df['rn_' + data_type] = rn
|
|
659
|
+
df['rn_err_' + data_type] = rn_err
|
|
660
|
+
df['i0_off_' + data_type] = i0_off
|
|
661
|
+
df['i0_off_err_' + data_type] = i0_off_err
|
|
662
|
+
df['ibias_off_' + data_type] = ibias_off
|
|
663
|
+
df['ibias_off_err_' + data_type] = ibias_off_err
|
|
664
|
+
df['ibias_true_' + data_type] = ibias_true
|
|
665
|
+
df['ibias_true_err_' + data_type] = ibias_true_err
|
|
666
|
+
df['percent_rn_' + data_type] = round(1000*df['r0_' + data_type]/rn)/10
|
|
667
|
+
|
|
668
|
+
|
|
669
|
+
# store also in in pandas Series
|
|
670
|
+
results = dict()
|
|
671
|
+
results['rp'] = rp
|
|
672
|
+
results['rp_err'] = rp_err
|
|
673
|
+
results['rn'] = rn
|
|
674
|
+
results['rn_err'] = rn_err
|
|
675
|
+
results['rshunt'] = param_dict['rshunt']
|
|
676
|
+
results['rshunt_err'] = param_dict['rshunt_err']
|
|
677
|
+
results['rsh'] = param_dict['rshunt']
|
|
678
|
+
results['rsh_err'] = param_dict['rshunt_err']
|
|
679
|
+
results['i0_off'] = i0_off
|
|
680
|
+
results['i0_off_err'] = i0_off_err
|
|
681
|
+
results['ibias_off'] = ibias_off
|
|
682
|
+
results['ibias_off_err'] = ibias_off_err
|
|
683
|
+
results['lgc_invert_offset'] = lgc_invert_offset
|
|
684
|
+
results['close_loop_norm'] = (
|
|
685
|
+
df['close_loop_norm_' + data_type].values[0]
|
|
686
|
+
)
|
|
687
|
+
results['output_variable_offset'] = (
|
|
688
|
+
df['output_variable_offset_' + data_type].values[0]
|
|
689
|
+
)
|
|
690
|
+
results['output_variable_gain'] = (
|
|
691
|
+
df['output_variable_gain_' + data_type].values[0]
|
|
692
|
+
)
|
|
693
|
+
|
|
694
|
+
acquisition_column = f'acquisition_name_{data_type}'
|
|
695
|
+
if acquisition_column not in df.columns:
|
|
696
|
+
acquisition_column = f'group_name_{data_type}'
|
|
697
|
+
results['acquisition_name_sweep'] = df[acquisition_column].values[0]
|
|
698
|
+
|
|
699
|
+
|
|
700
|
+
# add i0 variable offset (usead for true current calculations)
|
|
701
|
+
norm = results['close_loop_norm']
|
|
702
|
+
voltage_offset = results['output_variable_offset']
|
|
703
|
+
gain = results['output_variable_gain']
|
|
704
|
+
results['i0_variable_offset'] = (
|
|
705
|
+
voltage_offset * gain/norm
|
|
706
|
+
)
|
|
707
|
+
|
|
708
|
+
|
|
709
|
+
# avg temperature
|
|
710
|
+
temperature_list = ['mc','cp','still']
|
|
711
|
+
for temp in temperature_list:
|
|
712
|
+
temp_par = 'temperature_' + temp
|
|
713
|
+
if (temp_par in list(df.columns)
|
|
714
|
+
and not np.isnan(df[temp_par].iloc[0])):
|
|
715
|
+
temp_vals = df[temp_par].values
|
|
716
|
+
results[temp_par + '_sweep_avg'] = (
|
|
717
|
+
np.median(temp_vals)
|
|
718
|
+
)
|
|
719
|
+
|
|
720
|
+
# save rp/rn
|
|
721
|
+
self._readout_params[chan]['rp'] = rp
|
|
722
|
+
self._readout_params[chan]['rp_err'] = rp_err
|
|
723
|
+
self._readout_params[chan]['rn'] = rn
|
|
724
|
+
self._readout_params[chan]['rn_err'] = rn_err
|
|
725
|
+
|
|
726
|
+
# convert to pandas Series
|
|
727
|
+
result_series = pd.Series(results.copy())
|
|
728
|
+
self.set_ivsweep_results(chan,
|
|
729
|
+
result_series,
|
|
730
|
+
data_type,
|
|
731
|
+
metadata=None,
|
|
732
|
+
tag=tag)
|
|
733
|
+
|
|
734
|
+
|
|
735
|
+
# save data
|
|
736
|
+
self._ibis_objects[chan] = ivobj
|
|
737
|
+
self.set_ivsweep_data(chan, df, tag=tag)
|
|
738
|
+
|
|
739
|
+
if lgc_plot:
|
|
740
|
+
ivobj.plot_all_curves(lgcsave=False,
|
|
741
|
+
savepath=None,
|
|
742
|
+
savename=None)
|
|
743
|
+
|
|
744
|
+
|
|
745
|
+
|
|
746
|
+
# save hdf5
|
|
747
|
+
if self._save_hdf5:
|
|
748
|
+
|
|
749
|
+
# build full path
|
|
750
|
+
dir_name = './'
|
|
751
|
+
if self._save_path is not None:
|
|
752
|
+
dir_name = self._save_path + '/'
|
|
753
|
+
output_file = dir_name + self._file_name
|
|
754
|
+
|
|
755
|
+
# save
|
|
756
|
+
self.save_hdf5(output_file, overwrite=True)
|
|
757
|
+
|
|
758
|
+
|
|
759
|
+
|
|
760
|
+
|
|
761
|
+
def analyze_didv(self, channels=None,
|
|
762
|
+
enable_normal=True, enable_sc=True,
|
|
763
|
+
enable_transition=True,
|
|
764
|
+
normal_percent_rn_min=99.8, nb_points_normal_max=4,
|
|
765
|
+
sc_percent_rn_max=0.05, nb_points_sc_max=4,
|
|
766
|
+
transition_percent_rn_max=70,
|
|
767
|
+
transition_percent_rn_min=5,
|
|
768
|
+
inf_loop_gain_approx='auto',
|
|
769
|
+
lgc_plot=False, lgc_save_plot=False,
|
|
770
|
+
plot_save_path=None,
|
|
771
|
+
tag='default',
|
|
772
|
+
**kwargs):
|
|
773
|
+
|
|
774
|
+
"""
|
|
775
|
+
Fit dIdV for specified or all available channels
|
|
776
|
+
1 pole fit for normal and sc tes
|
|
777
|
+
2+3 poles fit for TES in transition
|
|
778
|
+
|
|
779
|
+
"""
|
|
780
|
+
|
|
781
|
+
|
|
782
|
+
# fit SC data
|
|
783
|
+
if enable_sc:
|
|
784
|
+
|
|
785
|
+
|
|
786
|
+
self.fit_didv_sc(
|
|
787
|
+
channels=channels,
|
|
788
|
+
percent_rn_max=sc_percent_rn_max,
|
|
789
|
+
nb_points_max=nb_points_sc_max,
|
|
790
|
+
lgc_plot=lgc_plot,
|
|
791
|
+
lgc_save_plot=lgc_save_plot,
|
|
792
|
+
plot_save_path=plot_save_path,
|
|
793
|
+
tag=tag,
|
|
794
|
+
**kwargs
|
|
795
|
+
)
|
|
796
|
+
|
|
797
|
+
|
|
798
|
+
# fit normal data
|
|
799
|
+
if enable_normal:
|
|
800
|
+
|
|
801
|
+
self.fit_didv_normal(
|
|
802
|
+
channels=channels,
|
|
803
|
+
percent_rn_min=normal_percent_rn_min,
|
|
804
|
+
nb_points_max=nb_points_normal_max,
|
|
805
|
+
lgc_plot=lgc_plot,
|
|
806
|
+
lgc_save_plot=lgc_save_plot,
|
|
807
|
+
plot_save_path=plot_save_path,
|
|
808
|
+
tag=tag,
|
|
809
|
+
**kwargs
|
|
810
|
+
)
|
|
811
|
+
|
|
812
|
+
# fit transition data
|
|
813
|
+
if enable_transition:
|
|
814
|
+
|
|
815
|
+
self.fit_didv_transition(
|
|
816
|
+
channels=channels,
|
|
817
|
+
percent_rn_min=transition_percent_rn_min,
|
|
818
|
+
percent_rn_max=transition_percent_rn_max,
|
|
819
|
+
lgc_plot=lgc_plot,
|
|
820
|
+
lgc_save_plot=lgc_save_plot,
|
|
821
|
+
plot_save_path=plot_save_path,
|
|
822
|
+
tag=tag,
|
|
823
|
+
**kwargs
|
|
824
|
+
)
|
|
825
|
+
|
|
826
|
+
|
|
827
|
+
|
|
828
|
+
def fit_didv_sc(self, channels=None,
|
|
829
|
+
percent_rn_max=0.05, nb_points_max=4,
|
|
830
|
+
lgc_plot=False, lgc_save_plot=False,
|
|
831
|
+
plot_save_path=None,
|
|
832
|
+
tag='default',
|
|
833
|
+
**kwargs):
|
|
834
|
+
"""
|
|
835
|
+
"""
|
|
836
|
+
|
|
837
|
+
self._fit_didv('sc', channels=channels,
|
|
838
|
+
percent_rn_max=percent_rn_max,
|
|
839
|
+
nb_points_max=nb_points_max,
|
|
840
|
+
lgc_plot=lgc_plot,
|
|
841
|
+
lgc_save_plot=lgc_save_plot,
|
|
842
|
+
plot_save_path=plot_save_path,
|
|
843
|
+
tag=tag,
|
|
844
|
+
**kwargs)
|
|
845
|
+
|
|
846
|
+
# save hdf5
|
|
847
|
+
if self._save_hdf5:
|
|
848
|
+
|
|
849
|
+
# build full path
|
|
850
|
+
dir_name = './'
|
|
851
|
+
if self._save_path is not None:
|
|
852
|
+
dir_name = self._save_path + '/'
|
|
853
|
+
output_file = dir_name + self._file_name
|
|
854
|
+
|
|
855
|
+
# save
|
|
856
|
+
self.save_hdf5(output_file, overwrite=True)
|
|
857
|
+
|
|
858
|
+
|
|
859
|
+
|
|
860
|
+
|
|
861
|
+
def fit_didv_normal(self, channels=None,
|
|
862
|
+
percent_rn_min=99.8, nb_points_max=4,
|
|
863
|
+
lgc_plot=False, lgc_save_plot=False,
|
|
864
|
+
plot_save_path=None,
|
|
865
|
+
tag='default',
|
|
866
|
+
**kwargs):
|
|
867
|
+
"""
|
|
868
|
+
"""
|
|
869
|
+
|
|
870
|
+
self._fit_didv('normal', channels=channels,
|
|
871
|
+
percent_rn_min=percent_rn_min,
|
|
872
|
+
nb_points_max=nb_points_max,
|
|
873
|
+
lgc_plot=lgc_plot,
|
|
874
|
+
lgc_save_plot=lgc_save_plot,
|
|
875
|
+
plot_save_path=plot_save_path,
|
|
876
|
+
tag=tag,
|
|
877
|
+
**kwargs)
|
|
878
|
+
|
|
879
|
+
|
|
880
|
+
# save hdf5
|
|
881
|
+
if self._save_hdf5:
|
|
882
|
+
|
|
883
|
+
# build full path
|
|
884
|
+
dir_name = './'
|
|
885
|
+
if self._save_path is not None:
|
|
886
|
+
dir_name = self._save_path + '/'
|
|
887
|
+
output_file = dir_name + self._file_name
|
|
888
|
+
|
|
889
|
+
# save
|
|
890
|
+
self.save_hdf5(output_file, overwrite=True)
|
|
891
|
+
|
|
892
|
+
|
|
893
|
+
|
|
894
|
+
def fit_didv_transition(self, channels=None,
|
|
895
|
+
percent_rn_min=5,
|
|
896
|
+
percent_rn_max=70,
|
|
897
|
+
lgc_plot=False, lgc_save_plot=False,
|
|
898
|
+
inf_loop_gain_approx='auto',
|
|
899
|
+
plot_save_path=None,
|
|
900
|
+
tag='default',
|
|
901
|
+
**kwargs):
|
|
902
|
+
"""
|
|
903
|
+
"""
|
|
904
|
+
|
|
905
|
+
self._fit_didv('transition', channels=channels,
|
|
906
|
+
percent_rn_min=percent_rn_min,
|
|
907
|
+
percent_rn_max=percent_rn_max,
|
|
908
|
+
inf_loop_gain_approx=inf_loop_gain_approx,
|
|
909
|
+
lgc_plot=lgc_plot,
|
|
910
|
+
lgc_save_plot=lgc_save_plot,
|
|
911
|
+
plot_save_path=plot_save_path,
|
|
912
|
+
tag=tag,
|
|
913
|
+
**kwargs)
|
|
914
|
+
|
|
915
|
+
# save hdf5
|
|
916
|
+
if self._save_hdf5:
|
|
917
|
+
|
|
918
|
+
# build full path
|
|
919
|
+
dir_name = './'
|
|
920
|
+
if self._save_path is not None:
|
|
921
|
+
dir_name = self._save_path + '/'
|
|
922
|
+
output_file = dir_name + self._file_name
|
|
923
|
+
|
|
924
|
+
# save
|
|
925
|
+
self.save_hdf5(output_file, overwrite=True)
|
|
926
|
+
|
|
927
|
+
|
|
928
|
+
def plot_didv_summary(self, channel, poles=3):
|
|
929
|
+
"""
|
|
930
|
+
"""
|
|
931
|
+
|
|
932
|
+
if channel not in self._didv_summary:
|
|
933
|
+
print(f'ERROR: No dIdV analysis done for {channel}')
|
|
934
|
+
return
|
|
935
|
+
|
|
936
|
+
print(f'Summary dIdV Analysis for {channel}')
|
|
937
|
+
print('=====================================')
|
|
938
|
+
|
|
939
|
+
# SC
|
|
940
|
+
if 'sc' in self._didv_summary[channel]:
|
|
941
|
+
data = self._didv_summary[channel]['sc']
|
|
942
|
+
|
|
943
|
+
print(f'\nTES Superconducting Measurements:\n')
|
|
944
|
+
print('Rp from dIdV fit = {:.2f} +/- {:.3f} mOhms'.format(
|
|
945
|
+
np.array(data['rp'])*1e3, np.array(data['rp_err'])*1e3))
|
|
946
|
+
print('Rp rom IV Sweep = {:.2f} +/- {:.3f} mOhms'.format(
|
|
947
|
+
np.array(data['rp_iv'])*1e3, np.array(data['rp_iv_err'])*1e3))
|
|
948
|
+
|
|
949
|
+
# normal
|
|
950
|
+
if 'normal' in self._didv_summary[channel]:
|
|
951
|
+
data = self._didv_summary[channel]['normal']
|
|
952
|
+
|
|
953
|
+
print(f'\nTES Normal Measurements:\n')
|
|
954
|
+
print('Rn from dIdV fit = {:.2f} +/- {:.3f} mOhms'.format(
|
|
955
|
+
np.array(data['rn'])*1e3, np.array(data['rn_err'])*1e3))
|
|
956
|
+
print('Rn rom IV Sweep = {:.2f} +/- {:.3f} mOhms'.format(
|
|
957
|
+
np.array(data['rn_iv'])*1e3, np.array(data['rn_iv_err'])*1e3))
|
|
958
|
+
|
|
959
|
+
|
|
960
|
+
|
|
961
|
+
# transition
|
|
962
|
+
if 'transition' in self._didv_summary[channel]:
|
|
963
|
+
|
|
964
|
+
data = self._didv_summary[channel]['transition'][poles]
|
|
965
|
+
print(f'\nMeasurements TES in Transition {poles}-poles Fit:')
|
|
966
|
+
|
|
967
|
+
|
|
968
|
+
plt.plot(data['Rn %'], data['chi2'], 'bo')
|
|
969
|
+
plt.title(f'{channel} dIdV Fit chi2', fontweight='bold')
|
|
970
|
+
plt.xlabel('Rn %', fontweight='bold')
|
|
971
|
+
plt.ylabel('Chi2/Ndof', fontweight='bold')
|
|
972
|
+
plt.grid(True)
|
|
973
|
+
plt.show()
|
|
974
|
+
|
|
975
|
+
# plot small signal
|
|
976
|
+
|
|
977
|
+
print('\nSmall Signal Parameters')
|
|
978
|
+
|
|
979
|
+
plt.errorbar(data['Rn %'], data['l'], yerr=data['l_err'],
|
|
980
|
+
fmt='o', ecolor='red', capsize=5,
|
|
981
|
+
linestyle='-', color='blue')
|
|
982
|
+
plt.title(f'{channel} loop gain (l)', fontweight='bold')
|
|
983
|
+
plt.xlabel('Rn %', fontweight='bold')
|
|
984
|
+
plt.ylabel('loop gain (l)', fontweight='bold')
|
|
985
|
+
plt.grid(True)
|
|
986
|
+
plt.show()
|
|
987
|
+
|
|
988
|
+
plt.errorbar(data['Rn %'], data['beta'], yerr=data['beta_err'],
|
|
989
|
+
fmt='o', ecolor='red', capsize=5,
|
|
990
|
+
linestyle='-', color='blue')
|
|
991
|
+
plt.title(f'{channel} beta', fontweight='bold')
|
|
992
|
+
plt.xlabel('Rn %', fontweight='bold')
|
|
993
|
+
plt.ylabel('beta', fontweight='bold')
|
|
994
|
+
plt.grid(True)
|
|
995
|
+
plt.show()
|
|
996
|
+
|
|
997
|
+
plt.errorbar(np.array(data['Rn %']), np.array(data['L'])*1e9,
|
|
998
|
+
yerr=np.array(data['L_err'])*1e9,
|
|
999
|
+
fmt='o', ecolor='red', capsize=5,
|
|
1000
|
+
linestyle='-', color='blue')
|
|
1001
|
+
plt.title(f'{channel} Effective inductance L', fontweight='bold')
|
|
1002
|
+
plt.xlabel('Rn %', fontweight='bold')
|
|
1003
|
+
plt.ylabel('L [nH]', fontweight='bold')
|
|
1004
|
+
plt.grid(True)
|
|
1005
|
+
plt.show()
|
|
1006
|
+
|
|
1007
|
+
plt.errorbar(np.array(data['Rn %']), np.array(data['tau0'])*1e3,
|
|
1008
|
+
yerr=np.array(data['tau0_err'])*1e3,
|
|
1009
|
+
fmt='o', ecolor='red', capsize=5,
|
|
1010
|
+
linestyle='-', color='blue')
|
|
1011
|
+
plt.title(f'{channel} Tau0', fontweight='bold')
|
|
1012
|
+
plt.xlabel('Rn %', fontweight='bold')
|
|
1013
|
+
plt.ylabel('Tau0 [ms]', fontweight='bold')
|
|
1014
|
+
plt.grid(True)
|
|
1015
|
+
plt.show()
|
|
1016
|
+
|
|
1017
|
+
plt.errorbar(data['Rn %'], data['gratio'], yerr=data['gratio_err'],
|
|
1018
|
+
fmt='o', ecolor='red', capsize=5,
|
|
1019
|
+
linestyle='-', color='blue')
|
|
1020
|
+
plt.title(f'{channel} gratio', fontweight='bold')
|
|
1021
|
+
plt.xlabel('Rn %', fontweight='bold')
|
|
1022
|
+
plt.ylabel('gratio', fontweight='bold')
|
|
1023
|
+
plt.grid(True)
|
|
1024
|
+
plt.show()
|
|
1025
|
+
|
|
1026
|
+
|
|
1027
|
+
print('\nFall Times')
|
|
1028
|
+
pars = ['tau+', 'tau-','tau3']
|
|
1029
|
+
for par in pars:
|
|
1030
|
+
plt.plot(np.array(data['Rn %']),
|
|
1031
|
+
np.array(data[par])*1e3, 'bo')
|
|
1032
|
+
plt.title(f'{channel} {par}', fontweight='bold')
|
|
1033
|
+
plt.xlabel('Rn %', fontweight='bold')
|
|
1034
|
+
plt.ylabel(f'{par} [ms]', fontweight='bold')
|
|
1035
|
+
plt.grid(True)
|
|
1036
|
+
plt.show()
|
|
1037
|
+
|
|
1038
|
+
|
|
1039
|
+
def calc_energy_resolution(self, channels=None,
|
|
1040
|
+
template=None, template_name=None,
|
|
1041
|
+
lgc_power_template=False,
|
|
1042
|
+
collection_eff=1, poles=3,
|
|
1043
|
+
lgc_plot=False, tag='default'):
|
|
1044
|
+
"""
|
|
1045
|
+
Calculate resolution
|
|
1046
|
+
"""
|
|
1047
|
+
|
|
1048
|
+
# channels
|
|
1049
|
+
available_channels = list()
|
|
1050
|
+
ivdata_label = 'ivsweep_data_' + tag
|
|
1051
|
+
for chan, chan_dict in self._filter_data.items():
|
|
1052
|
+
if ivdata_label in chan_dict.keys():
|
|
1053
|
+
available_channels.append(chan)
|
|
1054
|
+
|
|
1055
|
+
if not available_channels:
|
|
1056
|
+
raise ValueError('ERROR: No channel available. '
|
|
1057
|
+
'Set data first!')
|
|
1058
|
+
if channels is None:
|
|
1059
|
+
channels = available_channels
|
|
1060
|
+
else:
|
|
1061
|
+
if isinstance(channels, str):
|
|
1062
|
+
channels = [channels]
|
|
1063
|
+
for chan in channels:
|
|
1064
|
+
if chan not in available_channels:
|
|
1065
|
+
raise ValueError(f'ERROR: No data for channel {chan}'
|
|
1066
|
+
' available. Set data first!')
|
|
1067
|
+
# number of channels
|
|
1068
|
+
nb_channels = len(channels)
|
|
1069
|
+
|
|
1070
|
+
|
|
1071
|
+
# Do resolution analysis
|
|
1072
|
+
for chan in channels:
|
|
1073
|
+
|
|
1074
|
+
# get dataframe
|
|
1075
|
+
df = self.get_ivsweep_data(chan, tag=tag)
|
|
1076
|
+
|
|
1077
|
+
# check if psd available
|
|
1078
|
+
if 'psd' not in df.columns:
|
|
1079
|
+
raise ValueError('ERROR: No PSD available! Is it a dIdV only sweep?')
|
|
1080
|
+
|
|
1081
|
+
# get didvanalysis
|
|
1082
|
+
if (chan not in self._didv_objects.keys()
|
|
1083
|
+
or 'transition' not in self._didv_objects[chan]):
|
|
1084
|
+
raise ValueError(
|
|
1085
|
+
f'ERROR: No DIDV analysis done for channel {chan}.'
|
|
1086
|
+
' Use first "analyze_didv" function')
|
|
1087
|
+
|
|
1088
|
+
didv_objs = self._didv_objects[chan]['transition']
|
|
1089
|
+
|
|
1090
|
+
# Initialize data
|
|
1091
|
+
resolution_data = dict()
|
|
1092
|
+
resolution_data['tes_bias_ua'] = list()
|
|
1093
|
+
resolution_data['percent_rn'] = list()
|
|
1094
|
+
resolution_data['resolution_dirac'] = list()
|
|
1095
|
+
|
|
1096
|
+
if template is not None:
|
|
1097
|
+
resolution_data['resolution_template'] = list()
|
|
1098
|
+
|
|
1099
|
+
|
|
1100
|
+
for bias, obj in didv_objs.items():
|
|
1101
|
+
|
|
1102
|
+
# get psd amps2/Hz
|
|
1103
|
+
df_bias = df[df.tes_bias_ua==bias]
|
|
1104
|
+
df_index = df_bias.index[0]
|
|
1105
|
+
|
|
1106
|
+
if df_bias.empty:
|
|
1107
|
+
raise ValueError(f'ERROR: Unable to get psd for {chan},'
|
|
1108
|
+
f' tes bias = {bias}')
|
|
1109
|
+
|
|
1110
|
+
psd = df_bias['psd'].iloc[0]
|
|
1111
|
+
fs = df_bias['fs_iv'].iloc[0]
|
|
1112
|
+
percent_rn = df_bias['percent_rn_iv'].iloc[0]
|
|
1113
|
+
|
|
1114
|
+
# calculate resolution for direct delta
|
|
1115
|
+
resolution_dirac = obj.calc_energy_resolution(
|
|
1116
|
+
chan, psd, fs=fs, template=None,
|
|
1117
|
+
collection_eff=collection_eff,
|
|
1118
|
+
poles=poles
|
|
1119
|
+
)
|
|
1120
|
+
|
|
1121
|
+
resolution_template = None
|
|
1122
|
+
if template is not None:
|
|
1123
|
+
resolution_template = obj.calc_energy_resolution(
|
|
1124
|
+
chan, psd, fs=fs, template=template,
|
|
1125
|
+
collection_eff=collection_eff,
|
|
1126
|
+
lgc_power_template=lgc_power_template,
|
|
1127
|
+
poles=poles)
|
|
1128
|
+
|
|
1129
|
+
# store
|
|
1130
|
+
resolution_data['tes_bias_ua'].append(bias)
|
|
1131
|
+
resolution_data['percent_rn'].append(percent_rn)
|
|
1132
|
+
resolution_data['resolution_dirac'].append(resolution_dirac)
|
|
1133
|
+
df.loc[df_index, 'resolution_dirac'] = resolution_dirac
|
|
1134
|
+
df.loc[df_index, 'resolution_collection_efficiency'] = collection_eff
|
|
1135
|
+
if template is not None:
|
|
1136
|
+
resolution_data['resolution_template'].append(resolution_template)
|
|
1137
|
+
df.loc[df_index, 'resolution_template'] = resolution_template
|
|
1138
|
+
|
|
1139
|
+
# save
|
|
1140
|
+
self._resolution_data[chan] = resolution_data
|
|
1141
|
+
self.set_ivsweep_data(chan, df, tag=tag)
|
|
1142
|
+
|
|
1143
|
+
# display
|
|
1144
|
+
if lgc_plot:
|
|
1145
|
+
|
|
1146
|
+
# Create the plot
|
|
1147
|
+
fig, ax1 = plt.subplots(figsize=(8,6))
|
|
1148
|
+
|
|
1149
|
+
# Plot the data
|
|
1150
|
+
ax1.plot(np.array(resolution_data['percent_rn']),
|
|
1151
|
+
np.array(resolution_data['resolution_dirac'])*1e3, 'b-o',
|
|
1152
|
+
label='Dirac Delta Deposit')
|
|
1153
|
+
|
|
1154
|
+
if template is not None:
|
|
1155
|
+
|
|
1156
|
+
label_ext = 'External Template'
|
|
1157
|
+
if template_name is not None:
|
|
1158
|
+
label_ext = template_name
|
|
1159
|
+
|
|
1160
|
+
ax1.plot(np.array(resolution_data['percent_rn']),
|
|
1161
|
+
np.array(resolution_data['resolution_template'])*1e3, 'r-o',
|
|
1162
|
+
label=label_ext)
|
|
1163
|
+
|
|
1164
|
+
# axis label
|
|
1165
|
+
ax1.set_xlabel('% Rn', fontweight='bold')
|
|
1166
|
+
ax1.set_ylabel('Energy Resolution [meV]', fontweight='bold')
|
|
1167
|
+
ax1.legend()
|
|
1168
|
+
ax1.set_title(fr'{chan} Energy resolution ($\epsilon_p$ = {collection_eff})',
|
|
1169
|
+
fontweight='bold')
|
|
1170
|
+
|
|
1171
|
+
# add tes bias
|
|
1172
|
+
#ax2 = ax1.twiny()
|
|
1173
|
+
#ax2.xaxis.set_ticks_position('bottom')
|
|
1174
|
+
#ax2.xaxis.set_label_position('bottom')
|
|
1175
|
+
#ax2.spines['bottom'].set_position(('outward', 36))
|
|
1176
|
+
#ax2.set_xticks(resolution_data['percent_rn'])
|
|
1177
|
+
#ax2.set_xticklabels(resolution_data['tes_bias_ua'])
|
|
1178
|
+
#ax2.set_xlabel('TES bias [uA]')
|
|
1179
|
+
|
|
1180
|
+
# Show the plot
|
|
1181
|
+
plt.show()
|
|
1182
|
+
|
|
1183
|
+
|
|
1184
|
+
|
|
1185
|
+
# save hdf5
|
|
1186
|
+
if self._save_hdf5:
|
|
1187
|
+
|
|
1188
|
+
# build full path
|
|
1189
|
+
dir_name = './'
|
|
1190
|
+
if self._save_path is not None:
|
|
1191
|
+
dir_name = self._save_path + '/'
|
|
1192
|
+
output_file = dir_name + self._file_name
|
|
1193
|
+
|
|
1194
|
+
# save
|
|
1195
|
+
self.save_hdf5(output_file, overwrite=True)
|
|
1196
|
+
|
|
1197
|
+
|
|
1198
|
+
|
|
1199
|
+
def set_tbath(self, tbath):
|
|
1200
|
+
"""
|
|
1201
|
+
Set bath temperature
|
|
1202
|
+
"""
|
|
1203
|
+
|
|
1204
|
+
self._tbath = tbath
|
|
1205
|
+
|
|
1206
|
+
def set_tload_guess(self, tload):
|
|
1207
|
+
"""
|
|
1208
|
+
Set bath temperature
|
|
1209
|
+
"""
|
|
1210
|
+
|
|
1211
|
+
self._tload_guess = tload
|
|
1212
|
+
|
|
1213
|
+
|
|
1214
|
+
def set_tc(self, channel, tc):
|
|
1215
|
+
"""
|
|
1216
|
+
Set Tc
|
|
1217
|
+
"""
|
|
1218
|
+
|
|
1219
|
+
self._tc[channel] = tc
|
|
1220
|
+
|
|
1221
|
+
|
|
1222
|
+
def set_gta(self, channel, gta):
|
|
1223
|
+
"""
|
|
1224
|
+
Set Gta for specified channel
|
|
1225
|
+
"""
|
|
1226
|
+
|
|
1227
|
+
self._gta[channel] = gta
|
|
1228
|
+
|
|
1229
|
+
|
|
1230
|
+
def analyze_noise(self, channels=None, poles=2,
|
|
1231
|
+
lgc_plot=False,
|
|
1232
|
+
fit_range=(100, 1e5),
|
|
1233
|
+
xlims=None, ylims_current=None, ylims_power=None,
|
|
1234
|
+
lgc_save_fig=False, save_path=None,
|
|
1235
|
+
tag='default'):
|
|
1236
|
+
"""
|
|
1237
|
+
Function to analyze and plot noise PSD with all the theoretical noise
|
|
1238
|
+
components (calculated from the didv fits).
|
|
1239
|
+
|
|
1240
|
+
lgc_plot : bool, optional
|
|
1241
|
+
If True, a plot of the fit is shown.
|
|
1242
|
+
xlims : NoneType, tuple, optional
|
|
1243
|
+
Limits to be passed to ax.set_xlim().
|
|
1244
|
+
ylims_current : NoneType, tuple, optional
|
|
1245
|
+
Limits to be passed to ax.set_ylim() for the current noise
|
|
1246
|
+
plots.
|
|
1247
|
+
ylims_power : NoneType, tuple, optional
|
|
1248
|
+
Limits to be passed to ax.set_ylim() for the power noise
|
|
1249
|
+
plots.
|
|
1250
|
+
lgc_save : bool, optional
|
|
1251
|
+
If True, the figure is saved.
|
|
1252
|
+
save_path : str
|
|
1253
|
+
Directory to save data
|
|
1254
|
+
|
|
1255
|
+
"""
|
|
1256
|
+
|
|
1257
|
+
# channels
|
|
1258
|
+
available_channels = list()
|
|
1259
|
+
ivdata_label = 'ivsweep_data_' + tag
|
|
1260
|
+
for chan, chan_dict in self._filter_data.items():
|
|
1261
|
+
if ivdata_label in chan_dict.keys():
|
|
1262
|
+
available_channels.append(chan)
|
|
1263
|
+
|
|
1264
|
+
if not available_channels:
|
|
1265
|
+
raise ValueError('ERROR: No channel available. '
|
|
1266
|
+
'Set data first!')
|
|
1267
|
+
if channels is None:
|
|
1268
|
+
channels = available_channels
|
|
1269
|
+
else:
|
|
1270
|
+
if isinstance(channels, str):
|
|
1271
|
+
channels = [channels]
|
|
1272
|
+
for chan in channels:
|
|
1273
|
+
if chan not in available_channels:
|
|
1274
|
+
raise ValueError(f'ERROR: No data for channel {chan}'
|
|
1275
|
+
' available. Set data first!')
|
|
1276
|
+
|
|
1277
|
+
# check avalaible data
|
|
1278
|
+
for chan in channels:
|
|
1279
|
+
|
|
1280
|
+
# check tc
|
|
1281
|
+
if chan not in self._tc.keys():
|
|
1282
|
+
raise ValueError(f'ERROR: Tc not available for channel {chan}!\n'
|
|
1283
|
+
f'Set first Tc with function set_tc("{chan}",tc)')
|
|
1284
|
+
|
|
1285
|
+
|
|
1286
|
+
# check dIdV analysis done
|
|
1287
|
+
df = self.get_ivsweep_data(chan, tag=tag)
|
|
1288
|
+
|
|
1289
|
+
# check if psd available
|
|
1290
|
+
if 'psd' not in df.columns:
|
|
1291
|
+
raise ValueError(f'ERROR: No PSD available for channel {chan}! '
|
|
1292
|
+
f'Is it a dIdV only sweep?')
|
|
1293
|
+
|
|
1294
|
+
# check didv analysis
|
|
1295
|
+
if (chan not in self._didv_objects.keys()
|
|
1296
|
+
or ('transition' not in self._didv_objects[chan]
|
|
1297
|
+
or 'sc' not in self._didv_objects[chan]
|
|
1298
|
+
or 'normal' not in self._didv_objects[chan])):
|
|
1299
|
+
raise ValueError(
|
|
1300
|
+
f'ERROR: No DIDV analysis done for channel {chan}.'
|
|
1301
|
+
f' Use first "analyze_didv" function'
|
|
1302
|
+
)
|
|
1303
|
+
|
|
1304
|
+
|
|
1305
|
+
# check if temperature available
|
|
1306
|
+
if self._tbath is None:
|
|
1307
|
+
if ('temperature_mc' not in df.columns
|
|
1308
|
+
or np.isnan(df['temperature_mc'].iloc[0])):
|
|
1309
|
+
raise ValueError(
|
|
1310
|
+
f'ERROR: Bath temperature not available in '
|
|
1311
|
+
f'raw data. Set Tbath first using function '
|
|
1312
|
+
f'"set_tbath(tbath)"!')
|
|
1313
|
+
|
|
1314
|
+
if self._tload_guess is None:
|
|
1315
|
+
if ('temperature_cp' not in df.columns
|
|
1316
|
+
or np.isnan(df['temperature_cp'].iloc[0])):
|
|
1317
|
+
raise ValueError(
|
|
1318
|
+
f'ERROR: CP temperature not available in raw data! '
|
|
1319
|
+
f'Set Tload first using function '
|
|
1320
|
+
f'"set_tload_guess(tload)"!')
|
|
1321
|
+
|
|
1322
|
+
|
|
1323
|
+
# Loop channels and do noise analysis
|
|
1324
|
+
for chan in channels:
|
|
1325
|
+
self._noisedict[chan] = {'transition': {}, 'sc': {}, 'normal': {}}
|
|
1326
|
+
# get dataframe
|
|
1327
|
+
df = self.get_ivsweep_data(chan, tag=tag)
|
|
1328
|
+
|
|
1329
|
+
# get dIdV QETpy objects
|
|
1330
|
+
didv_normal_objs = self._didv_objects[chan]['normal']
|
|
1331
|
+
didv_sc_objs = self._didv_objects[chan]['sc']
|
|
1332
|
+
didv_transition_objs = self._didv_objects[chan]['transition']
|
|
1333
|
+
|
|
1334
|
+
|
|
1335
|
+
# get IV sweep result
|
|
1336
|
+
ivsweep_result = self.get_ivsweep_results(chan)
|
|
1337
|
+
|
|
1338
|
+
|
|
1339
|
+
# Instantiate Noise model
|
|
1340
|
+
noise_sim = NoiseModel(verbose=False)
|
|
1341
|
+
|
|
1342
|
+
# set Tc
|
|
1343
|
+
noise_sim.set_tc(chan, self._tc[chan])
|
|
1344
|
+
|
|
1345
|
+
# ----------------------------
|
|
1346
|
+
# Normal noise
|
|
1347
|
+
# ----------------------------
|
|
1348
|
+
|
|
1349
|
+
if self._verbose:
|
|
1350
|
+
print(f'\nINFO: Calculating {chan} SQUID noise from normal noise')
|
|
1351
|
+
|
|
1352
|
+
# initialize fit par list
|
|
1353
|
+
lgc_plot_once = lgc_plot
|
|
1354
|
+
|
|
1355
|
+
# initialize SQUID noise list
|
|
1356
|
+
squid_noise_list = []
|
|
1357
|
+
|
|
1358
|
+
# loop normal noise
|
|
1359
|
+
for bias, obj in didv_normal_objs.items():
|
|
1360
|
+
self._noisedict[chan]['normal'][bias] = {}
|
|
1361
|
+
|
|
1362
|
+
# get normal psd
|
|
1363
|
+
df_bias = df[df.tes_bias_ua==bias]
|
|
1364
|
+
df_index = df_bias.index[0]
|
|
1365
|
+
|
|
1366
|
+
if df_bias.empty:
|
|
1367
|
+
raise ValueError(f'ERROR: Unable to get psd for {chan},'
|
|
1368
|
+
f' tes bias = {bias}')
|
|
1369
|
+
|
|
1370
|
+
# didv fit result
|
|
1371
|
+
fitresult = obj.get_fit_results(chan, poles=1)
|
|
1372
|
+
|
|
1373
|
+
# get psd
|
|
1374
|
+
psd = df_bias['psd'].iloc[0]
|
|
1375
|
+
psd_freqs = df_bias['psd_freq'].iloc[0]
|
|
1376
|
+
tes_bias = df_bias['tes_bias'].iloc[0]
|
|
1377
|
+
|
|
1378
|
+
# set Tload and Tbath
|
|
1379
|
+
tload_guess = self._tload_guess
|
|
1380
|
+
tbath = self._tbath
|
|
1381
|
+
if tload_guess is None:
|
|
1382
|
+
tload_guess = df_bias['temperature_cp'].iloc[0]
|
|
1383
|
+
if tbath is None:
|
|
1384
|
+
tbath = df_bias['temperature_mc'].iloc[0]
|
|
1385
|
+
|
|
1386
|
+
# set data
|
|
1387
|
+
noise_sim.set_tload_guess(tload_guess)
|
|
1388
|
+
noise_sim.set_tbath(tbath)
|
|
1389
|
+
noise_sim.set_psd(chan, psd, psd_freqs, 'normal' )
|
|
1390
|
+
|
|
1391
|
+
# set IV sweep results
|
|
1392
|
+
noise_sim.set_iv_didv_results_from_dict(
|
|
1393
|
+
chan,
|
|
1394
|
+
didv_results=fitresult,
|
|
1395
|
+
poles=1,
|
|
1396
|
+
ivsweep_results=ivsweep_result
|
|
1397
|
+
)
|
|
1398
|
+
|
|
1399
|
+
# set inductance
|
|
1400
|
+
L = float(fitresult['smallsignalparams']['L'])
|
|
1401
|
+
noise_sim.set_inductance(chan, L, 'normal')
|
|
1402
|
+
|
|
1403
|
+
# fit
|
|
1404
|
+
noise_sim.calc_squid_noise(channels=chan,
|
|
1405
|
+
lgc_plot=lgc_plot_once)
|
|
1406
|
+
lgc_plot_once = False
|
|
1407
|
+
|
|
1408
|
+
squid_noise = (
|
|
1409
|
+
noise_sim._noise_data[chan]['sim']['normal']['s_isquid']
|
|
1410
|
+
)
|
|
1411
|
+
squid_noise_freqs = (
|
|
1412
|
+
noise_sim._noise_data[chan]['sim']['normal']['freqs']
|
|
1413
|
+
)
|
|
1414
|
+
|
|
1415
|
+
squid_noise_list.append(squid_noise)
|
|
1416
|
+
self._noisedict[chan]['normal'][bias] = noise_sim.get_noise_data(chan, 'normal')
|
|
1417
|
+
|
|
1418
|
+
|
|
1419
|
+
# take average
|
|
1420
|
+
sum_array = np.zeros_like(squid_noise_list[0])
|
|
1421
|
+
|
|
1422
|
+
# Sum up all arrays
|
|
1423
|
+
for arr in squid_noise_list:
|
|
1424
|
+
sum_array += arr
|
|
1425
|
+
|
|
1426
|
+
squid_noise = sum_array / len(squid_noise_list)
|
|
1427
|
+
|
|
1428
|
+
# replace noise_sim fit result
|
|
1429
|
+
noise_sim.set_squid_noise(chan, squid_noise, squid_noise_freqs)
|
|
1430
|
+
|
|
1431
|
+
# add to IV Sweep result
|
|
1432
|
+
ivsweep_result['noise_model_squid_noise'] = squid_noise
|
|
1433
|
+
ivsweep_result['noise_model_squid_noise_freqs'] = squid_noise_freqs
|
|
1434
|
+
|
|
1435
|
+
# ----------------------------
|
|
1436
|
+
# SC Fit
|
|
1437
|
+
# ----------------------------
|
|
1438
|
+
|
|
1439
|
+
if self._verbose:
|
|
1440
|
+
print(f'INFO: Performing {chan} SC Noise Fit')
|
|
1441
|
+
|
|
1442
|
+
tload_list = []
|
|
1443
|
+
|
|
1444
|
+
lgc_plot_once = lgc_plot
|
|
1445
|
+
for bias, obj in didv_sc_objs.items():
|
|
1446
|
+
self._noisedict[chan]['sc'][bias] = {}
|
|
1447
|
+
|
|
1448
|
+
# get normal psd
|
|
1449
|
+
df_bias = df[df.tes_bias_ua==bias]
|
|
1450
|
+
df_index = df_bias.index[0]
|
|
1451
|
+
|
|
1452
|
+
if df_bias.empty:
|
|
1453
|
+
raise ValueError(f'ERROR: Unable to get psd for {chan},'
|
|
1454
|
+
f' tes bias = {bias}')
|
|
1455
|
+
|
|
1456
|
+
# didv fit result
|
|
1457
|
+
fitresult = obj.get_fit_results(chan, poles=1)
|
|
1458
|
+
|
|
1459
|
+
# get psd
|
|
1460
|
+
psd = df_bias['psd'].iloc[0]
|
|
1461
|
+
psd_freqs = df_bias['psd_freq'].iloc[0]
|
|
1462
|
+
tes_bias = df_bias['tes_bias'].iloc[0]
|
|
1463
|
+
|
|
1464
|
+
# set Tload and Tbath
|
|
1465
|
+
tload_guess = self._tload_guess
|
|
1466
|
+
tbath = self._tbath
|
|
1467
|
+
if tload_guess is None:
|
|
1468
|
+
tload_guess = df_bias['temperature_cp'].iloc[0]
|
|
1469
|
+
if tbath is None:
|
|
1470
|
+
tbath = df_bias['temperature_mc'].iloc[0]
|
|
1471
|
+
|
|
1472
|
+
noise_sim.set_tload_guess(tload_guess)
|
|
1473
|
+
noise_sim.set_tbath(tbath)
|
|
1474
|
+
|
|
1475
|
+
# set data
|
|
1476
|
+
noise_sim.set_psd(chan, psd, psd_freqs, 'sc' )
|
|
1477
|
+
|
|
1478
|
+
# set IV sweep results
|
|
1479
|
+
noise_sim.set_iv_didv_results_from_dict(
|
|
1480
|
+
chan,
|
|
1481
|
+
didv_results=fitresult,
|
|
1482
|
+
poles=1,
|
|
1483
|
+
ivsweep_results=ivsweep_result
|
|
1484
|
+
)
|
|
1485
|
+
|
|
1486
|
+
# set inductance
|
|
1487
|
+
L = float(fitresult['smallsignalparams']['L'])
|
|
1488
|
+
noise_sim.set_inductance(chan, L, 'sc')
|
|
1489
|
+
|
|
1490
|
+
# fit
|
|
1491
|
+
noise_sim.fit_sc_noise(channels=chan,
|
|
1492
|
+
fit_range=fit_range,
|
|
1493
|
+
lgc_plot=lgc_plot_once,
|
|
1494
|
+
xlims=xlims, ylims=ylims_current)
|
|
1495
|
+
lgc_plot_once = False
|
|
1496
|
+
|
|
1497
|
+
# store result
|
|
1498
|
+
tload_list.append(
|
|
1499
|
+
noise_sim._noise_data[chan]['sc']['fit']['tload']
|
|
1500
|
+
)
|
|
1501
|
+
self._noisedict[chan]['sc'][bias] = noise_sim.get_noise_data(chan, 'sc')
|
|
1502
|
+
|
|
1503
|
+
|
|
1504
|
+
# take median and replace fit value
|
|
1505
|
+
tload = np.median(tload_list)
|
|
1506
|
+
noise_sim._noise_data[chan]['sc']['fit']['tload'] = tload
|
|
1507
|
+
|
|
1508
|
+
if self._verbose:
|
|
1509
|
+
tload_str = format(tload*1e3, '.2f')
|
|
1510
|
+
print(f'INFO: Channel {chan} Tload from SC Fit: {tload_str} mK')
|
|
1511
|
+
|
|
1512
|
+
|
|
1513
|
+
# save in sweep result
|
|
1514
|
+
ivsweep_result['noise_model_fit_tload'] = tload
|
|
1515
|
+
|
|
1516
|
+
# ----------------------------
|
|
1517
|
+
# Transition data model
|
|
1518
|
+
# ----------------------------
|
|
1519
|
+
|
|
1520
|
+
|
|
1521
|
+
# initialize tbath list
|
|
1522
|
+
tbath_list = []
|
|
1523
|
+
gta_list = []
|
|
1524
|
+
|
|
1525
|
+
for bias, obj in didv_transition_objs.items():
|
|
1526
|
+
self._noisedict[chan]['transition'][bias] = {}
|
|
1527
|
+
|
|
1528
|
+
# get normal psd
|
|
1529
|
+
df_bias = df[df.tes_bias_ua==bias]
|
|
1530
|
+
df_index = df_bias.index[0]
|
|
1531
|
+
|
|
1532
|
+
# didv fit result
|
|
1533
|
+
fitresult = obj.get_fit_results(chan, poles=poles)
|
|
1534
|
+
|
|
1535
|
+
# get psd
|
|
1536
|
+
psd = df_bias['psd'].iloc[0]
|
|
1537
|
+
psd_freqs = df_bias['psd_freq'].iloc[0]
|
|
1538
|
+
tes_bias = df_bias['tes_bias'].iloc[0]
|
|
1539
|
+
r0 = float(df_bias['r0_iv'].iloc[0])
|
|
1540
|
+
p0 = float(df_bias['p0_iv'].iloc[0])
|
|
1541
|
+
percent_rn = float(df_bias['percent_rn_iv'].iloc[0])
|
|
1542
|
+
|
|
1543
|
+
if self._verbose:
|
|
1544
|
+
print(f'\nINFO: Analyzing {chan} Noise in Transition for '
|
|
1545
|
+
f'QET bias = {bias} uA ({percent_rn}% Rn)')
|
|
1546
|
+
|
|
1547
|
+
# set Tbath
|
|
1548
|
+
tbath = self._tbath
|
|
1549
|
+
if tbath is None:
|
|
1550
|
+
tbath = df_bias['temperature_mc'].iloc[0]
|
|
1551
|
+
noise_sim.set_tbath(tbath)
|
|
1552
|
+
tbath_list.append(tbath)
|
|
1553
|
+
|
|
1554
|
+
# set data
|
|
1555
|
+
noise_sim.set_psd(chan, psd, psd_freqs, 'transition' )
|
|
1556
|
+
|
|
1557
|
+
# set didv data
|
|
1558
|
+
# set IV sweep results
|
|
1559
|
+
noise_sim.set_iv_didv_results_from_dict(
|
|
1560
|
+
chan,
|
|
1561
|
+
didv_results=fitresult,
|
|
1562
|
+
poles=poles,
|
|
1563
|
+
ivsweep_results=ivsweep_result
|
|
1564
|
+
)
|
|
1565
|
+
|
|
1566
|
+
# analyze
|
|
1567
|
+
noise_sim.analyze_noise(channels=chan,
|
|
1568
|
+
lgc_plot=lgc_plot,
|
|
1569
|
+
xlims=xlims,
|
|
1570
|
+
ylims_current=ylims_current,
|
|
1571
|
+
ylims_power=ylims_power)
|
|
1572
|
+
# power and current noise in transition at the bias point(s)
|
|
1573
|
+
self._noisedict[chan]['transition'][bias] = noise_sim.get_noise_data(chan, 'transition')
|
|
1574
|
+
|
|
1575
|
+
tbath = np.median(tbath_list)
|
|
1576
|
+
ivsweep_result['noise_model_tbath'] = tbath
|
|
1577
|
+
ivsweep_result['noise_model_tc'] = self._tc[chan]
|
|
1578
|
+
ivsweep_result['noise_dict'] = self._noisedict[chan]
|
|
1579
|
+
|
|
1580
|
+
# save ivsweep
|
|
1581
|
+
self.set_ivsweep_results(chan, ivsweep_result, 'iv')
|
|
1582
|
+
|
|
1583
|
+
|
|
1584
|
+
# save hdf5
|
|
1585
|
+
if self._save_hdf5:
|
|
1586
|
+
|
|
1587
|
+
# build full path
|
|
1588
|
+
dir_name = './'
|
|
1589
|
+
if self._save_path is not None:
|
|
1590
|
+
dir_name = self._save_path + '/'
|
|
1591
|
+
output_file = dir_name + self._file_name
|
|
1592
|
+
|
|
1593
|
+
# save
|
|
1594
|
+
self.save_hdf5(output_file, overwrite=True)
|
|
1595
|
+
|
|
1596
|
+
|
|
1597
|
+
|
|
1598
|
+
|
|
1599
|
+
def _fit_didv(self, data_type,
|
|
1600
|
+
channels=None,
|
|
1601
|
+
percent_rn_max=None, percent_rn_min=None,
|
|
1602
|
+
nb_points_max=None,
|
|
1603
|
+
inf_loop_gain_approx='auto',
|
|
1604
|
+
lgc_plot=False, lgc_save_plot=False,
|
|
1605
|
+
plot_save_path=None,
|
|
1606
|
+
tag='default',
|
|
1607
|
+
**kwargs):
|
|
1608
|
+
"""
|
|
1609
|
+
"""
|
|
1610
|
+
|
|
1611
|
+
# channels
|
|
1612
|
+
available_channels = list()
|
|
1613
|
+
ivdata_label = 'ivsweep_data_' + tag
|
|
1614
|
+
for chan, chan_dict in self._filter_data.items():
|
|
1615
|
+
if ivdata_label in chan_dict.keys():
|
|
1616
|
+
available_channels.append(chan)
|
|
1617
|
+
|
|
1618
|
+
if not available_channels:
|
|
1619
|
+
raise ValueError('ERROR: No channel available. '
|
|
1620
|
+
'Set data first!')
|
|
1621
|
+
if channels is None:
|
|
1622
|
+
channels = available_channels
|
|
1623
|
+
else:
|
|
1624
|
+
if isinstance(channels, str):
|
|
1625
|
+
channels = [channels]
|
|
1626
|
+
for chan in channels:
|
|
1627
|
+
if chan not in available_channels:
|
|
1628
|
+
raise ValueError(f'ERROR: No data for channel {chan}'
|
|
1629
|
+
' available. Set data first!')
|
|
1630
|
+
# number of channels
|
|
1631
|
+
nb_channels = len(channels)
|
|
1632
|
+
|
|
1633
|
+
|
|
1634
|
+
# check if IV analysis done
|
|
1635
|
+
for chan in channels:
|
|
1636
|
+
if chan not in self._ibis_objects.keys():
|
|
1637
|
+
raise ValueError(
|
|
1638
|
+
f'ERROR: No IV analysis done for channel {chan}.'
|
|
1639
|
+
' Use first "analyze_sweep" function')
|
|
1640
|
+
|
|
1641
|
+
|
|
1642
|
+
|
|
1643
|
+
# loop channels, fit, and calculate small signal parameters
|
|
1644
|
+
for ichan, chan in enumerate(channels):
|
|
1645
|
+
|
|
1646
|
+
if self._verbose:
|
|
1647
|
+
if data_type == 'sc':
|
|
1648
|
+
print(f'\n{chan} SC dIdV analysis')
|
|
1649
|
+
elif data_type == 'normal':
|
|
1650
|
+
print(f'\n{chan} Normal dIdV analysis')
|
|
1651
|
+
else:
|
|
1652
|
+
print(f'\n{chan} Transition dIdV analysis')
|
|
1653
|
+
|
|
1654
|
+
# lgc plot SC/Normal
|
|
1655
|
+
lgc_plot_sc = lgc_plot
|
|
1656
|
+
lgc_plot_normal = lgc_plot
|
|
1657
|
+
|
|
1658
|
+
# reset summary
|
|
1659
|
+
if chan not in self._didv_summary:
|
|
1660
|
+
self._didv_summary[chan] = {}
|
|
1661
|
+
self._didv_summary[chan][data_type] = {}
|
|
1662
|
+
|
|
1663
|
+
|
|
1664
|
+
# get dataframe
|
|
1665
|
+
df = self.get_ivsweep_data(chan, tag=tag)
|
|
1666
|
+
|
|
1667
|
+
# get IV analysis result + errors
|
|
1668
|
+
iv_results_didv = self.get_ivsweep_results(
|
|
1669
|
+
chan, iv_type='didv', tag=tag
|
|
1670
|
+
)
|
|
1671
|
+
iv_results_iv = None
|
|
1672
|
+
iv_type = 'didv'
|
|
1673
|
+
if 'rp_iv' in df.columns:
|
|
1674
|
+
iv_type = 'iv'
|
|
1675
|
+
iv_results_iv = self.get_ivsweep_results(
|
|
1676
|
+
chan, iv_type='iv', tag=tag
|
|
1677
|
+
)
|
|
1678
|
+
|
|
1679
|
+
# choose results (default = iv data)
|
|
1680
|
+
iv_results = iv_results_didv.copy()
|
|
1681
|
+
if iv_results_iv is not None:
|
|
1682
|
+
iv_results = iv_results_iv.copy()
|
|
1683
|
+
|
|
1684
|
+
rp_iv = iv_results['rp']
|
|
1685
|
+
rp_iv_err = iv_results['rp_err']
|
|
1686
|
+
rn_iv = iv_results['rn']
|
|
1687
|
+
rn_iv_err = iv_results['rn_err']
|
|
1688
|
+
|
|
1689
|
+
# filter dataframe
|
|
1690
|
+
var_name = None
|
|
1691
|
+
if 'percent_rn_iv' in df.columns:
|
|
1692
|
+
var_name = 'percent_rn_iv'
|
|
1693
|
+
elif 'percent_rn_didv' in df.columns:
|
|
1694
|
+
var_name = 'percent_rn_didv'
|
|
1695
|
+
else:
|
|
1696
|
+
raise ValueError(
|
|
1697
|
+
f'ERROR: unable to find "percent_rn" in dataframe'
|
|
1698
|
+
' Something went wrong with sweep analysis')
|
|
1699
|
+
|
|
1700
|
+
cut = (df[var_name] ==df[var_name])
|
|
1701
|
+
if percent_rn_max is not None:
|
|
1702
|
+
cut &= (df[var_name] < percent_rn_max)
|
|
1703
|
+
if percent_rn_min is not None:
|
|
1704
|
+
cut &= (df[var_name] > percent_rn_min)
|
|
1705
|
+
|
|
1706
|
+
|
|
1707
|
+
if sum(cut)==0:
|
|
1708
|
+
raise ValueError(
|
|
1709
|
+
f'ERROR: Unable to find fit points for channel {chan}. '
|
|
1710
|
+
f'You may need to change "percent_rn_max" and/or '
|
|
1711
|
+
f'"percent_rn_min"')
|
|
1712
|
+
|
|
1713
|
+
df_filter = df[cut]
|
|
1714
|
+
if (data_type == 'normal' or data_type == 'transition'):
|
|
1715
|
+
df_filter = df_filter.sort_values(by='tes_bias_ua',
|
|
1716
|
+
key=abs, ascending=False)
|
|
1717
|
+
else:
|
|
1718
|
+
df_filter = df_filter.sort_values(by='tes_bias_ua',
|
|
1719
|
+
key=abs, ascending=True)
|
|
1720
|
+
|
|
1721
|
+
# loop and fit
|
|
1722
|
+
rpn_list = []
|
|
1723
|
+
dt_list = []
|
|
1724
|
+
inductance_list = []
|
|
1725
|
+
|
|
1726
|
+
nb_points = len(df_filter)
|
|
1727
|
+
if nb_points_max is not None:
|
|
1728
|
+
nb_points = min(len(df_filter), nb_points_max)
|
|
1729
|
+
|
|
1730
|
+
# list of index
|
|
1731
|
+
df_indices = df_filter.index.to_list()
|
|
1732
|
+
|
|
1733
|
+
# loop and fit
|
|
1734
|
+
|
|
1735
|
+
for ind in range(nb_points):
|
|
1736
|
+
|
|
1737
|
+
pd_series = df_filter.iloc[ind]
|
|
1738
|
+
state = pd_series['state']
|
|
1739
|
+
df_index = df_indices[ind]
|
|
1740
|
+
|
|
1741
|
+
|
|
1742
|
+
# ignore if "transtion" data but state "sc" or "normal"
|
|
1743
|
+
# based on linearity
|
|
1744
|
+
if (data_type == 'transition'
|
|
1745
|
+
and (state == 'sc' or state == 'normal')):
|
|
1746
|
+
continue
|
|
1747
|
+
|
|
1748
|
+
|
|
1749
|
+
# case transiton, add r0,i0,p0 to iv results
|
|
1750
|
+
if data_type == 'transition':
|
|
1751
|
+
iv_results['ibias'] = pd_series['tes_bias_' + iv_type]
|
|
1752
|
+
iv_results['ibias_err'] = 0
|
|
1753
|
+
iv_results['r0'] = pd_series['r0_' + iv_type]
|
|
1754
|
+
iv_results['r0_err'] = pd_series['r0_err_' + iv_type]
|
|
1755
|
+
iv_results['i0'] = pd_series['i0_' + iv_type]
|
|
1756
|
+
iv_results['i0_err'] = pd_series['i0_err_' + iv_type]
|
|
1757
|
+
iv_results['p0'] = pd_series['p0_' + iv_type]
|
|
1758
|
+
iv_results['p0_err'] = pd_series['p0_err_' + iv_type]
|
|
1759
|
+
|
|
1760
|
+
# display
|
|
1761
|
+
if self._verbose:
|
|
1762
|
+
print('\n\n{} TES bias {:.3f} uA, '
|
|
1763
|
+
'R0 = {:.2f} mOhms (% Rn = {:.2f})'.format(
|
|
1764
|
+
chan,
|
|
1765
|
+
pd_series['tes_bias_ua'],
|
|
1766
|
+
iv_results['r0']*1e3,
|
|
1767
|
+
pd_series[var_name])
|
|
1768
|
+
)
|
|
1769
|
+
|
|
1770
|
+
print('========================================'
|
|
1771
|
+
'=========================\n')
|
|
1772
|
+
|
|
1773
|
+
# instantiate didv and set data
|
|
1774
|
+
didvanalysis = DIDVAnalysis(verbose=False)
|
|
1775
|
+
didvanalysis.set_processed_data(chan, pd_series)
|
|
1776
|
+
|
|
1777
|
+
# fit
|
|
1778
|
+
results = None
|
|
1779
|
+
if (data_type == 'normal' or data_type == 'sc'):
|
|
1780
|
+
didvanalysis.dofit(1)
|
|
1781
|
+
didvanalysis.calc_smallsignal_params(poles=1)
|
|
1782
|
+
results = {1: didvanalysis.get_fit_results(chan,1)}
|
|
1783
|
+
else:
|
|
1784
|
+
if self._verbose:
|
|
1785
|
+
print('INFO: Fitting dIdV (2 and 3-poles)')
|
|
1786
|
+
didvanalysis.dofit([2,3])
|
|
1787
|
+
didvanalysis.set_ivsweep_results_from_data(chan, iv_results)
|
|
1788
|
+
didvanalysis.calc_smallsignal_params(poles=[2,3])
|
|
1789
|
+
didvanalysis.calc_bias_params_infinite_loop_gain()
|
|
1790
|
+
|
|
1791
|
+
# calc dpdi
|
|
1792
|
+
if 'psd_freq' in pd_series:
|
|
1793
|
+
print(f'INFO: Calculating dPdI (2 and 3-poles)')
|
|
1794
|
+
freqs = pd_series['psd_freq']
|
|
1795
|
+
didvanalysis.calc_dpdi(freqs, channels=chan,
|
|
1796
|
+
list_of_poles=[2,3])
|
|
1797
|
+
results = {2: didvanalysis.get_fit_results(chan,2),
|
|
1798
|
+
3: didvanalysis.get_fit_results(chan,3)}
|
|
1799
|
+
|
|
1800
|
+
# store results
|
|
1801
|
+
if 1 in results:
|
|
1802
|
+
rpn = results[1]['smallsignalparams']['rp']
|
|
1803
|
+
if data_type == 'normal':
|
|
1804
|
+
rpn -= rp_iv
|
|
1805
|
+
rpn_list.append(rpn)
|
|
1806
|
+
dt_list.append(results[1]['smallsignalparams']['dt'])
|
|
1807
|
+
inductance_list.append(results[1]['smallsignalparams']['L'])
|
|
1808
|
+
|
|
1809
|
+
# transition
|
|
1810
|
+
for poles in [2,3]:
|
|
1811
|
+
|
|
1812
|
+
if (data_type != 'transition'
|
|
1813
|
+
or poles not in results.keys()):
|
|
1814
|
+
continue
|
|
1815
|
+
|
|
1816
|
+
cov_matrix = np.abs(results[poles]['ssp_light']['cov'])
|
|
1817
|
+
vals_vector = results[poles]['ssp_light']['vals']
|
|
1818
|
+
sigmas_vector = results[poles]['ssp_light']['sigmas']
|
|
1819
|
+
|
|
1820
|
+
biasparams_ilg = (
|
|
1821
|
+
didvanalysis.get_bias_params_infinite_loop_gain(
|
|
1822
|
+
chan, poles=poles)
|
|
1823
|
+
)
|
|
1824
|
+
|
|
1825
|
+
if self._verbose:
|
|
1826
|
+
|
|
1827
|
+
print(f'\nResults from {poles}-poles dIdV fit')
|
|
1828
|
+
print('---------------------------------')
|
|
1829
|
+
|
|
1830
|
+
|
|
1831
|
+
print('\nFit chi2/Ndof = {:.3f}'.format(
|
|
1832
|
+
results[poles]['cost'])
|
|
1833
|
+
)
|
|
1834
|
+
|
|
1835
|
+
print(f'\nFit time constants, NOT dIdV Poles: ')
|
|
1836
|
+
print('Tau1: {:.3g} s'.format(
|
|
1837
|
+
np.abs(results[poles]['params']['tau1'])))
|
|
1838
|
+
print('Tau2: {:.3g} s'.format(
|
|
1839
|
+
results[poles]['params']['tau2']))
|
|
1840
|
+
print('Tau3: {:.4g} s'.format(
|
|
1841
|
+
results[poles]['params']['tau3']))
|
|
1842
|
+
|
|
1843
|
+
print(f'\nTrue dIdV Poles (from {poles}-poles fit): ')
|
|
1844
|
+
print('Tau_plus: {:.3g} s'.format(
|
|
1845
|
+
results[poles]['falltimes'][0]))
|
|
1846
|
+
print('Tau_minus: {:.3g} s'.format(
|
|
1847
|
+
results[poles]['falltimes'][1]))
|
|
1848
|
+
print('Tau_third: {:.4g} s'.format(
|
|
1849
|
+
results[poles]['falltimes'][2]))
|
|
1850
|
+
|
|
1851
|
+
print(f'\nSmall Signal Parameters:')
|
|
1852
|
+
print('l (loop gain) = {:.3f} +/- {:.4f}'.format(
|
|
1853
|
+
vals_vector['l'], sigmas_vector['sigma_l']))
|
|
1854
|
+
print('beta = {:.3f} +/- {:.4f}'.format(
|
|
1855
|
+
vals_vector['beta'], sigmas_vector['sigma_beta']))
|
|
1856
|
+
print('gratio = {:.3f} +/- {:.4f}'.format(
|
|
1857
|
+
vals_vector['gratio'], sigmas_vector['sigma_gratio']))
|
|
1858
|
+
print('tau0 = {:.3g} +/- {:.4g} ms'.format(
|
|
1859
|
+
vals_vector['tau0']*1e3, sigmas_vector['sigma_tau0']*1e3))
|
|
1860
|
+
print('L = {:.3f} +/- {:.4f} nH'.format(
|
|
1861
|
+
vals_vector['L']*1e9, sigmas_vector['sigma_L']*1e9))
|
|
1862
|
+
|
|
1863
|
+
if lgc_plot and poles==3:
|
|
1864
|
+
cov_matrix_reduced = cov_matrix[:5,:5]
|
|
1865
|
+
std_deviations = np.sqrt(np.diag(cov_matrix_reduced))
|
|
1866
|
+
cor_matrix = cov_matrix_reduced / np.outer(std_deviations,
|
|
1867
|
+
std_deviations)
|
|
1868
|
+
|
|
1869
|
+
labels = ['beta', 'loopgain', 'L', 'tau0', 'gratio']
|
|
1870
|
+
ticks = np.arange(0, 5, 1)
|
|
1871
|
+
plt.figure(figsize=(6,4))
|
|
1872
|
+
plt.matshow(np.log(cor_matrix), fignum=1)
|
|
1873
|
+
plt.xticks(ticks=ticks, labels=labels)
|
|
1874
|
+
plt.yticks(ticks=ticks, labels=labels)
|
|
1875
|
+
plt.colorbar(label = "log(corr matrix term)")
|
|
1876
|
+
plt.title("Correlation Matrix")
|
|
1877
|
+
plt.show()
|
|
1878
|
+
|
|
1879
|
+
|
|
1880
|
+
|
|
1881
|
+
# store summary
|
|
1882
|
+
if poles not in self._didv_summary[chan]['transition']:
|
|
1883
|
+
self._didv_summary[chan]['transition'][poles] = {
|
|
1884
|
+
'Rn %': [], 'tes_bias_ua': [], 'chi2': [],
|
|
1885
|
+
'tau+': [], 'tau-': [], 'tau3': [],
|
|
1886
|
+
'l': [], 'l_err': [], 'beta': [], 'beta_err': [],
|
|
1887
|
+
'gratio': [], 'gratio_err': [], 'tau0': [],
|
|
1888
|
+
'tau0_err': [], 'L': [], 'L_err': []
|
|
1889
|
+
}
|
|
1890
|
+
else:
|
|
1891
|
+
current = self._didv_summary[chan]['transition'][poles]
|
|
1892
|
+
current['Rn %'].append(pd_series[var_name])
|
|
1893
|
+
current['tes_bias_ua'].append(pd_series['tes_bias_ua'])
|
|
1894
|
+
current['chi2'].append(results[poles]['cost'])
|
|
1895
|
+
current['tau+'].append(results[poles]['falltimes'][0])
|
|
1896
|
+
current['tau-'].append(results[poles]['falltimes'][1])
|
|
1897
|
+
current['tau3'].append(results[poles]['falltimes'][2])
|
|
1898
|
+
current['l'].append(vals_vector['l'])
|
|
1899
|
+
current['l_err'].append(sigmas_vector['sigma_l'])
|
|
1900
|
+
current['beta'].append(vals_vector['beta'])
|
|
1901
|
+
current['beta_err'].append(sigmas_vector['sigma_beta'])
|
|
1902
|
+
current['gratio'].append(vals_vector['gratio'])
|
|
1903
|
+
current['gratio_err'].append(sigmas_vector['sigma_gratio'])
|
|
1904
|
+
current['tau0'].append(vals_vector['tau0'])
|
|
1905
|
+
current['tau0_err'].append(sigmas_vector['sigma_tau0'])
|
|
1906
|
+
current['L'].append(vals_vector['L'])
|
|
1907
|
+
current['L_err'].append(sigmas_vector['sigma_L'])
|
|
1908
|
+
self._didv_summary[chan]['transition'][poles] = current
|
|
1909
|
+
|
|
1910
|
+
|
|
1911
|
+
# store in dataframe
|
|
1912
|
+
df.loc[df_index, f'didv_{poles}poles_chi2'] = results[poles]['cost']
|
|
1913
|
+
|
|
1914
|
+
df.loc[df_index, f'didv_{poles}poles_tau+'] = results[poles]['falltimes'][0]
|
|
1915
|
+
df.loc[df_index, f'didv_{poles}poles_tau-'] = results[poles]['falltimes'][1]
|
|
1916
|
+
df.loc[df_index, f'didv_{poles}poles_tau3'] = results[poles]['falltimes'][2]
|
|
1917
|
+
|
|
1918
|
+
df.loc[df_index, f'didv_{poles}poles_l'] = vals_vector['l']
|
|
1919
|
+
df.loc[df_index, f'didv_{poles}poles_l_err'] = (
|
|
1920
|
+
sigmas_vector['sigma_l']
|
|
1921
|
+
)
|
|
1922
|
+
df.loc[df_index, f'didv_{poles}poles_beta'] = vals_vector['beta']
|
|
1923
|
+
df.loc[df_index, f'didv_{poles}poles_beta_err'] = (
|
|
1924
|
+
sigmas_vector['sigma_beta']
|
|
1925
|
+
)
|
|
1926
|
+
|
|
1927
|
+
df.loc[df_index, f'didv_{poles}poles_gratio'] = vals_vector['gratio']
|
|
1928
|
+
df.loc[df_index, f'didv_{poles}poles_gratio_err'] = (
|
|
1929
|
+
sigmas_vector['sigma_gratio']
|
|
1930
|
+
)
|
|
1931
|
+
|
|
1932
|
+
df.loc[df_index, f'didv_{poles}poles_tau0'] = vals_vector['tau0']
|
|
1933
|
+
df.loc[df_index, f'didv_{poles}poles_tau0_err'] = (
|
|
1934
|
+
sigmas_vector['sigma_tau0']
|
|
1935
|
+
)
|
|
1936
|
+
|
|
1937
|
+
df.loc[df_index, f'didv_{poles}poles_L'] = vals_vector['L']
|
|
1938
|
+
df.loc[df_index, f'didv_{poles}poles_L_err'] = (
|
|
1939
|
+
sigmas_vector['sigma_L']
|
|
1940
|
+
)
|
|
1941
|
+
|
|
1942
|
+
|
|
1943
|
+
# infinite loop gain bias
|
|
1944
|
+
df.loc[df_index, f'didv_{poles}poles_r0_infinite_lgain'] = (
|
|
1945
|
+
biasparams_ilg['r0']
|
|
1946
|
+
)
|
|
1947
|
+
df.loc[df_index, f'didv_{poles}poles_r0_err_infinite_lgain'] = (
|
|
1948
|
+
biasparams_ilg['r0_err']
|
|
1949
|
+
)
|
|
1950
|
+
df.loc[df_index, f'didv_{poles}poles_i0_infinite_lgain'] = (
|
|
1951
|
+
biasparams_ilg['i0']
|
|
1952
|
+
)
|
|
1953
|
+
df.loc[df_index, f'didv_{poles}poles_i0_err_infinite_lgain'] = (
|
|
1954
|
+
biasparams_ilg['i0_err']
|
|
1955
|
+
)
|
|
1956
|
+
|
|
1957
|
+
df.loc[df_index, f'didv_{poles}poles_p0_infinite_lgain'] = (
|
|
1958
|
+
biasparams_ilg['p0']
|
|
1959
|
+
)
|
|
1960
|
+
df.loc[df_index, f'didv_{poles}poles_p0_err_infinite_lgain'] = (
|
|
1961
|
+
biasparams_ilg['p0_err']
|
|
1962
|
+
)
|
|
1963
|
+
|
|
1964
|
+
|
|
1965
|
+
# display
|
|
1966
|
+
if lgc_plot:
|
|
1967
|
+
|
|
1968
|
+
# sc
|
|
1969
|
+
if (lgc_plot_sc and data_type == 'sc'):
|
|
1970
|
+
didvanalysis.plot_fit_result(chan)
|
|
1971
|
+
lgc_plot_sc = False
|
|
1972
|
+
|
|
1973
|
+
# normal
|
|
1974
|
+
if (lgc_plot_normal and data_type == 'normal'):
|
|
1975
|
+
didvanalysis.plot_fit_result(chan)
|
|
1976
|
+
lgc_plot_normal = False
|
|
1977
|
+
|
|
1978
|
+
|
|
1979
|
+
# transition
|
|
1980
|
+
if data_type == 'transition':
|
|
1981
|
+
didvanalysis.plot_fit_result(chan)
|
|
1982
|
+
|
|
1983
|
+
|
|
1984
|
+
# save didvobjects
|
|
1985
|
+
if chan not in self._didv_objects:
|
|
1986
|
+
self._didv_objects[chan] = dict()
|
|
1987
|
+
if data_type not in self._didv_objects[chan]:
|
|
1988
|
+
self._didv_objects[chan][data_type] = dict()
|
|
1989
|
+
self._didv_objects[chan][data_type][pd_series['tes_bias_ua']] = didvanalysis
|
|
1990
|
+
|
|
1991
|
+
|
|
1992
|
+
# mean/std of rn/rp
|
|
1993
|
+
rpn_didv_fit = np.mean(rpn_list)
|
|
1994
|
+
rpn_err_didv_fit = np.std(rpn_list)
|
|
1995
|
+
dt_didv_fit = np.mean(dt_list)
|
|
1996
|
+
dt_err_didv_fit = np.std(dt_list)
|
|
1997
|
+
inductance_fit = np.median(inductance_list)
|
|
1998
|
+
|
|
1999
|
+
|
|
2000
|
+
# store summary
|
|
2001
|
+
|
|
2002
|
+
if data_type == 'normal':
|
|
2003
|
+
self._didv_summary[chan]['normal'] = {
|
|
2004
|
+
'rn':rpn_didv_fit,
|
|
2005
|
+
'rn_err': rpn_err_didv_fit,
|
|
2006
|
+
'rn_iv': rn_iv,
|
|
2007
|
+
'rn_iv_err': rn_iv_err}
|
|
2008
|
+
|
|
2009
|
+
# add inductance to IV result
|
|
2010
|
+
iv_results_didv['normal_didv_fit_L'] = inductance_fit
|
|
2011
|
+
iv_results_didv['normal_didv_fit_rn'] = rpn_didv_fit
|
|
2012
|
+
iv_results_didv['normal_didv_fit_dt'] = dt_didv_fit
|
|
2013
|
+
if iv_results_iv is not None:
|
|
2014
|
+
iv_results_iv['normal_didv_fit_L'] = inductance_fit
|
|
2015
|
+
iv_results_iv['normal_didv_fit_rn'] = rpn_didv_fit
|
|
2016
|
+
iv_results_iv['normal_didv_fit_dt'] = dt_didv_fit
|
|
2017
|
+
|
|
2018
|
+
elif data_type == 'sc':
|
|
2019
|
+
self._didv_summary[chan]['sc'] = {
|
|
2020
|
+
'rp':rpn_didv_fit,
|
|
2021
|
+
'rp_err': rpn_err_didv_fit,
|
|
2022
|
+
'rp_iv': rn_iv,
|
|
2023
|
+
'rp_iv_err': rn_iv_err}
|
|
2024
|
+
|
|
2025
|
+
# add inductance to IV result
|
|
2026
|
+
iv_results_didv['sc_didv_fit_L'] = inductance_fit
|
|
2027
|
+
iv_results_didv['sc_didv_fit_rp'] = rpn_didv_fit
|
|
2028
|
+
iv_results_didv['sc_didv_fit_dt'] = dt_didv_fit
|
|
2029
|
+
if iv_results_iv is not None:
|
|
2030
|
+
iv_results_iv['sc_didv_fit_L'] = inductance_fit
|
|
2031
|
+
iv_results_iv['sc_didv_fit_rp'] = rpn_didv_fit
|
|
2032
|
+
iv_results_iv['sc_didv_fit_dt'] = dt_didv_fit
|
|
2033
|
+
|
|
2034
|
+
if self._verbose:
|
|
2035
|
+
|
|
2036
|
+
if data_type == 'normal':
|
|
2037
|
+
print('{} Rn from dIdV fit = {:.2f} +/- {:.3f} mOhms'.format(
|
|
2038
|
+
chan, rpn_didv_fit*1e3,rpn_err_didv_fit*1e3))
|
|
2039
|
+
print('{} Rn from IV Sweep = {:.2f} +/- {:.3f} mOhms'.format(
|
|
2040
|
+
chan, rn_iv*1e3, rn_iv_err*1e3))
|
|
2041
|
+
elif data_type == 'sc':
|
|
2042
|
+
print('{} Rp from dIdV fit = {:.2f} +/- {:.3f} mOhms'.format(
|
|
2043
|
+
chan, rpn_didv_fit*1e3,rpn_err_didv_fit*1e3))
|
|
2044
|
+
print('{} Rp from IV Sweep = {:.2f} +/- {:.3f} mOhms'.format(
|
|
2045
|
+
chan, rp_iv*1e3, rp_iv_err*1e3))
|
|
2046
|
+
|
|
2047
|
+
# store df
|
|
2048
|
+
self.set_ivsweep_data(chan, df, tag=tag)
|
|
2049
|
+
|
|
2050
|
+
# store updated iv results
|
|
2051
|
+
if (data_type == 'sc' or data_type == 'normal'):
|
|
2052
|
+
self.set_ivsweep_results(chan, iv_results_didv,
|
|
2053
|
+
'didv', tag=tag)
|
|
2054
|
+
if iv_results_iv is not None:
|
|
2055
|
+
self.set_ivsweep_results(chan, iv_results_iv,
|
|
2056
|
+
'iv', tag=tag)
|
|
2057
|
+
|
|
2058
|
+
def _set_file_name(self):
|
|
2059
|
+
"""
|
|
2060
|
+
Set file name
|
|
2061
|
+
"""
|
|
2062
|
+
|
|
2063
|
+
|
|
2064
|
+
now = datetime.now()
|
|
2065
|
+
date_text = now.strftime('%Y') + now.strftime('%m') + now.strftime('%d')
|
|
2066
|
+
time_text = now.strftime('%H') + now.strftime('%M')
|
|
2067
|
+
timestamp_id = ('D' + date_text + '_T'
|
|
2068
|
+
+ time_text + now.strftime('%S'))
|
|
2069
|
+
|
|
2070
|
+
file_name = 'ivsweep_analysis_' + timestamp_id + '.hdf5'
|
|
2071
|
+
|
|
2072
|
+
return file_name
|