essreduce 24.3.11__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/__init__.py ADDED
@@ -0,0 +1,16 @@
1
+ # SPDX-License-Identifier: BSD-3-Clause
2
+ # Copyright (c) 2024 Scipp contributors (https://github.com/scipp)
3
+
4
+ # flake8: noqa
5
+ import importlib.metadata
6
+
7
+ from . import nexus
8
+
9
+ try:
10
+ __version__ = importlib.metadata.version(__package__ or __name__)
11
+ except importlib.metadata.PackageNotFoundError:
12
+ __version__ = "0.0.0"
13
+
14
+ del importlib
15
+
16
+ __all__ = ['nexus']
ess/reduce/logging.py ADDED
@@ -0,0 +1,17 @@
1
+ # SPDX-License-Identifier: BSD-3-Clause
2
+ # Copyright (c) 2024 Scipp contributors (https://github.com/scipp)
3
+
4
+ """Logging tools for ess.reduce."""
5
+
6
+ import logging
7
+
8
+
9
+ def get_logger() -> logging.Logger:
10
+ """Return the logger for ess.reduce.
11
+
12
+ Returns
13
+ -------
14
+ :
15
+ The requested logger.
16
+ """
17
+ return logging.getLogger('scipp.ess.reduce')
ess/reduce/nexus.py ADDED
@@ -0,0 +1,391 @@
1
+ # SPDX-License-Identifier: BSD-3-Clause
2
+ # Copyright (c) 2024 Scipp contributors (https://github.com/scipp)
3
+
4
+ """NeXus utilities.
5
+
6
+ This module defines functions and domain types that can be used
7
+ to build Sciline pipelines for simple workflows.
8
+ If multiple different kinds of files (e.g., sample and background runs)
9
+ are needed, custom types and providers need to be defined to wrap
10
+ the basic ones here.
11
+ """
12
+
13
+ from contextlib import nullcontext
14
+ from pathlib import Path
15
+ from typing import BinaryIO, ContextManager, NewType, Optional, Type, Union, cast
16
+
17
+ import scipp as sc
18
+ import scippnexus as snx
19
+
20
+ from .logging import get_logger
21
+
22
+ FilePath = NewType('FilePath', Path)
23
+ """Full path to a NeXus file on disk."""
24
+ NeXusFile = NewType('NeXusFile', BinaryIO)
25
+ """An open NeXus file.
26
+
27
+ Can be any file handle for reading binary data.
28
+
29
+ Note that this cannot be used as a parameter in Sciline as there are no
30
+ concrete implementations of ``BinaryIO``.
31
+ The type alias is provided for callers of load functions outside of pipelines.
32
+ """
33
+ NeXusGroup = NewType('NeXusGroup', snx.Group)
34
+ """A ScippNexus group in an open file."""
35
+
36
+ NeXusDetectorName = NewType('NeXusDetectorName', str)
37
+ """Name of a detector (bank) in a NeXus file."""
38
+ NeXusEntryName = NewType('NeXusEntryName', str)
39
+ """Name of an entry in a NeXus file."""
40
+ NeXusMonitorName = NewType('NeXusMonitorName', str)
41
+ """Name of a monitor in a NeXus file."""
42
+ NeXusSourceName = NewType('NeXusSourceName', str)
43
+ """Name of a source in a NeXus file."""
44
+
45
+ RawDetector = NewType('RawDetector', sc.DataGroup)
46
+ """Full raw data from a NeXus detector."""
47
+ RawDetectorData = NewType('RawDetectorData', sc.DataArray)
48
+ """Data extracted from a RawDetector."""
49
+ RawMonitor = NewType('RawMonitor', sc.DataGroup)
50
+ """Full raw data from a NeXus monitor."""
51
+ RawMonitorData = NewType('RawMonitorData', sc.DataArray)
52
+ """Data extracted from a RawMonitor."""
53
+ RawSample = NewType('RawSample', sc.DataGroup)
54
+ """Raw data from a NeXus sample."""
55
+ RawSource = NewType('RawSource', sc.DataGroup)
56
+ """Raw data from a NeXus source."""
57
+
58
+
59
+ def load_detector(
60
+ file_path: Union[FilePath, NeXusFile, NeXusGroup],
61
+ *,
62
+ detector_name: NeXusDetectorName,
63
+ entry_name: Optional[NeXusEntryName] = None,
64
+ ) -> RawDetector:
65
+ """Load a single detector (bank) from a NeXus file.
66
+
67
+ The detector positions are computed automatically from NeXus transformations,
68
+ and the combined transformation is stored under the name 'transform'.
69
+
70
+ Parameters
71
+ ----------
72
+ file_path:
73
+ Indicates where to load data from.
74
+ One of:
75
+
76
+ - Path to a NeXus file on disk.
77
+ - File handle or buffer for reading binary data.
78
+ - A ScippNexus group of the root of a NeXus file.
79
+ detector_name:
80
+ Name of the detector (bank) to load.
81
+ Must be a group in an instrument group in the entry (see below).
82
+ entry_name:
83
+ Name of the entry that contains the detector.
84
+ If ``None``, the entry will be located based
85
+ on its NeXus class, but there cannot be more than 1.
86
+
87
+ Returns
88
+ -------
89
+ :
90
+ A data group containing the detector events or histogram
91
+ and any auxiliary data stored in the same NeXus group.
92
+ """
93
+ return RawDetector(
94
+ _load_group_with_positions(
95
+ file_path,
96
+ group_name=detector_name,
97
+ nx_class=snx.NXdetector,
98
+ entry_name=entry_name,
99
+ )
100
+ )
101
+
102
+
103
+ def load_monitor(
104
+ file_path: Union[FilePath, NeXusFile, NeXusGroup],
105
+ *,
106
+ monitor_name: NeXusMonitorName,
107
+ entry_name: Optional[NeXusEntryName] = None,
108
+ ) -> RawMonitor:
109
+ """Load a single monitor from a NeXus file.
110
+
111
+ The monitor position is computed automatically from NeXus transformations,
112
+ and the combined transformation is stored under the name 'transform'.
113
+
114
+ Parameters
115
+ ----------
116
+ file_path:
117
+ Indicates where to load data from.
118
+ One of:
119
+
120
+ - Path to a NeXus file on disk.
121
+ - File handle or buffer for reading binary data.
122
+ - A ScippNexus group of the root of a NeXus file.
123
+ monitor_name:
124
+ Name of the monitor to load.
125
+ Must be a group in an instrument group in the entry (see below).
126
+ entry_name:
127
+ Name of the entry that contains the monitor.
128
+ If ``None``, the entry will be located based
129
+ on its NeXus class, but there cannot be more than 1.
130
+
131
+ Returns
132
+ -------
133
+ :
134
+ A data group containing the monitor events or histogram
135
+ and any auxiliary data stored in the same NeXus group.
136
+ """
137
+ return RawMonitor(
138
+ _load_group_with_positions(
139
+ file_path,
140
+ group_name=monitor_name,
141
+ nx_class=snx.NXmonitor,
142
+ entry_name=entry_name,
143
+ )
144
+ )
145
+
146
+
147
+ def load_source(
148
+ file_path: Union[FilePath, NeXusFile, NeXusGroup],
149
+ *,
150
+ source_name: Optional[NeXusSourceName] = None,
151
+ entry_name: Optional[NeXusEntryName] = None,
152
+ ) -> RawSource:
153
+ """Load a source from a NeXus file.
154
+
155
+ The source position is computed automatically from NeXus transformations,
156
+ and the combined transformation is stored under the name 'transform'.
157
+
158
+ Parameters
159
+ ----------
160
+ file_path:
161
+ Indicates where to load data from.
162
+ One of:
163
+
164
+ - Path to a NeXus file on disk.
165
+ - File handle or buffer for reading binary data.
166
+ - A ScippNexus group of the root of a NeXus file.
167
+ source_name:
168
+ Name of the source to load.
169
+ Must be a group in an instrument group in the entry (see below).
170
+ If ``None``, the source will be located based
171
+ on its NeXus class.
172
+ entry_name:
173
+ Name of the instrument that contains the source.
174
+ If ``None``, the entry will be located based
175
+ on its NeXus class, but there cannot be more than 1.
176
+
177
+ Returns
178
+ -------
179
+ :
180
+ A data group containing all data stored in
181
+ the source NeXus group.
182
+ """
183
+ return RawSource(
184
+ _load_group_with_positions(
185
+ file_path,
186
+ group_name=source_name,
187
+ nx_class=snx.NXsource,
188
+ entry_name=entry_name,
189
+ )
190
+ )
191
+
192
+
193
+ def load_sample(
194
+ file_path: Union[FilePath, NeXusFile, NeXusGroup],
195
+ entry_name: Optional[NeXusEntryName] = None,
196
+ ) -> RawSample:
197
+ """Load a sample from a NeXus file.
198
+
199
+ The sample is located based on its NeXus class.
200
+ There can be only one sample in a NeXus file or
201
+ in the entry indicated by ``entry_name``.
202
+
203
+ Parameters
204
+ ----------
205
+ file_path:
206
+ Indicates where to load data from.
207
+ One of:
208
+
209
+ - Path to a NeXus file on disk.
210
+ - File handle or buffer for reading binary data.
211
+ - A ScippNexus group of the root of a NeXus file.
212
+ entry_name:
213
+ Name of the instrument that contains the source.
214
+ If ``None``, the entry will be located based
215
+ on its NeXus class, but there cannot be more than 1.
216
+
217
+ Returns
218
+ -------
219
+ :
220
+ A data group containing all data stored in
221
+ the sample NeXus group.
222
+ """
223
+ with _open_nexus_file(file_path) as f:
224
+ entry = _unique_child_group(f, snx.NXentry, entry_name)
225
+ loaded = cast(sc.DataGroup, _unique_child_group(entry, snx.NXsample, None)[()])
226
+ return RawSample(loaded)
227
+
228
+
229
+ def _load_group_with_positions(
230
+ file_path: Union[FilePath, NeXusFile, NeXusGroup],
231
+ *,
232
+ group_name: Optional[str],
233
+ nx_class: Type[snx.NXobject],
234
+ entry_name: Optional[NeXusEntryName] = None,
235
+ ) -> sc.DataGroup:
236
+ with _open_nexus_file(file_path) as f:
237
+ entry = _unique_child_group(f, snx.NXentry, entry_name)
238
+ instrument = _unique_child_group(entry, snx.NXinstrument, None)
239
+ loaded = cast(
240
+ sc.DataGroup, _unique_child_group(instrument, nx_class, group_name)[()]
241
+ )
242
+
243
+ transform_out_name = 'transform'
244
+ if transform_out_name in loaded:
245
+ raise RuntimeError(
246
+ f"Loaded data contains an item '{transform_out_name}' but we want to "
247
+ "store the combined NeXus transformations under that name."
248
+ )
249
+ position_out_name = 'position'
250
+ if position_out_name in loaded:
251
+ raise RuntimeError(
252
+ f"Loaded data contains an item '{position_out_name}' but we want to "
253
+ "store the computed positions under that name."
254
+ )
255
+
256
+ loaded = snx.compute_positions(
257
+ loaded, store_position=position_out_name, store_transform=transform_out_name
258
+ )
259
+ return loaded
260
+
261
+
262
+ def _open_nexus_file(
263
+ file_path: Union[FilePath, NeXusFile, NeXusGroup]
264
+ ) -> ContextManager:
265
+ if isinstance(file_path, getattr(NeXusGroup, '__supertype__', type(None))):
266
+ return nullcontext(file_path)
267
+ return snx.File(file_path)
268
+
269
+
270
+ def _unique_child_group(
271
+ group: snx.Group, nx_class: Type[snx.NXobject], name: Optional[str]
272
+ ) -> snx.Group:
273
+ if name is not None:
274
+ child = group[name]
275
+ if isinstance(child, snx.Field):
276
+ raise ValueError(
277
+ f"Expected a NeXus group as item '{name}' but got a field."
278
+ )
279
+ if child.nx_class != nx_class:
280
+ raise ValueError(
281
+ f"The NeXus group '{name}' was expected to be a "
282
+ f'{nx_class} but is a {child.nx_class}.'
283
+ )
284
+ return child
285
+
286
+ children = group[nx_class]
287
+ if len(children) != 1:
288
+ raise ValueError(f'Expected exactly one {nx_class} group, got {len(children)}')
289
+ return next(iter(children.values())) # type: ignore[return-value]
290
+
291
+
292
+ def extract_detector_data(detector: RawDetector) -> RawDetectorData:
293
+ """Get and return the events or histogram from a detector loaded from NeXus.
294
+
295
+ This function looks for a data array in the detector group and returns that.
296
+
297
+ Parameters
298
+ ----------
299
+ detector:
300
+ A detector loaded from NeXus.
301
+
302
+ Returns
303
+ -------
304
+ :
305
+ A data array containing the events or histogram.
306
+
307
+ Raises
308
+ ------
309
+ ValueError
310
+ If there is more than one data array.
311
+
312
+ See also
313
+ --------
314
+ load_detector:
315
+ Load a detector from a NeXus file in a format compatible with
316
+ ``extract_detector_data``.
317
+ """
318
+ return RawDetectorData(_extract_events_or_histogram(detector))
319
+
320
+
321
+ def extract_monitor_data(monitor: RawMonitor) -> RawMonitorData:
322
+ """Get and return the events or histogram from a monitor loaded from NeXus.
323
+
324
+ This function looks for a data array in the monitor group and returns that.
325
+
326
+ Parameters
327
+ ----------
328
+ monitor:
329
+ A monitor loaded from NeXus.
330
+
331
+ Returns
332
+ -------
333
+ :
334
+ A data array containing the events or histogram.
335
+
336
+ Raises
337
+ ------
338
+ ValueError
339
+ If there is more than one data array.
340
+
341
+ See also
342
+ --------
343
+ load_monitor:
344
+ Load a monitor from a NeXus file in a format compatible with
345
+ ``extract_monitor_data``.
346
+ """
347
+ return RawMonitorData(_extract_events_or_histogram(monitor))
348
+
349
+
350
+ def _extract_events_or_histogram(dg: sc.DataGroup) -> sc.DataArray:
351
+ event_data_arrays = {
352
+ key: value
353
+ for key, value in dg.items()
354
+ if isinstance(value, sc.DataArray) and value.bins is not None
355
+ }
356
+ histogram_data_arrays = {
357
+ key: value
358
+ for key, value in dg.items()
359
+ if isinstance(value, sc.DataArray) and value.bins is None
360
+ }
361
+ if (array := _select_unique_array(event_data_arrays, 'event')) is not None:
362
+ if histogram_data_arrays:
363
+ get_logger().info(
364
+ "Selecting event data '%s' in favor of histogram data {%s}",
365
+ next(iter(event_data_arrays.keys())),
366
+ ', '.join(map(lambda k: f"'{k}'", histogram_data_arrays)),
367
+ )
368
+ return array
369
+
370
+ if (array := _select_unique_array(histogram_data_arrays, 'histogram')) is not None:
371
+ return array
372
+
373
+ raise ValueError(
374
+ "Raw data loaded from NeXus does not contain events or a histogram. "
375
+ "Expected to find a data array, "
376
+ f"but the data only contains {set(dg.keys())}"
377
+ )
378
+
379
+
380
+ def _select_unique_array(
381
+ arrays: dict[str, sc.DataArray], mapping_name: str
382
+ ) -> Optional[sc.DataArray]:
383
+ if not arrays:
384
+ return None
385
+ if len(arrays) > 1:
386
+ raise ValueError(
387
+ f"Raw data loaded from NeXus contains more than one {mapping_name} "
388
+ "data array. Cannot uniquely identify the data to extract. "
389
+ f"Got {mapping_name} items {set(arrays.keys())}"
390
+ )
391
+ return next(iter(arrays.values()))
ess/reduce/py.typed ADDED
File without changes
@@ -0,0 +1,29 @@
1
+ BSD 3-Clause License
2
+
3
+ Copyright (c) 2024, Scipp contributors (https://github.com/scipp)
4
+ All rights reserved.
5
+
6
+ Redistribution and use in source and binary forms, with or without
7
+ modification, are permitted provided that the following conditions are met:
8
+
9
+ 1. Redistributions of source code must retain the above copyright notice, this
10
+ list of conditions and the following disclaimer.
11
+
12
+ 2. Redistributions in binary form must reproduce the above copyright notice,
13
+ this list of conditions and the following disclaimer in the documentation
14
+ and/or other materials provided with the distribution.
15
+
16
+ 3. Neither the name of the copyright holder nor the names of its
17
+ contributors may be used to endorse or promote products derived from
18
+ this software without specific prior written permission.
19
+
20
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,71 @@
1
+ Metadata-Version: 2.1
2
+ Name: essreduce
3
+ Version: 24.3.11
4
+ Summary: Common data reduction tools for the ESS facility
5
+ Author: Scipp contributors
6
+ License: BSD 3-Clause License
7
+
8
+ Copyright (c) 2024, Scipp contributors (https://github.com/scipp)
9
+ All rights reserved.
10
+
11
+ Redistribution and use in source and binary forms, with or without
12
+ modification, are permitted provided that the following conditions are met:
13
+
14
+ 1. Redistributions of source code must retain the above copyright notice, this
15
+ list of conditions and the following disclaimer.
16
+
17
+ 2. Redistributions in binary form must reproduce the above copyright notice,
18
+ this list of conditions and the following disclaimer in the documentation
19
+ and/or other materials provided with the distribution.
20
+
21
+ 3. Neither the name of the copyright holder nor the names of its
22
+ contributors may be used to endorse or promote products derived from
23
+ this software without specific prior written permission.
24
+
25
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
26
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
28
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
29
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
31
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
32
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
33
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
34
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35
+
36
+ Project-URL: Bug Tracker, https://github.com/scipp/essreduce/issues
37
+ Project-URL: Documentation, https://scipp.github.io/essreduce
38
+ Project-URL: Source, https://github.com/scipp/essreduce
39
+ Classifier: Intended Audience :: Science/Research
40
+ Classifier: License :: OSI Approved :: BSD License
41
+ Classifier: Natural Language :: English
42
+ Classifier: Operating System :: OS Independent
43
+ Classifier: Programming Language :: Python :: 3
44
+ Classifier: Programming Language :: Python :: 3 :: Only
45
+ Classifier: Programming Language :: Python :: 3.10
46
+ Classifier: Programming Language :: Python :: 3.11
47
+ Classifier: Programming Language :: Python :: 3.12
48
+ Classifier: Topic :: Scientific/Engineering
49
+ Classifier: Typing :: Typed
50
+ Requires-Python: >=3.10
51
+ Description-Content-Type: text/markdown
52
+ License-File: LICENSE
53
+ Requires-Dist: scipp >=24.02.0
54
+ Requires-Dist: scippnexus >=24.03.0
55
+
56
+ [![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-2.1-4baaaa.svg)](CODE_OF_CONDUCT.md)
57
+ [![PyPI badge](http://img.shields.io/pypi/v/essreduce.svg)](https://pypi.python.org/pypi/essreduce)
58
+ [![Anaconda-Server Badge](https://anaconda.org/scipp/essreduce/badges/version.svg)](https://anaconda.org/scipp/essreduce)
59
+ [![License: BSD 3-Clause](https://img.shields.io/badge/License-BSD%203--Clause-blue.svg)](LICENSE)
60
+
61
+ # ESSreduce
62
+
63
+ ## About
64
+
65
+ Common data reduction tools for the ESS facility
66
+
67
+ ## Installation
68
+
69
+ ```sh
70
+ python -m pip install essreduce
71
+ ```
@@ -0,0 +1,9 @@
1
+ ess/reduce/__init__.py,sha256=4QNAPVaH53uwheIL3JYt81qrvMroCvgEjGX4KX6MKrA,356
2
+ ess/reduce/logging.py,sha256=6n8Czq4LZ3OK9ENlKsWSI1M3KvKv6_HSoUiV4__IUlU,357
3
+ ess/reduce/nexus.py,sha256=fsRATeRdRERnia3lXayFggrWmQYwEGDNKRxKVYZXCSY,12458
4
+ ess/reduce/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
+ essreduce-24.3.11.dist-info/LICENSE,sha256=nVEiume4Qj6jMYfSRjHTM2jtJ4FGu0g-5Sdh7osfEYw,1553
6
+ essreduce-24.3.11.dist-info/METADATA,sha256=cFpIWjpfuRDknnXVZ5njXLg7RlL0sZyC-0HgZJTamT0,3360
7
+ essreduce-24.3.11.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
8
+ essreduce-24.3.11.dist-info/top_level.txt,sha256=0JxTCgMKPLKtp14wb1-RKisQPQWX7i96innZNvHBr-s,4
9
+ essreduce-24.3.11.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: bdist_wheel (0.42.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1 @@
1
+ ess