From 2969af8764b74d03a9da444aebc71b9d2d11a778 Mon Sep 17 00:00:00 2001 From: Dawn Date: Tue, 28 Jul 2026 13:59:38 +0200 Subject: [PATCH] fix: drop spots_indexed from no-crystal guard Indexing was removed from the crystal score (w_indexed=0.00), so it must not act as a hard veto either: a crystal that diffracts but fails to index should still be targeted, not routed to grid centre. Guard now checks max spots_low_res only. Co-Authored-By: Claude Fable 5 --- src/aarecommon/math/find_xtal.py | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/src/aarecommon/math/find_xtal.py b/src/aarecommon/math/find_xtal.py index 848946f..e78f354 100644 --- a/src/aarecommon/math/find_xtal.py +++ b/src/aarecommon/math/find_xtal.py @@ -323,24 +323,24 @@ def raster_centre_of_mass(result_array, images) -> CenterOfMassModel | None: return _to_com_model(com, images, "Center of mass") -def raster_highest_score( - images, min_low_res_spots: float = 10.0, min_spots_indexed: int = 1 -) -> CenterOfMassModel | None: +def raster_highest_score(images, min_low_res_spots: float = 10.0) -> CenterOfMassModel | None: """Target the grid cell with the highest crystal score. If the whole grid is too weak to hold a crystal — max spots_low_res below - min_low_res_spots OR max spots_indexed below min_spots_indexed — target the - geometric centre of the grid instead of collecting at a noisy cell, so a - 'nothing here' result is centred and deliberate rather than random noise. + min_low_res_spots — target the geometric centre of the grid instead of + collecting at a noisy cell, so a 'nothing here' result is centred and + deliberate rather than random noise. + + spots_indexed is deliberately not part of the guard: indexing was dropped + from the crystal score (w_indexed=0.00), so a crystal that diffracts but + fails to index must still be targeted, not routed to centre. """ score_array = compute_crystal_score_array(images) max_low_res = max((getattr(img, "spots_low_res", 0) or 0 for img in images), default=0) - max_indexed = max((getattr(img, "spots_indexed", 0) or 0 for img in images), default=0) - if max_low_res < min_low_res_spots or max_indexed < min_spots_indexed: + if max_low_res < min_low_res_spots: n_nx, n_ny = score_array.shape logger.info( - f"No crystal in loop (max spots_low_res={max_low_res} < {min_low_res_spots} " - f"or max spots_indexed={max_indexed} < {min_spots_indexed}); " + f"No crystal in loop (max spots_low_res={max_low_res} < {min_low_res_spots}); " f"targeting grid centre of {n_nx}x{n_ny} grid" ) # get_com_mm maps index i -> (i+0.5)*step, so index (N-1)/2 is the true @@ -689,9 +689,12 @@ if __name__ == "__main__": com = raster_highest_score(noise) assert (com.n_x, com.n_y) == (1.0, 1.0), com - # Strong low-res but nothing indexed anywhere -> OR rule routes to centre. - no_index = [_cell(x, y, 40, 0) for x in range(3) for y in range(3)] + # Strong low-res but nothing indexed anywhere -> still targets the best + # cell: indexing is dropped from the score (w_indexed=0.00) and must not + # veto a diffracting crystal. + no_index = [_cell(x, y, 12, 0) for x in range(3) for y in range(3)] + no_index[6] = _cell(2, 0, 40, 0) com = raster_highest_score(no_index) - assert (com.n_x, com.n_y) == (1.0, 1.0), com + assert (round(com.n_x), round(com.n_y)) == (2, 0), com print("find_xtal self-check passed")