diff --git a/src/dodal/devices/fast_grid_scan.py b/src/dodal/devices/fast_grid_scan.py index 9a6c9d3684..a6e942a598 100644 --- a/src/dodal/devices/fast_grid_scan.py +++ b/src/dodal/devices/fast_grid_scan.py @@ -45,8 +45,19 @@ def end(self): # refering to the first position return self.steps_to_motor_position(self.full_steps - 1) - def is_within(self, steps): - return 0 <= steps <= self.full_steps + def is_within(self, steps: float): + """ + Determine whether a single axis coordinate is within the grid. + The coordinate is from a continuous coordinate space based on the + XRC grid where the origin corresponds to the centre of the first grid box. + + Args: + steps: The coordinate to check + + Returns: + True if the coordinate falls within the grid. + """ + return -0.5 <= steps <= self.full_steps - 0.5 class GridScanParamsCommon(AbstractExperimentWithBeamParams): diff --git a/tests/devices/unit_tests/test_gridscan.py b/tests/devices/unit_tests/test_gridscan.py index 87063d42c0..900ea758c6 100644 --- a/tests/devices/unit_tests/test_gridscan.py +++ b/tests/devices/unit_tests/test_gridscan.py @@ -1,4 +1,5 @@ from asyncio import TimeoutError, wait_for +from contextlib import nullcontext from dataclasses import dataclass import numpy as np @@ -12,6 +13,7 @@ from dodal.devices.fast_grid_scan import ( FastGridScanCommon, + GridScanParamsCommon, PandAFastGridScan, PandAGridScanParams, ZebraFastGridScan, @@ -246,63 +248,62 @@ def panda_grid_scan_params(): ) -@pytest.mark.parametrize( - "grid_position", - [ - (np.array([-1, 2, 4])), - (np.array([11, 2, 4])), - (np.array([1, 17, 4])), - (np.array([1, 5, 22])), - ], -) -def test_given_x_y_z_out_of_range_then_converting_to_motor_coords_raises( - zebra_grid_scan_params: ZebraGridScanParams, - panda_grid_scan_params: PandAGridScanParams, - grid_position, -): - with pytest.raises(IndexError): - zebra_grid_scan_params.grid_position_to_motor_position(grid_position) - with pytest.raises(IndexError): - panda_grid_scan_params.grid_position_to_motor_position(grid_position) - - -def test_given_x_y_z_of_origin_when_get_motor_positions_then_initial_positions_returned( - zebra_grid_scan_params: ZebraGridScanParams, - panda_grid_scan_params: PandAGridScanParams, -): - motor_positions = [ - zebra_grid_scan_params.grid_position_to_motor_position(np.array([0, 0, 0])), - panda_grid_scan_params.grid_position_to_motor_position(np.array([0, 0, 0])), - ] - assert [np.allclose(position, np.array([0, 1, 4])) for position in motor_positions] +@pytest.fixture(params=["zebra_grid_scan_params", "panda_grid_scan_params"]) +def common_grid_scan_params(request): + return request.getfixturevalue(request.param) @pytest.mark.parametrize( - "grid_position, expected_x, expected_y, expected_z", + "grid_position, expected", [ - (np.array([1, 1, 1]), 0.3, 1.2, 4.1), - (np.array([2, 11, 16]), 0.6, 3.2, 5.6), - (np.array([6, 5, 5]), 1.8, 2.0, 4.5), + [np.array([-1, 2, 4]), pytest.raises(IndexError)], + [np.array([11, 2, 4]), pytest.raises(IndexError)], + [np.array([1, 17, 4]), pytest.raises(IndexError)], + [np.array([1, 5, 22]), pytest.raises(IndexError)], + [np.array([0, 0, 0]), nullcontext(np.array([0, 1, 4]))], + [np.array([1, 1, 1]), nullcontext(np.array([0.3, 1.2, 4.1]))], + [np.array([2, 11, 16]), nullcontext(np.array([0.6, 3.2, 5.6]))], + [np.array([6, 5, 5]), nullcontext(np.array([1.8, 2.0, 4.5]))], + [np.array([-0.51, 5, 5]), pytest.raises(IndexError)], + [ + np.array([-0.5, 5, 5]), + nullcontext(np.array([-0.5 * 0.3, 1 + 5 * 0.2, 4 + 5 * 0.1])), + ], + [np.array([5, -0.51, 5]), pytest.raises(IndexError)], + [ + np.array([5, -0.5, 5]), + nullcontext(np.array([5 * 0.3, 1 - 0.5 * 0.2, 4 + 5 * 0.1])), + ], + [np.array([5, 5, -0.51]), pytest.raises(IndexError)], + [ + np.array([5, 5, -0.5]), + nullcontext(np.array([5 * 0.3, 1 + 5 * 0.2, 4 - 0.5 * 0.1])), + ], + [np.array([9.51, 5, 5]), pytest.raises(IndexError)], + [ + np.array([9.5, 5, 5]), + nullcontext(np.array([9.5 * 0.3, 1 + 5 * 0.2, 4 + 5 * 0.1])), + ], + [np.array([5, 14.51, 5]), pytest.raises(IndexError)], + [ + np.array([5, 14.5, 5]), + nullcontext(np.array([5 * 0.3, 1 + 14.5 * 0.2, 4 + 5 * 0.1])), + ], + [np.array([5, 5, 19.51]), pytest.raises(IndexError)], + [ + np.array([5, 5, 19.5]), + nullcontext(np.array([5 * 0.3, 1 + 5 * 0.2, 4 + 19.5 * 0.1])), + ], ], ) -def test_given_various_x_y_z_when_get_motor_positions_then_expected_positions_returned( - zebra_grid_scan_params: ZebraGridScanParams, - panda_grid_scan_params: PandAGridScanParams, - grid_position, - expected_x, - expected_y, - expected_z, +def test_given_x_y_z_out_of_range_then_converting_to_motor_coords_raises( + common_grid_scan_params: GridScanParamsCommon, grid_position, expected ): - motor_positions = [ - zebra_grid_scan_params.grid_position_to_motor_position(grid_position), - panda_grid_scan_params.grid_position_to_motor_position(grid_position), - ] - [ - np.testing.assert_allclose( - position, np.array([expected_x, expected_y, expected_z]) + with expected as expected_value: + motor_position = common_grid_scan_params.grid_position_to_motor_position( + grid_position ) - for position in motor_positions - ] + assert np.allclose(motor_position, expected_value) @pytest.mark.parametrize( @@ -333,11 +334,9 @@ def kickoff_and_complete(device: FastGridScanCommon): def test_given_x_y_z_steps_when_full_number_calculated_then_answer_is_as_expected( - zebra_grid_scan_params: ZebraGridScanParams, - panda_grid_scan_params: PandAGridScanParams, + common_grid_scan_params: GridScanParamsCommon, ): - assert zebra_grid_scan_params.get_num_images() == 350 - assert panda_grid_scan_params.get_num_images() == 350 + assert common_grid_scan_params.get_num_images() == 350 @pytest.mark.parametrize(