Skip to content

Commit 5789298

Browse files
authored
Merge pull request #172 from scipp/to-events-fix
Fix `to_events` when bins in the input have unsorted edges
2 parents d419d36 + 3553952 commit 5789298

File tree

3 files changed

+29
-10
lines changed

3 files changed

+29
-10
lines changed

src/ess/reduce/time_of_flight/__init__.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,9 @@
66
neutron time-of-arrival at the detectors.
77
"""
88

9-
from .toa_to_tof import default_parameters, resample_tof_data, providers, TofWorkflow
109
from .simulation import simulate_beamline
10+
from .toa_to_tof import default_parameters, resample_tof_data, providers, TofWorkflow
11+
from .to_events import to_events
1112
from .types import (
1213
DistanceResolution,
1314
FrameFoldedTimeOfArrival,
@@ -56,4 +57,5 @@
5657
"providers",
5758
"resample_tof_data",
5859
"simulate_beamline",
60+
"to_events",
5961
]

src/ess/reduce/time_of_flight/to_events.py

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -45,19 +45,21 @@ def to_events(
4545
edge_sizes = {dim: da.sizes[dim] for dim in edge_dims}
4646
for dim in edge_dims:
4747
coord = da.coords[dim]
48-
low = sc.broadcast(coord[dim, :-1], sizes=edge_sizes).values
49-
high = sc.broadcast(coord[dim, 1:], sizes=edge_sizes).values
48+
left = sc.broadcast(coord[dim, :-1], sizes=edge_sizes).values
49+
right = sc.broadcast(coord[dim, 1:], sizes=edge_sizes).values
5050

5151
# The numpy.random.uniform function below does not support NaNs, so we need to
5252
# replace them with zeros, and then replace them back after the random numbers
5353
# have been generated.
54-
nans = np.isnan(low) | np.isnan(high)
55-
low = np.where(nans, 0.0, low)
56-
high = np.where(nans, 0.0, high)
54+
nans = np.isnan(left) | np.isnan(right)
55+
left = np.where(nans, 0.0, left)
56+
right = np.where(nans, 0.0, right)
57+
# Ensure left <= right
58+
left, right = np.minimum(left, right), np.maximum(left, right)
5759

5860
# In each bin, we generate a number of events with a uniform distribution.
5961
events = rng.uniform(
60-
low, high, size=(events_per_bin, *list(edge_sizes.values()))
62+
left, right, size=(events_per_bin, *list(edge_sizes.values()))
6163
)
6264
events[..., nans] = np.nan
6365
event_coords[dim] = sc.array(
@@ -77,20 +79,20 @@ def to_events(
7779
data = da.data
7880
if event_masks:
7981
inv_mask = (~reduce(lambda a, b: a | b, event_masks.values())).to(dtype=int)
80-
inv_mask.unit = ''
82+
inv_mask.unit = ""
8183
data = data * inv_mask
8284

8385
# Create the data counts, which are the original counts divided by the number of
8486
# events per bin
8587
sizes = {event_dim: events_per_bin} | da.sizes
8688
val = sc.broadcast(sc.values(data) / float(events_per_bin), sizes=sizes)
87-
kwargs = {'dims': sizes.keys(), 'values': val.values, 'unit': data.unit}
89+
kwargs = {"dims": sizes.keys(), "values": val.values, "unit": data.unit}
8890
if data.variances is not None:
8991
# Note here that all the events are correlated.
9092
# If we later histogram the events with different edges than the original
9193
# histogram, then neighboring bins will be correlated, and the error obtained
9294
# will be too small. It is however not clear what can be done to improve this.
93-
kwargs['variances'] = sc.broadcast(
95+
kwargs["variances"] = sc.broadcast(
9496
sc.variances(data) / float(events_per_bin), sizes=sizes
9597
).values
9698
new_data = sc.array(**kwargs)

tests/time_of_flight/to_events_test.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,3 +109,18 @@ def test_to_events_two_masks():
109109
assert "m1" not in result.masks
110110
assert sc.identical(hist.masks["m2"], result.masks["m2"])
111111
assert result["x", 2:4].data.sum() == sc.scalar(0.0, unit=table.unit)
112+
113+
114+
def test_to_events_1d_unsorted_bin_edges():
115+
table = sc.data.table_xyz(1000)
116+
hist = table.hist(x=10)
117+
hist.coords["x"].values = hist.coords["x"].values[
118+
[0, 1, 2, 3, 5, 4, 6, 7, 8, 9, 10]
119+
]
120+
events = to_events(hist, "event")
121+
assert "x" not in events.dims
122+
result = events.hist(x=sc.sort(hist.coords["x"], "x"))
123+
assert sc.allclose(hist.data[:3], result.data[:3])
124+
assert sc.allclose(hist.data[6:], result.data[6:])
125+
# The data in the middle gets re-ordered, but the sum should still be the same
126+
assert sc.isclose(hist.data[3:6].sum(), result.data[3:6].sum())

0 commit comments

Comments
 (0)