Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions requirements.conda.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
drmaa
hdf5plugin
ispyb>=11.0.1
junit-xml>=1.9
marshmallow-sqlalchemy
Expand Down
14 changes: 13 additions & 1 deletion src/dlstbx/services/xray_centering.py
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,19 @@ def garbage_collect(self):
del self._centering_data[dcid]

def add_pia_result(self, rw, header, message):
"""Process incoming PIA result."""
"""Process incoming PIA result.

This is invoked repeatedly as each image in the gridscan comes through.

Recipe Step Inputs:
gridinfo: This is the ISPyB DataCollectionGridInfo for the DataCollection
parameters: Other parameters in the recipe - see the relevant per-image-analysis-xxx.json
Args:
rw: an instance of RecipeWrapper
header: The message header
message: A dict which decodes to an instance of Message

"""

try:
recipe_step = RecipeStep(**rw.recipe_step)
Expand Down
5 changes: 5 additions & 0 deletions src/dlstbx/util/xray_centering.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,11 @@ class GridScan2DResult(GridScanResultBase):
def reshape_grid(
data: np.ndarray, steps: tuple[int, int], snaked: bool, orientation: Orientation
) -> np.ndarray:
"""
Converts the linear array to column major 2D, and unsnake.
NOTE, the current implementation deliberately mutates the input!
"""
# Transpose the array if orientation is horizontal
if orientation == Orientation.VERTICAL:
data = data.reshape(steps)
else:
Expand Down
18 changes: 18 additions & 0 deletions src/dlstbx/util/xray_centering_3d.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,24 @@


class GridScan3DResult(GridScanResultBase):
"""
Represents a single gridscan result, corresponding to a diffracting centre.

Coordinates expressed are in terms of grid boxes, with the centre of the box
lying on half-integer coordinates.

Attributes:
centre_of_mass: The position of the centre of mass of the crystal, for a
crystal of size (1, 1, 1) this will be on half-integer coordinates
max_voxel: Position of the maximum voxel, on integer coordinates!!!
max_count: max count achieved in a single voxel for the crystal
n_voxels: Number of voxels in the diffracting centre
total_count: Total of above-threshold spot counts in the labelled voxels
bounding_box: The rectangular prism that bounds the crystal, expressed
as the volume of whole boxes as a half-open range i.e such that
p1 = (x1, y1, z1) <= p < p2 = (x2, y2, z2) and
p2 - p1 gives the dimensions in whole voxels.
"""
centre_of_mass: Tuple[float, ...]
max_voxel: Tuple[int, ...]
max_count: float
Expand Down
60 changes: 60 additions & 0 deletions tests/util/test_xray_centering.py
Original file line number Diff line number Diff line change
Expand Up @@ -315,3 +315,63 @@ def test_single_connected_region(data, reflections_in_best_image):
)
assert result.centre_x == result.centre_x_box == 5
assert result.centre_y == result.centre_y_box == 5


EXPECTED_OUTPUT_COL_MAJOR = (np.array([
[1, 5, 9],
[2, 6, 10],
[3, 7, 11],
[4, 8, 12]
]))

GRID_INPUT_ROW_MAJOR_SNAKED_L_TO_R_FIRST = (np.array([
1, 2, 3, 4,
8, 7, 6, 5,
9, 10, 11, 12,
]))
GRID_INPUT_ROW_MAJOR_SNAKED_R_TO_L_FIRST = (np.array([
4, 3, 2, 1,
5, 6, 7, 8,
12, 11, 10, 9,
]))
GRID_INPUT_ROW_MAJOR_NOT_SNAKED_L_TO_R = (np.array([
1, 2, 3, 4,
5, 6, 7, 8,
9, 10, 11, 12,
]))
GRID_INPUT_COL_MAJOR_NOT_SNAKED = (np.array([
1, 5, 9,
2, 6, 10,
3, 7, 11,
4, 8, 12
]))
GRID_INPUT_COL_MAJOR_SNAKED_T_TO_B_FIRST = (np.array([
1, 5, 9,
10, 6, 2,
3, 7, 11,
12, 8, 4
]))

@pytest.mark.parametrize("data_in, expected_data, steps, snaked, orientation",
[
[GRID_INPUT_ROW_MAJOR_SNAKED_L_TO_R_FIRST, EXPECTED_OUTPUT_COL_MAJOR, (4, 3), True, dlstbx.util.xray_centering.Orientation.HORIZONTAL],
[GRID_INPUT_ROW_MAJOR_NOT_SNAKED_L_TO_R, EXPECTED_OUTPUT_COL_MAJOR, (4, 3), False,
dlstbx.util.xray_centering.Orientation.HORIZONTAL],
# -ve direction first snaking causes the axis to be flipped,
# is there anything that uses/relies on this?
# [GRID_INPUT_ROW_MAJOR_SNAKED_R_TO_L_FIRST, EXPECTED_OUTPUT_COL_MAJOR, (4, 3), True,
# dlstbx.util.xray_centering.Orientation.HORIZONTAL],
[GRID_INPUT_COL_MAJOR_NOT_SNAKED, EXPECTED_OUTPUT_COL_MAJOR, (4, 3), False,
dlstbx.util.xray_centering.Orientation.VERTICAL],
[GRID_INPUT_COL_MAJOR_SNAKED_T_TO_B_FIRST, EXPECTED_OUTPUT_COL_MAJOR, (4, 3), True,
dlstbx.util.xray_centering.Orientation.VERTICAL],
]
)
def test_reshape_grid(
data_in, expected_data, steps, snaked, orientation
):
# old_data_in = data_in.copy()
data_out = dlstbx.util.xray_centering.reshape_grid(data_in, steps, snaked=snaked, orientation=orientation)
assert np.all(data_out == expected_data), f"{data_out} != {expected_data}"
# The current operation of the gridscan processing relies on this mutation of the input
# assert np.all(data_in == old_data_in), f"{data_in} != {old_data_in}"
33 changes: 33 additions & 0 deletions tests/util/test_xray_centering_3d.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,36 @@ def test_gridscan3d():
"total_count": 44128.0,
"bounding_box": ((2, 3, 2), (7, 6, 6)),
}

# fmt: off
@pytest.mark.parametrize("input_data, expected_results",
[
[ # NB x is down
( # xy
np.array([[0, 0, 0],
[0, 0, 0],
[0, 1, 0],
[0, 0, 0],]),
# xz
np.array([[0, 0, 0],
[0, 0, 0],
[1, 0, 0],
[0, 0, 0]])
),
[dlstbx.util.xray_centering_3d.GridScan3DResult(
centre_of_mass=(2.5, 1.5, 0.5),
max_voxel=(2.5, 1.5, 0.5),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test fails raising pydantic error as max_voxel parameter is declared in GridScan3DResult class as max_voxel: Tuple[int, ...].

max_count=1,
n_voxels=1,
total_count=1,
bounding_box=((2, 1, 0), (3, 2, 1))
)],
],
])
# fmt: on
def test_gridscan_3d_coordinates(input_data,
expected_results):
results = dlstbx.util.xray_centering_3d.gridscan3d(
input_data
)
assert all([r == e for r, e in zip(results, expected_results, strict=True)]), f"{results} != {expected_results}"