Skip to content

Commit c69f4ee

Browse files
Stage zocalo and centre outside of common gridscan (#1557)
* Stage and unstage zocalo results device outside of common gridscan * Separate XRC from grid detect * Fix tests and add new tests
1 parent d55fb90 commit c69f4ee

29 files changed

+755
-513
lines changed

docs/user/hyperion/advanced/concurrent_operations.puml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ activate RE #ffeeee
6363
RE -> RE++ : start_preparing_data_collection_then_do_plan
6464
RE -> eiger ++ : arm the eiger
6565
RE -> GridscanISPyB ++ : send parameters to grdscan callback
66-
RE -> RE ++ #ffdddd : pin_centre_then_flyscan_plan
66+
RE -> RE ++ #ffdddd : pin_centre_then_xray_centre_plan
6767
RE -> RE: setup_beamline_for_oav
6868
RE -> RE: pin_tip_centre_plan
6969
...do pin tip centring...
@@ -121,7 +121,7 @@ snapshots later
121121
RE -> zebra : tidy zebra
122122
deactivate RE /'common_flyscan_xray_centre'/
123123
deactivate RE /' detect_grid_and_do_gridscan'/
124-
return /' pin_centre_then_flyscan_plan'/
124+
return /' pin_centre_then_xray_centre_plan'/
125125
deactivate GridscanISPyB
126126
deactivate RE /' start_preparing_data_collection_then_do_plan '/
127127
deactivate RE /'robot_load_then_xray_centre'/

src/mx_bluesky/beamlines/i04/experiment_plans/i04_grid_detect_then_xray_centre_plan.py

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,13 +43,19 @@
4343
setup_zebra_for_gridscan,
4444
tidy_up_zebra_after_gridscan,
4545
)
46+
from mx_bluesky.common.experiment_plans.change_aperture_then_move_plan import (
47+
get_results_then_change_aperture_and_move_to_xtal,
48+
)
4649
from mx_bluesky.common.experiment_plans.common_flyscan_xray_centre_plan import (
4750
BeamlineSpecificFGSFeatures,
4851
construct_beamline_specific_fast_gridscan_features,
4952
)
5053
from mx_bluesky.common.experiment_plans.common_grid_detect_then_xray_centre_plan import (
5154
grid_detect_then_xray_centre,
5255
)
56+
from mx_bluesky.common.experiment_plans.inner_plans.xrc_results_utils import (
57+
zocalo_stage_decorator,
58+
)
5359
from mx_bluesky.common.experiment_plans.oav_snapshot_plan import (
5460
setup_beamline_for_oav,
5561
)
@@ -85,6 +91,7 @@
8591
from mx_bluesky.common.utils.utils import (
8692
fix_transmission_and_exposure_time_for_current_wavelength,
8793
)
94+
from mx_bluesky.common.utils.xrc_result import XRayCentreEventHandler
8895

8996
DEFAULT_XRC_BEAMSIZE_MICRONS = 20
9097

@@ -154,10 +161,10 @@ def i04_default_grid_detect_and_xray_centre(
154161
composite = GridDetectThenXRayCentreComposite(
155162
eiger,
156163
synchrotron,
157-
zocalo,
158164
smargon,
159165
aperture_scatterguard,
160166
attenuator,
167+
zocalo,
161168
backlight,
162169
beamstop,
163170
beamsize,
@@ -194,12 +201,14 @@ def tidy_beamline():
194201
composite.detector_motion,
195202
)
196203

204+
@zocalo_stage_decorator(composite.zocalo)
197205
@bpp.finalize_decorator(tidy_beamline)
198206
def _inner_grid_detect_then_xrc():
199207
# These callbacks let us talk to ISPyB and Nexgen. They aren't included in the common plan because
200208
# Hyperion handles its callbacks differently to BlueAPI-managed plans, see
201209
# https://github.com/DiamondLightSource/mx-bluesky/issues/1117
202-
callbacks = create_gridscan_callbacks()
210+
flyscan_event_handler = XRayCentreEventHandler()
211+
callbacks = *create_gridscan_callbacks(), flyscan_event_handler
203212

204213
@bpp.subs_decorator(callbacks)
205214
@verify_undulator_gap_before_run_decorator(composite)
@@ -219,6 +228,14 @@ def grid_detect_then_xray_centre_with_callbacks():
219228

220229
try:
221230
yield from grid_detect_then_xray_centre_with_callbacks()
231+
assert isinstance(
232+
grid_common_params.specified_grid_params, SpecifiedThreeDGridScan
233+
), "Specified grid params couldn't be found after grid detection"
234+
yield from get_results_then_change_aperture_and_move_to_xtal(
235+
composite,
236+
grid_common_params.specified_grid_params,
237+
flyscan_event_handler,
238+
)
222239
except CrystalNotFoundError:
223240
yield from bps.mv(
224241
smargon.x, initial_x, smargon.y, initial_y, smargon.z, initial_z
@@ -316,7 +333,6 @@ def construct_i04_specific_features(
316333
fgs_motors,
317334
signals_to_read_pre_flyscan,
318335
signals_to_read_during_collection, # type: ignore # until https://github.com/DiamondLightSource/mx-bluesky/issues/1076
319-
get_xrc_results_from_zocalo=True,
320336
)
321337

322338

src/mx_bluesky/common/experiment_plans/change_aperture_then_move_plan.py

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,32 @@
44
from dodal.devices.smargon import Smargon, StubPosition
55

66
from mx_bluesky.common.device_setup_plans.manipulate_sample import move_x_y_z
7+
from mx_bluesky.common.experiment_plans.inner_plans.xrc_results_utils import (
8+
fetch_xrc_results_from_zocalo,
9+
)
710
from mx_bluesky.common.parameters.constants import PlanGroupCheckpointConstants
11+
from mx_bluesky.common.parameters.device_composites import (
12+
GridDetectThenXRayCentreComposite,
13+
)
14+
from mx_bluesky.common.parameters.gridscan import SpecifiedThreeDGridScan
815
from mx_bluesky.common.utils.log import LOGGER
916
from mx_bluesky.common.utils.tracing import TRACER
10-
from mx_bluesky.common.xrc_result import XRayCentreResult
17+
from mx_bluesky.common.utils.xrc_result import XRayCentreEventHandler, XRayCentreResult
18+
19+
20+
def get_results_then_change_aperture_and_move_to_xtal(
21+
composite: GridDetectThenXRayCentreComposite,
22+
parameters: SpecifiedThreeDGridScan,
23+
flyscan_event_handler: XRayCentreEventHandler,
24+
):
25+
yield from fetch_xrc_results_from_zocalo(composite.zocalo, parameters)
26+
flyscan_results = flyscan_event_handler.xray_centre_results
27+
assert flyscan_results, (
28+
"Flyscan result event not received or no crystal found and exception not raised"
29+
)
30+
yield from change_aperture_then_move_to_xtal(
31+
flyscan_results[0], composite.smargon, composite.aperture_scatterguard
32+
)
1133

1234

1335
def change_aperture_then_move_to_xtal(
@@ -23,7 +45,7 @@ def change_aperture_then_move_to_xtal(
2345
bounding_box_size = numpy.abs(
2446
best_hit.bounding_box_mm[1] - best_hit.bounding_box_mm[0]
2547
)
26-
yield from set_aperture_for_bbox_mm(
48+
yield from _set_aperture_for_bbox_mm(
2749
aperture_scatterguard,
2850
bounding_box_size,
2951
)
@@ -41,7 +63,7 @@ def change_aperture_then_move_to_xtal(
4163
yield from bps.mv(smargon.stub_offsets, StubPosition.CURRENT_AS_CENTER)
4264

4365

44-
def set_aperture_for_bbox_mm(
66+
def _set_aperture_for_bbox_mm(
4567
aperture_device: ApertureScatterguard,
4668
bbox_size_mm: list[float] | numpy.ndarray,
4769
group=PlanGroupCheckpointConstants.GRID_READY_FOR_DC,

src/mx_bluesky/common/experiment_plans/common_flyscan_xray_centre_plan.py

Lines changed: 6 additions & 139 deletions
Original file line numberDiff line numberDiff line change
@@ -6,43 +6,33 @@
66

77
import bluesky.plan_stubs as bps
88
import bluesky.preprocessors as bpp
9-
import numpy as np
109
from bluesky.protocols import Readable
1110
from bluesky.utils import FailedStatus, MsgGenerator
12-
from dodal.common.beamlines.commissioning_mode import read_commissioning_mode
11+
from dodal.devices.eiger import EigerDetector
1312
from dodal.devices.fast_grid_scan import (
1413
FastGridScanCommon,
1514
FastGridScanThreeD,
1615
GridScanInvalidError,
1716
)
18-
from dodal.devices.zocalo import ZocaloResults
19-
from dodal.devices.zocalo.zocalo_results import (
20-
XrcResult,
21-
get_full_processing_results,
22-
)
2317

2418
from mx_bluesky.common.experiment_plans.inner_plans.do_fgs import (
25-
ZOCALO_STAGE_GROUP,
2619
kickoff_and_complete_gridscan,
2720
)
2821
from mx_bluesky.common.experiment_plans.inner_plans.read_hardware import (
2922
read_hardware_plan,
3023
)
3124
from mx_bluesky.common.parameters.constants import (
3225
DocDescriptorNames,
33-
GridscanParamConstants,
3426
PlanGroupCheckpointConstants,
3527
PlanNameConstants,
3628
)
3729
from mx_bluesky.common.parameters.device_composites import FlyScanEssentialDevices
3830
from mx_bluesky.common.parameters.gridscan import SpecifiedThreeDGridScan
3931
from mx_bluesky.common.utils.exceptions import (
40-
CrystalNotFoundError,
4132
SampleError,
4233
)
4334
from mx_bluesky.common.utils.log import LOGGER
4435
from mx_bluesky.common.utils.tracing import TRACER
45-
from mx_bluesky.common.xrc_result import XRayCentreResult
4636

4737

4838
@dataclasses.dataclass
@@ -55,26 +45,19 @@ class BeamlineSpecificFGSFeatures:
5545
..., MsgGenerator
5646
] # Eventually replace with https://github.com/DiamondLightSource/mx-bluesky/issues/819
5747
read_during_collection_plan: Callable[..., MsgGenerator]
58-
get_xrc_results_from_zocalo: bool
59-
6048

61-
def generic_tidy(xrc_composite: FlyScanEssentialDevices, wait=True) -> MsgGenerator:
62-
"""Tidy Zocalo and turn off Eiger dev/shm. Ran after the beamline-specific tidy plan"""
6349

64-
LOGGER.info("Tidying up Zocalo")
65-
group = "generic_tidy"
66-
# make sure we don't consume any other results
67-
yield from bps.unstage(xrc_composite.zocalo, group=group)
50+
def tidy_eiger(eiger: EigerDetector) -> MsgGenerator:
51+
"""Turn off Eiger dev/shm. Ran after the beamline-specific tidy plan"""
6852

6953
# Turn off dev/shm streaming to avoid filling disk, see https://github.com/DiamondLightSource/hyperion/issues/1395
7054
LOGGER.info("Turning off Eiger dev/shm streaming")
7155
# Fix types in ophyd-async (https://github.com/DiamondLightSource/mx-bluesky/issues/855)
7256
yield from bps.abs_set(
73-
xrc_composite.eiger.odin.fan.dev_shm_enable, # type: ignore # until https://github.com/DiamondLightSource/mx-bluesky/issues/1076
57+
eiger.odin.fan.dev_shm_enable, # type: ignore # until https://github.com/DiamondLightSource/mx-bluesky/issues/1076
7458
0,
75-
group=group,
59+
wait=True,
7660
)
77-
yield from bps.wait(group)
7861

7962

8063
def construct_beamline_specific_fast_gridscan_features(
@@ -84,7 +67,6 @@ def construct_beamline_specific_fast_gridscan_features(
8467
fgs_motors: FastGridScanCommon,
8568
signals_to_read_pre_flyscan: Sequence[Readable],
8669
signals_to_read_during_collection: Sequence[Readable],
87-
get_xrc_results_from_zocalo: bool = False,
8870
) -> BeamlineSpecificFGSFeatures:
8971
"""Construct the class needed to do beamline-specific parts of the XRC FGS
9072
@@ -104,9 +86,6 @@ def construct_beamline_specific_fast_gridscan_features(
10486
10587
signals_to_read_during_collection (Callable): Signals which will be read and saved as a bluesky event
10688
document whilst the gridscan motion is in progress
107-
108-
get_xrc_results_from_zocalo (bool): If true, fetch grid scan results from zocalo after completion, as well as
109-
update the ispyb comment field with information about the results. See _fetch_xrc_results_from_zocalo
11089
"""
11190
read_pre_flyscan_plan = partial(
11291
read_hardware_plan,
@@ -127,7 +106,6 @@ def construct_beamline_specific_fast_gridscan_features(
127106
fgs_motors,
128107
read_pre_flyscan_plan,
129108
read_during_collection_plan,
130-
get_xrc_results_from_zocalo,
131109
)
132110

133111

@@ -159,7 +137,7 @@ def common_flyscan_xray_centre(
159137

160138
def _overall_tidy():
161139
yield from beamline_specific.tidy_plan()
162-
yield from generic_tidy(composite)
140+
yield from tidy_eiger(composite.eiger)
163141

164142
def _decorated_flyscan():
165143
@bpp.set_run_key_decorator(PlanNameConstants.GRIDSCAN_OUTER)
@@ -181,81 +159,16 @@ def run_gridscan_and_tidy(
181159
yield from beamline_specific.setup_trigger_plan(fgs_composite, parameters)
182160

183161
LOGGER.info("Starting grid scan")
184-
yield from bps.stage(
185-
fgs_composite.zocalo, group=ZOCALO_STAGE_GROUP
186-
) # connect to zocalo and make sure the queue is clear
187162
yield from run_gridscan(fgs_composite, params, beamline_specific)
188163

189164
LOGGER.info("Grid scan finished")
190165

191-
if beamline_specific.get_xrc_results_from_zocalo:
192-
yield from _fetch_xrc_results_from_zocalo(composite.zocalo, parameters)
193-
194166
yield from run_gridscan_and_tidy(composite, parameters, beamline_specific)
195167

196168
composite.eiger.set_detector_parameters(parameters.detector_params)
197169
yield from _decorated_flyscan()
198170

199171

200-
def _fetch_xrc_results_from_zocalo(
201-
zocalo_results: ZocaloResults,
202-
parameters: SpecifiedThreeDGridScan,
203-
) -> MsgGenerator:
204-
"""
205-
Get XRC results from the ZocaloResults device which was staged during a grid scan,
206-
and store them in XRayCentreEventHandler.xray_centre_results by firing an event.
207-
208-
The RunEngine must be subscribed to XRayCentreEventHandler for this plan to work.
209-
"""
210-
211-
LOGGER.info("Getting X-ray center Zocalo results...")
212-
213-
yield from bps.trigger(zocalo_results, wait=True)
214-
LOGGER.info("Zocalo triggered and read, interpreting results.")
215-
xrc_results = yield from get_full_processing_results(zocalo_results)
216-
LOGGER.info(f"Got xray centres, top 5: {xrc_results[:5]}")
217-
filtered_results = [
218-
result
219-
for result in xrc_results
220-
if result["total_count"]
221-
>= GridscanParamConstants.ZOCALO_MIN_TOTAL_COUNT_THRESHOLD
222-
]
223-
discarded_count = len(xrc_results) - len(filtered_results)
224-
if discarded_count > 0:
225-
LOGGER.info(f"Removed {discarded_count} results because below threshold")
226-
if filtered_results:
227-
flyscan_results = [
228-
_xrc_result_in_boxes_to_result_in_mm(xr, parameters)
229-
for xr in filtered_results
230-
]
231-
else:
232-
commissioning_mode = yield from read_commissioning_mode()
233-
if commissioning_mode:
234-
LOGGER.info("Commissioning mode enabled, returning dummy result")
235-
flyscan_results = [_generate_dummy_xrc_result(parameters)]
236-
else:
237-
LOGGER.warning("No X-ray centre received")
238-
raise CrystalNotFoundError()
239-
yield from _fire_xray_centre_result_event(flyscan_results)
240-
241-
242-
def _generate_dummy_xrc_result(params: SpecifiedThreeDGridScan) -> XRayCentreResult:
243-
com = [params.x_steps / 2, params.y_steps / 2, params.z_steps / 2]
244-
max_voxel = [round(p) for p in com]
245-
return _xrc_result_in_boxes_to_result_in_mm(
246-
XrcResult(
247-
centre_of_mass=com,
248-
max_voxel=max_voxel,
249-
bounding_box=[max_voxel, [p + 1 for p in max_voxel]],
250-
n_voxels=1,
251-
max_count=10000,
252-
total_count=100000,
253-
sample_id=params.sample_id,
254-
),
255-
params,
256-
)
257-
258-
259172
def run_gridscan(
260173
fgs_composite: FlyScanEssentialDevices,
261174
parameters: SpecifiedThreeDGridScan,
@@ -296,49 +209,3 @@ def run_gridscan(
296209
# in a GDA-happy state.
297210
if isinstance(beamline_specific.fgs_motors, FastGridScanThreeD):
298211
yield from bps.abs_set(beamline_specific.fgs_motors.z_steps, 0, wait=False)
299-
300-
301-
def _xrc_result_in_boxes_to_result_in_mm(
302-
xrc_result: XrcResult, parameters: SpecifiedThreeDGridScan
303-
) -> XRayCentreResult:
304-
fgs_params = parameters.fast_gridscan_params
305-
xray_centre = fgs_params.grid_position_to_motor_position(
306-
np.array(xrc_result["centre_of_mass"])
307-
)
308-
# A correction is applied to the bounding box to map discrete grid coordinates to
309-
# the corners of the box in motor-space; we do not apply this correction
310-
# to the xray-centre as it is already in continuous space and the conversion has
311-
# been performed already
312-
# In other words, xrc_result["bounding_box"] contains the position of the box centre,
313-
# so we subtract half a box to get the corner of the box
314-
return XRayCentreResult(
315-
centre_of_mass_mm=xray_centre,
316-
bounding_box_mm=(
317-
fgs_params.grid_position_to_motor_position(
318-
np.array(xrc_result["bounding_box"][0]) - 0.5
319-
),
320-
fgs_params.grid_position_to_motor_position(
321-
np.array(xrc_result["bounding_box"][1]) - 0.5
322-
),
323-
),
324-
max_count=xrc_result["max_count"],
325-
total_count=xrc_result["total_count"],
326-
sample_id=xrc_result["sample_id"],
327-
)
328-
329-
330-
def _fire_xray_centre_result_event(results: Sequence[XRayCentreResult]):
331-
def empty_plan():
332-
return iter([])
333-
334-
yield from bpp.set_run_key_wrapper(
335-
bpp.run_wrapper(
336-
empty_plan(),
337-
md={
338-
PlanNameConstants.FLYSCAN_RESULTS: [
339-
dataclasses.asdict(r) for r in results
340-
]
341-
},
342-
),
343-
PlanNameConstants.FLYSCAN_RESULTS,
344-
)

0 commit comments

Comments
 (0)