essreduce 25.5.0__py3-none-any.whl → 25.5.2__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/data.py CHANGED
@@ -40,6 +40,15 @@ _bifrost_registry = Registry(
40
40
  )
41
41
 
42
42
 
43
+ _dream_registry = Registry(
44
+ instrument='dream',
45
+ files={
46
+ "TEST_977695_00068064.hdf": "md5:9e6ee9ec70d7c5e8c0c93b9e07e8949f",
47
+ },
48
+ version='2',
49
+ )
50
+
51
+
43
52
  _loki_registry = Registry(
44
53
  instrument='loki',
45
54
  files={
@@ -94,3 +103,11 @@ def loki_tutorial_background_run_60393() -> str:
94
103
  def loki_tutorial_sample_transmission_run() -> str:
95
104
  """Sample transmission run (sample + sample holder/can + transmission monitor)."""
96
105
  return _loki_registry.get_path('60394-2022-02-28_2215.nxs')
106
+
107
+
108
+ def dream_coda_test_file() -> str:
109
+ """CODA file for DREAM where most pulses have been removed.
110
+
111
+ See ``tools/shrink_nexus.py``.
112
+ """
113
+ return _dream_registry.get_path('TEST_977695_00068064.hdf')
ess/reduce/nexus/types.py CHANGED
@@ -8,6 +8,7 @@ import sciline
8
8
  import scipp as sc
9
9
  import scippnexus as snx
10
10
  from scippneutron import metadata as scn_meta
11
+ from scippneutron.chopper import DiskChopper
11
12
 
12
13
  FilePath = NewType('FilePath', Path)
13
14
  """Full path to a NeXus file on disk."""
@@ -69,19 +70,13 @@ class TransmissionRun(Generic[ScatteringRunType]):
69
70
  """
70
71
 
71
72
 
72
- RunType = TypeVar(
73
- 'RunType',
74
- BackgroundRun,
75
- EmptyBeamRun,
76
- SampleRun,
77
- # Note that mypy does not seem to like this nesting, may need to find a workaround
78
- TransmissionRun[SampleRun],
79
- TransmissionRun[BackgroundRun],
80
- VanadiumRun,
81
- )
73
+ RunType = TypeVar('RunType')
82
74
  """TypeVar for specifying what run some data belongs to.
83
75
 
84
- Possible values:
76
+ This type must be constrained when used in a Sciline pipeline.
77
+ E.g., by passing ``run_types`` to :class:`ess.reduce.nexus.GenericNeXusWorkflow`.
78
+
79
+ ESSreduce provides the following but custom types can be used:
85
80
 
86
81
  - :class:`BackgroundRun`
87
82
  - :class:`EmptyBeamRun`
@@ -92,58 +87,28 @@ Possible values:
92
87
 
93
88
 
94
89
  # 1.2 Monitor types
95
- Monitor1 = NewType('Monitor1', int)
96
- """Identifier for an arbitrary monitor"""
97
- Monitor2 = NewType('Monitor2', int)
98
- """Identifier for an arbitrary monitor"""
99
- Monitor3 = NewType('Monitor3', int)
100
- """Identifier for an arbitrary monitor"""
101
- Monitor4 = NewType('Monitor4', int)
102
- """Identifier for an arbitrary monitor"""
103
- Monitor5 = NewType('Monitor5', int)
104
- """Identifier for an arbitrary monitor"""
105
- Monitor6 = NewType('Monitor6', int)
106
- """Identifier for an arbitrary monitor"""
107
90
  IncidentMonitor = NewType('IncidentMonitor', int)
108
91
  """Incident monitor"""
109
92
  TransmissionMonitor = NewType('TransmissionMonitor', int)
110
93
  """Transmission monitor"""
111
- FrameMonitor0 = NewType('FrameMonitor', int)
94
+ FrameMonitor0 = NewType('FrameMonitor0', int)
112
95
  """Frame monitor number 0"""
113
- FrameMonitor1 = NewType('FrameMonitor', int)
96
+ FrameMonitor1 = NewType('FrameMonitor1', int)
114
97
  """Frame monitor number 1"""
115
- FrameMonitor2 = NewType('FrameMonitor', int)
98
+ FrameMonitor2 = NewType('FrameMonitor2', int)
116
99
  """Frame monitor number 2"""
117
- FrameMonitor3 = NewType('FrameMonitor', int)
100
+ FrameMonitor3 = NewType('FrameMonitor3', int)
118
101
  """Frame monitor number 3"""
119
102
  CaveMonitor = NewType('CaveMonitor', int)
120
103
  """A monitor located in the instrument cave"""
121
- MonitorType = TypeVar(
122
- 'MonitorType',
123
- Monitor1,
124
- Monitor2,
125
- Monitor3,
126
- Monitor4,
127
- Monitor5,
128
- Monitor6,
129
- IncidentMonitor,
130
- TransmissionMonitor,
131
- FrameMonitor0,
132
- FrameMonitor1,
133
- FrameMonitor2,
134
- FrameMonitor3,
135
- CaveMonitor,
136
- )
104
+ MonitorType = TypeVar('MonitorType')
137
105
  """TypeVar for specifying what monitor some data belongs to.
138
106
 
139
- Possible values:
107
+ This type must be constrained when used in a Sciline pipeline.
108
+ E.g., by passing ``monitor_types`` to :class:`ess.reduce.nexus.GenericNeXusWorkflow`.
109
+
110
+ ESSreduce provides the following but custom types can be used:
140
111
 
141
- - :class:`Monitor1`
142
- - :class:`Monitor2`
143
- - :class:`Monitor3`
144
- - :class:`Monitor4`
145
- - :class:`Monitor5`
146
- - :class:`Monitor6`
147
112
  - :class:`IncidentMonitor`
148
113
  - :class:`TransmissionMonitor`
149
114
  - :class:`FrameMonitor0`
@@ -154,27 +119,20 @@ Possible values:
154
119
  """
155
120
 
156
121
 
157
- Component = TypeVar(
158
- 'Component',
122
+ Component = TypeVar('Component')
123
+ """A beamline component in a neXus file."""
124
+ COMPONENT_CONSTRAINTS = (
159
125
  snx.NXdetector,
160
126
  snx.NXsample,
161
127
  snx.NXsource,
162
128
  snx.NXdisk_chopper,
163
129
  snx.NXcrystal,
164
- Monitor1,
165
- Monitor2,
166
- Monitor3,
167
- Monitor4,
168
- Monitor5,
169
- Monitor6,
170
- IncidentMonitor,
171
- TransmissionMonitor,
172
- FrameMonitor0,
173
- FrameMonitor1,
174
- FrameMonitor2,
175
- FrameMonitor3,
176
- CaveMonitor,
177
130
  )
131
+ """Base constraints for the Component type variable.
132
+
133
+ This list will be supplemented with monitor types when creating a pipeline.
134
+ """
135
+
178
136
  UniqueComponent = TypeVar('UniqueComponent', snx.NXsample, snx.NXsource)
179
137
  """Components that can be identified by their type as there will only be one."""
180
138
 
@@ -336,15 +294,15 @@ class NeXusTransformation(Generic[Component, RunType]):
336
294
  return NeXusTransformation(value=transform)
337
295
 
338
296
 
339
- class Choppers(
297
+ class RawChoppers(
340
298
  sciline.Scope[RunType, sc.DataGroup[sc.DataGroup[Any]]],
341
299
  sc.DataGroup[sc.DataGroup[Any]],
342
300
  ):
343
301
  """All choppers in a NeXus file."""
344
302
 
345
303
 
346
- class Analyzers(
347
- sciline.Scope[RunType, sc.DataGroup[sc.DataGroup[Any]]],
348
- sc.DataGroup[sc.DataGroup[Any]],
304
+ class DiskChoppers(
305
+ sciline.Scope[RunType, sc.DataGroup[DiskChopper]],
306
+ sc.DataGroup[DiskChopper],
349
307
  ):
350
- """All analyzers in a NeXus file."""
308
+ """All disk choppers parsed from a NeXus file."""
@@ -5,7 +5,7 @@
5
5
 
6
6
  from collections.abc import Iterable
7
7
  from copy import deepcopy
8
- from typing import Any
8
+ from typing import Any, TypeVar
9
9
 
10
10
  import sciline
11
11
  import sciline.typing
@@ -15,16 +15,14 @@ from scipp.constants import g
15
15
  from scipp.core import label_based_index_to_positional_index
16
16
  from scippneutron.chopper import extract_chopper_from_nexus
17
17
 
18
- from ..utils import prune_type_vars
19
18
  from . import _nexus_loader as nexus
20
19
  from .types import (
20
+ COMPONENT_CONSTRAINTS,
21
21
  AllNeXusComponents,
22
- Analyzers,
23
22
  Beamline,
24
23
  CalibratedBeamline,
25
24
  CalibratedDetector,
26
25
  CalibratedMonitor,
27
- Choppers,
28
26
  Component,
29
27
  DetectorBankSizes,
30
28
  DetectorData,
@@ -47,6 +45,7 @@ from .types import (
47
45
  NeXusTransformationChain,
48
46
  Position,
49
47
  PreopenNeXusFile,
48
+ RawChoppers,
50
49
  RunType,
51
50
  SampleRun,
52
51
  TimeInterval,
@@ -514,9 +513,18 @@ def assemble_monitor_data(
514
513
 
515
514
  def parse_disk_choppers(
516
515
  choppers: AllNeXusComponents[snx.NXdisk_chopper, RunType],
517
- ) -> Choppers[RunType]:
518
- """Convert the NeXus representation of a chopper to ours."""
519
- return Choppers[RunType](
516
+ ) -> RawChoppers[RunType]:
517
+ """Convert the NeXus representation of a chopper to ours.
518
+
519
+ Returns
520
+ -------
521
+ :
522
+ A nested data group containing the loaded choppers.
523
+ The elements may be time-dependent arrays that first need to be processed
524
+ before they can be passed to other functions as
525
+ :class:`ess.reduce.nexus.types.DiskChoppers`.
526
+ """
527
+ return RawChoppers[RunType](
520
528
  choppers.apply(
521
529
  lambda chopper: extract_chopper_from_nexus(
522
530
  nexus.compute_component_position(chopper)
@@ -525,13 +533,6 @@ def parse_disk_choppers(
525
533
  )
526
534
 
527
535
 
528
- def parse_analyzers(
529
- analyzers: AllNeXusComponents[snx.NXcrystal, RunType],
530
- ) -> Analyzers[RunType]:
531
- """Convert the NeXus representation of an analyzer to ours."""
532
- return Analyzers[RunType](analyzers.apply(nexus.compute_component_position))
533
-
534
-
535
536
  def _drop(
536
537
  children: dict[str, snx.Field | snx.Group], classes: tuple[snx.NXobject, ...]
537
538
  ) -> dict[str, snx.Field | snx.Group]:
@@ -559,10 +560,10 @@ class _StrippedDetector(snx.NXdetector):
559
560
  class _DummyField:
560
561
  """Dummy field that can replace snx.Field in NXmonitor."""
561
562
 
562
- def __init__(self):
563
+ def __init__(self, dim: str):
563
564
  self.attrs = {}
564
- self.sizes = {'event_time_zero': 0}
565
- self.dims = ('event_time_zero',)
565
+ self.sizes = {dim: 0}
566
+ self.dims = (dim,)
566
567
  self.shape = (0,)
567
568
 
568
569
  def __getitem__(self, key: Any) -> sc.Variable:
@@ -572,14 +573,17 @@ class _DummyField:
572
573
  class _StrippedMonitor(snx.NXmonitor):
573
574
  """Monitor definition without event data for ScippNexus.
574
575
 
575
- Drops NXevent_data group, data is replaced by a dummy field.
576
+ Drops NXevent_data and NXdata groups, data is replaced by a dummy field.
576
577
  """
577
578
 
578
579
  def __init__(
579
580
  self, attrs: dict[str, Any], children: dict[str, snx.Field | snx.Group]
580
581
  ):
581
- children = _drop(children, (snx.NXevent_data,))
582
- children['data'] = _DummyField()
582
+ is_dense = snx.NXdata in (
583
+ getattr(child, 'nx_class', None) for child in children
584
+ )
585
+ children = _drop(children, (snx.NXevent_data, snx.NXdata))
586
+ children['data'] = _DummyField(dim='time' if is_dense else 'event_time_zero')
583
587
  super().__init__(attrs=attrs, children=children)
584
588
 
585
589
 
@@ -587,11 +591,21 @@ def _add_variances(da: sc.DataArray) -> sc.DataArray:
587
591
  out = da.copy(deep=False)
588
592
  if out.bins is not None:
589
593
  content = out.bins.constituents['data']
590
- if content.variances is None:
591
- content.variances = content.values
594
+ content.data = _assign_values_as_variances(content.data)
595
+ elif out.variances is None:
596
+ out.data = _assign_values_as_variances(out.data)
592
597
  return out
593
598
 
594
599
 
600
+ def _assign_values_as_variances(var: sc.Variable) -> sc.Variable:
601
+ try:
602
+ var.variances = var.values
603
+ except sc.VariancesError:
604
+ var = var.to(dtype=sc.DType.float64)
605
+ var.variances = var.values
606
+ return var
607
+
608
+
595
609
  def load_beamline_metadata_from_nexus(file_spec: NeXusFileSpec[SampleRun]) -> Beamline:
596
610
  """Load beamline metadata from a sample NeXus file."""
597
611
  return nexus.load_metadata(file_spec.value, Beamline)
@@ -628,7 +642,6 @@ _common_providers = (
628
642
  nx_class_for_source,
629
643
  nx_class_for_sample,
630
644
  nx_class_for_disk_chopper,
631
- nx_class_for_crystal,
632
645
  )
633
646
 
634
647
  _monitor_providers = (
@@ -647,24 +660,40 @@ _detector_providers = (
647
660
 
648
661
  _chopper_providers = (parse_disk_choppers,)
649
662
 
650
- _analyzer_providers = (parse_analyzers,)
651
-
652
663
  _metadata_providers = (
653
664
  load_beamline_metadata_from_nexus,
654
665
  load_measurement_metadata_from_nexus,
655
666
  )
656
667
 
657
668
 
658
- def LoadMonitorWorkflow() -> sciline.Pipeline:
669
+ def LoadMonitorWorkflow(
670
+ *,
671
+ run_types: Iterable[sciline.typing.Key],
672
+ monitor_types: Iterable[sciline.typing.Key],
673
+ ) -> sciline.Pipeline:
659
674
  """Generic workflow for loading monitor data from a NeXus file."""
660
- wf = sciline.Pipeline((*_common_providers, *_monitor_providers))
675
+ wf = sciline.Pipeline(
676
+ (*_common_providers, *_monitor_providers),
677
+ constraints=_gather_constraints(
678
+ run_types=run_types, monitor_types=monitor_types
679
+ ),
680
+ )
661
681
  wf[PreopenNeXusFile] = PreopenNeXusFile(False)
662
682
  return wf
663
683
 
664
684
 
665
- def LoadDetectorWorkflow() -> sciline.Pipeline:
685
+ def LoadDetectorWorkflow(
686
+ *,
687
+ run_types: Iterable[sciline.typing.Key],
688
+ monitor_types: Iterable[sciline.typing.Key],
689
+ ) -> sciline.Pipeline:
666
690
  """Generic workflow for loading detector data from a NeXus file."""
667
- wf = sciline.Pipeline((*_common_providers, *_detector_providers))
691
+ wf = sciline.Pipeline(
692
+ (*_common_providers, *_detector_providers),
693
+ constraints=_gather_constraints(
694
+ run_types=run_types, monitor_types=monitor_types
695
+ ),
696
+ )
668
697
  wf[DetectorBankSizes] = DetectorBankSizes({})
669
698
  wf[PreopenNeXusFile] = PreopenNeXusFile(False)
670
699
  return wf
@@ -672,8 +701,8 @@ def LoadDetectorWorkflow() -> sciline.Pipeline:
672
701
 
673
702
  def GenericNeXusWorkflow(
674
703
  *,
675
- run_types: Iterable[sciline.typing.Key] | None = None,
676
- monitor_types: Iterable[sciline.typing.Key] | None = None,
704
+ run_types: Iterable[sciline.typing.Key],
705
+ monitor_types: Iterable[sciline.typing.Key],
677
706
  ) -> sciline.Pipeline:
678
707
  """
679
708
  Generic workflow for loading detector and monitor data from a NeXus file.
@@ -692,13 +721,12 @@ def GenericNeXusWorkflow(
692
721
  Parameters
693
722
  ----------
694
723
  run_types:
695
- List of run types to include in the workflow. If not provided, all run types
696
- are included.
697
- Must be a possible value of :class:`ess.reduce.nexus.types.RunType`.
724
+ List of run types to include in the workflow.
725
+ Constrains the possible values of :class:`ess.reduce.nexus.types.RunType`.
698
726
  monitor_types:
699
- List of monitor types to include in the workflow. If not provided, all monitor
700
- types are included.
701
- Must be a possible value of :class:`ess.reduce.nexus.types.MonitorType`.
727
+ List of monitor types to include in the workflow.
728
+ Constrains the possible values of :class:`ess.reduce.nexus.types.MonitorType`
729
+ and :class:`ess.reduce.nexus.types.Component`.
702
730
 
703
731
  Returns
704
732
  -------
@@ -711,14 +739,27 @@ def GenericNeXusWorkflow(
711
739
  *_monitor_providers,
712
740
  *_detector_providers,
713
741
  *_chopper_providers,
714
- *_analyzer_providers,
715
742
  *_metadata_providers,
716
- )
743
+ ),
744
+ constraints=_gather_constraints(
745
+ run_types=run_types, monitor_types=monitor_types
746
+ ),
717
747
  )
718
748
  wf[DetectorBankSizes] = DetectorBankSizes({})
719
749
  wf[PreopenNeXusFile] = PreopenNeXusFile(False)
720
750
 
721
- if run_types is not None or monitor_types is not None:
722
- prune_type_vars(wf, run_types=run_types, monitor_types=monitor_types)
723
-
724
751
  return wf
752
+
753
+
754
+ def _gather_constraints(
755
+ *,
756
+ run_types: Iterable[sciline.typing.Key],
757
+ monitor_types: Iterable[sciline.typing.Key],
758
+ ) -> dict[TypeVar, Iterable[type]]:
759
+ mon = tuple(iter(monitor_types))
760
+ constraints = {
761
+ RunType: run_types,
762
+ MonitorType: mon,
763
+ Component: (*COMPONENT_CONSTRAINTS, *mon),
764
+ }
765
+ return constraints
ess/reduce/streaming.py CHANGED
@@ -138,6 +138,30 @@ class EternalAccumulator(Accumulator[T]):
138
138
  self._value = None
139
139
 
140
140
 
141
+ class MeanAccumulator(EternalAccumulator[T]):
142
+ """
143
+ Accumulator that computes the mean of pushed values.
144
+
145
+ Does not support event data.
146
+ """
147
+
148
+ def __init__(self, **kwargs: Any) -> None:
149
+ super().__init__(**kwargs)
150
+ self._count = 0
151
+
152
+ def _do_push(self, value: T) -> None:
153
+ super()._do_push(value)
154
+ self._count += 1
155
+
156
+ def _get_value(self) -> T:
157
+ return super()._get_value() / self._count
158
+
159
+ def clear(self) -> None:
160
+ """Clear the accumulated value and count."""
161
+ super().clear()
162
+ self._count = 0
163
+
164
+
141
165
  class RollingAccumulator(Accumulator[T]):
142
166
  """
143
167
  Accumulator that adds pushed values to a rolling window.
@@ -29,9 +29,10 @@ from .types import (
29
29
  ResampledMonitorTofData,
30
30
  SimulationResults,
31
31
  TimeOfFlightLookupTable,
32
+ TimeOfFlightLookupTableFilename,
32
33
  TimeResolution,
33
34
  )
34
- from .workflow import GenericTofWorkflow
35
+ from .workflow import GenericTofWorkflow, TofLutProvider
35
36
 
36
37
  __all__ = [
37
38
  "DetectorLtotal",
@@ -49,7 +50,9 @@ __all__ = [
49
50
  "ResampledMonitorTofData",
50
51
  "SimulationResults",
51
52
  "TimeOfFlightLookupTable",
53
+ "TimeOfFlightLookupTableFilename",
52
54
  "TimeResolution",
55
+ "TofLutProvider",
53
56
  "default_parameters",
54
57
  "providers",
55
58
  "resample_detector_time_of_flight_data",
@@ -12,7 +12,6 @@ from collections.abc import Callable
12
12
  import numpy as np
13
13
  import scipp as sc
14
14
  import scippneutron as scn
15
- from scipp._scipp.core import _bins_no_validate
16
15
  from scippneutron._utils import elem_unit
17
16
 
18
17
  try:
@@ -532,7 +531,7 @@ def _time_of_flight_data_events(
532
531
 
533
532
  parts = da.bins.constituents
534
533
  parts["data"] = tofs
535
- return da.bins.assign_coords(tof=_bins_no_validate(**parts))
534
+ return da.bins.assign_coords(tof=sc.bins(**parts, validate_indices=False))
536
535
 
537
536
 
538
537
  def detector_ltotal_from_straight_line_approximation(
@@ -3,9 +3,11 @@
3
3
  from collections.abc import Mapping
4
4
 
5
5
  import scipp as sc
6
+ import scippnexus as snx
6
7
  from scippneutron.chopper import DiskChopper
7
8
 
8
- from .types import SimulationResults
9
+ from ..nexus.types import DiskChoppers, Position, SampleRun
10
+ from .types import NumberOfSimulatedNeutrons, SimulationResults
9
11
 
10
12
 
11
13
  def simulate_beamline(
@@ -82,3 +84,25 @@ def simulate_beamline(
82
84
  weight=events.data,
83
85
  distance=furthest_chopper.distance,
84
86
  )
87
+
88
+
89
+ def simulate_chopper_cascade_using_tof(
90
+ choppers: DiskChoppers[SampleRun],
91
+ neutrons: NumberOfSimulatedNeutrons,
92
+ source_position: Position[snx.NXsource, SampleRun],
93
+ ) -> SimulationResults:
94
+ """
95
+ Simulate neutrons traveling through the chopper cascade using the ``tof`` package.
96
+
97
+ Parameters
98
+ ----------
99
+ choppers:
100
+ Chopper settings.
101
+ neutrons:
102
+ Number of neutrons to simulate.
103
+ source_position:
104
+ Position of the source.
105
+ """
106
+ return simulate_beamline(
107
+ choppers=choppers, neutrons=neutrons, source_position=source_position
108
+ )
@@ -46,6 +46,12 @@ class SimulationResults:
46
46
  distance: sc.Variable
47
47
 
48
48
 
49
+ NumberOfSimulatedNeutrons = NewType("NumberOfSimulatedNeutrons", int)
50
+ """
51
+ Number of neutrons simulated in the simulation that is used to create the lookup table.
52
+ This is typically a large number, e.g., 1e6 or 1e7.
53
+ """
54
+
49
55
  LtotalRange = NewType("LtotalRange", tuple[sc.Variable, sc.Variable])
50
56
  """
51
57
  Range (min, max) of the total length of the flight path from the source to the detector.
@@ -78,6 +84,10 @@ resolution in the lookup table will be at least the supplied value here, but may
78
84
  smaller if the pulse period is not an integer multiple of the time resolution.
79
85
  """
80
86
 
87
+ TimeOfFlightLookupTableFilename = NewType("TimeOfFlightLookupTableFilename", str)
88
+ """Filename of the time-of-flight lookup table."""
89
+
90
+
81
91
  TimeOfFlightLookupTable = NewType("TimeOfFlightLookupTable", sc.DataArray)
82
92
  """
83
93
  Lookup table giving time-of-flight as a function of distance and time of arrival.
@@ -1,18 +1,35 @@
1
1
  # SPDX-License-Identifier: BSD-3-Clause
2
2
  # Copyright (c) 2025 Scipp contributors (https://github.com/scipp)
3
3
  from collections.abc import Iterable
4
+ from enum import Enum, auto
4
5
 
5
6
  import sciline
7
+ import scipp as sc
6
8
 
7
9
  from ..nexus import GenericNeXusWorkflow
8
- from ..utils import prune_type_vars
9
- from .eto_to_tof import default_parameters, providers
10
+ from . import eto_to_tof, simulation
11
+ from .types import TimeOfFlightLookupTable, TimeOfFlightLookupTableFilename
12
+
13
+
14
+ class TofLutProvider(Enum):
15
+ """Provider for the time-of-flight lookup table."""
16
+
17
+ FILE = auto() # From file
18
+ TOF = auto() # Computed with 'tof' package from chopper settings
19
+ MCSTAS = auto() # McStas simulation (not implemented yet)
20
+
21
+
22
+ def load_tof_lookup_table(
23
+ filename: TimeOfFlightLookupTableFilename,
24
+ ) -> TimeOfFlightLookupTable:
25
+ return TimeOfFlightLookupTable(sc.io.load_hdf5(filename))
10
26
 
11
27
 
12
28
  def GenericTofWorkflow(
13
29
  *,
14
- run_types: Iterable[sciline.typing.Key] | None = None,
15
- monitor_types: Iterable[sciline.typing.Key] | None = None,
30
+ run_types: Iterable[sciline.typing.Key],
31
+ monitor_types: Iterable[sciline.typing.Key],
32
+ tof_lut_provider: TofLutProvider = TofLutProvider.FILE,
16
33
  ) -> sciline.Pipeline:
17
34
  """
18
35
  Generic workflow for computing the neutron time-of-flight for detector and monitor
@@ -35,13 +52,17 @@ def GenericTofWorkflow(
35
52
  Parameters
36
53
  ----------
37
54
  run_types:
38
- List of run types to include in the workflow. If not provided, all run types
39
- are included.
40
- Must be a possible value of :class:`ess.reduce.nexus.types.RunType`.
55
+ List of run types to include in the workflow.
56
+ Constrains the possible values of :class:`ess.reduce.nexus.types.RunType`.
41
57
  monitor_types:
42
- List of monitor types to include in the workflow. If not provided, all monitor
43
- types are included.
44
- Must be a possible value of :class:`ess.reduce.nexus.types.MonitorType`.
58
+ List of monitor types to include in the workflow.
59
+ Constrains the possible values of :class:`ess.reduce.nexus.types.MonitorType`
60
+ and :class:`ess.reduce.nexus.types.Component`.
61
+ tof_lut_provider:
62
+ Specifies how the time-of-flight lookup table is provided:
63
+ - FILE: Read from a file
64
+ - TOF: Computed from chopper settings using the 'tof' package
65
+ - MCSTAS: From McStas simulation (not implemented yet)
45
66
 
46
67
  Returns
47
68
  -------
@@ -50,12 +71,19 @@ def GenericTofWorkflow(
50
71
  """
51
72
  wf = GenericNeXusWorkflow(run_types=run_types, monitor_types=monitor_types)
52
73
 
53
- for provider in providers():
74
+ for provider in eto_to_tof.providers():
54
75
  wf.insert(provider)
55
- for key, value in default_parameters().items():
56
- wf[key] = value
57
76
 
58
- if run_types is not None or monitor_types is not None:
59
- prune_type_vars(wf, run_types=run_types, monitor_types=monitor_types)
77
+ if tof_lut_provider == TofLutProvider.FILE:
78
+ wf.insert(load_tof_lookup_table)
79
+ else:
80
+ wf.insert(eto_to_tof.compute_tof_lookup_table)
81
+ if tof_lut_provider == TofLutProvider.TOF:
82
+ wf.insert(simulation.simulate_chopper_cascade_using_tof)
83
+ if tof_lut_provider == TofLutProvider.MCSTAS:
84
+ raise NotImplementedError("McStas simulation not implemented yet")
85
+
86
+ for key, value in eto_to_tof.default_parameters().items():
87
+ wf[key] = value
60
88
 
61
89
  return wf
ess/reduce/workflow.py CHANGED
@@ -53,12 +53,14 @@ def get_typical_outputs(pipeline: Pipeline) -> tuple[Key, ...]:
53
53
  if (typical_outputs := getattr(pipeline, "typical_outputs", None)) is None:
54
54
  graph = pipeline.underlying_graph
55
55
  sink_nodes = [node for node, degree in graph.out_degree if degree == 0]
56
- return sorted(_with_pretty_names(sink_nodes))
56
+ return sorted(_with_pretty_names(sink_nodes), key=lambda x: x[0])
57
57
  return _with_pretty_names(typical_outputs)
58
58
 
59
59
 
60
60
  def get_possible_outputs(pipeline: Pipeline) -> tuple[Key, ...]:
61
- return sorted(_with_pretty_names(tuple(pipeline.underlying_graph.nodes)))
61
+ return sorted(
62
+ _with_pretty_names(tuple(pipeline.underlying_graph.nodes)), key=lambda x: x[0]
63
+ )
62
64
 
63
65
 
64
66
  def _with_pretty_names(outputs: Sequence[Key]) -> tuple[tuple[str, Key], ...]:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: essreduce
3
- Version: 25.5.0
3
+ Version: 25.5.2
4
4
  Summary: Common data reduction tools for the ESS facility
5
5
  Author: Scipp contributors
6
6
  License: BSD 3-Clause License
@@ -51,7 +51,7 @@ Classifier: Typing :: Typed
51
51
  Requires-Python: >=3.10
52
52
  Description-Content-Type: text/markdown
53
53
  License-File: LICENSE
54
- Requires-Dist: sciline>=25.04.1
54
+ Requires-Dist: sciline>=25.05.1
55
55
  Requires-Dist: scipp>=25.01.0
56
56
  Requires-Dist: scippneutron>=25.02.0
57
57
  Requires-Dist: scippnexus>=24.11.0
@@ -1,13 +1,12 @@
1
1
  ess/reduce/__init__.py,sha256=o1pWRP9YGwTukM_k-qlG6KcoXOpMb0PDVH59vod12lw,419
2
- ess/reduce/data.py,sha256=vaoeAJ6EpK1YghOiAALLdWiW17TgUnnnt0H-RGiGzXk,3756
2
+ ess/reduce/data.py,sha256=0N7iq1363tO16ntMztTNjxkQDFst-Gnp9awpgUOBVdY,4133
3
3
  ess/reduce/logging.py,sha256=6n8Czq4LZ3OK9ENlKsWSI1M3KvKv6_HSoUiV4__IUlU,357
4
4
  ess/reduce/parameter.py,sha256=4sCfoKOI2HuO_Q7JLH_jAXnEOFANSn5P3NdaOBzhJxc,4635
5
5
  ess/reduce/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
- ess/reduce/streaming.py,sha256=TBttQV5WdSpUKh38J0pdv53seMWtUFswxd6-ltaZb_M,17403
6
+ ess/reduce/streaming.py,sha256=zbqxQz5dASDq4ZVyx-TdbapBXMyBttImCYz_6WOj4pg,17978
7
7
  ess/reduce/ui.py,sha256=zmorAbDwX1cU3ygDT--OP58o0qU7OBcmJz03jPeYSLA,10884
8
8
  ess/reduce/uncertainty.py,sha256=LR4O6ApB6Z-W9gC_XW0ajupl8yFG-du0eee1AX_R-gk,6990
9
- ess/reduce/utils.py,sha256=RBAfJRNil6JjVF-jPaxeL0ssEEfPBBQEZ3ObEorpDLo,1132
10
- ess/reduce/workflow.py,sha256=sL34T_2Cjl_8iFlegujxI9VyOUwo6erVC8pOXnfWgYw,3060
9
+ ess/reduce/workflow.py,sha256=738-lcdgsORYfQ4A0UTk2IgnbVxC3jBdpscpaOFIpdc,3114
11
10
  ess/reduce/live/__init__.py,sha256=jPQVhihRVNtEDrE20PoKkclKV2aBF1lS7cCHootgFgI,204
12
11
  ess/reduce/live/raw.py,sha256=66qV0G2rP8gK5tXuk-syTlDLE2jT3ehfmSnET7Xzfd0,24392
13
12
  ess/reduce/live/roi.py,sha256=Hs-pW98k41WU6Kl3UQ41kQawk80c2QNOQ_WNctLzDPE,3795
@@ -16,18 +15,18 @@ ess/reduce/nexus/__init__.py,sha256=59bxKkNYg8DYcSykNvH6nCa5SYchJC4SbgZEKhkNdYc,
16
15
  ess/reduce/nexus/_nexus_loader.py,sha256=5N48AMJx1AaFZb6WZPPbVKUlXyFMVVtZrn7Bae57O3A,19842
17
16
  ess/reduce/nexus/json_generator.py,sha256=ME2Xn8L7Oi3uHJk9ZZdCRQTRX-OV_wh9-DJn07Alplk,2529
18
17
  ess/reduce/nexus/json_nexus.py,sha256=QrVc0p424nZ5dHX9gebAJppTw6lGZq9404P_OFl1giA,10282
19
- ess/reduce/nexus/types.py,sha256=vTQD4oQ5JKBHAYy9LWFICSo-dhVi3wX5IinMgjRDtF8,9806
20
- ess/reduce/nexus/workflow.py,sha256=zrBQGNLUxmvqXewe9uNUg9aP43_glfFD6nh5VGAtBK4,23456
18
+ ess/reduce/nexus/types.py,sha256=DE82JnbgpTlQnt7UN2a2Gur2N9QupV3CDL9j4Iy4lsE,9178
19
+ ess/reduce/nexus/workflow.py,sha256=Ytt80-muk5EiXmip890ahb_m5DQqlTGRQUyaTVXRNzo,24568
21
20
  ess/reduce/scripts/grow_nexus.py,sha256=hET3h06M0xlJd62E3palNLFvJMyNax2kK4XyJcOhl-I,3387
22
- ess/reduce/time_of_flight/__init__.py,sha256=v86c3zNTMMqZoR9eHaK0Q-JnzsbOI6XsBGI3mgy2CiU,1469
23
- ess/reduce/time_of_flight/eto_to_tof.py,sha256=ckXoSrltXdciYwipyUkF-DVtbsz2_XSLZvX2qJ_d8Bs,28238
21
+ ess/reduce/time_of_flight/__init__.py,sha256=UxMvY4aFkhZQmIbGSo4FBpBGRD2wDJbczLMVqcEhCSg,1583
22
+ ess/reduce/time_of_flight/eto_to_tof.py,sha256=JCu7C3AmJnB7GDJrL76oPjgxGesp67nct9xXRp3O8E4,28204
24
23
  ess/reduce/time_of_flight/fakes.py,sha256=0gtbSX3ZQilaM4ZP5dMr3fqbnhpyoVsZX2YEb8GgREE,4489
25
24
  ess/reduce/time_of_flight/interpolator_numba.py,sha256=wh2YS3j2rOu30v1Ok3xNHcwS7t8eEtZyZvbfXOCtgrQ,3835
26
25
  ess/reduce/time_of_flight/interpolator_scipy.py,sha256=_InoAPuMm2qhJKZQBAHOGRFqtvvuQ8TStoN7j_YgS4M,1853
27
- ess/reduce/time_of_flight/simulation.py,sha256=cIF_nWkLQlcWUCW2_wvWBU2ocg_8CSfOnfkoqdLdUgs,2923
26
+ ess/reduce/time_of_flight/simulation.py,sha256=vo-zjG6t-PLetv2_nj4dhMSTEyTQ1MsrhlM2XkhOtf8,3632
28
27
  ess/reduce/time_of_flight/to_events.py,sha256=w9mHpnWd3vwN2ouob-GK_1NPrTjCaOzPuC2QuEey-m0,4342
29
- ess/reduce/time_of_flight/types.py,sha256=xhziZQaCB4XAxvVopHHp2DZSBj7PUt-xgPzEDpni05g,6321
30
- ess/reduce/time_of_flight/workflow.py,sha256=-g9IyAz7sNrgL-5RZLUTlfjTb2YFej1Xig6GiC7c1bI,2156
28
+ ess/reduce/time_of_flight/types.py,sha256=OQeMYNN7QinXs_HDcoE6kkh_xNcyD0dEJWtnHQy5-uA,6675
29
+ ess/reduce/time_of_flight/workflow.py,sha256=mwNEXwvOnm-M8n4G9Cau1Vuyqzo4qMrhCbZ-S5vszW4,3181
31
30
  ess/reduce/widgets/__init__.py,sha256=SoSHBv8Dc3QXV9HUvPhjSYWMwKTGYZLpsWwsShIO97Q,5325
32
31
  ess/reduce/widgets/_base.py,sha256=_wN3FOlXgx_u0c-A_3yyoIH-SdUvDENGgquh9S-h5GI,4852
33
32
  ess/reduce/widgets/_binedges_widget.py,sha256=ZCQsGjYHnJr9GFUn7NjoZc1CdsnAzm_fMzyF-fTKKVY,2785
@@ -40,9 +39,9 @@ ess/reduce/widgets/_spinner.py,sha256=2VY4Fhfa7HMXox2O7UbofcdKsYG-AJGrsgGJB85nDX
40
39
  ess/reduce/widgets/_string_widget.py,sha256=iPAdfANyXHf-nkfhgkyH6gQDklia0LebLTmwi3m-iYQ,1482
41
40
  ess/reduce/widgets/_switchable_widget.py,sha256=fjKz99SKLhIF1BLgGVBSKKn3Lu_jYBwDYGeAjbJY3Q8,2390
42
41
  ess/reduce/widgets/_vector_widget.py,sha256=aTaBqCFHZQhrIoX6-sSqFWCPePEW8HQt5kUio8jP1t8,1203
43
- essreduce-25.5.0.dist-info/licenses/LICENSE,sha256=nVEiume4Qj6jMYfSRjHTM2jtJ4FGu0g-5Sdh7osfEYw,1553
44
- essreduce-25.5.0.dist-info/METADATA,sha256=yfoZMb19ayIQyCRk5_WPEuvrWGAYApWIs4Wr-69nwO8,3768
45
- essreduce-25.5.0.dist-info/WHEEL,sha256=0CuiUZ_p9E4cD6NyLD6UG80LBXYyiSYZOKDm5lp32xk,91
46
- essreduce-25.5.0.dist-info/entry_points.txt,sha256=PMZOIYzCifHMTe4pK3HbhxUwxjFaZizYlLD0td4Isb0,66
47
- essreduce-25.5.0.dist-info/top_level.txt,sha256=0JxTCgMKPLKtp14wb1-RKisQPQWX7i96innZNvHBr-s,4
48
- essreduce-25.5.0.dist-info/RECORD,,
42
+ essreduce-25.5.2.dist-info/licenses/LICENSE,sha256=nVEiume4Qj6jMYfSRjHTM2jtJ4FGu0g-5Sdh7osfEYw,1553
43
+ essreduce-25.5.2.dist-info/METADATA,sha256=LNEHFWDdswQNdu6s_5_eVHlmkVz35siN2fvirZ0KhFI,3768
44
+ essreduce-25.5.2.dist-info/WHEEL,sha256=zaaOINJESkSfm_4HQVc5ssNzHCPXhJm0kEUakpsEHaU,91
45
+ essreduce-25.5.2.dist-info/entry_points.txt,sha256=PMZOIYzCifHMTe4pK3HbhxUwxjFaZizYlLD0td4Isb0,66
46
+ essreduce-25.5.2.dist-info/top_level.txt,sha256=0JxTCgMKPLKtp14wb1-RKisQPQWX7i96innZNvHBr-s,4
47
+ essreduce-25.5.2.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.3.1)
2
+ Generator: setuptools (80.8.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
ess/reduce/utils.py DELETED
@@ -1,36 +0,0 @@
1
- # SPDX-License-Identifier: BSD-3-Clause
2
- # Copyright (c) 2025 Scipp contributors (https://github.com/scipp)
3
-
4
- from collections.abc import Iterable
5
- from typing import Any
6
-
7
- import sciline
8
-
9
- from .nexus.types import MonitorType, RunType
10
-
11
-
12
- def prune_type_vars(
13
- workflow: sciline.Pipeline,
14
- *,
15
- run_types: Iterable[sciline.typing.Key] | None,
16
- monitor_types: Iterable[sciline.typing.Key] | None,
17
- ) -> None:
18
- # Remove all nodes that use a run type or monitor types that is
19
- # not listed in the function arguments.
20
- excluded_run_types = excluded_type_args(RunType, run_types)
21
- excluded_monitor_types = excluded_type_args(MonitorType, monitor_types)
22
- excluded_types = excluded_run_types | excluded_monitor_types
23
-
24
- graph = workflow.underlying_graph
25
- to_remove = [
26
- node for node in graph if excluded_types & set(getattr(node, "__args__", set()))
27
- ]
28
- graph.remove_nodes_from(to_remove)
29
-
30
-
31
- def excluded_type_args(
32
- type_var: Any, keep: Iterable[sciline.typing.Key] | None
33
- ) -> set[sciline.typing.Key]:
34
- if keep is None:
35
- return set()
36
- return set(type_var.__constraints__) - set(keep)