qcodes-loop 0.1.3__tar.gz → 0.2.0__tar.gz
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.
- {qcodes_loop-0.1.3/src/qcodes_loop.egg-info → qcodes_loop-0.2.0}/PKG-INFO +12 -11
- {qcodes_loop-0.1.3 → qcodes_loop-0.2.0}/pyproject.toml +14 -15
- {qcodes_loop-0.1.3 → qcodes_loop-0.2.0}/setup.py +1 -0
- {qcodes_loop-0.1.3 → qcodes_loop-0.2.0}/src/qcodes_loop/__init__.py +1 -0
- {qcodes_loop-0.1.3 → qcodes_loop-0.2.0}/src/qcodes_loop/_version.py +1 -1
- {qcodes_loop-0.1.3 → qcodes_loop-0.2.0}/src/qcodes_loop/actions.py +21 -19
- {qcodes_loop-0.1.3 → qcodes_loop-0.2.0}/src/qcodes_loop/data/data_array.py +100 -75
- {qcodes_loop-0.1.3 → qcodes_loop-0.2.0}/src/qcodes_loop/data/data_set.py +102 -87
- {qcodes_loop-0.1.3 → qcodes_loop-0.2.0}/src/qcodes_loop/data/format.py +42 -35
- {qcodes_loop-0.1.3 → qcodes_loop-0.2.0}/src/qcodes_loop/data/gnuplot_format.py +79 -53
- {qcodes_loop-0.1.3 → qcodes_loop-0.2.0}/src/qcodes_loop/data/hdf5_format.py +145 -134
- {qcodes_loop-0.1.3 → qcodes_loop-0.2.0}/src/qcodes_loop/data/hdf5_format_hickle.py +9 -10
- {qcodes_loop-0.1.3 → qcodes_loop-0.2.0}/src/qcodes_loop/data/io.py +8 -9
- {qcodes_loop-0.1.3 → qcodes_loop-0.2.0}/src/qcodes_loop/data/location.py +28 -25
- {qcodes_loop-0.1.3 → qcodes_loop-0.2.0}/src/qcodes_loop/extensions/slack.py +2 -4
- {qcodes_loop-0.1.3 → qcodes_loop-0.2.0}/src/qcodes_loop/loops.py +207 -124
- {qcodes_loop-0.1.3 → qcodes_loop-0.2.0}/src/qcodes_loop/measure.py +28 -19
- {qcodes_loop-0.1.3 → qcodes_loop-0.2.0}/src/qcodes_loop/plots/base.py +45 -27
- qcodes_loop-0.2.0/src/qcodes_loop/plots/colors.py +188 -0
- {qcodes_loop-0.1.3 → qcodes_loop-0.2.0}/src/qcodes_loop/plots/pyqtgraph.py +138 -107
- {qcodes_loop-0.1.3 → qcodes_loop-0.2.0}/src/qcodes_loop/plots/qcmatplotlib.py +100 -83
- qcodes_loop-0.2.0/src/qcodes_loop/sweep_values.py +515 -0
- {qcodes_loop-0.1.3 → qcodes_loop-0.2.0}/src/qcodes_loop/tests/common.py +3 -12
- qcodes_loop-0.2.0/src/qcodes_loop/tests/data_mocks.py +166 -0
- qcodes_loop-0.2.0/src/qcodes_loop/tests/test_channels.py +278 -0
- {qcodes_loop-0.1.3 → qcodes_loop-0.2.0}/src/qcodes_loop/tests/test_combined_loop.py +129 -93
- {qcodes_loop-0.1.3 → qcodes_loop-0.2.0}/src/qcodes_loop/tests/test_data.py +157 -141
- {qcodes_loop-0.1.3 → qcodes_loop-0.2.0}/src/qcodes_loop/tests/test_format.py +104 -106
- {qcodes_loop-0.1.3 → qcodes_loop-0.2.0}/src/qcodes_loop/tests/test_generic_formatter.py +11 -9
- {qcodes_loop-0.1.3 → qcodes_loop-0.2.0}/src/qcodes_loop/tests/test_hdf5formatter.py +105 -92
- qcodes_loop-0.2.0/src/qcodes_loop/tests/test_issequence.py +54 -0
- qcodes_loop-0.2.0/src/qcodes_loop/tests/test_location_provider.py +120 -0
- {qcodes_loop-0.1.3 → qcodes_loop-0.2.0}/src/qcodes_loop/tests/test_loop.py +184 -152
- qcodes_loop-0.2.0/src/qcodes_loop/tests/test_make_sweep.py +39 -0
- {qcodes_loop-0.1.3 → qcodes_loop-0.2.0}/src/qcodes_loop/tests/test_measure.py +30 -30
- qcodes_loop-0.2.0/src/qcodes_loop/tests/test_permissive_range.py +33 -0
- {qcodes_loop-0.1.3 → qcodes_loop-0.2.0}/src/qcodes_loop/tests/test_plots.py +19 -13
- {qcodes_loop-0.1.3 → qcodes_loop-0.2.0}/src/qcodes_loop/tests/test_qcmatplotlib_functions.py +8 -16
- {qcodes_loop-0.1.3 → qcodes_loop-0.2.0}/src/qcodes_loop/tests/test_slack.py +97 -105
- qcodes_loop-0.2.0/src/qcodes_loop/tests/test_sweep_values.py +156 -0
- {qcodes_loop-0.1.3 → qcodes_loop-0.2.0}/src/qcodes_loop/tests/test_threading.py +4 -6
- {qcodes_loop-0.1.3 → qcodes_loop-0.2.0}/src/qcodes_loop/utils/magic.py +31 -30
- {qcodes_loop-0.1.3 → qcodes_loop-0.2.0/src/qcodes_loop.egg-info}/PKG-INFO +12 -11
- {qcodes_loop-0.1.3 → qcodes_loop-0.2.0}/src/qcodes_loop.egg-info/SOURCES.txt +5 -0
- {qcodes_loop-0.1.3 → qcodes_loop-0.2.0}/src/qcodes_loop.egg-info/requires.txt +6 -6
- qcodes_loop-0.1.3/src/qcodes_loop/plots/colors.py +0 -135
- qcodes_loop-0.1.3/src/qcodes_loop/tests/data_mocks.py +0 -150
- qcodes_loop-0.1.3/src/qcodes_loop/tests/test_channels.py +0 -296
- qcodes_loop-0.1.3/src/qcodes_loop/tests/test_location_provider.py +0 -118
- {qcodes_loop-0.1.3 → qcodes_loop-0.2.0}/LICENSE +0 -0
- {qcodes_loop-0.1.3 → qcodes_loop-0.2.0}/MANIFEST.in +0 -0
- {qcodes_loop-0.1.3 → qcodes_loop-0.2.0}/README.rst +0 -0
- {qcodes_loop-0.1.3 → qcodes_loop-0.2.0}/setup.cfg +0 -0
- {qcodes_loop-0.1.3 → qcodes_loop-0.2.0}/src/qcodes_loop/data/__init__.py +0 -0
- {qcodes_loop-0.1.3 → qcodes_loop-0.2.0}/src/qcodes_loop/extensions/__init__.py +0 -0
- {qcodes_loop-0.1.3 → qcodes_loop-0.2.0}/src/qcodes_loop/plots/__init__.py +0 -0
- {qcodes_loop-0.1.3 → qcodes_loop-0.2.0}/src/qcodes_loop/py.typed +0 -0
- {qcodes_loop-0.1.3 → qcodes_loop-0.2.0}/src/qcodes_loop/tests/__init__.py +0 -0
- {qcodes_loop-0.1.3 → qcodes_loop-0.2.0}/src/qcodes_loop/tests/test_waitsecs.py +0 -0
- {qcodes_loop-0.1.3 → qcodes_loop-0.2.0}/src/qcodes_loop/utils/__init__.py +0 -0
- {qcodes_loop-0.1.3 → qcodes_loop-0.2.0}/src/qcodes_loop/utils/qt_helpers.py +0 -0
- {qcodes_loop-0.1.3 → qcodes_loop-0.2.0}/src/qcodes_loop.egg-info/dependency_links.txt +0 -0
- {qcodes_loop-0.1.3 → qcodes_loop-0.2.0}/src/qcodes_loop.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: qcodes_loop
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: Features previously in QCoDeS
|
|
5
5
|
Maintainer-email: QCoDeS Core Developers <qcodes-support@microsoft.com>
|
|
6
6
|
License: MIT
|
|
@@ -12,19 +12,19 @@ Classifier: Development Status :: 3 - Alpha
|
|
|
12
12
|
Classifier: Intended Audience :: Science/Research
|
|
13
13
|
Classifier: License :: OSI Approved :: MIT License
|
|
14
14
|
Classifier: Programming Language :: Python :: 3 :: Only
|
|
15
|
-
Classifier: Programming Language :: Python :: 3.9
|
|
16
|
-
Classifier: Programming Language :: Python :: 3.10
|
|
17
15
|
Classifier: Programming Language :: Python :: 3.11
|
|
18
16
|
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
19
19
|
Classifier: Topic :: Scientific/Engineering
|
|
20
|
-
Requires-Python: >=3.
|
|
20
|
+
Requires-Python: >=3.11
|
|
21
21
|
Description-Content-Type: text/x-rst
|
|
22
22
|
License-File: LICENSE
|
|
23
23
|
Requires-Dist: qcodes>=0.42.0
|
|
24
24
|
Requires-Dist: h5py>=3.0.0
|
|
25
25
|
Requires-Dist: lazy_loader>=0.1
|
|
26
26
|
Requires-Dist: matplotlib>=3.3.0
|
|
27
|
-
Requires-Dist: numpy
|
|
27
|
+
Requires-Dist: numpy<2.5.0,>=1.21.0
|
|
28
28
|
Requires-Dist: pandas>=1.0.0
|
|
29
29
|
Requires-Dist: versioningit>=2.2.1
|
|
30
30
|
Requires-Dist: xarray>=0.18.0
|
|
@@ -42,19 +42,20 @@ Requires-Dist: hypothesis>=5.49.0; extra == "test"
|
|
|
42
42
|
Requires-Dist: pytest>=6.1.0; extra == "test"
|
|
43
43
|
Requires-Dist: pytest-xdist>=2.0.0; extra == "test"
|
|
44
44
|
Requires-Dist: pytest-mock>=3.0.0; extra == "test"
|
|
45
|
-
Requires-Dist: pyqtgraph>=0.
|
|
46
|
-
Requires-Dist:
|
|
45
|
+
Requires-Dist: pyqtgraph>=0.13.0; extra == "test"
|
|
46
|
+
Requires-Dist: pyside6>=6.8.0; extra == "test"
|
|
47
47
|
Requires-Dist: slack-sdk>=3.4.2; extra == "test"
|
|
48
48
|
Requires-Dist: requests; extra == "test"
|
|
49
49
|
Requires-Dist: urllib3; extra == "test"
|
|
50
50
|
Provides-Extra: docs
|
|
51
51
|
Requires-Dist: nbsphinx>=0.8.9; extra == "docs"
|
|
52
|
-
Requires-Dist:
|
|
53
|
-
Requires-Dist: pyqtgraph>=0.
|
|
54
|
-
Requires-Dist: sphinx<9.
|
|
52
|
+
Requires-Dist: pyside6>=6.8.0; extra == "docs"
|
|
53
|
+
Requires-Dist: pyqtgraph>=0.13.0; extra == "docs"
|
|
54
|
+
Requires-Dist: sphinx<9.2.0,>=4.5.0; extra == "docs"
|
|
55
55
|
Requires-Dist: slack-sdk>=3.4.2; extra == "docs"
|
|
56
56
|
Requires-Dist: requests; extra == "docs"
|
|
57
57
|
Requires-Dist: urllib3; extra == "docs"
|
|
58
|
+
Dynamic: license-file
|
|
58
59
|
|
|
59
60
|
qcodes_loop
|
|
60
61
|
===========
|
|
@@ -14,20 +14,20 @@ classifiers = [
|
|
|
14
14
|
"Intended Audience :: Science/Research",
|
|
15
15
|
"License :: OSI Approved :: MIT License",
|
|
16
16
|
"Programming Language :: Python :: 3 :: Only",
|
|
17
|
-
"Programming Language :: Python :: 3.9",
|
|
18
|
-
"Programming Language :: Python :: 3.10",
|
|
19
17
|
"Programming Language :: Python :: 3.11",
|
|
20
18
|
"Programming Language :: Python :: 3.12",
|
|
19
|
+
"Programming Language :: Python :: 3.13",
|
|
20
|
+
"Programming Language :: Python :: 3.14",
|
|
21
21
|
"Topic :: Scientific/Engineering",
|
|
22
22
|
]
|
|
23
23
|
license = {text = "MIT"}
|
|
24
|
-
requires-python = ">=3.
|
|
24
|
+
requires-python = ">=3.11"
|
|
25
25
|
dependencies = [
|
|
26
26
|
"qcodes>=0.42.0",
|
|
27
27
|
"h5py>=3.0.0",
|
|
28
28
|
"lazy_loader>=0.1",
|
|
29
29
|
"matplotlib>=3.3.0",
|
|
30
|
-
"numpy>=1.21.0",
|
|
30
|
+
"numpy>=1.21.0,<2.5.0", # 2.4 removed support for assigning non scalar values to array elements. Code needs updating for this
|
|
31
31
|
"pandas>=1.0.0",
|
|
32
32
|
"versioningit>=2.2.1",
|
|
33
33
|
"xarray>=0.18.0",
|
|
@@ -56,17 +56,17 @@ test = [
|
|
|
56
56
|
"pytest>=6.1.0",
|
|
57
57
|
"pytest-xdist>=2.0.0",
|
|
58
58
|
"pytest-mock>=3.0.0",
|
|
59
|
-
"pyqtgraph>=0.
|
|
60
|
-
"
|
|
59
|
+
"pyqtgraph>=0.13.0", # pyqtgraph tests
|
|
60
|
+
"pyside6>=6.8.0", # pyqtgraph tests
|
|
61
61
|
"slack-sdk>=3.4.2", # slack tests and typecheck
|
|
62
62
|
"requests", # slack tests and typecheck
|
|
63
63
|
"urllib3", # slack tests and typecheck
|
|
64
64
|
]
|
|
65
65
|
docs = [
|
|
66
66
|
"nbsphinx>=0.8.9",
|
|
67
|
-
"
|
|
68
|
-
"pyqtgraph>=0.
|
|
69
|
-
"sphinx>=4.5.0,<9.
|
|
67
|
+
"pyside6>=6.8.0", # pyqtgraph examples
|
|
68
|
+
"pyqtgraph>=0.13.0", # pyqtgraph examples
|
|
69
|
+
"sphinx>=4.5.0,<9.2.0",
|
|
70
70
|
"slack-sdk>=3.4.2", # slack docs
|
|
71
71
|
"requests", # slack docs
|
|
72
72
|
"urllib3", # slack docs
|
|
@@ -83,12 +83,6 @@ exclude_lines = [
|
|
|
83
83
|
"if TYPE_CHECKING:",
|
|
84
84
|
]
|
|
85
85
|
|
|
86
|
-
[tool.darker]
|
|
87
|
-
isort = true
|
|
88
|
-
|
|
89
|
-
[tool.isort]
|
|
90
|
-
profile = "black"
|
|
91
|
-
|
|
92
86
|
[tool.pytest.ini_options]
|
|
93
87
|
minversion = "6.0"
|
|
94
88
|
junit_family = "legacy"
|
|
@@ -104,6 +98,11 @@ filterwarnings = [
|
|
|
104
98
|
"ignore:SelectableGroups dict interface is deprecated:DeprecationWarning"
|
|
105
99
|
]
|
|
106
100
|
|
|
101
|
+
[tool.ruff]
|
|
102
|
+
|
|
103
|
+
[tool.ruff.lint]
|
|
104
|
+
|
|
105
|
+
select = ["I","UP"]
|
|
107
106
|
[tool.setuptools.cmdclass]
|
|
108
107
|
sdist = "versioningit.cmdclass.sdist"
|
|
109
108
|
build_py = "versioningit.cmdclass.build_py"
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
"""Actions, mainly to be executed in measurement Loops."""
|
|
2
|
+
|
|
2
3
|
import time
|
|
3
4
|
|
|
4
5
|
from qcodes.utils import is_function, thread_map
|
|
5
6
|
|
|
6
|
-
_NO_SNAPSHOT = {
|
|
7
|
+
_NO_SNAPSHOT = {"type": None, "description": "Action without snapshot"}
|
|
7
8
|
|
|
8
9
|
|
|
9
10
|
# exception when threading is attempted used to simultaneously
|
|
@@ -16,7 +17,7 @@ def _actions_snapshot(actions, update):
|
|
|
16
17
|
"""Make a list of snapshots from a list of actions."""
|
|
17
18
|
snapshot = []
|
|
18
19
|
for action in actions:
|
|
19
|
-
if hasattr(action,
|
|
20
|
+
if hasattr(action, "snapshot"):
|
|
20
21
|
snapshot.append(action.snapshot(update=update))
|
|
21
22
|
else:
|
|
22
23
|
snapshot.append(_NO_SNAPSHOT)
|
|
@@ -41,6 +42,7 @@ class Task:
|
|
|
41
42
|
**kwargs: pass to func, after evaluation if callable
|
|
42
43
|
|
|
43
44
|
"""
|
|
45
|
+
|
|
44
46
|
def __init__(self, func, *args, **kwargs):
|
|
45
47
|
self.func = func
|
|
46
48
|
self.args = args
|
|
@@ -62,7 +64,7 @@ class Task:
|
|
|
62
64
|
Returns:
|
|
63
65
|
dict: snapshot
|
|
64
66
|
"""
|
|
65
|
-
return {
|
|
67
|
+
return {"type": "Task", "func": repr(self.func)}
|
|
66
68
|
|
|
67
69
|
|
|
68
70
|
class Wait:
|
|
@@ -80,6 +82,7 @@ class Wait:
|
|
|
80
82
|
Raises:
|
|
81
83
|
ValueError: if delay is negative
|
|
82
84
|
"""
|
|
85
|
+
|
|
83
86
|
def __init__(self, delay):
|
|
84
87
|
if not delay >= 0:
|
|
85
88
|
raise ValueError(f"delay must be > 0, not {repr(delay)}")
|
|
@@ -98,7 +101,7 @@ class Wait:
|
|
|
98
101
|
Returns:
|
|
99
102
|
dict: snapshot
|
|
100
103
|
"""
|
|
101
|
-
return {
|
|
104
|
+
return {"type": "Wait", "delay": self.delay}
|
|
102
105
|
|
|
103
106
|
|
|
104
107
|
class _Measure:
|
|
@@ -107,6 +110,7 @@ class _Measure:
|
|
|
107
110
|
|
|
108
111
|
This should not be constructed manually, only by an ActiveLoop.
|
|
109
112
|
"""
|
|
113
|
+
|
|
110
114
|
def __init__(self, params_indices, data_set, use_threads):
|
|
111
115
|
self.use_threads = use_threads and len(params_indices) > 1
|
|
112
116
|
# the applicable DataSet.store function
|
|
@@ -124,7 +128,7 @@ class _Measure:
|
|
|
124
128
|
if param._instrument:
|
|
125
129
|
paramcheck.append((param, param._instrument))
|
|
126
130
|
|
|
127
|
-
if hasattr(param,
|
|
131
|
+
if hasattr(param, "names"):
|
|
128
132
|
part_ids = []
|
|
129
133
|
for i in range(len(param.names)):
|
|
130
134
|
param_id = data_set.action_id_map[action_indices + (i,)]
|
|
@@ -138,14 +142,16 @@ class _Measure:
|
|
|
138
142
|
|
|
139
143
|
if self.use_threads:
|
|
140
144
|
insts = [p[1] for p in paramcheck]
|
|
141
|
-
if
|
|
145
|
+
if len(set(insts)) != len(insts):
|
|
142
146
|
duplicates = [p for p in paramcheck if insts.count(p[1]) > 1]
|
|
143
|
-
raise UnsafeThreadingException(
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
147
|
+
raise UnsafeThreadingException(
|
|
148
|
+
"Can not use threading to "
|
|
149
|
+
"read "
|
|
150
|
+
"several things from the same "
|
|
151
|
+
"instrument. Specifically, you "
|
|
152
|
+
"asked for"
|
|
153
|
+
f" {duplicates}."
|
|
154
|
+
)
|
|
149
155
|
|
|
150
156
|
def __call__(self, loop_indices, **ignore_kwargs):
|
|
151
157
|
out_dict = {}
|
|
@@ -154,8 +160,7 @@ class _Measure:
|
|
|
154
160
|
else:
|
|
155
161
|
out = [g() for g in self.getters]
|
|
156
162
|
|
|
157
|
-
for param_out, param_id, composite in zip(out, self.param_ids,
|
|
158
|
-
self.composite):
|
|
163
|
+
for param_out, param_id, composite in zip(out, self.param_ids, self.composite):
|
|
159
164
|
if composite:
|
|
160
165
|
for val, part_id in zip(param_out, composite):
|
|
161
166
|
out_dict[part_id] = val
|
|
@@ -166,7 +171,6 @@ class _Measure:
|
|
|
166
171
|
|
|
167
172
|
|
|
168
173
|
class _Nest:
|
|
169
|
-
|
|
170
174
|
"""
|
|
171
175
|
Wrapper to make a callable nested ActiveLoop.
|
|
172
176
|
|
|
@@ -182,7 +186,6 @@ class _Nest:
|
|
|
182
186
|
|
|
183
187
|
|
|
184
188
|
class BreakIf:
|
|
185
|
-
|
|
186
189
|
"""
|
|
187
190
|
Loop action that breaks out of the loop if a condition is truthy.
|
|
188
191
|
|
|
@@ -198,8 +201,7 @@ class BreakIf:
|
|
|
198
201
|
|
|
199
202
|
def __init__(self, condition):
|
|
200
203
|
if not is_function(condition, 0):
|
|
201
|
-
raise TypeError(
|
|
202
|
-
'no arguments')
|
|
204
|
+
raise TypeError("BreakIf condition must be a callable with no arguments")
|
|
203
205
|
self.condition = condition
|
|
204
206
|
|
|
205
207
|
def __call__(self, **ignore_kwargs):
|
|
@@ -216,7 +218,7 @@ class BreakIf:
|
|
|
216
218
|
dict: snapshot
|
|
217
219
|
|
|
218
220
|
"""
|
|
219
|
-
return {
|
|
221
|
+
return {"type": "BreakIf", "condition": repr(self.condition)}
|
|
220
222
|
|
|
221
223
|
|
|
222
224
|
class _QcodesBreak(Exception):
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import collections.abc
|
|
2
|
-
from typing import TYPE_CHECKING, Any,
|
|
2
|
+
from typing import TYPE_CHECKING, Any, Optional
|
|
3
3
|
|
|
4
4
|
import numpy as np
|
|
5
5
|
|
|
@@ -12,8 +12,8 @@ from qcodes.utils import DelegateAttributes, full_class
|
|
|
12
12
|
|
|
13
13
|
_LOG = logging.getLogger(__name__)
|
|
14
14
|
|
|
15
|
-
class DataArray(DelegateAttributes):
|
|
16
15
|
|
|
16
|
+
class DataArray(DelegateAttributes):
|
|
17
17
|
"""
|
|
18
18
|
A container for one parameter in a measurement loop.
|
|
19
19
|
|
|
@@ -91,35 +91,46 @@ class DataArray(DelegateAttributes):
|
|
|
91
91
|
|
|
92
92
|
# attributes of self to include in the snapshot
|
|
93
93
|
SNAP_ATTRS = (
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
94
|
+
"array_id",
|
|
95
|
+
"name",
|
|
96
|
+
"shape",
|
|
97
|
+
"unit",
|
|
98
|
+
"label",
|
|
99
|
+
"action_indices",
|
|
100
|
+
"is_setpoint",
|
|
101
|
+
)
|
|
101
102
|
|
|
102
103
|
# attributes of the parameter (or keys in the incoming snapshot)
|
|
103
104
|
# to copy to DataArray attributes, if they aren't set some other way
|
|
104
|
-
COPY_ATTRS_FROM_INPUT = (
|
|
105
|
-
'name',
|
|
106
|
-
'label',
|
|
107
|
-
'unit')
|
|
105
|
+
COPY_ATTRS_FROM_INPUT = ("name", "label", "unit")
|
|
108
106
|
|
|
109
107
|
# keys in the parameter snapshot to omit from our snapshot
|
|
110
108
|
SNAP_OMIT_KEYS = (
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
109
|
+
"ts",
|
|
110
|
+
"value",
|
|
111
|
+
"__class__",
|
|
112
|
+
"set_arrays",
|
|
113
|
+
"shape",
|
|
114
|
+
"array_id",
|
|
115
|
+
"action_indices",
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
def __init__(
|
|
119
|
+
self,
|
|
120
|
+
parameter=None,
|
|
121
|
+
name=None,
|
|
122
|
+
full_name=None,
|
|
123
|
+
label=None,
|
|
124
|
+
snapshot=None,
|
|
125
|
+
array_id=None,
|
|
126
|
+
set_arrays=(),
|
|
127
|
+
shape=None,
|
|
128
|
+
action_indices=(),
|
|
129
|
+
unit=None,
|
|
130
|
+
units=None,
|
|
131
|
+
is_setpoint=False,
|
|
132
|
+
preset_data=None,
|
|
133
|
+
):
|
|
123
134
|
self.name = name
|
|
124
135
|
self.full_name = full_name or name
|
|
125
136
|
self.label = label
|
|
@@ -152,25 +163,23 @@ class DataArray(DelegateAttributes):
|
|
|
152
163
|
self._snapshot_input = {}
|
|
153
164
|
|
|
154
165
|
if parameter is not None:
|
|
155
|
-
param_full_name = getattr(parameter,
|
|
166
|
+
param_full_name = getattr(parameter, "full_name", None)
|
|
156
167
|
if param_full_name and not full_name:
|
|
157
168
|
self.full_name = parameter.full_name
|
|
158
169
|
|
|
159
|
-
if hasattr(parameter,
|
|
170
|
+
if hasattr(parameter, "snapshot") and not snapshot:
|
|
160
171
|
snapshot = parameter.snapshot()
|
|
161
172
|
else:
|
|
162
173
|
# TODO: why is this in an else clause?
|
|
163
174
|
for attr in self.COPY_ATTRS_FROM_INPUT:
|
|
164
|
-
if
|
|
165
|
-
not getattr(self, attr, None)):
|
|
175
|
+
if hasattr(parameter, attr) and not getattr(self, attr, None):
|
|
166
176
|
setattr(self, attr, getattr(parameter, attr))
|
|
167
177
|
|
|
168
178
|
for key, value in snapshot.items():
|
|
169
179
|
if key not in self.SNAP_OMIT_KEYS:
|
|
170
180
|
self._snapshot_input[key] = value
|
|
171
181
|
|
|
172
|
-
if
|
|
173
|
-
not getattr(self, key, None)):
|
|
182
|
+
if key in self.COPY_ATTRS_FROM_INPUT and not getattr(self, key, None):
|
|
174
183
|
setattr(self, key, value)
|
|
175
184
|
|
|
176
185
|
if not self.label:
|
|
@@ -193,10 +202,12 @@ class DataArray(DelegateAttributes):
|
|
|
193
202
|
|
|
194
203
|
@data_set.setter
|
|
195
204
|
def data_set(self, new_data_set):
|
|
196
|
-
if (
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
205
|
+
if (
|
|
206
|
+
self._data_set is not None
|
|
207
|
+
and new_data_set is not None
|
|
208
|
+
and self._data_set != new_data_set
|
|
209
|
+
):
|
|
210
|
+
raise RuntimeError("A DataArray can only be part of one DataSet")
|
|
200
211
|
self._data_set = new_data_set
|
|
201
212
|
|
|
202
213
|
def nest(self, size, action_index=None, set_array=None):
|
|
@@ -225,20 +236,21 @@ class DataArray(DelegateAttributes):
|
|
|
225
236
|
chained method calls.
|
|
226
237
|
"""
|
|
227
238
|
if self.ndarray is not None and not self._preset:
|
|
228
|
-
raise RuntimeError(
|
|
229
|
-
|
|
239
|
+
raise RuntimeError(
|
|
240
|
+
f"Only preset arrays can be nested after data is initialized! {self}"
|
|
241
|
+
)
|
|
230
242
|
|
|
231
243
|
if set_array is None:
|
|
232
244
|
if self.set_arrays:
|
|
233
|
-
raise TypeError(
|
|
245
|
+
raise TypeError("a setpoint array must be its own inner loop")
|
|
234
246
|
set_array = self
|
|
235
247
|
|
|
236
|
-
self.shape = (size,
|
|
248
|
+
self.shape = (size,) + self.shape
|
|
237
249
|
|
|
238
250
|
if action_index is not None:
|
|
239
|
-
self.action_indices = (action_index,
|
|
251
|
+
self.action_indices = (action_index,) + self.action_indices
|
|
240
252
|
|
|
241
|
-
self.set_arrays = (set_array,
|
|
253
|
+
self.set_arrays = (set_array,) + self.set_arrays
|
|
242
254
|
|
|
243
255
|
if self._preset:
|
|
244
256
|
inner_data = self.ndarray
|
|
@@ -289,9 +301,12 @@ class DataArray(DelegateAttributes):
|
|
|
289
301
|
if self.shape is None:
|
|
290
302
|
self.shape = data.shape
|
|
291
303
|
elif data.shape != self.shape:
|
|
292
|
-
raise ValueError(
|
|
293
|
-
|
|
294
|
-
|
|
304
|
+
raise ValueError(
|
|
305
|
+
"preset data must be a sequence "
|
|
306
|
+
"with shape matching the array shape",
|
|
307
|
+
data.shape,
|
|
308
|
+
self.shape,
|
|
309
|
+
)
|
|
295
310
|
self.ndarray = data
|
|
296
311
|
self._preset = True
|
|
297
312
|
|
|
@@ -300,8 +315,10 @@ class DataArray(DelegateAttributes):
|
|
|
300
315
|
|
|
301
316
|
elif self.ndarray is not None:
|
|
302
317
|
if self.ndarray.shape != self.shape:
|
|
303
|
-
raise ValueError(
|
|
304
|
-
|
|
318
|
+
raise ValueError(
|
|
319
|
+
"data has already been initialized, "
|
|
320
|
+
"but its shape doesn't match self.shape"
|
|
321
|
+
)
|
|
305
322
|
return
|
|
306
323
|
else:
|
|
307
324
|
self.ndarray = np.ndarray(self.shape)
|
|
@@ -319,7 +336,7 @@ class DataArray(DelegateAttributes):
|
|
|
319
336
|
# what people want anyway.
|
|
320
337
|
if self.ndarray.dtype != float:
|
|
321
338
|
self.ndarray = self.ndarray.astype(float)
|
|
322
|
-
self.ndarray.fill(float(
|
|
339
|
+
self.ndarray.fill(float("nan"))
|
|
323
340
|
|
|
324
341
|
def __setitem__(self, loop_indices, value):
|
|
325
342
|
"""
|
|
@@ -342,8 +359,7 @@ class DataArray(DelegateAttributes):
|
|
|
342
359
|
if isinstance(index, slice):
|
|
343
360
|
start, stop, step = index.indices(self.shape[i])
|
|
344
361
|
min_indices[i] = start
|
|
345
|
-
max_indices[i] = start + (
|
|
346
|
-
((stop - start - 1)//step) * step)
|
|
362
|
+
max_indices[i] = start + (((stop - start - 1) // step) * step)
|
|
347
363
|
|
|
348
364
|
min_li = self.flat_index(min_indices, self._min_indices)
|
|
349
365
|
max_li = self.flat_index(max_indices, self._max_indices)
|
|
@@ -354,7 +370,7 @@ class DataArray(DelegateAttributes):
|
|
|
354
370
|
def __getitem__(self, loop_indices):
|
|
355
371
|
return self.ndarray[loop_indices]
|
|
356
372
|
|
|
357
|
-
delegate_attr_objects = [
|
|
373
|
+
delegate_attr_objects = ["ndarray"]
|
|
358
374
|
|
|
359
375
|
def __len__(self):
|
|
360
376
|
"""
|
|
@@ -385,13 +401,15 @@ class DataArray(DelegateAttributes):
|
|
|
385
401
|
int: the resulting flat index.
|
|
386
402
|
"""
|
|
387
403
|
if len(indices) < len(self.shape):
|
|
388
|
-
indices = indices + index_fill[len(indices):]
|
|
404
|
+
indices = indices + index_fill[len(indices) :]
|
|
389
405
|
return np.ravel_multi_index(tuple(zip(indices)), self.shape)[0]
|
|
390
406
|
|
|
391
407
|
def _update_modified_range(self, low, high):
|
|
392
408
|
if self.modified_range:
|
|
393
|
-
self.modified_range = (
|
|
394
|
-
|
|
409
|
+
self.modified_range = (
|
|
410
|
+
min(self.modified_range[0], low),
|
|
411
|
+
max(self.modified_range[1], high),
|
|
412
|
+
)
|
|
395
413
|
else:
|
|
396
414
|
self.modified_range = (low, high)
|
|
397
415
|
|
|
@@ -410,9 +428,10 @@ class DataArray(DelegateAttributes):
|
|
|
410
428
|
if last_saved_index >= self.modified_range[1]:
|
|
411
429
|
self.modified_range = None
|
|
412
430
|
else:
|
|
413
|
-
self.modified_range = (
|
|
414
|
-
|
|
415
|
-
|
|
431
|
+
self.modified_range = (
|
|
432
|
+
max(self.modified_range[0], last_saved_index + 1),
|
|
433
|
+
self.modified_range[1],
|
|
434
|
+
)
|
|
416
435
|
self.last_saved_index = last_saved_index
|
|
417
436
|
|
|
418
437
|
def clear_save(self):
|
|
@@ -438,7 +457,7 @@ class DataArray(DelegateAttributes):
|
|
|
438
457
|
int: the last flat index which has been synced from the server,
|
|
439
458
|
or -1 if no data has been synced.
|
|
440
459
|
"""
|
|
441
|
-
if not hasattr(self,
|
|
460
|
+
if not hasattr(self, "synced_index"):
|
|
442
461
|
self.init_data()
|
|
443
462
|
self.synced_index = -1
|
|
444
463
|
|
|
@@ -471,11 +490,7 @@ class DataArray(DelegateAttributes):
|
|
|
471
490
|
]
|
|
472
491
|
|
|
473
492
|
if vals:
|
|
474
|
-
return {
|
|
475
|
-
'start': synced_index + 1,
|
|
476
|
-
'stop': latest_index,
|
|
477
|
-
'vals': vals
|
|
478
|
-
}
|
|
493
|
+
return {"start": synced_index + 1, "stop": latest_index, "vals": vals}
|
|
479
494
|
|
|
480
495
|
def apply_changes(self, start, stop, vals):
|
|
481
496
|
"""
|
|
@@ -497,14 +512,17 @@ class DataArray(DelegateAttributes):
|
|
|
497
512
|
self.synced_index = stop
|
|
498
513
|
|
|
499
514
|
def __repr__(self):
|
|
500
|
-
array_id_or_none = f
|
|
501
|
-
return
|
|
502
|
-
|
|
503
|
-
|
|
515
|
+
array_id_or_none = f" {self.array_id}" if self.array_id else ""
|
|
516
|
+
return "{}[{}]:{}\n{}".format(
|
|
517
|
+
self.__class__.__name__,
|
|
518
|
+
",".join(map(str, self.shape)),
|
|
519
|
+
array_id_or_none,
|
|
520
|
+
repr(self.ndarray),
|
|
521
|
+
)
|
|
504
522
|
|
|
505
523
|
def snapshot(self, update=False):
|
|
506
524
|
"""JSON representation of this DataArray."""
|
|
507
|
-
snap = {
|
|
525
|
+
snap = {"__class__": full_class(self)}
|
|
508
526
|
|
|
509
527
|
snap.update(self._snapshot_input)
|
|
510
528
|
|
|
@@ -531,25 +549,26 @@ class DataArray(DelegateAttributes):
|
|
|
531
549
|
last_index = max(last_index, self.last_saved_index)
|
|
532
550
|
if self.modified_range is not None:
|
|
533
551
|
last_index = max(last_index, self.modified_range[1])
|
|
534
|
-
if getattr(self,
|
|
552
|
+
if getattr(self, "synced_index", None) is not None:
|
|
535
553
|
last_index = max(last_index, self.synced_index)
|
|
536
554
|
|
|
537
555
|
return (last_index + 1) / self.ndarray.size
|
|
538
556
|
|
|
539
557
|
def to_xarray(self) -> "xr.DataArray":
|
|
540
|
-
"""
|
|
558
|
+
"""Return this DataArray as an xarray dataarray
|
|
541
559
|
|
|
542
560
|
Returns:
|
|
543
561
|
DataArray in xarray format
|
|
544
562
|
"""
|
|
545
563
|
import xarray as xr
|
|
564
|
+
|
|
546
565
|
xarray_dictionary = data_array_to_xarray_dictionary(self)
|
|
547
566
|
xarray_dataarray = xr.DataArray.from_dict(xarray_dictionary)
|
|
548
567
|
return xarray_dataarray
|
|
549
568
|
|
|
550
569
|
@classmethod
|
|
551
570
|
def from_xarray(
|
|
552
|
-
cls, xarray_dataarray: "xr.DataArray", array_id:
|
|
571
|
+
cls, xarray_dataarray: "xr.DataArray", array_id: str | None = None
|
|
553
572
|
) -> "DataArray":
|
|
554
573
|
"""Create a DataArray from an xarray DataArray
|
|
555
574
|
|
|
@@ -560,12 +579,14 @@ class DataArray(DelegateAttributes):
|
|
|
560
579
|
"""
|
|
561
580
|
xarray_dict = xarray_dataarray.to_dict()
|
|
562
581
|
if array_id is None:
|
|
563
|
-
array_id = list(xarray_dict[
|
|
564
|
-
data_array = xarray_data_array_dictionary_to_data_array(
|
|
582
|
+
array_id = list(xarray_dict["dims"])[0]
|
|
583
|
+
data_array = xarray_data_array_dictionary_to_data_array(
|
|
584
|
+
array_id, xarray_dict, is_setpoint=False
|
|
585
|
+
)
|
|
565
586
|
return data_array
|
|
566
587
|
|
|
567
588
|
|
|
568
|
-
def data_array_to_xarray_dictionary(data_array: DataArray) ->
|
|
589
|
+
def data_array_to_xarray_dictionary(data_array: DataArray) -> dict[str, Any]:
|
|
569
590
|
"""Convert DataArray to a dictionary in xarray format.
|
|
570
591
|
|
|
571
592
|
Args:
|
|
@@ -576,7 +597,7 @@ def data_array_to_xarray_dictionary(data_array: DataArray) -> Dict[str, Any]:
|
|
|
576
597
|
"""
|
|
577
598
|
key_mapping = {"unit": "units", "name": "name", "label": "long_name"}
|
|
578
599
|
|
|
579
|
-
data_dictionary:
|
|
600
|
+
data_dictionary: dict[str, Any] = {"name": data_array.array_id}
|
|
580
601
|
data_dictionary["attrs"] = {
|
|
581
602
|
target_key: getattr(data_array, key) for key, target_key in key_mapping.items()
|
|
582
603
|
}
|
|
@@ -598,7 +619,11 @@ def data_array_to_xarray_dictionary(data_array: DataArray) -> Dict[str, Any]:
|
|
|
598
619
|
|
|
599
620
|
|
|
600
621
|
def xarray_data_array_dictionary_to_data_array(
|
|
601
|
-
|
|
622
|
+
array_id: str,
|
|
623
|
+
array_dictionary: dict[str, Any],
|
|
624
|
+
is_setpoint: bool = False,
|
|
625
|
+
preset_data=None,
|
|
626
|
+
):
|
|
602
627
|
"""Convert xarray dictionary to a DataArray
|
|
603
628
|
|
|
604
629
|
This conversion is for bith the data array and the the internal xarray structure, e.g. the datavars and coords.
|