qcodespp 0.1.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.
Files changed (82) hide show
  1. qcodespp/__init__.py +77 -0
  2. qcodespp/actions.py +237 -0
  3. qcodespp/data/__init__.py +0 -0
  4. qcodespp/data/data_array.py +555 -0
  5. qcodespp/data/data_set.py +926 -0
  6. qcodespp/data/format.py +338 -0
  7. qcodespp/data/gnuplot_format.py +425 -0
  8. qcodespp/data/hdf5_format.py +537 -0
  9. qcodespp/data/hdf5_format_hickle.py +63 -0
  10. qcodespp/data/io.py +241 -0
  11. qcodespp/data/location.py +171 -0
  12. qcodespp/instrument_drivers/Keysight/Keysight_34450A.py +9 -0
  13. qcodespp/instrument_drivers/Keysight/__init__.py +0 -0
  14. qcodespp/instrument_drivers/Keysight/private/Keysight_344xxA.py +456 -0
  15. qcodespp/instrument_drivers/Keysight/private/__init__.py +0 -0
  16. qcodespp/instrument_drivers/Lakeshore/Model_340.py +483 -0
  17. qcodespp/instrument_drivers/Lakeshore/__init__.py +0 -0
  18. qcodespp/instrument_drivers/QDevil/159fit_params_high_latest.json +386 -0
  19. qcodespp/instrument_drivers/QDevil/159fit_params_low_latest.json +386 -0
  20. qcodespp/instrument_drivers/QDevil/QDAC2.py +3229 -0
  21. qcodespp/instrument_drivers/QDevil/QDAC2_Array.py +300 -0
  22. qcodespp/instrument_drivers/QDevil/QSwitch_elab.py +652 -0
  23. qcodespp/instrument_drivers/QDevil/__init__.py +0 -0
  24. qcodespp/instrument_drivers/ZI/ZIHF2LI.py +415 -0
  25. qcodespp/instrument_drivers/ZI/ZIMFLI.py +764 -0
  26. qcodespp/instrument_drivers/ZI/ZIMFLI_sync.py +108 -0
  27. qcodespp/instrument_drivers/ZI/ZIUHFLI.py +2179 -0
  28. qcodespp/instrument_drivers/ZI/__init__.py +1 -0
  29. qcodespp/instrument_drivers/__init__.py +0 -0
  30. qcodespp/instrument_drivers/cryogenic/CryogenicSMS120C.py +599 -0
  31. qcodespp/instrument_drivers/cryogenic/__init__.py +0 -0
  32. qcodespp/instrument_drivers/instrument_mocks.py +379 -0
  33. qcodespp/instrument_drivers/multiplexer_instrument.py +287 -0
  34. qcodespp/instrument_drivers/national_instruments/PXI_4461.py +451 -0
  35. qcodespp/instrument_drivers/national_instruments/PXI_4462.py +290 -0
  36. qcodespp/instrument_drivers/national_instruments/PXI_6259.py +436 -0
  37. qcodespp/instrument_drivers/national_instruments/PXI_6733.py +230 -0
  38. qcodespp/instrument_drivers/national_instruments/PXI_6733_e.py +243 -0
  39. qcodespp/instrument_drivers/national_instruments/__init__.py +0 -0
  40. qcodespp/instrument_drivers/oxford/__init__.py +0 -0
  41. qcodespp/instrument_drivers/oxford/mercuryiPS_serial.py +658 -0
  42. qcodespp/instrument_drivers/oxford/triton.py +434 -0
  43. qcodespp/instrument_drivers/tektronix/Keithley_2000_Scan.py +65 -0
  44. qcodespp/instrument_drivers/tektronix/Keithley_2182a.py +190 -0
  45. qcodespp/instrument_drivers/tektronix/Keithley_2400.py +158 -0
  46. qcodespp/instrument_drivers/tektronix/Keithley_2450.py +242 -0
  47. qcodespp/instrument_drivers/tektronix/Keithley_2700.py +397 -0
  48. qcodespp/instrument_drivers/tektronix/Keithley_6500.py +161 -0
  49. qcodespp/instrument_drivers/tektronix/__init__.py +0 -0
  50. qcodespp/instrument_drivers/yokogawa/GS200.py +513 -0
  51. qcodespp/instrument_drivers/yokogawa/__init__.py +0 -0
  52. qcodespp/loops.py +1342 -0
  53. qcodespp/measure.py +310 -0
  54. qcodespp/parameters.py +311 -0
  55. qcodespp/plotting/RemotePlot.py +469 -0
  56. qcodespp/plotting/RemotePlotClient.py +367 -0
  57. qcodespp/plotting/RemoteQtPlotWidgets.py +871 -0
  58. qcodespp/plotting/__init__.py +0 -0
  59. qcodespp/plotting/analysis_tools.py +195 -0
  60. qcodespp/plotting/colors.py +135 -0
  61. qcodespp/plotting/offline/__init__.py +0 -0
  62. qcodespp/plotting/offline/datatypes.py +999 -0
  63. qcodespp/plotting/offline/design.py +906 -0
  64. qcodespp/plotting/offline/design.ui +1578 -0
  65. qcodespp/plotting/offline/editor.py +2966 -0
  66. qcodespp/plotting/offline/filters.py +561 -0
  67. qcodespp/plotting/offline/fits.py +1171 -0
  68. qcodespp/plotting/offline/helpers.py +288 -0
  69. qcodespp/plotting/offline/iconGadget.png +0 -0
  70. qcodespp/plotting/offline/main.py +55 -0
  71. qcodespp/plotting/offline/popupwindows.py +1848 -0
  72. qcodespp/plotting/offline/qcodes_pp_extension.py +328 -0
  73. qcodespp/plotting/offline/sidebars.py +1108 -0
  74. qcodespp/station.py +290 -0
  75. qcodespp/utils/__init__.py +0 -0
  76. qcodespp/utils/helpers.py +618 -0
  77. qcodespp/utils/visa_helpers.py +61 -0
  78. qcodespp/version.py +12 -0
  79. qcodespp-0.1.0.dist-info/METADATA +91 -0
  80. qcodespp-0.1.0.dist-info/RECORD +82 -0
  81. qcodespp-0.1.0.dist-info/WHEEL +4 -0
  82. qcodespp-0.1.0.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,425 @@
1
+ import numpy as np
2
+ import re
3
+ import math
4
+ import json
5
+ import logging
6
+
7
+ from qcodes.utils.helpers import deep_update
8
+ from qcodespp.utils.helpers import NumpyJSONEncoder
9
+ from qcodespp.data.data_array import DataArray
10
+ from qcodespp.data.format import Formatter
11
+
12
+
13
+ log = logging.getLogger(__name__)
14
+
15
+
16
+ class GNUPlotFormat(Formatter):
17
+ """
18
+ Saves data in one or more gnuplot-format files. We make one file for
19
+ each set of matching dependent variables in the loop.
20
+
21
+ Args:
22
+
23
+ extension (default 'dat'): file extension for data files
24
+
25
+ terminator (default '\\\\n'): newline character(s) to use on write
26
+ not used for reading, we will read any combination of '\\\\r' and '\\\\n'
27
+
28
+ separator (default '\\\\t'): field (column) separator, must be whitespace.
29
+ Only used for writing, we will read with any whitespace separation.
30
+
31
+ comment (default '# '): lines starting with this are not data
32
+ Comments are written with this full string, and identified on read
33
+ by just the string after stripping whitespace.
34
+
35
+ number_format (default 'g'): from the format mini-language, how to
36
+ format numeric data into a string
37
+
38
+ always_nest (default True): whether to always make a folder for files
39
+ or just make a single data file if all data has the same setpoints
40
+
41
+ These files are basically tab-separated values, but any quantity of
42
+ any whitespace characters is accepted.
43
+
44
+ Each row represents one setting of the setpoint variable(s)
45
+ the setpoint variable(s) are in the first column(s)
46
+ measured variable(s) come after.
47
+
48
+ The data is preceded by comment lines (starting with #).
49
+ We use three:
50
+
51
+ - one for the variable name
52
+ - the (longer) axis label, in quotes so a label can contain whitespace.
53
+ - for each dependent var, the (max) number of points in that dimension
54
+ (this also tells us how many dependent vars we have in this file)
55
+
56
+ ::
57
+
58
+ # id1\tid2\tid3...
59
+ # "label1"\t"label2"\t"label3"...
60
+ # 100\t250
61
+ 1\t2\t3...
62
+ 2\t3\t4...
63
+
64
+ For data of 2 dependent variables, gnuplot puts each inner loop into one
65
+ block, then increments the outer loop in the next block, separated by a
66
+ blank line.
67
+
68
+ We extend this to an arbitrary quantity of dependent variables by using
69
+ one blank line for each loop level that resets. (gnuplot *does* seem to
70
+ use 2 blank lines sometimes, to denote a whole new dataset, which sort
71
+ of corresponds to our situation.)
72
+ """
73
+
74
+ def __init__(self, extension='dat', terminator='\n', separator='\t',
75
+ comment='# ', number_format=None, metadata_file=None):
76
+ self.metadata_file = metadata_file or 'snapshot.json'
77
+ # file extension: accept either with or without leading dot
78
+ self.extension = '.' + extension.lstrip('.')
79
+
80
+ # line terminator (only used for writing; will read any \r\n combo)
81
+ if terminator not in ('\r', '\n', '\r\n'):
82
+ raise ValueError(
83
+ r'GNUPlotFormat terminator must be \r, \n, or \r\n')
84
+ self.terminator = terminator
85
+
86
+ # field separator (only used for writing; will read any whitespace)
87
+ if not re.fullmatch(r'\s+', separator):
88
+ raise ValueError('GNUPlotFormat separator must be whitespace')
89
+ self.separator = separator
90
+
91
+ # beginning of a comment line. (when reading, just checks the
92
+ # non-whitespace character(s) of comment
93
+ self.comment = comment
94
+ self.comment_chars = comment.rstrip()
95
+ if not self.comment_chars:
96
+ raise ValueError('comment must have some non-whitespace')
97
+ self.comment_len = len(self.comment_chars)
98
+
99
+ # number format (only used for writing; will read any number)
100
+ if number_format is None:
101
+ self.number_format = '{}'
102
+ else:
103
+ self.number_format = '{:' + number_format + '}'
104
+
105
+ def read_one_file(self, data_set, f, ids_read):
106
+ """
107
+ Called by Formatter.read to bring one data file into
108
+ a DataSet. Setpoint data may be duplicated across multiple files,
109
+ but each measured DataArray must only map to one file.
110
+
111
+ args:
112
+ data_set: the DataSet we are reading into
113
+ f: a file-like object to read from
114
+ ids_read: a `set` of array_ids that we have already read.
115
+ when you read an array, check that it's not in this set (except
116
+ setpoints, which can be in several files with different inner loop)
117
+ then add it to the set so other files know not to read it again
118
+ """
119
+ if not f.name.endswith(self.extension):
120
+ return
121
+
122
+ arrays = data_set.arrays
123
+ ids = self._read_comment_line(f).split()
124
+ labels = self._get_labels(self._read_comment_line(f))
125
+ shape = tuple(map(int, self._read_comment_line(f).split()))
126
+ ndim = len(shape)
127
+
128
+ set_arrays = ()
129
+ data_arrays = []
130
+ indexed_ids = list(enumerate(ids))
131
+
132
+ for i, array_id in indexed_ids[:ndim]:
133
+ snap = data_set.get_array_metadata(array_id)
134
+ # if 'data_type' in snap.keys():
135
+ # if 'str' in snap['data_type']:
136
+ # data_type=str
137
+ # else:
138
+ # data_type=float
139
+ # else:
140
+ # data_type=float
141
+
142
+ # setpoint arrays
143
+ set_shape = shape[: i + 1]
144
+ if array_id in arrays:
145
+ set_array = arrays[array_id]
146
+ if set_array.shape != set_shape:
147
+ raise ValueError(
148
+ 'shapes do not match for set array: ' + array_id)
149
+ if array_id not in ids_read:
150
+ # it's OK for setpoints to be duplicated across
151
+ # multiple files, but we should only empty the
152
+ # array out the first time we see it, so subsequent
153
+ # reads can check for consistency
154
+ set_array.clear()
155
+ else:
156
+ set_array = DataArray(label=labels[i], array_id=array_id,
157
+ set_arrays=set_arrays, shape=set_shape,
158
+ is_setpoint=True, snapshot=snap)
159
+ set_array.init_data()
160
+ data_set.add_array(set_array)
161
+
162
+ set_arrays = set_arrays + (set_array, )
163
+ ids_read.add(array_id)
164
+
165
+ for i, array_id in indexed_ids[ndim:]:
166
+ snap = data_set.get_array_metadata(array_id)
167
+ if 'data_type' in snap.keys():
168
+ if 'str' in snap['data_type']:
169
+ data_type=str
170
+ else:
171
+ data_type=float
172
+ else:
173
+ data_type=float
174
+
175
+ # data arrays
176
+ if array_id in ids_read:
177
+ raise ValueError('duplicate data id found: ' + array_id)
178
+
179
+ if array_id in arrays:
180
+ data_array = arrays[array_id]
181
+ data_array.clear()
182
+ else:
183
+ data_array = DataArray(label=labels[i], array_id=array_id,
184
+ set_arrays=set_arrays, shape=shape,
185
+ snapshot=snap,data_type=data_type)
186
+ data_array.init_data()
187
+ data_set.add_array(data_array)
188
+ data_arrays.append(data_array)
189
+ ids_read.add(array_id)
190
+
191
+ indices = [0] * ndim
192
+ first_point = True
193
+ resetting = 0
194
+ for line in f:
195
+ if self._is_comment(line):
196
+ continue
197
+
198
+ # ignore leading or trailing whitespace (including in blank lines)
199
+ line = line.strip()
200
+
201
+ if not line:
202
+ # each consecutive blank line implies one more loop to reset
203
+ # when we read the next data point. Don't depend on the number
204
+ # of setpoints that change, as there could be weird cases, like
205
+ # bidirectional sweeps, or highly diagonal sweeps, where this
206
+ # is incorrect. Anyway this really only matters for >2D sweeps.
207
+ if not first_point:
208
+ resetting += 1
209
+ continue
210
+
211
+
212
+ values = tuple(map(str, line.split(self.separator)))
213
+
214
+ if resetting:
215
+ indices[-resetting - 1] += 1
216
+ indices[-resetting:] = [0] * resetting
217
+ resetting = 0
218
+
219
+ for value, set_array in zip(values[:ndim], set_arrays):
220
+ nparray = set_array.ndarray
221
+ myindices = tuple(indices[:nparray.ndim])
222
+ stored_value = nparray[myindices]
223
+ if math.isnan(stored_value):
224
+ nparray[myindices] = value
225
+ elif stored_value != float(value):
226
+ raise ValueError('inconsistent setpoint values',
227
+ stored_value, value, set_array.name,
228
+ myindices, indices)
229
+
230
+ for value, data_array in zip(values[ndim:], data_arrays):
231
+ # set .ndarray directly to avoid the overhead of __setitem__
232
+ # which updates modified_range on every call
233
+ data_array.ndarray[tuple(indices)] = value
234
+
235
+ indices[-1] += 1
236
+ first_point = False
237
+
238
+ # Since we skipped __setitem__, back up to the last read point and
239
+ # mark it as saved that far.
240
+ # Using mark_saved is better than directly setting last_saved_index
241
+ # because it also ensures modified_range is set correctly.
242
+ indices[-1] -= 1
243
+ for array in set_arrays + tuple(data_arrays):
244
+ array.mark_saved(array.flat_index(indices[:array.ndim]))
245
+
246
+ def _is_comment(self, line):
247
+ return line[:self.comment_len] == self.comment_chars
248
+
249
+ def _read_comment_line(self, f):
250
+ s = f.readline()
251
+ if not self._is_comment(s):
252
+ raise ValueError('expected a comment line, found:\n' + s)
253
+ return s[self.comment_len:]
254
+
255
+ def _get_labels(self, labelstr):
256
+ labelstr = labelstr.strip()
257
+ if labelstr[0] != '"' or labelstr[-1] != '"':
258
+ # fields are *not* quoted
259
+ return labelstr.split()
260
+ else:
261
+ # fields *are* quoted (and escaped)
262
+ parts = re.split('"\\s+"', labelstr[1:-1])
263
+ return [l.replace('\\"', '"').replace('\\\\', '\\') for l in parts]
264
+
265
+ def write(self, data_set, io_manager, location, force_write=False,
266
+ write_metadata=True, only_complete=True, filename=None, force_rewrite=False):
267
+ """
268
+ Write updates in this DataSet to storage.
269
+
270
+ Will choose append if possible, overwrite if not.
271
+
272
+ Args:
273
+ data_set (DataSet): the data we're storing
274
+ io_manager (io_manager): the base location to write to
275
+ location (str): the file location within io_manager
276
+ only_complete (bool): passed to match_save_range, answers the
277
+ following question: Should we write all available new data,
278
+ or only complete rows? Is used to make sure that everything
279
+ gets written when the DataSet is finalised, even if some
280
+ dataarrays are strange (like, full of nans)
281
+ filename (Optional[str]): Filename to save to. Will override
282
+ the usual naming scheme and possibly overwrite files, so
283
+ use with care. The file will be saved in the normal location.
284
+ """
285
+ arrays = data_set.arrays
286
+
287
+ # puts everything with same dimensions together
288
+ groups = self.group_arrays(arrays)
289
+ existing_files = set(io_manager.list(location))
290
+ written_files = set()
291
+
292
+ # Every group gets its own datafile
293
+ for group in groups:
294
+ log.debug('Attempting to write the following '
295
+ 'group: {}'.format(group.name))
296
+ # it might be useful to output the whole group as below but it is
297
+ # very verbose
298
+ #log.debug('containing {}'.format(group))
299
+
300
+ if filename:
301
+ self.filename=filename
302
+ fn = io_manager.join(location, filename + self.extension)
303
+ else:
304
+ self.filename=group.name
305
+ fn = io_manager.join(location, group.name + self.extension)
306
+
307
+ written_files.add(fn)
308
+
309
+ # fn may or may not be an absolute path depending on the location manager
310
+ # used however, io_manager always returns relative paths so make sure both are
311
+ # relative by calling to_location
312
+ file_exists = io_manager.to_location(fn) in existing_files
313
+ save_range = self.match_save_range(group, file_exists,
314
+ only_complete=only_complete, force_rewrite=force_rewrite)
315
+
316
+ if save_range is None:
317
+ log.debug('Cannot match save range, skipping this group.')
318
+ continue
319
+
320
+ overwrite = save_range[0] == 0 or force_write
321
+ open_mode = 'w' if overwrite else 'a'
322
+ shape = group.set_arrays[-1].shape
323
+
324
+ with io_manager.open(fn, open_mode) as f:
325
+ if overwrite:
326
+ f.write(self._make_header(group))
327
+ log.debug('Wrote header to file')
328
+
329
+ for i in range(save_range[0], save_range[1] + 1):
330
+ indices = np.unravel_index(i, shape)
331
+
332
+ # insert a blank line for each loop that reset (to index 0)
333
+ # note that if *all* indices are zero (the first point)
334
+ # we won't put any blanks
335
+ for j, index in enumerate(reversed(indices)):
336
+ if index != 0:
337
+ if j:
338
+ f.write(self.terminator * j)
339
+ break
340
+
341
+ one_point = self._data_point(group, indices)
342
+ f.write(self.separator.join(one_point) + self.terminator)
343
+ log.debug('Wrote to file from '
344
+ '{} to {}'.format(save_range[0], save_range[1]+1))
345
+ # now that we've saved the data, mark it as such in the data.
346
+ # we mark the data arrays and the inner setpoint array. Outer
347
+ # setpoint arrays have different dimension (so would need a
348
+ # different unraveled index) but more importantly could have
349
+ # a different saved range anyway depending on whether there
350
+ # is outer data taken before or after the inner loop. Anyway we
351
+ # never look at the outer setpoint last_saved_index or
352
+ # modified_range, we just assume it's got the values we need.
353
+ for array in group.data + (group.set_arrays[-1],):
354
+ array.mark_saved(save_range[1])
355
+
356
+ if write_metadata:
357
+ self.write_metadata(
358
+ data_set, io_manager=io_manager, location=location)
359
+
360
+ def write_metadata(self, data_set, io_manager, location, read_first=True):
361
+ """
362
+ Write all metadata in this DataSet to storage.
363
+
364
+ Args:
365
+ data_set (DataSet): the data we're storing
366
+
367
+ io_manager (io_manager): the base location to write to
368
+
369
+ location (str): the file location within io_manager
370
+
371
+ read_first (bool, optional): read previously saved metadata before
372
+ writing? The current metadata will still be the used if
373
+ there are changes, but if the saved metadata has information
374
+ not present in the current metadata, it will be retained.
375
+ Default True.
376
+ """
377
+ if read_first:
378
+ # In case the saved file has more metadata than we have here,
379
+ # read it in first. But any changes to the in-memory copy should
380
+ # override the saved file data.
381
+ memory_metadata = data_set.metadata
382
+ data_set.metadata = {}
383
+ self.read_metadata(data_set)
384
+ deep_update(data_set.metadata, memory_metadata)
385
+
386
+ fn = io_manager.join(location, self.metadata_file)
387
+ with io_manager.open(fn, 'w') as snap_file:
388
+ json.dump(data_set.metadata, snap_file, sort_keys=False,
389
+ indent=4, ensure_ascii=False, cls=NumpyJSONEncoder)
390
+
391
+ def read_metadata(self, data_set):
392
+ io_manager = data_set.io
393
+ location = data_set.location
394
+ fn = io_manager.join(location, self.metadata_file)
395
+ if io_manager.list(fn):
396
+ with io_manager.open(fn, 'r') as snap_file:
397
+ metadata = json.load(snap_file)
398
+ data_set.metadata.update(metadata)
399
+
400
+ def _make_header(self, group):
401
+ ids, labels = [], []
402
+ for array in group.set_arrays + group.data:
403
+ ids.append(array.array_id)
404
+ label = getattr(array, 'label', array.array_id)
405
+ label = label.replace('\\', '\\\\').replace('"', '\\"')
406
+ labels.append('"' + label + '"')
407
+
408
+ shape = [str(size) for size in group.set_arrays[-1].shape]
409
+ if len(shape) != len(group.set_arrays):
410
+ raise ValueError('array dimensionality does not match setpoints')
411
+
412
+ out = (self._comment_line(ids) + self._comment_line(labels) +
413
+ self._comment_line(shape))
414
+
415
+ return out
416
+
417
+ def _comment_line(self, items):
418
+ return self.comment + self.separator.join(items) + self.terminator
419
+
420
+ def _data_point(self, group, indices):
421
+ for array in group.set_arrays:
422
+ yield self.number_format.format(array[indices[:array.ndim]])
423
+
424
+ for array in group.data:
425
+ yield self.number_format.format(array[indices])