mx-bluesky 1.1.0__py3-none-any.whl → 1.4.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/__init__.py +8 -3
- mx_bluesky/__main__.py +12 -7
- mx_bluesky/_version.py +2 -2
- mx_bluesky/beamlines/i04/callbacks/murko_callback.py +14 -4
- mx_bluesky/beamlines/i04/thawing_plan.py +48 -10
- mx_bluesky/beamlines/i24/serial/__init__.py +3 -0
- mx_bluesky/beamlines/i24/serial/extruder/i24ssx_Extruder_Collect_py3v2.py +68 -90
- mx_bluesky/beamlines/i24/serial/fixed_target/FT-gui-edm/DiamondChipI24-py3v1.edl +1 -1
- mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_Chip_Collect_py3v1.py +104 -126
- mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_Chip_Manager_py3v1.py +139 -162
- mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_Chip_Mapping_py3v1.py +25 -36
- mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_Chip_StartUp_py3v1.py +24 -34
- mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_moveonclick.py +14 -11
- mx_bluesky/beamlines/i24/serial/log.py +58 -49
- mx_bluesky/beamlines/i24/serial/parameters/fixed_target/cs/cs_maker.json +3 -3
- mx_bluesky/beamlines/i24/serial/run_extruder.sh +30 -5
- mx_bluesky/beamlines/i24/serial/run_fixed_target.sh +31 -7
- mx_bluesky/beamlines/i24/serial/run_serial.py +24 -8
- mx_bluesky/beamlines/i24/serial/setup_beamline/ca.py +0 -2
- mx_bluesky/beamlines/i24/serial/setup_beamline/setup_beamline.py +1 -1
- mx_bluesky/beamlines/i24/serial/setup_beamline/setup_detector.py +8 -18
- mx_bluesky/beamlines/i24/serial/setup_beamline/setup_zebra_plans.py +2 -2
- mx_bluesky/common/__init__.py +0 -0
- mx_bluesky/common/device_setup_plans/read_hardware_for_setup.py +14 -0
- mx_bluesky/common/parameters/components.py +221 -0
- mx_bluesky/common/parameters/constants.py +133 -0
- mx_bluesky/common/plans/__init__.py +1 -0
- mx_bluesky/common/plans/do_fgs.py +121 -0
- mx_bluesky/common/utils/log.py +116 -0
- mx_bluesky/{hyperion → common/utils}/tracing.py +2 -2
- mx_bluesky/hyperion/__main__.py +11 -9
- mx_bluesky/hyperion/device_setup_plans/dcm_pitch_roll_mirror_adjuster.py +31 -26
- mx_bluesky/hyperion/device_setup_plans/read_hardware_for_setup.py +6 -12
- mx_bluesky/hyperion/device_setup_plans/setup_oav.py +6 -12
- mx_bluesky/hyperion/device_setup_plans/setup_panda.py +1 -2
- mx_bluesky/hyperion/device_setup_plans/setup_zebra.py +48 -17
- mx_bluesky/hyperion/device_setup_plans/smargon.py +6 -6
- mx_bluesky/hyperion/device_setup_plans/utils.py +13 -2
- mx_bluesky/hyperion/device_setup_plans/xbpm_feedback.py +4 -4
- mx_bluesky/hyperion/experiment_plans/experiment_registry.py +9 -0
- mx_bluesky/hyperion/experiment_plans/flyscan_xray_centre_plan.py +59 -108
- mx_bluesky/hyperion/experiment_plans/grid_detect_then_xray_centre_plan.py +7 -5
- mx_bluesky/hyperion/experiment_plans/load_centre_collect_full_plan.py +46 -0
- mx_bluesky/hyperion/experiment_plans/oav_grid_detection_plan.py +19 -18
- mx_bluesky/hyperion/experiment_plans/oav_snapshot_plan.py +8 -5
- mx_bluesky/hyperion/experiment_plans/pin_centre_then_xray_centre_plan.py +4 -4
- mx_bluesky/hyperion/experiment_plans/pin_tip_centring_plan.py +17 -17
- mx_bluesky/hyperion/experiment_plans/robot_load_and_change_energy.py +241 -0
- mx_bluesky/hyperion/experiment_plans/robot_load_then_centre_plan.py +24 -181
- mx_bluesky/hyperion/experiment_plans/rotation_scan_plan.py +6 -4
- mx_bluesky/hyperion/experiment_plans/set_energy_plan.py +3 -11
- mx_bluesky/hyperion/external_interaction/callbacks/__main__.py +1 -2
- mx_bluesky/hyperion/external_interaction/callbacks/common/callback_util.py +18 -0
- mx_bluesky/hyperion/external_interaction/callbacks/common/ispyb_mapping.py +1 -9
- mx_bluesky/hyperion/external_interaction/callbacks/grid_detection_callback.py +18 -13
- mx_bluesky/hyperion/external_interaction/callbacks/ispyb_callback_base.py +32 -15
- mx_bluesky/hyperion/external_interaction/callbacks/log_uid_tag_callback.py +1 -1
- mx_bluesky/hyperion/external_interaction/callbacks/robot_load/ispyb_callback.py +3 -5
- mx_bluesky/hyperion/external_interaction/callbacks/rotation/ispyb_callback.py +4 -3
- mx_bluesky/hyperion/external_interaction/callbacks/xray_centre/ispyb_callback.py +23 -18
- mx_bluesky/hyperion/external_interaction/config_server.py +22 -10
- mx_bluesky/hyperion/external_interaction/ispyb/ispyb_store.py +1 -1
- mx_bluesky/hyperion/external_interaction/ispyb/ispyb_utils.py +0 -2
- mx_bluesky/hyperion/external_interaction/nexus/nexus_utils.py +2 -2
- mx_bluesky/hyperion/external_interaction/nexus/write_nexus.py +1 -1
- mx_bluesky/hyperion/log.py +0 -84
- mx_bluesky/hyperion/parameters/components.py +1 -242
- mx_bluesky/hyperion/parameters/constants.py +22 -118
- mx_bluesky/hyperion/parameters/gridscan.py +20 -11
- mx_bluesky/hyperion/parameters/load_centre_collect.py +50 -0
- mx_bluesky/hyperion/parameters/robot_load.py +16 -0
- mx_bluesky/hyperion/parameters/rotation.py +9 -5
- mx_bluesky/hyperion/utils/utils.py +17 -0
- mx_bluesky/hyperion/utils/validation.py +5 -6
- {mx_bluesky-1.1.0.dist-info → mx_bluesky-1.4.0.dist-info}/METADATA +4 -2
- {mx_bluesky-1.1.0.dist-info → mx_bluesky-1.4.0.dist-info}/RECORD +80 -70
- {mx_bluesky-1.1.0.dist-info → mx_bluesky-1.4.0.dist-info}/WHEEL +1 -1
- mx_bluesky/example.py +0 -19
- {mx_bluesky-1.1.0.dist-info → mx_bluesky-1.4.0.dist-info}/LICENSE +0 -0
- {mx_bluesky-1.1.0.dist-info → mx_bluesky-1.4.0.dist-info}/entry_points.txt +0 -0
- {mx_bluesky-1.1.0.dist-info → mx_bluesky-1.4.0.dist-info}/top_level.txt +0 -0
|
@@ -1,253 +1,12 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
import datetime
|
|
4
|
-
import json
|
|
5
|
-
from abc import abstractmethod
|
|
6
|
-
from collections.abc import Sequence
|
|
7
|
-
from enum import StrEnum
|
|
8
|
-
from pathlib import Path
|
|
9
|
-
from typing import SupportsInt, TypeVar
|
|
10
|
-
|
|
11
|
-
from dodal.devices.aperturescatterguard import ApertureValue
|
|
12
|
-
from dodal.devices.detector import (
|
|
13
|
-
DetectorParams,
|
|
14
|
-
TriggerMode,
|
|
15
|
-
)
|
|
16
3
|
from pydantic import (
|
|
17
4
|
BaseModel,
|
|
18
|
-
ConfigDict,
|
|
19
5
|
Field,
|
|
20
|
-
field_serializer,
|
|
21
|
-
field_validator,
|
|
22
|
-
model_validator,
|
|
23
6
|
)
|
|
24
|
-
from scanspec.core import AxesPoints
|
|
25
|
-
from semver import Version
|
|
26
7
|
|
|
27
8
|
from mx_bluesky.hyperion.external_interaction.config_server import FeatureFlags
|
|
28
|
-
from mx_bluesky.hyperion.parameters.constants import CONST
|
|
29
|
-
|
|
30
|
-
T = TypeVar("T")
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
class ParameterVersion(Version):
|
|
34
|
-
@classmethod
|
|
35
|
-
def _parse(cls, version):
|
|
36
|
-
if isinstance(version, cls):
|
|
37
|
-
return version
|
|
38
|
-
return cls.parse(version)
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
PARAMETER_VERSION = ParameterVersion.parse("5.1.0")
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
class RotationAxis(StrEnum):
|
|
45
|
-
OMEGA = "omega"
|
|
46
|
-
PHI = "phi"
|
|
47
|
-
CHI = "chi"
|
|
48
|
-
KAPPA = "kappa"
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
class XyzAxis(StrEnum):
|
|
52
|
-
X = "sam_x"
|
|
53
|
-
Y = "sam_y"
|
|
54
|
-
Z = "sam_z"
|
|
55
|
-
|
|
56
9
|
|
|
57
|
-
class IspybExperimentType(StrEnum):
|
|
58
|
-
# Enum values from ispyb column data type
|
|
59
|
-
SAD = "SAD" # at or slightly above the peak
|
|
60
|
-
SAD_INVERSE_BEAM = "SAD - Inverse Beam"
|
|
61
|
-
OSC = "OSC" # "native" (in the absence of a heavy atom)
|
|
62
|
-
COLLECT_MULTIWEDGE = (
|
|
63
|
-
"Collect - Multiwedge" # "poorly determined" ~ EDNA complex strategy???
|
|
64
|
-
)
|
|
65
|
-
MAD = "MAD"
|
|
66
|
-
HELICAL = "Helical"
|
|
67
|
-
MULTI_POSITIONAL = "Multi-positional"
|
|
68
|
-
MESH = "Mesh"
|
|
69
|
-
BURN = "Burn"
|
|
70
|
-
MAD_INVERSE_BEAM = "MAD - Inverse Beam"
|
|
71
|
-
CHARACTERIZATION = "Characterization"
|
|
72
|
-
DEHYDRATION = "Dehydration"
|
|
73
|
-
TOMO = "tomo"
|
|
74
|
-
EXPERIMENT = "experiment"
|
|
75
|
-
EM = "EM"
|
|
76
|
-
PDF = "PDF"
|
|
77
|
-
PDF_BRAGG = "PDF+Bragg"
|
|
78
|
-
BRAGG = "Bragg"
|
|
79
|
-
SINGLE_PARTICLE = "single particle"
|
|
80
|
-
SERIAL_FIXED = "Serial Fixed"
|
|
81
|
-
SERIAL_JET = "Serial Jet"
|
|
82
|
-
STANDARD = "Standard" # Routine structure determination experiment
|
|
83
|
-
TIME_RESOLVED = "Time Resolved" # Investigate the change of a system over time
|
|
84
|
-
DLS_ANVIL_HP = "Diamond Anvil High Pressure" # HP sample environment pressure cell
|
|
85
|
-
CUSTOM = "Custom" # Special or non-standard data collection
|
|
86
|
-
XRF_MAP = "XRF map"
|
|
87
|
-
ENERGY_SCAN = "Energy scan"
|
|
88
|
-
XRF_SPECTRUM = "XRF spectrum"
|
|
89
|
-
XRF_MAP_XAS = "XRF map xas"
|
|
90
|
-
MESH_3D = "Mesh3D"
|
|
91
|
-
SCREENING = "Screening"
|
|
92
|
-
STILL = "Still"
|
|
93
|
-
SSX_CHIP = "SSX-Chip"
|
|
94
|
-
SSX_JET = "SSX-Jet"
|
|
95
|
-
|
|
96
|
-
# Aliases for historic hyperion experiment type mapping
|
|
97
|
-
ROTATION = "SAD"
|
|
98
|
-
GRIDSCAN_2D = "mesh"
|
|
99
|
-
GRIDSCAN_3D = "Mesh3D"
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
class HyperionParameters(BaseModel):
|
|
103
|
-
model_config = ConfigDict(
|
|
104
|
-
arbitrary_types_allowed=True,
|
|
105
|
-
extra="allow",
|
|
106
|
-
)
|
|
107
|
-
|
|
108
|
-
def __hash__(self) -> int:
|
|
109
|
-
return self.json().__hash__()
|
|
110
10
|
|
|
11
|
+
class WithFeatures(BaseModel):
|
|
111
12
|
features: FeatureFlags = Field(default=FeatureFlags())
|
|
112
|
-
parameter_model_version: ParameterVersion
|
|
113
|
-
|
|
114
|
-
@field_serializer("parameter_model_version")
|
|
115
|
-
def serialize_parameter_version(self, version: ParameterVersion):
|
|
116
|
-
return str(version)
|
|
117
|
-
|
|
118
|
-
@field_validator("parameter_model_version", mode="before")
|
|
119
|
-
@classmethod
|
|
120
|
-
def _validate_version(cls, version_str: str):
|
|
121
|
-
version = ParameterVersion.parse(version_str)
|
|
122
|
-
assert (
|
|
123
|
-
version >= ParameterVersion(major=PARAMETER_VERSION.major)
|
|
124
|
-
), f"Parameter version too old! This version of hyperion uses {PARAMETER_VERSION}"
|
|
125
|
-
assert (
|
|
126
|
-
version <= ParameterVersion(major=PARAMETER_VERSION.major + 1)
|
|
127
|
-
), f"Parameter version too new! This version of hyperion uses {PARAMETER_VERSION}"
|
|
128
|
-
return version
|
|
129
|
-
|
|
130
|
-
@classmethod
|
|
131
|
-
def from_json(cls, input: str | None):
|
|
132
|
-
assert input is not None
|
|
133
|
-
return cls(**json.loads(input))
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
class WithSnapshot(BaseModel):
|
|
137
|
-
snapshot_directory: Path
|
|
138
|
-
snapshot_omegas_deg: list[float] | None = None
|
|
139
|
-
|
|
140
|
-
@property
|
|
141
|
-
def take_snapshots(self) -> bool:
|
|
142
|
-
return bool(self.snapshot_omegas_deg)
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
class DiffractionExperiment(HyperionParameters, WithSnapshot):
|
|
146
|
-
"""For all experiments which use beam"""
|
|
147
|
-
|
|
148
|
-
visit: str = Field(min_length=1)
|
|
149
|
-
file_name: str
|
|
150
|
-
exposure_time_s: float = Field(gt=0)
|
|
151
|
-
comment: str = Field(default="")
|
|
152
|
-
beamline: str = Field(default=CONST.I03.BEAMLINE, pattern=r"BL\d{2}[BIJS]")
|
|
153
|
-
insertion_prefix: str = Field(
|
|
154
|
-
default=CONST.I03.INSERTION_PREFIX, pattern=r"SR\d{2}[BIJS]"
|
|
155
|
-
)
|
|
156
|
-
det_dist_to_beam_converter_path: str = Field(
|
|
157
|
-
default=CONST.PARAM.DETECTOR.BEAM_XY_LUT_PATH
|
|
158
|
-
)
|
|
159
|
-
zocalo_environment: str = Field(default=CONST.ZOCALO_ENV)
|
|
160
|
-
trigger_mode: TriggerMode = Field(default=TriggerMode.FREE_RUN)
|
|
161
|
-
detector_distance_mm: float | None = Field(default=None, gt=0)
|
|
162
|
-
demand_energy_ev: float | None = Field(default=None, gt=0)
|
|
163
|
-
run_number: int | None = Field(default=None, ge=0)
|
|
164
|
-
selected_aperture: ApertureValue | None = Field(default=None)
|
|
165
|
-
transmission_frac: float = Field(default=0.1)
|
|
166
|
-
ispyb_experiment_type: IspybExperimentType
|
|
167
|
-
storage_directory: str
|
|
168
|
-
|
|
169
|
-
@model_validator(mode="before")
|
|
170
|
-
@classmethod
|
|
171
|
-
def validate_snapshot_directory(cls, values):
|
|
172
|
-
snapshot_dir = values.get(
|
|
173
|
-
"snapshot_directory", Path(values["storage_directory"], "snapshots")
|
|
174
|
-
)
|
|
175
|
-
values["snapshot_directory"] = (
|
|
176
|
-
snapshot_dir if isinstance(snapshot_dir, Path) else Path(snapshot_dir)
|
|
177
|
-
)
|
|
178
|
-
return values
|
|
179
|
-
|
|
180
|
-
@property
|
|
181
|
-
def visit_directory(self) -> Path:
|
|
182
|
-
return (
|
|
183
|
-
Path(CONST.I03.BASE_DATA_DIR) / str(datetime.date.today().year) / self.visit
|
|
184
|
-
)
|
|
185
|
-
|
|
186
|
-
@property
|
|
187
|
-
def num_images(self) -> int:
|
|
188
|
-
return 0
|
|
189
|
-
|
|
190
|
-
@property
|
|
191
|
-
@abstractmethod
|
|
192
|
-
def detector_params(self) -> DetectorParams: ...
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
class WithScan(BaseModel):
|
|
196
|
-
"""For experiments where the scan is known"""
|
|
197
|
-
|
|
198
|
-
@property
|
|
199
|
-
@abstractmethod
|
|
200
|
-
def scan_points(self) -> AxesPoints: ...
|
|
201
|
-
|
|
202
|
-
@property
|
|
203
|
-
@abstractmethod
|
|
204
|
-
def num_images(self) -> int: ...
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
class SplitScan(BaseModel):
|
|
208
|
-
@property
|
|
209
|
-
@abstractmethod
|
|
210
|
-
def scan_indices(self) -> Sequence[SupportsInt]:
|
|
211
|
-
"""Should return the first index of each scan (i.e. for each nexus file)"""
|
|
212
|
-
...
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
class WithSample(BaseModel):
|
|
216
|
-
sample_id: int
|
|
217
|
-
sample_puck: int | None = None
|
|
218
|
-
sample_pin: int | None = None
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
class DiffractionExperimentWithSample(DiffractionExperiment, WithSample): ...
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
class WithOavCentring(BaseModel):
|
|
225
|
-
oav_centring_file: str = Field(default=CONST.I03.OAV_CENTRING_FILE)
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
class OptionalXyzStarts(BaseModel):
|
|
229
|
-
x_start_um: float | None = None
|
|
230
|
-
y_start_um: float | None = None
|
|
231
|
-
z_start_um: float | None = None
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
class XyzStarts(BaseModel):
|
|
235
|
-
x_start_um: float
|
|
236
|
-
y_start_um: float
|
|
237
|
-
z_start_um: float
|
|
238
|
-
|
|
239
|
-
def _start_for_axis(self, axis: XyzAxis) -> float:
|
|
240
|
-
match axis:
|
|
241
|
-
case XyzAxis.X:
|
|
242
|
-
return self.x_start_um
|
|
243
|
-
case XyzAxis.Y:
|
|
244
|
-
return self.y_start_um
|
|
245
|
-
case XyzAxis.Z:
|
|
246
|
-
return self.z_start_um
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
class OptionalGonioAngleStarts(BaseModel):
|
|
250
|
-
omega_start_deg: float | None = None
|
|
251
|
-
phi_start_deg: float | None = None
|
|
252
|
-
chi_start_deg: float | None = None
|
|
253
|
-
kappa_start_deg: float | None = None
|
|
@@ -1,107 +1,21 @@
|
|
|
1
1
|
import os
|
|
2
|
-
from enum import Enum
|
|
3
2
|
|
|
4
|
-
from dodal.devices.aperturescatterguard import ApertureValue
|
|
5
3
|
from dodal.devices.detector import EIGER2_X_16M_SIZE
|
|
6
4
|
from pydantic.dataclasses import dataclass
|
|
7
5
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
DEV_ISPYB_DATABASE_CFG = "/dls_sw/dasc/mariadb/credentials/ispyb-hyperion-dev.cfg"
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
@dataclass(frozen=True)
|
|
23
|
-
class PlanNameConstants:
|
|
24
|
-
# Robot load subplan
|
|
25
|
-
ROBOT_LOAD = "robot_load"
|
|
26
|
-
# Gridscan
|
|
27
|
-
GRID_DETECT_AND_DO_GRIDSCAN = "grid_detect_and_do_gridscan"
|
|
28
|
-
GRID_DETECT_INNER = "grid_detect"
|
|
29
|
-
GRIDSCAN_OUTER = "run_gridscan_move_and_tidy"
|
|
30
|
-
GRIDSCAN_AND_MOVE = "run_gridscan_and_move"
|
|
31
|
-
GRIDSCAN_MAIN = "run_gridscan"
|
|
32
|
-
DO_FGS = "do_fgs"
|
|
33
|
-
# Rotation scan
|
|
34
|
-
ROTATION_MULTI = "multi_rotation_wrapper"
|
|
35
|
-
ROTATION_OUTER = "rotation_scan_with_cleanup"
|
|
36
|
-
ROTATION_MAIN = "rotation_scan_main"
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
@dataclass(frozen=True)
|
|
40
|
-
class PlanGroupCheckpointConstants:
|
|
41
|
-
# For places to synchronise / stop and wait in plans, use as bluesky group names
|
|
42
|
-
GRID_READY_FOR_DC = "grid_ready_for_data_collection"
|
|
43
|
-
ROTATION_READY_FOR_DC = "rotation_ready_for_data_collection"
|
|
44
|
-
MOVE_GONIO_TO_START = "move_gonio_to_start"
|
|
45
|
-
READY_FOR_OAV = "ready_for_oav"
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
@dataclass(frozen=True)
|
|
49
|
-
class DocDescriptorNames:
|
|
50
|
-
# Robot load event descriptor
|
|
51
|
-
ROBOT_LOAD = "robot_load"
|
|
52
|
-
# For callbacks to use
|
|
53
|
-
OAV_ROTATION_SNAPSHOT_TRIGGERED = "rotation_snapshot_triggered"
|
|
54
|
-
OAV_GRID_SNAPSHOT_TRIGGERED = "snapshot_to_ispyb"
|
|
55
|
-
HARDWARE_READ_PRE = "read_hardware_for_callbacks_pre_collection"
|
|
56
|
-
HARDWARE_READ_DURING = "read_hardware_for_callbacks_during_collection"
|
|
57
|
-
ZOCALO_HW_READ = "zocalo_read_hardware_plan"
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
@dataclass(frozen=True)
|
|
61
|
-
class HardwareConstants:
|
|
62
|
-
OAV_REFRESH_DELAY = 0.3
|
|
63
|
-
PANDA_FGS_RUN_UP_DEFAULT = 0.17
|
|
64
|
-
CRYOJET_MARGIN_MM = 0.2
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
@dataclass(frozen=True)
|
|
68
|
-
class TriggerConstants:
|
|
69
|
-
ZOCALO = "trigger_zocalo_on"
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
@dataclass(frozen=True)
|
|
73
|
-
class GridscanParamConstants:
|
|
74
|
-
WIDTH_UM = 600.0
|
|
75
|
-
EXPOSURE_TIME_S = 0.004
|
|
76
|
-
USE_ROI = True
|
|
77
|
-
BOX_WIDTH_UM = 20.0
|
|
78
|
-
OMEGA_1 = 0.0
|
|
79
|
-
OMEGA_2 = 90.0
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
@dataclass(frozen=True)
|
|
83
|
-
class RotationParamConstants:
|
|
84
|
-
DEFAULT_APERTURE_POSITION = ApertureValue.LARGE
|
|
85
|
-
|
|
6
|
+
from mx_bluesky.common.parameters.constants import (
|
|
7
|
+
DocDescriptorNames,
|
|
8
|
+
EnvironmentConstants,
|
|
9
|
+
ExperimentParamConstants,
|
|
10
|
+
HardwareConstants,
|
|
11
|
+
OavConstants,
|
|
12
|
+
PlanGroupCheckpointConstants,
|
|
13
|
+
PlanNameConstants,
|
|
14
|
+
SimConstants,
|
|
15
|
+
TriggerConstants,
|
|
16
|
+
)
|
|
86
17
|
|
|
87
|
-
|
|
88
|
-
class DetectorParamConstants:
|
|
89
|
-
BEAM_XY_LUT_PATH = (
|
|
90
|
-
"tests/test_data/test_det_dist_converter.txt"
|
|
91
|
-
if TEST_MODE
|
|
92
|
-
else "/dls_sw/i03/software/daq_configuration/lookup/DetDistToBeamXYConverter.txt"
|
|
93
|
-
)
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
@dataclass(frozen=True)
|
|
97
|
-
class ExperimentParamConstants:
|
|
98
|
-
DETECTOR = DetectorParamConstants()
|
|
99
|
-
GRIDSCAN = GridscanParamConstants()
|
|
100
|
-
ROTATION = RotationParamConstants()
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
_test_oav_file = "tests/test_data/test_OAVCentring.json"
|
|
104
|
-
_live_oav_file = "/dls_sw/i03/software/daq_configuration/json/OAVCentring_hyperion.json"
|
|
18
|
+
TEST_MODE = os.environ.get("HYPERION_TEST_MODE")
|
|
105
19
|
|
|
106
20
|
|
|
107
21
|
@dataclass(frozen=True)
|
|
@@ -110,16 +24,22 @@ class I03Constants:
|
|
|
110
24
|
BEAMLINE = "BL03S" if TEST_MODE else "BL03I"
|
|
111
25
|
DETECTOR = EIGER2_X_16M_SIZE
|
|
112
26
|
INSERTION_PREFIX = "SR03S" if TEST_MODE else "SR03I"
|
|
113
|
-
OAV_CENTRING_FILE =
|
|
27
|
+
OAV_CENTRING_FILE = OavConstants.OAV_CONFIG_JSON
|
|
114
28
|
SHUTTER_TIME_S = 0.06
|
|
115
29
|
USE_PANDA_FOR_GRIDSCAN = False
|
|
116
|
-
USE_GPU_FOR_GRIDSCAN_ANALYSIS = False
|
|
117
30
|
THAWING_TIME = 20
|
|
118
|
-
|
|
31
|
+
SET_STUB_OFFSETS = False
|
|
32
|
+
|
|
33
|
+
# Turns on GPU processing for zocalo and logs a comparison between GPU and CPU-
|
|
34
|
+
# processed results. GPU results never used in analysis for now
|
|
35
|
+
COMPARE_CPU_AND_GPU_ZOCALO = False
|
|
119
36
|
|
|
120
37
|
|
|
121
38
|
@dataclass(frozen=True)
|
|
122
39
|
class HyperionConstants:
|
|
40
|
+
DESCRIPTORS = DocDescriptorNames()
|
|
41
|
+
TRIGGER = TriggerConstants()
|
|
42
|
+
ZOCALO_ENV = EnvironmentConstants.ZOCALO_ENV
|
|
123
43
|
HARDWARE = HardwareConstants()
|
|
124
44
|
I03 = I03Constants()
|
|
125
45
|
PARAM = ExperimentParamConstants()
|
|
@@ -136,23 +56,7 @@ class HyperionConstants:
|
|
|
136
56
|
)
|
|
137
57
|
GRAYLOG_PORT = 12232
|
|
138
58
|
PARAMETER_SCHEMA_DIRECTORY = "src/hyperion/parameters/schemas/"
|
|
139
|
-
|
|
59
|
+
LOG_FILE_NAME = "hyperion.log"
|
|
140
60
|
|
|
141
61
|
|
|
142
62
|
CONST = HyperionConstants()
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
class Actions(Enum):
|
|
146
|
-
START = "start"
|
|
147
|
-
STOP = "stop"
|
|
148
|
-
SHUTDOWN = "shutdown"
|
|
149
|
-
STATUS = "status"
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
class Status(Enum):
|
|
153
|
-
WARN = "Warn"
|
|
154
|
-
FAILED = "Failed"
|
|
155
|
-
SUCCESS = "Success"
|
|
156
|
-
BUSY = "Busy"
|
|
157
|
-
ABORTING = "Aborting"
|
|
158
|
-
IDLE = "Idle"
|
|
@@ -14,30 +14,35 @@ from pydantic import Field, PrivateAttr
|
|
|
14
14
|
from scanspec.core import Path as ScanPath
|
|
15
15
|
from scanspec.specs import Line, Static
|
|
16
16
|
|
|
17
|
-
from mx_bluesky.
|
|
17
|
+
from mx_bluesky.common.parameters.components import (
|
|
18
18
|
DiffractionExperimentWithSample,
|
|
19
19
|
IspybExperimentType,
|
|
20
20
|
OptionalGonioAngleStarts,
|
|
21
21
|
SplitScan,
|
|
22
|
-
|
|
22
|
+
WithOptionalEnergyChange,
|
|
23
23
|
WithScan,
|
|
24
24
|
XyzStarts,
|
|
25
25
|
)
|
|
26
|
+
from mx_bluesky.common.parameters.constants import GridscanParamConstants
|
|
27
|
+
from mx_bluesky.hyperion.parameters.components import WithFeatures
|
|
26
28
|
from mx_bluesky.hyperion.parameters.constants import CONST, I03Constants
|
|
29
|
+
from mx_bluesky.hyperion.parameters.robot_load import RobotLoadAndEnergyChange
|
|
27
30
|
|
|
28
31
|
|
|
32
|
+
# This will be restructed once Once https://github.com/DiamondLightSource/mx-bluesky/issues/323#issue-2500957290 is further along
|
|
33
|
+
# to handle slightly different parameters between different beamline implementations
|
|
29
34
|
class GridCommon(
|
|
30
|
-
DiffractionExperimentWithSample,
|
|
35
|
+
DiffractionExperimentWithSample,
|
|
36
|
+
OptionalGonioAngleStarts,
|
|
37
|
+
WithFeatures,
|
|
31
38
|
):
|
|
32
39
|
grid_width_um: float = Field(default=CONST.PARAM.GRIDSCAN.WIDTH_UM)
|
|
33
40
|
exposure_time_s: float = Field(default=CONST.PARAM.GRIDSCAN.EXPOSURE_TIME_S)
|
|
34
41
|
use_roi_mode: bool = Field(default=CONST.PARAM.GRIDSCAN.USE_ROI)
|
|
35
42
|
panda_runup_distance_mm: float = Field(
|
|
36
|
-
default=
|
|
43
|
+
default=GridscanParamConstants.PANDA_RUN_UP_DISTANCE_MM
|
|
37
44
|
)
|
|
38
|
-
|
|
39
|
-
use_gpu: bool = Field(default=CONST.I03.USE_GPU_FOR_GRIDSCAN_ANALYSIS)
|
|
40
|
-
use_cpu_and_gpu_zocalo: bool = Field(default=CONST.I03.USE_CPU_AND_GPU_ZOCALO)
|
|
45
|
+
|
|
41
46
|
ispyb_experiment_type: IspybExperimentType = Field(
|
|
42
47
|
default=IspybExperimentType.GRIDSCAN_3D
|
|
43
48
|
)
|
|
@@ -70,12 +75,13 @@ class GridCommon(
|
|
|
70
75
|
use_roi_mode=self.use_roi_mode,
|
|
71
76
|
det_dist_to_beam_converter_path=self.det_dist_to_beam_converter_path,
|
|
72
77
|
trigger_mode=self.trigger_mode,
|
|
73
|
-
enable_dev_shm=self.
|
|
78
|
+
enable_dev_shm=self.features.compare_cpu_and_gpu_zocalo,
|
|
74
79
|
**optional_args,
|
|
75
80
|
)
|
|
76
81
|
|
|
77
82
|
|
|
78
|
-
class GridScanWithEdgeDetect(GridCommon):
|
|
83
|
+
class GridScanWithEdgeDetect(GridCommon):
|
|
84
|
+
box_size_um: float = Field(default=CONST.PARAM.GRIDSCAN.BOX_WIDTH_UM)
|
|
79
85
|
|
|
80
86
|
|
|
81
87
|
class PinTipCentreThenXrayCentre(GridCommon):
|
|
@@ -85,6 +91,10 @@ class PinTipCentreThenXrayCentre(GridCommon):
|
|
|
85
91
|
class RobotLoadThenCentre(GridCommon):
|
|
86
92
|
thawing_time: float = Field(default=CONST.I03.THAWING_TIME)
|
|
87
93
|
|
|
94
|
+
def robot_load_params(self):
|
|
95
|
+
my_params = self.model_dump()
|
|
96
|
+
return RobotLoadAndEnergyChange(**my_params)
|
|
97
|
+
|
|
88
98
|
def pin_centre_then_xray_centre_params(self):
|
|
89
99
|
my_params = self.model_dump()
|
|
90
100
|
del my_params["thawing_time"]
|
|
@@ -99,11 +109,10 @@ class SpecifiedGridScan(GridCommon, XyzStarts, WithScan):
|
|
|
99
109
|
...
|
|
100
110
|
|
|
101
111
|
|
|
102
|
-
class ThreeDGridScan(SpecifiedGridScan, SplitScan):
|
|
112
|
+
class ThreeDGridScan(SpecifiedGridScan, SplitScan, WithOptionalEnergyChange):
|
|
103
113
|
"""Parameters representing a so-called 3D grid scan, which consists of doing a
|
|
104
114
|
gridscan in X and Y, followed by one in X and Z."""
|
|
105
115
|
|
|
106
|
-
demand_energy_ev: float | None = Field(default=None)
|
|
107
116
|
grid1_omega_deg: float = Field(default=CONST.PARAM.GRIDSCAN.OMEGA_1) # type: ignore
|
|
108
117
|
grid2_omega_deg: float = Field(default=CONST.PARAM.GRIDSCAN.OMEGA_2)
|
|
109
118
|
x_step_size_um: float = Field(default=CONST.PARAM.GRIDSCAN.BOX_WIDTH_UM)
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
from typing import TypeVar
|
|
2
|
+
|
|
3
|
+
from pydantic import BaseModel, model_validator
|
|
4
|
+
|
|
5
|
+
from mx_bluesky.common.parameters.components import (
|
|
6
|
+
MxBlueskyParameters,
|
|
7
|
+
WithSample,
|
|
8
|
+
WithVisit,
|
|
9
|
+
)
|
|
10
|
+
from mx_bluesky.hyperion.parameters.gridscan import (
|
|
11
|
+
RobotLoadThenCentre,
|
|
12
|
+
)
|
|
13
|
+
from mx_bluesky.hyperion.parameters.rotation import MultiRotationScan
|
|
14
|
+
|
|
15
|
+
T = TypeVar("T", bound=BaseModel)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def construct_from_values(parent_context: dict, key: str, t: type[T]) -> T:
|
|
19
|
+
values = dict(parent_context)
|
|
20
|
+
values |= values[key]
|
|
21
|
+
return t(**values)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class LoadCentreCollect(MxBlueskyParameters, WithVisit, WithSample):
|
|
25
|
+
"""Experiment parameters to perform the combined robot load,
|
|
26
|
+
pin-tip centre and rotation scan operations."""
|
|
27
|
+
|
|
28
|
+
robot_load_then_centre: RobotLoadThenCentre
|
|
29
|
+
multi_rotation_scan: MultiRotationScan
|
|
30
|
+
|
|
31
|
+
@model_validator(mode="before")
|
|
32
|
+
@classmethod
|
|
33
|
+
def validate_model(cls, values):
|
|
34
|
+
allowed_keys = (
|
|
35
|
+
LoadCentreCollect.model_fields.keys()
|
|
36
|
+
| RobotLoadThenCentre.model_fields.keys()
|
|
37
|
+
| MultiRotationScan.model_fields.keys()
|
|
38
|
+
)
|
|
39
|
+
disallowed_keys = values.keys() - allowed_keys
|
|
40
|
+
assert (
|
|
41
|
+
disallowed_keys == set()
|
|
42
|
+
), f"Unexpected fields found in LoadCentreCollect {disallowed_keys}"
|
|
43
|
+
|
|
44
|
+
values["robot_load_then_centre"] = construct_from_values(
|
|
45
|
+
values, "robot_load_then_centre", RobotLoadThenCentre
|
|
46
|
+
)
|
|
47
|
+
values["multi_rotation_scan"] = construct_from_values(
|
|
48
|
+
values, "multi_rotation_scan", MultiRotationScan
|
|
49
|
+
)
|
|
50
|
+
return values
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
from pydantic import Field
|
|
2
|
+
|
|
3
|
+
from mx_bluesky.common.parameters.components import (
|
|
4
|
+
MxBlueskyParameters,
|
|
5
|
+
WithOptionalEnergyChange,
|
|
6
|
+
WithSample,
|
|
7
|
+
WithSnapshot,
|
|
8
|
+
WithVisit,
|
|
9
|
+
)
|
|
10
|
+
from mx_bluesky.hyperion.parameters.constants import CONST
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class RobotLoadAndEnergyChange(
|
|
14
|
+
MxBlueskyParameters, WithSample, WithSnapshot, WithOptionalEnergyChange, WithVisit
|
|
15
|
+
):
|
|
16
|
+
thawing_time: float = Field(default=CONST.I03.THAWING_TIME)
|
|
@@ -17,7 +17,7 @@ from scanspec.core import AxesPoints
|
|
|
17
17
|
from scanspec.core import Path as ScanPath
|
|
18
18
|
from scanspec.specs import Line
|
|
19
19
|
|
|
20
|
-
from mx_bluesky.
|
|
20
|
+
from mx_bluesky.common.parameters.components import (
|
|
21
21
|
DiffractionExperimentWithSample,
|
|
22
22
|
IspybExperimentType,
|
|
23
23
|
OptionalGonioAngleStarts,
|
|
@@ -115,12 +115,16 @@ class MultiRotationScan(RotationExperiment, SplitScan):
|
|
|
115
115
|
|
|
116
116
|
def _single_rotation_scan(self, scan: RotationScanPerSweep) -> RotationScan:
|
|
117
117
|
# self has everything from RotationExperiment
|
|
118
|
-
|
|
119
|
-
|
|
118
|
+
allowed_keys = RotationScan.model_fields.keys()
|
|
119
|
+
params_dump = self.model_dump()
|
|
120
120
|
# provided `scan` has everything from RotationScanPerSweep
|
|
121
|
-
|
|
121
|
+
scan_dump = scan.model_dump()
|
|
122
|
+
rotation_scan_kv_pairs = {
|
|
123
|
+
k: v for k, v in (params_dump | scan_dump).items() if k in allowed_keys
|
|
124
|
+
}
|
|
122
125
|
# together they have everything for RotationScan
|
|
123
|
-
|
|
126
|
+
rotation_scan = RotationScan(**rotation_scan_kv_pairs)
|
|
127
|
+
return rotation_scan
|
|
124
128
|
|
|
125
129
|
@model_validator(mode="after")
|
|
126
130
|
@classmethod
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import math
|
|
2
|
+
from math import asin
|
|
3
|
+
|
|
1
4
|
from scanspec.core import AxesPoints, Axis
|
|
2
5
|
from scipy.constants import physical_constants
|
|
3
6
|
|
|
@@ -23,3 +26,17 @@ def convert_angstrom_to_eV(wavelength: float) -> float:
|
|
|
23
26
|
def number_of_frames_from_scan_spec(scan_points: AxesPoints[Axis]):
|
|
24
27
|
ax = list(scan_points.keys())[0]
|
|
25
28
|
return len(scan_points[ax])
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def energy_to_bragg_angle(energy_kev: float, d_a: float) -> float:
|
|
32
|
+
"""Compute the bragg angle given the energy in kev.
|
|
33
|
+
|
|
34
|
+
Args:
|
|
35
|
+
energy_kev: The energy in keV
|
|
36
|
+
d_a: The lattice spacing in Angstroms
|
|
37
|
+
Returns:
|
|
38
|
+
The bragg angle in degrees
|
|
39
|
+
"""
|
|
40
|
+
wavelength_a = convert_eV_to_angstrom(energy_kev * 1000)
|
|
41
|
+
d = d_a
|
|
42
|
+
return asin(wavelength_a / (2 * d)) * 180 / math.pi
|
|
@@ -8,7 +8,7 @@ from unittest.mock import patch
|
|
|
8
8
|
import bluesky.preprocessors as bpp
|
|
9
9
|
from bluesky.run_engine import RunEngine
|
|
10
10
|
from dodal.beamlines import i03
|
|
11
|
-
from dodal.devices.oav.oav_parameters import
|
|
11
|
+
from dodal.devices.oav.oav_parameters import OAVConfig
|
|
12
12
|
from ophyd_async.core import set_mock_value
|
|
13
13
|
|
|
14
14
|
from mx_bluesky.hyperion.device_setup_plans.read_hardware_for_setup import (
|
|
@@ -23,8 +23,8 @@ from mx_bluesky.hyperion.external_interaction.callbacks.rotation.nexus_callback
|
|
|
23
23
|
from mx_bluesky.hyperion.parameters.constants import CONST
|
|
24
24
|
from mx_bluesky.hyperion.parameters.rotation import RotationScan
|
|
25
25
|
|
|
26
|
-
DISPLAY_CONFIGURATION = "tests/
|
|
27
|
-
ZOOM_LEVELS_XML = "tests/
|
|
26
|
+
DISPLAY_CONFIGURATION = "tests/test_data/test_display.configuration"
|
|
27
|
+
ZOOM_LEVELS_XML = "tests/test_data/test_jCameraManZoomLevels.xml"
|
|
28
28
|
TEST_DATA_DIRECTORY = Path("tests/test_data/nexus_files/rotation")
|
|
29
29
|
TEST_METAFILE = "ins_8_5_meta.h5.gz"
|
|
30
30
|
FAKE_DATAFILE = "../fake_data.h5"
|
|
@@ -94,15 +94,14 @@ def fake_create_rotation_devices():
|
|
|
94
94
|
robot = i03.robot(fake_with_ophyd_sim=True)
|
|
95
95
|
oav = i03.oav(
|
|
96
96
|
fake_with_ophyd_sim=True,
|
|
97
|
-
params=
|
|
98
|
-
zoom_params_file=ZOOM_LEVELS_XML,
|
|
97
|
+
params=OAVConfig(
|
|
98
|
+
zoom_params_file=ZOOM_LEVELS_XML, display_config_file=DISPLAY_CONFIGURATION
|
|
99
99
|
),
|
|
100
100
|
)
|
|
101
101
|
xbpm_feedback = i03.xbpm_feedback(fake_with_ophyd_sim=True)
|
|
102
102
|
|
|
103
103
|
set_mock_value(smargon.omega.max_velocity, 131)
|
|
104
104
|
set_mock_value(dcm.energy_in_kev.user_readback, 12700)
|
|
105
|
-
oav.zoom_controller.fvst.sim_put("1.0x") # type: ignore
|
|
106
105
|
|
|
107
106
|
return RotationScanComposite(
|
|
108
107
|
attenuator=attenuator,
|