scipp / essreduce

Common functionality for ESS data reduction
https://scipp.github.io/essreduce/
BSD 3-Clause "New" or "Revised" License
1 stars 1 forks source link

Support loading histogram monitors from NeXus #101

Closed jl-wynen closed 2 days ago

jl-wynen commented 2 weeks ago

Simulations may contain histogram monitors instead of event monitors. E.g., BIFROST. This needs some adjustments to the workflow. This could be achieved with an argument in the GenericNeXusWorkflow constructor.

Here is a first version of what the providers could look like. Though we might want to merge NeXusMonitorData and NeXusMonitorEventData as well as NeXusMonitorDataLocationSpec and NeXusMonitorEventLocationSpec.

@dataclass
class NeXusMonitorDataLocationSpec(
    NeXusLocationSpec[snx.NXevent_data], Generic[RunType, MonitorType]
):
    """NeXus filename and parameters to identify (parts of) monitor data to load."""

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

def monitor_data_by_name(
    filename: NeXusFileSpec[RunType],
    name: NeXusMonitorName[MonitorType],
    selection: PulseSelection[RunType],
) -> NeXusMonitorDataLocationSpec[RunType, MonitorType]:
    if selection.value != slice(None, None):
        raise ValueError("Pulse selection is not supported for histogrammed monitors")
    return NeXusMonitorDataLocationSpec[RunType, MonitorType](
        filename=filename.value,
        component_name=name,
    )

def load_data(
    file_path: AnyRunFilename,
    *,
    entry_name: NeXusEntryName | None = None,
    component_name: str,
) -> sc.DataArray:
    from ess.reduce.nexus._nexus_loader import _open_nexus_file, _unique_child_group

    with _open_nexus_file(file_path) as f:
        entry = _unique_child_group(f, snx.NXentry, entry_name)
        instrument = _unique_child_group(entry, snx.NXinstrument, None)
        component = instrument[component_name]
        data = _unique_child_group(component, snx.NXdata, None)
        return data[()]

def load_nexus_monitor_data(
    location: NeXusMonitorDataLocationSpec[RunType, MonitorType],
) -> NeXusMonitorData[RunType, MonitorType]:
    if location.selection:
        raise ValueError("Data selection is not supported for histogrammed monitors")

    return NeXusMonitorEventData[RunType, MonitorType](
        load_data(
            file_path=location.filename,
            entry_name=location.entry_name,
            component_name=location.component_name,
        )
    )

def assemble_monitor_data_bifrost(
    monitor: CalibratedMonitor[RunType, MonitorType],
    data: NeXusMonitorData[RunType, MonitorType],
) -> MonitorData[RunType, MonitorType]:
    return MonitorData[RunType, MonitorType](
        base_assemble_monitor_data(monitor, data)
    )
SimonHeybrock commented 3 days ago

@jl-wynen wasn't this fixed by #106?