essreduce 25.2.3__py3-none-any.whl → 25.2.5__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.
@@ -8,14 +8,16 @@ from collections.abc import Generator, Mapping
8
8
  from contextlib import AbstractContextManager, contextmanager, nullcontext
9
9
  from dataclasses import dataclass
10
10
  from math import prod
11
- from typing import cast
11
+ from typing import TypeVar, cast
12
12
 
13
13
  import scipp as sc
14
14
  import scippnexus as snx
15
15
 
16
16
  from ..logging import get_logger
17
17
  from .types import (
18
+ Beamline,
18
19
  FilePath,
20
+ Measurement,
19
21
  NeXusAllLocationSpec,
20
22
  NeXusEntryName,
21
23
  NeXusFile,
@@ -27,6 +29,8 @@ from .types import (
27
29
  class NoNewDefinitionsType: ...
28
30
 
29
31
 
32
+ _Model = TypeVar('_Model', Beamline, Measurement)
33
+
30
34
  NoNewDefinitions = NoNewDefinitionsType()
31
35
 
32
36
 
@@ -74,6 +78,18 @@ def load_all_components(
74
78
  return components
75
79
 
76
80
 
81
+ def load_metadata(
82
+ file_path: FilePath | NeXusFile | NeXusGroup,
83
+ model: type[_Model],
84
+ *,
85
+ entry_name: NeXusEntryName | None = None,
86
+ definitions: Mapping | NoNewDefinitionsType = NoNewDefinitions,
87
+ ) -> _Model:
88
+ with _open_nexus_file(file_path, definitions=definitions) as f:
89
+ entry = _unique_child_group(f, snx.NXentry, entry_name)
90
+ return model.from_nexus_entry(entry)
91
+
92
+
77
93
  def compute_component_position(dg: sc.DataGroup) -> sc.DataGroup:
78
94
  # In some downstream packages we use some of the Nexus components which attempt
79
95
  # to compute positions without having actual Nexus data defining depends_on chains.
@@ -164,10 +180,15 @@ def _attempt_to_open_without_locking(
164
180
  # HDF5 tracks file locking flags internally within a single process.
165
181
  # If the same file is opened multiple times, we can get a flag mismatch.
166
182
  # We can try opening without locking, maybe this matches the original flags.
167
- if "file locking flag values don't match" in err.args[0]:
168
- return True
169
- if "file locking 'ignore disabled locks' flag values don't match" in err.args[0]:
170
- return True
183
+ error_message = err.args[0]
184
+ if isinstance(error_message, str):
185
+ if "file locking flag values don't match" in error_message:
186
+ return True
187
+ if (
188
+ "file locking 'ignore disabled locks' flag values don't match"
189
+ in error_message
190
+ ):
191
+ return True
171
192
  return False
172
193
 
173
194
 
ess/reduce/nexus/types.py CHANGED
@@ -7,6 +7,7 @@ from typing import Any, BinaryIO, Generic, NewType, TypeVar
7
7
  import sciline
8
8
  import scipp as sc
9
9
  import scippnexus as snx
10
+ from scippneutron import metadata as scn_meta
10
11
 
11
12
  FilePath = NewType('FilePath', Path)
12
13
  """Full path to a NeXus file on disk."""
@@ -177,6 +178,13 @@ Component = TypeVar(
177
178
  UniqueComponent = TypeVar('UniqueComponent', snx.NXsample, snx.NXsource)
178
179
  """Components that can be identified by their type as there will only be one."""
179
180
 
181
+ Beamline = scn_meta.Beamline
182
+ """Beamline metadata."""
183
+ Measurement = scn_meta.Measurement
184
+ """measurement metadata."""
185
+ Source = scn_meta.Source
186
+ """Neutron source metadata."""
187
+
180
188
 
181
189
  class NeXusName(sciline.Scope[Component, str], str):
182
190
  """Name of a component in a NeXus file."""
@@ -19,6 +19,7 @@ from . import _nexus_loader as nexus
19
19
  from .types import (
20
20
  AllNeXusComponents,
21
21
  Analyzers,
22
+ Beamline,
22
23
  CalibratedBeamline,
23
24
  CalibratedDetector,
24
25
  CalibratedMonitor,
@@ -29,6 +30,7 @@ from .types import (
29
30
  DetectorPositionOffset,
30
31
  Filename,
31
32
  GravityVector,
33
+ Measurement,
32
34
  MonitorData,
33
35
  MonitorPositionOffset,
34
36
  MonitorType,
@@ -45,6 +47,7 @@ from .types import (
45
47
  Position,
46
48
  PreopenNeXusFile,
47
49
  RunType,
50
+ SampleRun,
48
51
  TimeInterval,
49
52
  UniqueComponent,
50
53
  )
@@ -586,6 +589,18 @@ def _add_variances(da: sc.DataArray) -> sc.DataArray:
586
589
  return out
587
590
 
588
591
 
592
+ def load_beamline_metadata_from_nexus(file_spec: NeXusFileSpec[SampleRun]) -> Beamline:
593
+ """Load beamline metadata from a sample NeXus file."""
594
+ return nexus.load_metadata(file_spec.value, Beamline)
595
+
596
+
597
+ def load_measurement_metadata_from_nexus(
598
+ file_spec: NeXusFileSpec[SampleRun],
599
+ ) -> Measurement:
600
+ """Load measurement metadata from a sample NeXus file."""
601
+ return nexus.load_metadata(file_spec.value, Measurement)
602
+
603
+
589
604
  definitions = snx.base_definitions()
590
605
  definitions["NXdetector"] = _StrippedDetector
591
606
  definitions["NXmonitor"] = _StrippedMonitor
@@ -631,6 +646,11 @@ _chopper_providers = (parse_disk_choppers,)
631
646
 
632
647
  _analyzer_providers = (parse_analyzers,)
633
648
 
649
+ _metadata_providers = (
650
+ load_beamline_metadata_from_nexus,
651
+ load_measurement_metadata_from_nexus,
652
+ )
653
+
634
654
 
635
655
  def LoadMonitorWorkflow() -> sciline.Pipeline:
636
656
  """Generic workflow for loading monitor data from a NeXus file."""
@@ -689,6 +709,7 @@ def GenericNeXusWorkflow(
689
709
  *_detector_providers,
690
710
  *_chopper_providers,
691
711
  *_analyzer_providers,
712
+ *_metadata_providers,
692
713
  )
693
714
  )
694
715
  wf[DetectorBankSizes] = DetectorBankSizes({})
ess/reduce/streaming.py CHANGED
@@ -68,7 +68,18 @@ class Accumulator(ABC, Generic[T]):
68
68
  def _do_push(self, value: T) -> None: ...
69
69
 
70
70
  @property
71
- @abstractmethod
71
+ def is_empty(self) -> bool:
72
+ """
73
+ Check if the accumulator is empty.
74
+
75
+ Returns
76
+ -------
77
+ :
78
+ True if the accumulator is empty, False otherwise.
79
+ """
80
+ return False
81
+
82
+ @property
72
83
  def value(self) -> T:
73
84
  """
74
85
  Get the accumulated value.
@@ -77,6 +88,24 @@ class Accumulator(ABC, Generic[T]):
77
88
  -------
78
89
  :
79
90
  Accumulated value.
91
+
92
+ Raises
93
+ ------
94
+ ValueError
95
+ If the accumulator is empty.
96
+ """
97
+ if self.is_empty:
98
+ raise ValueError("Cannot get value from empty accumulator")
99
+ return self._get_value()
100
+
101
+ @abstractmethod
102
+ def _get_value(self) -> T:
103
+ """Return the accumulated value, assuming it exists."""
104
+
105
+ @abstractmethod
106
+ def clear(self) -> None:
107
+ """
108
+ Clear the accumulator, resetting it to its initial state.
80
109
  """
81
110
 
82
111
 
@@ -92,7 +121,10 @@ class EternalAccumulator(Accumulator[T]):
92
121
  self._value: T | None = None
93
122
 
94
123
  @property
95
- def value(self) -> T:
124
+ def is_empty(self) -> bool:
125
+ return self._value is None
126
+
127
+ def _get_value(self) -> T:
96
128
  return deepcopy(self._value)
97
129
 
98
130
  def _do_push(self, value: T) -> None:
@@ -101,6 +133,10 @@ class EternalAccumulator(Accumulator[T]):
101
133
  else:
102
134
  self._value += value
103
135
 
136
+ def clear(self) -> None:
137
+ """Clear the accumulated value."""
138
+ self._value = None
139
+
104
140
 
105
141
  class RollingAccumulator(Accumulator[T]):
106
142
  """
@@ -121,7 +157,10 @@ class RollingAccumulator(Accumulator[T]):
121
157
  self._values: list[T] = []
122
158
 
123
159
  @property
124
- def value(self) -> T:
160
+ def is_empty(self) -> bool:
161
+ return len(self._values) == 0
162
+
163
+ def _get_value(self) -> T:
125
164
  # Naive and potentially slow implementation if values and/or window are large!
126
165
  return sc.reduce(self._values).sum()
127
166
 
@@ -130,6 +169,10 @@ class RollingAccumulator(Accumulator[T]):
130
169
  if len(self._values) > self._window:
131
170
  self._values.pop(0)
132
171
 
172
+ def clear(self) -> None:
173
+ """Clear the accumulated values."""
174
+ self._values = []
175
+
133
176
 
134
177
  class StreamProcessor:
135
178
  """
@@ -299,6 +342,16 @@ class StreamProcessor:
299
342
  self._finalize_workflow[key] = self._accumulators[key].value
300
343
  return self._finalize_workflow.compute(self._target_keys)
301
344
 
345
+ def clear(self) -> None:
346
+ """
347
+ Clear all accumulators, resetting them to their initial state.
348
+
349
+ This is useful for restarting a streaming computation without
350
+ creating a new StreamProcessor instance.
351
+ """
352
+ for accumulator in self._accumulators.values():
353
+ accumulator.clear()
354
+
302
355
 
303
356
  def _find_descendants(
304
357
  workflow: sciline.Pipeline, keys: tuple[sciline.typing.Key, ...]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: essreduce
3
- Version: 25.2.3
3
+ Version: 25.2.5
4
4
  Summary: Common data reduction tools for the ESS facility
5
5
  Author: Scipp contributors
6
6
  License: BSD 3-Clause License
@@ -53,7 +53,7 @@ Description-Content-Type: text/markdown
53
53
  License-File: LICENSE
54
54
  Requires-Dist: sciline>=24.06.2
55
55
  Requires-Dist: scipp>=25.01.0
56
- Requires-Dist: scippneutron>=24.11.0
56
+ Requires-Dist: scippneutron>=25.02.0
57
57
  Requires-Dist: scippnexus>=24.11.0
58
58
  Provides-Extra: test
59
59
  Requires-Dist: ipywidgets; extra == "test"
@@ -3,7 +3,7 @@ ess/reduce/data.py,sha256=vaoeAJ6EpK1YghOiAALLdWiW17TgUnnnt0H-RGiGzXk,3756
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=ffFiWpq9AK1GOfRG-rlvT_Gz7HMBnE4FD0qPZiej4Gg,10306
6
+ ess/reduce/streaming.py,sha256=XNUr5Vg8lufBqjTjjzd0nfiRR2f70zkTixXkG_O26Bk,11687
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
9
  ess/reduce/workflow.py,sha256=sL34T_2Cjl_8iFlegujxI9VyOUwo6erVC8pOXnfWgYw,3060
@@ -12,11 +12,11 @@ ess/reduce/live/raw.py,sha256=pzXsPZQERtUm5tabTXjxd-XHH4WDDP13TTBG0lGPcqg,25262
12
12
  ess/reduce/live/roi.py,sha256=Hs-pW98k41WU6Kl3UQ41kQawk80c2QNOQ_WNctLzDPE,3795
13
13
  ess/reduce/live/workflow.py,sha256=bsbwvTqPhRO6mC__3b7MgU7DWwAnOvGvG-t2n22EKq8,4285
14
14
  ess/reduce/nexus/__init__.py,sha256=59bxKkNYg8DYcSykNvH6nCa5SYchJC4SbgZEKhkNdYc,967
15
- ess/reduce/nexus/_nexus_loader.py,sha256=NbKIepTxv-UirVlViImh8Ozm16k-ZIiF6AQ9-oKuDHU,19222
15
+ ess/reduce/nexus/_nexus_loader.py,sha256=NpBTBjWLKSnW0B1EarFdt2QxV0X7QBQLk2dSJq2ZuOw,19847
16
16
  ess/reduce/nexus/json_generator.py,sha256=ME2Xn8L7Oi3uHJk9ZZdCRQTRX-OV_wh9-DJn07Alplk,2529
17
17
  ess/reduce/nexus/json_nexus.py,sha256=QrVc0p424nZ5dHX9gebAJppTw6lGZq9404P_OFl1giA,10282
18
- ess/reduce/nexus/types.py,sha256=Az_pZtaTIlEAA4Po_YOLabez8w4HeHcr0asY3rS6BXg,9676
19
- ess/reduce/nexus/workflow.py,sha256=jzdh0ubp9Mmb98a04KIeM8Xo9bpAqpnsfwFWz2VllnQ,23676
18
+ ess/reduce/nexus/types.py,sha256=15XcHbNbOfnAYjWXzzKyYDVNyNixRnP0hJ-Q2duwMWE,9896
19
+ ess/reduce/nexus/workflow.py,sha256=ABVc9E1Qcos0wLcDE8bGDAOz3aPpHrj4TJyfHsQbx7I,24297
20
20
  ess/reduce/scripts/grow_nexus.py,sha256=hET3h06M0xlJd62E3palNLFvJMyNax2kK4XyJcOhl-I,3387
21
21
  ess/reduce/time_of_flight/__init__.py,sha256=92w88NpGIBysuqCPSvdZ_XgBd7cFAk9qaO9zflpUbfM,1097
22
22
  ess/reduce/time_of_flight/fakes.py,sha256=rlBgceFVbHIhP_xPyUzYVf-2wEu--G8hA-kxPzAnPbM,4236
@@ -36,9 +36,9 @@ ess/reduce/widgets/_spinner.py,sha256=2VY4Fhfa7HMXox2O7UbofcdKsYG-AJGrsgGJB85nDX
36
36
  ess/reduce/widgets/_string_widget.py,sha256=iPAdfANyXHf-nkfhgkyH6gQDklia0LebLTmwi3m-iYQ,1482
37
37
  ess/reduce/widgets/_switchable_widget.py,sha256=fjKz99SKLhIF1BLgGVBSKKn3Lu_jYBwDYGeAjbJY3Q8,2390
38
38
  ess/reduce/widgets/_vector_widget.py,sha256=aTaBqCFHZQhrIoX6-sSqFWCPePEW8HQt5kUio8jP1t8,1203
39
- essreduce-25.2.3.dist-info/LICENSE,sha256=nVEiume4Qj6jMYfSRjHTM2jtJ4FGu0g-5Sdh7osfEYw,1553
40
- essreduce-25.2.3.dist-info/METADATA,sha256=aKumfO4ID_7M42EvaeTFiVAW2acxYhi0--N7E4U9lfY,3708
41
- essreduce-25.2.3.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
42
- essreduce-25.2.3.dist-info/entry_points.txt,sha256=PMZOIYzCifHMTe4pK3HbhxUwxjFaZizYlLD0td4Isb0,66
43
- essreduce-25.2.3.dist-info/top_level.txt,sha256=0JxTCgMKPLKtp14wb1-RKisQPQWX7i96innZNvHBr-s,4
44
- essreduce-25.2.3.dist-info/RECORD,,
39
+ essreduce-25.2.5.dist-info/LICENSE,sha256=nVEiume4Qj6jMYfSRjHTM2jtJ4FGu0g-5Sdh7osfEYw,1553
40
+ essreduce-25.2.5.dist-info/METADATA,sha256=ri1OfgsTeyU-LR2NMBoAB5zJXLCRAl72qkND7TbOf5M,3708
41
+ essreduce-25.2.5.dist-info/WHEEL,sha256=jB7zZ3N9hIM9adW7qlTAyycLYW9npaWKLRzaoVcLKcM,91
42
+ essreduce-25.2.5.dist-info/entry_points.txt,sha256=PMZOIYzCifHMTe4pK3HbhxUwxjFaZizYlLD0td4Isb0,66
43
+ essreduce-25.2.5.dist-info/top_level.txt,sha256=0JxTCgMKPLKtp14wb1-RKisQPQWX7i96innZNvHBr-s,4
44
+ essreduce-25.2.5.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (75.8.0)
2
+ Generator: setuptools (75.8.2)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5