mx-bluesky 1.4.8__py3-none-any.whl → 1.5.0__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/aithre_lasershaping/__init__.py +8 -0
- mx_bluesky/beamlines/aithre_lasershaping/beamline_safe.py +36 -0
- mx_bluesky/beamlines/aithre_lasershaping/goniometer_controls.py +43 -0
- mx_bluesky/beamlines/i24/serial/__init__.py +4 -2
- mx_bluesky/beamlines/i24/serial/blueapi_config.yaml +6 -1
- mx_bluesky/beamlines/i24/serial/dcid.py +5 -5
- mx_bluesky/beamlines/i24/serial/extruder/EX-gui-edm/DetStage.edl +2 -2
- mx_bluesky/beamlines/i24/serial/extruder/EX-gui-edm/DiamondExtruder-I24-py3v1.edl +9 -9
- mx_bluesky/beamlines/i24/serial/extruder/i24ssx_Extruder_Collect_py3v2.py +18 -3
- mx_bluesky/beamlines/i24/serial/fixed_target/FT-gui-edm/DetStage.edl +2 -2
- mx_bluesky/beamlines/i24/serial/fixed_target/FT-gui-edm/DiamondChipI24-py3v1.edl +14 -14
- mx_bluesky/beamlines/i24/serial/fixed_target/FT-gui-edm/pumpprobe-py3v1.edl +2 -2
- mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_Chip_Collect_py3v1.py +55 -4
- mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_Chip_Manager_py3v1.py +9 -2
- mx_bluesky/beamlines/i24/serial/setup_beamline/setup_beamline.py +0 -8
- mx_bluesky/beamlines/i24/serial/web_gui_plans/general_plans.py +77 -8
- mx_bluesky/common/device_setup_plans/manipulate_sample.py +9 -14
- mx_bluesky/common/device_setup_plans/utils.py +49 -0
- mx_bluesky/common/{plans → experiment_plans}/common_flyscan_xray_centre_plan.py +11 -19
- mx_bluesky/{hyperion/experiment_plans/grid_detect_then_xray_centre_plan.py → common/experiment_plans/common_grid_detect_then_xray_centre_plan.py} +108 -126
- mx_bluesky/common/{plans → experiment_plans}/inner_plans/do_fgs.py +1 -1
- mx_bluesky/common/experiment_plans/oav_grid_detection_plan.py +5 -13
- mx_bluesky/{hyperion → common}/experiment_plans/oav_snapshot_plan.py +8 -2
- mx_bluesky/common/external_interaction/nexus/nexus_utils.py +2 -2
- mx_bluesky/common/parameters/components.py +1 -1
- mx_bluesky/common/parameters/device_composites.py +65 -0
- mx_bluesky/common/utils/__init__.py +0 -0
- mx_bluesky/common/utils/log.py +12 -11
- mx_bluesky/hyperion/__main__.py +6 -11
- mx_bluesky/hyperion/baton_handler.py +8 -3
- mx_bluesky/hyperion/device_setup_plans/smargon.py +2 -7
- mx_bluesky/hyperion/device_setup_plans/utils.py +0 -47
- mx_bluesky/hyperion/experiment_plans/__init__.py +5 -5
- mx_bluesky/hyperion/experiment_plans/experiment_registry.py +6 -7
- mx_bluesky/hyperion/experiment_plans/hyperion_flyscan_xray_centre_plan.py +40 -41
- mx_bluesky/hyperion/experiment_plans/hyperion_grid_detect_then_xray_centre_plan.py +60 -0
- mx_bluesky/hyperion/experiment_plans/load_centre_collect_full_plan.py +40 -10
- mx_bluesky/hyperion/experiment_plans/pin_centre_then_xray_centre_plan.py +26 -15
- mx_bluesky/hyperion/experiment_plans/robot_load_and_change_energy.py +2 -11
- mx_bluesky/hyperion/experiment_plans/robot_load_then_centre_plan.py +8 -6
- mx_bluesky/hyperion/experiment_plans/rotation_scan_plan.py +33 -36
- mx_bluesky/hyperion/external_interaction/agamemnon.py +68 -62
- mx_bluesky/hyperion/external_interaction/callbacks/__main__.py +1 -1
- mx_bluesky/hyperion/external_interaction/callbacks/rotation/ispyb_callback.py +3 -3
- mx_bluesky/hyperion/external_interaction/callbacks/rotation/ispyb_mapping.py +6 -3
- mx_bluesky/hyperion/external_interaction/callbacks/rotation/nexus_callback.py +2 -2
- mx_bluesky/hyperion/external_interaction/config_server.py +4 -1
- mx_bluesky/hyperion/parameters/cli.py +3 -10
- mx_bluesky/hyperion/parameters/constants.py +1 -1
- mx_bluesky/hyperion/parameters/device_composites.py +5 -27
- mx_bluesky/hyperion/parameters/load_centre_collect.py +4 -4
- mx_bluesky/hyperion/parameters/rotation.py +9 -8
- mx_bluesky/hyperion/utils/context.py +5 -2
- mx_bluesky/hyperion/utils/validation.py +11 -18
- {mx_bluesky-1.4.8.dist-info → mx_bluesky-1.5.0.dist-info}/METADATA +5 -5
- {mx_bluesky-1.4.8.dist-info → mx_bluesky-1.5.0.dist-info}/RECORD +65 -62
- {mx_bluesky-1.4.8.dist-info → mx_bluesky-1.5.0.dist-info}/WHEEL +1 -1
- mx_bluesky/common/device_setup_plans/check_beamstop.py +0 -27
- mx_bluesky/common/external_interaction/test_config_server.py +0 -38
- /mx_bluesky/common/{plans → experiment_plans}/__init__.py +0 -0
- /mx_bluesky/common/{plans → experiment_plans}/inner_plans/__init__ .py +0 -0
- /mx_bluesky/common/{plans → experiment_plans}/read_hardware.py +0 -0
- /mx_bluesky/common/{plans → experiment_plans}/write_sample_status.py +0 -0
- {mx_bluesky-1.4.8.dist-info → mx_bluesky-1.5.0.dist-info}/entry_points.txt +0 -0
- {mx_bluesky-1.4.8.dist-info → mx_bluesky-1.5.0.dist-info}/licenses/LICENSE +0 -0
- {mx_bluesky-1.4.8.dist-info → mx_bluesky-1.5.0.dist-info}/top_level.txt +0 -0
|
@@ -19,7 +19,7 @@ from dodal.devices.oav.oav_detector import OAV
|
|
|
19
19
|
from dodal.devices.oav.oav_parameters import OAVParameters
|
|
20
20
|
from dodal.devices.robot import BartRobot
|
|
21
21
|
from dodal.devices.s4_slit_gaps import S4SlitGaps
|
|
22
|
-
from dodal.devices.smargon import Smargon
|
|
22
|
+
from dodal.devices.smargon import CombinedMove, Smargon
|
|
23
23
|
from dodal.devices.synchrotron import Synchrotron
|
|
24
24
|
from dodal.devices.undulator import Undulator
|
|
25
25
|
from dodal.devices.xbpm_feedback import XBPMFeedback
|
|
@@ -32,16 +32,22 @@ from dodal.plans.preprocessors.verify_undulator_gap import (
|
|
|
32
32
|
|
|
33
33
|
from mx_bluesky.common.device_setup_plans.manipulate_sample import (
|
|
34
34
|
cleanup_sample_environment,
|
|
35
|
-
move_phi_chi_omega,
|
|
36
|
-
move_x_y_z,
|
|
37
35
|
setup_sample_environment,
|
|
38
36
|
)
|
|
39
|
-
from mx_bluesky.common.
|
|
40
|
-
|
|
37
|
+
from mx_bluesky.common.device_setup_plans.utils import (
|
|
38
|
+
start_preparing_data_collection_then_do_plan,
|
|
39
|
+
)
|
|
40
|
+
from mx_bluesky.common.experiment_plans.oav_snapshot_plan import (
|
|
41
|
+
OavSnapshotComposite,
|
|
42
|
+
oav_snapshot_plan,
|
|
43
|
+
setup_beamline_for_OAV,
|
|
44
|
+
)
|
|
45
|
+
from mx_bluesky.common.experiment_plans.read_hardware import (
|
|
41
46
|
read_hardware_for_zocalo,
|
|
42
47
|
standard_read_hardware_during_collection,
|
|
43
48
|
standard_read_hardware_pre_collection,
|
|
44
49
|
)
|
|
50
|
+
from mx_bluesky.common.parameters.components import WithSnapshot
|
|
45
51
|
from mx_bluesky.common.preprocessors.preprocessors import (
|
|
46
52
|
transmission_and_xbpm_feedback_for_collection_decorator,
|
|
47
53
|
)
|
|
@@ -52,18 +58,10 @@ from mx_bluesky.hyperion.device_setup_plans.setup_zebra import (
|
|
|
52
58
|
setup_zebra_for_rotation,
|
|
53
59
|
tidy_up_zebra_after_rotation_scan,
|
|
54
60
|
)
|
|
55
|
-
from mx_bluesky.hyperion.device_setup_plans.utils import (
|
|
56
|
-
start_preparing_data_collection_then_do_plan,
|
|
57
|
-
)
|
|
58
|
-
from mx_bluesky.hyperion.experiment_plans.oav_snapshot_plan import (
|
|
59
|
-
OavSnapshotComposite,
|
|
60
|
-
oav_snapshot_plan,
|
|
61
|
-
setup_beamline_for_OAV,
|
|
62
|
-
)
|
|
63
61
|
from mx_bluesky.hyperion.parameters.constants import CONST
|
|
64
62
|
from mx_bluesky.hyperion.parameters.rotation import (
|
|
65
|
-
MultiRotationScan,
|
|
66
63
|
RotationScan,
|
|
64
|
+
SingleRotationScan,
|
|
67
65
|
)
|
|
68
66
|
|
|
69
67
|
|
|
@@ -118,7 +116,7 @@ class RotationMotionProfile:
|
|
|
118
116
|
|
|
119
117
|
|
|
120
118
|
def calculate_motion_profile(
|
|
121
|
-
params:
|
|
119
|
+
params: SingleRotationScan,
|
|
122
120
|
motor_time_to_speed_s: float,
|
|
123
121
|
max_velocity_deg_s: float,
|
|
124
122
|
) -> RotationMotionProfile:
|
|
@@ -212,7 +210,7 @@ def calculate_motion_profile(
|
|
|
212
210
|
|
|
213
211
|
def rotation_scan_plan(
|
|
214
212
|
composite: RotationScanComposite,
|
|
215
|
-
params:
|
|
213
|
+
params: SingleRotationScan,
|
|
216
214
|
motion_values: RotationMotionProfile,
|
|
217
215
|
):
|
|
218
216
|
"""A stub plan to collect diffraction images from a sample continuously rotating
|
|
@@ -319,7 +317,7 @@ def _cleanup_plan(composite: RotationScanComposite, **kwargs):
|
|
|
319
317
|
|
|
320
318
|
def _move_and_rotation(
|
|
321
319
|
composite: RotationScanComposite,
|
|
322
|
-
params:
|
|
320
|
+
params: SingleRotationScan,
|
|
323
321
|
oav_params: OAVParameters,
|
|
324
322
|
):
|
|
325
323
|
motor_time_to_speed = yield from bps.rd(composite.smargon.omega.acceleration_time)
|
|
@@ -330,19 +328,18 @@ def _move_and_rotation(
|
|
|
330
328
|
return num / 1000 if num else num
|
|
331
329
|
|
|
332
330
|
LOGGER.info("moving to position (if specified)")
|
|
333
|
-
yield from
|
|
331
|
+
yield from bps.abs_set(
|
|
334
332
|
composite.smargon,
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
params.phi_start_deg,
|
|
343
|
-
params.chi_start_deg,
|
|
333
|
+
CombinedMove(
|
|
334
|
+
x=_div_by_1000_if_not_none(params.x_start_um),
|
|
335
|
+
y=_div_by_1000_if_not_none(params.y_start_um),
|
|
336
|
+
z=_div_by_1000_if_not_none(params.z_start_um),
|
|
337
|
+
phi=params.phi_start_deg,
|
|
338
|
+
chi=params.chi_start_deg,
|
|
339
|
+
),
|
|
344
340
|
group=CONST.WAIT.MOVE_GONIO_TO_START,
|
|
345
341
|
)
|
|
342
|
+
|
|
346
343
|
if params.take_snapshots:
|
|
347
344
|
yield from bps.wait(CONST.WAIT.MOVE_GONIO_TO_START)
|
|
348
345
|
yield from setup_beamline_for_OAV(
|
|
@@ -353,15 +350,15 @@ def _move_and_rotation(
|
|
|
353
350
|
yield from bps.prepare(
|
|
354
351
|
composite.aperture_scatterguard,
|
|
355
352
|
params.selected_aperture,
|
|
356
|
-
group=CONST.WAIT.
|
|
353
|
+
group=CONST.WAIT.PREPARE_APERTURE,
|
|
357
354
|
)
|
|
358
355
|
yield from oav_snapshot_plan(composite, params, oav_params)
|
|
359
356
|
yield from rotation_scan_plan(composite, params, motion_values)
|
|
360
357
|
|
|
361
358
|
|
|
362
|
-
def
|
|
359
|
+
def rotation_scan(
|
|
363
360
|
composite: RotationScanComposite,
|
|
364
|
-
parameters:
|
|
361
|
+
parameters: RotationScan,
|
|
365
362
|
oav_params: OAVParameters | None = None,
|
|
366
363
|
) -> MsgGenerator:
|
|
367
364
|
@bpp.set_run_key_decorator(CONST.PLAN.ROTATION_MULTI_OUTER)
|
|
@@ -373,15 +370,15 @@ def multi_rotation_scan(
|
|
|
373
370
|
),
|
|
374
371
|
}
|
|
375
372
|
)
|
|
376
|
-
def
|
|
377
|
-
yield from
|
|
373
|
+
def _wrapped_rotation_scan():
|
|
374
|
+
yield from rotation_scan_internal(composite, parameters, oav_params)
|
|
378
375
|
|
|
379
|
-
yield from
|
|
376
|
+
yield from _wrapped_rotation_scan()
|
|
380
377
|
|
|
381
378
|
|
|
382
|
-
def
|
|
379
|
+
def rotation_scan_internal(
|
|
383
380
|
composite: RotationScanComposite,
|
|
384
|
-
parameters:
|
|
381
|
+
parameters: RotationScan,
|
|
385
382
|
oav_params: OAVParameters | None = None,
|
|
386
383
|
) -> MsgGenerator:
|
|
387
384
|
parameters.features.update_self_from_server()
|
|
@@ -419,7 +416,7 @@ def multi_rotation_scan_internal(
|
|
|
419
416
|
}
|
|
420
417
|
)
|
|
421
418
|
def rotation_scan_core(
|
|
422
|
-
params:
|
|
419
|
+
params: SingleRotationScan,
|
|
423
420
|
):
|
|
424
421
|
yield from _move_and_rotation(composite, params, oav_params)
|
|
425
422
|
|
|
@@ -2,6 +2,7 @@ import dataclasses
|
|
|
2
2
|
import json
|
|
3
3
|
import re
|
|
4
4
|
import traceback
|
|
5
|
+
from collections.abc import Sequence
|
|
5
6
|
from os import path
|
|
6
7
|
from typing import Any, TypeVar
|
|
7
8
|
|
|
@@ -127,83 +128,88 @@ def get_param_version() -> SemanticVersion:
|
|
|
127
128
|
return SemanticVersion.validate_from_str(str(PARAMETER_VERSION))
|
|
128
129
|
|
|
129
130
|
|
|
130
|
-
def populate_parameters_from_agamemnon(agamemnon_params):
|
|
131
|
+
def populate_parameters_from_agamemnon(agamemnon_params) -> Sequence[LoadCentreCollect]:
|
|
131
132
|
visit, detector_distance = get_withvisit_parameters_from_agamemnon(agamemnon_params)
|
|
132
133
|
with_energy_params = get_withenergy_parameters_from_agamemnon(agamemnon_params)
|
|
133
134
|
pin_type = get_pin_type_from_agamemnon_parameters(agamemnon_params)
|
|
134
|
-
|
|
135
|
+
collections = agamemnon_params["collection"]
|
|
135
136
|
visit_directory, file_name = path.split(agamemnon_params["prefix"])
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
"
|
|
146
|
-
"
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
"
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
"
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
137
|
+
|
|
138
|
+
return [
|
|
139
|
+
LoadCentreCollect.model_validate(
|
|
140
|
+
{
|
|
141
|
+
"parameter_model_version": get_param_version(),
|
|
142
|
+
"visit": visit,
|
|
143
|
+
"detector_distance_mm": detector_distance,
|
|
144
|
+
"sample_id": agamemnon_params["sample"]["id"],
|
|
145
|
+
"sample_puck": agamemnon_params["sample"]["container"],
|
|
146
|
+
"sample_pin": agamemnon_params["sample"]["position"],
|
|
147
|
+
"select_centres": {
|
|
148
|
+
"name": "TopNByMaxCount",
|
|
149
|
+
"n": pin_type.expected_number_of_crystals,
|
|
150
|
+
},
|
|
151
|
+
"robot_load_then_centre": {
|
|
152
|
+
"storage_directory": str(visit_directory) + "/xraycentring",
|
|
153
|
+
"file_name": file_name,
|
|
154
|
+
"tip_offset_um": pin_type.full_width / 2,
|
|
155
|
+
"grid_width_um": pin_type.full_width,
|
|
156
|
+
"omega_start_deg": 0.0,
|
|
157
|
+
"chi_start_deg": collection["chi"],
|
|
158
|
+
"transmission_frac": 1.0,
|
|
159
|
+
"features": {"use_gpu_results": True},
|
|
160
|
+
**with_energy_params,
|
|
161
|
+
},
|
|
162
|
+
"multi_rotation_scan": {
|
|
163
|
+
"comment": collection["comment"],
|
|
164
|
+
"storage_directory": str(visit_directory),
|
|
165
|
+
"exposure_time_s": collection["exposure_time"],
|
|
166
|
+
"file_name": file_name,
|
|
167
|
+
"transmission_frac": collection["transmission"],
|
|
168
|
+
"rotation_increment_deg": collection["omega_increment"],
|
|
169
|
+
"ispyb_experiment_type": collection["experiment_type"],
|
|
170
|
+
"snapshot_omegas_deg": [0.0, 90.0, 180.0, 270.0],
|
|
171
|
+
"rotation_scans": [
|
|
172
|
+
{
|
|
173
|
+
"scan_width_deg": (
|
|
174
|
+
collection["number_of_images"]
|
|
175
|
+
* collection["omega_increment"]
|
|
176
|
+
),
|
|
177
|
+
"omega_start_deg": collection["omega_start"],
|
|
178
|
+
"phi_start_deg": collection["phi_start"],
|
|
179
|
+
"chi_start_deg": collection["chi"],
|
|
180
|
+
"rotation_direction": "Positive",
|
|
181
|
+
}
|
|
182
|
+
],
|
|
183
|
+
**with_energy_params,
|
|
184
|
+
},
|
|
185
|
+
}
|
|
186
|
+
)
|
|
187
|
+
for collection in collections
|
|
188
|
+
]
|
|
184
189
|
|
|
185
190
|
|
|
186
|
-
def create_parameters_from_agamemnon() -> LoadCentreCollect
|
|
191
|
+
def create_parameters_from_agamemnon() -> Sequence[LoadCentreCollect]:
|
|
187
192
|
beamline_name = get_beamline_name("i03")
|
|
188
193
|
agamemnon_params = get_next_instruction(beamline_name)
|
|
189
194
|
return (
|
|
190
|
-
populate_parameters_from_agamemnon(agamemnon_params)
|
|
191
|
-
if agamemnon_params
|
|
192
|
-
else None
|
|
195
|
+
populate_parameters_from_agamemnon(agamemnon_params) if agamemnon_params else []
|
|
193
196
|
)
|
|
194
197
|
|
|
195
198
|
|
|
196
|
-
def compare_params(load_centre_collect_params):
|
|
199
|
+
def compare_params(load_centre_collect_params: LoadCentreCollect):
|
|
197
200
|
try:
|
|
198
|
-
|
|
201
|
+
lcc_requests = create_parameters_from_agamemnon()
|
|
199
202
|
# Log differences against GDA populated parameters
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
f"Different parameters found when directly reading from Hyperion: {differences}"
|
|
203
|
+
if not lcc_requests:
|
|
204
|
+
LOGGER.info("Agamemnon returned no instructions")
|
|
205
|
+
else:
|
|
206
|
+
differences = DeepDiff(
|
|
207
|
+
lcc_requests[0], load_centre_collect_params, math_epsilon=1e-5
|
|
206
208
|
)
|
|
209
|
+
if differences:
|
|
210
|
+
LOGGER.info(
|
|
211
|
+
f"Different parameters found when directly reading from Hyperion: {differences}"
|
|
212
|
+
)
|
|
207
213
|
except (ValueError, KeyError):
|
|
208
214
|
LOGGER.warning(f"Failed to compare parameters: {traceback.format_exc()}")
|
|
209
215
|
except Exception:
|
|
@@ -93,7 +93,7 @@ def setup_logging(dev_mode: bool):
|
|
|
93
93
|
(ISPYB_ZOCALO_CALLBACK_LOGGER, "hyperion_ispyb_callback.log"),
|
|
94
94
|
(NEXUS_LOGGER, "hyperion_nexus_callback.log"),
|
|
95
95
|
]:
|
|
96
|
-
logging_path, debug_logging_path = _get_logging_dirs()
|
|
96
|
+
logging_path, debug_logging_path = _get_logging_dirs(dev_mode)
|
|
97
97
|
if logger.handlers == []:
|
|
98
98
|
handlers = set_up_all_logging_handlers(
|
|
99
99
|
logger,
|
|
@@ -25,7 +25,7 @@ from mx_bluesky.hyperion.external_interaction.callbacks.rotation.ispyb_mapping i
|
|
|
25
25
|
populate_data_collection_info_for_rotation,
|
|
26
26
|
)
|
|
27
27
|
from mx_bluesky.hyperion.parameters.constants import CONST
|
|
28
|
-
from mx_bluesky.hyperion.parameters.rotation import
|
|
28
|
+
from mx_bluesky.hyperion.parameters.rotation import SingleRotationScan
|
|
29
29
|
|
|
30
30
|
if TYPE_CHECKING:
|
|
31
31
|
from event_model.documents import Event, RunStart, RunStop
|
|
@@ -62,7 +62,7 @@ class RotationISPyBCallback(BaseISPyBCallback):
|
|
|
62
62
|
)
|
|
63
63
|
hyperion_params = doc.get("mx_bluesky_parameters")
|
|
64
64
|
assert isinstance(hyperion_params, str)
|
|
65
|
-
self.params =
|
|
65
|
+
self.params = SingleRotationScan.model_validate_json(hyperion_params)
|
|
66
66
|
dcgid = (
|
|
67
67
|
self.ispyb_ids.data_collection_group_id
|
|
68
68
|
if (self.params.sample_id == self.last_sample_id)
|
|
@@ -86,7 +86,7 @@ class RotationISPyBCallback(BaseISPyBCallback):
|
|
|
86
86
|
ISPYB_ZOCALO_CALLBACK_LOGGER.info("Beginning ispyb deposition")
|
|
87
87
|
data_collection_group_info = populate_data_collection_group(self.params)
|
|
88
88
|
data_collection_info = populate_data_collection_info_for_rotation(
|
|
89
|
-
cast(
|
|
89
|
+
cast(SingleRotationScan, self.params)
|
|
90
90
|
)
|
|
91
91
|
data_collection_info = populate_remaining_data_collection_info(
|
|
92
92
|
self.params.comment,
|
|
@@ -1,17 +1,20 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
from mx_bluesky.common.external_interaction.ispyb.data_model import DataCollectionInfo
|
|
4
|
-
from mx_bluesky.hyperion.parameters.rotation import
|
|
4
|
+
from mx_bluesky.hyperion.parameters.rotation import SingleRotationScan
|
|
5
5
|
|
|
6
6
|
|
|
7
|
-
def populate_data_collection_info_for_rotation(params:
|
|
7
|
+
def populate_data_collection_info_for_rotation(params: SingleRotationScan):
|
|
8
8
|
info = DataCollectionInfo(
|
|
9
9
|
omega_start=params.omega_start_deg,
|
|
10
10
|
data_collection_number=params.detector_params.run_number, # type:ignore # the validator always makes this int
|
|
11
11
|
n_images=params.num_images,
|
|
12
12
|
axis_range=params.rotation_increment_deg,
|
|
13
13
|
axis_start=params.omega_start_deg,
|
|
14
|
-
axis_end=(
|
|
14
|
+
axis_end=(
|
|
15
|
+
params.omega_start_deg
|
|
16
|
+
+ params.scan_width_deg * params.rotation_direction.multiplier
|
|
17
|
+
),
|
|
15
18
|
kappa_start=params.kappa_start_deg,
|
|
16
19
|
)
|
|
17
20
|
return info
|
|
@@ -16,7 +16,7 @@ from mx_bluesky.common.external_interaction.nexus.nexus_utils import (
|
|
|
16
16
|
from mx_bluesky.common.external_interaction.nexus.write_nexus import NexusWriter
|
|
17
17
|
from mx_bluesky.common.utils.log import NEXUS_LOGGER
|
|
18
18
|
from mx_bluesky.hyperion.parameters.constants import CONST
|
|
19
|
-
from mx_bluesky.hyperion.parameters.rotation import
|
|
19
|
+
from mx_bluesky.hyperion.parameters.rotation import SingleRotationScan
|
|
20
20
|
|
|
21
21
|
if TYPE_CHECKING:
|
|
22
22
|
from event_model.documents import Event, EventDescriptor, RunStart
|
|
@@ -85,7 +85,7 @@ class RotationNexusFileCallback(PlanReactiveCallback):
|
|
|
85
85
|
NEXUS_LOGGER.info(
|
|
86
86
|
f"Nexus writer received start document with experiment parameters {hyperion_params}"
|
|
87
87
|
)
|
|
88
|
-
parameters =
|
|
88
|
+
parameters = SingleRotationScan.model_validate_json(hyperion_params)
|
|
89
89
|
NEXUS_LOGGER.info("Setting up nexus file...")
|
|
90
90
|
|
|
91
91
|
det_size = (
|
|
@@ -21,7 +21,9 @@ class HyperionFeatureFlags(FeatureFlags):
|
|
|
21
21
|
set_stub_offsets: If True then set the stub offsets after moving to the crystal (ignored for
|
|
22
22
|
multi-centre)
|
|
23
23
|
omega_flip: If True then invert the smargon omega motor rotation commands with respect to
|
|
24
|
-
the hyperion request.
|
|
24
|
+
the hyperion request. See "Hyperion Coordinate Systems" in the documentation.
|
|
25
|
+
alternate_rotation_direction: If True then the for multi-sample pins the rotation direction of
|
|
26
|
+
successive rotation scans is alternated between positive and negative.
|
|
25
27
|
"""
|
|
26
28
|
|
|
27
29
|
@staticmethod
|
|
@@ -41,3 +43,4 @@ class HyperionFeatureFlags(FeatureFlags):
|
|
|
41
43
|
use_gpu_results: bool = CONST.I03.USE_GPU_RESULTS
|
|
42
44
|
set_stub_offsets: bool = CONST.I03.SET_STUB_OFFSETS
|
|
43
45
|
omega_flip: bool = CONST.I03.OMEGA_FLIP
|
|
46
|
+
alternate_rotation_direction: bool = CONST.I03.ALTERNATE_ROTATION_DIRECTION
|
|
@@ -8,7 +8,6 @@ from mx_bluesky._version import version
|
|
|
8
8
|
@dataclass
|
|
9
9
|
class HyperionArgs:
|
|
10
10
|
dev_mode: bool = False
|
|
11
|
-
verbose_event_logging: bool = False
|
|
12
11
|
|
|
13
12
|
|
|
14
13
|
def _add_callback_relevant_args(parser: argparse.ArgumentParser) -> None:
|
|
@@ -29,16 +28,11 @@ def parse_callback_dev_mode_arg() -> bool:
|
|
|
29
28
|
|
|
30
29
|
|
|
31
30
|
def parse_cli_args() -> HyperionArgs:
|
|
32
|
-
"""Parses all arguments relevant to hyperion.
|
|
33
|
-
|
|
34
|
-
|
|
31
|
+
"""Parses all arguments relevant to hyperion.
|
|
32
|
+
Returns:
|
|
33
|
+
an HyperionArgs dataclass with the fields: (dev_mode: bool)"""
|
|
35
34
|
parser = argparse.ArgumentParser()
|
|
36
35
|
_add_callback_relevant_args(parser)
|
|
37
|
-
parser.add_argument(
|
|
38
|
-
"--verbose-event-logging",
|
|
39
|
-
action="store_true",
|
|
40
|
-
help="Log all bluesky event documents to graylog",
|
|
41
|
-
)
|
|
42
36
|
parser.add_argument(
|
|
43
37
|
"--version",
|
|
44
38
|
help="Print hyperion version string",
|
|
@@ -47,6 +41,5 @@ def parse_cli_args() -> HyperionArgs:
|
|
|
47
41
|
)
|
|
48
42
|
args = parser.parse_args()
|
|
49
43
|
return HyperionArgs(
|
|
50
|
-
verbose_event_logging=args.verbose_event_logging or False,
|
|
51
44
|
dev_mode=args.dev or False,
|
|
52
45
|
)
|
|
@@ -19,7 +19,6 @@ TEST_MODE = os.environ.get("HYPERION_TEST_MODE")
|
|
|
19
19
|
|
|
20
20
|
@dataclass(frozen=True)
|
|
21
21
|
class I03Constants:
|
|
22
|
-
BASE_DATA_DIR = "/tmp/dls/i03/data/" if TEST_MODE else "/dls/i03/data/"
|
|
23
22
|
BEAMLINE = "BL03S" if TEST_MODE else "BL03I"
|
|
24
23
|
DETECTOR = EIGER2_X_16M_SIZE
|
|
25
24
|
INSERTION_PREFIX = "SR03S" if TEST_MODE else "SR03I"
|
|
@@ -28,6 +27,7 @@ class I03Constants:
|
|
|
28
27
|
USE_PANDA_FOR_GRIDSCAN = False
|
|
29
28
|
SET_STUB_OFFSETS = False
|
|
30
29
|
OMEGA_FLIP = True
|
|
30
|
+
ALTERNATE_ROTATION_DIRECTION = True
|
|
31
31
|
|
|
32
32
|
# Turns on GPU processing for zocalo and logs a comparison between GPU and CPU-
|
|
33
33
|
# processed results.
|
|
@@ -7,19 +7,14 @@ from dodal.devices.aperturescatterguard import (
|
|
|
7
7
|
from dodal.devices.attenuator.attenuator import BinaryFilterAttenuator
|
|
8
8
|
from dodal.devices.backlight import Backlight
|
|
9
9
|
from dodal.devices.common_dcm import BaseDCM
|
|
10
|
-
from dodal.devices.detector.detector_motion import DetectorMotion
|
|
11
10
|
from dodal.devices.eiger import EigerDetector
|
|
12
11
|
from dodal.devices.fast_grid_scan import (
|
|
13
12
|
PandAFastGridScan,
|
|
14
13
|
ZebraFastGridScan,
|
|
15
14
|
)
|
|
16
15
|
from dodal.devices.flux import Flux
|
|
17
|
-
from dodal.devices.i03 import Beamstop
|
|
18
|
-
from dodal.devices.oav.oav_detector import OAV
|
|
19
|
-
from dodal.devices.oav.pin_image_recognition import PinTipDetection
|
|
20
16
|
from dodal.devices.robot import BartRobot
|
|
21
17
|
from dodal.devices.s4_slit_gaps import S4SlitGaps
|
|
22
|
-
from dodal.devices.smargon import Smargon
|
|
23
18
|
from dodal.devices.synchrotron import Synchrotron
|
|
24
19
|
from dodal.devices.undulator import Undulator
|
|
25
20
|
from dodal.devices.xbpm_feedback import XBPMFeedback
|
|
@@ -28,9 +23,12 @@ from dodal.devices.zebra.zebra_controlled_shutter import ZebraShutter
|
|
|
28
23
|
from dodal.devices.zocalo import ZocaloResults
|
|
29
24
|
from ophyd_async.fastcs.panda import HDFPanda
|
|
30
25
|
|
|
31
|
-
from mx_bluesky.common.
|
|
26
|
+
from mx_bluesky.common.experiment_plans.common_flyscan_xray_centre_plan import (
|
|
32
27
|
FlyScanEssentialDevices,
|
|
33
28
|
)
|
|
29
|
+
from mx_bluesky.common.parameters.device_composites import (
|
|
30
|
+
GridDetectThenXRayCentreComposite,
|
|
31
|
+
)
|
|
34
32
|
|
|
35
33
|
|
|
36
34
|
@pydantic.dataclasses.dataclass(config={"arbitrary_types_allowed": True})
|
|
@@ -57,28 +55,8 @@ class HyperionFlyScanXRayCentreComposite(FlyScanEssentialDevices):
|
|
|
57
55
|
|
|
58
56
|
|
|
59
57
|
@pydantic.dataclasses.dataclass(config={"arbitrary_types_allowed": True})
|
|
60
|
-
class GridDetectThenXRayCentreComposite:
|
|
58
|
+
class HyperionGridDetectThenXRayCentreComposite(GridDetectThenXRayCentreComposite):
|
|
61
59
|
"""All devices which are directly or indirectly required by this plan"""
|
|
62
60
|
|
|
63
|
-
aperture_scatterguard: ApertureScatterguard
|
|
64
|
-
attenuator: BinaryFilterAttenuator
|
|
65
|
-
backlight: Backlight
|
|
66
|
-
beamstop: Beamstop
|
|
67
|
-
dcm: BaseDCM
|
|
68
|
-
detector_motion: DetectorMotion
|
|
69
|
-
eiger: EigerDetector
|
|
70
|
-
zebra_fast_grid_scan: ZebraFastGridScan
|
|
71
|
-
flux: Flux
|
|
72
|
-
oav: OAV
|
|
73
|
-
pin_tip_detection: PinTipDetection
|
|
74
|
-
smargon: Smargon
|
|
75
|
-
synchrotron: Synchrotron
|
|
76
|
-
s4_slit_gaps: S4SlitGaps
|
|
77
|
-
undulator: Undulator
|
|
78
|
-
xbpm_feedback: XBPMFeedback
|
|
79
|
-
zebra: Zebra
|
|
80
|
-
zocalo: ZocaloResults
|
|
81
61
|
panda: HDFPanda
|
|
82
62
|
panda_fast_grid_scan: PandAFastGridScan
|
|
83
|
-
robot: BartRobot
|
|
84
|
-
sample_shutter: ZebraShutter
|
|
@@ -12,7 +12,7 @@ from mx_bluesky.hyperion.parameters.components import WithHyperionUDCFeatures
|
|
|
12
12
|
from mx_bluesky.hyperion.parameters.robot_load import (
|
|
13
13
|
RobotLoadThenCentre,
|
|
14
14
|
)
|
|
15
|
-
from mx_bluesky.hyperion.parameters.rotation import
|
|
15
|
+
from mx_bluesky.hyperion.parameters.rotation import RotationScan
|
|
16
16
|
|
|
17
17
|
T = TypeVar("T", bound=BaseModel)
|
|
18
18
|
|
|
@@ -34,7 +34,7 @@ class LoadCentreCollect(
|
|
|
34
34
|
pin-tip centre and rotation scan operations."""
|
|
35
35
|
|
|
36
36
|
robot_load_then_centre: RobotLoadThenCentre
|
|
37
|
-
multi_rotation_scan:
|
|
37
|
+
multi_rotation_scan: RotationScan
|
|
38
38
|
|
|
39
39
|
@model_validator(mode="before")
|
|
40
40
|
@classmethod
|
|
@@ -42,7 +42,7 @@ class LoadCentreCollect(
|
|
|
42
42
|
allowed_keys = (
|
|
43
43
|
LoadCentreCollect.model_fields.keys()
|
|
44
44
|
| RobotLoadThenCentre.model_fields.keys()
|
|
45
|
-
|
|
|
45
|
+
| RotationScan.model_fields.keys()
|
|
46
46
|
)
|
|
47
47
|
|
|
48
48
|
disallowed_keys = values.keys() - allowed_keys
|
|
@@ -74,7 +74,7 @@ class LoadCentreCollect(
|
|
|
74
74
|
values, values["robot_load_then_centre"], RobotLoadThenCentre
|
|
75
75
|
)
|
|
76
76
|
new_multi_rotation_scan_params = construct_from_values(
|
|
77
|
-
values, values["multi_rotation_scan"],
|
|
77
|
+
values, values["multi_rotation_scan"], RotationScan
|
|
78
78
|
)
|
|
79
79
|
values["multi_rotation_scan"] = new_multi_rotation_scan_params
|
|
80
80
|
values["robot_load_then_centre"] = new_robot_load_then_centre_params
|
|
@@ -42,7 +42,8 @@ class RotationScanPerSweep(OptionalGonioAngleStarts, OptionalXyzStarts):
|
|
|
42
42
|
omega_start_deg: The initial angle of the rotation in degrees (default 0)
|
|
43
43
|
scan_width_deg: The sweep of the rotation in degrees, this must be positive (default 360)
|
|
44
44
|
rotation_direction: Indicates the direction of rotation, if RotationDirection.POSITIVE
|
|
45
|
-
the final angle is obtained by adding scan_width_deg, otherwise by subtraction (default NEGATIVE)
|
|
45
|
+
the final angle is obtained by adding scan_width_deg, otherwise by subtraction (default NEGATIVE).
|
|
46
|
+
See "Hyperion Coordinate Systems" in the documentation.
|
|
46
47
|
nexus_vds_start_img: The frame number of the first frame captured during the rotation
|
|
47
48
|
"""
|
|
48
49
|
|
|
@@ -104,7 +105,7 @@ class RotationExperiment(DiffractionExperimentWithSample, WithHyperionUDCFeature
|
|
|
104
105
|
return aperture_position
|
|
105
106
|
|
|
106
107
|
|
|
107
|
-
class
|
|
108
|
+
class SingleRotationScan(WithScan, RotationScanPerSweep, RotationExperiment):
|
|
108
109
|
@property
|
|
109
110
|
def detector_params(self):
|
|
110
111
|
return self._detector_params(self.omega_start_deg)
|
|
@@ -129,12 +130,12 @@ class RotationScan(WithScan, RotationScanPerSweep, RotationExperiment):
|
|
|
129
130
|
return int(self.scan_width_deg / self.rotation_increment_deg)
|
|
130
131
|
|
|
131
132
|
|
|
132
|
-
class
|
|
133
|
+
class RotationScan(RotationExperiment, SplitScan):
|
|
133
134
|
rotation_scans: Annotated[list[RotationScanPerSweep], Len(min_length=1)]
|
|
134
135
|
|
|
135
|
-
def _single_rotation_scan(self, scan: RotationScanPerSweep) ->
|
|
136
|
+
def _single_rotation_scan(self, scan: RotationScanPerSweep) -> SingleRotationScan:
|
|
136
137
|
# self has everything from RotationExperiment
|
|
137
|
-
allowed_keys =
|
|
138
|
+
allowed_keys = SingleRotationScan.model_fields.keys() # type: ignore # mypy doesn't recognise this as a property...
|
|
138
139
|
params_dump = self.model_dump()
|
|
139
140
|
# provided `scan` has everything from RotationScanPerSweep
|
|
140
141
|
scan_dump = scan.model_dump()
|
|
@@ -142,13 +143,13 @@ class MultiRotationScan(RotationExperiment, SplitScan):
|
|
|
142
143
|
k: v for k, v in (params_dump | scan_dump).items() if k in allowed_keys
|
|
143
144
|
}
|
|
144
145
|
# together they have everything for RotationScan
|
|
145
|
-
rotation_scan =
|
|
146
|
+
rotation_scan = SingleRotationScan(**rotation_scan_kv_pairs)
|
|
146
147
|
return rotation_scan
|
|
147
148
|
|
|
148
149
|
@model_validator(mode="after")
|
|
149
150
|
@classmethod
|
|
150
151
|
def correct_start_vds(cls, values: Any) -> Any:
|
|
151
|
-
assert isinstance(values,
|
|
152
|
+
assert isinstance(values, RotationScan)
|
|
152
153
|
start_img = 0.0
|
|
153
154
|
for scan in values.rotation_scans:
|
|
154
155
|
scan.nexus_vds_start_img = int(start_img)
|
|
@@ -167,7 +168,7 @@ class MultiRotationScan(RotationExperiment, SplitScan):
|
|
|
167
168
|
return self
|
|
168
169
|
|
|
169
170
|
@property
|
|
170
|
-
def single_rotation_scans(self) -> Iterator[
|
|
171
|
+
def single_rotation_scans(self) -> Iterator[SingleRotationScan]:
|
|
171
172
|
for scan in self.rotation_scans:
|
|
172
173
|
yield self._single_rotation_scan(scan)
|
|
173
174
|
|
|
@@ -5,11 +5,14 @@ import mx_bluesky.hyperion.experiment_plans as hyperion_plans
|
|
|
5
5
|
from mx_bluesky.common.utils.log import LOGGER
|
|
6
6
|
|
|
7
7
|
|
|
8
|
-
def setup_context() -> BlueskyContext:
|
|
8
|
+
def setup_context(dev_mode: bool = False) -> BlueskyContext:
|
|
9
9
|
context = BlueskyContext()
|
|
10
10
|
context.with_plan_module(hyperion_plans)
|
|
11
11
|
|
|
12
|
-
context.with_dodal_module(
|
|
12
|
+
context.with_dodal_module(
|
|
13
|
+
get_beamline_based_on_environment_variable(),
|
|
14
|
+
mock=dev_mode,
|
|
15
|
+
)
|
|
13
16
|
|
|
14
17
|
LOGGER.info(f"Plans found in context: {context.plan_functions.keys()}")
|
|
15
18
|
|