petal-qc 0.0.0__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 petal-qc might be problematic. Click here for more details.

Files changed (51) hide show
  1. petal_qc/BTreport/CheckBTtests.py +321 -0
  2. petal_qc/BTreport/__init__.py +0 -0
  3. petal_qc/BTreport/bustapeReport.py +144 -0
  4. petal_qc/__init__.py +14 -0
  5. petal_qc/metrology/Cluster.py +90 -0
  6. petal_qc/metrology/DataFile.py +47 -0
  7. petal_qc/metrology/PetalMetrology.py +327 -0
  8. petal_qc/metrology/__init__.py +0 -0
  9. petal_qc/metrology/all2csv.py +57 -0
  10. petal_qc/metrology/analyze_locking_points.py +597 -0
  11. petal_qc/metrology/cold_noise.py +106 -0
  12. petal_qc/metrology/comparisonTable.py +59 -0
  13. petal_qc/metrology/convert_mitutoyo.py +175 -0
  14. petal_qc/metrology/convert_smartscope.py +145 -0
  15. petal_qc/metrology/coreMetrology.py +402 -0
  16. petal_qc/metrology/data2csv.py +63 -0
  17. petal_qc/metrology/do_Metrology.py +117 -0
  18. petal_qc/metrology/flatness4nigel.py +157 -0
  19. petal_qc/metrology/gtkutils.py +120 -0
  20. petal_qc/metrology/petal_flatness.py +353 -0
  21. petal_qc/metrology/show_data_file.py +118 -0
  22. petal_qc/metrology/testSummary.py +37 -0
  23. petal_qc/metrology/test_paralelism.py +71 -0
  24. petal_qc/thermal/CSVImage.py +69 -0
  25. petal_qc/thermal/DebugPlot.py +76 -0
  26. petal_qc/thermal/IRBFile.py +768 -0
  27. petal_qc/thermal/IRCore.py +110 -0
  28. petal_qc/thermal/IRDataGetter.py +359 -0
  29. petal_qc/thermal/IRPetal.py +1338 -0
  30. petal_qc/thermal/IRPetalParam.py +71 -0
  31. petal_qc/thermal/PetalColorMaps.py +62 -0
  32. petal_qc/thermal/Petal_IR_Analysis.py +142 -0
  33. petal_qc/thermal/PipeFit.py +598 -0
  34. petal_qc/thermal/__init__.py +0 -0
  35. petal_qc/thermal/analyze_IRCore.py +417 -0
  36. petal_qc/thermal/contours.py +378 -0
  37. petal_qc/thermal/create_IRCore.py +185 -0
  38. petal_qc/thermal/pipe_read.py +182 -0
  39. petal_qc/thermal/show_IR_petal.py +420 -0
  40. petal_qc/utils/Geometry.py +756 -0
  41. petal_qc/utils/Progress.py +182 -0
  42. petal_qc/utils/__init__.py +0 -0
  43. petal_qc/utils/all_files.py +35 -0
  44. petal_qc/utils/docx_utils.py +186 -0
  45. petal_qc/utils/fit_utils.py +188 -0
  46. petal_qc/utils/utils.py +180 -0
  47. petal_qc-0.0.0.dist-info/METADATA +29 -0
  48. petal_qc-0.0.0.dist-info/RECORD +51 -0
  49. petal_qc-0.0.0.dist-info/WHEEL +5 -0
  50. petal_qc-0.0.0.dist-info/entry_points.txt +3 -0
  51. petal_qc-0.0.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,378 @@
1
+ """Utilities for contours.
2
+
3
+ A Contour is a a list of 2D points tha tdefine a curve or contour.
4
+ This provides aset of utilities to operate on these lists.
5
+
6
+ """
7
+ import random
8
+ import sys
9
+
10
+ import matplotlib.path as mplPath
11
+ import numpy as np
12
+
13
+ from petal_qc.utils.Geometry import Point
14
+
15
+
16
+ def pldist(point, start, end):
17
+ """Return distance of point to line formed by start-end.
18
+
19
+ Args:
20
+ ----
21
+ point: The point
22
+ start: Starting point of line
23
+ end: End point of line
24
+
25
+ """
26
+ if np.all(np.equal(start, end)):
27
+ return np.linalg.norm(point - start)
28
+
29
+ return np.divide(np.abs(np.linalg.norm(np.cross(end - start, start - point))),
30
+ np.linalg.norm(end - start))
31
+
32
+
33
+ def closest_segment_point(P, A, B):
34
+ """Return distance to segment and closest point in segment.
35
+
36
+ Args:
37
+ ----
38
+ P: point
39
+ A: start of segment
40
+ B: end of segment
41
+
42
+ """
43
+ try:
44
+ M = B - A
45
+
46
+ except TypeError:
47
+ pass
48
+
49
+ t0 = np.dot(P-A, M)/np.dot(M, M)
50
+ C = A + t0*M
51
+ d = np.linalg.norm(P-C)
52
+
53
+ return d, C
54
+
55
+
56
+ def find_closest(x0, y0, cont, return_index=False):
57
+ """Find point in contour closest to given point.
58
+
59
+ Args:
60
+ ----
61
+ x0: X of point
62
+ y0: Y of point
63
+ cont: The contour
64
+ return_index: if True returns the index in array
65
+
66
+ Return:
67
+ ------
68
+ min_point: the coordinates of closest point
69
+
70
+ """
71
+ npts = len(cont)
72
+ min_dist = sys.float_info.max
73
+ min_point = None
74
+ imin = -1
75
+ for ipt in range(npts):
76
+ x, y = cont[ipt, :]
77
+ dist = (x-x0)**2+(y-y0)**2
78
+ if dist < min_dist:
79
+ imin = ipt
80
+ min_dist = dist
81
+ min_point = (x, y)
82
+
83
+ if return_index:
84
+ return imin, min_point
85
+ else:
86
+ return min_point
87
+
88
+
89
+ def generate_points_inside_contour(C, n, region=None):
90
+ """Generate n points that are inside the contour.
91
+
92
+ Args:
93
+ ----
94
+ C: the contour
95
+ n: number of points to generate
96
+ region: if given generate withing region (Xmin, Ymin, Xwidth, Ywidth)
97
+
98
+ Returns
99
+ -------
100
+ Array with points
101
+
102
+ """
103
+ out = np.zeros([n, 2])
104
+ if region:
105
+ min_x = region[0]
106
+ min_y = region[1]
107
+ max_x = min_x + region[2]
108
+ max_y = min_y + region[3]
109
+ else:
110
+ min_x, min_y, max_x, max_y = contour_bounds(C)
111
+
112
+ npts = 0
113
+ path = mplPath.Path(C)
114
+ while npts < n:
115
+ P = [random.uniform(min_x, max_x), random.uniform(min_y, max_y)]
116
+ if path.contains_point(P):
117
+ out[npts, :] = P
118
+ npts += 1
119
+
120
+ return out
121
+
122
+
123
+ def find_closest_point(x0, y0, C):
124
+ """Find the closest point in contour."""
125
+ P = np.array([x0, y0])
126
+ indx, A = find_closest(x0, y0, C, return_index=True)
127
+
128
+ i1 = indx - 1
129
+ if indx == 0:
130
+ i1 -= 1
131
+
132
+ i2 = indx + 1
133
+ if indx == len(C)-1:
134
+ i2 = 2
135
+
136
+ d1, P1 = closest_segment_point(P, C[i1, :], A)
137
+ d2, P2 = closest_segment_point(P, A, C[i2, :])
138
+ if d1 <= d2:
139
+ return d1, P1
140
+
141
+ else:
142
+ return d2, P2
143
+
144
+
145
+ def contour_simplify(C, epsilon, return_mask=False):
146
+ """Simplyfy the contour using RDP algorithm.
147
+
148
+ Args:
149
+ ----
150
+ C: the contour
151
+ epsilon: RDP alg epsilon
152
+ return_mask: if True, return the indices of selected points
153
+ rather than the list of points.
154
+
155
+ """
156
+ stk = []
157
+
158
+ start_index = 0
159
+ last_index = len(C) - 1
160
+ stk.append([start_index, last_index])
161
+
162
+ global_start_index = start_index
163
+ indices = np.ones(last_index - start_index + 1, dtype=bool)
164
+
165
+ while stk:
166
+ start_index, last_index = stk.pop()
167
+
168
+ dmax = 0.0
169
+ index = start_index
170
+
171
+ for i in range(index + 1, last_index):
172
+ if indices[i - global_start_index]:
173
+ d = pldist(C[i], C[start_index], C[last_index])
174
+ if d > dmax:
175
+ index = i
176
+ dmax = d
177
+
178
+ if dmax > epsilon:
179
+ stk.append([start_index, index])
180
+ stk.append([index, last_index])
181
+ else:
182
+ for i in range(start_index + 1, last_index):
183
+ indices[i - global_start_index] = False
184
+
185
+ if return_mask:
186
+ return indices
187
+ else:
188
+ return C[indices]
189
+
190
+
191
+ def contour_smooth(C, distance):
192
+ """Removes outliers that are at distance > distance from neighbours."""
193
+ npts = len(C)
194
+ for i in range(npts-2):
195
+ dst, P = closest_segment_point(C[i, :], C[i-1, :], C[i+1, :])
196
+ if dst > distance:
197
+ C[i] = P
198
+
199
+ return C
200
+
201
+
202
+ def contour_length(C):
203
+ """Return the length of a contour."""
204
+ n = len(C)
205
+ dst = 0.0
206
+ for i in range(n-1):
207
+ dd = np.linalg.norm(C[i, :] - C[i+1, :])
208
+ dst += dd
209
+
210
+ return dst
211
+
212
+
213
+ def contour_path_length(C, norm=False):
214
+ """Return the path length of a contour.
215
+
216
+ Args:
217
+ ----
218
+ C: the contour
219
+ norm: if true, the lenght wil be normalized to 1.
220
+
221
+ Returns
222
+ -------
223
+ An array with the length of the contour at each points.
224
+ 0 for the first point total_length (or 1) for tha last.
225
+
226
+ """
227
+ npts = len(C)
228
+ D = np.zeros(npts)
229
+ dst = 0
230
+ X0 = 0
231
+ for i in range(npts):
232
+ X = C[i, :]
233
+ if i:
234
+ dst += np.linalg.norm(X-X0)
235
+
236
+ D[i] = dst
237
+ X0 = X
238
+
239
+ D = D/dst
240
+ return D
241
+
242
+
243
+ def contour_area(C):
244
+ """Return the area of a contour."""
245
+ def Ia(i, j):
246
+ return (C[i, 0]-C[j, 0])*(C[i, 1]+C[j, 1])
247
+
248
+ n = len(C)
249
+ area = Ia(0, n-1)
250
+ for i in range(n-2):
251
+ area += Ia(i+1, i)
252
+
253
+ return 0.5*abs(area)
254
+
255
+
256
+ def in_contour(x, y, C):
257
+ """Tell if it is inside the contour."""
258
+ path = mplPath.Path(C)
259
+ return path.contains_point(x, y, C)
260
+
261
+
262
+ def get_average_in_contour(img, C):
263
+ """Gets average and std of points within contour.
264
+
265
+ We are assuming here that coordinates are integers, ie,
266
+ indices of the matrix of values.
267
+
268
+ Args:
269
+ ----
270
+ img: The image
271
+ C: The contour
272
+
273
+ Returns
274
+ -------
275
+ avg, std: averate and std.
276
+
277
+ """
278
+ values = []
279
+ path = mplPath.Path(C)
280
+ my, mx = img.shape
281
+ min_x, min_y, max_x, max_y = contour_bounds(C)
282
+ for ix in range(int(min_x), int(max_x+1)):
283
+ if ix < 0 or ix >= mx:
284
+ continue
285
+
286
+ for iy in range(int(min_y), int(max_y)+1):
287
+ if iy < 0 or iy >= my:
288
+ continue
289
+
290
+ if path.contains_point([ix, iy]):
291
+ values.append(img[iy, ix])
292
+
293
+ return np.mean(values), np.std(values)
294
+
295
+
296
+ def contour_bounds(C):
297
+ """Compute the contour bounds.
298
+
299
+ Returns
300
+ -------
301
+ (min_x, min_y, max_x, maxy)
302
+
303
+ """
304
+ cmin = np.amin(C, axis=0)
305
+ cmax = np.amax(C, axis=0)
306
+ return cmin[0], cmin[1], cmax[0], cmax[1]
307
+
308
+
309
+ def contour_CM(C):
310
+ """Return the contour center of mass."""
311
+ return np.mean(C, axis=0)
312
+
313
+
314
+ def inset_contour(C, dist):
315
+ """Create an inset of the controur.
316
+
317
+ Args:
318
+ ----
319
+ C: Input contour
320
+ dist: distance inwards
321
+
322
+ Return:
323
+ ------
324
+ array: output contour
325
+
326
+ """
327
+ n = len(C)
328
+ out = np.zeros(C.shape)
329
+ M = Point(np.mean(C[:, 0]), np.mean([C[:, 1]]))
330
+ for i in range(n-1):
331
+ A = Point(C[i, :])
332
+ B = Point(C[i+1, :])
333
+ delta = (B - A).norm().cw()
334
+
335
+ O = A + dist*delta
336
+ out[i, 0] = O.x
337
+ out[i, 1] = O.y
338
+
339
+ out[-1, :] = out[0, :]
340
+ return out
341
+
342
+
343
+ def adjust_contour(cont):
344
+ """Swap contour axis (x<->y) and flip Y."""
345
+ out = np.zeros(cont.shape)
346
+ out[:, 0] = cont[:, 1]
347
+ out[:, 1] = cont[:, 0]
348
+ return out
349
+
350
+
351
+ def contour_eval(C, x):
352
+ """Interpolate contour Y value at X.
353
+
354
+ For this to work properly we need that the contour behaves somehow as a 1D
355
+ as a function (monotonic).
356
+
357
+ """
358
+ value = np.interp(x, C[:, 0], C[:, 1])
359
+ return value
360
+
361
+
362
+ if __name__ == "__main__":
363
+ C = np.loadtxt("long_contour.csv")
364
+
365
+ CM0 = contour_CM(C)
366
+ d0, pt0 = find_closest_point(CM0[0], CM0[1], C)
367
+
368
+ Cs = contour_simplify(C, 1.)
369
+ CMs = contour_CM(Cs)
370
+ ds, pts = find_closest_point(CM0[0], CM0[1], Cs)
371
+ d = np.linalg.norm(pt0-pts)
372
+ print("no. of points {}".format(len(C)))
373
+ print("no. of points {}".format(len(Cs)))
374
+ print(d0)
375
+ print(ds)
376
+ print(pt0)
377
+ print(pts)
378
+ print("Distance:", d)
@@ -0,0 +1,185 @@
1
+ #!/usr/bin/env python3
2
+ """Create IRCore object from file."""
3
+ import json
4
+ import os
5
+ import sys
6
+ from datetime import datetime
7
+ from pathlib import Path
8
+
9
+ import dateutil
10
+ import matplotlib.pyplot as plt
11
+
12
+ from petal_qc.thermal import IRBFile
13
+ from petal_qc.thermal import IRCore
14
+ from petal_qc.thermal import IRPetal
15
+ from petal_qc.thermal import Petal_IR_Analysis
16
+ from petal_qc.thermal import PipeFit
17
+ from petal_qc.thermal.IRDataGetter import IRDataGetter
18
+ from petal_qc.thermal.IRPetalParam import IRPetalParam
19
+ from petal_qc.utils.utils import output_folder
20
+
21
+
22
+ def get_db_date(timestamp=None):
23
+ """Convert a date string into the expected DB format.
24
+
25
+ Args:
26
+ ----
27
+ timestamp: A date in string format
28
+
29
+ """
30
+ def date2string(the_date):
31
+ out = the_date.isoformat(timespec='milliseconds')
32
+ if out[-1] not in ['zZ']:
33
+ out += 'Z'
34
+
35
+ return out
36
+ # return the_date.strftime("%Y-%m-%dT%H:%M:%S.000Z")
37
+
38
+ out = None
39
+ if timestamp is None:
40
+ out = date2string(datetime.now())
41
+ elif isinstance(timestamp, datetime):
42
+ out = date2string(timestamp)
43
+ else:
44
+ try:
45
+ this_date = dateutil.parser.parse(timestamp)
46
+ out = date2string(this_date)
47
+ except Exception:
48
+ out = ""
49
+
50
+ return out
51
+
52
+
53
+ def create_IR_core(options):
54
+ """Entry point."""
55
+ # Obtain the Data getter.
56
+ try:
57
+ getter = IRDataGetter.factory(options.institute, options)
58
+
59
+ except NotImplemented:
60
+ print("*** Invalid institute name. ***")
61
+ return
62
+
63
+ # Set parameters from command line
64
+ params = IRPetal.IRPetalParam(options)
65
+ params.debug = False
66
+
67
+ # Open the sequence file
68
+ print("## ", options.files)
69
+ irbf = IRBFile.open_file(options.files)
70
+
71
+ # FInd first image below the threshold or the corresponding frame
72
+ # We will use the pipe obtained from here as the reference
73
+ # for the next
74
+ try:
75
+ print("Get the reference image.")
76
+ min_T, i_min, values = getter.find_reference_image(irbf, params.thrs, nframes=10)
77
+ print("Image size: {} x {}".format(values[0].shape[0], values[0].shape[1]))
78
+
79
+ if options.debug:
80
+ Petal_IR_Analysis.show_2D_image(values)
81
+
82
+ except LookupError as e:
83
+ print(e)
84
+ sys.exit()
85
+
86
+ # Get the pipes
87
+ print("Get the pipes.")
88
+ pipes = getter.extract_pipe_path(values, params)
89
+ npipes = len(pipes)
90
+ transforms = [None, None]
91
+ fitter = [None, None]
92
+ sensors = [None, None]
93
+
94
+ print("Fit pipes and find sensor positions.")
95
+ ordered_pipes = [None, None]
96
+ pipe_order = [0, 0]
97
+ for i in range(2):
98
+ pipe_type = PipeFit.PipeFit.guess_pipe_type(pipes[i])
99
+ pipe_order[i] = pipe_type
100
+ PF = PipeFit.PipeFit(pipe_type)
101
+ R = PF.fit_ex(pipes[i], factor=getter.factor)
102
+ if options.debug:
103
+ PF.plot()
104
+
105
+ transforms[pipe_type] = R
106
+ fitter[pipe_type] = PF
107
+
108
+ # Reorder points in pipe contour so that first point corresponds to
109
+ # the U-shape pipe minimum.
110
+ pipes[i] = IRPetal.reorder_pipe_points(pipes[i], pipe_type, R)
111
+ if ordered_pipes[pipe_type] is not None:
112
+ print("### Expect probles. 2 pipes of sme type")
113
+
114
+ ordered_pipes[pipe_type] = pipes[i]
115
+
116
+ # Now make the inverse transform of the area of sernsors and EoS
117
+ S = []
118
+ for s in PF.sensors:
119
+ o = PF.transform_inv(s, R)
120
+ S.append(o)
121
+
122
+ sensors[pipe_type] = S
123
+
124
+ pipes = ordered_pipes
125
+ deltaT = 0.0
126
+ if options.tco2 != 0:
127
+ deltaT = -35.0 - options.tco2
128
+
129
+ # get the framea from where extract the data
130
+ # reorder if needed
131
+ frames = getter.get_analysis_frame(irbf)
132
+ if pipe_order[0]:
133
+ tmp = frames[0]
134
+ frames[0] = frames[1]
135
+ frames[1] = tmp
136
+
137
+ values = getter.get_IR_data(frames, rotate=True)
138
+ results = getter.analyze_IR_image(values, pipes, sensors, 0, params)
139
+ if options.debug:
140
+ fig, ax = plt.subplots(2, 2, tight_layout=True)
141
+ ax[0][0].set_title("Side 0")
142
+ ax[0][1].set_title("Side 1")
143
+
144
+ for iside in range(2):
145
+ R = results[iside]
146
+ ax[0][iside].plot(R.path_length, R.path_temp)
147
+ ax[1][iside].plot([x for x in range(10)], R.sensor_avg)
148
+
149
+ core = IRCore.IRCore(options.SN, options.alias, results, params)
150
+ core.set_files(options.files)
151
+ core.date = get_db_date(frames[0].timestamp)
152
+ core.institute = params.institute
153
+ core.apply_deltaT(deltaT)
154
+
155
+ # Check if we are given an output folder
156
+ ofile = output_folder(options.folder, options.out)
157
+ with open(ofile, "w") as ofile:
158
+ core.to_json(ofile)
159
+
160
+ if options.debug:
161
+ plt.show()
162
+
163
+
164
+ if __name__ == "__main__":
165
+ from argparse import ArgumentParser
166
+
167
+ # Argument parser
168
+ parser = ArgumentParser()
169
+ parser.add_argument('files', nargs='*', help="Input files")
170
+ parser.add_argument("--nframe", type=int, default=-1, help="Number of frames. (negative means all.")
171
+ parser.add_argument('--frame', type=int, default=-1, help="First frame to start.")
172
+ parser.add_argument("--tco2", default=0, type=float, help="CO2 inlet temperature.")
173
+ parser.add_argument("--out", default="core.json", help="Output file name")
174
+ parser.add_argument("--alias", default="", help="Alias")
175
+ parser.add_argument("--SN", default="", help="serial number")
176
+ parser.add_argument("--folder", default=None, help="Folder to store output files. Superseeds folder in --out")
177
+
178
+ IRPetalParam.add_parameters(parser)
179
+
180
+ options = parser.parse_args()
181
+ if len(options.files) == 0:
182
+ print("I need an input file")
183
+ sys.exit()
184
+
185
+ create_IR_core(options)