essreduce 24.11.2__py3-none-any.whl → 24.12.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.
- ess/reduce/live/raw.py +3 -1
- ess/reduce/nexus/types.py +29 -2
- ess/reduce/nexus/workflow.py +49 -22
- ess/reduce/parameter.py +11 -1
- ess/reduce/ui.py +87 -6
- ess/reduce/uncertainty.py +11 -11
- ess/reduce/widgets/__init__.py +25 -21
- ess/reduce/widgets/_base.py +63 -0
- ess/reduce/widgets/_binedges_widget.py +3 -1
- ess/reduce/widgets/_bounds_widget.py +2 -1
- ess/reduce/widgets/_filename_widget.py +10 -0
- ess/reduce/widgets/_linspace_widget.py +3 -1
- ess/reduce/widgets/_string_widget.py +48 -0
- ess/reduce/widgets/_vector_widget.py +3 -1
- ess/reduce/workflow.py +1 -4
- {essreduce-24.11.2.dist-info → essreduce-24.12.0.dist-info}/METADATA +1 -1
- essreduce-24.12.0.dist-info/RECORD +36 -0
- essreduce-24.11.2.dist-info/RECORD +0 -33
- {essreduce-24.11.2.dist-info → essreduce-24.12.0.dist-info}/LICENSE +0 -0
- {essreduce-24.11.2.dist-info → essreduce-24.12.0.dist-info}/WHEEL +0 -0
- {essreduce-24.11.2.dist-info → essreduce-24.12.0.dist-info}/entry_points.txt +0 -0
- {essreduce-24.11.2.dist-info → essreduce-24.12.0.dist-info}/top_level.txt +0 -0
ess/reduce/live/raw.py
CHANGED
|
@@ -285,7 +285,9 @@ class RollingDetectorView(Detector):
|
|
|
285
285
|
wf[RollingDetectorViewWindow] = window
|
|
286
286
|
if isinstance(projection, LogicalView):
|
|
287
287
|
wf[LogicalView] = projection
|
|
288
|
-
wf[NeXusTransformation[snx.NXdetector, SampleRun]] =
|
|
288
|
+
wf[NeXusTransformation[snx.NXdetector, SampleRun]] = NeXusTransformation[
|
|
289
|
+
snx.NXdetector, SampleRun
|
|
290
|
+
](sc.scalar(1))
|
|
289
291
|
wf.insert(RollingDetectorView.from_detector_and_logical_view)
|
|
290
292
|
elif projection == 'cylinder_mantle_z':
|
|
291
293
|
wf.insert(make_cylinder_mantle_coords)
|
ess/reduce/nexus/types.py
CHANGED
|
@@ -78,7 +78,17 @@ RunType = TypeVar(
|
|
|
78
78
|
TransmissionRun[BackgroundRun],
|
|
79
79
|
VanadiumRun,
|
|
80
80
|
)
|
|
81
|
-
"""TypeVar
|
|
81
|
+
"""TypeVar for specifying what run some data belongs to.
|
|
82
|
+
|
|
83
|
+
Possible values:
|
|
84
|
+
|
|
85
|
+
- :class:`BackgroundRun`
|
|
86
|
+
- :class:`EmptyBeamRun`
|
|
87
|
+
- :class:`SampleRun`
|
|
88
|
+
- :class:`TransmissionRun`
|
|
89
|
+
- :class:`VanadiumRun`
|
|
90
|
+
"""
|
|
91
|
+
|
|
82
92
|
|
|
83
93
|
# 1.2 Monitor types
|
|
84
94
|
Monitor1 = NewType('Monitor1', int)
|
|
@@ -91,6 +101,8 @@ Monitor4 = NewType('Monitor4', int)
|
|
|
91
101
|
"""Identifier for an arbitrary monitor"""
|
|
92
102
|
Monitor5 = NewType('Monitor5', int)
|
|
93
103
|
"""Identifier for an arbitrary monitor"""
|
|
104
|
+
Monitor6 = NewType('Monitor6', int)
|
|
105
|
+
"""Identifier for an arbitrary monitor"""
|
|
94
106
|
IncidentMonitor = NewType('IncidentMonitor', int)
|
|
95
107
|
"""Incident monitor"""
|
|
96
108
|
TransmissionMonitor = NewType('TransmissionMonitor', int)
|
|
@@ -102,10 +114,24 @@ MonitorType = TypeVar(
|
|
|
102
114
|
Monitor3,
|
|
103
115
|
Monitor4,
|
|
104
116
|
Monitor5,
|
|
117
|
+
Monitor6,
|
|
105
118
|
IncidentMonitor,
|
|
106
119
|
TransmissionMonitor,
|
|
107
120
|
)
|
|
108
|
-
"""TypeVar
|
|
121
|
+
"""TypeVar for specifying what monitor some data belongs to.
|
|
122
|
+
|
|
123
|
+
Possible values:
|
|
124
|
+
|
|
125
|
+
- :class:`Monitor1`
|
|
126
|
+
- :class:`Monitor2`
|
|
127
|
+
- :class:`Monitor3`
|
|
128
|
+
- :class:`Monitor4`
|
|
129
|
+
- :class:`Monitor5`
|
|
130
|
+
- :class:`Monitor6`
|
|
131
|
+
- :class:`IncidentMonitor`
|
|
132
|
+
- :class:`TransmissionMonitor`
|
|
133
|
+
"""
|
|
134
|
+
|
|
109
135
|
|
|
110
136
|
Component = TypeVar(
|
|
111
137
|
'Component',
|
|
@@ -119,6 +145,7 @@ Component = TypeVar(
|
|
|
119
145
|
Monitor3,
|
|
120
146
|
Monitor4,
|
|
121
147
|
Monitor5,
|
|
148
|
+
Monitor6,
|
|
122
149
|
IncidentMonitor,
|
|
123
150
|
TransmissionMonitor,
|
|
124
151
|
)
|
ess/reduce/nexus/workflow.py
CHANGED
|
@@ -3,12 +3,12 @@
|
|
|
3
3
|
|
|
4
4
|
"""Workflow and workflow components for interacting with NeXus files."""
|
|
5
5
|
|
|
6
|
-
from collections.abc import
|
|
6
|
+
from collections.abc import Iterable
|
|
7
7
|
from copy import deepcopy
|
|
8
8
|
from typing import Any
|
|
9
9
|
|
|
10
|
-
import networkx as nx
|
|
11
10
|
import sciline
|
|
11
|
+
import sciline.typing
|
|
12
12
|
import scipp as sc
|
|
13
13
|
import scippnexus as snx
|
|
14
14
|
from scipp.constants import g
|
|
@@ -649,30 +649,39 @@ def LoadDetectorWorkflow() -> sciline.Pipeline:
|
|
|
649
649
|
|
|
650
650
|
def GenericNeXusWorkflow(
|
|
651
651
|
*,
|
|
652
|
-
run_types:
|
|
653
|
-
monitor_types:
|
|
652
|
+
run_types: Iterable[sciline.typing.Key] | None = None,
|
|
653
|
+
monitor_types: Iterable[sciline.typing.Key] | None = None,
|
|
654
654
|
) -> sciline.Pipeline:
|
|
655
655
|
"""
|
|
656
656
|
Generic workflow for loading detector and monitor data from a NeXus file.
|
|
657
657
|
|
|
658
|
+
It is possible to limit which run types and monitor types
|
|
659
|
+
are supported by the returned workflow.
|
|
660
|
+
This is useful to reduce the size of the workflow and make it easier to inspect.
|
|
661
|
+
Make sure to add *all* required run types and monitor types when using this feature.
|
|
662
|
+
|
|
663
|
+
Attention
|
|
664
|
+
---------
|
|
665
|
+
Filtering by run type and monitor type does not work with nested type vars.
|
|
666
|
+
E.g., if you have a type like ``Outer[Inner[RunType]]``, this type and its
|
|
667
|
+
provider will be removed.
|
|
668
|
+
|
|
658
669
|
Parameters
|
|
659
670
|
----------
|
|
660
671
|
run_types:
|
|
661
672
|
List of run types to include in the workflow. If not provided, all run types
|
|
662
|
-
are included.
|
|
663
|
-
|
|
673
|
+
are included.
|
|
674
|
+
Must be a possible value of :class:`ess.reduce.nexus.types.RunType`.
|
|
664
675
|
monitor_types:
|
|
665
676
|
List of monitor types to include in the workflow. If not provided, all monitor
|
|
666
|
-
types are included.
|
|
667
|
-
|
|
677
|
+
types are included.
|
|
678
|
+
Must be a possible value of :class:`ess.reduce.nexus.types.MonitorType`.
|
|
668
679
|
|
|
669
680
|
Returns
|
|
670
681
|
-------
|
|
671
682
|
:
|
|
672
683
|
The workflow.
|
|
673
684
|
"""
|
|
674
|
-
if monitor_types is not None and run_types is None:
|
|
675
|
-
raise ValueError("run_types must be specified if monitor_types is specified")
|
|
676
685
|
wf = sciline.Pipeline(
|
|
677
686
|
(
|
|
678
687
|
*_common_providers,
|
|
@@ -685,16 +694,34 @@ def GenericNeXusWorkflow(
|
|
|
685
694
|
wf[DetectorBankSizes] = DetectorBankSizes({})
|
|
686
695
|
wf[PreopenNeXusFile] = PreopenNeXusFile(False)
|
|
687
696
|
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
# their ancestors is what we need to strip unused run and monitor types.
|
|
692
|
-
for rt in run_types or ():
|
|
693
|
-
ancestors |= nx.ancestors(g, DetectorData[rt])
|
|
694
|
-
ancestors.add(DetectorData[rt])
|
|
695
|
-
for mt in monitor_types or ():
|
|
696
|
-
ancestors |= nx.ancestors(g, MonitorData[rt, mt])
|
|
697
|
-
ancestors.add(MonitorData[rt, mt])
|
|
698
|
-
if run_types is not None:
|
|
699
|
-
g.remove_nodes_from(set(g.nodes) - ancestors)
|
|
697
|
+
if run_types is not None or monitor_types is not None:
|
|
698
|
+
_prune_type_vars(wf, run_types=run_types, monitor_types=monitor_types)
|
|
699
|
+
|
|
700
700
|
return wf
|
|
701
|
+
|
|
702
|
+
|
|
703
|
+
def _prune_type_vars(
|
|
704
|
+
workflow: sciline.Pipeline,
|
|
705
|
+
*,
|
|
706
|
+
run_types: Iterable[sciline.typing.Key] | None,
|
|
707
|
+
monitor_types: Iterable[sciline.typing.Key] | None,
|
|
708
|
+
) -> None:
|
|
709
|
+
# Remove all nodes that use a run type or monitor types that is
|
|
710
|
+
# not listed in the function arguments.
|
|
711
|
+
excluded_run_types = _excluded_type_args(RunType, run_types)
|
|
712
|
+
excluded_monitor_types = _excluded_type_args(MonitorType, monitor_types)
|
|
713
|
+
excluded_types = excluded_run_types | excluded_monitor_types
|
|
714
|
+
|
|
715
|
+
graph = workflow.underlying_graph
|
|
716
|
+
to_remove = [
|
|
717
|
+
node for node in graph if excluded_types & set(getattr(node, "__args__", set()))
|
|
718
|
+
]
|
|
719
|
+
graph.remove_nodes_from(to_remove)
|
|
720
|
+
|
|
721
|
+
|
|
722
|
+
def _excluded_type_args(
|
|
723
|
+
type_var: Any, keep: Iterable[sciline.typing.Key] | None
|
|
724
|
+
) -> set[sciline.typing.Key]:
|
|
725
|
+
if keep is None:
|
|
726
|
+
return set()
|
|
727
|
+
return set(type_var.__constraints__) - set(keep)
|
ess/reduce/parameter.py
CHANGED
|
@@ -71,7 +71,12 @@ class ParamWithOptions(Parameter[T]):
|
|
|
71
71
|
|
|
72
72
|
@classmethod
|
|
73
73
|
def from_enum(cls: type[C], t: type[T], default: T) -> C:
|
|
74
|
-
return cls(
|
|
74
|
+
return cls(
|
|
75
|
+
name=t.__name__,
|
|
76
|
+
description=t.__doc__,
|
|
77
|
+
options=t.__members__,
|
|
78
|
+
default=default,
|
|
79
|
+
)
|
|
75
80
|
|
|
76
81
|
|
|
77
82
|
@dataclass
|
|
@@ -126,6 +131,11 @@ class StringParameter(Parameter[str]):
|
|
|
126
131
|
pass
|
|
127
132
|
|
|
128
133
|
|
|
134
|
+
@dataclass
|
|
135
|
+
class MultiStringParameter(Parameter[tuple[str, ...]]):
|
|
136
|
+
"""Widget for entering multiple strings."""
|
|
137
|
+
|
|
138
|
+
|
|
129
139
|
@dataclass(kw_only=True)
|
|
130
140
|
class ParamWithBounds(Parameter[T]):
|
|
131
141
|
bounds: tuple[T, T]
|
ess/reduce/ui.py
CHANGED
|
@@ -10,6 +10,7 @@ from ipywidgets import Layout
|
|
|
10
10
|
|
|
11
11
|
from .parameter import Parameter
|
|
12
12
|
from .widgets import SwitchWidget, create_parameter_widget, default_layout
|
|
13
|
+
from .widgets._base import get_fields, set_fields
|
|
13
14
|
from .workflow import (
|
|
14
15
|
Key,
|
|
15
16
|
assign_parameter_values,
|
|
@@ -81,11 +82,13 @@ class ParameterBox(widgets.VBox):
|
|
|
81
82
|
self._input_widgets.clear()
|
|
82
83
|
self._input_widgets.update(
|
|
83
84
|
{
|
|
84
|
-
node:
|
|
85
|
-
for node, parameter in
|
|
85
|
+
node: create_parameter_widget(parameter)
|
|
86
|
+
for node, parameter in new_input_parameters.items()
|
|
86
87
|
}
|
|
87
88
|
)
|
|
88
|
-
self._input_box.children =
|
|
89
|
+
self._input_box.children = [
|
|
90
|
+
widgets.HBox([widget]) for widget in self._input_widgets.values()
|
|
91
|
+
]
|
|
89
92
|
|
|
90
93
|
self.parameter_refresh_button.on_click(_refresh_input_box)
|
|
91
94
|
|
|
@@ -97,8 +100,7 @@ class ParameterBox(widgets.VBox):
|
|
|
97
100
|
return {
|
|
98
101
|
node: widget.value
|
|
99
102
|
for node, widget_box in self._input_widgets.items()
|
|
100
|
-
if (not isinstance((widget := widget_box
|
|
101
|
-
or widget.enabled
|
|
103
|
+
if (not isinstance((widget := widget_box), SwitchWidget)) or widget.enabled
|
|
102
104
|
}
|
|
103
105
|
|
|
104
106
|
|
|
@@ -130,7 +132,7 @@ class ResultBox(widgets.VBox):
|
|
|
130
132
|
result_registry.clear()
|
|
131
133
|
result_registry.update(compute_result)
|
|
132
134
|
for i in compute_result.values():
|
|
133
|
-
display.display(
|
|
135
|
+
display.display(i)
|
|
134
136
|
|
|
135
137
|
def clear_output(_: widgets.Button) -> None:
|
|
136
138
|
self.output.clear_output()
|
|
@@ -232,3 +234,82 @@ def workflow_widget(result_registry: dict | None = None) -> widgets.Widget:
|
|
|
232
234
|
workflow_selection_box = widgets.HBox([workflow_select], layout=default_layout)
|
|
233
235
|
workflow_box = widgets.Box(layout=default_layout)
|
|
234
236
|
return widgets.VBox([workflow_selection_box, workflow_box])
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
def _get_parameter_box(widget: WorkflowWidget | ParameterBox) -> ParameterBox:
|
|
240
|
+
if isinstance(widget, WorkflowWidget):
|
|
241
|
+
return widget.parameter_box
|
|
242
|
+
elif isinstance(widget, ParameterBox):
|
|
243
|
+
return widget
|
|
244
|
+
else:
|
|
245
|
+
raise TypeError(
|
|
246
|
+
f"Expected target_widget to be a WorkflowWidget or ParameterBox, "
|
|
247
|
+
f"got {type(widget)}."
|
|
248
|
+
)
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
def set_parameter_widget_values(
|
|
252
|
+
widget: WorkflowWidget | ParameterBox, new_parameter_values: dict[type, Any]
|
|
253
|
+
) -> None:
|
|
254
|
+
"""Set the values of the input widgets in the target widget.
|
|
255
|
+
|
|
256
|
+
Nodes that don't exist in the input widgets will be ignored.
|
|
257
|
+
|
|
258
|
+
Example
|
|
259
|
+
-------
|
|
260
|
+
.. code-block::
|
|
261
|
+
|
|
262
|
+
set_parameter_widget_values(widget, {
|
|
263
|
+
'WavelengthBins': {'start': 1.0, 'stop': 14.0, 'nbins': 500}
|
|
264
|
+
})
|
|
265
|
+
|
|
266
|
+
Parameters
|
|
267
|
+
----------
|
|
268
|
+
widget:
|
|
269
|
+
The widget containing the input widgets.
|
|
270
|
+
new_parameter_values:
|
|
271
|
+
A dictionary of values/state to set each fields/state or value of input widgets.
|
|
272
|
+
|
|
273
|
+
Raises
|
|
274
|
+
------
|
|
275
|
+
TypeError:
|
|
276
|
+
If the widget is not a WorkflowWidget or a ParameterBox.
|
|
277
|
+
|
|
278
|
+
"""
|
|
279
|
+
parameter_box = _get_parameter_box(widget)
|
|
280
|
+
# Walk through the existing input widgets and set the values
|
|
281
|
+
# ``node`s that don't exist in the input widgets will be ignored.
|
|
282
|
+
for node, widget in parameter_box._input_widgets.items():
|
|
283
|
+
if node in new_parameter_values:
|
|
284
|
+
# We shouldn't use `get` here because ``None`` is a valid value.
|
|
285
|
+
set_fields(widget, new_parameter_values[node])
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
def get_parameter_widget_values(
|
|
289
|
+
widget: WorkflowWidget | ParameterBox,
|
|
290
|
+
) -> dict[type, Any]:
|
|
291
|
+
"""Return the current values of the input widgets in the target widget.
|
|
292
|
+
|
|
293
|
+
The result of this function can be used to set the values of the input widgets
|
|
294
|
+
using the :py:func:`~set_parameter_widget_values` function.
|
|
295
|
+
|
|
296
|
+
Parameters
|
|
297
|
+
----------
|
|
298
|
+
widget:
|
|
299
|
+
The widget containing the input widgets.
|
|
300
|
+
|
|
301
|
+
Returns
|
|
302
|
+
-------
|
|
303
|
+
:
|
|
304
|
+
A dictionary of the current values/state of each input widget.
|
|
305
|
+
|
|
306
|
+
Raises
|
|
307
|
+
------
|
|
308
|
+
TypeError:
|
|
309
|
+
If the widget is not a WorkflowWidget or a ParameterBox.
|
|
310
|
+
|
|
311
|
+
"""
|
|
312
|
+
return {
|
|
313
|
+
node: get_fields(widget)
|
|
314
|
+
for node, widget in _get_parameter_box(widget)._input_widgets.items()
|
|
315
|
+
}
|
ess/reduce/uncertainty.py
CHANGED
|
@@ -13,7 +13,7 @@ The recommended use of this module is via the :py:func:`broadcast_uncertainties`
|
|
|
13
13
|
helper function.
|
|
14
14
|
"""
|
|
15
15
|
|
|
16
|
-
from enum import Enum
|
|
16
|
+
from enum import Enum, auto
|
|
17
17
|
from typing import TypeVar, overload
|
|
18
18
|
|
|
19
19
|
import numpy as np
|
|
@@ -23,18 +23,18 @@ from scipp.core.concepts import irreducible_mask
|
|
|
23
23
|
T = TypeVar("T", bound=sc.Variable | sc.DataArray)
|
|
24
24
|
|
|
25
25
|
|
|
26
|
-
UncertaintyBroadcastMode
|
|
27
|
-
|
|
28
|
-
)
|
|
29
|
-
"""
|
|
30
|
-
Mode for broadcasting uncertainties.
|
|
26
|
+
class UncertaintyBroadcastMode(Enum):
|
|
27
|
+
"""Mode for broadcasting uncertainties.
|
|
31
28
|
|
|
32
|
-
-
|
|
33
|
-
|
|
34
|
-
- `fail`: Do not broadcast, simply return the input data.
|
|
29
|
+
See https://doi.org/10.3233/JNR-220049 for context.
|
|
30
|
+
"""
|
|
35
31
|
|
|
36
|
-
|
|
37
|
-
"""
|
|
32
|
+
drop = auto()
|
|
33
|
+
"""Drop variances if the data is broadcast."""
|
|
34
|
+
upper_bound = auto()
|
|
35
|
+
"""Compute an upper bound for the variances."""
|
|
36
|
+
fail = auto()
|
|
37
|
+
"""Do not broadcast, simply return the input data."""
|
|
38
38
|
|
|
39
39
|
|
|
40
40
|
@overload
|
ess/reduce/widgets/__init__.py
CHANGED
|
@@ -10,6 +10,7 @@ from ..parameter import (
|
|
|
10
10
|
BooleanParameter,
|
|
11
11
|
FilenameParameter,
|
|
12
12
|
MultiFilenameParameter,
|
|
13
|
+
MultiStringParameter,
|
|
13
14
|
ParamWithOptions,
|
|
14
15
|
StringParameter,
|
|
15
16
|
Parameter,
|
|
@@ -21,9 +22,11 @@ from ..parameter import (
|
|
|
21
22
|
from ._config import default_layout, default_style
|
|
22
23
|
|
|
23
24
|
from ._binedges_widget import BinEdgesWidget
|
|
25
|
+
from ._filename_widget import FilenameWidget, MultiFilenameWidget
|
|
24
26
|
from ._linspace_widget import LinspaceWidget
|
|
25
27
|
from ._vector_widget import VectorWidget
|
|
26
28
|
from ._bounds_widget import BoundsWidget
|
|
29
|
+
from ._string_widget import MultiStringWidget, StringWidget
|
|
27
30
|
from ._switchable_widget import SwitchWidget
|
|
28
31
|
from ._optional_widget import OptionalWidget
|
|
29
32
|
|
|
@@ -101,42 +104,41 @@ def boolean_parameter_widget(param: BooleanParameter):
|
|
|
101
104
|
|
|
102
105
|
@create_parameter_widget.register(StringParameter)
|
|
103
106
|
def string_parameter_widget(param: StringParameter):
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
)
|
|
107
|
+
return StringWidget(
|
|
108
|
+
description=param.name,
|
|
109
|
+
value=param.default,
|
|
110
|
+
layout=default_layout,
|
|
111
|
+
style=default_style,
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
@create_parameter_widget.register(MultiStringParameter)
|
|
116
|
+
def multi_string_parameter_widget(param: MultiStringParameter):
|
|
117
|
+
return MultiStringWidget(
|
|
118
|
+
description=param.name,
|
|
119
|
+
value=param.default,
|
|
120
|
+
layout=default_layout,
|
|
121
|
+
style=default_style,
|
|
122
|
+
)
|
|
121
123
|
|
|
122
124
|
|
|
123
125
|
@create_parameter_widget.register(FilenameParameter)
|
|
124
126
|
def filename_parameter_widget(param: FilenameParameter):
|
|
125
|
-
return
|
|
127
|
+
return FilenameWidget(
|
|
126
128
|
description=param.name,
|
|
129
|
+
value=param.default,
|
|
127
130
|
layout=default_layout,
|
|
128
131
|
style=default_style,
|
|
129
|
-
value=param.default,
|
|
130
132
|
)
|
|
131
133
|
|
|
132
134
|
|
|
133
135
|
@create_parameter_widget.register(MultiFilenameParameter)
|
|
134
136
|
def multi_filename_parameter_widget(param: MultiFilenameParameter):
|
|
135
|
-
return
|
|
137
|
+
return MultiFilenameWidget(
|
|
136
138
|
description=param.name,
|
|
139
|
+
value=param.default,
|
|
137
140
|
layout=default_layout,
|
|
138
141
|
style=default_style,
|
|
139
|
-
value=param.default,
|
|
140
142
|
)
|
|
141
143
|
|
|
142
144
|
|
|
@@ -182,7 +184,9 @@ __all__ = [
|
|
|
182
184
|
'BinEdgesWidget',
|
|
183
185
|
'BoundsWidget',
|
|
184
186
|
'EssWidget',
|
|
187
|
+
'FilenameWidget',
|
|
185
188
|
'LinspaceWidget',
|
|
189
|
+
'MultiFilenameWidget',
|
|
186
190
|
'OptionalWidget',
|
|
187
191
|
'SwitchWidget',
|
|
188
192
|
'VectorWidget',
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# SPDX-License-Identifier: BSD-3-Clause
|
|
2
|
+
# Copyright (c) 2024 Scipp contributors (https://github.com/scipp)
|
|
3
|
+
import warnings
|
|
4
|
+
from typing import Any, Protocol, runtime_checkable
|
|
5
|
+
|
|
6
|
+
from ipywidgets import Widget
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@runtime_checkable
|
|
10
|
+
class WidgetWithFieldsProtocol(Protocol):
|
|
11
|
+
def set_fields(self, new_values: dict[str, Any]) -> None: ...
|
|
12
|
+
|
|
13
|
+
def get_fields(self) -> dict[str, Any]: ...
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class WidgetWithFieldsMixin:
|
|
17
|
+
def set_fields(self, new_values: dict[str, Any]) -> None:
|
|
18
|
+
# Extract valid fields
|
|
19
|
+
new_field_names = set(new_values.keys())
|
|
20
|
+
valid_field_names = new_field_names & set(self.fields.keys())
|
|
21
|
+
# Warn for invalid fields
|
|
22
|
+
invalid_field_names = new_field_names - valid_field_names
|
|
23
|
+
for field_name in invalid_field_names:
|
|
24
|
+
warning_msg = f"Cannot set field '{field_name}'."
|
|
25
|
+
" The field does not exist in the widget."
|
|
26
|
+
"The field value will be ignored."
|
|
27
|
+
warnings.warn(warning_msg, UserWarning, stacklevel=1)
|
|
28
|
+
# Set valid fields
|
|
29
|
+
for field_name in valid_field_names:
|
|
30
|
+
self.fields[field_name].value = new_values[field_name]
|
|
31
|
+
|
|
32
|
+
def get_fields(self) -> dict[str, Any]:
|
|
33
|
+
return {
|
|
34
|
+
field_name: field_sub_widget.value
|
|
35
|
+
for field_name, field_sub_widget in self.fields.items()
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _has_widget_value_setter(widget: Widget) -> bool:
|
|
40
|
+
widget_type = type(widget)
|
|
41
|
+
return (
|
|
42
|
+
widget_property := getattr(widget_type, 'value', None)
|
|
43
|
+
) is not None and getattr(widget_property, 'fset', None) is not None
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def set_fields(widget: Widget, new_values: Any) -> None:
|
|
47
|
+
if isinstance(widget, WidgetWithFieldsProtocol) and isinstance(new_values, dict):
|
|
48
|
+
widget.set_fields(new_values)
|
|
49
|
+
elif _has_widget_value_setter(widget):
|
|
50
|
+
widget.value = new_values
|
|
51
|
+
else:
|
|
52
|
+
warnings.warn(
|
|
53
|
+
f"Cannot set value or fields for widget of type {type(widget)}."
|
|
54
|
+
" The new_value(s) will be ignored.",
|
|
55
|
+
UserWarning,
|
|
56
|
+
stacklevel=1,
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def get_fields(widget: Widget) -> Any:
|
|
61
|
+
if isinstance(widget, WidgetWithFieldsProtocol):
|
|
62
|
+
return widget.get_fields()
|
|
63
|
+
return widget.value
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
import ipywidgets as ipw
|
|
4
4
|
import scipp as sc
|
|
5
5
|
|
|
6
|
+
from ._base import WidgetWithFieldsMixin
|
|
7
|
+
|
|
6
8
|
UNITS_LIBRARY = {
|
|
7
9
|
"wavelength": {"options": ("angstrom", "nm")},
|
|
8
10
|
"Q": {"options": ("1/angstrom", "1/nm")},
|
|
@@ -19,7 +21,7 @@ UNITS_LIBRARY = {
|
|
|
19
21
|
}
|
|
20
22
|
|
|
21
23
|
|
|
22
|
-
class BinEdgesWidget(ipw.HBox, ipw.ValueWidget):
|
|
24
|
+
class BinEdgesWidget(ipw.HBox, ipw.ValueWidget, WidgetWithFieldsMixin):
|
|
23
25
|
def __init__(
|
|
24
26
|
self,
|
|
25
27
|
name: str,
|
|
@@ -4,9 +4,10 @@ import scipp as sc
|
|
|
4
4
|
from ipywidgets import FloatText, GridBox, Label, Text, ValueWidget
|
|
5
5
|
|
|
6
6
|
from ..parameter import ParamWithBounds
|
|
7
|
+
from ._base import WidgetWithFieldsMixin
|
|
7
8
|
|
|
8
9
|
|
|
9
|
-
class BoundsWidget(GridBox, ValueWidget):
|
|
10
|
+
class BoundsWidget(GridBox, ValueWidget, WidgetWithFieldsMixin):
|
|
10
11
|
def __init__(self):
|
|
11
12
|
super().__init__()
|
|
12
13
|
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# SPDX-License-Identifier: BSD-3-Clause
|
|
2
|
+
# Copyright (c) 2024 Scipp contributors (https://github.com/scipp)
|
|
3
|
+
|
|
4
|
+
from ._string_widget import MultiStringWidget, StringWidget
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class FilenameWidget(StringWidget): ...
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class MultiFilenameWidget(MultiStringWidget): ...
|
|
@@ -3,8 +3,10 @@
|
|
|
3
3
|
import scipp as sc
|
|
4
4
|
from ipywidgets import FloatText, GridBox, IntText, Label, ValueWidget
|
|
5
5
|
|
|
6
|
+
from ._base import WidgetWithFieldsMixin
|
|
6
7
|
|
|
7
|
-
|
|
8
|
+
|
|
9
|
+
class LinspaceWidget(GridBox, ValueWidget, WidgetWithFieldsMixin):
|
|
8
10
|
def __init__(self, dim: str, unit: str):
|
|
9
11
|
super().__init__()
|
|
10
12
|
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# SPDX-License-Identifier: BSD-3-Clause
|
|
2
|
+
# Copyright (c) 2024 Scipp contributors (https://github.com/scipp)
|
|
3
|
+
from ipywidgets import HBox, Text, ValueWidget
|
|
4
|
+
|
|
5
|
+
from ._config import default_layout
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class StringWidget(HBox, ValueWidget):
|
|
9
|
+
def __init__(self, description: str, value: str | None = None, **kwargs):
|
|
10
|
+
super().__init__(layout=default_layout)
|
|
11
|
+
self.text_widget = Text(description=description, value=value, **kwargs)
|
|
12
|
+
self.children = [self.text_widget]
|
|
13
|
+
|
|
14
|
+
@property
|
|
15
|
+
def value(self) -> str | None:
|
|
16
|
+
v = self.text_widget.value.strip()
|
|
17
|
+
if not v:
|
|
18
|
+
return None
|
|
19
|
+
return v
|
|
20
|
+
|
|
21
|
+
@value.setter
|
|
22
|
+
def value(self, value: str | None):
|
|
23
|
+
if value is None:
|
|
24
|
+
self.text_widget.value = ''
|
|
25
|
+
else:
|
|
26
|
+
self.text_widget.value = value
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class MultiStringWidget(StringWidget):
|
|
30
|
+
def __init__(
|
|
31
|
+
self, description: str, value: tuple[str, ...] | None = None, **kwargs
|
|
32
|
+
):
|
|
33
|
+
# Special case handling to allow initialising with a single string
|
|
34
|
+
if not isinstance(value, str) and value is not None:
|
|
35
|
+
value = ', '.join(value)
|
|
36
|
+
|
|
37
|
+
super().__init__(description, value, **kwargs)
|
|
38
|
+
|
|
39
|
+
@property
|
|
40
|
+
def value(self) -> tuple[str, ...]:
|
|
41
|
+
v = super().value
|
|
42
|
+
if v is None:
|
|
43
|
+
return ()
|
|
44
|
+
return tuple(s.strip() for s in v.split(','))
|
|
45
|
+
|
|
46
|
+
@value.setter
|
|
47
|
+
def value(self, value: tuple[str, ...]):
|
|
48
|
+
self.text_widget.value = ', '.join(value)
|
|
@@ -4,8 +4,10 @@
|
|
|
4
4
|
import scipp as sc
|
|
5
5
|
from ipywidgets import FloatText, HBox, Label, Text, ValueWidget
|
|
6
6
|
|
|
7
|
+
from ._base import WidgetWithFieldsMixin
|
|
7
8
|
|
|
8
|
-
|
|
9
|
+
|
|
10
|
+
class VectorWidget(HBox, ValueWidget, WidgetWithFieldsMixin):
|
|
9
11
|
def __init__(self, name: str, variable: sc.Variable, components: str):
|
|
10
12
|
super().__init__()
|
|
11
13
|
|
ess/reduce/workflow.py
CHANGED
|
@@ -86,10 +86,7 @@ def assign_parameter_values(pipeline: Pipeline, values: dict[Key, Any]) -> Pipel
|
|
|
86
86
|
"""Set a value for a parameter in the pipeline."""
|
|
87
87
|
pipeline = pipeline.copy()
|
|
88
88
|
for key, value in values.items():
|
|
89
|
-
if (
|
|
90
|
-
isinstance(value, tuple)
|
|
91
|
-
and (mapper := parameter_mappers.get(key)) is not None
|
|
92
|
-
):
|
|
89
|
+
if (mapper := parameter_mappers.get(key)) is not None:
|
|
93
90
|
pipeline = mapper(pipeline, value)
|
|
94
91
|
else:
|
|
95
92
|
pipeline[key] = value
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
ess/reduce/__init__.py,sha256=-8a2I4mbJdBgNqKxZJe0lAEQJb-lRjX1xmZvGUWCmmU,382
|
|
2
|
+
ess/reduce/data.py,sha256=vaoeAJ6EpK1YghOiAALLdWiW17TgUnnnt0H-RGiGzXk,3756
|
|
3
|
+
ess/reduce/logging.py,sha256=6n8Czq4LZ3OK9ENlKsWSI1M3KvKv6_HSoUiV4__IUlU,357
|
|
4
|
+
ess/reduce/parameter.py,sha256=4sCfoKOI2HuO_Q7JLH_jAXnEOFANSn5P3NdaOBzhJxc,4635
|
|
5
|
+
ess/reduce/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
6
|
+
ess/reduce/streaming.py,sha256=nEO1Xg6LbQ36F44UaVmSNe2MSWmpwz97y9tnlc-z0zA,7493
|
|
7
|
+
ess/reduce/ui.py,sha256=LHgRhcU4ghhbNLk3b64VoA-0oM6yBf4_5a1Smhenmcs,10793
|
|
8
|
+
ess/reduce/uncertainty.py,sha256=LR4O6ApB6Z-W9gC_XW0ajupl8yFG-du0eee1AX_R-gk,6990
|
|
9
|
+
ess/reduce/workflow.py,sha256=sL34T_2Cjl_8iFlegujxI9VyOUwo6erVC8pOXnfWgYw,3060
|
|
10
|
+
ess/reduce/live/__init__.py,sha256=jPQVhihRVNtEDrE20PoKkclKV2aBF1lS7cCHootgFgI,204
|
|
11
|
+
ess/reduce/live/raw.py,sha256=GQmzgh-ByqGYaiB9srRtOxmg6jQqOGNdXxL5gngGy5U,18927
|
|
12
|
+
ess/reduce/live/workflow.py,sha256=bsbwvTqPhRO6mC__3b7MgU7DWwAnOvGvG-t2n22EKq8,4285
|
|
13
|
+
ess/reduce/nexus/__init__.py,sha256=PxJkhlGcFRzVU4SICBhymK5_5FjM5oXPZ8YUpd0v1pE,967
|
|
14
|
+
ess/reduce/nexus/_nexus_loader.py,sha256=-chpzKcZGr2cXmvSYlGiKdKV591mMG-0BsBusGWTer4,16119
|
|
15
|
+
ess/reduce/nexus/json_generator.py,sha256=ME2Xn8L7Oi3uHJk9ZZdCRQTRX-OV_wh9-DJn07Alplk,2529
|
|
16
|
+
ess/reduce/nexus/json_nexus.py,sha256=QrVc0p424nZ5dHX9gebAJppTw6lGZq9404P_OFl1giA,10282
|
|
17
|
+
ess/reduce/nexus/types.py,sha256=aQMPVURUF0JaMl-82LovPxtAuqtvD150AVg17VHPyhM,8982
|
|
18
|
+
ess/reduce/nexus/workflow.py,sha256=jzdh0ubp9Mmb98a04KIeM8Xo9bpAqpnsfwFWz2VllnQ,23676
|
|
19
|
+
ess/reduce/scripts/grow_nexus.py,sha256=hET3h06M0xlJd62E3palNLFvJMyNax2kK4XyJcOhl-I,3387
|
|
20
|
+
ess/reduce/widgets/__init__.py,sha256=cjRJp4qhzCQgXZFtiZgHNmHFJVLo2Z2MozVG_LZYKlI,5281
|
|
21
|
+
ess/reduce/widgets/_base.py,sha256=0OTQsyoHXRdBs14cWtmJVqQqJcM6EIRA8dct_NQWUic,2200
|
|
22
|
+
ess/reduce/widgets/_binedges_widget.py,sha256=ZCQsGjYHnJr9GFUn7NjoZc1CdsnAzm_fMzyF-fTKKVY,2785
|
|
23
|
+
ess/reduce/widgets/_bounds_widget.py,sha256=CAyswvMA49mGMl2413TKBMbuG0ULCQPEaSODq8ghiDo,1084
|
|
24
|
+
ess/reduce/widgets/_config.py,sha256=LywjxYau1hsBZ-c2W1_DQ4CRMaaai3anjA8Q8Hn0Y6Y,222
|
|
25
|
+
ess/reduce/widgets/_filename_widget.py,sha256=jlDZOvECJBBjsmfBHjhn8-ig_DQxp3C-Uy07DITs_nI,262
|
|
26
|
+
ess/reduce/widgets/_linspace_widget.py,sha256=5CWIxd-yZST3OILTHeA0Q6AMYt6FdTSs8UYvKKdmtHg,1240
|
|
27
|
+
ess/reduce/widgets/_optional_widget.py,sha256=A7vwcVwykj7CIyHSPVh6XL5o-8Z1uJTuzha8TR09Zlc,2216
|
|
28
|
+
ess/reduce/widgets/_string_widget.py,sha256=iPAdfANyXHf-nkfhgkyH6gQDklia0LebLTmwi3m-iYQ,1482
|
|
29
|
+
ess/reduce/widgets/_switchable_widget.py,sha256=SZi65C1iShTi5Xhh7W0A7uwA-12AVaH5xmiIWOkc27o,1726
|
|
30
|
+
ess/reduce/widgets/_vector_widget.py,sha256=aTaBqCFHZQhrIoX6-sSqFWCPePEW8HQt5kUio8jP1t8,1203
|
|
31
|
+
essreduce-24.12.0.dist-info/LICENSE,sha256=nVEiume4Qj6jMYfSRjHTM2jtJ4FGu0g-5Sdh7osfEYw,1553
|
|
32
|
+
essreduce-24.12.0.dist-info/METADATA,sha256=nmgXZ0Dnz80YbVTjNuBKomMX4k7n2hyq63TGeva2B7c,3619
|
|
33
|
+
essreduce-24.12.0.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
|
|
34
|
+
essreduce-24.12.0.dist-info/entry_points.txt,sha256=PMZOIYzCifHMTe4pK3HbhxUwxjFaZizYlLD0td4Isb0,66
|
|
35
|
+
essreduce-24.12.0.dist-info/top_level.txt,sha256=0JxTCgMKPLKtp14wb1-RKisQPQWX7i96innZNvHBr-s,4
|
|
36
|
+
essreduce-24.12.0.dist-info/RECORD,,
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
ess/reduce/__init__.py,sha256=-8a2I4mbJdBgNqKxZJe0lAEQJb-lRjX1xmZvGUWCmmU,382
|
|
2
|
-
ess/reduce/data.py,sha256=vaoeAJ6EpK1YghOiAALLdWiW17TgUnnnt0H-RGiGzXk,3756
|
|
3
|
-
ess/reduce/logging.py,sha256=6n8Czq4LZ3OK9ENlKsWSI1M3KvKv6_HSoUiV4__IUlU,357
|
|
4
|
-
ess/reduce/parameter.py,sha256=9glQk8UgsPDIdxq007bRi3xTKNe24nuAcNDYzYyTydk,4443
|
|
5
|
-
ess/reduce/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
6
|
-
ess/reduce/streaming.py,sha256=nEO1Xg6LbQ36F44UaVmSNe2MSWmpwz97y9tnlc-z0zA,7493
|
|
7
|
-
ess/reduce/ui.py,sha256=EjzyMUODHn-5-aHRXE3DDWUsaPbsQRiUP2F4o1Hq260,8427
|
|
8
|
-
ess/reduce/uncertainty.py,sha256=UrS7wB2aK8QoLD3qG2Ud-XmjDl7Y9dANY31k83-cZKs,6982
|
|
9
|
-
ess/reduce/workflow.py,sha256=v0_QlTEub9FLeZAbbZMV_NrLJUT61jTRCFa9Wjli7sE,3125
|
|
10
|
-
ess/reduce/live/__init__.py,sha256=jPQVhihRVNtEDrE20PoKkclKV2aBF1lS7cCHootgFgI,204
|
|
11
|
-
ess/reduce/live/raw.py,sha256=w-h-G-rXIFxwJ9rxIlCLQepGxLmUjtF2rbXtAUxDgZs,18849
|
|
12
|
-
ess/reduce/live/workflow.py,sha256=bsbwvTqPhRO6mC__3b7MgU7DWwAnOvGvG-t2n22EKq8,4285
|
|
13
|
-
ess/reduce/nexus/__init__.py,sha256=PxJkhlGcFRzVU4SICBhymK5_5FjM5oXPZ8YUpd0v1pE,967
|
|
14
|
-
ess/reduce/nexus/_nexus_loader.py,sha256=-chpzKcZGr2cXmvSYlGiKdKV591mMG-0BsBusGWTer4,16119
|
|
15
|
-
ess/reduce/nexus/json_generator.py,sha256=ME2Xn8L7Oi3uHJk9ZZdCRQTRX-OV_wh9-DJn07Alplk,2529
|
|
16
|
-
ess/reduce/nexus/json_nexus.py,sha256=QrVc0p424nZ5dHX9gebAJppTw6lGZq9404P_OFl1giA,10282
|
|
17
|
-
ess/reduce/nexus/types.py,sha256=Bthjg4T2qXYF1rjU9i0DRAEqkCuYXbV3qF2zWPl_s-Q,8571
|
|
18
|
-
ess/reduce/nexus/workflow.py,sha256=xcOjfJRECQ_CAxPbc0nr-BbGXe3rUISEDcu3g7D87Nk,22868
|
|
19
|
-
ess/reduce/scripts/grow_nexus.py,sha256=hET3h06M0xlJd62E3palNLFvJMyNax2kK4XyJcOhl-I,3387
|
|
20
|
-
ess/reduce/widgets/__init__.py,sha256=f5-wlyiUQtUFe2bi_DTTpTSx8jGF50snLdhoawZfLgE,5106
|
|
21
|
-
ess/reduce/widgets/_binedges_widget.py,sha256=09VXYAWJammkzyRYvfFpjzn0E2-BSaDco3osnTUuv8c,2720
|
|
22
|
-
ess/reduce/widgets/_bounds_widget.py,sha256=p5hADiSGfHKMfvEILQqpcqggu_G3bLf5vNrW5rYTA0s,1020
|
|
23
|
-
ess/reduce/widgets/_config.py,sha256=LywjxYau1hsBZ-c2W1_DQ4CRMaaai3anjA8Q8Hn0Y6Y,222
|
|
24
|
-
ess/reduce/widgets/_linspace_widget.py,sha256=73nPfNSinxfuHN5-kkBoo2oTuGygbLVXPvxaw35xsaU,1175
|
|
25
|
-
ess/reduce/widgets/_optional_widget.py,sha256=A7vwcVwykj7CIyHSPVh6XL5o-8Z1uJTuzha8TR09Zlc,2216
|
|
26
|
-
ess/reduce/widgets/_switchable_widget.py,sha256=SZi65C1iShTi5Xhh7W0A7uwA-12AVaH5xmiIWOkc27o,1726
|
|
27
|
-
ess/reduce/widgets/_vector_widget.py,sha256=BvuTtc-NGFTkMKIiZS0Elnl34Hi_3A4rthdNLoDI5fM,1138
|
|
28
|
-
essreduce-24.11.2.dist-info/LICENSE,sha256=nVEiume4Qj6jMYfSRjHTM2jtJ4FGu0g-5Sdh7osfEYw,1553
|
|
29
|
-
essreduce-24.11.2.dist-info/METADATA,sha256=Lv30Wx2xHZw6RqkkkRTcPX3FyLlk1BYtpDVddaKTNmQ,3619
|
|
30
|
-
essreduce-24.11.2.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
|
|
31
|
-
essreduce-24.11.2.dist-info/entry_points.txt,sha256=PMZOIYzCifHMTe4pK3HbhxUwxjFaZizYlLD0td4Isb0,66
|
|
32
|
-
essreduce-24.11.2.dist-info/top_level.txt,sha256=0JxTCgMKPLKtp14wb1-RKisQPQWX7i96innZNvHBr-s,4
|
|
33
|
-
essreduce-24.11.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|