Skip to content
Merged
Show file tree
Hide file tree
Changes from 45 commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
a94ab98
WIP begin making callbacks generic
olliesilvester Feb 9, 2026
5a2c51f
wip
olliesilvester Feb 11, 2026
0a84a71
wip
olliesilvester Feb 11, 2026
40fa5c4
wip
olliesilvester Feb 11, 2026
0ff8cc8
Merge remote-tracking branch 'origin/main' into 364_make_nexus_callba…
olliesilvester Feb 12, 2026
0fa10d9
Some fixes
olliesilvester Feb 12, 2026
757ac75
Partially fix nexus tests
olliesilvester Feb 12, 2026
145a648
Fix nexus tests
olliesilvester Feb 16, 2026
e63677e
fix test
olliesilvester Feb 16, 2026
c2a232d
fix typing
olliesilvester Feb 16, 2026
860562b
Address some todos and add validator
olliesilvester Feb 16, 2026
0070405
Merge branch 'main' into 364_make_nexus_callbacks_generic
olliesilvester Feb 17, 2026
ab8fd89
Merge branch 'main' into 364_make_nexus_callbacks_generic
olliesilvester Feb 18, 2026
7c037bb
Fixes from merge
olliesilvester Feb 18, 2026
bf9449f
Add test
olliesilvester Feb 18, 2026
16885da
Remove SingleGrid class
olliesilvester Feb 18, 2026
d856a8c
don't implement dummy mode xrc results for 2d grids
olliesilvester Feb 18, 2026
a31185d
Link to issue in comments
olliesilvester Feb 18, 2026
20d1d60
Merge remote-tracking branch 'origin/main' into 364_make_nexus_callba…
olliesilvester Mar 2, 2026
668c8e0
Keep up to date
olliesilvester Mar 2, 2026
0e68e2b
Add vmxm FGS entry point
olliesilvester Feb 18, 2026
945cb4c
Fixes and tests
olliesilvester Feb 18, 2026
4a93d73
Typo
olliesilvester Feb 18, 2026
6198594
make codecov happy
olliesilvester Feb 18, 2026
638a8c1
improve tests
olliesilvester Feb 19, 2026
49ffcd9
wip
olliesilvester Feb 19, 2026
c212134
wip
olliesilvester Feb 23, 2026
eb47c55
wip
olliesilvester Feb 25, 2026
f41d162
Make common ispyb callback for gridscans with no grid detect
olliesilvester Feb 27, 2026
5a0d9f3
Fixes
olliesilvester Feb 27, 2026
5e8af69
Add test stubs
olliesilvester Feb 27, 2026
6441918
Add fixes and tests
olliesilvester Mar 2, 2026
55e3517
Remove comment
olliesilvester Mar 2, 2026
0293b71
Fix tests
olliesilvester Mar 3, 2026
3d52fce
Fix device types and params
olliesilvester Mar 6, 2026
2a3e3f2
Add blueapi entry point and fix ispyb slits read
olliesilvester Mar 10, 2026
f76dbc0
Extend gda param model to include box size and omega start
olliesilvester Mar 10, 2026
fd121e9
Use SAD experiment type
olliesilvester Mar 10, 2026
54d8aaa
Use correct vmxm detector type
olliesilvester Mar 10, 2026
0e03d9b
Merge remote-tracking branch 'origin/main' into 364_make_nexus_callba…
rtuck99 Apr 23, 2026
f388814
Merge remote-tracking branch 'origin/main' into 364_make_nexus_callba…
rtuck99 May 5, 2026
28cff71
Update uv.lock
rtuck99 May 5, 2026
176d60b
Tidy gridscan plantUML
rtuck99 May 5, 2026
f36a3f3
Merge branch '364_make_nexus_callbacks_generic' into add_vmxm_gridsca…
rtuck99 May 5, 2026
07a0d7a
Merge branch 'add_vmxm_gridscan_plan' into ispyb_integration_vmxm_gri…
rtuck99 May 5, 2026
2e37ac2
Update dodal dependency to require branch
rtuck99 May 6, 2026
9d0c3d9
Merge remote-tracking branch 'origin/main' into ispyb_integration_vmx…
rtuck99 May 13, 2026
a300a43
Fixup use of typevars for the gonio
rtuck99 May 14, 2026
e0b9a12
Repin dodal
rtuck99 May 14, 2026
6f61bc7
Update uv.lock
rtuck99 May 14, 2026
d0b499b
Override the default grid box size for Vmxm
rtuck99 May 14, 2026
529d0f0
unpin dodal
rtuck99 May 15, 2026
9f367c0
Update uv.lock
rtuck99 May 15, 2026
a9bbe0f
Fix i02-1 gridscan unit test
rtuck99 May 15, 2026
31b6037
Merge branch 'main' into ispyb_integration_vmxm_grid_scan
rtuck99 May 15, 2026
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
160 changes: 88 additions & 72 deletions docs/developer/hyperion/reference/gridscan.puml
Original file line number Diff line number Diff line change
@@ -1,38 +1,70 @@
@startuml
title Gridscan Parameter Relationships

class DiffractionExperiment
class DiffractionExperimentWithSample
class GridCommon {
grid_width_um
exposure_time_s
package dodal {
class AbstractExperimentParameterBase
class AbstractExperimentWithBeamParams {
transmission_fraction
}
class GridScanParamsCommon {
x_steps
y_steps
x_step_size_mm
y_step_size_mm
x_start_mm
y1_start_mm
z1_start_mm
set_stub_offsets
}
class GridScanParamsThreeD
class PandAGridScanParams
class ZebraGridScanParamsThreeD
class ZebraGridScanParamsTwoD
}
class GridScanWithEdgeDetect {
box_size_um
}
class HyperionGridCommon {
enable_dev_shm

rectangle "Internal Experiment Parameter Model" {
class MxBlueSkyParameters
class DiffractionExperiment
class DiffractionExperimentWithSample
class GenericGrid {
grid_width_um
exposure_time_s
}
class GenericGridWithHyperionDetectorParams
class GridScanWithEdgeDetect {
box_size_um
}
class PinTipCentreThenXrayCentre
class RobotLoadThenCentre
}
class HyperionThreeDGridScan {
x_step_size_um
y_step_size_um
z_step_size_um
y2_start_um
z2_start_um
--
grid_1_spec
grid_2_spec
scan_indices
scan_spec
scan_points
scan_points_first_grid
scan_points_second_grid
num_images
FGS_Params
panda_FGS_Params



rectangle "Specified Grids" {
class SpecifiedGrids<GenericParamType> {
omega_starts_deg: float[]
x_step_size_um
y_step_sizes_um
x_steps
y_steps: int[]
--
fast_grid_scan_params: GenericParamType
grid_specs
scan_indices
scan_points
scan_spec
num_images
}
class "SpecifiedGrids<ZebraGridScanParamsThreeD>" as SpecifiedGridsZebraGridScanParamsThreeD
class "SpecifiedGrids<ZebraGridScanParamsTwoD>" as SpecifiedGridsZebraGridScanParamsTwoD
class SpecifiedTwoDGridScan
class SpecifiedThreeDGridScan
class HyperionSpecifiedThreeDGridScan {
--
panda_fast_gridscan_params: PandAGridScanParams
}
}
class MxBlueSkyParameters
class SpecifiedGrid

class XyzStarts {
x_start_um
y_start_um
Expand All @@ -43,67 +75,51 @@ class OptionalXYZStarts {
y_start_um
z_start_um
}
class RotationScanPerSweep

MxBlueSkyParameters <|-- DiffractionExperiment
DiffractionExperiment <|-- DiffractionExperimentWithSample
DiffractionExperimentWithSample <|-- GridCommon
GridCommon <|-- GridScanWithEdgeDetect
GridCommon <|-- HyperionGridCommon
HyperionGridCommon <|-- HyperionThreeDGridScan
SpecifiedGrid <|-- HyperionThreeDGridScan
XyzStarts <|-- SpecifiedGrid
DiffractionExperimentWithSample <|-- GenericGrid
GenericGrid <|-- GenericGridWithHyperionDetectorParams
GenericGridWithHyperionDetectorParams <|-- GridScanWithEdgeDetect
GenericGridWithHyperionDetectorParams <|-- PinTipCentreThenXrayCentre
GenericGridWithHyperionDetectorParams <|-- RobotLoadThenCentre
GenericGrid <|-- SpecifiedGrids
SpecifiedGridsZebraGridScanParamsTwoD <|-- SpecifiedTwoDGridScan
SpecifiedGridsZebraGridScanParamsThreeD <|-- SpecifiedThreeDGridScan
SpecifiedThreeDGridScan <|-- HyperionSpecifiedThreeDGridScan
XyzStarts <|-- SpecifiedGrids
OptionalXYZStarts <|-- RotationScanPerSweep
class GridParamUpdate {
x_start_um
y_start_um
y2_start_um
z_start_um
z2_start_um
x_steps
y_steps
z_steps
x_step_size_um
y_step_size_um
z_step_size_um
x_start_um: float
y_starts_um: float[]
z_starts_um: float[]
x_steps: float
y_steps: float[]
x_step_size_um: float
y_step_sizes_um: float[]
}

class GridDetectionCallback {
get_grid_parameters() -> GridParamUpdate
}

GridDetectionCallback --> GridParamUpdate : generates from event. Adds 0.5 to get box-centres
GridParamUpdate --> HyperionThreeDGridScan : combines with GridScanWithEdgeDetect
GridParamUpdate --> HyperionSpecifiedThreeDGridScan : combines with GridScanWithEdgeDetect

class experiment_plans {
grid_detect_then_xray_centre()
common_flyscan_xray_centre()
create_parameters_for_flyscan_xray_centre(GridScanWithEdgeDetect, GridParamUpdate) -> HyperionThreeDGridScan
}

class AbstractExperimentBase
class AbstractExperimentWithBeamParams
class GridScanParamsCommon {
x_steps
y_steps
z_steps
x_step_size_mm
y_step_size_mm
z_step_size_mm
x_start_mm
y1_start_mm
y2_start_mm
z1_start_mm
z2_start_mm
create_parameters_for_flyscan_xray_centre(GridScanWithEdgeDetect, GridParamUpdate) -> HyperionSpecifiedThreeDGridScan
}
class PandAGridScanParams
class ZebraGridScanParamsThreeD

AbstractExperimentBase <|-- AbstractExperimentWithBeamParams
AbstractExperimentParameterBase <|-- AbstractExperimentWithBeamParams
AbstractExperimentWithBeamParams <|-- GridScanParamsCommon
GridScanParamsCommon <|-- PandAGridScanParams
GridScanParamsCommon <|-- ZebraGridScanParamsThreeD
GridScanParamsCommon <|-- ZebraGridScanParamsTwoD
GridScanParamsCommon <|-- GridScanParamsThreeD
GridScanParamsThreeD <|-- PandAGridScanParams
GridScanParamsThreeD <|-- ZebraGridScanParamsThreeD

HyperionThreeDGridScan --> ZebraGridScanParamsThreeD : generates
HyperionThreeDGridScan --> PandAGridScanParams : generates
HyperionSpecifiedThreeDGridScan --> ZebraGridScanParamsThreeD : generates
HyperionSpecifiedThreeDGridScan --> PandAGridScanParams : generates
SpecifiedTwoDGridScan --> ZebraGridScanParamsTwoD : generates
@enduml
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ dev = [
"tox-uv",
"types-mock",
"types-requests",
"sphinxcontrib.mermaid", # To build dodal docs
"sphinxcontrib.mermaid", # To build dodal docs
]

[project.scripts]
Expand Down
3 changes: 3 additions & 0 deletions src/mx_bluesky/beamlines/i02_1/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from mx_bluesky.beamlines.i02_1.i02_1_gridscan_plan import i02_1_gridscan_plan

__all__ = ["i02_1_gridscan_plan"]
16 changes: 16 additions & 0 deletions src/mx_bluesky/beamlines/i02_1/composites.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
from pathlib import Path

from mx_bluesky.beamlines.i02_1.parameters.gridscan import SpecifiedTwoDGridScan


class I02_1FgsParams(SpecifiedTwoDGridScan): # noqa: N801
"""For VMXm gridscans, GDA currently takes the snapshots and provides bluesky with a path, and
sends over the grid parameters"""

path_to_xtal_snapshot: Path
beam_size_x: float
beam_size_y: float
microns_per_pixel_x: float
microns_per_pixel_y: float
upper_left_x: int # position of X,Y for the top left of the grid, in pixels
upper_left_y: int
45 changes: 45 additions & 0 deletions src/mx_bluesky/beamlines/i02_1/device_setup_plans/setup_zebra.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import bluesky.plan_stubs as bps
from dodal.devices.zebra.zebra import Zebra

from mx_bluesky.common.parameters.constants import PlanGroupCheckpointConstants

ZEBRA_STATUS_TIMEOUT = 30


# Control Eiger from motion controller. Fast shutter is configured in GDA
def setup_zebra_for_gridscan(
zebra: Zebra,
ttl_detector: int | None = None,
group=PlanGroupCheckpointConstants.SETUP_ZEBRA_FOR_GRIDSCAN,
wait=True,
):
"""
Assumes that the motion controller, as part of its gridscan PLC, will send triggers as required to the zebra's
IN1_TTL to control the detector. The fast shutter is configured in GDA, don't need to touch it in Bluesky for now.
"""
ttl_detector = ttl_detector or zebra.mapping.outputs.TTL_EIGER
yield from bps.abs_set(
zebra.output.out_pvs[ttl_detector],
zebra.mapping.sources.IN1_TTL,
)
if wait:
yield from bps.wait(group, timeout=ZEBRA_STATUS_TIMEOUT)


def tidy_up_zebra_after_gridscan(
zebra: Zebra,
ttl_detector: int | None = None,
group=PlanGroupCheckpointConstants.TIDY_ZEBRA_AFTER_GRIDSCAN,
wait=False,
):
"""Revert zebra to state expected by GDA"""
ttl_detector = ttl_detector or zebra.mapping.outputs.TTL_EIGER

yield from bps.abs_set(
zebra.output.out_pvs[ttl_detector],
zebra.mapping.sources.OR1,
group=group,
)

if wait:
yield from bps.wait(group)
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
from collections.abc import Sequence

from mx_bluesky.beamlines.i02_1.composites import I02_1FgsParams
from mx_bluesky.common.external_interaction.callbacks.grid.grid_detect_and_scan.ispyb_mapping import (
construct_comment_for_gridscan,
)
from mx_bluesky.common.external_interaction.callbacks.grid.gridscan.ispyb_callback import (
GridscanISPyBCallback as CommonGridscanISPyBCallback,
)
from mx_bluesky.common.external_interaction.ispyb.data_model import (
DataCollectionGridInfo,
DataCollectionInfo,
Orientation,
ScanDataInfo,
)
from mx_bluesky.common.utils.log import ISPYB_ZOCALO_CALLBACK_LOGGER


def _make_comment(x_steps: int, y_steps: int) -> str:
return f"Diffraction grid scan of {x_steps} by {y_steps}."


class GridscanISPyBCallback(CommonGridscanISPyBCallback):
def _get_scan_infos(self, doc) -> Sequence[ScanDataInfo]:
"""
For VMXm, grid information is available immediately after the plan is triggered.
"""
assert isinstance(self.params, I02_1FgsParams)
assert self.ispyb_ids.data_collection_ids, "No current data collection"
assert self.data_collection_group_info, "No data collection group"
data = doc["data"]
scan_data_infos = []

for grid_num in range(self.params.num_grids):
omega = data.get("gonio-omega", self.params.omega_starts_deg[grid_num])

ISPYB_ZOCALO_CALLBACK_LOGGER.info(
f"Generating dc info for gridplane XY, omega {omega}"
)
data_collection_number = self.params.detector_params.run_number
file_template = f"{self.params.detector_params.prefix}_{data_collection_number}_master.h5"
# Snapshots have already been taken in GDA

data_collection_info = DataCollectionInfo(
xtal_snapshot1=str(self.params.path_to_xtal_snapshot),
xtal_snapshot2=str(self.params.path_to_xtal_snapshot),
xtal_snapshot3=str(self.params.path_to_xtal_snapshot),
n_images=self.params.num_images,
data_collection_number=data_collection_number,
file_template=file_template,
)
data_collection_grid_info = DataCollectionGridInfo(
dx_in_mm=self.params.x_step_size_um / 1000,
dy_in_mm=self.params.y_step_sizes_um[grid_num] / 1000,
steps_x=self.params.x_steps,
steps_y=self.params.y_steps[grid_num],
microns_per_pixel_x=self.params.microns_per_pixel_x,
microns_per_pixel_y=self.params.microns_per_pixel_y,
snapshot_offset_x_pixel=self.params.upper_left_x,
snapshot_offset_y_pixel=self.params.upper_left_y,
orientation=Orientation.HORIZONTAL,
snaked=True,
)
data_collection_info.comments = construct_comment_for_gridscan(
data_collection_grid_info
)

data_collection_id = self.ispyb_ids.data_collection_ids[0]

self.data_collection_group_info.comments = _make_comment(
self.params.x_steps, self.params.y_steps[0]
)

self._populate_axis_info(data_collection_info, doc["data"])

scan_data_info = ScanDataInfo(
data_collection_info=data_collection_info,
data_collection_id=data_collection_id,
data_collection_grid_info=data_collection_grid_info,
)

scan_data_infos.append(scan_data_info)

ISPYB_ZOCALO_CALLBACK_LOGGER.info(
"Updating ispyb data collection after loading grid params"
)

return scan_data_infos
Loading
Loading