mx-bluesky 1.5.6__py3-none-any.whl → 1.5.8__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/i02_1/parameters/__init__.py +0 -0
- mx_bluesky/beamlines/i02_1/parameters/gridscan.py +35 -0
- mx_bluesky/beamlines/i04/experiment_plans/i04_grid_detect_then_xray_centre_plan.py +6 -3
- mx_bluesky/beamlines/i04/redis_to_murko_forwarder.py +3 -1
- mx_bluesky/beamlines/i04/thawing_plan.py +15 -8
- mx_bluesky/beamlines/i24/jungfrau_commissioning/do_external_acquisition.py +44 -0
- mx_bluesky/beamlines/i24/jungfrau_commissioning/do_internal_acquisition.py +46 -0
- mx_bluesky/beamlines/i24/jungfrau_commissioning/plan_utils.py +73 -0
- mx_bluesky/common/device_setup_plans/robot_load_unload.py +2 -1
- mx_bluesky/common/experiment_plans/common_flyscan_xray_centre_plan.py +4 -3
- mx_bluesky/common/experiment_plans/inner_plans/do_fgs.py +7 -8
- mx_bluesky/common/external_interaction/callbacks/common/ispyb_callback_base.py +6 -6
- mx_bluesky/common/external_interaction/callbacks/common/zocalo_callback.py +30 -22
- mx_bluesky/common/external_interaction/callbacks/xray_centre/ispyb_callback.py +73 -15
- mx_bluesky/common/external_interaction/callbacks/xray_centre/ispyb_mapping.py +0 -20
- mx_bluesky/common/parameters/device_composites.py +2 -2
- mx_bluesky/common/parameters/gridscan.py +67 -49
- mx_bluesky/hyperion/device_setup_plans/smargon.py +13 -8
- mx_bluesky/hyperion/experiment_plans/robot_load_then_centre_plan.py +2 -2
- mx_bluesky/hyperion/external_interaction/callbacks/__main__.py +10 -2
- mx_bluesky/hyperion/external_interaction/callbacks/rotation/ispyb_callback.py +37 -1
- mx_bluesky/hyperion/parameters/device_composites.py +2 -2
- mx_bluesky/hyperion/parameters/gridscan.py +3 -3
- {mx_bluesky-1.5.6.dist-info → mx_bluesky-1.5.8.dist-info}/METADATA +2 -2
- {mx_bluesky-1.5.6.dist-info → mx_bluesky-1.5.8.dist-info}/RECORD +30 -25
- {mx_bluesky-1.5.6.dist-info → mx_bluesky-1.5.8.dist-info}/WHEEL +0 -0
- {mx_bluesky-1.5.6.dist-info → mx_bluesky-1.5.8.dist-info}/entry_points.txt +0 -0
- {mx_bluesky-1.5.6.dist-info → mx_bluesky-1.5.8.dist-info}/licenses/LICENSE +0 -0
- {mx_bluesky-1.5.6.dist-info → mx_bluesky-1.5.8.dist-info}/top_level.txt +0 -0
|
@@ -1,23 +1,28 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
from collections.abc import Callable, Sequence
|
|
4
|
+
from enum import StrEnum
|
|
5
|
+
from math import isclose
|
|
4
6
|
from time import time
|
|
5
7
|
from typing import TYPE_CHECKING, Any, TypeVar
|
|
6
8
|
|
|
7
9
|
from bluesky import preprocessors as bpp
|
|
8
10
|
from bluesky.utils import MsgGenerator, make_decorator
|
|
11
|
+
from dodal.devices.zocalo import ZocaloStartInfo
|
|
9
12
|
|
|
10
13
|
from mx_bluesky.common.external_interaction.callbacks.common.ispyb_callback_base import (
|
|
11
14
|
BaseISPyBCallback,
|
|
15
|
+
D,
|
|
12
16
|
)
|
|
13
17
|
from mx_bluesky.common.external_interaction.callbacks.common.ispyb_mapping import (
|
|
14
18
|
populate_data_collection_group,
|
|
15
19
|
populate_remaining_data_collection_info,
|
|
16
20
|
)
|
|
21
|
+
from mx_bluesky.common.external_interaction.callbacks.common.zocalo_callback import (
|
|
22
|
+
ZocaloInfoGenerator,
|
|
23
|
+
)
|
|
17
24
|
from mx_bluesky.common.external_interaction.callbacks.xray_centre.ispyb_mapping import (
|
|
18
25
|
construct_comment_for_gridscan,
|
|
19
|
-
populate_xy_data_collection_info,
|
|
20
|
-
populate_xz_data_collection_info,
|
|
21
26
|
)
|
|
22
27
|
from mx_bluesky.common.external_interaction.ispyb.data_model import (
|
|
23
28
|
DataCollectionGridInfo,
|
|
@@ -33,14 +38,21 @@ from mx_bluesky.common.external_interaction.ispyb.ispyb_store import (
|
|
|
33
38
|
)
|
|
34
39
|
from mx_bluesky.common.parameters.components import DiffractionExperimentWithSample
|
|
35
40
|
from mx_bluesky.common.parameters.constants import DocDescriptorNames, PlanNameConstants
|
|
36
|
-
from mx_bluesky.common.parameters.gridscan import
|
|
37
|
-
GridCommon,
|
|
38
|
-
)
|
|
41
|
+
from mx_bluesky.common.parameters.gridscan import GridCommon
|
|
39
42
|
from mx_bluesky.common.utils.exceptions import (
|
|
40
43
|
ISPyBDepositionNotMade,
|
|
41
44
|
SampleException,
|
|
42
45
|
)
|
|
43
46
|
from mx_bluesky.common.utils.log import ISPYB_ZOCALO_CALLBACK_LOGGER, set_dcgid_tag
|
|
47
|
+
from mx_bluesky.common.utils.utils import number_of_frames_from_scan_spec
|
|
48
|
+
|
|
49
|
+
OMEGA_TOLERANCE = 1
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class GridscanPlane(StrEnum):
|
|
53
|
+
OMEGA_XY = "0"
|
|
54
|
+
OMEGA_XZ = "90"
|
|
55
|
+
|
|
44
56
|
|
|
45
57
|
if TYPE_CHECKING:
|
|
46
58
|
from event_model import Event, RunStart, RunStop
|
|
@@ -89,10 +101,10 @@ class GridscanISPyBCallback(BaseISPyBCallback):
|
|
|
89
101
|
) -> None:
|
|
90
102
|
super().__init__(emit=emit)
|
|
91
103
|
self.ispyb: StoreInIspyb
|
|
92
|
-
self.ispyb_ids: IspybIds = IspybIds()
|
|
93
104
|
self.param_type = param_type
|
|
94
105
|
self._start_of_fgs_uid: str | None = None
|
|
95
106
|
self._processing_start_time: float | None = None
|
|
107
|
+
self._grid_plane_to_id_map: dict[GridscanPlane, int] = {}
|
|
96
108
|
self.data_collection_group_info: DataCollectionGroupInfo | None
|
|
97
109
|
|
|
98
110
|
def activity_gated_start(self, doc: RunStart):
|
|
@@ -108,6 +120,7 @@ class GridscanISPyBCallback(BaseISPyBCallback):
|
|
|
108
120
|
mx_bluesky_parameters = doc.get("mx_bluesky_parameters")
|
|
109
121
|
assert isinstance(mx_bluesky_parameters, str)
|
|
110
122
|
self.params = self.param_type.model_validate_json(mx_bluesky_parameters)
|
|
123
|
+
assert isinstance(self.params, DiffractionExperimentWithSample)
|
|
111
124
|
self.ispyb = StoreInIspyb(self.ispyb_config)
|
|
112
125
|
self.data_collection_group_info = populate_data_collection_group(
|
|
113
126
|
self.params
|
|
@@ -118,8 +131,8 @@ class GridscanISPyBCallback(BaseISPyBCallback):
|
|
|
118
131
|
data_collection_info=populate_remaining_data_collection_info(
|
|
119
132
|
"MX-Bluesky: Xray centring 1 -",
|
|
120
133
|
None,
|
|
121
|
-
|
|
122
|
-
self.params.detector_params,
|
|
134
|
+
DataCollectionInfo(
|
|
135
|
+
data_collection_number=self.params.detector_params.run_number,
|
|
123
136
|
),
|
|
124
137
|
self.params,
|
|
125
138
|
),
|
|
@@ -128,7 +141,11 @@ class GridscanISPyBCallback(BaseISPyBCallback):
|
|
|
128
141
|
data_collection_info=populate_remaining_data_collection_info(
|
|
129
142
|
"MX-Bluesky: Xray centring 2 -",
|
|
130
143
|
None,
|
|
131
|
-
|
|
144
|
+
DataCollectionInfo(
|
|
145
|
+
data_collection_number=(
|
|
146
|
+
self.params.detector_params.run_number + 1
|
|
147
|
+
),
|
|
148
|
+
),
|
|
132
149
|
self.params,
|
|
133
150
|
)
|
|
134
151
|
),
|
|
@@ -175,7 +192,6 @@ class GridscanISPyBCallback(BaseISPyBCallback):
|
|
|
175
192
|
assert self.params, "ISPyB handler didn't receive parameters!"
|
|
176
193
|
assert self.data_collection_group_info, "No data collection group"
|
|
177
194
|
data = doc["data"]
|
|
178
|
-
data_collection_id = None
|
|
179
195
|
data_collection_info = DataCollectionInfo(
|
|
180
196
|
xtal_snapshot1=data.get("oav-grid_snapshot-last_path_full_overlay"),
|
|
181
197
|
xtal_snapshot2=data.get("oav-grid_snapshot-last_path_outer"),
|
|
@@ -214,10 +230,9 @@ class GridscanISPyBCallback(BaseISPyBCallback):
|
|
|
214
230
|
f"by {data_collection_grid_info.steps_y} "
|
|
215
231
|
)
|
|
216
232
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
]
|
|
233
|
+
data_collection_id = self.ispyb_ids.data_collection_ids[
|
|
234
|
+
self._oav_snapshot_event_idx
|
|
235
|
+
]
|
|
221
236
|
self._populate_axis_info(data_collection_info, doc["data"]["smargon-omega"])
|
|
222
237
|
|
|
223
238
|
scan_data_info = ScanDataInfo(
|
|
@@ -228,6 +243,11 @@ class GridscanISPyBCallback(BaseISPyBCallback):
|
|
|
228
243
|
ISPYB_ZOCALO_CALLBACK_LOGGER.info(
|
|
229
244
|
"Updating ispyb data collection after oav snapshot."
|
|
230
245
|
)
|
|
246
|
+
grid_plane = _smargon_omega_to_xyxz_plane(doc["data"]["smargon-omega"])
|
|
247
|
+
# Snapshots may be triggered in a different order to gridscans, so save
|
|
248
|
+
# the mapping to the data collection id in order to trigger Zocalo correctly.
|
|
249
|
+
self._grid_plane_to_id_map[grid_plane] = data_collection_id
|
|
250
|
+
|
|
231
251
|
self._oav_snapshot_event_idx += 1
|
|
232
252
|
return [scan_data_info]
|
|
233
253
|
|
|
@@ -294,5 +314,43 @@ class GridscanISPyBCallback(BaseISPyBCallback):
|
|
|
294
314
|
self.ispyb_ids.data_collection_group_id,
|
|
295
315
|
)
|
|
296
316
|
self.data_collection_group_info = None
|
|
317
|
+
self._grid_plane_to_id_map.clear()
|
|
297
318
|
return super().activity_gated_stop(doc)
|
|
298
|
-
return self.
|
|
319
|
+
return self.tag_doc(doc)
|
|
320
|
+
|
|
321
|
+
def tag_doc(self, doc: D) -> D:
|
|
322
|
+
doc = super().tag_doc(doc)
|
|
323
|
+
assert isinstance(doc, dict)
|
|
324
|
+
if self._grid_plane_to_id_map:
|
|
325
|
+
doc["grid_plane_to_id_map"] = self._grid_plane_to_id_map
|
|
326
|
+
return doc # type: ignore
|
|
327
|
+
|
|
328
|
+
|
|
329
|
+
def generate_start_info_from_omega_map() -> ZocaloInfoGenerator:
|
|
330
|
+
"""
|
|
331
|
+
Generate the zocalo trigger info from bluesky runs where the frame number is
|
|
332
|
+
computed using metadata added to the document by the ISPyB callback and the
|
|
333
|
+
run start which together can be used to determine the correct frame numbering.
|
|
334
|
+
"""
|
|
335
|
+
doc = yield []
|
|
336
|
+
omega_to_scan_spec = doc["omega_to_scan_spec"]
|
|
337
|
+
start_frame = 0
|
|
338
|
+
infos = []
|
|
339
|
+
for i, omega in enumerate([GridscanPlane.OMEGA_XY, GridscanPlane.OMEGA_XZ]):
|
|
340
|
+
frames = number_of_frames_from_scan_spec(omega_to_scan_spec[omega])
|
|
341
|
+
infos.append(
|
|
342
|
+
ZocaloStartInfo(
|
|
343
|
+
doc["grid_plane_to_id_map"][omega], None, start_frame, frames, i
|
|
344
|
+
)
|
|
345
|
+
)
|
|
346
|
+
start_frame += frames
|
|
347
|
+
yield infos
|
|
348
|
+
|
|
349
|
+
|
|
350
|
+
def _smargon_omega_to_xyxz_plane(smargon_omega: float) -> GridscanPlane:
|
|
351
|
+
modulo_180 = abs(smargon_omega) % 180
|
|
352
|
+
is_xy = isclose(modulo_180, 0, abs_tol=OMEGA_TOLERANCE)
|
|
353
|
+
assert is_xy or isclose(modulo_180, 90, abs_tol=OMEGA_TOLERANCE), (
|
|
354
|
+
f"Smargon snapshot omega not in tolerance of compass point {smargon_omega}"
|
|
355
|
+
)
|
|
356
|
+
return GridscanPlane.OMEGA_XY if is_xy else GridscanPlane.OMEGA_XZ
|
|
@@ -1,33 +1,13 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import numpy
|
|
4
|
-
from dodal.devices.detector import DetectorParams
|
|
5
4
|
from dodal.devices.oav import utils as oav_utils
|
|
6
5
|
|
|
7
6
|
from mx_bluesky.common.external_interaction.ispyb.data_model import (
|
|
8
7
|
DataCollectionGridInfo,
|
|
9
|
-
DataCollectionInfo,
|
|
10
8
|
)
|
|
11
9
|
|
|
12
10
|
|
|
13
|
-
def populate_xz_data_collection_info(detector_params: DetectorParams):
|
|
14
|
-
assert (
|
|
15
|
-
detector_params.omega_start is not None
|
|
16
|
-
and detector_params.run_number is not None
|
|
17
|
-
), "StoreGridscanInIspyb failed to get parameters"
|
|
18
|
-
run_number = detector_params.run_number + 1
|
|
19
|
-
info = DataCollectionInfo(
|
|
20
|
-
data_collection_number=run_number,
|
|
21
|
-
)
|
|
22
|
-
return info
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
def populate_xy_data_collection_info(detector_params: DetectorParams):
|
|
26
|
-
return DataCollectionInfo(
|
|
27
|
-
data_collection_number=detector_params.run_number,
|
|
28
|
-
)
|
|
29
|
-
|
|
30
|
-
|
|
31
11
|
def construct_comment_for_gridscan(grid_info: DataCollectionGridInfo) -> str:
|
|
32
12
|
assert grid_info is not None, "StoreGridScanInIspyb failed to get parameters"
|
|
33
13
|
|
|
@@ -8,7 +8,7 @@ from dodal.devices.common_dcm import BaseDCM
|
|
|
8
8
|
from dodal.devices.detector.detector_motion import DetectorMotion
|
|
9
9
|
from dodal.devices.eiger import EigerDetector
|
|
10
10
|
from dodal.devices.fast_grid_scan import (
|
|
11
|
-
|
|
11
|
+
ZebraFastGridScanThreeD,
|
|
12
12
|
)
|
|
13
13
|
from dodal.devices.flux import Flux
|
|
14
14
|
from dodal.devices.mx_phase1.beamstop import Beamstop
|
|
@@ -53,7 +53,7 @@ class GridDetectThenXRayCentreComposite(FlyScanEssentialDevices):
|
|
|
53
53
|
beamstop: Beamstop
|
|
54
54
|
dcm: BaseDCM
|
|
55
55
|
detector_motion: DetectorMotion
|
|
56
|
-
zebra_fast_grid_scan:
|
|
56
|
+
zebra_fast_grid_scan: ZebraFastGridScanThreeD
|
|
57
57
|
flux: Flux
|
|
58
58
|
oav: OAV
|
|
59
59
|
pin_tip_detection: PinTipDetection
|
|
@@ -1,15 +1,19 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
from abc import abstractmethod
|
|
4
|
+
from typing import Generic, TypeVar
|
|
5
|
+
|
|
3
6
|
from dodal.devices.aperturescatterguard import ApertureValue
|
|
4
7
|
from dodal.devices.detector.det_dim_constants import EIGER2_X_9M_SIZE, EIGER2_X_16M_SIZE
|
|
5
8
|
from dodal.devices.detector.detector import DetectorParams
|
|
6
9
|
from dodal.devices.fast_grid_scan import (
|
|
7
|
-
|
|
10
|
+
GridScanParamsCommon,
|
|
11
|
+
ZebraGridScanParamsThreeD,
|
|
8
12
|
)
|
|
9
13
|
from dodal.utils import get_beamline_name
|
|
10
14
|
from pydantic import Field, PrivateAttr
|
|
11
15
|
from scanspec.core import Path as ScanPath
|
|
12
|
-
from scanspec.specs import Line, Static
|
|
16
|
+
from scanspec.specs import Concat, Line, Product, Static
|
|
13
17
|
|
|
14
18
|
from mx_bluesky.common.parameters.components import (
|
|
15
19
|
DiffractionExperimentWithSample,
|
|
@@ -33,6 +37,10 @@ DETECTOR_SIZE_PER_BEAMLINE = {
|
|
|
33
37
|
"i04": EIGER2_X_16M_SIZE,
|
|
34
38
|
}
|
|
35
39
|
|
|
40
|
+
GridScanParamType = TypeVar(
|
|
41
|
+
"GridScanParamType", bound=GridScanParamsCommon, covariant=True
|
|
42
|
+
)
|
|
43
|
+
|
|
36
44
|
|
|
37
45
|
class GridCommon(
|
|
38
46
|
DiffractionExperimentWithSample,
|
|
@@ -87,36 +95,80 @@ class GridCommon(
|
|
|
87
95
|
)
|
|
88
96
|
|
|
89
97
|
|
|
90
|
-
class SpecifiedGrid(XyzStarts, WithScan):
|
|
98
|
+
class SpecifiedGrid(GridCommon, XyzStarts, WithScan, Generic[GridScanParamType]):
|
|
91
99
|
"""A specified grid is one which has defined values for the start position,
|
|
92
100
|
grid and box sizes, etc., as opposed to parameters for a plan which will create
|
|
93
101
|
those parameters at some point (e.g. through optical pin detection)."""
|
|
94
102
|
|
|
103
|
+
grid1_omega_deg: float = Field(default=GridscanParamConstants.OMEGA_1)
|
|
104
|
+
x_step_size_um: float = Field(default=GridscanParamConstants.BOX_WIDTH_UM)
|
|
105
|
+
y_step_size_um: float = Field(default=GridscanParamConstants.BOX_WIDTH_UM)
|
|
106
|
+
x_steps: int = Field(gt=0)
|
|
107
|
+
y_steps: int = Field(gt=0)
|
|
108
|
+
_set_stub_offsets: bool = PrivateAttr(default_factory=lambda: False)
|
|
109
|
+
|
|
110
|
+
@property
|
|
111
|
+
@abstractmethod
|
|
112
|
+
def FGS_params(self) -> GridScanParamType: ...
|
|
113
|
+
|
|
114
|
+
def do_set_stub_offsets(self, value: bool):
|
|
115
|
+
self._set_stub_offsets = value
|
|
116
|
+
|
|
117
|
+
@property
|
|
118
|
+
def grid_1_spec(self):
|
|
119
|
+
x_end = self.x_start_um + self.x_step_size_um * (self.x_steps - 1)
|
|
120
|
+
y1_end = self.y_start_um + self.y_step_size_um * (self.y_steps - 1)
|
|
121
|
+
grid_1_x = Line("sam_x", self.x_start_um, x_end, self.x_steps)
|
|
122
|
+
grid_1_y = Line("sam_y", self.y_start_um, y1_end, self.y_steps)
|
|
123
|
+
grid_1_z = Static("sam_z", self.z_start_um)
|
|
124
|
+
return grid_1_y.zip(grid_1_z) * ~grid_1_x
|
|
125
|
+
|
|
126
|
+
@property
|
|
127
|
+
def scan_indices(self) -> list[int]:
|
|
128
|
+
"""The first index of each gridscan, useful for writing nexus files/VDS"""
|
|
129
|
+
return [
|
|
130
|
+
0,
|
|
131
|
+
len(ScanPath(self.grid_1_spec.calculate()).consume().midpoints["sam_x"]),
|
|
132
|
+
]
|
|
133
|
+
|
|
134
|
+
@property
|
|
135
|
+
@abstractmethod
|
|
136
|
+
def scan_spec(self) -> Product[str] | Concat[str]:
|
|
137
|
+
"""A fully specified ScanSpec object representing all grids, with x, y, z and
|
|
138
|
+
omega positions."""
|
|
139
|
+
|
|
140
|
+
@property
|
|
141
|
+
def scan_points(self):
|
|
142
|
+
"""A list of all the points in the scan_spec."""
|
|
143
|
+
return ScanPath(self.scan_spec.calculate()).consume().midpoints
|
|
144
|
+
|
|
145
|
+
@property
|
|
146
|
+
def scan_points_first_grid(self):
|
|
147
|
+
"""A list of all the points in the first grid scan."""
|
|
148
|
+
return ScanPath(self.grid_1_spec.calculate()).consume().midpoints
|
|
149
|
+
|
|
150
|
+
@property
|
|
151
|
+
def num_images(self) -> int:
|
|
152
|
+
return len(self.scan_points["sam_x"])
|
|
153
|
+
|
|
95
154
|
|
|
96
155
|
class SpecifiedThreeDGridScan(
|
|
97
|
-
|
|
98
|
-
SpecifiedGrid,
|
|
156
|
+
SpecifiedGrid[ZebraGridScanParamsThreeD],
|
|
99
157
|
SplitScan,
|
|
100
158
|
WithOptionalEnergyChange,
|
|
101
159
|
):
|
|
102
160
|
"""Parameters representing a so-called 3D grid scan, which consists of doing a
|
|
103
161
|
gridscan in X and Y, followed by one in X and Z."""
|
|
104
162
|
|
|
105
|
-
|
|
106
|
-
grid2_omega_deg: float = Field(default=GridscanParamConstants.OMEGA_2)
|
|
107
|
-
x_step_size_um: float = Field(default=GridscanParamConstants.BOX_WIDTH_UM)
|
|
108
|
-
y_step_size_um: float = Field(default=GridscanParamConstants.BOX_WIDTH_UM)
|
|
163
|
+
z_steps: int = Field(gt=0)
|
|
109
164
|
z_step_size_um: float = Field(default=GridscanParamConstants.BOX_WIDTH_UM)
|
|
110
165
|
y2_start_um: float
|
|
111
166
|
z2_start_um: float
|
|
112
|
-
|
|
113
|
-
y_steps: int = Field(gt=0)
|
|
114
|
-
z_steps: int = Field(gt=0)
|
|
115
|
-
_set_stub_offsets: bool = PrivateAttr(default_factory=lambda: False)
|
|
167
|
+
grid2_omega_deg: float = Field(default=GridscanParamConstants.OMEGA_2)
|
|
116
168
|
|
|
117
169
|
@property
|
|
118
|
-
def FGS_params(self) ->
|
|
119
|
-
return
|
|
170
|
+
def FGS_params(self) -> ZebraGridScanParamsThreeD:
|
|
171
|
+
return ZebraGridScanParamsThreeD(
|
|
120
172
|
x_steps=self.x_steps,
|
|
121
173
|
y_steps=self.y_steps,
|
|
122
174
|
z_steps=self.z_steps,
|
|
@@ -133,18 +185,6 @@ class SpecifiedThreeDGridScan(
|
|
|
133
185
|
transmission_fraction=self.transmission_frac,
|
|
134
186
|
)
|
|
135
187
|
|
|
136
|
-
def do_set_stub_offsets(self, value: bool):
|
|
137
|
-
self._set_stub_offsets = value
|
|
138
|
-
|
|
139
|
-
@property
|
|
140
|
-
def grid_1_spec(self):
|
|
141
|
-
x_end = self.x_start_um + self.x_step_size_um * (self.x_steps - 1)
|
|
142
|
-
y1_end = self.y_start_um + self.y_step_size_um * (self.y_steps - 1)
|
|
143
|
-
grid_1_x = Line("sam_x", self.x_start_um, x_end, self.x_steps)
|
|
144
|
-
grid_1_y = Line("sam_y", self.y_start_um, y1_end, self.y_steps)
|
|
145
|
-
grid_1_z = Static("sam_z", self.z_start_um)
|
|
146
|
-
return grid_1_y.zip(grid_1_z) * ~grid_1_x
|
|
147
|
-
|
|
148
188
|
@property
|
|
149
189
|
def grid_2_spec(self):
|
|
150
190
|
x_end = self.x_start_um + self.x_step_size_um * (self.x_steps - 1)
|
|
@@ -154,35 +194,13 @@ class SpecifiedThreeDGridScan(
|
|
|
154
194
|
grid_2_y = Static("sam_y", self.y2_start_um)
|
|
155
195
|
return grid_2_z.zip(grid_2_y) * ~grid_2_x
|
|
156
196
|
|
|
157
|
-
@property
|
|
158
|
-
def scan_indices(self):
|
|
159
|
-
"""The first index of each gridscan, useful for writing nexus files/VDS"""
|
|
160
|
-
return [
|
|
161
|
-
0,
|
|
162
|
-
len(ScanPath(self.grid_1_spec.calculate()).consume().midpoints["sam_x"]),
|
|
163
|
-
]
|
|
164
|
-
|
|
165
197
|
@property
|
|
166
198
|
def scan_spec(self):
|
|
167
199
|
"""A fully specified ScanSpec object representing both grids, with x, y, z and
|
|
168
200
|
omega positions."""
|
|
169
201
|
return self.grid_1_spec.concat(self.grid_2_spec)
|
|
170
202
|
|
|
171
|
-
@property
|
|
172
|
-
def scan_points(self):
|
|
173
|
-
"""A list of all the points in the scan_spec."""
|
|
174
|
-
return ScanPath(self.scan_spec.calculate()).consume().midpoints
|
|
175
|
-
|
|
176
|
-
@property
|
|
177
|
-
def scan_points_first_grid(self):
|
|
178
|
-
"""A list of all the points in the first grid scan."""
|
|
179
|
-
return ScanPath(self.grid_1_spec.calculate()).consume().midpoints
|
|
180
|
-
|
|
181
203
|
@property
|
|
182
204
|
def scan_points_second_grid(self):
|
|
183
205
|
"""A list of all the points in the second grid scan."""
|
|
184
206
|
return ScanPath(self.grid_2_spec.calculate()).consume().midpoints
|
|
185
|
-
|
|
186
|
-
@property
|
|
187
|
-
def num_images(self) -> int:
|
|
188
|
-
return len(self.scan_points["sam_x"])
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import numpy as np
|
|
2
2
|
from bluesky import plan_stubs as bps
|
|
3
|
+
from bluesky.utils import FailedStatus
|
|
3
4
|
from dodal.devices.smargon import CombinedMove, Smargon
|
|
5
|
+
from ophyd_async.epics.motor import MotorLimitsException
|
|
4
6
|
|
|
5
7
|
from mx_bluesky.common.utils.exceptions import SampleException
|
|
6
8
|
|
|
@@ -9,12 +11,15 @@ def move_smargon_warn_on_out_of_range(
|
|
|
9
11
|
smargon: Smargon, position: np.ndarray | list[float] | tuple[float, float, float]
|
|
10
12
|
):
|
|
11
13
|
"""Throws a SampleException if the specified position is out of range for the
|
|
12
|
-
smargon. Otherwise moves to that position."""
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
"Pin tip centring failed - pin too long/short/bent and out of range"
|
|
14
|
+
smargon. Otherwise moves to that position. The check is from ophyd-async"""
|
|
15
|
+
try:
|
|
16
|
+
yield from bps.mv(
|
|
17
|
+
smargon, CombinedMove(x=position[0], y=position[1], z=position[2])
|
|
17
18
|
)
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
except FailedStatus as fs:
|
|
20
|
+
if isinstance(fs.__cause__, MotorLimitsException):
|
|
21
|
+
raise SampleException(
|
|
22
|
+
"Pin tip centring failed - pin too long/short/bent and out of range"
|
|
23
|
+
) from fs.__cause__
|
|
24
|
+
else:
|
|
25
|
+
raise fs
|
|
@@ -12,7 +12,7 @@ from dodal.devices.attenuator.attenuator import BinaryFilterAttenuator
|
|
|
12
12
|
from dodal.devices.backlight import Backlight
|
|
13
13
|
from dodal.devices.detector.detector_motion import DetectorMotion
|
|
14
14
|
from dodal.devices.eiger import EigerDetector
|
|
15
|
-
from dodal.devices.fast_grid_scan import PandAFastGridScan,
|
|
15
|
+
from dodal.devices.fast_grid_scan import PandAFastGridScan, ZebraFastGridScanThreeD
|
|
16
16
|
from dodal.devices.flux import Flux
|
|
17
17
|
from dodal.devices.focusing_mirror import FocusingMirrorWithStripes, MirrorVoltages
|
|
18
18
|
from dodal.devices.i03 import Beamstop
|
|
@@ -72,7 +72,7 @@ class RobotLoadThenCentreComposite:
|
|
|
72
72
|
backlight: Backlight
|
|
73
73
|
detector_motion: DetectorMotion
|
|
74
74
|
eiger: EigerDetector
|
|
75
|
-
zebra_fast_grid_scan:
|
|
75
|
+
zebra_fast_grid_scan: ZebraFastGridScanThreeD
|
|
76
76
|
flux: Flux
|
|
77
77
|
oav: OAV
|
|
78
78
|
pin_tip_detection: PinTipDetection
|
|
@@ -23,6 +23,7 @@ from mx_bluesky.common.external_interaction.callbacks.sample_handling.sample_han
|
|
|
23
23
|
)
|
|
24
24
|
from mx_bluesky.common.external_interaction.callbacks.xray_centre.ispyb_callback import (
|
|
25
25
|
GridscanISPyBCallback,
|
|
26
|
+
generate_start_info_from_omega_map,
|
|
26
27
|
)
|
|
27
28
|
from mx_bluesky.common.external_interaction.callbacks.xray_centre.nexus_callback import (
|
|
28
29
|
GridscanNexusFileCallback,
|
|
@@ -41,6 +42,7 @@ from mx_bluesky.hyperion.external_interaction.callbacks.robot_actions.ispyb_call
|
|
|
41
42
|
)
|
|
42
43
|
from mx_bluesky.hyperion.external_interaction.callbacks.rotation.ispyb_callback import (
|
|
43
44
|
RotationISPyBCallback,
|
|
45
|
+
generate_start_info_from_ordered_runs,
|
|
44
46
|
)
|
|
45
47
|
from mx_bluesky.hyperion.external_interaction.callbacks.rotation.nexus_callback import (
|
|
46
48
|
RotationNexusFileCallback,
|
|
@@ -66,7 +68,9 @@ def create_gridscan_callbacks() -> tuple[
|
|
|
66
68
|
GridscanNexusFileCallback(param_type=HyperionSpecifiedThreeDGridScan),
|
|
67
69
|
GridscanISPyBCallback(
|
|
68
70
|
param_type=GridCommonWithHyperionDetectorParams,
|
|
69
|
-
emit=ZocaloCallback(
|
|
71
|
+
emit=ZocaloCallback(
|
|
72
|
+
CONST.PLAN.DO_FGS, CONST.ZOCALO_ENV, generate_start_info_from_omega_map
|
|
73
|
+
),
|
|
70
74
|
),
|
|
71
75
|
)
|
|
72
76
|
|
|
@@ -77,7 +81,11 @@ def create_rotation_callbacks() -> tuple[
|
|
|
77
81
|
return (
|
|
78
82
|
RotationNexusFileCallback(),
|
|
79
83
|
RotationISPyBCallback(
|
|
80
|
-
emit=ZocaloCallback(
|
|
84
|
+
emit=ZocaloCallback(
|
|
85
|
+
CONST.PLAN.ROTATION_MULTI,
|
|
86
|
+
CONST.ZOCALO_ENV,
|
|
87
|
+
generate_start_info_from_ordered_runs,
|
|
88
|
+
)
|
|
81
89
|
),
|
|
82
90
|
)
|
|
83
91
|
|
|
@@ -3,6 +3,8 @@ from __future__ import annotations
|
|
|
3
3
|
from collections.abc import Callable, Sequence
|
|
4
4
|
from typing import TYPE_CHECKING, Any, cast
|
|
5
5
|
|
|
6
|
+
from dodal.devices.zocalo import ZocaloStartInfo
|
|
7
|
+
|
|
6
8
|
from mx_bluesky.common.external_interaction.callbacks.common.ispyb_callback_base import (
|
|
7
9
|
BaseISPyBCallback,
|
|
8
10
|
)
|
|
@@ -10,6 +12,9 @@ from mx_bluesky.common.external_interaction.callbacks.common.ispyb_mapping impor
|
|
|
10
12
|
populate_data_collection_group,
|
|
11
13
|
populate_remaining_data_collection_info,
|
|
12
14
|
)
|
|
15
|
+
from mx_bluesky.common.external_interaction.callbacks.common.zocalo_callback import (
|
|
16
|
+
ZocaloInfoGenerator,
|
|
17
|
+
)
|
|
13
18
|
from mx_bluesky.common.external_interaction.ispyb.data_model import (
|
|
14
19
|
DataCollectionInfo,
|
|
15
20
|
DataCollectionPositionInfo,
|
|
@@ -21,6 +26,7 @@ from mx_bluesky.common.external_interaction.ispyb.ispyb_store import (
|
|
|
21
26
|
)
|
|
22
27
|
from mx_bluesky.common.parameters.components import IspybExperimentType
|
|
23
28
|
from mx_bluesky.common.utils.log import ISPYB_ZOCALO_CALLBACK_LOGGER, set_dcgid_tag
|
|
29
|
+
from mx_bluesky.common.utils.utils import number_of_frames_from_scan_spec
|
|
24
30
|
from mx_bluesky.hyperion.external_interaction.callbacks.rotation.ispyb_mapping import (
|
|
25
31
|
populate_data_collection_info_for_rotation,
|
|
26
32
|
)
|
|
@@ -175,4 +181,34 @@ class RotationISPyBCallback(BaseISPyBCallback):
|
|
|
175
181
|
if doc.get("run_start") == self.uid_to_finalize_on:
|
|
176
182
|
self.uid_to_finalize_on = None
|
|
177
183
|
return super().activity_gated_stop(doc)
|
|
178
|
-
return self.
|
|
184
|
+
return self.tag_doc(doc)
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def generate_start_info_from_ordered_runs() -> ZocaloInfoGenerator:
|
|
188
|
+
"""
|
|
189
|
+
Generate the zocalo trigger info from bluesky runs where the frame number is
|
|
190
|
+
computed using the order in which the run start docs are received.
|
|
191
|
+
Yields:
|
|
192
|
+
A list of the ZocaloStartInfo objects extracted from the event
|
|
193
|
+
Send:
|
|
194
|
+
A dict containing the run start document
|
|
195
|
+
"""
|
|
196
|
+
start_frame = 0
|
|
197
|
+
doc = yield []
|
|
198
|
+
while doc:
|
|
199
|
+
zocalo_info = []
|
|
200
|
+
if (
|
|
201
|
+
isinstance(scan_points := doc.get("scan_points"), list)
|
|
202
|
+
and isinstance(ispyb_ids := doc.get("ispyb_dcids"), tuple)
|
|
203
|
+
and len(ispyb_ids) > 0
|
|
204
|
+
):
|
|
205
|
+
ISPYB_ZOCALO_CALLBACK_LOGGER.info(f"Zocalo triggering for {ispyb_ids}")
|
|
206
|
+
ids_and_shape = list(zip(ispyb_ids, scan_points, strict=False))
|
|
207
|
+
for idx, id_and_shape in enumerate(ids_and_shape):
|
|
208
|
+
id, shape = id_and_shape
|
|
209
|
+
num_frames = number_of_frames_from_scan_spec(shape)
|
|
210
|
+
zocalo_info.append(
|
|
211
|
+
ZocaloStartInfo(id, None, start_frame, num_frames, idx)
|
|
212
|
+
)
|
|
213
|
+
start_frame += num_frames
|
|
214
|
+
doc = yield zocalo_info
|
|
@@ -10,7 +10,7 @@ from dodal.devices.common_dcm import BaseDCM
|
|
|
10
10
|
from dodal.devices.eiger import EigerDetector
|
|
11
11
|
from dodal.devices.fast_grid_scan import (
|
|
12
12
|
PandAFastGridScan,
|
|
13
|
-
|
|
13
|
+
ZebraFastGridScanThreeD,
|
|
14
14
|
)
|
|
15
15
|
from dodal.devices.flux import Flux
|
|
16
16
|
from dodal.devices.robot import BartRobot
|
|
@@ -51,7 +51,7 @@ class HyperionFlyScanXRayCentreComposite(FlyScanEssentialDevices):
|
|
|
51
51
|
sample_shutter: ZebraShutter
|
|
52
52
|
backlight: Backlight
|
|
53
53
|
xbpm_feedback: XBPMFeedback
|
|
54
|
-
zebra_fast_grid_scan:
|
|
54
|
+
zebra_fast_grid_scan: ZebraFastGridScanThreeD
|
|
55
55
|
|
|
56
56
|
|
|
57
57
|
@pydantic.dataclasses.dataclass(config={"arbitrary_types_allowed": True})
|
|
@@ -2,7 +2,7 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
from dodal.devices.fast_grid_scan import (
|
|
4
4
|
PandAGridScanParams,
|
|
5
|
-
|
|
5
|
+
ZebraGridScanParamsThreeD,
|
|
6
6
|
)
|
|
7
7
|
|
|
8
8
|
from mx_bluesky.common.parameters.gridscan import (
|
|
@@ -44,8 +44,8 @@ class HyperionSpecifiedThreeDGridScan(SpecifiedThreeDGridScan):
|
|
|
44
44
|
|
|
45
45
|
# Relative to common grid scan, stub offsets are defined by config server
|
|
46
46
|
@property
|
|
47
|
-
def FGS_params(self) ->
|
|
48
|
-
return
|
|
47
|
+
def FGS_params(self) -> ZebraGridScanParamsThreeD:
|
|
48
|
+
return ZebraGridScanParamsThreeD(
|
|
49
49
|
x_steps=self.x_steps,
|
|
50
50
|
y_steps=self.y_steps,
|
|
51
51
|
z_steps=self.z_steps,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mx-bluesky
|
|
3
|
-
Version: 1.5.
|
|
3
|
+
Version: 1.5.8
|
|
4
4
|
Summary: Bluesky tools for MX Beamlines at DLS
|
|
5
5
|
Author-email: Dominic Oram <dominic.oram@diamond.ac.uk>
|
|
6
6
|
License: Apache License
|
|
@@ -241,7 +241,7 @@ Requires-Dist: blueapi>=0.15.0
|
|
|
241
241
|
Requires-Dist: ophyd>=1.10.5
|
|
242
242
|
Requires-Dist: ophyd-async>=0.10.0a2
|
|
243
243
|
Requires-Dist: bluesky>=1.13.1
|
|
244
|
-
Requires-Dist: dls-dodal==1.
|
|
244
|
+
Requires-Dist: dls-dodal==1.61.0
|
|
245
245
|
Provides-Extra: dev
|
|
246
246
|
Requires-Dist: black; extra == "dev"
|
|
247
247
|
Requires-Dist: build; extra == "dev"
|