itkdb-gtk 0.0.12__py3-none-any.whl → 0.0.16__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 → GetShipments.py} +9 -8
- itkdb_gtk/GlueWeight.py +8 -5
- itkdb_gtk/{gndVITests.py → GroundVITests.py} +41 -8
- itkdb_gtk/ITkDBlogin.py +1 -0
- itkdb_gtk/ITkDButils.py +6 -2
- itkdb_gtk/{sendShipments.py → SendShipments.py} +9 -7
- itkdb_gtk/ShowAttachments.py +136 -8
- itkdb_gtk/ShowComments.py +12 -3
- itkdb_gtk/ShowDefects.py +17 -5
- itkdb_gtk/UploadModuleIV.py +857 -0
- itkdb_gtk/{uploadMultipleTests.py → UploadMultipleTests.py} +13 -15
- itkdb_gtk/{uploadPetalInformation.py → UploadPetalInformation.py} +9 -9
- itkdb_gtk/{uploadTest.py → UploadTest.py} +87 -49
- itkdb_gtk/WireBondGui.py +863 -0
- itkdb_gtk/__init__.py +19 -11
- itkdb_gtk/dashBoard.py +68 -21
- itkdb_gtk/dbGtkUtils.py +189 -40
- itkdb_gtk/readAVSdata.py +8 -4
- itkdb_gtk/readGoogleSheet.py +1 -1
- itkdb_gtk/untrash_component.py +35 -0
- {itkdb_gtk-0.0.12.dist-info → itkdb_gtk-0.0.16.dist-info}/METADATA +16 -11
- itkdb_gtk-0.0.16.dist-info/RECORD +27 -0
- {itkdb_gtk-0.0.12.dist-info → itkdb_gtk-0.0.16.dist-info}/WHEEL +1 -1
- {itkdb_gtk-0.0.12.dist-info → itkdb_gtk-0.0.16.dist-info}/entry_points.txt +3 -1
- itkdb_gtk/checkComponent.py +0 -151
- itkdb_gtk/checkJSon.py +0 -56
- itkdb_gtk-0.0.12.dist-info/RECORD +0 -26
- {itkdb_gtk-0.0.12.dist-info → itkdb_gtk-0.0.16.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,857 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Read IV files and create plots.
|
|
3
|
+
|
|
4
|
+
Analisis de la IV con macros de la webApp
|
|
5
|
+
|
|
6
|
+
SENSOR_IV_Analysis.py in
|
|
7
|
+
https://gitlab.cern.ch/atlas-itk/sw/db/production_database_scripts.git
|
|
8
|
+
|
|
9
|
+
webApp aqui:
|
|
10
|
+
https://itk-pdb-webapps-strips.web.cern.ch
|
|
11
|
+
|
|
12
|
+
"""
|
|
13
|
+
import os
|
|
14
|
+
import json
|
|
15
|
+
import warnings
|
|
16
|
+
from pathlib import Path
|
|
17
|
+
import shutil
|
|
18
|
+
|
|
19
|
+
import gi
|
|
20
|
+
import tempfile
|
|
21
|
+
|
|
22
|
+
gi.require_version("Gtk", "3.0")
|
|
23
|
+
from gi.repository import Gtk, Gio
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
from matplotlib.backends.backend_gtk3agg import FigureCanvasGTK3Agg as FigureCanvas
|
|
27
|
+
from matplotlib.backends.backend_gtk3 import NavigationToolbar2GTK3 as NavigationToolbar
|
|
28
|
+
import numpy as np
|
|
29
|
+
import matplotlib as mpl
|
|
30
|
+
import matplotlib.pyplot as plt
|
|
31
|
+
|
|
32
|
+
try:
|
|
33
|
+
import itkdb_gtk
|
|
34
|
+
|
|
35
|
+
except ImportError:
|
|
36
|
+
import sys
|
|
37
|
+
from pathlib import Path
|
|
38
|
+
cwd = Path(sys.argv[0]).parent.parent
|
|
39
|
+
sys.path.append(cwd.as_posix())
|
|
40
|
+
|
|
41
|
+
from itkdb_gtk import dbGtkUtils, ITkDBlogin, ITkDButils, UploadTest
|
|
42
|
+
|
|
43
|
+
# Check if Gtk can be open
|
|
44
|
+
gtk_runs, gtk_args = Gtk.init_check()
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def remove_files(W, flist):
|
|
48
|
+
for f in flist:
|
|
49
|
+
os.unlink(f)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
#
|
|
53
|
+
# The following is taken from
|
|
54
|
+
# https://gitlab.cern.ch/atlas-itk/sw/db/production_database_scripts.git
|
|
55
|
+
#
|
|
56
|
+
mm = 1e-3
|
|
57
|
+
cm = 1e-2
|
|
58
|
+
UpperV = 500 # For sensor to pass, I must be < Imax up until this voltage and no breakdown must be detected before then.
|
|
59
|
+
StabilityV = 700 # Voltage with multiple current readings to check stability
|
|
60
|
+
|
|
61
|
+
AreaDict = {
|
|
62
|
+
"Unknown": (97.621 - 0.450) * (97.950 - 0.550) * mm * mm,
|
|
63
|
+
#
|
|
64
|
+
"ATLAS12": 95.7 * 95.64 * mm * mm,
|
|
65
|
+
"ATLAS17LS": (97.621 - 0.450) * (97.950 - 0.550) * mm * mm,
|
|
66
|
+
#
|
|
67
|
+
"ATLAS18R0": 89.9031 * cm * cm,
|
|
68
|
+
"ATLAS18R1": 89.0575 * cm * cm,
|
|
69
|
+
"ATLAS18R2": 74.1855 * cm * cm,
|
|
70
|
+
"ATLAS18R3": 80.1679 * cm * cm,
|
|
71
|
+
"ATLAS18R4": 87.4507 * cm * cm,
|
|
72
|
+
"ATLAS18R5": 91.1268 * cm * cm,
|
|
73
|
+
#
|
|
74
|
+
"ATLAS18SS": 93.6269 * cm * cm,
|
|
75
|
+
"ATLAS18LS": 93.6269 * cm * cm,
|
|
76
|
+
#
|
|
77
|
+
"ATLASDUMMY18": (97.621 - 0.450) * (97.950 - 0.550) * mm * mm,
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def LocateMicroDischarge(
|
|
82
|
+
I,
|
|
83
|
+
V,
|
|
84
|
+
sm_window=2,
|
|
85
|
+
bd_limit=5.5,
|
|
86
|
+
allow_running_bd=True,
|
|
87
|
+
use_additional_cond=False,
|
|
88
|
+
tolerence=0.05,
|
|
89
|
+
voltage_span=4,
|
|
90
|
+
fit_window=5,
|
|
91
|
+
):
|
|
92
|
+
"""
|
|
93
|
+
Function for BDV estimation - if questions please contact Vera Latonova (vera.latonova@cern.ch).
|
|
94
|
+
I,V must have same shape and voltages must be in ascending order,
|
|
95
|
+
same indexes of I&V arrays must correspond each other,
|
|
96
|
+
only invalid data or holdstep should be stripped before
|
|
97
|
+
but it is not necessary. Measurments U=0&I=0 are removed.
|
|
98
|
+
If there is same or higher amount of same voltages in row than
|
|
99
|
+
sm_window, from this sequence we cannot estimete dI/dV and
|
|
100
|
+
we have to remove this averaged point.
|
|
101
|
+
|
|
102
|
+
It is assumed that only parameter use_additional_cond would be
|
|
103
|
+
changed by caller. Changing of other parameters may affect
|
|
104
|
+
BDV unexpectedly.
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
@param[in] I - array of currents without any cut
|
|
108
|
+
@param[in] V - array of voltages, ascending order, without any cut
|
|
109
|
+
@param[in] sm_window - size of smoothing window
|
|
110
|
+
@param[in] bd_limit - BD limit for |U| < 500V
|
|
111
|
+
@param[in] allow_running_bd - allow increase bd_limit for |U| > 500
|
|
112
|
+
@param[in] use_additional_cond - use additional BD contition
|
|
113
|
+
@param[in] tolerence - configuration of additional condition
|
|
114
|
+
@param[in] voltage_span - max width of hump on spectra which may be neglected
|
|
115
|
+
in voltage steps in additional contition
|
|
116
|
+
@param[in] fit_window - number of points used for linear fit before BD voltage
|
|
117
|
+
|
|
118
|
+
@return BD voltage (always positive) or NO_BD_CONST = 9.99e99 if not found.
|
|
119
|
+
"""
|
|
120
|
+
NO_BD_CONST = 9.99e99
|
|
121
|
+
|
|
122
|
+
# add nan to the end of array
|
|
123
|
+
V = np.abs(V)
|
|
124
|
+
I = np.abs(I)
|
|
125
|
+
|
|
126
|
+
# skip zeros
|
|
127
|
+
ind = np.where(np.logical_or(I != 0, V != 0))
|
|
128
|
+
V = V[ind]
|
|
129
|
+
I = I[ind]
|
|
130
|
+
|
|
131
|
+
V_ = np.append(V, np.nan * np.ones(sm_window - 1))
|
|
132
|
+
I_ = np.append(I, np.nan * np.ones(sm_window - 1))
|
|
133
|
+
|
|
134
|
+
# make 2D array of I's, V's each row_ind shifted by row_ind index
|
|
135
|
+
# i.e from array [1,3,5] we make (for sm_window=2) 2D array
|
|
136
|
+
# [ 1,3,5,nan]
|
|
137
|
+
# [nan,5,1,3]
|
|
138
|
+
# than get average from each column -> I_avg, V_avg
|
|
139
|
+
r = np.arange(sm_window)
|
|
140
|
+
|
|
141
|
+
V2 = np.outer(np.ones(sm_window), V_)
|
|
142
|
+
row_ind, col_ind = np.ogrid[: V2.shape[0], : V2.shape[1]]
|
|
143
|
+
col_ind = col_ind - r[:, np.newaxis]
|
|
144
|
+
V2 = V2[row_ind, col_ind]
|
|
145
|
+
# strip fields with nans
|
|
146
|
+
V2 = np.transpose(V2[:, (sm_window - 1) : -(sm_window - 1)])
|
|
147
|
+
|
|
148
|
+
I2 = np.outer(np.ones(sm_window), I_)
|
|
149
|
+
row_ind, col_ind = np.ogrid[: I2.shape[0], : I2.shape[1]]
|
|
150
|
+
col_ind = col_ind - r[:, np.newaxis]
|
|
151
|
+
I2 = I2[row_ind, col_ind]
|
|
152
|
+
I2 = np.transpose(I2[:, (sm_window - 1) : -(sm_window - 1)])
|
|
153
|
+
|
|
154
|
+
# get V & I averages
|
|
155
|
+
try:
|
|
156
|
+
V_avg = np.average(V2, axis=1)
|
|
157
|
+
I_avg = np.average(I2, axis=1)
|
|
158
|
+
except ZeroDivisionError:
|
|
159
|
+
# not enough data
|
|
160
|
+
return NO_BD_CONST
|
|
161
|
+
|
|
162
|
+
# find dI / dV array
|
|
163
|
+
# I'm not able to write this without cycle
|
|
164
|
+
dIdV = np.array([])
|
|
165
|
+
for i in range(V2.shape[0]):
|
|
166
|
+
with warnings.catch_warnings():
|
|
167
|
+
warnings.filterwarnings("error")
|
|
168
|
+
try:
|
|
169
|
+
dIdV = np.append(dIdV, np.polyfit(V2[i, :], I2[i, :], 1)[0])
|
|
170
|
+
except (np.RankWarning, TypeError):
|
|
171
|
+
dIdV = np.append(dIdV, np.nan)
|
|
172
|
+
|
|
173
|
+
# stripping U[n] == U[n+1] (i.e. hodlsetp) => fit cannot be sucessful =>
|
|
174
|
+
# dIdV is nan @holdstep
|
|
175
|
+
ind = np.where(np.isfinite(dIdV))
|
|
176
|
+
I_avg = I_avg[ind]
|
|
177
|
+
V_avg = V_avg[ind]
|
|
178
|
+
dIdV = dIdV[ind]
|
|
179
|
+
|
|
180
|
+
# get running BDV limit & compare
|
|
181
|
+
bd_limit_running = bd_limit + np.where(
|
|
182
|
+
allow_running_bd and V_avg > 500, (V_avg - 500.0) / 100.0, 0
|
|
183
|
+
)
|
|
184
|
+
V_avg_BD_ind = dIdV / (I_avg / V_avg) > bd_limit_running
|
|
185
|
+
V_avg_BD = V_avg[V_avg_BD_ind]
|
|
186
|
+
|
|
187
|
+
# Estimate BDV
|
|
188
|
+
BDV = np.array([])
|
|
189
|
+
|
|
190
|
+
# no break-down
|
|
191
|
+
if V_avg_BD.shape == (0,):
|
|
192
|
+
return NO_BD_CONST
|
|
193
|
+
|
|
194
|
+
# if V_avg_BD_ind[0] == True ... BDV <- V[0]
|
|
195
|
+
# for others V_avg_BD_ind[n] == True BDV <- (V_avg[n] + V_avg[n-1])/2
|
|
196
|
+
if V_avg_BD_ind[0]:
|
|
197
|
+
BDV = np.append(BDV, V[0])
|
|
198
|
+
V_avg_BD_ind[0] = False
|
|
199
|
+
|
|
200
|
+
BDV = np.append(
|
|
201
|
+
BDV,
|
|
202
|
+
(V_avg[np.where(V_avg_BD_ind)] + V_avg[np.where(V_avg_BD_ind)[0] - 1]) / 2.0,
|
|
203
|
+
)
|
|
204
|
+
|
|
205
|
+
###########################################################################
|
|
206
|
+
## Application of additional condition ####################################
|
|
207
|
+
###########################################################################
|
|
208
|
+
if not use_additional_cond:
|
|
209
|
+
return BDV[0]
|
|
210
|
+
|
|
211
|
+
# get index if V <= BDV
|
|
212
|
+
B = np.where(np.less.outer(BDV, V))
|
|
213
|
+
col_ind = np.mgrid[: BDV.shape[0], : V.shape[0]][1]
|
|
214
|
+
col_ind[B[0], B[1]] = 0
|
|
215
|
+
V_BDV_ind = np.max(col_ind, axis=1)
|
|
216
|
+
|
|
217
|
+
back_ok_v_ind = 0
|
|
218
|
+
while True:
|
|
219
|
+
with warnings.catch_warnings():
|
|
220
|
+
warnings.filterwarnings("error")
|
|
221
|
+
try:
|
|
222
|
+
a, b = np.polyfit(
|
|
223
|
+
V[
|
|
224
|
+
max(back_ok_v_ind, V_BDV_ind[0] - fit_window) : max(
|
|
225
|
+
back_ok_v_ind, V_BDV_ind[0]
|
|
226
|
+
)
|
|
227
|
+
],
|
|
228
|
+
I[
|
|
229
|
+
max(back_ok_v_ind, V_BDV_ind[0] - fit_window) : max(
|
|
230
|
+
back_ok_v_ind, V_BDV_ind[0]
|
|
231
|
+
)
|
|
232
|
+
],
|
|
233
|
+
1,
|
|
234
|
+
)
|
|
235
|
+
except (np.RankWarning, TypeError):
|
|
236
|
+
return BDV[0]
|
|
237
|
+
|
|
238
|
+
ind = np.where(1 - (a * V + b) / I <= tolerence)[0]
|
|
239
|
+
try:
|
|
240
|
+
back_ok_v_ind = np.min(ind[ind > V_BDV_ind[0] + 1])
|
|
241
|
+
except ValueError:
|
|
242
|
+
# sensor is not going back
|
|
243
|
+
return BDV[0]
|
|
244
|
+
# hump is too long -- it cannot be skipped
|
|
245
|
+
if back_ok_v_ind - V_BDV_ind[0] > voltage_span:
|
|
246
|
+
return BDV[0]
|
|
247
|
+
|
|
248
|
+
# skip BDVs inside hump
|
|
249
|
+
ind = BDV >= V[back_ok_v_ind]
|
|
250
|
+
BDV = BDV[ind]
|
|
251
|
+
V_BDV_ind = V_BDV_ind[ind]
|
|
252
|
+
if V_avg_BD.shape == (0,):
|
|
253
|
+
return NO_BD_CONST
|
|
254
|
+
return NO_BD_CONST
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
def scale_iv(I, T1, T2):
|
|
258
|
+
"""Normalize corrent to given temperature (T2)
|
|
259
|
+
|
|
260
|
+
Args:
|
|
261
|
+
I (array): Current
|
|
262
|
+
T1 (float): Original temperature
|
|
263
|
+
T2 (float): New temperature.
|
|
264
|
+
|
|
265
|
+
Return:
|
|
266
|
+
Array with scaled currents.
|
|
267
|
+
|
|
268
|
+
"""
|
|
269
|
+
factor = (T2 / T1) ** 2 * np.exp((-1.21 / 8.62) * (1 / T2 - 1 / T1))
|
|
270
|
+
return factor * I
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
class IVwindow(dbGtkUtils.ITkDBWindow):
|
|
274
|
+
"""GUI for IV file handling."""
|
|
275
|
+
|
|
276
|
+
def __init__(self, session, title="IV window", options=None):
|
|
277
|
+
"""Initialization."""
|
|
278
|
+
super().__init__(
|
|
279
|
+
session=session, title=title, show_search=None, gtk_runs=gtk_runs
|
|
280
|
+
)
|
|
281
|
+
self.mdata = {}
|
|
282
|
+
self.mod_type = {}
|
|
283
|
+
self.mod_SN = {}
|
|
284
|
+
self.difference = None
|
|
285
|
+
self.canvas = None
|
|
286
|
+
|
|
287
|
+
self.init_window()
|
|
288
|
+
|
|
289
|
+
def init_window(self):
|
|
290
|
+
"""Prepare the Gtk window."""
|
|
291
|
+
self.hb.props.title = "IV data"
|
|
292
|
+
|
|
293
|
+
button = Gtk.Button()
|
|
294
|
+
icon = Gio.ThemedIcon(name="view-refresh-symbolic")
|
|
295
|
+
image = Gtk.Image.new_from_gicon(icon, Gtk.IconSize.BUTTON)
|
|
296
|
+
button.add(image)
|
|
297
|
+
button.set_tooltip_text("Click to refresh canvas.")
|
|
298
|
+
button.connect("clicked", self.on_refresh)
|
|
299
|
+
self.hb.pack_end(button)
|
|
300
|
+
|
|
301
|
+
# Button to upload
|
|
302
|
+
button = Gtk.Button()
|
|
303
|
+
icon = Gio.ThemedIcon(name="document-send-symbolic")
|
|
304
|
+
image = Gtk.Image.new_from_gicon(icon, Gtk.IconSize.BUTTON)
|
|
305
|
+
button.add(image)
|
|
306
|
+
button.set_tooltip_text("Click to upload test")
|
|
307
|
+
button.connect("clicked", self.upload_test)
|
|
308
|
+
self.hb.pack_end(button)
|
|
309
|
+
|
|
310
|
+
# File entry and search button
|
|
311
|
+
self.single_file = Gtk.FileChooserButton()
|
|
312
|
+
self.single_file.connect("file-set", self.on_single_file_set)
|
|
313
|
+
|
|
314
|
+
self.double_file = Gtk.FileChooserButton()
|
|
315
|
+
self.double_file.connect("file-set", self.on_double_file_set)
|
|
316
|
+
|
|
317
|
+
self.single_SN = Gtk.Label(label="(None)")
|
|
318
|
+
self.double_SN = Gtk.Label(label="(None)")
|
|
319
|
+
|
|
320
|
+
grid = Gtk.Grid(column_spacing=5, row_spacing=1)
|
|
321
|
+
|
|
322
|
+
grid.attach(Gtk.Label(label="Files"), 1, 0, 1, 1)
|
|
323
|
+
grid.attach(Gtk.Label(label="Serial No."), 2, 0, 1, 1)
|
|
324
|
+
|
|
325
|
+
grid.attach(Gtk.Label(label="Single Data File"), 0, 1, 1, 1)
|
|
326
|
+
grid.attach(self.single_file, 1, 1, 1, 1)
|
|
327
|
+
grid.attach(self.single_SN, 2, 1, 1, 1)
|
|
328
|
+
|
|
329
|
+
grid.attach(Gtk.Label(label="Double Data File"), 0, 2, 1, 1)
|
|
330
|
+
grid.attach(self.double_file, 1, 2, 1, 1)
|
|
331
|
+
grid.attach(self.double_SN, 2, 2, 1, 1)
|
|
332
|
+
|
|
333
|
+
btn = Gtk.Button(label="Compute difference")
|
|
334
|
+
btn.connect("clicked", self.on_difference)
|
|
335
|
+
grid.attach(btn, 1, 3, 1, 1)
|
|
336
|
+
|
|
337
|
+
btn = Gtk.Button(label="Upload to DB")
|
|
338
|
+
btn.connect("clicked", self.upload_test)
|
|
339
|
+
grid.attach(btn, 2, 3, 1, 1)
|
|
340
|
+
|
|
341
|
+
self.mainBox.pack_start(grid, False, True, 0)
|
|
342
|
+
|
|
343
|
+
self.fig = mpl.figure.Figure()
|
|
344
|
+
self.fig.tight_layout()
|
|
345
|
+
sw = Gtk.ScrolledWindow() # Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
|
|
346
|
+
|
|
347
|
+
# A scrolled window border goes outside the scrollbars and viewport
|
|
348
|
+
sw.set_border_width(10)
|
|
349
|
+
sw.set_size_request(310, 310)
|
|
350
|
+
|
|
351
|
+
self.canvas = FigureCanvas(self.fig) # a Gtk.DrawingArea
|
|
352
|
+
self.canvas.set_size_request(400, 300)
|
|
353
|
+
sw.add(self.canvas)
|
|
354
|
+
self.mainBox.pack_start(sw, True, True, 0)
|
|
355
|
+
|
|
356
|
+
# Create toolbar
|
|
357
|
+
try:
|
|
358
|
+
toolbar = NavigationToolbar(self.canvas)
|
|
359
|
+
except TypeError:
|
|
360
|
+
toolbar = NavigationToolbar(self.canvas, self)
|
|
361
|
+
|
|
362
|
+
self.mainBox.pack_start(toolbar, False, False, 0)
|
|
363
|
+
|
|
364
|
+
# The text view
|
|
365
|
+
self.mainBox.pack_start(self.message_panel.frame, True, True, 5)
|
|
366
|
+
|
|
367
|
+
self.show_all()
|
|
368
|
+
|
|
369
|
+
def upload_test(self, *args):
|
|
370
|
+
"""Upload available tests."""
|
|
371
|
+
try:
|
|
372
|
+
mdata = self.mdata["double"]
|
|
373
|
+
except KeyError:
|
|
374
|
+
dbGtkUtils.complain("Cannot Upload to DB", "Data not yet ready.")
|
|
375
|
+
return
|
|
376
|
+
|
|
377
|
+
# Check if we are dealing with sensors or modules
|
|
378
|
+
is_module = "Module_SN" in mdata
|
|
379
|
+
|
|
380
|
+
if is_module:
|
|
381
|
+
test = ITkDButils.get_test_skeleton(
|
|
382
|
+
self.session, "MODULE", self.mdata["double"]["TestType"]
|
|
383
|
+
)
|
|
384
|
+
tp = "ATLAS18{}".format(self.mod_type["double"][0:2])
|
|
385
|
+
area = AreaDict[tp] / cm**2
|
|
386
|
+
|
|
387
|
+
else:
|
|
388
|
+
test = ITkDButils.get_test_skeleton(
|
|
389
|
+
self.session, "SENSOR", self.mdata["double"]["TestType"]
|
|
390
|
+
)
|
|
391
|
+
area = AreaDict[self.mod_type["double"]] / cm**2
|
|
392
|
+
|
|
393
|
+
# The data arrays
|
|
394
|
+
V = np.abs(mdata["curve"]["V"])
|
|
395
|
+
I = np.abs(mdata["curve"]["I"])
|
|
396
|
+
passed = True
|
|
397
|
+
|
|
398
|
+
# Find Current @ 500V
|
|
399
|
+
indx = np.where(V == 500)[0]
|
|
400
|
+
i_500 = I[indx][0] / area
|
|
401
|
+
self.write_message("I @ 500V = {:.2f} nA/cm2\n".format(i_500))
|
|
402
|
+
|
|
403
|
+
# Compute current stability
|
|
404
|
+
IStability = abs(I[abs(V) == StabilityV])
|
|
405
|
+
IVariation = -1
|
|
406
|
+
if np.size(IStability) > 1: # Maybe make == 4?
|
|
407
|
+
IVariation = abs(np.std(IStability) / np.mean(IStability))
|
|
408
|
+
|
|
409
|
+
self.write_message("I stability = {:.6f} nA\n".format(IVariation))
|
|
410
|
+
|
|
411
|
+
# Search for Micro discharges
|
|
412
|
+
# Check for micro-discharge in non-normalized current,
|
|
413
|
+
# removing duplicate Voltage entries (e.g. for stability measurements)
|
|
414
|
+
comments = []
|
|
415
|
+
defects = []
|
|
416
|
+
UniqueVs, UniqueIndices = np.unique(V, return_index=True)
|
|
417
|
+
MicroDischargeV = LocateMicroDischarge(I[UniqueIndices], UniqueVs)
|
|
418
|
+
if MicroDischargeV < np.max(V):
|
|
419
|
+
comments.append("Found micro discharge: {:.1f} V\n".format(MicroDischargeV))
|
|
420
|
+
self.write_message(comments[-1])
|
|
421
|
+
|
|
422
|
+
if MicroDischargeV < UpperV:
|
|
423
|
+
txt = "microdischarge happening before {:.1f}V.".format(UpperV)
|
|
424
|
+
defects.append({
|
|
425
|
+
"name": "MicroDischarge",
|
|
426
|
+
"description": txt,
|
|
427
|
+
"properties": {}
|
|
428
|
+
}
|
|
429
|
+
)
|
|
430
|
+
self.write_message("...{}. FAILED\n".format(txt))
|
|
431
|
+
passed = False
|
|
432
|
+
|
|
433
|
+
test["component"] = self.mod_SN["double"]
|
|
434
|
+
test["institution"] = mdata["Institute"]
|
|
435
|
+
test["runNumber"] = mdata["RunNumber"]
|
|
436
|
+
test["date"] = ITkDButils.get_db_date(
|
|
437
|
+
"{} {}".format(mdata["Date"], mdata["Time"])
|
|
438
|
+
)
|
|
439
|
+
test["passed"] = passed
|
|
440
|
+
test["problems"] = False
|
|
441
|
+
test["properties"]["VBIAS_SMU"] = mdata["Vbias_SMU"]
|
|
442
|
+
test["properties"]["RSERIES"] = mdata["Rseries"]
|
|
443
|
+
test["properties"]["TEST_DMM"] = mdata["Test_DMM"]
|
|
444
|
+
test["properties"]["RSHUNT"] = mdata["Rshunt"]
|
|
445
|
+
test["properties"]["RUNNUMBER"] = mdata["RunNumber"]
|
|
446
|
+
test["properties"]["COMMENTS"] = mdata["Comments"]
|
|
447
|
+
test["properties"]["ALGORITHM_VERSION"] = "0.0.0"
|
|
448
|
+
if is_module:
|
|
449
|
+
test["properties"]["SOFTWARE_TYPE_VERSION"] = "pyProbe"
|
|
450
|
+
test["properties"]["MODULE_STAGE"] = mdata["Module_Stage"]
|
|
451
|
+
|
|
452
|
+
test["results"]["TEMPERATURE"] = mdata["Temperature"]
|
|
453
|
+
test["results"]["HUMIDITY"] = mdata["Humidity"]
|
|
454
|
+
test["results"]["VBD"] = MicroDischargeV
|
|
455
|
+
test["results"]["I_500V"] = i_500
|
|
456
|
+
test["results"]["VOLTAGE"] = -np.abs(V)
|
|
457
|
+
test["results"]["CURRENT"] = -np.abs(I)
|
|
458
|
+
test["results"]["RMS_STABILITY"] = IVariation
|
|
459
|
+
test["results"]["SHUNT_VOLTAGE"] = np.zeros(V.shape)
|
|
460
|
+
test["defects"] = defects
|
|
461
|
+
test["comments"] = comments
|
|
462
|
+
|
|
463
|
+
# write attachment.
|
|
464
|
+
if is_module:
|
|
465
|
+
items = [
|
|
466
|
+
"Type",
|
|
467
|
+
"Wafer",
|
|
468
|
+
"Module_SN",
|
|
469
|
+
"Module_Stage",
|
|
470
|
+
"Date",
|
|
471
|
+
"Time",
|
|
472
|
+
"Institute",
|
|
473
|
+
"TestType",
|
|
474
|
+
"Vbias_SMU",
|
|
475
|
+
"Rseries",
|
|
476
|
+
"Test_DMM",
|
|
477
|
+
"Rshunt",
|
|
478
|
+
"Software type and version, fw version",
|
|
479
|
+
"RunNumber",
|
|
480
|
+
"Temperature",
|
|
481
|
+
"Humidity",
|
|
482
|
+
"Comments",
|
|
483
|
+
]
|
|
484
|
+
else:
|
|
485
|
+
items = [
|
|
486
|
+
"Type",
|
|
487
|
+
"Batch",
|
|
488
|
+
"Wafer",
|
|
489
|
+
"Component",
|
|
490
|
+
"Date",
|
|
491
|
+
"Time",
|
|
492
|
+
"Institute",
|
|
493
|
+
"TestType",
|
|
494
|
+
"Vbias_SMU",
|
|
495
|
+
"Rseries",
|
|
496
|
+
"Test_DMM",
|
|
497
|
+
"Rshunt",
|
|
498
|
+
"RunNumber",
|
|
499
|
+
"Temperature",
|
|
500
|
+
"Humidity",
|
|
501
|
+
"Comments",
|
|
502
|
+
]
|
|
503
|
+
|
|
504
|
+
try:
|
|
505
|
+
fnam = "{}_{}_IV_{}-".format(
|
|
506
|
+
self.mod_SN["double"], mdata["Module_Stage"], mdata["RunNumber"]
|
|
507
|
+
)
|
|
508
|
+
except KeyError:
|
|
509
|
+
fnam = "{}_W_IV_{}".format(self.mod_SN["double"], mdata["RunNumber"])
|
|
510
|
+
|
|
511
|
+
data_out = tempfile.NamedTemporaryFile(
|
|
512
|
+
"w", prefix=fnam, suffix=".dat", delete=False
|
|
513
|
+
)
|
|
514
|
+
data_out.write("{}\n".format(fnam))
|
|
515
|
+
for key in items:
|
|
516
|
+
if key == "Module_SN" or key == "Component":
|
|
517
|
+
data_out.write("{}: {}\n".format(key, self.mod_SN["double"]))
|
|
518
|
+
else:
|
|
519
|
+
data_out.write("{}: {}\n".format(key, mdata[key]))
|
|
520
|
+
|
|
521
|
+
for il, label in enumerate(mdata["curve"]["labels"]):
|
|
522
|
+
if il:
|
|
523
|
+
data_out.write("\t")
|
|
524
|
+
data_out.write(label)
|
|
525
|
+
data_out.write("\n")
|
|
526
|
+
|
|
527
|
+
ncol = len(mdata["curve"]["labels"])
|
|
528
|
+
ndata = len(self.difference)
|
|
529
|
+
for i in range(ndata):
|
|
530
|
+
v = -abs(V[i])
|
|
531
|
+
iv = -abs(self.difference[i])
|
|
532
|
+
if ncol > 2:
|
|
533
|
+
data_out.write("{:.2f}\t{:.2f}\t{:.2f}\n".format(v, iv, 0.0))
|
|
534
|
+
else:
|
|
535
|
+
data_out.write("{:.2f}\t{:.2f}\n".format(v, iv))
|
|
536
|
+
|
|
537
|
+
print(data_out.name)
|
|
538
|
+
data_out.close()
|
|
539
|
+
|
|
540
|
+
js_out = tempfile.NamedTemporaryFile(
|
|
541
|
+
"w", prefix="payload-", suffix=".json", delete=False
|
|
542
|
+
)
|
|
543
|
+
js_out.write(json.dumps(test, indent=3, cls=dbGtkUtils.MyEncoder))
|
|
544
|
+
js_out.close()
|
|
545
|
+
|
|
546
|
+
if dbGtkUtils.ask_for_confirmation("Save Data File locally?", "Saves attached file also locally."):
|
|
547
|
+
fc = Gtk.FileChooserDialog(title="Save data file", action=Gtk.FileChooserAction.SAVE)
|
|
548
|
+
fc.add_buttons(
|
|
549
|
+
Gtk.STOCK_CANCEL,
|
|
550
|
+
Gtk.ResponseType.CANCEL,
|
|
551
|
+
Gtk.STOCK_OPEN,
|
|
552
|
+
Gtk.ResponseType.OK,
|
|
553
|
+
)
|
|
554
|
+
|
|
555
|
+
fc.set_current_name("{}.dat".format(fnam))
|
|
556
|
+
rc = fc.run()
|
|
557
|
+
if rc == Gtk.ResponseType.OK:
|
|
558
|
+
shutil.copyfile(data_out.name, fc.get_filename())
|
|
559
|
+
|
|
560
|
+
fc.hide()
|
|
561
|
+
fc.destroy()
|
|
562
|
+
|
|
563
|
+
attachment = ITkDButils.Attachment(data_out.name, "resultsFile", fnam)
|
|
564
|
+
uploadW = UploadTest.UploadTest(self.session, js_out.name, attachment)
|
|
565
|
+
uploadW.connect("destroy", remove_files, [data_out.name, js_out.name])
|
|
566
|
+
|
|
567
|
+
def on_refresh(self, *args):
|
|
568
|
+
"""Refresh canvas."""
|
|
569
|
+
if self.fig and self.canvas:
|
|
570
|
+
self.fig.tight_layout()
|
|
571
|
+
self.canvas.draw()
|
|
572
|
+
|
|
573
|
+
def find_module(self, SN):
|
|
574
|
+
"""Find module (SN) on database
|
|
575
|
+
|
|
576
|
+
Args:
|
|
577
|
+
----
|
|
578
|
+
SN (str): Module Serial number
|
|
579
|
+
"""
|
|
580
|
+
md = ITkDButils.get_DB_component(self.session, SN)
|
|
581
|
+
if md is None:
|
|
582
|
+
dbGtkUtils.complain(
|
|
583
|
+
"Could not find {}".format(SN), str(ITkDButils.get_db_response())
|
|
584
|
+
)
|
|
585
|
+
|
|
586
|
+
return md
|
|
587
|
+
|
|
588
|
+
def on_single_file_set(self, *args):
|
|
589
|
+
"""File chosen."""
|
|
590
|
+
obj_type = ["sensor", "module"]
|
|
591
|
+
fnam = self.single_file.get_filename()
|
|
592
|
+
if fnam is None or not Path(fnam).exists():
|
|
593
|
+
dbGtkUtils.complain("Could not find data file", fnam, parent=self)
|
|
594
|
+
|
|
595
|
+
mdata = self.read_file(fnam)
|
|
596
|
+
|
|
597
|
+
is_module = 1
|
|
598
|
+
try:
|
|
599
|
+
SN = mdata["Module_SN"]
|
|
600
|
+
except KeyError:
|
|
601
|
+
SN = mdata["Component"]
|
|
602
|
+
is_module = 0
|
|
603
|
+
|
|
604
|
+
self.write_message("Reading data for {} {}\n".format(obj_type[is_module], SN))
|
|
605
|
+
md = self.find_module(SN)
|
|
606
|
+
if md is None:
|
|
607
|
+
self.write_message("...object does not exist.\n")
|
|
608
|
+
self.single_file.unselect_all()
|
|
609
|
+
return
|
|
610
|
+
|
|
611
|
+
# All good
|
|
612
|
+
self.mod_SN["single"] = SN
|
|
613
|
+
self.mdata["single"] = mdata
|
|
614
|
+
self.mod_type["single"] = md["type"]["code"]
|
|
615
|
+
print(self.mod_type["single"])
|
|
616
|
+
|
|
617
|
+
self.single_SN.set_text("{} - {}".format(SN, md["type"]["name"]))
|
|
618
|
+
self.fig.clf()
|
|
619
|
+
self.show_curve(
|
|
620
|
+
131,
|
|
621
|
+
mdata["curve"]["V"],
|
|
622
|
+
mdata["curve"]["I"],
|
|
623
|
+
self.mod_type["single"][0:4] if is_module else "Single",
|
|
624
|
+
mdata["curve"]["labels"][0],
|
|
625
|
+
mdata["curve"]["labels"][1],
|
|
626
|
+
)
|
|
627
|
+
|
|
628
|
+
def on_double_file_set(self, *args):
|
|
629
|
+
"File chosen for the 'double module'"
|
|
630
|
+
obj_type = ["sensor", "module"]
|
|
631
|
+
fnam = self.double_file.get_filename()
|
|
632
|
+
if fnam is None or not Path(fnam).exists():
|
|
633
|
+
dbGtkUtils.complain("Could not find data file", fnam, parent=self)
|
|
634
|
+
|
|
635
|
+
mdata = self.read_file(fnam)
|
|
636
|
+
is_module = 1
|
|
637
|
+
# Check SN in data file
|
|
638
|
+
try:
|
|
639
|
+
SN = mdata["Module_SN"]
|
|
640
|
+
except KeyError:
|
|
641
|
+
is_module = 0
|
|
642
|
+
SN = mdata["Component"]
|
|
643
|
+
|
|
644
|
+
halfM_SN = SN
|
|
645
|
+
if "single" in self.mod_SN:
|
|
646
|
+
if self.mod_SN["single"] == SN:
|
|
647
|
+
dbGtkUtils.complain(
|
|
648
|
+
"Wrong {}} SN", "{} already used.".format(obj_type[is_module], SN)
|
|
649
|
+
)
|
|
650
|
+
self.double_file.unselect_all()
|
|
651
|
+
return
|
|
652
|
+
|
|
653
|
+
# Check that it exists in the DB
|
|
654
|
+
if len(SN) != 14 or SN[0:4] != "20US":
|
|
655
|
+
self.write_message("Invalid SN: {}\n".format(SN))
|
|
656
|
+
SN = dbGtkUtils.get_a_value(
|
|
657
|
+
"Invalid SN", "Give Ring or corresponding Half Module SN"
|
|
658
|
+
)
|
|
659
|
+
|
|
660
|
+
self.write_message("Reading data for module {}\n".format(SN))
|
|
661
|
+
md = self.find_module(SN)
|
|
662
|
+
if md is None:
|
|
663
|
+
self.write_message("...object does not exist.\n")
|
|
664
|
+
self.double_file.unselect_all()
|
|
665
|
+
return
|
|
666
|
+
|
|
667
|
+
found_child = False
|
|
668
|
+
if md["type"]["name"].find("Ring") >= 0:
|
|
669
|
+
self.write_message("...This is a Ring module. Searching children in DB\n")
|
|
670
|
+
for child in md["children"]:
|
|
671
|
+
if child["component"]:
|
|
672
|
+
ctype = child["type"]["code"]
|
|
673
|
+
if ctype.find("MODULE") < 0:
|
|
674
|
+
continue
|
|
675
|
+
|
|
676
|
+
cSN = child["component"]["serialNumber"]
|
|
677
|
+
if cSN == self.mod_SN["single"]:
|
|
678
|
+
continue
|
|
679
|
+
|
|
680
|
+
halfM_SN = cSN
|
|
681
|
+
found_child = True
|
|
682
|
+
self.write_message("...found {}\n".format(halfM_SN))
|
|
683
|
+
break
|
|
684
|
+
|
|
685
|
+
if not found_child:
|
|
686
|
+
self.write_message("Requesting a Half Module SN\n")
|
|
687
|
+
halfM_SN = dbGtkUtils.get_a_value(
|
|
688
|
+
"Give Half Module SN", "Serial Number"
|
|
689
|
+
)
|
|
690
|
+
|
|
691
|
+
md = ITkDButils.get_DB_component(self.session, halfM_SN)
|
|
692
|
+
if md is None:
|
|
693
|
+
dbGtkUtils.complain(
|
|
694
|
+
"Could not find {}".format(halfM_SN),
|
|
695
|
+
str(ITkDButils.get_db_response()),
|
|
696
|
+
)
|
|
697
|
+
self.double_file.unselect_all()
|
|
698
|
+
return
|
|
699
|
+
|
|
700
|
+
self.write_message("... {}\n".format(halfM_SN))
|
|
701
|
+
|
|
702
|
+
if "single" in self.mod_type:
|
|
703
|
+
if is_module and self.mod_type["single"] == md["type"]["code"]:
|
|
704
|
+
dbGtkUtils.complain(
|
|
705
|
+
"Wrong module type.",
|
|
706
|
+
"Module type cannot be {}".format(self.mod_type["single"]),
|
|
707
|
+
)
|
|
708
|
+
|
|
709
|
+
self.double_file.unselect_all()
|
|
710
|
+
return
|
|
711
|
+
|
|
712
|
+
self.mod_SN["double"] = halfM_SN
|
|
713
|
+
self.mod_type["double"] = md["type"]["code"]
|
|
714
|
+
self.mdata["double"] = mdata
|
|
715
|
+
|
|
716
|
+
self.double_SN.set_text("{} - {}".format(halfM_SN, md["type"]["name"]))
|
|
717
|
+
self.show_curve(
|
|
718
|
+
133,
|
|
719
|
+
mdata["curve"]["V"],
|
|
720
|
+
mdata["curve"]["I"],
|
|
721
|
+
"Double",
|
|
722
|
+
mdata["curve"]["labels"][0],
|
|
723
|
+
None,
|
|
724
|
+
)
|
|
725
|
+
|
|
726
|
+
# Compute difference if single already available
|
|
727
|
+
if "single" in self.mdata:
|
|
728
|
+
self.on_difference()
|
|
729
|
+
|
|
730
|
+
def on_difference(self, *args):
|
|
731
|
+
"""Compute difference."""
|
|
732
|
+
if "single" not in self.mdata or "double" not in self.mdata:
|
|
733
|
+
dbGtkUtils.complain(
|
|
734
|
+
"Data needed", "Check if single oand doubel module data are available"
|
|
735
|
+
)
|
|
736
|
+
return
|
|
737
|
+
|
|
738
|
+
is_module = "Module_SN" in self.mdata["double"]
|
|
739
|
+
double_I = self.mdata["double"]["curve"]["I"]
|
|
740
|
+
single_I = scale_iv(
|
|
741
|
+
self.mdata["single"]["curve"]["I"],
|
|
742
|
+
self.mdata["single"]["Temperature"] + 273.0,
|
|
743
|
+
self.mdata["double"]["Temperature"] + 273.0,
|
|
744
|
+
)
|
|
745
|
+
|
|
746
|
+
try:
|
|
747
|
+
nmin = double_I.size
|
|
748
|
+
self.difference = double_I - single_I
|
|
749
|
+
except ValueError:
|
|
750
|
+
nmin = np.min([double_I.size, single_I.size])
|
|
751
|
+
self.write_message(
|
|
752
|
+
"Size of current arrays is not the same: {} {}\n".format(
|
|
753
|
+
double_I.size, single_I.size
|
|
754
|
+
)
|
|
755
|
+
)
|
|
756
|
+
self.difference = double_I[:nmin] - single_I[:nmin]
|
|
757
|
+
|
|
758
|
+
self.show_curve(
|
|
759
|
+
132,
|
|
760
|
+
self.mdata["double"]["curve"]["V"][:nmin],
|
|
761
|
+
self.difference,
|
|
762
|
+
self.mod_type["double"][0:4] if is_module else "Diff",
|
|
763
|
+
self.mdata["double"]["curve"]["labels"][0],
|
|
764
|
+
None,
|
|
765
|
+
)
|
|
766
|
+
|
|
767
|
+
def show_curve(self, subplot, X, Y, title=None, xlabel="X", ylabel="Y"):
|
|
768
|
+
"""Shows data"""
|
|
769
|
+
ax = self.fig.add_subplot(subplot)
|
|
770
|
+
plt.cla()
|
|
771
|
+
if xlabel:
|
|
772
|
+
ax.set_xlabel(xlabel)
|
|
773
|
+
|
|
774
|
+
if ylabel:
|
|
775
|
+
ax.set_ylabel(ylabel)
|
|
776
|
+
|
|
777
|
+
if title:
|
|
778
|
+
ax.set_title(title)
|
|
779
|
+
|
|
780
|
+
ax.plot(X, Y)
|
|
781
|
+
ax.grid()
|
|
782
|
+
self.on_refresh()
|
|
783
|
+
|
|
784
|
+
@staticmethod
|
|
785
|
+
def read_file(fnam):
|
|
786
|
+
"""Read a data file. Return dictionary with all teh data."""
|
|
787
|
+
labels = []
|
|
788
|
+
metadata = {}
|
|
789
|
+
with open(fnam, "r", encoding="utf-8") as ifile:
|
|
790
|
+
first = True
|
|
791
|
+
for line in ifile:
|
|
792
|
+
if first:
|
|
793
|
+
first = False
|
|
794
|
+
ipos = line.rfind(".")
|
|
795
|
+
metadata["fname"] = line[:ipos]
|
|
796
|
+
continue
|
|
797
|
+
|
|
798
|
+
if line.find("Voltage [V]") >= 0 or line.find("Voltage[V]") >= 0:
|
|
799
|
+
labels = line.split("\t")
|
|
800
|
+
break
|
|
801
|
+
|
|
802
|
+
rc = line.find(":")
|
|
803
|
+
if rc >= 0:
|
|
804
|
+
key = line[:rc].strip()
|
|
805
|
+
val = line[rc + 1 :].strip()
|
|
806
|
+
if key in ["Temperature", "Humidity"]:
|
|
807
|
+
metadata[key] = float(val)
|
|
808
|
+
else:
|
|
809
|
+
metadata[key] = val
|
|
810
|
+
|
|
811
|
+
V = []
|
|
812
|
+
I = []
|
|
813
|
+
for line in ifile:
|
|
814
|
+
data = [float(s) for s in line.split()]
|
|
815
|
+
V.append(data[0])
|
|
816
|
+
I.append(data[1])
|
|
817
|
+
|
|
818
|
+
metadata["curve"] = {
|
|
819
|
+
"V": np.abs(np.array(V)),
|
|
820
|
+
"I": np.abs(np.array(I)),
|
|
821
|
+
"labels": labels[0:2],
|
|
822
|
+
}
|
|
823
|
+
return metadata
|
|
824
|
+
|
|
825
|
+
|
|
826
|
+
def main():
|
|
827
|
+
"""Main entryy."""
|
|
828
|
+
import sys
|
|
829
|
+
|
|
830
|
+
# DB login
|
|
831
|
+
dlg = ITkDBlogin.ITkDBlogin()
|
|
832
|
+
client = dlg.get_client()
|
|
833
|
+
if client is None:
|
|
834
|
+
print("Could not connect to DB with provided credentials.")
|
|
835
|
+
dlg.die()
|
|
836
|
+
sys.exit()
|
|
837
|
+
|
|
838
|
+
client.user_gui = dlg
|
|
839
|
+
|
|
840
|
+
# Start the Application
|
|
841
|
+
win = IVwindow(client)
|
|
842
|
+
win.show_all()
|
|
843
|
+
win.set_accept_focus(True)
|
|
844
|
+
win.present()
|
|
845
|
+
win.connect("destroy", Gtk.main_quit)
|
|
846
|
+
|
|
847
|
+
try:
|
|
848
|
+
Gtk.main()
|
|
849
|
+
except KeyboardInterrupt:
|
|
850
|
+
print("Arrggggg !!!")
|
|
851
|
+
|
|
852
|
+
dlg.die()
|
|
853
|
+
print("Bye !!")
|
|
854
|
+
sys.exit()
|
|
855
|
+
|
|
856
|
+
if __name__ == "__main__":
|
|
857
|
+
main()
|