mx-bluesky 1.4.0__py3-none-any.whl → 1.4.1a0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- mx_bluesky/_version.py +2 -2
- mx_bluesky/beamlines/i04/thawing_plan.py +1 -1
- mx_bluesky/beamlines/i24/serial/dcid.py +19 -21
- mx_bluesky/beamlines/i24/serial/extruder/i24ssx_Extruder_Collect_py3v2.py +2 -2
- mx_bluesky/beamlines/i24/serial/fixed_target/FT-gui-edm/DiamondChipI24-py3v1.edl +1 -4
- mx_bluesky/beamlines/i24/serial/fixed_target/ft_utils.py +0 -1
- mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_Chip_Collect_py3v1.py +14 -24
- mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_Chip_Manager_py3v1.py +18 -76
- mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_Chip_StartUp_py3v1.py +0 -199
- mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_moveonclick.py +4 -6
- mx_bluesky/beamlines/i24/serial/log.py +1 -1
- mx_bluesky/beamlines/i24/serial/parameters/constants.py +0 -1
- mx_bluesky/beamlines/i24/serial/run_fixed_target.sh +4 -3
- mx_bluesky/beamlines/i24/serial/setup_beamline/setup_beamline.py +78 -80
- mx_bluesky/beamlines/i24/serial/setup_beamline/setup_detector.py +1 -2
- mx_bluesky/beamlines/i24/serial/setup_beamline/setup_zebra_plans.py +24 -26
- mx_bluesky/beamlines/i24/serial/write_nexus.py +11 -11
- mx_bluesky/common/external_interaction/config_server.py +46 -0
- mx_bluesky/common/parameters/components.py +52 -15
- mx_bluesky/common/parameters/constants.py +6 -1
- mx_bluesky/common/parameters/gridscan.py +94 -0
- mx_bluesky/{hyperion → common}/parameters/robot_load.py +2 -2
- mx_bluesky/common/plans/do_fgs.py +2 -2
- mx_bluesky/common/utils/log.py +2 -0
- mx_bluesky/hyperion/__main__.py +2 -1
- mx_bluesky/hyperion/device_setup_plans/setup_panda.py +4 -4
- mx_bluesky/hyperion/device_setup_plans/setup_zebra.py +1 -1
- mx_bluesky/hyperion/experiment_plans/__init__.py +4 -0
- mx_bluesky/hyperion/experiment_plans/change_aperture_then_move_plan.py +83 -0
- mx_bluesky/hyperion/experiment_plans/common/xrc_result.py +47 -0
- mx_bluesky/hyperion/experiment_plans/experiment_registry.py +9 -9
- mx_bluesky/hyperion/experiment_plans/flyscan_xray_centre_plan.py +131 -89
- mx_bluesky/hyperion/experiment_plans/grid_detect_then_xray_centre_plan.py +50 -18
- mx_bluesky/hyperion/experiment_plans/load_centre_collect_full_plan.py +52 -10
- mx_bluesky/hyperion/experiment_plans/oav_grid_detection_plan.py +2 -2
- mx_bluesky/hyperion/experiment_plans/oav_snapshot_plan.py +3 -9
- mx_bluesky/hyperion/experiment_plans/optimise_attenuation_plan.py +2 -2
- mx_bluesky/hyperion/experiment_plans/pin_centre_then_xray_centre_plan.py +36 -17
- mx_bluesky/hyperion/experiment_plans/pin_tip_centring_plan.py +2 -2
- mx_bluesky/hyperion/experiment_plans/robot_load_and_change_energy.py +6 -10
- mx_bluesky/hyperion/experiment_plans/robot_load_then_centre_plan.py +46 -11
- mx_bluesky/hyperion/experiment_plans/rotation_scan_plan.py +18 -3
- mx_bluesky/hyperion/experiment_plans/set_energy_plan.py +2 -3
- mx_bluesky/hyperion/external_interaction/callbacks/common/abstract_event.py +66 -0
- mx_bluesky/hyperion/external_interaction/callbacks/grid_detection_callback.py +15 -15
- mx_bluesky/hyperion/external_interaction/callbacks/rotation/ispyb_callback.py +3 -1
- mx_bluesky/hyperion/external_interaction/callbacks/rotation/nexus_callback.py +5 -3
- mx_bluesky/hyperion/external_interaction/callbacks/xray_centre/ispyb_callback.py +5 -2
- mx_bluesky/hyperion/external_interaction/callbacks/xray_centre/nexus_callback.py +5 -4
- mx_bluesky/hyperion/external_interaction/config_server.py +8 -37
- mx_bluesky/hyperion/external_interaction/ispyb/exp_eye_store.py +1 -1
- mx_bluesky/hyperion/parameters/components.py +4 -9
- mx_bluesky/hyperion/parameters/constants.py +0 -1
- mx_bluesky/hyperion/parameters/gridscan.py +33 -76
- mx_bluesky/hyperion/parameters/load_centre_collect.py +14 -9
- mx_bluesky/hyperion/parameters/rotation.py +15 -6
- {mx_bluesky-1.4.0.dist-info → mx_bluesky-1.4.1a0.dist-info}/METADATA +35 -34
- {mx_bluesky-1.4.0.dist-info → mx_bluesky-1.4.1a0.dist-info}/RECORD +62 -58
- {mx_bluesky-1.4.0.dist-info → mx_bluesky-1.4.1a0.dist-info}/WHEEL +1 -1
- mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_Chip_Mapping_py3v1.py +0 -150
- {mx_bluesky-1.4.0.dist-info → mx_bluesky-1.4.1a0.dist-info}/LICENSE +0 -0
- {mx_bluesky-1.4.0.dist-info → mx_bluesky-1.4.1a0.dist-info}/entry_points.txt +0 -0
- {mx_bluesky-1.4.0.dist-info → mx_bluesky-1.4.1a0.dist-info}/top_level.txt +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import dataclasses
|
|
4
|
-
from collections.abc import Callable
|
|
4
|
+
from collections.abc import Callable, Sequence
|
|
5
5
|
from functools import partial
|
|
6
6
|
from pathlib import Path
|
|
7
7
|
from typing import Protocol
|
|
@@ -9,10 +9,12 @@ from typing import Protocol
|
|
|
9
9
|
import bluesky.plan_stubs as bps
|
|
10
10
|
import bluesky.preprocessors as bpp
|
|
11
11
|
import numpy as np
|
|
12
|
-
|
|
12
|
+
import pydantic
|
|
13
|
+
from blueapi.core import BlueskyContext
|
|
14
|
+
from bluesky.callbacks import CallbackBase
|
|
15
|
+
from bluesky.utils import MsgGenerator
|
|
13
16
|
from dodal.devices.aperturescatterguard import (
|
|
14
17
|
ApertureScatterguard,
|
|
15
|
-
ApertureValue,
|
|
16
18
|
)
|
|
17
19
|
from dodal.devices.attenuator import Attenuator
|
|
18
20
|
from dodal.devices.backlight import Backlight
|
|
@@ -29,7 +31,7 @@ from dodal.devices.fast_grid_scan import (
|
|
|
29
31
|
from dodal.devices.flux import Flux
|
|
30
32
|
from dodal.devices.robot import BartRobot
|
|
31
33
|
from dodal.devices.s4_slit_gaps import S4SlitGaps
|
|
32
|
-
from dodal.devices.smargon import Smargon
|
|
34
|
+
from dodal.devices.smargon import Smargon
|
|
33
35
|
from dodal.devices.synchrotron import Synchrotron
|
|
34
36
|
from dodal.devices.undulator import Undulator
|
|
35
37
|
from dodal.devices.xbpm_feedback import XBPMFeedback
|
|
@@ -38,14 +40,15 @@ from dodal.devices.zebra_controlled_shutter import ZebraShutter
|
|
|
38
40
|
from dodal.devices.zocalo.zocalo_results import (
|
|
39
41
|
ZOCALO_READING_PLAN_NAME,
|
|
40
42
|
ZOCALO_STAGE_GROUP,
|
|
43
|
+
XrcResult,
|
|
41
44
|
ZocaloResults,
|
|
42
45
|
get_full_processing_results,
|
|
43
46
|
)
|
|
47
|
+
from event_model import RunStart
|
|
44
48
|
from ophyd_async.fastcs.panda import HDFPanda
|
|
45
49
|
|
|
46
50
|
from mx_bluesky.common.plans.do_fgs import kickoff_and_complete_gridscan
|
|
47
51
|
from mx_bluesky.common.utils.tracing import TRACER
|
|
48
|
-
from mx_bluesky.hyperion.device_setup_plans.manipulate_sample import move_x_y_z
|
|
49
52
|
from mx_bluesky.hyperion.device_setup_plans.read_hardware_for_setup import (
|
|
50
53
|
read_hardware_during_collection,
|
|
51
54
|
read_hardware_pre_collection,
|
|
@@ -64,9 +67,16 @@ from mx_bluesky.hyperion.device_setup_plans.xbpm_feedback import (
|
|
|
64
67
|
transmission_and_xbpm_feedback_for_collection_decorator,
|
|
65
68
|
)
|
|
66
69
|
from mx_bluesky.hyperion.exceptions import WarningException
|
|
70
|
+
from mx_bluesky.hyperion.experiment_plans.change_aperture_then_move_plan import (
|
|
71
|
+
change_aperture_then_move_to_xtal,
|
|
72
|
+
)
|
|
73
|
+
from mx_bluesky.hyperion.experiment_plans.common.xrc_result import XRayCentreResult
|
|
74
|
+
from mx_bluesky.hyperion.external_interaction.callbacks.xray_centre.ispyb_callback import (
|
|
75
|
+
ispyb_activation_wrapper,
|
|
76
|
+
)
|
|
67
77
|
from mx_bluesky.hyperion.log import LOGGER
|
|
68
78
|
from mx_bluesky.hyperion.parameters.constants import CONST
|
|
69
|
-
from mx_bluesky.hyperion.parameters.gridscan import
|
|
79
|
+
from mx_bluesky.hyperion.parameters.gridscan import HyperionThreeDGridScan
|
|
70
80
|
from mx_bluesky.hyperion.utils.context import device_composite_from_context
|
|
71
81
|
|
|
72
82
|
|
|
@@ -80,7 +90,7 @@ class CrystalNotFoundException(WarningException):
|
|
|
80
90
|
pass
|
|
81
91
|
|
|
82
92
|
|
|
83
|
-
@dataclasses.dataclass
|
|
93
|
+
@pydantic.dataclasses.dataclass(config={"arbitrary_types_allowed": True})
|
|
84
94
|
class FlyScanXRayCentreComposite:
|
|
85
95
|
"""All devices which are directly or indirectly required by this plan"""
|
|
86
96
|
|
|
@@ -109,26 +119,29 @@ class FlyScanXRayCentreComposite:
|
|
|
109
119
|
return self.smargon
|
|
110
120
|
|
|
111
121
|
|
|
122
|
+
class XRayCentreEventHandler(CallbackBase):
|
|
123
|
+
def __init__(self):
|
|
124
|
+
super().__init__()
|
|
125
|
+
self.xray_centre_results: Sequence[XRayCentreResult] | None = None
|
|
126
|
+
|
|
127
|
+
def start(self, doc: RunStart) -> RunStart | None:
|
|
128
|
+
if "xray_centre_results" in doc:
|
|
129
|
+
self.xray_centre_results = [
|
|
130
|
+
XRayCentreResult(**result_dict)
|
|
131
|
+
for result_dict in doc["xray_centre_results"] # type: ignore
|
|
132
|
+
]
|
|
133
|
+
return doc
|
|
134
|
+
|
|
135
|
+
|
|
112
136
|
def create_devices(context: BlueskyContext) -> FlyScanXRayCentreComposite:
|
|
113
137
|
"""Creates the devices required for the plan and connect to them"""
|
|
114
138
|
return device_composite_from_context(context, FlyScanXRayCentreComposite)
|
|
115
139
|
|
|
116
140
|
|
|
117
|
-
def
|
|
118
|
-
composite: FlyScanXRayCentreComposite,
|
|
119
|
-
parameters: ThreeDGridScan,
|
|
141
|
+
def flyscan_xray_centre_no_move(
|
|
142
|
+
composite: FlyScanXRayCentreComposite, parameters: HyperionThreeDGridScan
|
|
120
143
|
) -> MsgGenerator:
|
|
121
|
-
"""
|
|
122
|
-
|
|
123
|
-
The ispyb handler should be added to the whole gridscan as we want to capture errors
|
|
124
|
-
at any point in it.
|
|
125
|
-
|
|
126
|
-
Args:
|
|
127
|
-
parameters (ThreeDGridScan): The parameters to run the scan.
|
|
128
|
-
|
|
129
|
-
Returns:
|
|
130
|
-
Generator: The plan for the gridscan
|
|
131
|
-
"""
|
|
144
|
+
"""Perform a flyscan and determine the centres of interest"""
|
|
132
145
|
parameters.features.update_self_from_server()
|
|
133
146
|
composite.eiger.set_detector_parameters(parameters.detector_params)
|
|
134
147
|
composite.zocalo.zocalo_environment = CONST.ZOCALO_ENV
|
|
@@ -152,25 +165,66 @@ def flyscan_xray_centre(
|
|
|
152
165
|
composite.attenuator,
|
|
153
166
|
parameters.transmission_frac,
|
|
154
167
|
)
|
|
155
|
-
def
|
|
168
|
+
def run_gridscan_and_fetch_and_tidy(
|
|
156
169
|
fgs_composite: FlyScanXRayCentreComposite,
|
|
157
|
-
params:
|
|
170
|
+
params: HyperionThreeDGridScan,
|
|
158
171
|
feature_controlled: _FeatureControlled,
|
|
159
|
-
):
|
|
160
|
-
yield from
|
|
172
|
+
) -> MsgGenerator:
|
|
173
|
+
yield from run_gridscan_and_fetch_results(
|
|
174
|
+
fgs_composite, params, feature_controlled
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
yield from run_gridscan_and_fetch_and_tidy(
|
|
178
|
+
composite, parameters, feature_controlled
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
def flyscan_xray_centre(
|
|
183
|
+
composite: FlyScanXRayCentreComposite,
|
|
184
|
+
parameters: HyperionThreeDGridScan,
|
|
185
|
+
) -> MsgGenerator:
|
|
186
|
+
"""Create the plan to run the grid scan based on provided parameters.
|
|
187
|
+
|
|
188
|
+
The ispyb handler should be added to the whole gridscan as we want to capture errors
|
|
189
|
+
at any point in it.
|
|
161
190
|
|
|
162
|
-
|
|
191
|
+
Args:
|
|
192
|
+
parameters (ThreeDGridScan): The parameters to run the scan.
|
|
193
|
+
|
|
194
|
+
Returns:
|
|
195
|
+
Generator: The plan for the gridscan
|
|
196
|
+
"""
|
|
197
|
+
xrc_event_handler = XRayCentreEventHandler()
|
|
198
|
+
|
|
199
|
+
@bpp.subs_decorator(xrc_event_handler)
|
|
200
|
+
def flyscan_and_fetch_results() -> MsgGenerator:
|
|
201
|
+
yield from ispyb_activation_wrapper(
|
|
202
|
+
flyscan_xray_centre_no_move(composite, parameters), parameters
|
|
203
|
+
)
|
|
204
|
+
|
|
205
|
+
yield from flyscan_and_fetch_results()
|
|
206
|
+
|
|
207
|
+
xray_centre_results = xrc_event_handler.xray_centre_results
|
|
208
|
+
assert (
|
|
209
|
+
xray_centre_results
|
|
210
|
+
), "Flyscan result event not received or no crystal found and exception not raised"
|
|
211
|
+
yield from change_aperture_then_move_to_xtal(
|
|
212
|
+
xray_centre_results[0],
|
|
213
|
+
composite.smargon,
|
|
214
|
+
composite.aperture_scatterguard,
|
|
215
|
+
parameters,
|
|
216
|
+
)
|
|
163
217
|
|
|
164
218
|
|
|
165
219
|
@bpp.set_run_key_decorator(CONST.PLAN.GRIDSCAN_AND_MOVE)
|
|
166
220
|
@bpp.run_decorator(md={"subplan_name": CONST.PLAN.GRIDSCAN_AND_MOVE})
|
|
167
|
-
def
|
|
221
|
+
def run_gridscan_and_fetch_results(
|
|
168
222
|
fgs_composite: FlyScanXRayCentreComposite,
|
|
169
|
-
parameters:
|
|
223
|
+
parameters: HyperionThreeDGridScan,
|
|
170
224
|
feature_controlled: _FeatureControlled,
|
|
171
225
|
) -> MsgGenerator:
|
|
172
226
|
"""A multi-run plan which runs a gridscan, gets the results from zocalo
|
|
173
|
-
and
|
|
227
|
+
and fires an event with the centres of mass determined by zocalo"""
|
|
174
228
|
|
|
175
229
|
# We get the initial motor positions so we can return to them on zocalo failure
|
|
176
230
|
initial_xyz = np.array(
|
|
@@ -198,37 +252,17 @@ def run_gridscan_and_move(
|
|
|
198
252
|
)
|
|
199
253
|
LOGGER.info("Zocalo triggered and read, interpreting results.")
|
|
200
254
|
xrc_results = yield from get_full_processing_results(fgs_composite.zocalo)
|
|
201
|
-
LOGGER.info(f"Got xray
|
|
255
|
+
LOGGER.info(f"Got xray centres, top 5: {xrc_results[:5]}")
|
|
202
256
|
if xrc_results:
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
)
|
|
208
|
-
with TRACER.start_span("change_aperture"):
|
|
209
|
-
bbox_size = np.abs(
|
|
210
|
-
np.array(best_result["bounding_box"][1])
|
|
211
|
-
- np.array(best_result["bounding_box"][0])
|
|
212
|
-
)
|
|
213
|
-
yield from set_aperture_for_bbox_size(
|
|
214
|
-
fgs_composite.aperture_scatterguard, bbox_size
|
|
215
|
-
)
|
|
257
|
+
flyscan_results = [
|
|
258
|
+
_xrc_result_in_boxes_to_result_in_mm(xr, parameters)
|
|
259
|
+
for xr in xrc_results
|
|
260
|
+
]
|
|
216
261
|
else:
|
|
217
262
|
LOGGER.warning("No X-ray centre received")
|
|
218
263
|
raise CrystalNotFoundException()
|
|
264
|
+
yield from _fire_xray_centre_result_event(flyscan_results)
|
|
219
265
|
|
|
220
|
-
# once we have the results, go to the appropriate position
|
|
221
|
-
LOGGER.info("Moving to centre of mass.")
|
|
222
|
-
with TRACER.start_span("move_to_result"):
|
|
223
|
-
x, y, z = xray_centre
|
|
224
|
-
yield from move_x_y_z(fgs_composite.sample_motors, x, y, z, wait=True)
|
|
225
|
-
|
|
226
|
-
if parameters.FGS_params.set_stub_offsets:
|
|
227
|
-
LOGGER.info("Recentring smargon co-ordinate system to this point.")
|
|
228
|
-
yield from bps.mv(
|
|
229
|
-
fgs_composite.sample_motors.stub_offsets, # type: ignore # See: https://github.com/bluesky/bluesky/issues/1809
|
|
230
|
-
StubPosition.CURRENT_AS_CENTER, # type: ignore # See: https://github.com/bluesky/bluesky/issues/1809
|
|
231
|
-
)
|
|
232
266
|
finally:
|
|
233
267
|
# Turn off dev/shm streaming to avoid filling disk, see https://github.com/DiamondLightSource/hyperion/issues/1395
|
|
234
268
|
LOGGER.info("Turning off Eiger dev/shm streaming")
|
|
@@ -239,11 +273,44 @@ def run_gridscan_and_move(
|
|
|
239
273
|
yield from bps.wait()
|
|
240
274
|
|
|
241
275
|
|
|
276
|
+
def _xrc_result_in_boxes_to_result_in_mm(
|
|
277
|
+
xrc_result: XrcResult, parameters: HyperionThreeDGridScan
|
|
278
|
+
) -> XRayCentreResult:
|
|
279
|
+
fgs_params = parameters.FGS_params
|
|
280
|
+
xray_centre = fgs_params.grid_position_to_motor_position(
|
|
281
|
+
np.array(xrc_result["centre_of_mass"])
|
|
282
|
+
)
|
|
283
|
+
return XRayCentreResult(
|
|
284
|
+
centre_of_mass_mm=xray_centre,
|
|
285
|
+
bounding_box_mm=(
|
|
286
|
+
fgs_params.grid_position_to_motor_position(
|
|
287
|
+
np.array(xrc_result["bounding_box"][0])
|
|
288
|
+
),
|
|
289
|
+
fgs_params.grid_position_to_motor_position(
|
|
290
|
+
np.array(xrc_result["bounding_box"][1])
|
|
291
|
+
),
|
|
292
|
+
),
|
|
293
|
+
max_count=xrc_result["max_count"],
|
|
294
|
+
total_count=xrc_result["total_count"],
|
|
295
|
+
)
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
@bpp.set_run_key_decorator(CONST.PLAN.FLYSCAN_RESULTS)
|
|
299
|
+
def _fire_xray_centre_result_event(results: Sequence[XRayCentreResult]):
|
|
300
|
+
def empty_plan():
|
|
301
|
+
return iter([])
|
|
302
|
+
|
|
303
|
+
yield from bpp.run_wrapper(
|
|
304
|
+
empty_plan(),
|
|
305
|
+
md={"xray_centre_results": [dataclasses.asdict(r) for r in results]},
|
|
306
|
+
)
|
|
307
|
+
|
|
308
|
+
|
|
242
309
|
@bpp.set_run_key_decorator(CONST.PLAN.GRIDSCAN_MAIN)
|
|
243
310
|
@bpp.run_decorator(md={"subplan_name": CONST.PLAN.GRIDSCAN_MAIN})
|
|
244
311
|
def run_gridscan(
|
|
245
312
|
fgs_composite: FlyScanXRayCentreComposite,
|
|
246
|
-
parameters:
|
|
313
|
+
parameters: HyperionThreeDGridScan,
|
|
247
314
|
feature_controlled: _FeatureControlled,
|
|
248
315
|
md={ # noqa
|
|
249
316
|
"plan_name": CONST.PLAN.GRIDSCAN_MAIN,
|
|
@@ -314,31 +381,6 @@ def wait_for_gridscan_valid(fgs_motors: FastGridScanCommon, timeout=0.5):
|
|
|
314
381
|
raise WarningException("Scan invalid - pin too long/short/bent and out of range")
|
|
315
382
|
|
|
316
383
|
|
|
317
|
-
def set_aperture_for_bbox_size(
|
|
318
|
-
aperture_device: ApertureScatterguard,
|
|
319
|
-
bbox_size: list[int] | np.ndarray,
|
|
320
|
-
):
|
|
321
|
-
# bbox_size is [x,y,z], for i03 we only care about x
|
|
322
|
-
new_selected_aperture = (
|
|
323
|
-
ApertureValue.MEDIUM if bbox_size[0] < 2 else ApertureValue.LARGE
|
|
324
|
-
)
|
|
325
|
-
LOGGER.info(
|
|
326
|
-
f"Setting aperture to {new_selected_aperture} based on bounding box size {bbox_size}."
|
|
327
|
-
)
|
|
328
|
-
|
|
329
|
-
@bpp.set_run_key_decorator("change_aperture")
|
|
330
|
-
@bpp.run_decorator(
|
|
331
|
-
md={
|
|
332
|
-
"subplan_name": "change_aperture",
|
|
333
|
-
"aperture_size": new_selected_aperture.value,
|
|
334
|
-
}
|
|
335
|
-
)
|
|
336
|
-
def set_aperture():
|
|
337
|
-
yield from bps.abs_set(aperture_device, new_selected_aperture)
|
|
338
|
-
|
|
339
|
-
yield from set_aperture()
|
|
340
|
-
|
|
341
|
-
|
|
342
384
|
@dataclasses.dataclass
|
|
343
385
|
class _FeatureControlled:
|
|
344
386
|
class _ZebraSetup(Protocol):
|
|
@@ -350,7 +392,7 @@ class _FeatureControlled:
|
|
|
350
392
|
def __call__(
|
|
351
393
|
self,
|
|
352
394
|
fgs_composite: FlyScanXRayCentreComposite,
|
|
353
|
-
parameters:
|
|
395
|
+
parameters: HyperionThreeDGridScan,
|
|
354
396
|
initial_xyz: np.ndarray,
|
|
355
397
|
) -> MsgGenerator: ...
|
|
356
398
|
|
|
@@ -362,7 +404,7 @@ class _FeatureControlled:
|
|
|
362
404
|
|
|
363
405
|
def _get_feature_controlled(
|
|
364
406
|
fgs_composite: FlyScanXRayCentreComposite,
|
|
365
|
-
parameters:
|
|
407
|
+
parameters: HyperionThreeDGridScan,
|
|
366
408
|
):
|
|
367
409
|
if parameters.features.use_panda_for_gridscan:
|
|
368
410
|
return _FeatureControlled(
|
|
@@ -411,7 +453,7 @@ def _panda_tidy(fgs_composite: FlyScanXRayCentreComposite):
|
|
|
411
453
|
|
|
412
454
|
def _zebra_triggering_setup(
|
|
413
455
|
fgs_composite: FlyScanXRayCentreComposite,
|
|
414
|
-
parameters:
|
|
456
|
+
parameters: HyperionThreeDGridScan,
|
|
415
457
|
initial_xyz: np.ndarray,
|
|
416
458
|
):
|
|
417
459
|
yield from setup_zebra_for_gridscan(
|
|
@@ -421,7 +463,7 @@ def _zebra_triggering_setup(
|
|
|
421
463
|
|
|
422
464
|
def _panda_triggering_setup(
|
|
423
465
|
fgs_composite: FlyScanXRayCentreComposite,
|
|
424
|
-
parameters:
|
|
466
|
+
parameters: HyperionThreeDGridScan,
|
|
425
467
|
initial_xyz: np.ndarray,
|
|
426
468
|
):
|
|
427
469
|
LOGGER.info("Setting up Panda for flyscan")
|
|
@@ -440,15 +482,15 @@ def _panda_triggering_setup(
|
|
|
440
482
|
)
|
|
441
483
|
|
|
442
484
|
sample_velocity_mm_per_s = (
|
|
443
|
-
parameters.panda_FGS_params.
|
|
485
|
+
parameters.panda_FGS_params.x_step_size_mm * 1e3 / time_between_x_steps_ms
|
|
444
486
|
)
|
|
445
487
|
if sample_velocity_mm_per_s > smargon_speed_limit_mm_per_s:
|
|
446
488
|
raise SmargonSpeedException(
|
|
447
489
|
f"Smargon speed was calculated from x step size\
|
|
448
|
-
{parameters.panda_FGS_params.
|
|
490
|
+
{parameters.panda_FGS_params.x_step_size_mm}mm and\
|
|
449
491
|
time_between_x_steps_ms {time_between_x_steps_ms} as\
|
|
450
|
-
{sample_velocity_mm_per_s}. The smargon's speed limit is\
|
|
451
|
-
{smargon_speed_limit_mm_per_s}
|
|
492
|
+
{sample_velocity_mm_per_s}mm/s. The smargon's speed limit is\
|
|
493
|
+
{smargon_speed_limit_mm_per_s}mm/s."
|
|
452
494
|
)
|
|
453
495
|
else:
|
|
454
496
|
LOGGER.info(
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
import dataclasses
|
|
4
3
|
from pathlib import Path
|
|
5
4
|
|
|
6
|
-
|
|
5
|
+
import pydantic
|
|
6
|
+
from blueapi.core import BlueskyContext
|
|
7
7
|
from bluesky import plan_stubs as bps
|
|
8
8
|
from bluesky import preprocessors as bpp
|
|
9
|
+
from bluesky.preprocessors import subs_decorator
|
|
10
|
+
from bluesky.utils import MsgGenerator
|
|
9
11
|
from dodal.devices.aperturescatterguard import ApertureScatterguard
|
|
10
12
|
from dodal.devices.attenuator import Attenuator
|
|
11
13
|
from dodal.devices.backlight import Backlight, BacklightPosition
|
|
@@ -29,17 +31,22 @@ from dodal.devices.zocalo import ZocaloResults
|
|
|
29
31
|
from ophyd_async.fastcs.panda import HDFPanda
|
|
30
32
|
|
|
31
33
|
from mx_bluesky.common.parameters.constants import OavConstants
|
|
34
|
+
from mx_bluesky.common.parameters.gridscan import GridScanWithEdgeDetect
|
|
32
35
|
from mx_bluesky.hyperion.device_setup_plans.manipulate_sample import (
|
|
33
36
|
move_aperture_if_required,
|
|
34
37
|
)
|
|
35
38
|
from mx_bluesky.hyperion.device_setup_plans.utils import (
|
|
36
39
|
start_preparing_data_collection_then_do_plan,
|
|
37
40
|
)
|
|
41
|
+
from mx_bluesky.hyperion.experiment_plans.change_aperture_then_move_plan import (
|
|
42
|
+
change_aperture_then_move_to_xtal,
|
|
43
|
+
)
|
|
38
44
|
from mx_bluesky.hyperion.experiment_plans.flyscan_xray_centre_plan import (
|
|
39
45
|
FlyScanXRayCentreComposite as FlyScanXRayCentreComposite,
|
|
40
46
|
)
|
|
41
47
|
from mx_bluesky.hyperion.experiment_plans.flyscan_xray_centre_plan import (
|
|
42
|
-
|
|
48
|
+
XRayCentreEventHandler,
|
|
49
|
+
flyscan_xray_centre_no_move,
|
|
43
50
|
)
|
|
44
51
|
from mx_bluesky.hyperion.experiment_plans.oav_grid_detection_plan import (
|
|
45
52
|
OavGridDetectionComposite,
|
|
@@ -55,13 +62,12 @@ from mx_bluesky.hyperion.external_interaction.callbacks.xray_centre.ispyb_callba
|
|
|
55
62
|
from mx_bluesky.hyperion.log import LOGGER
|
|
56
63
|
from mx_bluesky.hyperion.parameters.constants import CONST
|
|
57
64
|
from mx_bluesky.hyperion.parameters.gridscan import (
|
|
58
|
-
|
|
59
|
-
ThreeDGridScan,
|
|
65
|
+
HyperionThreeDGridScan,
|
|
60
66
|
)
|
|
61
67
|
from mx_bluesky.hyperion.utils.context import device_composite_from_context
|
|
62
68
|
|
|
63
69
|
|
|
64
|
-
@dataclasses.dataclass
|
|
70
|
+
@pydantic.dataclasses.dataclass(config={"arbitrary_types_allowed": True})
|
|
65
71
|
class GridDetectThenXRayCentreComposite:
|
|
66
72
|
"""All devices which are directly or indirectly required by this plan"""
|
|
67
73
|
|
|
@@ -87,6 +93,10 @@ class GridDetectThenXRayCentreComposite:
|
|
|
87
93
|
robot: BartRobot
|
|
88
94
|
sample_shutter: ZebraShutter
|
|
89
95
|
|
|
96
|
+
@property
|
|
97
|
+
def sample_motors(self):
|
|
98
|
+
return self.smargon
|
|
99
|
+
|
|
90
100
|
|
|
91
101
|
def create_devices(context: BlueskyContext) -> GridDetectThenXRayCentreComposite:
|
|
92
102
|
return device_composite_from_context(context, GridDetectThenXRayCentreComposite)
|
|
@@ -95,10 +105,10 @@ def create_devices(context: BlueskyContext) -> GridDetectThenXRayCentreComposite
|
|
|
95
105
|
def create_parameters_for_flyscan_xray_centre(
|
|
96
106
|
grid_scan_with_edge_params: GridScanWithEdgeDetect,
|
|
97
107
|
grid_parameters: GridParamUpdate,
|
|
98
|
-
) ->
|
|
108
|
+
) -> HyperionThreeDGridScan:
|
|
99
109
|
params_json = grid_scan_with_edge_params.model_dump()
|
|
100
110
|
params_json.update(grid_parameters)
|
|
101
|
-
flyscan_xray_centre_parameters =
|
|
111
|
+
flyscan_xray_centre_parameters = HyperionThreeDGridScan(**params_json)
|
|
102
112
|
LOGGER.info(f"Parameters for FGS: {flyscan_xray_centre_parameters}")
|
|
103
113
|
return flyscan_xray_centre_parameters
|
|
104
114
|
|
|
@@ -134,6 +144,14 @@ def detect_grid_and_do_gridscan(
|
|
|
134
144
|
parameters.box_size_um,
|
|
135
145
|
)
|
|
136
146
|
|
|
147
|
+
if parameters.selected_aperture:
|
|
148
|
+
# Start moving the aperture/scatterguard into position without moving it in
|
|
149
|
+
yield from bps.abs_set(
|
|
150
|
+
composite.aperture_scatterguard.aperture_outside_beam,
|
|
151
|
+
parameters.selected_aperture,
|
|
152
|
+
group=CONST.WAIT.GRID_READY_FOR_DC,
|
|
153
|
+
)
|
|
154
|
+
|
|
137
155
|
yield from run_grid_detection_plan(
|
|
138
156
|
oav_params,
|
|
139
157
|
snapshot_template,
|
|
@@ -150,7 +168,7 @@ def detect_grid_and_do_gridscan(
|
|
|
150
168
|
group=CONST.WAIT.GRID_READY_FOR_DC,
|
|
151
169
|
)
|
|
152
170
|
|
|
153
|
-
yield from
|
|
171
|
+
yield from flyscan_xray_centre_no_move(
|
|
154
172
|
FlyScanXRayCentreComposite(
|
|
155
173
|
aperture_scatterguard=composite.aperture_scatterguard,
|
|
156
174
|
attenuator=composite.attenuator,
|
|
@@ -193,19 +211,33 @@ def grid_detect_then_xray_centre(
|
|
|
193
211
|
|
|
194
212
|
oav_params = OAVParameters("xrayCentring", oav_config)
|
|
195
213
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
214
|
+
flyscan_event_handler = XRayCentreEventHandler()
|
|
215
|
+
|
|
216
|
+
@subs_decorator(flyscan_event_handler)
|
|
217
|
+
def plan_to_perform():
|
|
218
|
+
yield from ispyb_activation_wrapper(
|
|
219
|
+
detect_grid_and_do_gridscan(
|
|
220
|
+
composite,
|
|
221
|
+
parameters,
|
|
222
|
+
oav_params,
|
|
223
|
+
),
|
|
199
224
|
parameters,
|
|
200
|
-
|
|
201
|
-
),
|
|
202
|
-
parameters,
|
|
203
|
-
)
|
|
225
|
+
)
|
|
204
226
|
|
|
205
|
-
|
|
227
|
+
yield from start_preparing_data_collection_then_do_plan(
|
|
206
228
|
eiger,
|
|
207
229
|
composite.detector_motion,
|
|
208
230
|
parameters.detector_params.detector_distance,
|
|
209
|
-
plan_to_perform,
|
|
231
|
+
plan_to_perform(),
|
|
210
232
|
group=CONST.WAIT.GRID_READY_FOR_DC,
|
|
211
233
|
)
|
|
234
|
+
|
|
235
|
+
assert (
|
|
236
|
+
flyscan_event_handler.xray_centre_results
|
|
237
|
+
), "Flyscan result event not received or no crystal found and exception not raised"
|
|
238
|
+
|
|
239
|
+
yield from change_aperture_then_move_to_xtal(
|
|
240
|
+
flyscan_event_handler.xray_centre_results[0],
|
|
241
|
+
composite.smargon,
|
|
242
|
+
composite.aperture_scatterguard,
|
|
243
|
+
)
|
|
@@ -1,25 +1,36 @@
|
|
|
1
|
-
import
|
|
1
|
+
from collections.abc import Sequence
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
import pydantic
|
|
4
|
+
from blueapi.core import BlueskyContext, MsgGenerator
|
|
5
|
+
from bluesky.preprocessors import subs_wrapper
|
|
4
6
|
from dodal.devices.oav.oav_parameters import OAVParameters
|
|
7
|
+
from dodal.devices.smargon import Smargon
|
|
5
8
|
|
|
9
|
+
import mx_bluesky.hyperion.experiment_plans.common.xrc_result as flyscan_result
|
|
10
|
+
from mx_bluesky.hyperion.experiment_plans.flyscan_xray_centre_plan import (
|
|
11
|
+
XRayCentreEventHandler,
|
|
12
|
+
)
|
|
6
13
|
from mx_bluesky.hyperion.experiment_plans.robot_load_then_centre_plan import (
|
|
7
14
|
RobotLoadThenCentreComposite,
|
|
8
|
-
|
|
15
|
+
robot_load_then_xray_centre,
|
|
9
16
|
)
|
|
10
17
|
from mx_bluesky.hyperion.experiment_plans.rotation_scan_plan import (
|
|
18
|
+
MultiRotationScan,
|
|
11
19
|
RotationScanComposite,
|
|
12
20
|
multi_rotation_scan,
|
|
13
21
|
)
|
|
22
|
+
from mx_bluesky.hyperion.log import LOGGER
|
|
14
23
|
from mx_bluesky.hyperion.parameters.load_centre_collect import LoadCentreCollect
|
|
15
24
|
from mx_bluesky.hyperion.utils.context import device_composite_from_context
|
|
16
25
|
|
|
17
26
|
|
|
18
|
-
@dataclasses.dataclass
|
|
27
|
+
@pydantic.dataclasses.dataclass(config={"arbitrary_types_allowed": True})
|
|
19
28
|
class LoadCentreCollectComposite(RobotLoadThenCentreComposite, RotationScanComposite):
|
|
20
29
|
"""Composite that provides access to the required devices."""
|
|
21
30
|
|
|
22
|
-
|
|
31
|
+
@property
|
|
32
|
+
def sample_motors(self) -> Smargon:
|
|
33
|
+
return self.smargon
|
|
23
34
|
|
|
24
35
|
|
|
25
36
|
def create_devices(context: BlueskyContext) -> LoadCentreCollectComposite:
|
|
@@ -27,11 +38,11 @@ def create_devices(context: BlueskyContext) -> LoadCentreCollectComposite:
|
|
|
27
38
|
return device_composite_from_context(context, LoadCentreCollectComposite)
|
|
28
39
|
|
|
29
40
|
|
|
30
|
-
def
|
|
41
|
+
def load_centre_collect_full(
|
|
31
42
|
composite: LoadCentreCollectComposite,
|
|
32
|
-
|
|
43
|
+
parameters: LoadCentreCollect,
|
|
33
44
|
oav_params: OAVParameters | None = None,
|
|
34
|
-
):
|
|
45
|
+
) -> MsgGenerator:
|
|
35
46
|
"""Attempt a complete data collection experiment, consisting of the following:
|
|
36
47
|
* Load the sample if necessary
|
|
37
48
|
* Move to the specified goniometer start angles
|
|
@@ -41,6 +52,37 @@ def load_centre_collect_full_plan(
|
|
|
41
52
|
"""
|
|
42
53
|
if not oav_params:
|
|
43
54
|
oav_params = OAVParameters(context="xrayCentring")
|
|
44
|
-
yield from robot_load_then_centre(composite, params.robot_load_then_centre)
|
|
45
55
|
|
|
46
|
-
|
|
56
|
+
flyscan_event_handler = XRayCentreEventHandler()
|
|
57
|
+
yield from subs_wrapper(
|
|
58
|
+
robot_load_then_xray_centre(composite, parameters.robot_load_then_centre),
|
|
59
|
+
flyscan_event_handler,
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
assert (
|
|
63
|
+
flyscan_event_handler.xray_centre_results
|
|
64
|
+
), "Flyscan result event not received or no crystal found and exception not raised"
|
|
65
|
+
|
|
66
|
+
selection_func = flyscan_result.resolve_selection_fn(parameters.selection_params)
|
|
67
|
+
hits: Sequence[flyscan_result.XRayCentreResult] = selection_func(
|
|
68
|
+
flyscan_event_handler.xray_centre_results
|
|
69
|
+
)
|
|
70
|
+
LOGGER.info(
|
|
71
|
+
f"Selected hits {hits} using {selection_func}, args={parameters.selection_params}"
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
multi_rotation = parameters.multi_rotation_scan
|
|
75
|
+
rotation_template = multi_rotation.rotation_scans.copy()
|
|
76
|
+
|
|
77
|
+
multi_rotation.rotation_scans.clear()
|
|
78
|
+
|
|
79
|
+
for hit in hits:
|
|
80
|
+
for rot in rotation_template:
|
|
81
|
+
combination = rot.model_copy()
|
|
82
|
+
combination.x_start_um, combination.y_start_um, combination.z_start_um = (
|
|
83
|
+
axis * 1000 for axis in hit.centre_of_mass_mm
|
|
84
|
+
)
|
|
85
|
+
multi_rotation.rotation_scans.append(combination)
|
|
86
|
+
multi_rotation = MultiRotationScan.model_validate(multi_rotation)
|
|
87
|
+
|
|
88
|
+
yield from multi_rotation_scan(composite, multi_rotation, oav_params)
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
import dataclasses
|
|
4
3
|
import math
|
|
5
4
|
from typing import TYPE_CHECKING
|
|
6
5
|
|
|
7
6
|
import bluesky.plan_stubs as bps
|
|
8
7
|
import numpy as np
|
|
8
|
+
import pydantic
|
|
9
9
|
from blueapi.core import BlueskyContext
|
|
10
10
|
from dodal.devices.backlight import Backlight
|
|
11
11
|
from dodal.devices.oav.oav_detector import OAV
|
|
@@ -26,7 +26,7 @@ if TYPE_CHECKING:
|
|
|
26
26
|
from dodal.devices.oav.oav_parameters import OAVParameters
|
|
27
27
|
|
|
28
28
|
|
|
29
|
-
@dataclasses.dataclass
|
|
29
|
+
@pydantic.dataclasses.dataclass(config={"arbitrary_types_allowed": True})
|
|
30
30
|
class OavGridDetectionComposite:
|
|
31
31
|
"""All devices which are directly or indirectly required by this plan"""
|
|
32
32
|
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
from datetime import datetime
|
|
2
2
|
from typing import Protocol
|
|
3
3
|
|
|
4
|
-
from blueapi.core import MsgGenerator
|
|
5
4
|
from bluesky import plan_stubs as bps
|
|
6
|
-
from
|
|
5
|
+
from bluesky.utils import MsgGenerator
|
|
6
|
+
from dodal.devices.aperturescatterguard import ApertureScatterguard
|
|
7
7
|
from dodal.devices.backlight import Backlight, BacklightPosition
|
|
8
8
|
from dodal.devices.oav.oav_detector import OAV
|
|
9
9
|
from dodal.devices.oav.oav_parameters import OAVParameters
|
|
@@ -33,22 +33,16 @@ def setup_beamline_for_OAV(
|
|
|
33
33
|
max_vel = yield from bps.rd(smargon.omega.max_velocity)
|
|
34
34
|
yield from bps.abs_set(smargon.omega.velocity, max_vel, group=group)
|
|
35
35
|
yield from bps.abs_set(backlight, BacklightPosition.IN, group=group)
|
|
36
|
-
yield from bps.
|
|
37
|
-
aperture_scatterguard,
|
|
38
|
-
ApertureValue.ROBOT_LOAD,
|
|
39
|
-
group=group,
|
|
40
|
-
)
|
|
36
|
+
yield from bps.trigger(aperture_scatterguard.move_out, group=group)
|
|
41
37
|
|
|
42
38
|
|
|
43
39
|
def oav_snapshot_plan(
|
|
44
40
|
composite: OavSnapshotComposite,
|
|
45
41
|
parameters: WithSnapshot,
|
|
46
42
|
oav_parameters: OAVParameters,
|
|
47
|
-
wait: bool = True,
|
|
48
43
|
) -> MsgGenerator:
|
|
49
44
|
if not parameters.take_snapshots:
|
|
50
45
|
return
|
|
51
|
-
yield from bps.wait(group=CONST.WAIT.READY_FOR_OAV)
|
|
52
46
|
yield from _setup_oav(composite, parameters, oav_parameters)
|
|
53
47
|
for omega in parameters.snapshot_omegas_deg or []:
|
|
54
48
|
yield from _take_oav_snapshot(composite, omega)
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import dataclasses
|
|
2
1
|
from enum import Enum
|
|
3
2
|
|
|
4
3
|
import bluesky.plan_stubs as bps
|
|
5
4
|
import bluesky.preprocessors as bpp
|
|
6
5
|
import numpy as np
|
|
6
|
+
import pydantic
|
|
7
7
|
from blueapi.core import BlueskyContext
|
|
8
8
|
from dodal.devices.attenuator import Attenuator
|
|
9
9
|
from dodal.devices.xspress3.xspress3 import Xspress3
|
|
@@ -22,7 +22,7 @@ class Direction(Enum):
|
|
|
22
22
|
NEGATIVE = "negative"
|
|
23
23
|
|
|
24
24
|
|
|
25
|
-
@dataclasses.dataclass
|
|
25
|
+
@pydantic.dataclasses.dataclass(config={"arbitrary_types_allowed": True})
|
|
26
26
|
class OptimizeAttenuationComposite:
|
|
27
27
|
"""All devices which are directly or indirectly required by this plan"""
|
|
28
28
|
|