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