Skip to content

Commit

Permalink
Merge pull request #202 from scipp/load-events-no-t-zero
Browse files Browse the repository at this point in the history
fix: make event_time_zero optional
  • Loading branch information
SimonHeybrock authored Apr 2, 2024
2 parents 6e4e566 + 2fd1884 commit 86a4bfe
Show file tree
Hide file tree
Showing 4 changed files with 72 additions and 11 deletions.
7 changes: 4 additions & 3 deletions src/scippnexus/nxdata.py
Original file line number Diff line number Diff line change
Expand Up @@ -765,9 +765,10 @@ def _group_events(
# copy since sc.bin cannot deal with a non-contiguous view
grouping = asvariable(grouping)
event_id = grouping.flatten(to='event_id').copy()
event_data.bins.coords['event_time_zero'] = sc.bins_like(
event_data, fill_value=event_data.coords['event_time_zero']
)
if 'event_time_zero' in event_data.coords:
event_data.bins.coords['event_time_zero'] = sc.bins_like(
event_data, fill_value=event_data.coords['event_time_zero']
)
# After loading raw NXevent_data it is guaranteed that the event table
# is contiguous and that there is no masking. We can therefore use the
# more efficient approach of binning from scratch instead of erasing the
Expand Down
25 changes: 17 additions & 8 deletions src/scippnexus/nxevent_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,11 @@ def _check_for_missing_fields(fields):


class NXevent_data(NXobject):
mandatory_fields = ("event_time_zero", "event_index", "event_time_offset")
handled_fields = mandatory_fields + ("event_id",)
mandatory_fields = ("event_index", "event_time_offset")
handled_fields = mandatory_fields + (
"event_time_zero",
"event_id",
)

def __init__(self, attrs: Dict[str, Any], children: Dict[str, Union[Field, Group]]):
super().__init__(attrs=attrs, children=children)
Expand Down Expand Up @@ -68,9 +71,12 @@ def read_children(self, select: ScippIndex) -> sc.DataGroup:

select = self.convert_label_index_to_positional(select)
index = to_plain_index([_pulse_dimension], select)
event_time_zero = children['event_time_zero'][index]
last_loaded, event_index = self._get_event_index(children, index)

coords = {}
if 'event_time_zero' in children:
coords['event_time_zero'] = children['event_time_zero'][index]

last_loaded, event_index = self._get_event_index(children, index)
num_event = children["event_time_offset"].shape[0]
# Some files contain uint64 "max" indices, which turn into negatives during
# conversion to int64. This is a hack to get around this.
Expand All @@ -95,9 +101,9 @@ def read_children(self, select: ScippIndex) -> sc.DataGroup:
event_index -= event_index.min()

dg = sc.DataGroup(
event_time_zero=event_time_zero,
event_index=event_index,
event_time_offset=event_time_offset,
**coords,
)
if (event_id := children.get('event_id')) is not None:
dg['event_id'] = event_id[event_select]
Expand Down Expand Up @@ -129,7 +135,10 @@ def _get_event_index(self, children: sc.DataGroup, index):
def assemble(self, children: sc.DataGroup) -> sc.DataArray:
_check_for_missing_fields(children)
event_time_offset = children['event_time_offset']
event_time_zero = children['event_time_zero']

coords = {}
if 'event_time_zero' in children:
coords['event_time_zero'] = children['event_time_zero']
event_index = children['event_index']

# Weights are not stored in NeXus, so use 1s
Expand All @@ -150,7 +159,7 @@ def assemble(self, children: sc.DataGroup) -> sc.DataArray:
# different institutions. We try to make sure here that it is what would be the
# first index of the next pulse. In other words, ensure that event_index
# includes the bin edge for the last pulse.
if event_time_zero.ndim == 0:
if 'event_time_zero' in coords and coords['event_time_zero'].ndim == 0:
begins = event_index[_pulse_dimension, 0]
else:
begins = event_index
Expand All @@ -161,7 +170,7 @@ def assemble(self, children: sc.DataGroup) -> sc.DataArray:
path = self._children['event_index'].name
raise NexusStructureError(f"Invalid index in NXevent_data at {path}:\n{e}")

return sc.DataArray(data=binned, coords={'event_time_zero': event_time_zero})
return sc.DataArray(data=binned, coords=coords)


base_definitions_dict['NXevent_data'] = NXevent_data
35 changes: 35 additions & 0 deletions tests/nxdetector_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -929,3 +929,38 @@ def test_detector_with_event_data_and_unordered_event_time_zero_can_be_loaded(nx
},
)
assert_identical(pixel2, ref2)


def test_detector_with_event_data_and_no_event_time_zero_can_be_loaded(nxroot):
detector_numbers = sc.array(dims=[''], unit=None, values=np.array([1, 2]))
detector0 = nxroot.create_class('detector0', NXdetector)
detector0.create_field('detector_number', detector_numbers)
event_id = sc.array(dims=[''], unit=None, values=[1, 2, 1, 1, 2, 2])
event_time_offset = sc.array(dims=[''], unit='s', values=[11, 22, 33, 44, 55, 66])
event_data0 = detector0.create_class('events', snx.NXevent_data)
event_data0.create_field('event_id', event_id)
event_data0.create_field('event_time_offset', event_time_offset)
event_data0.create_field(
'event_index', sc.array(dims=[''], unit='None', values=[0, 3, 4, 5])
)
da = detector0[...]['events']
pixel1 = da.values[0]
ref1 = sc.DataArray(
data=sc.array(dims=['event'], values=[1, 1, 1], unit='counts', dtype='float32'),
coords={
'event_time_offset': sc.array(
dims=['event'], values=[11, 33, 44], unit='s'
),
},
)
assert_identical(pixel1, ref1)
pixel2 = da.values[1]
ref2 = sc.DataArray(
data=sc.array(dims=['event'], values=[1, 1, 1], unit='counts', dtype='float32'),
coords={
'event_time_offset': sc.array(
dims=['event'], values=[22, 55, 66], unit='s'
),
},
)
assert_identical(pixel2, ref2)
16 changes: 16 additions & 0 deletions tests/nxevent_data_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,22 @@ def test_event_data_without_event_id_can_be_loaded(nxroot):
assert 'event_time_offset' in da.bins.coords


def create_event_data_without_event_time_zero(group):
group['event_id'] = sc.array(dims=[''], unit=None, values=[1, 2, 4, 1, 2, 2])
group['event_time_offset'] = sc.array(
dims=[''], unit='s', values=[456, 7, 3, 345, 632, 23]
)
group['event_index'] = sc.array(dims=[''], unit=None, values=[0, 3, 3, 5])


def test_event_data_without_event_time_zero_can_be_loaded(nxroot):
event_data = nxroot['entry'].create_class('events_0', snx.NXevent_data)
create_event_data_without_event_time_zero(event_data)
da = event_data[...]
assert len(da.bins.coords) == 2
assert 'event_time_offset' in da.bins.coords


def test_event_mode_monitor_without_event_id_can_be_loaded(nxroot):
monitor = nxroot['entry'].create_class('monitor', snx.NXmonitor)
create_event_data_without_event_id(monitor)
Expand Down

0 comments on commit 86a4bfe

Please sign in to comment.