mx-bluesky 1.4.0__py3-none-any.whl → 1.4.1__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/redis_to_murko_forwarder.py +178 -0
- mx_bluesky/beamlines/i04/thawing_plan.py +1 -1
- mx_bluesky/beamlines/i24/serial/dcid.py +143 -171
- mx_bluesky/beamlines/i24/serial/extruder/EX-gui-edm/DiamondExtruder-I24-py3v1.edl +1 -1
- mx_bluesky/beamlines/i24/serial/extruder/i24ssx_Extruder_Collect_py3v2.py +54 -21
- mx_bluesky/beamlines/i24/serial/fixed_target/FT-gui-edm/DiamondChipI24-py3v1.edl +2 -5
- mx_bluesky/beamlines/i24/serial/fixed_target/ft_utils.py +0 -1
- mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_Chip_Collect_py3v1.py +67 -50
- mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_Chip_Manager_py3v1.py +26 -79
- 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/__init__.py +4 -0
- mx_bluesky/beamlines/i24/serial/parameters/constants.py +6 -1
- mx_bluesky/beamlines/i24/serial/parameters/experiment_parameters.py +42 -15
- mx_bluesky/beamlines/i24/serial/run_fixed_target.sh +4 -3
- mx_bluesky/beamlines/i24/serial/setup_beamline/pv.py +2 -0
- mx_bluesky/beamlines/i24/serial/setup_beamline/setup_beamline.py +103 -81
- 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 +74 -72
- mx_bluesky/common/external_interaction/config_server.py +46 -0
- mx_bluesky/common/parameters/components.py +52 -15
- mx_bluesky/common/parameters/constants.py +11 -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/dcm_pitch_roll_mirror_adjuster.py +21 -31
- 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/device_setup_plans/smargon.py +3 -3
- mx_bluesky/hyperion/exceptions.py +13 -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 +133 -97
- mx_bluesky/hyperion/experiment_plans/grid_detect_then_xray_centre_plan.py +42 -18
- mx_bluesky/hyperion/experiment_plans/load_centre_collect_full_plan.py +75 -9
- mx_bluesky/hyperion/experiment_plans/oav_grid_detection_plan.py +2 -2
- mx_bluesky/hyperion/experiment_plans/oav_snapshot_plan.py +1 -1
- 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 +5 -5
- mx_bluesky/hyperion/experiment_plans/robot_load_and_change_energy.py +28 -28
- mx_bluesky/hyperion/experiment_plans/robot_load_then_centre_plan.py +64 -16
- mx_bluesky/hyperion/experiment_plans/rotation_scan_plan.py +11 -3
- mx_bluesky/hyperion/experiment_plans/set_energy_plan.py +10 -10
- mx_bluesky/hyperion/external_interaction/callbacks/__init__.py +0 -4
- mx_bluesky/hyperion/external_interaction/callbacks/__main__.py +4 -0
- mx_bluesky/hyperion/external_interaction/callbacks/common/abstract_event.py +66 -0
- mx_bluesky/hyperion/external_interaction/callbacks/common/callback_util.py +5 -0
- mx_bluesky/hyperion/external_interaction/callbacks/grid_detection_callback.py +15 -15
- mx_bluesky/hyperion/external_interaction/callbacks/robot_load/ispyb_callback.py +18 -10
- 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/sample_handling/__init__.py +0 -0
- mx_bluesky/hyperion/external_interaction/callbacks/sample_handling/sample_handling_callback.py +84 -0
- mx_bluesky/hyperion/external_interaction/callbacks/xray_centre/ispyb_callback.py +15 -9
- 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/exceptions.py +0 -9
- mx_bluesky/hyperion/external_interaction/ispyb/exp_eye_store.py +65 -15
- 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.1.dist-info}/METADATA +35 -34
- {mx_bluesky-1.4.0.dist-info → mx_bluesky-1.4.1.dist-info}/RECORD +77 -70
- {mx_bluesky-1.4.0.dist-info → mx_bluesky-1.4.1.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.1.dist-info}/LICENSE +0 -0
- {mx_bluesky-1.4.0.dist-info → mx_bluesky-1.4.1.dist-info}/entry_points.txt +0 -0
- {mx_bluesky-1.4.0.dist-info → mx_bluesky-1.4.1.dist-info}/top_level.txt +0 -0
|
@@ -12,7 +12,7 @@ from mx_bluesky.hyperion.external_interaction.nexus.nexus_utils import (
|
|
|
12
12
|
from mx_bluesky.hyperion.external_interaction.nexus.write_nexus import NexusWriter
|
|
13
13
|
from mx_bluesky.hyperion.log import NEXUS_LOGGER
|
|
14
14
|
from mx_bluesky.hyperion.parameters.constants import CONST
|
|
15
|
-
from mx_bluesky.hyperion.parameters.gridscan import
|
|
15
|
+
from mx_bluesky.hyperion.parameters.gridscan import HyperionThreeDGridScan
|
|
16
16
|
|
|
17
17
|
if TYPE_CHECKING:
|
|
18
18
|
from event_model.documents import Event, EventDescriptor, RunStart
|
|
@@ -45,11 +45,12 @@ class GridscanNexusFileCallback(PlanReactiveCallback):
|
|
|
45
45
|
|
|
46
46
|
def activity_gated_start(self, doc: RunStart):
|
|
47
47
|
if doc.get("subplan_name") == CONST.PLAN.GRIDSCAN_OUTER:
|
|
48
|
-
|
|
48
|
+
hyperion_params = doc.get("hyperion_parameters")
|
|
49
|
+
assert isinstance(hyperion_params, str)
|
|
49
50
|
NEXUS_LOGGER.info(
|
|
50
|
-
f"Nexus writer received start document with experiment parameters {
|
|
51
|
+
f"Nexus writer received start document with experiment parameters {hyperion_params}"
|
|
51
52
|
)
|
|
52
|
-
parameters =
|
|
53
|
+
parameters = HyperionThreeDGridScan.model_validate_json(hyperion_params)
|
|
53
54
|
d_size = parameters.detector_params.detector_size_constants.det_size_pixels
|
|
54
55
|
grid_n_img_1 = parameters.scan_indices[1]
|
|
55
56
|
grid_n_img_2 = parameters.num_images - grid_n_img_1
|
|
@@ -1,47 +1,18 @@
|
|
|
1
|
+
from functools import cache
|
|
2
|
+
|
|
1
3
|
from daq_config_server.client import ConfigServer
|
|
2
|
-
from pydantic import BaseModel, Field, model_validator
|
|
3
4
|
|
|
5
|
+
from mx_bluesky.common.external_interaction.config_server import FeatureFlags
|
|
4
6
|
from mx_bluesky.hyperion.log import LOGGER
|
|
5
7
|
from mx_bluesky.hyperion.parameters.constants import CONST
|
|
6
8
|
|
|
7
|
-
_CONFIG_SERVER: ConfigServer | None = None
|
|
8
|
-
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
10
|
+
class HyperionFeatureFlags(FeatureFlags):
|
|
11
|
+
@staticmethod
|
|
12
|
+
@cache
|
|
13
|
+
def get_config_server() -> ConfigServer:
|
|
14
|
+
return ConfigServer(CONST.CONFIG_SERVER_URL, LOGGER)
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
class FeatureFlags(BaseModel):
|
|
18
|
-
# The default value will be used as the fallback when doing a best-effort fetch
|
|
19
|
-
# from the service
|
|
20
16
|
use_panda_for_gridscan: bool = CONST.I03.USE_PANDA_FOR_GRIDSCAN
|
|
21
17
|
compare_cpu_and_gpu_zocalo: bool = CONST.I03.COMPARE_CPU_AND_GPU_ZOCALO
|
|
22
18
|
set_stub_offsets: bool = CONST.I03.SET_STUB_OFFSETS
|
|
23
|
-
|
|
24
|
-
# Feature values supplied at construction will override values from the config server
|
|
25
|
-
overriden_features: dict = Field(default_factory=dict, exclude=True)
|
|
26
|
-
|
|
27
|
-
@model_validator(mode="before")
|
|
28
|
-
@classmethod
|
|
29
|
-
def mark_overridden_features(cls, values):
|
|
30
|
-
assert isinstance(values, dict)
|
|
31
|
-
values["overriden_features"] = values.copy()
|
|
32
|
-
return values
|
|
33
|
-
|
|
34
|
-
@classmethod
|
|
35
|
-
def _get_flags(cls):
|
|
36
|
-
flags = config_server().best_effort_get_all_feature_flags()
|
|
37
|
-
return {f: flags[f] for f in flags if f in cls.model_fields.keys()}
|
|
38
|
-
|
|
39
|
-
def update_self_from_server(self):
|
|
40
|
-
"""Used to update the feature flags from the server during a plan. Where there are flags which were explicitly set from externally supplied parameters, these values will be used instead."""
|
|
41
|
-
for flag, value in self._get_flags().items():
|
|
42
|
-
updated_value = (
|
|
43
|
-
value
|
|
44
|
-
if flag not in self.overriden_features.keys()
|
|
45
|
-
else self.overriden_features[flag]
|
|
46
|
-
)
|
|
47
|
-
setattr(self, flag, updated_value)
|
|
@@ -1,13 +1,4 @@
|
|
|
1
|
-
from mx_bluesky.hyperion.exceptions import WarningException
|
|
2
|
-
|
|
3
|
-
|
|
4
1
|
class ISPyBDepositionNotMade(Exception):
|
|
5
2
|
"""Raised when the ISPyB or Zocalo callbacks can't access ISPyB deposition numbers."""
|
|
6
3
|
|
|
7
4
|
pass
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
class NoCentreFoundException(WarningException):
|
|
11
|
-
"""Error for if zocalo is unable to find the centre during a gridscan."""
|
|
12
|
-
|
|
13
|
-
pass
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import configparser
|
|
2
|
+
from dataclasses import dataclass
|
|
3
|
+
from enum import StrEnum
|
|
2
4
|
|
|
3
5
|
from requests import patch, post
|
|
4
6
|
from requests.auth import AuthBase
|
|
@@ -29,20 +31,44 @@ def _get_base_url_and_token() -> tuple[str, str]:
|
|
|
29
31
|
return expeye_config["url"], expeye_config["token"]
|
|
30
32
|
|
|
31
33
|
|
|
34
|
+
def _send_and_get_response(auth, url, data, send_func) -> dict:
|
|
35
|
+
response = send_func(url, auth=auth, json=data)
|
|
36
|
+
if not response.ok:
|
|
37
|
+
raise ISPyBDepositionNotMade(f"Could not write {data} to {url}: {response}")
|
|
38
|
+
return response.json()
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
@dataclass
|
|
42
|
+
class BLSample:
|
|
43
|
+
container_id: int
|
|
44
|
+
bl_sample_id: int
|
|
45
|
+
bl_sample_status: str | None
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class BLSampleStatus(StrEnum):
|
|
49
|
+
# The sample has been loaded
|
|
50
|
+
LOADED = "LOADED"
|
|
51
|
+
# Problem with the sample e.g. pin too long/short
|
|
52
|
+
ERROR_SAMPLE = "ERROR - sample"
|
|
53
|
+
# Any other general error
|
|
54
|
+
ERROR_BEAMLINE = "ERROR - beamline"
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
assert all(
|
|
58
|
+
len(value) <= 20 for value in BLSampleStatus
|
|
59
|
+
), "Column size limit of 20 for BLSampleStatus"
|
|
60
|
+
|
|
61
|
+
|
|
32
62
|
class ExpeyeInteraction:
|
|
63
|
+
"""Exposes functionality from the Expeye core API"""
|
|
64
|
+
|
|
33
65
|
CREATE_ROBOT_ACTION = "/proposals/{proposal}/sessions/{visit_number}/robot-actions"
|
|
34
66
|
UPDATE_ROBOT_ACTION = "/robot-actions/{action_id}"
|
|
35
67
|
|
|
36
68
|
def __init__(self) -> None:
|
|
37
69
|
url, token = _get_base_url_and_token()
|
|
38
|
-
self.
|
|
39
|
-
self.
|
|
40
|
-
|
|
41
|
-
def _send_and_get_response(self, url, data, send_func) -> dict:
|
|
42
|
-
response = send_func(url, auth=self.auth, json=data)
|
|
43
|
-
if not response.ok:
|
|
44
|
-
raise ISPyBDepositionNotMade(f"Could not write {data} to {url}: {response}")
|
|
45
|
-
return response.json()
|
|
70
|
+
self._base_url = url
|
|
71
|
+
self._auth = BearerAuth(token)
|
|
46
72
|
|
|
47
73
|
def start_load(
|
|
48
74
|
self,
|
|
@@ -66,7 +92,7 @@ class ExpeyeInteraction:
|
|
|
66
92
|
Returns:
|
|
67
93
|
RobotActionID: The id of the robot load action that is created
|
|
68
94
|
"""
|
|
69
|
-
url = self.
|
|
95
|
+
url = self._base_url + self.CREATE_ROBOT_ACTION.format(
|
|
70
96
|
proposal=proposal_reference, visit_number=visit_number
|
|
71
97
|
)
|
|
72
98
|
|
|
@@ -77,7 +103,7 @@ class ExpeyeInteraction:
|
|
|
77
103
|
"containerLocation": container_location,
|
|
78
104
|
"dewarLocation": dewar_location,
|
|
79
105
|
}
|
|
80
|
-
response = self.
|
|
106
|
+
response = _send_and_get_response(self._auth, url, data, post)
|
|
81
107
|
return response["robotActionId"]
|
|
82
108
|
|
|
83
109
|
def update_barcode_and_snapshots(
|
|
@@ -95,14 +121,14 @@ class ExpeyeInteraction:
|
|
|
95
121
|
snapshot_before_path (str): Path to the snapshot before robot load
|
|
96
122
|
snapshot_after_path (str): Path to the snapshot after robot load
|
|
97
123
|
"""
|
|
98
|
-
url = self.
|
|
124
|
+
url = self._base_url + self.UPDATE_ROBOT_ACTION.format(action_id=action_id)
|
|
99
125
|
|
|
100
126
|
data = {
|
|
101
127
|
"sampleBarcode": barcode,
|
|
102
128
|
"xtalSnapshotBefore": snapshot_before_path,
|
|
103
129
|
"xtalSnapshotAfter": snapshot_after_path,
|
|
104
130
|
}
|
|
105
|
-
self.
|
|
131
|
+
_send_and_get_response(self._auth, url, data, patch)
|
|
106
132
|
|
|
107
133
|
def end_load(self, action_id: RobotActionID, status: str, reason: str):
|
|
108
134
|
"""Finish an existing robot action, providing final information about how it went
|
|
@@ -113,13 +139,37 @@ class ExpeyeInteraction:
|
|
|
113
139
|
otherwise error
|
|
114
140
|
reason (str): If the status is in error than the reason for that error
|
|
115
141
|
"""
|
|
116
|
-
url = self.
|
|
142
|
+
url = self._base_url + self.UPDATE_ROBOT_ACTION.format(action_id=action_id)
|
|
117
143
|
|
|
118
144
|
run_status = "SUCCESS" if status == "success" else "ERROR"
|
|
119
145
|
|
|
120
146
|
data = {
|
|
121
147
|
"endTimestamp": get_current_time_string(),
|
|
122
148
|
"status": run_status,
|
|
123
|
-
"message": reason,
|
|
149
|
+
"message": reason[:255] if reason else "",
|
|
124
150
|
}
|
|
125
|
-
self.
|
|
151
|
+
_send_and_get_response(self._auth, url, data, patch)
|
|
152
|
+
|
|
153
|
+
def update_sample_status(
|
|
154
|
+
self, bl_sample_id: int, bl_sample_status: BLSampleStatus
|
|
155
|
+
) -> BLSample:
|
|
156
|
+
"""Update the blSampleStatus of a sample.
|
|
157
|
+
Args:
|
|
158
|
+
bl_sample_id: The sample ID
|
|
159
|
+
bl_sample_status: The sample status
|
|
160
|
+
status_message: An optional message
|
|
161
|
+
Returns:
|
|
162
|
+
The updated sample
|
|
163
|
+
"""
|
|
164
|
+
data = {"blSampleStatus": (str(bl_sample_status))}
|
|
165
|
+
response = _send_and_get_response(
|
|
166
|
+
self._auth, self._base_url + f"/samples/{bl_sample_id}", data, patch
|
|
167
|
+
)
|
|
168
|
+
return self._sample_from_json(response)
|
|
169
|
+
|
|
170
|
+
def _sample_from_json(self, response) -> BLSample:
|
|
171
|
+
return BLSample(
|
|
172
|
+
bl_sample_id=response["blSampleId"],
|
|
173
|
+
bl_sample_status=response["blSampleStatus"],
|
|
174
|
+
container_id=response["containerId"],
|
|
175
|
+
)
|
|
@@ -1,12 +1,7 @@
|
|
|
1
|
-
from
|
|
1
|
+
from pydantic import BaseModel, Field
|
|
2
2
|
|
|
3
|
-
from
|
|
4
|
-
BaseModel,
|
|
5
|
-
Field,
|
|
6
|
-
)
|
|
3
|
+
from mx_bluesky.hyperion.external_interaction.config_server import HyperionFeatureFlags
|
|
7
4
|
|
|
8
|
-
from mx_bluesky.hyperion.external_interaction.config_server import FeatureFlags
|
|
9
5
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
features: FeatureFlags = Field(default=FeatureFlags())
|
|
6
|
+
class WithHyperionFeatures(BaseModel):
|
|
7
|
+
features: HyperionFeatureFlags = Field(default=HyperionFeatureFlags())
|
|
@@ -27,7 +27,6 @@ class I03Constants:
|
|
|
27
27
|
OAV_CENTRING_FILE = OavConstants.OAV_CONFIG_JSON
|
|
28
28
|
SHUTTER_TIME_S = 0.06
|
|
29
29
|
USE_PANDA_FOR_GRIDSCAN = False
|
|
30
|
-
THAWING_TIME = 20
|
|
31
30
|
SET_STUB_OFFSETS = False
|
|
32
31
|
|
|
33
32
|
# Turns on GPU processing for zocalo and logs a comparison between GPU and CPU-
|
|
@@ -1,8 +1,5 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
import os
|
|
4
|
-
|
|
5
|
-
from dodal.devices.aperturescatterguard import ApertureValue
|
|
6
3
|
from dodal.devices.detector import (
|
|
7
4
|
DetectorParams,
|
|
8
5
|
)
|
|
@@ -15,38 +12,21 @@ from scanspec.core import Path as ScanPath
|
|
|
15
12
|
from scanspec.specs import Line, Static
|
|
16
13
|
|
|
17
14
|
from mx_bluesky.common.parameters.components import (
|
|
18
|
-
DiffractionExperimentWithSample,
|
|
19
|
-
IspybExperimentType,
|
|
20
|
-
OptionalGonioAngleStarts,
|
|
21
15
|
SplitScan,
|
|
22
16
|
WithOptionalEnergyChange,
|
|
23
|
-
|
|
24
|
-
|
|
17
|
+
WithPandaGridScan,
|
|
18
|
+
)
|
|
19
|
+
from mx_bluesky.common.parameters.gridscan import (
|
|
20
|
+
GridCommon,
|
|
21
|
+
SpecifiedGrid,
|
|
25
22
|
)
|
|
26
|
-
from mx_bluesky.
|
|
27
|
-
from mx_bluesky.hyperion.parameters.components import WithFeatures
|
|
23
|
+
from mx_bluesky.hyperion.parameters.components import WithHyperionFeatures
|
|
28
24
|
from mx_bluesky.hyperion.parameters.constants import CONST, I03Constants
|
|
29
|
-
from mx_bluesky.hyperion.parameters.robot_load import RobotLoadAndEnergyChange
|
|
30
25
|
|
|
31
26
|
|
|
32
|
-
|
|
33
|
-
#
|
|
34
|
-
|
|
35
|
-
DiffractionExperimentWithSample,
|
|
36
|
-
OptionalGonioAngleStarts,
|
|
37
|
-
WithFeatures,
|
|
38
|
-
):
|
|
39
|
-
grid_width_um: float = Field(default=CONST.PARAM.GRIDSCAN.WIDTH_UM)
|
|
40
|
-
exposure_time_s: float = Field(default=CONST.PARAM.GRIDSCAN.EXPOSURE_TIME_S)
|
|
41
|
-
use_roi_mode: bool = Field(default=CONST.PARAM.GRIDSCAN.USE_ROI)
|
|
42
|
-
panda_runup_distance_mm: float = Field(
|
|
43
|
-
default=GridscanParamConstants.PANDA_RUN_UP_DISTANCE_MM
|
|
44
|
-
)
|
|
45
|
-
|
|
46
|
-
ispyb_experiment_type: IspybExperimentType = Field(
|
|
47
|
-
default=IspybExperimentType.GRIDSCAN_3D
|
|
48
|
-
)
|
|
49
|
-
selected_aperture: ApertureValue | None = Field(default=ApertureValue.SMALL)
|
|
27
|
+
class HyperionGridCommon(GridCommon, WithHyperionFeatures):
|
|
28
|
+
# This class only exists so that we can properly select enable_dev_shm. Remove in
|
|
29
|
+
# https://github.com/DiamondLightSource/hyperion/issues/1395"""
|
|
50
30
|
|
|
51
31
|
@property
|
|
52
32
|
def detector_params(self):
|
|
@@ -60,7 +40,6 @@ class GridCommon(
|
|
|
60
40
|
assert (
|
|
61
41
|
self.detector_distance_mm is not None
|
|
62
42
|
), "Detector distance must be filled before generating DetectorParams"
|
|
63
|
-
os.makedirs(self.storage_directory, exist_ok=True)
|
|
64
43
|
return DetectorParams(
|
|
65
44
|
detector_size_constants=I03Constants.DETECTOR,
|
|
66
45
|
expected_energy_ev=self.demand_energy_ev,
|
|
@@ -80,36 +59,13 @@ class GridCommon(
|
|
|
80
59
|
)
|
|
81
60
|
|
|
82
61
|
|
|
83
|
-
class
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
class RobotLoadThenCentre(GridCommon):
|
|
92
|
-
thawing_time: float = Field(default=CONST.I03.THAWING_TIME)
|
|
93
|
-
|
|
94
|
-
def robot_load_params(self):
|
|
95
|
-
my_params = self.model_dump()
|
|
96
|
-
return RobotLoadAndEnergyChange(**my_params)
|
|
97
|
-
|
|
98
|
-
def pin_centre_then_xray_centre_params(self):
|
|
99
|
-
my_params = self.model_dump()
|
|
100
|
-
del my_params["thawing_time"]
|
|
101
|
-
return PinTipCentreThenXrayCentre(**my_params)
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
class SpecifiedGridScan(GridCommon, XyzStarts, WithScan):
|
|
105
|
-
"""A specified grid scan is one which has defined values for the start position,
|
|
106
|
-
grid and box sizes, etc., as opposed to parameters for a plan which will create
|
|
107
|
-
those parameters at some point (e.g. through optical pin detection)."""
|
|
108
|
-
|
|
109
|
-
...
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
class ThreeDGridScan(SpecifiedGridScan, SplitScan, WithOptionalEnergyChange):
|
|
62
|
+
class HyperionThreeDGridScan(
|
|
63
|
+
HyperionGridCommon,
|
|
64
|
+
SpecifiedGrid,
|
|
65
|
+
SplitScan,
|
|
66
|
+
WithOptionalEnergyChange,
|
|
67
|
+
WithPandaGridScan,
|
|
68
|
+
):
|
|
113
69
|
"""Parameters representing a so-called 3D grid scan, which consists of doing a
|
|
114
70
|
gridscan in X and Y, followed by one in X and Z."""
|
|
115
71
|
|
|
@@ -131,14 +87,14 @@ class ThreeDGridScan(SpecifiedGridScan, SplitScan, WithOptionalEnergyChange):
|
|
|
131
87
|
x_steps=self.x_steps,
|
|
132
88
|
y_steps=self.y_steps,
|
|
133
89
|
z_steps=self.z_steps,
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
90
|
+
x_step_size_mm=self.x_step_size_um / 1000,
|
|
91
|
+
y_step_size_mm=self.y_step_size_um / 1000,
|
|
92
|
+
z_step_size_mm=self.z_step_size_um / 1000,
|
|
93
|
+
x_start_mm=self.x_start_um / 1000,
|
|
94
|
+
y1_start_mm=self.y_start_um / 1000,
|
|
95
|
+
z1_start_mm=self.z_start_um / 1000,
|
|
96
|
+
y2_start_mm=self.y2_start_um / 1000,
|
|
97
|
+
z2_start_mm=self.z2_start_um / 1000,
|
|
142
98
|
set_stub_offsets=self.features.set_stub_offsets,
|
|
143
99
|
dwell_time_ms=self.exposure_time_s * 1000,
|
|
144
100
|
transmission_fraction=self.transmission_frac,
|
|
@@ -147,6 +103,7 @@ class ThreeDGridScan(SpecifiedGridScan, SplitScan, WithOptionalEnergyChange):
|
|
|
147
103
|
@property
|
|
148
104
|
def panda_FGS_params(self) -> PandAGridScanParams:
|
|
149
105
|
if self.y_steps % 2 and self.z_steps > 0:
|
|
106
|
+
# See https://github.com/DiamondLightSource/hyperion/issues/1118 for explanation
|
|
150
107
|
raise OddYStepsException(
|
|
151
108
|
"The number of Y steps must be even for a PandA gridscan"
|
|
152
109
|
)
|
|
@@ -154,14 +111,14 @@ class ThreeDGridScan(SpecifiedGridScan, SplitScan, WithOptionalEnergyChange):
|
|
|
154
111
|
x_steps=self.x_steps,
|
|
155
112
|
y_steps=self.y_steps,
|
|
156
113
|
z_steps=self.z_steps,
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
114
|
+
x_step_size_mm=self.x_step_size_um / 1000,
|
|
115
|
+
y_step_size_mm=self.y_step_size_um / 1000,
|
|
116
|
+
z_step_size_mm=self.z_step_size_um / 1000,
|
|
117
|
+
x_start_mm=self.x_start_um / 1000,
|
|
118
|
+
y1_start_mm=self.y_start_um / 1000,
|
|
119
|
+
z1_start_mm=self.z_start_um / 1000,
|
|
120
|
+
y2_start_mm=self.y2_start_um / 1000,
|
|
121
|
+
z2_start_mm=self.z2_start_um / 1000,
|
|
165
122
|
set_stub_offsets=self.features.set_stub_offsets,
|
|
166
123
|
run_up_distance_mm=self.panda_runup_distance_mm,
|
|
167
124
|
transmission_fraction=self.transmission_frac,
|
|
@@ -4,10 +4,11 @@ from pydantic import BaseModel, model_validator
|
|
|
4
4
|
|
|
5
5
|
from mx_bluesky.common.parameters.components import (
|
|
6
6
|
MxBlueskyParameters,
|
|
7
|
+
WithCentreSelection,
|
|
7
8
|
WithSample,
|
|
8
9
|
WithVisit,
|
|
9
10
|
)
|
|
10
|
-
from mx_bluesky.
|
|
11
|
+
from mx_bluesky.common.parameters.gridscan import (
|
|
11
12
|
RobotLoadThenCentre,
|
|
12
13
|
)
|
|
13
14
|
from mx_bluesky.hyperion.parameters.rotation import MultiRotationScan
|
|
@@ -15,13 +16,15 @@ from mx_bluesky.hyperion.parameters.rotation import MultiRotationScan
|
|
|
15
16
|
T = TypeVar("T", bound=BaseModel)
|
|
16
17
|
|
|
17
18
|
|
|
18
|
-
def construct_from_values(parent_context: dict,
|
|
19
|
-
values =
|
|
20
|
-
values |=
|
|
19
|
+
def construct_from_values(parent_context: dict, child_dict: dict, t: type[T]) -> T:
|
|
20
|
+
values = {k: v for k, v in parent_context.items() if not isinstance(v, dict)}
|
|
21
|
+
values |= child_dict
|
|
21
22
|
return t(**values)
|
|
22
23
|
|
|
23
24
|
|
|
24
|
-
class LoadCentreCollect(
|
|
25
|
+
class LoadCentreCollect(
|
|
26
|
+
MxBlueskyParameters, WithVisit, WithSample, WithCentreSelection
|
|
27
|
+
):
|
|
25
28
|
"""Experiment parameters to perform the combined robot load,
|
|
26
29
|
pin-tip centre and rotation scan operations."""
|
|
27
30
|
|
|
@@ -41,10 +44,12 @@ class LoadCentreCollect(MxBlueskyParameters, WithVisit, WithSample):
|
|
|
41
44
|
disallowed_keys == set()
|
|
42
45
|
), f"Unexpected fields found in LoadCentreCollect {disallowed_keys}"
|
|
43
46
|
|
|
44
|
-
|
|
45
|
-
values, "robot_load_then_centre", RobotLoadThenCentre
|
|
47
|
+
new_robot_load_then_centre_params = construct_from_values(
|
|
48
|
+
values, values["robot_load_then_centre"], RobotLoadThenCentre
|
|
46
49
|
)
|
|
47
|
-
|
|
48
|
-
values, "multi_rotation_scan", MultiRotationScan
|
|
50
|
+
new_multi_rotation_scan_params = construct_from_values(
|
|
51
|
+
values, values["multi_rotation_scan"], MultiRotationScan
|
|
49
52
|
)
|
|
53
|
+
values["multi_rotation_scan"] = new_multi_rotation_scan_params
|
|
54
|
+
values["robot_load_then_centre"] = new_robot_load_then_centre_params
|
|
50
55
|
return values
|
|
@@ -47,7 +47,9 @@ class RotationExperiment(DiffractionExperimentWithSample):
|
|
|
47
47
|
default=IspybExperimentType.ROTATION
|
|
48
48
|
)
|
|
49
49
|
|
|
50
|
-
def
|
|
50
|
+
def _detector_params_impl(
|
|
51
|
+
self, omega_start_deg: float, num_images_per_trigger: int, num_triggers: int
|
|
52
|
+
) -> DetectorParams:
|
|
51
53
|
self.det_dist_to_beam_converter_path = (
|
|
52
54
|
self.det_dist_to_beam_converter_path
|
|
53
55
|
or CONST.PARAM.DETECTOR.BEAM_XY_LUT_PATH
|
|
@@ -66,13 +68,16 @@ class RotationExperiment(DiffractionExperimentWithSample):
|
|
|
66
68
|
detector_distance=self.detector_distance_mm,
|
|
67
69
|
omega_start=omega_start_deg,
|
|
68
70
|
omega_increment=self.rotation_increment_deg,
|
|
69
|
-
num_images_per_trigger=
|
|
70
|
-
num_triggers=
|
|
71
|
+
num_images_per_trigger=num_images_per_trigger,
|
|
72
|
+
num_triggers=num_triggers,
|
|
71
73
|
use_roi_mode=False,
|
|
72
74
|
det_dist_to_beam_converter_path=self.det_dist_to_beam_converter_path,
|
|
73
75
|
**optional_args,
|
|
74
76
|
)
|
|
75
77
|
|
|
78
|
+
def _detector_params(self, omega_start_deg: float) -> DetectorParams:
|
|
79
|
+
return self._detector_params_impl(omega_start_deg, self.num_images, 1)
|
|
80
|
+
|
|
76
81
|
@field_validator("selected_aperture")
|
|
77
82
|
@classmethod
|
|
78
83
|
def _set_default_aperture_position(cls, aperture_position: ApertureValue | None):
|
|
@@ -115,7 +120,7 @@ class MultiRotationScan(RotationExperiment, SplitScan):
|
|
|
115
120
|
|
|
116
121
|
def _single_rotation_scan(self, scan: RotationScanPerSweep) -> RotationScan:
|
|
117
122
|
# self has everything from RotationExperiment
|
|
118
|
-
allowed_keys = RotationScan.model_fields.keys()
|
|
123
|
+
allowed_keys = RotationScan.model_fields.keys() # type: ignore # mypy doesn't recognise this as a property...
|
|
119
124
|
params_dump = self.model_dump()
|
|
120
125
|
# provided `scan` has everything from RotationScanPerSweep
|
|
121
126
|
scan_dump = scan.model_dump()
|
|
@@ -156,8 +161,12 @@ class MultiRotationScan(RotationExperiment, SplitScan):
|
|
|
156
161
|
return list(accumulate([0, *self._num_images_per_scan()]))
|
|
157
162
|
|
|
158
163
|
@property
|
|
159
|
-
def detector_params(self):
|
|
160
|
-
return self.
|
|
164
|
+
def detector_params(self) -> DetectorParams:
|
|
165
|
+
return self._detector_params_impl(
|
|
166
|
+
self.rotation_scans[0].omega_start_deg,
|
|
167
|
+
self._num_images_per_scan()[0],
|
|
168
|
+
len(self._num_images_per_scan()),
|
|
169
|
+
)
|
|
161
170
|
|
|
162
171
|
@property
|
|
163
172
|
def ispyb_params(self): # pyright: ignore
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: mx-bluesky
|
|
3
|
-
Version: 1.4.
|
|
3
|
+
Version: 1.4.1
|
|
4
4
|
Summary: Bluesky tools for MX Beamlines at DLS
|
|
5
5
|
Author-email: Dominic Oram <dominic.oram@diamond.ac.uk>
|
|
6
|
-
License:
|
|
6
|
+
License: Apache License
|
|
7
7
|
Version 2.0, January 2004
|
|
8
8
|
http://www.apache.org/licenses/
|
|
9
9
|
|
|
@@ -213,7 +213,7 @@ Classifier: Programming Language :: Python :: 3.12
|
|
|
213
213
|
Requires-Python: >=3.11
|
|
214
214
|
Description-Content-Type: text/x-rst
|
|
215
215
|
License-File: LICENSE
|
|
216
|
-
Requires-Dist:
|
|
216
|
+
Requires-Dist: annotated_types
|
|
217
217
|
Requires-Dist: caproto
|
|
218
218
|
Requires-Dist: fastapi[all]
|
|
219
219
|
Requires-Dist: flask-restful
|
|
@@ -233,38 +233,39 @@ Requires-Dist: requests
|
|
|
233
233
|
Requires-Dist: scanspec
|
|
234
234
|
Requires-Dist: scipy
|
|
235
235
|
Requires-Dist: semver
|
|
236
|
-
Requires-Dist:
|
|
237
|
-
Requires-Dist:
|
|
238
|
-
Requires-Dist:
|
|
239
|
-
Requires-Dist: ophyd
|
|
240
|
-
Requires-Dist:
|
|
241
|
-
Requires-Dist:
|
|
236
|
+
Requires-Dist: matplotlib
|
|
237
|
+
Requires-Dist: blueapi>=0.5.0
|
|
238
|
+
Requires-Dist: daq-config-server>=0.1.1
|
|
239
|
+
Requires-Dist: ophyd==1.9.0
|
|
240
|
+
Requires-Dist: ophyd-async>=0.8a5
|
|
241
|
+
Requires-Dist: bluesky>=1.13.0a4
|
|
242
|
+
Requires-Dist: dls-dodal==1.36.2
|
|
242
243
|
Provides-Extra: dev
|
|
243
|
-
Requires-Dist: black
|
|
244
|
-
Requires-Dist: build
|
|
245
|
-
Requires-Dist: diff-cover
|
|
246
|
-
Requires-Dist: GitPython
|
|
247
|
-
Requires-Dist: import-linter
|
|
248
|
-
Requires-Dist: ipython
|
|
249
|
-
Requires-Dist: mypy
|
|
250
|
-
Requires-Dist: myst-parser
|
|
251
|
-
Requires-Dist: pipdeptree
|
|
252
|
-
Requires-Dist: pre-commit
|
|
253
|
-
Requires-Dist: pydata-sphinx-theme
|
|
254
|
-
Requires-Dist: pyright
|
|
255
|
-
Requires-Dist: pytest-asyncio
|
|
256
|
-
Requires-Dist: pytest-cov
|
|
257
|
-
Requires-Dist: pytest-random-order
|
|
258
|
-
Requires-Dist: pytest
|
|
259
|
-
Requires-Dist: ruff
|
|
260
|
-
Requires-Dist: sphinx-autobuild
|
|
261
|
-
Requires-Dist: sphinx-copybutton
|
|
262
|
-
Requires-Dist: sphinxcontrib-plantuml
|
|
263
|
-
Requires-Dist: sphinx-design
|
|
264
|
-
Requires-Dist: tox-direct
|
|
265
|
-
Requires-Dist: tox
|
|
266
|
-
Requires-Dist: types-mock
|
|
267
|
-
Requires-Dist: types-requests
|
|
244
|
+
Requires-Dist: black; extra == "dev"
|
|
245
|
+
Requires-Dist: build; extra == "dev"
|
|
246
|
+
Requires-Dist: diff-cover; extra == "dev"
|
|
247
|
+
Requires-Dist: GitPython; extra == "dev"
|
|
248
|
+
Requires-Dist: import-linter; extra == "dev"
|
|
249
|
+
Requires-Dist: ipython; extra == "dev"
|
|
250
|
+
Requires-Dist: mypy; extra == "dev"
|
|
251
|
+
Requires-Dist: myst-parser; extra == "dev"
|
|
252
|
+
Requires-Dist: pipdeptree; extra == "dev"
|
|
253
|
+
Requires-Dist: pre-commit; extra == "dev"
|
|
254
|
+
Requires-Dist: pydata-sphinx-theme>=0.12; extra == "dev"
|
|
255
|
+
Requires-Dist: pyright; extra == "dev"
|
|
256
|
+
Requires-Dist: pytest-asyncio; extra == "dev"
|
|
257
|
+
Requires-Dist: pytest-cov; extra == "dev"
|
|
258
|
+
Requires-Dist: pytest-random-order; extra == "dev"
|
|
259
|
+
Requires-Dist: pytest; extra == "dev"
|
|
260
|
+
Requires-Dist: ruff; extra == "dev"
|
|
261
|
+
Requires-Dist: sphinx-autobuild; extra == "dev"
|
|
262
|
+
Requires-Dist: sphinx-copybutton; extra == "dev"
|
|
263
|
+
Requires-Dist: sphinxcontrib-plantuml; extra == "dev"
|
|
264
|
+
Requires-Dist: sphinx-design; extra == "dev"
|
|
265
|
+
Requires-Dist: tox-direct; extra == "dev"
|
|
266
|
+
Requires-Dist: tox; extra == "dev"
|
|
267
|
+
Requires-Dist: types-mock; extra == "dev"
|
|
268
|
+
Requires-Dist: types-requests; extra == "dev"
|
|
268
269
|
|
|
269
270
|
mx-bluesky
|
|
270
271
|
===========================
|