Skip to content

Commit 303d322

Browse files
committed
Add open_component_group
1 parent 77fbe0a commit 303d322

File tree

2 files changed

+95
-14
lines changed

2 files changed

+95
-14
lines changed

src/ess/reduce/nexus/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
load_all_components,
2121
load_component,
2222
load_data,
23+
open_component_group,
2324
open_nexus_file,
2425
)
2526
from .workflow import GenericNeXusWorkflow
@@ -32,6 +33,7 @@
3233
'load_all_components',
3334
'load_component',
3435
'load_data',
36+
'open_component_group',
3537
'open_nexus_file',
3638
'types',
3739
]

src/ess/reduce/nexus/_nexus_loader.py

Lines changed: 93 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -46,29 +46,52 @@ def load_component(
4646
location: NeXusLocationSpec,
4747
*,
4848
nx_class: type[snx.NXobject],
49+
parent_class: type[snx.NXobject] | None = None,
4950
definitions: Mapping | None | NoNewDefinitionsType = NoNewDefinitions,
5051
) -> sc.DataGroup:
51-
"""Load a single component of a given class from NeXus."""
52+
"""Load a single component of a given class from NeXus.
53+
54+
Parameters
55+
----------
56+
location:
57+
Specifies (part of) the location of the component to load.
58+
nx_class:
59+
NeXus class of the component to load.
60+
parent_class:
61+
NeXus class of the parent of the component to load.
62+
If ``None``, is deduced from ``nx_class`` if possible.
63+
definitions:
64+
Application definitions to use for the file.
65+
66+
Returns
67+
-------
68+
:
69+
The loaded component as a data group.
70+
"""
5271
selection = location.selection
53-
group_name = location.component_name
54-
with _open_component_parent(
55-
location, nx_class=nx_class, definitions=definitions
56-
) as parent:
57-
component = _unique_child_group(parent, nx_class, group_name)
58-
loaded = cast(sc.DataGroup, component[selection])
59-
loaded['nexus_component_name'] = component.name.rsplit('/', 1)[-1]
72+
with open_component_group(
73+
location,
74+
nx_class=nx_class,
75+
parent_class=parent_class,
76+
definitions=definitions,
77+
) as group:
78+
loaded = cast(sc.DataGroup, group[selection])
79+
loaded['nexus_component_name'] = group.name.rsplit('/', 1)[-1]
6080
return loaded
6181

6282

6383
def load_all_components(
6484
location: NeXusAllLocationSpec,
6585
*,
6686
nx_class: type[snx.NXobject],
87+
parent_class: type[snx.NXobject] | None = None,
6788
definitions: Mapping | None | NoNewDefinitionsType = NoNewDefinitions,
6889
) -> sc.DataGroup:
6990
"""Load all components of a given class from NeXus."""
7091
with _open_component_parent(
71-
location, nx_class=nx_class, definitions=definitions
92+
location,
93+
parent_class=_deduce_component_parent_class(nx_class, parent_class),
94+
definitions=definitions,
7295
) as parent:
7396
components = sc.DataGroup()
7497
for name, component in parent[nx_class].items():
@@ -226,21 +249,77 @@ def _open_nexus_file_from_path(
226249

227250

228251
@contextmanager
229-
def _open_component_parent(
252+
def open_component_group(
230253
location: NeXusLocationSpec,
231254
*,
232255
nx_class: type[snx.NXobject],
256+
parent_class: type[snx.NXobject] | None = None,
257+
definitions: Mapping | None | NoNewDefinitionsType = NoNewDefinitions,
258+
) -> Generator[snx.Group, None, None]:
259+
"""Open the HDF5 group of a NeXus component.
260+
261+
Parameters
262+
----------
263+
location:
264+
Specifies (part of) the location of the component to load.
265+
nx_class:
266+
NeXus class of the component to load.
267+
parent_class:
268+
NeXus class of the parent of the component to load.
269+
If ``None``, is deduced from ``nx_class`` if possible.
270+
definitions:
271+
Application definitions to use for the file.
272+
273+
Returns
274+
-------
275+
:
276+
A context manager for the group of the specified component.
277+
"""
278+
group_name = location.component_name
279+
with _open_component_parent(
280+
location,
281+
parent_class=_deduce_component_parent_class(nx_class, parent_class),
282+
definitions=definitions,
283+
) as parent:
284+
yield _unique_child_group(parent, nx_class, group_name)
285+
286+
287+
@contextmanager
288+
def _open_component_parent(
289+
location: NeXusLocationSpec,
290+
*,
291+
parent_class: type[snx.NXobject],
233292
definitions: Mapping | None | NoNewDefinitionsType = NoNewDefinitions,
234293
) -> Generator[snx.Group, None, None]:
235294
"""Locate the parent group of a NeXus component."""
236295
file_path = location.filename
237296
entry_name = location.entry_name
238297
with open_nexus_file(file_path, definitions=definitions) as f:
239298
entry = _unique_child_group(f, snx.NXentry, entry_name)
240-
if nx_class is snx.NXsample:
241-
yield entry
242-
else:
243-
yield _unique_child_group(entry, snx.NXinstrument, None)
299+
match parent_class:
300+
case snx.NXentry:
301+
yield entry
302+
case snx.NXinstrument:
303+
yield _unique_child_group(entry, snx.NXinstrument, None)
304+
case _:
305+
raise NotImplementedError(
306+
f"No support for loading a NeXus component from a {parent_class}."
307+
)
308+
309+
310+
def _deduce_component_parent_class(
311+
nx_class: type[snx.NXobject], parent_class: type[snx.NXobject] | None
312+
) -> type[snx.NXobject]:
313+
if parent_class is not None:
314+
return parent_class
315+
316+
match nx_class:
317+
case snx.NXsample:
318+
return snx.NXentry
319+
case _:
320+
# Most components are in the instrument,
321+
# callers need to override this for specialized components stored elsewhere.
322+
return snx.NXinstrument
244323

245324

246325
def _unique_child_group(

0 commit comments

Comments
 (0)