import numpy as np
import pytest
from ..tasks.regrid_data import RegridData
def _run_regrid(
translation_values,
rotation_angles,
integration_intensity_values,
translation_grid_params,
rotation_grid_params,
integration_radial_axis="2theta",
integration_radial_axis_values=None,
):
if integration_radial_axis_values is None:
integration_radial_axis_values = np.array([0.1])
task = RegridData(
inputs={
"translation_values": np.asarray(translation_values, dtype=float),
"translation_grid_params": translation_grid_params,
"rotation_angles": np.asarray(rotation_angles, dtype=float),
"rotation_grid_params": rotation_grid_params,
"integration_intensity_values": np.asarray(
integration_intensity_values, dtype=float
),
"integration_radial_axis": integration_radial_axis,
"integration_radial_axis_values": integration_radial_axis_values,
}
)
task.execute()
return task
[docs]
def test_compute_bin_edges_centered_multiple_bins():
"""Edges are offset by half the grid spacing so bins center on grid points."""
task = RegridData(
inputs={
"translation_values": np.array([0, 0, 0, 10, 10, 10]),
"translation_grid_params": (0, 10, 2),
"rotation_angles": np.array([0, 5, 10, 0, 5, 10]),
"rotation_grid_params": (0, 10, 3),
"integration_intensity_values": np.zeros((6, 1)),
"integration_radial_axis_values": np.array([0.1]),
}
)
edges = task.compute_bin_edges_centered(0, 10, 3)
np.testing.assert_allclose(edges, [-2.5, 2.5, 7.5, 12.5])
[docs]
def test_compute_bin_edges_centered_single_bin_raises():
"""A single-bin grid has no spacing to compute a bin from,
so it raises a ValueError."""
task = RegridData(
inputs={
"translation_values": np.array([0, 0, 10, 10]),
"translation_grid_params": (0, 10, 2),
"rotation_angles": np.array([0, 10, 0, 10]),
"rotation_grid_params": (0, 10, 2),
"integration_intensity_values": np.zeros((4, 1)),
"integration_radial_axis_values": np.array([0.1]),
}
)
with pytest.raises(ValueError, match="n_bins must be at least 2"):
task.compute_bin_edges_centered(5, 5, 1)
[docs]
def test_run_averages_points_that_fall_in_same_bin():
"""When two readings land in the same bin the regridded value is their average,
and a bin with no readings is zero rather than NaN."""
# n_trans=3, n_rot=2, but the (10, 0) bin got two readings and (20, 10) got none.
translation_values = [0, 0, 10, 10, 10, 20]
rotation_angles = [0, 10, 0, 0, 10, 0]
intensity = [[1], [2], [10], [20], [4], [5]]
task = _run_regrid(
translation_values=translation_values,
rotation_angles=rotation_angles,
integration_intensity_values=intensity,
translation_grid_params=(0, 20, 3),
rotation_grid_params=(0, 10, 2),
)
expected = np.array([[[1], [2]], [[15], [4]], [[5], [0]]])
np.testing.assert_allclose(task.outputs.integration_intensity_values, expected)
[docs]
def test_run_handles_descending_grid_params():
"""A grid given as (high, low, count) -- e.g. a motor scanning from a high
position down to a low one -- must regrid correctly and produce ascending
output grids, not silently drop every point."""
# A full raster following the physical scan direction: translation sweeps
# 20 -> 10 -> 0, and at each stop rotation sweeps 10 -> 0.
translation_values = [20, 20, 10, 10, 0, 0]
rotation_angles = [10, 0, 10, 0, 10, 0]
intensity = [[1], [2], [3], [4], [5], [6]]
task = _run_regrid(
translation_values=translation_values,
rotation_angles=rotation_angles,
integration_intensity_values=intensity,
translation_grid_params=(20, 0, 3), # descending
rotation_grid_params=(10, 0, 2), # descending
)
expected = np.array([[[6], [5]], [[4], [3]], [[2], [1]]])
np.testing.assert_allclose(task.outputs.integration_intensity_values, expected)
np.testing.assert_allclose(task.outputs.translation_values, [0, 10, 20])
np.testing.assert_allclose(task.outputs.rotation_angles, [0, 10])
[docs]
def test_run_drops_points_outside_grid_range():
"""A motor readback beyond the nominal scan range is excluded from the
regridded output instead of raising or corrupting a bin."""
translation_values = [0, 0, 10, 10, 20, 20]
rotation_angles = [0, 10, 0, 10, 0, 500] # last point overshoots
intensity = [[1], [2], [3], [4], [5], [999]]
task = _run_regrid(
translation_values=translation_values,
rotation_angles=rotation_angles,
integration_intensity_values=intensity,
translation_grid_params=(0, 20, 3),
rotation_grid_params=(0, 10, 2),
)
expected = np.array([[[1], [2]], [[3], [4]], [[5], [0]]])
np.testing.assert_allclose(task.outputs.integration_intensity_values, expected)
[docs]
def test_run_handles_integer_intensity_values():
"""Integer intensity input handled in regrid_histogram."""
task = RegridData(
inputs={
"translation_values": np.array([0, 0, 10, 10]),
"translation_grid_params": (0, 10, 2),
"rotation_angles": np.array([0, 0, 0, 0]),
"rotation_grid_params": (0, 10, 2),
"integration_intensity_values": np.array(
[[1], [2], [3], [4]], dtype=np.int32
),
"integration_radial_axis_values": np.array([0.1]),
}
)
task.execute()
expected = np.array([[[1.5], [0]], [[3.5], [0]]])
np.testing.assert_allclose(task.outputs.integration_intensity_values, expected)