itkdb-gtk 0.9.0__py3-none-any.whl → 0.9.1.dev1__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.

Potentially problematic release.


This version of itkdb-gtk might be problematic. Click here for more details.

itkdb_gtk/GetShipments.py CHANGED
@@ -25,7 +25,7 @@ gtk_runs, gtk_args = Gtk.init_check()
25
25
  class ReceiveShipments(dbGtkUtils.ITkDBWindow):
26
26
  """Find shipments related to given recipient."""
27
27
 
28
- def __init__(self, session, recipient="IFIC"):
28
+ def __init__(self, session, recipient="IFIC", help=None):
29
29
  """Initialization.
30
30
 
31
31
  Args:
@@ -44,7 +44,7 @@ class ReceiveShipments(dbGtkUtils.ITkDBWindow):
44
44
  if gtk_runs:
45
45
  super().__init__(session=session, title="Upload AVS Data",
46
46
  show_search="Cliick to search shipments",
47
- gtk_runs=gtk_runs)
47
+ gtk_runs=gtk_runs, help=help)
48
48
 
49
49
  self.init_window()
50
50
 
itkdb_gtk/GlueWeight.py CHANGED
@@ -86,7 +86,7 @@ def remove_defaul_keys(data, default_value=-9999):
86
86
  class GlueWeight(Gtk.Window):
87
87
  """Upluead Glue Weight test."""
88
88
 
89
- def __init__(self, session, ifile=None):
89
+ def __init__(self, session, ifile=None, help=None):
90
90
  """Initialization.
91
91
 
92
92
  Args:
@@ -113,7 +113,7 @@ class GlueWeight(Gtk.Window):
113
113
  session, "MODULE", "GLUE_WEIGHT", defaults)
114
114
 
115
115
  if gtk_runs:
116
- super().__init__(title="Upload AVS Data")
116
+ super().__init__(title="Upload Glue Weight", help=help)
117
117
  self.init_window()
118
118
 
119
119
  def init_window(self):
itkdb_gtk/ITkDButils.py CHANGED
@@ -279,7 +279,7 @@ def upload_test(client, data, attachments=None):
279
279
  return (str(e))
280
280
 
281
281
 
282
- def create_shipment(session, sender, recipient, items, name=None, send=False,
282
+ def create_shipment(session, sender, recipient, items, name=None, send=False,
283
283
  type="domestic", attachment=None, comments=None):
284
284
  """Create a chipment.
285
285
 
@@ -34,14 +34,15 @@ def find_children(W):
34
34
  return None
35
35
 
36
36
 
37
- class GroundingTest(dbGtkUtils.ITkDBWindow):
37
+ class PetalReceptionTests(dbGtkUtils.ITkDBWindow):
38
38
  """Dashboard class."""
39
39
 
40
- def __init__(self, session):
40
+ def __init__(self, session, help=None):
41
41
  """Initialization."""
42
42
  super().__init__(title="ITkDB Dashboard",
43
43
  session=session,
44
- show_search="Find object with given SN.")
44
+ show_search="Find object with given SN.",
45
+ help=help)
45
46
 
46
47
  # Members
47
48
  self.dbObject = None
@@ -267,7 +268,7 @@ def main():
267
268
 
268
269
  client.user_gui = dlg
269
270
 
270
- gTest = GroundingTest(client)
271
+ gTest = PetalReceptionTests(client)
271
272
 
272
273
  gTest.present()
273
274
  gTest.connect("destroy", Gtk.main_quit)
@@ -0,0 +1,490 @@
1
+ #!/usr/bin/env python3
2
+ """A collection of utilities for sensor data."""
3
+ import io
4
+ from pathlib import Path
5
+ import numpy as np
6
+ import warnings
7
+ import tempfile
8
+ from itkdb_gtk import ITkDButils
9
+
10
+ #
11
+ # The following is taken from
12
+ # https://gitlab.cern.ch/atlas-itk/sw/db/production_database_scripts.git
13
+ #
14
+ mm = 1e-3
15
+ cm = 1e-2
16
+ UpperV = 500 # For sensor to pass, I must be < Imax up until this voltage and no breakdown must be detected before then.
17
+ StabilityV = 700 # Voltage with multiple current readings to check stability
18
+
19
+ AreaDict = {
20
+ "Unknown": (97.621 - 0.450) * (97.950 - 0.550) * mm * mm,
21
+ #
22
+ "ATLAS12": 95.7 * 95.64 * mm * mm,
23
+ "ATLAS17LS": (97.621 - 0.450) * (97.950 - 0.550) * mm * mm,
24
+ #
25
+ "ATLAS18R0": 89.9031 * cm * cm,
26
+ "ATLAS18R1": 89.0575 * cm * cm,
27
+ "ATLAS18R2": 74.1855 * cm * cm,
28
+ "ATLAS18R3": 80.1679 * cm * cm,
29
+ "ATLAS18R4": 87.4507 * cm * cm,
30
+ "ATLAS18R5": 91.1268 * cm * cm,
31
+ #
32
+ "ATLAS18SS": 93.6269 * cm * cm,
33
+ "ATLAS18LS": 93.6269 * cm * cm,
34
+ #
35
+ "ATLASDUMMY18": (97.621 - 0.450) * (97.950 - 0.550) * mm * mm,
36
+ }
37
+
38
+
39
+ NO_BD_CONST = 9.99e99
40
+
41
+ def LocateMicroDischarge(
42
+ I,
43
+ V,
44
+ sm_window=2,
45
+ bd_limit=5.5,
46
+ allow_running_bd=True,
47
+ use_additional_cond=False,
48
+ tolerence=0.05,
49
+ voltage_span=4,
50
+ fit_window=5,
51
+ ):
52
+ """
53
+ Function for BDV estimation - if questions please contact Vera Latonova (vera.latonova@cern.ch).
54
+ I,V must have same shape and voltages must be in ascending order,
55
+ same indexes of I&V arrays must correspond each other,
56
+ only invalid data or holdstep should be stripped before
57
+ but it is not necessary. Measurments U=0&I=0 are removed.
58
+ If there is same or higher amount of same voltages in row than
59
+ sm_window, from this sequence we cannot estimete dI/dV and
60
+ we have to remove this averaged point.
61
+
62
+ It is assumed that only parameter use_additional_cond would be
63
+ changed by caller. Changing of other parameters may affect
64
+ BDV unexpectedly.
65
+
66
+
67
+ @param[in] I - array of currents without any cut
68
+ @param[in] V - array of voltages, ascending order, without any cut
69
+ @param[in] sm_window - size of smoothing window
70
+ @param[in] bd_limit - BD limit for |U| < 500V
71
+ @param[in] allow_running_bd - allow increase bd_limit for |U| > 500
72
+ @param[in] use_additional_cond - use additional BD contition
73
+ @param[in] tolerence - configuration of additional condition
74
+ @param[in] voltage_span - max width of hump on spectra which may be neglected
75
+ in voltage steps in additional contition
76
+ @param[in] fit_window - number of points used for linear fit before BD voltage
77
+
78
+ @return BD voltage (always positive) or NO_BD_CONST = 9.99e99 if not found.
79
+ """
80
+
81
+ # add nan to the end of array
82
+ V = np.abs(V)
83
+ I = np.abs(I)
84
+
85
+ # skip zeros
86
+ ind = np.where(np.logical_or(I != 0, V != 0))
87
+ V = V[ind]
88
+ I = I[ind]
89
+
90
+ V_ = np.append(V, np.nan * np.ones(sm_window - 1))
91
+ I_ = np.append(I, np.nan * np.ones(sm_window - 1))
92
+
93
+ # make 2D array of I's, V's each row_ind shifted by row_ind index
94
+ # i.e from array [1,3,5] we make (for sm_window=2) 2D array
95
+ # [ 1,3,5,nan]
96
+ # [nan,5,1,3]
97
+ # than get average from each column -> I_avg, V_avg
98
+ r = np.arange(sm_window)
99
+
100
+ V2 = np.outer(np.ones(sm_window), V_)
101
+ row_ind, col_ind = np.ogrid[: V2.shape[0], : V2.shape[1]]
102
+ col_ind = col_ind - r[:, np.newaxis]
103
+ V2 = V2[row_ind, col_ind]
104
+ # strip fields with nans
105
+ V2 = np.transpose(V2[:, (sm_window - 1) : -(sm_window - 1)])
106
+
107
+ I2 = np.outer(np.ones(sm_window), I_)
108
+ row_ind, col_ind = np.ogrid[: I2.shape[0], : I2.shape[1]]
109
+ col_ind = col_ind - r[:, np.newaxis]
110
+ I2 = I2[row_ind, col_ind]
111
+ I2 = np.transpose(I2[:, (sm_window - 1) : -(sm_window - 1)])
112
+
113
+ # get V & I averages
114
+ try:
115
+ V_avg = np.average(V2, axis=1)
116
+ I_avg = np.average(I2, axis=1)
117
+ except ZeroDivisionError:
118
+ # not enough data
119
+ return NO_BD_CONST
120
+
121
+ # find dI / dV array
122
+ # I'm not able to write this without cycle
123
+ dIdV = np.array([])
124
+ for i in range(V2.shape[0]):
125
+ with warnings.catch_warnings():
126
+ warnings.filterwarnings("error")
127
+ try:
128
+ dIdV = np.append(dIdV, np.polyfit(V2[i, :], I2[i, :], 1)[0])
129
+ except (np.RankWarning, TypeError):
130
+ dIdV = np.append(dIdV, np.nan)
131
+
132
+ # stripping U[n] == U[n+1] (i.e. hodlsetp) => fit cannot be sucessful =>
133
+ # dIdV is nan @holdstep
134
+ ind = np.where(np.isfinite(dIdV))
135
+ I_avg = I_avg[ind]
136
+ V_avg = V_avg[ind]
137
+ dIdV = dIdV[ind]
138
+
139
+ # get running BDV limit & compare
140
+ bd_limit_running = bd_limit + np.where(
141
+ allow_running_bd and V_avg > 500, (V_avg - 500.0) / 100.0, 0
142
+ )
143
+ V_avg_BD_ind = dIdV / (I_avg / V_avg) > bd_limit_running
144
+ V_avg_BD = V_avg[V_avg_BD_ind]
145
+
146
+ # Estimate BDV
147
+ BDV = np.array([])
148
+
149
+ # no break-down
150
+ if V_avg_BD.shape == (0,):
151
+ return NO_BD_CONST
152
+
153
+ # if V_avg_BD_ind[0] == True ... BDV <- V[0]
154
+ # for others V_avg_BD_ind[n] == True BDV <- (V_avg[n] + V_avg[n-1])/2
155
+ if V_avg_BD_ind[0]:
156
+ BDV = np.append(BDV, V[0])
157
+ V_avg_BD_ind[0] = False
158
+
159
+ BDV = np.append(
160
+ BDV,
161
+ (V_avg[np.where(V_avg_BD_ind)] + V_avg[np.where(V_avg_BD_ind)[0] - 1]) / 2.0,
162
+ )
163
+
164
+ ###########################################################################
165
+ ## Application of additional condition ####################################
166
+ ###########################################################################
167
+ if not use_additional_cond:
168
+ return BDV[0]
169
+
170
+ # get index if V <= BDV
171
+ B = np.where(np.less.outer(BDV, V))
172
+ col_ind = np.mgrid[: BDV.shape[0], : V.shape[0]][1]
173
+ col_ind[B[0], B[1]] = 0
174
+ V_BDV_ind = np.max(col_ind, axis=1)
175
+
176
+ back_ok_v_ind = 0
177
+ while True:
178
+ with warnings.catch_warnings():
179
+ warnings.filterwarnings("error")
180
+ try:
181
+ a, b = np.polyfit(
182
+ V[
183
+ max(back_ok_v_ind, V_BDV_ind[0] - fit_window) : max(
184
+ back_ok_v_ind, V_BDV_ind[0]
185
+ )
186
+ ],
187
+ I[
188
+ max(back_ok_v_ind, V_BDV_ind[0] - fit_window) : max(
189
+ back_ok_v_ind, V_BDV_ind[0]
190
+ )
191
+ ],
192
+ 1,
193
+ )
194
+ except (np.RankWarning, TypeError):
195
+ return BDV[0]
196
+
197
+ ind = np.where(1 - (a * V + b) / I <= tolerence)[0]
198
+ try:
199
+ back_ok_v_ind = np.min(ind[ind > V_BDV_ind[0] + 1])
200
+ except ValueError:
201
+ # sensor is not going back
202
+ return BDV[0]
203
+ # hump is too long -- it cannot be skipped
204
+ if back_ok_v_ind - V_BDV_ind[0] > voltage_span:
205
+ return BDV[0]
206
+
207
+ # skip BDVs inside hump
208
+ ind = BDV >= V[back_ok_v_ind]
209
+ BDV = BDV[ind]
210
+ V_BDV_ind = V_BDV_ind[ind]
211
+ if V_avg_BD.shape == (0,):
212
+ return NO_BD_CONST
213
+
214
+ return NO_BD_CONST
215
+
216
+
217
+ def scale_iv(I, T1, T2):
218
+ """Normalize corrent to given temperature (T2)
219
+
220
+ Args:
221
+ I (array): Current
222
+ T1 (float): Original temperature
223
+ T2 (float): New temperature.
224
+
225
+ Return:
226
+ Array with scaled currents.
227
+
228
+ """
229
+ factor = (T2 / T1) ** 2 * np.exp((-1.21 / 8.62) * (1 / T2 - 1 / T1))
230
+ return factor * I
231
+
232
+
233
+ def sensor_data_to_json(session, mdata, mod_type, logger=None):
234
+ """Read a Sensor data file and return the corresponding JSon for hte PDB.
235
+
236
+ Args:
237
+ mdata: Data as returned by :function:`read_sensor_file`.
238
+ logger: an object that writes messages via a write_message method
239
+ """
240
+ # Check if we are dealing with sensors or modules
241
+ is_module = "Module_SN" in mdata
242
+
243
+ if is_module:
244
+ test = ITkDButils.get_test_skeleton(session, "MODULE", mdata["TestType"])
245
+ tp = "ATLAS18{}".format(mod_type[0:2])
246
+ area = AreaDict[tp] / cm**2
247
+ SN = mdata["Module_SN"]
248
+
249
+ else:
250
+ test = ITkDButils.get_test_skeleton(session, "SENSOR", mdata["TestType"])
251
+ area = AreaDict[mod_type] / cm**2
252
+ SN = mdata["Component"]
253
+
254
+ if logger:
255
+ logger.write_message("Analyzing {}\n".format(SN))
256
+
257
+ # The data arrays
258
+ V = np.abs(mdata["curve"]["V"])
259
+ I = np.abs(mdata["curve"]["I"])
260
+ passed = True
261
+
262
+ # Find Current @ 500V
263
+ indx = np.where(V == 500)[0]
264
+ i_500 = I[indx][0] / area
265
+ if logger:
266
+ logger.write_message("I @ 500V = {:.2f} nA/cm2\n".format(i_500))
267
+
268
+ # Compute current stability
269
+ IStability = abs(I[abs(V) == StabilityV])
270
+ IVariation = -1
271
+ if np.size(IStability) > 1: # Maybe make == 4?
272
+ IVariation = abs(np.std(IStability) / np.mean(IStability))
273
+
274
+ if logger:
275
+ logger.write_message("I stability = {:.6f} nA\n".format(IVariation))
276
+
277
+ # Search for Micro discharges
278
+ # Check for micro-discharge in non-normalized current,
279
+ # removing duplicate Voltage entries (e.g. for stability measurements)
280
+ comments = []
281
+ defects = []
282
+ UniqueVs, UniqueIndices = np.unique(V, return_index=True)
283
+ MicroDischargeV = LocateMicroDischarge(I[UniqueIndices], UniqueVs)
284
+ if MicroDischargeV < np.max(V):
285
+ comments.append("Found micro discharge: {:.1f} V\n".format(MicroDischargeV))
286
+ if logger:
287
+ logger.write_message(comments[-1])
288
+
289
+ if MicroDischargeV < UpperV:
290
+ txt = "microdischarge happening before {:.1f}V.".format(UpperV)
291
+ defects.append({
292
+ "name": "MicroDischarge",
293
+ "description": txt,
294
+ "properties": {}
295
+ }
296
+ )
297
+ if logger:
298
+ logger.write_message("...{}. FAILED\n".format(txt))
299
+
300
+ passed = False
301
+ else:
302
+ if MicroDischargeV == NO_BD_CONST:
303
+ MicroDischargeV = 700.0
304
+
305
+ test["component"] = SN
306
+ test["institution"] = mdata["Institute"]
307
+ test["runNumber"] = mdata["RunNumber"]
308
+ test["date"] = ITkDButils.get_db_date(
309
+ "{} {}".format(mdata["Date"], mdata["Time"])
310
+ )
311
+ test["passed"] = passed
312
+ test["problems"] = False
313
+ test["properties"]["VBIAS_SMU"] = mdata["Vbias_SMU"]
314
+ test["properties"]["RSERIES"] = mdata["Rseries"]
315
+ test["properties"]["TEST_DMM"] = mdata["Test_DMM"]
316
+ test["properties"]["RSHUNT"] = mdata["Rshunt"]
317
+ test["properties"]["RUNNUMBER"] = mdata["RunNumber"]
318
+ test["properties"]["COMMENTS"] = mdata["Comments"]
319
+ test["properties"]["ALGORITHM_VERSION"] = "0.0.0"
320
+ if is_module:
321
+ test["properties"]["SOFTWARE_TYPE_VERSION"] = "pyProbe"
322
+ test["properties"]["MODULE_STAGE"] = mdata["Module_Stage"]
323
+
324
+ test["results"]["TEMPERATURE"] = mdata["Temperature"]
325
+ test["results"]["HUMIDITY"] = mdata["Humidity"]
326
+ test["results"]["VBD"] = MicroDischargeV
327
+ test["results"]["I_500V"] = i_500
328
+ test["results"]["VOLTAGE"] = -np.abs(V)
329
+ test["results"]["CURRENT"] = -np.abs(I)
330
+ test["results"]["RMS_STABILITY"] = IVariation
331
+ test["results"]["SHUNT_VOLTAGE"] = np.zeros(V.shape)
332
+ test["defects"] = defects
333
+ test["comments"] = comments
334
+
335
+ return test
336
+
337
+
338
+ def read_sensor_file(fnam):
339
+ """Read a data file. Return dictionary with all teh data."""
340
+ labels = []
341
+ metadata = {}
342
+ with open(fnam, "r", encoding="utf-8") as ifile:
343
+ first = True
344
+ for line in ifile:
345
+ if first:
346
+ first = False
347
+ ipos = line.rfind(".")
348
+ metadata["fname"] = line[:ipos]
349
+ continue
350
+
351
+ if line.find("Voltage [V]") >= 0 or line.find("Voltage[V]") >= 0:
352
+ labels = line.split("\t")
353
+ break
354
+
355
+ rc = line.find(":")
356
+ if rc >= 0:
357
+ key = line[:rc].strip()
358
+ val = line[rc + 1 :].strip()
359
+ if key in ["Temperature", "Humidity"]:
360
+ metadata[key] = float(val)
361
+ else:
362
+ metadata[key] = val
363
+
364
+ V = []
365
+ I = []
366
+ S = []
367
+ for line in ifile:
368
+ data = [float(s) for s in line.split()]
369
+ V.append(data[0])
370
+ I.append(data[1])
371
+ try:
372
+ S.append(data[2])
373
+ except IndexError:
374
+ S.append(0.0)
375
+
376
+ metadata["curve"] = {
377
+ "V": np.abs(np.array(V)),
378
+ "I": np.abs(np.array(I)),
379
+ "S": np.abs(np.array(S)),
380
+ "labels": labels,
381
+ }
382
+ return metadata
383
+
384
+
385
+ def save_sensor_data(fnam, mdata, name=None):
386
+ """Save sensor dat in file with the proper format.
387
+
388
+ Args:
389
+ fnam: file name or file object.
390
+ mdata (dict): data as returned by :function:`read_sensor_file`
391
+
392
+ """
393
+ if hasattr(fnam, "write") and callable(fnam.write):
394
+ data_out = fnam
395
+ if name:
396
+ fnam = name
397
+ else:
398
+ fnam = build_file_name(mdata)
399
+
400
+ else:
401
+ data_out = open(fnam, 'w', encoding="utf-8")
402
+ fnam = Path(fnam).name
403
+
404
+ is_module = "Module_SN" in mdata
405
+ if is_module:
406
+ SN = mdata["Module_SN"]
407
+ else:
408
+ SN = mdata["Component"]
409
+
410
+ if is_module:
411
+ items = [
412
+ "Type",
413
+ "Wafer",
414
+ "Module_SN",
415
+ "Module_Stage",
416
+ "Date",
417
+ "Time",
418
+ "Institute",
419
+ "TestType",
420
+ "Vbias_SMU",
421
+ "Rseries",
422
+ "Test_DMM",
423
+ "Rshunt",
424
+ "Software type and version, fw version",
425
+ "RunNumber",
426
+ "Temperature",
427
+ "Humidity",
428
+ "Comments",
429
+ ]
430
+ else:
431
+ items = [
432
+ "Type",
433
+ "Batch",
434
+ "Wafer",
435
+ "Component",
436
+ "Date",
437
+ "Time",
438
+ "Institute",
439
+ "TestType",
440
+ "Vbias_SMU",
441
+ "Rseries",
442
+ "Test_DMM",
443
+ "Rshunt",
444
+ "RunNumber",
445
+ "Temperature",
446
+ "Humidity",
447
+ "Comments",
448
+ ]
449
+
450
+ data_out.write("{}\n".format(fnam))
451
+ for key in items:
452
+ if key == "Module_SN" or key == "Component":
453
+ data_out.write("{}: {}\n".format(key, SN))
454
+ else:
455
+ data_out.write("{}: {}\n".format(key, mdata[key]))
456
+
457
+ labels = [lbl for lbl in mdata["curve"]["labels"]]
458
+ if len(labels) < 3:
459
+ labels.append("Shunt_voltage [mV]")
460
+
461
+ for il, label in enumerate(labels):
462
+ if il:
463
+ data_out.write("\t")
464
+ data_out.write(label)
465
+ data_out.write("\n")
466
+
467
+
468
+ # The data arrays
469
+ V = np.abs(mdata["curve"]["V"])
470
+ I = np.abs(mdata["curve"]["I"])
471
+ S = np.abs(mdata["curve"]["S"])
472
+
473
+ ndata = len(V)
474
+ for i in range(ndata):
475
+ data_out.write("{:10.2f}\t{:10.2f}\t{:10.2f}\n".format(V[i], I[i], S[i]))
476
+
477
+ print(data_out.name)
478
+ data_out.close()
479
+
480
+ def build_file_name(mdata):
481
+ """Create a file name from the data."""
482
+ is_module = "Module_SN" in mdata
483
+ if is_module:
484
+ SN = mdata["Module_SN"]
485
+ fnam = "{}_{}_IV_{}".format(SN, mdata["Module_Stage"], mdata["RunNumber"])
486
+ else:
487
+ SN = mdata["Component"]
488
+ fnam = "{}-W{}_IV_{}".format(mdata["Batch"], mdata["Wafer"], mdata["RunNumber"])
489
+
490
+ return fnam