Skip to content

Commit 0281ed9

Browse files
committed
Test and implement
1 parent a6c429e commit 0281ed9

File tree

2 files changed

+152
-2
lines changed

2 files changed

+152
-2
lines changed

src/ess/reduce/time_of_flight/resample.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,11 @@ def rebin_strictly_increasing(da: sc.DataArray, dim: str) -> sc.DataArray:
8585
return da[dim, slices[0]]
8686
if not slices:
8787
raise ValueError("No strictly increasing sections found.")
88-
sections = [da[dim, section] for section in slices]
88+
if da.coords[dim].dtype not in (sc.DType.float64, sc.DType.float32):
89+
# rebin does not like integer coords.
90+
da = da.assign_coords({dim: da.coords[dim].to(dtype='float64')})
91+
# Slices refer to the indices in the coord, which are bin edges. For slicing data
92+
# we need to stop at the last index minus one.
93+
sections = [da[dim, section.start : section.stop - 1] for section in slices]
8994
edges = make_regular_grid(da.coords[dim], dim=dim, slices=slices)
90-
return sc.concat([sc.rebin(section, {dim: edges}) for section in sections]).sum()
95+
return sc.reduce([sc.rebin(section, {dim: edges}) for section in sections]).sum()

tests/time_of_flight/resample_tests.py

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import numpy as np
55
import pytest
66
import scipp as sc
7+
from scipp.testing import assert_identical
78

89
from ess.reduce.time_of_flight import resample
910

@@ -255,3 +256,147 @@ def test_ensures_last_bin_edge_is_included(self):
255256
assert sc.identical(grid, expected)
256257
# Test that the maximum value from original data is included in the grid
257258
assert grid[-1].value == 5
259+
260+
261+
class TestRebinStrictlyIncreasing:
262+
"""Tests for rebin_strictly_increasing function."""
263+
264+
def test_basic_functionality(self):
265+
# Create a data array with a simple time-of-flight coordinate that has two
266+
# strictly increasing sections
267+
tof = sc.array(dims=['tof'], values=[1, 2, 3, 2, 3, 4, 5])
268+
data = sc.array(dims=['tof'], values=[10, 20, 15, 25, 35, 11])
269+
da = sc.DataArray(data=data, coords={'tof': tof})
270+
271+
result = resample.rebin_strictly_increasing(da, 'tof')
272+
273+
# Check the rebinned result has a regular grid from 1 to 5
274+
expected_tof = sc.array(dims=['tof'], values=[1.0, 2, 3, 4, 5])
275+
assert sc.identical(result.coords['tof'], expected_tof)
276+
277+
# Check the data values are properly rebinned and combined
278+
expected_data = sc.array(dims=['tof'], values=[10.0, 20 + 25, 35, 11])
279+
assert_identical(result.data, expected_data)
280+
281+
def test_with_different_step_sizes(self):
282+
# First section has step size 1, second has step size 0.5
283+
tof = sc.array(dims=['tof'], values=[1, 2, 4, 3.5, 4, 4.5, 5])
284+
data = sc.array(dims=['tof'], values=[10, 20, 15, 25, 35, 45])
285+
da = sc.DataArray(data=data, coords={'tof': tof})
286+
287+
result = resample.rebin_strictly_increasing(da, 'tof')
288+
289+
# Should use step size 1 from the first section (where min value is found)
290+
expected_tof = sc.array(dims=['tof'], values=[1.0, 2, 3, 4, 5])
291+
assert_identical(result.coords['tof'], expected_tof)
292+
293+
def test_with_units(self):
294+
tof = sc.array(dims=['tof'], values=[1.0, 2.0, 3.0, 2.0, 3.0, 4.0], unit='ms')
295+
data = sc.array(dims=['tof'], values=[10, 20, 15, 25, 35], unit='counts')
296+
da = sc.DataArray(data=data, coords={'tof': tof})
297+
298+
result = resample.rebin_strictly_increasing(da, 'tof')
299+
300+
# Check units are preserved
301+
assert result.coords['tof'].unit == sc.Unit('ms')
302+
assert result.data.unit == sc.Unit('counts')
303+
304+
# Check values
305+
expected_tof = sc.array(dims=['tof'], values=[1.0, 2.0, 3.0, 4.0], unit='ms')
306+
assert sc.identical(result.coords['tof'], expected_tof)
307+
308+
def test_with_single_increasing_section(self):
309+
tof = sc.array(dims=['tof'], values=[1, 2, 3, 4, 5, 6])
310+
data = sc.array(dims=['tof'], values=[10, 20, 30, 40, 50])
311+
da = sc.DataArray(data=data, coords={'tof': tof})
312+
313+
result = resample.rebin_strictly_increasing(da, 'tof')
314+
315+
# For a single increasing section, should return just that section
316+
assert sc.identical(result, da)
317+
318+
def test_with_three_increasing_sections(self):
319+
tof = sc.array(dims=['tof'], values=[1, 2, 3, 1, 2, 3, 4, 2, 3, 4, 5])
320+
data = sc.array(dims=['tof'], values=[5, 10, 6, 12, 18, 8, 14, 21, 28, 35])
321+
da = sc.DataArray(data=data, coords={'tof': tof})
322+
323+
result = resample.rebin_strictly_increasing(da, 'tof')
324+
325+
expected_tof = sc.array(dims=['tof'], values=[1.0, 2, 3, 4, 5])
326+
assert_identical(result.coords['tof'], expected_tof)
327+
328+
# Sum of all three sections properly rebinned
329+
expected_data = sc.array(
330+
dims=['tof'], values=[5.0 + 12, 10 + 18 + 21, 8 + 28, 35]
331+
)
332+
assert_identical(result.data, expected_data)
333+
334+
def test_with_nan_values_in_coordinate(self):
335+
tof = sc.array(dims=['tof'], values=[1, 2, 3, np.nan, 5, 6, 7])
336+
data = sc.array(dims=['tof'], values=[10, 20, 40, 50, 60, 70])
337+
da = sc.DataArray(data=data, coords={'tof': tof})
338+
339+
result = resample.rebin_strictly_increasing(da, 'tof')
340+
341+
# Should have two increasing sections: [1,2,3] and [5,6,7]
342+
expected_tof = sc.array(dims=['tof'], values=[1.0, 2, 3, 4, 5, 6, 7])
343+
assert_identical(result.coords['tof'], expected_tof)
344+
345+
# Data should be correctly rebinned, excluding the NaN point
346+
expected_data = sc.array(dims=['tof'], values=[10.0, 20, 0, 0, 60, 70])
347+
assert_identical(result.data, expected_data)
348+
349+
def test_with_no_increasing_sections_raises_error(self):
350+
tof = sc.array(dims=['tof'], values=[5, 4, 3, 2, 1, 0])
351+
data = sc.array(dims=['tof'], values=[10, 20, 30, 40, 50])
352+
da = sc.DataArray(data=data, coords={'tof': tof})
353+
354+
with pytest.raises(ValueError, match="No strictly increasing sections found."):
355+
resample.rebin_strictly_increasing(da, 'tof')
356+
357+
def test_with_variances(self):
358+
tof = sc.array(dims=['tof'], values=[1, 2, 3, 2, 3, 4])
359+
values = [
360+
10.0,
361+
20.0,
362+
15.0,
363+
25.0,
364+
35.0,
365+
] # Using float for values to match variances
366+
variances = [1.0, 2.0, 1.5, 2.5, 3.5] # Float variances
367+
data = sc.array(dims=['tof'], values=values, variances=variances)
368+
da = sc.DataArray(data=data, coords={'tof': tof})
369+
370+
result = resample.rebin_strictly_increasing(da, 'tof')
371+
372+
# Check that variances are properly propagated
373+
assert result.data.variances is not None
374+
expected_variances = sc.array(dims=['tof'], values=[1.0, 2.0 + 2.5, 3.5])
375+
assert_identical(sc.variances(result.data), expected_variances)
376+
377+
def test_additional_coords_are_dropped(self):
378+
tof = sc.array(dims=['tof'], values=[1, 2, 3, 2, 3, 4, 5])
379+
data = sc.array(dims=['tof'], values=[10, 20, 15, 25, 35, 45])
380+
energy = sc.array(dims=['tof'], values=[1.1, 1.2, 1.3, 1.2, 1.4, 1.2])
381+
da = sc.DataArray(data=data, coords={'tof': tof, 'energy': energy})
382+
383+
result = resample.rebin_strictly_increasing(da, 'tof')
384+
385+
# Rebin cannot preserve coords
386+
assert 'energy' not in result.coords
387+
388+
def test_masks_are_applied(self):
389+
tof = sc.array(dims=['tof'], values=[1, 2, 3, 2, 3, 4, 5])
390+
data = sc.array(dims=['tof'], values=[10, 20, 15, 25, 35, 45])
391+
da = sc.DataArray(data=data, coords={'tof': tof})
392+
393+
baseline = resample.rebin_strictly_increasing(da, 'tof')
394+
395+
# Add a mask
396+
mask = sc.array(dims=['tof'], values=[False, False, True, False, False, True])
397+
da.masks['quality'] = mask
398+
399+
result = resample.rebin_strictly_increasing(da, 'tof')
400+
# Rebin applies masks
401+
assert 'quality' not in baseline.masks
402+
assert result.sum().value < baseline.sum().value

0 commit comments

Comments
 (0)