Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Load histogram monitors #104

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/ess/reduce/nexus/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from . import types
from ._nexus_loader import (
load_event_data,
load_hist_data,
group_event_data,
load_component,
compute_component_position,
Expand All @@ -23,6 +24,7 @@
'types',
'group_event_data',
'load_event_data',
'load_hist_data',
'load_component',
'compute_component_position',
'extract_events_or_histogram',
Expand Down
43 changes: 43 additions & 0 deletions src/ess/reduce/nexus/_nexus_loader.py
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,49 @@ def _select_unique_array(
return next(iter(arrays.values()))


def load_hist_data(
file_path: AnyRunFilename,
selection=(),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

selection isn't documented or type hinted.

*,
entry_name: NeXusEntryName | None = None,
component_name: str,
definitions: Mapping | NoNewDefinitionsType = NoNewDefinitions,
) -> sc.DataArray:
"""Load NXdata of a detector or monitor from a NeXus file.

Parameters
----------
file_path:
Indicates where to load data from.
One of:

- Path to a NeXus file on disk.
- File handle or buffer for reading binary data.
- A ScippNexus group of the root of a NeXus file.
component_name:
Name of the NXdetector or NXmonitor containing the NXevent_data to load.
Must be a group in an instrument group in the entry (see below).
entry_name:
Name of the entry that contains the detector.
If ``None``, the entry will be located based
on its NeXus class, but there cannot be more than 1.
definitions:
Definitions used by scippnexus loader, see :py:`scippnexus.File`
for documentation.

Returns
-------
:
Data array with events grouped by event_time_zero, as in the NeXus file.
"""
with _open_nexus_file(file_path, definitions=definitions) as f:
entry = _unique_child_group(f, snx.NXentry, entry_name)
instrument = _unique_child_group(entry, snx.NXinstrument, None)
component = instrument[component_name]
event_data = _unique_child_group(component, snx.NXdata, None)
return event_data[selection]


def load_event_data(
file_path: AnyRunFilename,
selection=(),
Expand Down
13 changes: 13 additions & 0 deletions src/ess/reduce/nexus/generic_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,12 @@ class NeXusMonitorEventData(
"""Data array loaded from a NeXus NXevent_data group within an NXmonitor."""


class NeXusMonitorHistData(
sciline.ScopeTwoParams[RunType, MonitorType, sc.DataArray], sc.DataArray
):
"""Data array loaded from a NeXus NXdata group within an NXmonitor."""


class SourcePosition(sciline.Scope[RunType, sc.Variable], sc.Variable):
"""Position of the neutron source."""

Expand Down Expand Up @@ -198,3 +204,10 @@ class NeXusMonitorEventLocationSpec(
NeXusLocationSpec[snx.NXevent_data], Generic[RunType, MonitorType]
):
"""NeXus filename and parameters to identify (parts of) monitor events to load."""


@dataclass
class NeXusMonitorHistLocationSpec(
NeXusLocationSpec[snx.NXdata], Generic[RunType, MonitorType]
):
"""NeXus filename and parameters to identify a monitor histogram to load."""
96 changes: 77 additions & 19 deletions src/ess/reduce/nexus/generic_workflow.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@
instrument-specific wrappers or implementations.
"""

from collections.abc import Callable
from typing import Any

import sciline
import scippnexus as snx

Expand Down Expand Up @@ -73,6 +76,16 @@ def monitor_events_by_name(
)


def monitor_histogram_by_name(
filename: gt.NeXusFileSpec[RunType],
name: gt.NeXusMonitorName[MonitorType],
) -> gt.NeXusMonitorHistLocationSpec[RunType, MonitorType]:
return gt.NeXusMonitorHistLocationSpec[RunType, MonitorType](
filename=filename.value,
component_name=name,
)


def detector_by_name(
filename: gt.NeXusFileSpec[RunType], name: NeXusDetectorName
) -> gt.NeXusComponentLocationSpec[snx.NXdetector, RunType]:
Expand Down Expand Up @@ -133,6 +146,14 @@ def load_nexus_monitor_event_data(
)


def load_nexus_monitor_histogram_data(
location: gt.NeXusMonitorHistLocationSpec[RunType, MonitorType],
) -> gt.NeXusMonitorHistData[RunType, MonitorType]:
return gt.NeXusMonitorHistData[RunType, MonitorType](
workflow.load_nexus_monitor_histogram_data(location)
)


def get_source_position(source: gt.NeXusSource[RunType]) -> gt.SourcePosition[RunType]:
return gt.SourcePosition[RunType](workflow.get_source_position(source))

Expand Down Expand Up @@ -181,12 +202,21 @@ def get_calibrated_monitor(
)


def assemble_monitor_data(
def assemble_monitor_event_data(
monitor: gt.CalibratedMonitor[RunType, MonitorType],
event_data: gt.NeXusMonitorEventData[RunType, MonitorType],
) -> gt.MonitorData[RunType, MonitorType]:
return gt.MonitorData[RunType, MonitorType](
workflow.assemble_monitor_data(monitor, event_data)
workflow.assemble_monitor_event_data(monitor, event_data)
)


def assemble_monitor_histogram_data(
monitor: gt.CalibratedMonitor[RunType, MonitorType],
hist_data: gt.NeXusMonitorHistData[RunType, MonitorType],
) -> gt.MonitorData[RunType, MonitorType]:
return gt.MonitorData[RunType, MonitorType](
workflow.assemble_monitor_histogram_data(monitor, hist_data)
)


Expand All @@ -199,28 +229,50 @@ def assemble_monitor_data(
load_nexus_monitor.__doc__ = workflow.load_nexus_monitor.__doc__
load_nexus_detector_event_data.__doc__ = workflow.load_nexus_detector_event_data.__doc__
load_nexus_monitor_event_data.__doc__ = workflow.load_nexus_monitor_event_data.__doc__
load_nexus_monitor_histogram_data.__doc__ = (
workflow.load_nexus_monitor_histogram_data.__doc__
)
get_source_position.__doc__ = workflow.get_source_position.__doc__
get_sample_position.__doc__ = workflow.get_sample_position.__doc__
get_calibrated_detector.__doc__ = workflow.get_calibrated_detector.__doc__
assemble_detector_data.__doc__ = workflow.assemble_detector_data.__doc__
get_calibrated_monitor.__doc__ = workflow.get_calibrated_monitor.__doc__
assemble_monitor_data.__doc__ = workflow.assemble_monitor_data.__doc__
assemble_monitor_event_data.__doc__ = workflow.assemble_monitor_event_data.__doc__
assemble_monitor_histogram_data.__doc__ = (
workflow.assemble_monitor_histogram_data.__doc__
)
monitor_events_by_name.__doc__ = workflow.monitor_events_by_name.__doc__
monitor_histogram_by_name.__doc__ = workflow.monitor_histogram_by_name.__doc__


_common_providers = (workflow.gravity_vector_neg_y, file_path_to_file_spec, all_pulses)

_monitor_providers = (
no_monitor_position_offset,
unique_source_spec,
monitor_by_name,
monitor_events_by_name,
load_nexus_monitor,
load_nexus_monitor_event_data,
load_nexus_source,
get_source_position,
get_calibrated_monitor,
assemble_monitor_data,
)

def _monitor_providers(*, event_monitor: bool) -> tuple[Callable[..., Any], ...]:
if event_monitor:
data_providers = (
assemble_monitor_event_data,
load_nexus_monitor_event_data,
monitor_events_by_name,
)
else:
data_providers = (
assemble_monitor_histogram_data,
load_nexus_monitor_histogram_data,
monitor_histogram_by_name,
)

return (
no_monitor_position_offset,
unique_source_spec,
monitor_by_name,
load_nexus_monitor,
load_nexus_source,
get_source_position,
get_calibrated_monitor,
*data_providers,
)


_detector_providers = (
no_detector_position_offset,
Expand All @@ -239,9 +291,11 @@ def assemble_monitor_data(
)


def LoadMonitorWorkflow() -> sciline.Pipeline:
def LoadMonitorWorkflow(*, event_monitor: bool = True) -> sciline.Pipeline:
"""Generic workflow for loading monitor data from a NeXus file."""
wf = sciline.Pipeline((*_common_providers, *_monitor_providers))
wf = sciline.Pipeline(
(*_common_providers, *_monitor_providers(event_monitor=event_monitor))
)
return wf


Expand All @@ -252,10 +306,14 @@ def LoadDetectorWorkflow() -> sciline.Pipeline:
return wf


def GenericNeXusWorkflow() -> sciline.Pipeline:
def GenericNeXusWorkflow(*, event_monitor: bool = True) -> sciline.Pipeline:
"""Generic workflow for loading detector and monitor data from a NeXus file."""
wf = sciline.Pipeline(
(*_common_providers, *_monitor_providers, *_detector_providers)
(
*_common_providers,
*_monitor_providers(event_monitor=event_monitor),
*_detector_providers,
)
)
wf[DetectorBankSizes] = DetectorBankSizes({})
return wf
7 changes: 7 additions & 0 deletions src/ess/reduce/nexus/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@
"""Data array loaded from a NeXus NXevent_data group within an NXdetector."""
AnyRunAnyNeXusMonitorEventData = NewType('AnyRunAnyNeXusMonitorEventData', sc.DataArray)
"""Data array loaded from a NeXus NXevent_data group within an NXmonitor."""
AnyRunAnyNeXusMonitorHistData = NewType('AnyRunAnyNeXusMonitorHistData', sc.DataArray)
"""Data array loaded from a NeXus NXdata group within an NXmonitor."""

AnyRunSourcePosition = NewType('AnyRunSourcePosition', sc.Variable)
"""Position of the neutron source."""
Expand Down Expand Up @@ -94,3 +96,8 @@ class NeXusLocationSpec(Generic[Component]):
@dataclass
class NeXusEventDataLocationSpec(NeXusLocationSpec[Component]):
"""NeXus filename and parameters to identify (parts of) events to load."""


@dataclass
class NeXusHistDataLocationSpec(NeXusLocationSpec[Component]):
"""NeXus filename and parameters to identify (parts of) a histogram to load."""
Loading