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.
Files changed (60) hide show
  1. pytesprocess/__init__.py +9 -0
  2. pytesprocess/_version.py +2 -0
  3. pytesprocess/cli/__init__.py +1 -0
  4. pytesprocess/cli/commands/__init__.py +5 -0
  5. pytesprocess/cli/commands/event.py +66 -0
  6. pytesprocess/cli/commands/filter.py +17 -0
  7. pytesprocess/cli/commands/ivsweep.py +29 -0
  8. pytesprocess/cli/common.py +86 -0
  9. pytesprocess/cli/main.py +81 -0
  10. pytesprocess/config/__init__.py +4 -0
  11. pytesprocess/config/loader.py +94 -0
  12. pytesprocess/config/manager.py +297 -0
  13. pytesprocess/config/resolvers/__init__.py +5 -0
  14. pytesprocess/config/resolvers/common.py +56 -0
  15. pytesprocess/config/resolvers/feature.py +293 -0
  16. pytesprocess/config/resolvers/salting.py +86 -0
  17. pytesprocess/config/resolvers/trigger.py +84 -0
  18. pytesprocess/config/selectors.py +108 -0
  19. pytesprocess/config/validation.py +314 -0
  20. pytesprocess/config/warnings.py +2 -0
  21. pytesprocess/core/__init__.py +10 -0
  22. pytesprocess/core/algorithms.py +1455 -0
  23. pytesprocess/core/didv.py +1648 -0
  24. pytesprocess/core/eventbuilder.py +495 -0
  25. pytesprocess/core/filterbuilder.py +81 -0
  26. pytesprocess/core/filterdata.py +1849 -0
  27. pytesprocess/core/ivsweep.py +2072 -0
  28. pytesprocess/core/noise.py +923 -0
  29. pytesprocess/core/noisemodel.py +1408 -0
  30. pytesprocess/core/oftrigger.py +1035 -0
  31. pytesprocess/core/template.py +450 -0
  32. pytesprocess/process/__init__.py +6 -0
  33. pytesprocess/process/data_source.py +185 -0
  34. pytesprocess/process/event_context.py +35 -0
  35. pytesprocess/process/feature_plan.py +186 -0
  36. pytesprocess/process/feature_resources.py +267 -0
  37. pytesprocess/process/features.py +1024 -0
  38. pytesprocess/process/filterprocess.py +1176 -0
  39. pytesprocess/process/ivprocess.py +1380 -0
  40. pytesprocess/process/processing_data.py +967 -0
  41. pytesprocess/process/randoms.py +921 -0
  42. pytesprocess/process/triggers.py +1011 -0
  43. pytesprocess/salting/__init__.py +7 -0
  44. pytesprocess/salting/generator.py +364 -0
  45. pytesprocess/salting/injector.py +329 -0
  46. pytesprocess/salting/sampling.py +84 -0
  47. pytesprocess/utils/__init__.py +5 -0
  48. pytesprocess/utils/arg_utils.py +122 -0
  49. pytesprocess/utils/dataframe_output.py +120 -0
  50. pytesprocess/utils/filter_hdf5.py +594 -0
  51. pytesprocess/utils/utils.py +701 -0
  52. pytesprocess/workflows/__init__.py +3 -0
  53. pytesprocess/workflows/processing.py +317 -0
  54. pytesprocess/workflows/salting.py +133 -0
  55. pytesprocess-0.1.1.dist-info/METADATA +211 -0
  56. pytesprocess-0.1.1.dist-info/RECORD +60 -0
  57. pytesprocess-0.1.1.dist-info/WHEEL +5 -0
  58. pytesprocess-0.1.1.dist-info/entry_points.txt +2 -0
  59. pytesprocess-0.1.1.dist-info/licenses/LICENSE +21 -0
  60. 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