Skip to content

Commit 65e420e

Browse files
committed
Support loading histogram monitors
1 parent 1447a0a commit 65e420e

File tree

3 files changed

+88
-8
lines changed

3 files changed

+88
-8
lines changed

src/ess/reduce/data.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,15 @@ def get_path(self, name: str, unzip: bool = False) -> str:
4040
)
4141

4242

43+
_dream_registry = Registry(
44+
instrument='dream',
45+
files={
46+
"TEST_977695_00068064.hdf": "md5:9e6ee9ec70d7c5e8c0c93b9e07e8949f",
47+
},
48+
version='2',
49+
)
50+
51+
4352
_loki_registry = Registry(
4453
instrument='loki',
4554
files={
@@ -94,3 +103,11 @@ def loki_tutorial_background_run_60393() -> str:
94103
def loki_tutorial_sample_transmission_run() -> str:
95104
"""Sample transmission run (sample + sample holder/can + transmission monitor)."""
96105
return _loki_registry.get_path('60394-2022-02-28_2215.nxs')
106+
107+
108+
def dream_coda_test_file() -> str:
109+
"""CODA file for DREAM where most pulses have been removed.
110+
111+
See ``tools/shrink_nexus.py``.
112+
"""
113+
return _dream_registry.get_path('TEST_977695_00068064.hdf')

src/ess/reduce/nexus/workflow.py

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -560,10 +560,10 @@ def __init__(
560560
class _DummyField:
561561
"""Dummy field that can replace snx.Field in NXmonitor."""
562562

563-
def __init__(self):
563+
def __init__(self, dim: str):
564564
self.attrs = {}
565-
self.sizes = {'event_time_zero': 0}
566-
self.dims = ('event_time_zero',)
565+
self.sizes = {dim: 0}
566+
self.dims = (dim,)
567567
self.shape = (0,)
568568

569569
def __getitem__(self, key: Any) -> sc.Variable:
@@ -573,14 +573,17 @@ def __getitem__(self, key: Any) -> sc.Variable:
573573
class _StrippedMonitor(snx.NXmonitor):
574574
"""Monitor definition without event data for ScippNexus.
575575
576-
Drops NXevent_data group, data is replaced by a dummy field.
576+
Drops NXevent_data and NXdata groups, data is replaced by a dummy field.
577577
"""
578578

579579
def __init__(
580580
self, attrs: dict[str, Any], children: dict[str, snx.Field | snx.Group]
581581
):
582-
children = _drop(children, (snx.NXevent_data,))
583-
children['data'] = _DummyField()
582+
is_dense = snx.NXdata in (
583+
getattr(child, 'nx_class', None) for child in children
584+
)
585+
children = _drop(children, (snx.NXevent_data, snx.NXdata))
586+
children['data'] = _DummyField(dim='time' if is_dense else 'event_time_zero')
584587
super().__init__(attrs=attrs, children=children)
585588

586589

@@ -590,6 +593,12 @@ def _add_variances(da: sc.DataArray) -> sc.DataArray:
590593
content = out.bins.constituents['data']
591594
if content.variances is None:
592595
content.variances = content.values
596+
elif out.variances is None:
597+
try:
598+
out.variances = out.values
599+
except sc.VariancesError:
600+
out = out.to(dtype=sc.DType.float64)
601+
out.variances = out.values
593602
return out
594603

595604

tests/nexus/workflow_test.py

Lines changed: 56 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -471,6 +471,21 @@ def monitor_event_data() -> workflow.NeXusData[FrameMonitor1, SampleRun]:
471471
)
472472

473473

474+
@pytest.fixture
475+
def monitor_histogram_data() -> workflow.NeXusData[FrameMonitor1, SampleRun]:
476+
time = sc.epoch(unit='ns') + sc.arange('time', 1, 6, unit='s').to(unit='ns')
477+
frame_time = sc.arange('frame_time', 12, unit='ms').to(unit='ns')
478+
return workflow.NeXusData[FrameMonitor1, SampleRun](
479+
sc.DataArray(
480+
10.0
481+
* sc.arange('x', 5 * 12, unit='counts').fold(
482+
'x', sizes={'time': 5, 'frame_time': 12}
483+
),
484+
coords={'time': time, 'frame_time': frame_time},
485+
)
486+
)
487+
488+
474489
def test_assemble_monitor_data_adds_events_as_values_and_coords(
475490
calibrated_monitor, monitor_event_data
476491
) -> None:
@@ -482,7 +497,7 @@ def test_assemble_monitor_data_adds_events_as_values_and_coords(
482497
)
483498

484499

485-
def test_assemble_monitor_data_adds_variances_to_weights(
500+
def test_assemble_monitor_data_adds_variances_to_events(
486501
calibrated_monitor, monitor_event_data
487502
) -> None:
488503
monitor_data = workflow.assemble_monitor_data(
@@ -494,6 +509,30 @@ def test_assemble_monitor_data_adds_variances_to_weights(
494509
)
495510

496511

512+
def test_assemble_monitor_data_adds_histogram_as_values_and_coords(
513+
calibrated_monitor, monitor_histogram_data
514+
) -> None:
515+
monitor_data = workflow.assemble_monitor_data(
516+
calibrated_monitor, monitor_histogram_data
517+
)
518+
assert_identical(
519+
monitor_data.drop_coords(tuple(calibrated_monitor.coords)),
520+
monitor_histogram_data,
521+
)
522+
523+
524+
def test_assemble_monitor_data_adds_variances_to_weights(
525+
calibrated_monitor, monitor_histogram_data
526+
) -> None:
527+
monitor_data = workflow.assemble_monitor_data(
528+
calibrated_monitor, monitor_histogram_data
529+
)
530+
assert_identical(
531+
sc.variances(monitor_data.drop_coords(tuple(calibrated_monitor.coords))),
532+
monitor_histogram_data,
533+
)
534+
535+
497536
def test_assemble_monitor_preserves_coords(calibrated_monitor, monitor_event_data):
498537
calibrated_monitor.coords['abc'] = sc.scalar(1.2)
499538
monitor_data = workflow.assemble_monitor_data(
@@ -510,7 +549,7 @@ def test_assemble_monitor_preserves_masks(calibrated_monitor, monitor_event_data
510549
assert 'mymask' in monitor_data.masks
511550

512551

513-
def test_load_monitor_workflow() -> None:
552+
def test_load_event_monitor_workflow() -> None:
514553
wf = LoadMonitorWorkflow(run_types=[SampleRun], monitor_types=[FrameMonitor1])
515554
wf[Filename[SampleRun]] = data.loki_tutorial_sample_run_60250()
516555
wf[NeXusName[FrameMonitor1]] = 'monitor_1'
@@ -519,6 +558,21 @@ def test_load_monitor_workflow() -> None:
519558
assert 'source_position' in da.coords
520559
assert da.bins is not None
521560
assert da.dims == ('event_time_zero',)
561+
assert da.bins.constituents['data'].variances is not None
562+
563+
564+
def test_load_histogram_monitor_workflow() -> None:
565+
wf = LoadMonitorWorkflow(run_types=[SampleRun], monitor_types=[FrameMonitor1])
566+
wf[Filename[SampleRun]] = data.dream_coda_test_file()
567+
wf[NeXusName[FrameMonitor1]] = 'monitor_bunker'
568+
da = wf.compute(MonitorData[SampleRun, FrameMonitor1])
569+
assert 'position' in da.coords
570+
assert 'source_position' in da.coords
571+
assert da.bins is None
572+
assert da.dims == ('time', 'frame_time')
573+
assert 'time' in da.coords.keys()
574+
assert 'frame_time' in da.coords.keys()
575+
assert da.variances is not None
522576

523577

524578
def test_load_detector_workflow() -> None:

0 commit comments

Comments
 (0)