mx-bluesky 0.3.1__py3-none-any.whl → 1.1.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/i04/__init__.py +3 -0
- mx_bluesky/{i04 → beamlines/i04}/thawing_plan.py +5 -4
- mx_bluesky/{i24 → beamlines/i24}/serial/blueapi_config.yaml +1 -1
- mx_bluesky/{i24 → beamlines/i24}/serial/dcid.py +2 -2
- mx_bluesky/{i24 → beamlines/i24}/serial/extruder/EX-gui-edm/DetStage.edl +3 -3
- mx_bluesky/{i24 → beamlines/i24}/serial/extruder/EX-gui-edm/DiamondExtruder-I24-py3v1.edl +7 -7
- mx_bluesky/{i24 → beamlines/i24}/serial/extruder/i24ssx_Extruder_Collect_py3v2.py +12 -9
- mx_bluesky/{i24 → beamlines/i24}/serial/fixed_target/FT-gui-edm/CustomChip_py3v1.edl +3 -3
- mx_bluesky/{i24 → beamlines/i24}/serial/fixed_target/FT-gui-edm/DetStage.edl +3 -3
- mx_bluesky/{i24 → beamlines/i24}/serial/fixed_target/FT-gui-edm/DiamondChipI24-py3v1.edl +245 -200
- mx_bluesky/{i24 → beamlines/i24}/serial/fixed_target/FT-gui-edm/MappingLite-oxford_py3v1.edl +4 -4
- mx_bluesky/{i24 → beamlines/i24}/serial/fixed_target/FT-gui-edm/pumpprobe-py3v1.edl +8 -8
- mx_bluesky/beamlines/i24/serial/fixed_target/__init__.py +0 -0
- mx_bluesky/{i24 → beamlines/i24}/serial/fixed_target/i24ssx_Chip_Collect_py3v1.py +80 -70
- mx_bluesky/{i24 → beamlines/i24}/serial/fixed_target/i24ssx_Chip_Manager_py3v1.py +20 -21
- mx_bluesky/{i24 → beamlines/i24}/serial/fixed_target/i24ssx_Chip_Mapping_py3v1.py +5 -5
- mx_bluesky/{i24 → beamlines/i24}/serial/fixed_target/i24ssx_Chip_StartUp_py3v1.py +7 -4
- mx_bluesky/{i24 → beamlines/i24}/serial/fixed_target/i24ssx_moveonclick.py +59 -39
- mx_bluesky/{i24 → beamlines/i24}/serial/log.py +1 -9
- mx_bluesky/beamlines/i24/serial/parameters/__init__.py +15 -0
- mx_bluesky/{i24 → beamlines/i24}/serial/parameters/constants.py +1 -1
- mx_bluesky/{i24 → beamlines/i24}/serial/parameters/experiment_parameters.py +4 -25
- mx_bluesky/{i24 → beamlines/i24}/serial/parameters/utils.py +5 -3
- mx_bluesky/{i24 → beamlines/i24}/serial/run_serial.py +1 -1
- mx_bluesky/{i24 → beamlines/i24}/serial/setup_beamline/pv_abstract.py +1 -1
- mx_bluesky/{i24 → beamlines/i24}/serial/setup_beamline/setup_beamline.py +2 -2
- mx_bluesky/{i24 → beamlines/i24}/serial/setup_beamline/setup_detector.py +5 -5
- mx_bluesky/{i24 → beamlines/i24}/serial/write_nexus.py +6 -3
- mx_bluesky/hyperion/__init__.py +1 -0
- mx_bluesky/hyperion/__main__.py +374 -0
- mx_bluesky/hyperion/device_setup_plans/__init__.py +0 -0
- mx_bluesky/hyperion/device_setup_plans/dcm_pitch_roll_mirror_adjuster.py +134 -0
- mx_bluesky/hyperion/device_setup_plans/manipulate_sample.py +110 -0
- mx_bluesky/hyperion/device_setup_plans/position_detector.py +16 -0
- mx_bluesky/hyperion/device_setup_plans/read_hardware_for_setup.py +60 -0
- mx_bluesky/hyperion/device_setup_plans/setup_oav.py +87 -0
- mx_bluesky/hyperion/device_setup_plans/setup_panda.py +210 -0
- mx_bluesky/hyperion/device_setup_plans/setup_zebra.py +214 -0
- mx_bluesky/hyperion/device_setup_plans/smargon.py +25 -0
- mx_bluesky/hyperion/device_setup_plans/utils.py +44 -0
- mx_bluesky/hyperion/device_setup_plans/xbpm_feedback.py +93 -0
- mx_bluesky/hyperion/exceptions.py +47 -0
- mx_bluesky/hyperion/experiment_plans/__init__.py +30 -0
- mx_bluesky/hyperion/experiment_plans/experiment_registry.py +84 -0
- mx_bluesky/hyperion/experiment_plans/flyscan_xray_centre_plan.py +528 -0
- mx_bluesky/hyperion/experiment_plans/grid_detect_then_xray_centre_plan.py +209 -0
- mx_bluesky/hyperion/experiment_plans/oav_grid_detection_plan.py +173 -0
- mx_bluesky/hyperion/experiment_plans/oav_snapshot_plan.py +81 -0
- mx_bluesky/hyperion/experiment_plans/optimise_attenuation_plan.py +463 -0
- mx_bluesky/hyperion/experiment_plans/pin_centre_then_xray_centre_plan.py +119 -0
- mx_bluesky/hyperion/experiment_plans/pin_tip_centring_plan.py +164 -0
- mx_bluesky/hyperion/experiment_plans/robot_load_then_centre_plan.py +322 -0
- mx_bluesky/hyperion/experiment_plans/rotation_scan_plan.py +436 -0
- mx_bluesky/hyperion/experiment_plans/set_energy_plan.py +68 -0
- mx_bluesky/hyperion/external_interaction/__init__.py +9 -0
- mx_bluesky/hyperion/external_interaction/callbacks/__init__.py +10 -0
- mx_bluesky/hyperion/external_interaction/callbacks/__main__.py +148 -0
- mx_bluesky/hyperion/external_interaction/callbacks/aperture_change_callback.py +22 -0
- mx_bluesky/hyperion/external_interaction/callbacks/common/__init__.py +0 -0
- mx_bluesky/hyperion/external_interaction/callbacks/common/callback_util.py +46 -0
- mx_bluesky/hyperion/external_interaction/callbacks/common/ispyb_mapping.py +70 -0
- mx_bluesky/hyperion/external_interaction/callbacks/grid_detection_callback.py +88 -0
- mx_bluesky/hyperion/external_interaction/callbacks/ispyb_callback_base.py +203 -0
- mx_bluesky/hyperion/external_interaction/callbacks/log_uid_tag_callback.py +20 -0
- mx_bluesky/hyperion/external_interaction/callbacks/logging_callback.py +29 -0
- mx_bluesky/hyperion/external_interaction/callbacks/plan_reactive_callback.py +101 -0
- mx_bluesky/hyperion/external_interaction/callbacks/robot_load/ispyb_callback.py +88 -0
- mx_bluesky/hyperion/external_interaction/callbacks/rotation/__init__.py +0 -0
- mx_bluesky/hyperion/external_interaction/callbacks/rotation/ispyb_callback.py +174 -0
- mx_bluesky/hyperion/external_interaction/callbacks/rotation/ispyb_mapping.py +17 -0
- mx_bluesky/hyperion/external_interaction/callbacks/rotation/nexus_callback.py +102 -0
- mx_bluesky/hyperion/external_interaction/callbacks/xray_centre/__init__.py +0 -0
- mx_bluesky/hyperion/external_interaction/callbacks/xray_centre/ispyb_callback.py +269 -0
- mx_bluesky/hyperion/external_interaction/callbacks/xray_centre/ispyb_mapping.py +53 -0
- mx_bluesky/hyperion/external_interaction/callbacks/xray_centre/nexus_callback.py +95 -0
- mx_bluesky/hyperion/external_interaction/callbacks/zocalo_callback.py +92 -0
- mx_bluesky/hyperion/external_interaction/config_server.py +35 -0
- mx_bluesky/hyperion/external_interaction/exceptions.py +13 -0
- mx_bluesky/hyperion/external_interaction/ispyb/__init__.py +0 -0
- mx_bluesky/hyperion/external_interaction/ispyb/data_model.py +95 -0
- mx_bluesky/hyperion/external_interaction/ispyb/exp_eye_store.py +125 -0
- mx_bluesky/hyperion/external_interaction/ispyb/ispyb_store.py +276 -0
- mx_bluesky/hyperion/external_interaction/ispyb/ispyb_utils.py +29 -0
- mx_bluesky/hyperion/external_interaction/nexus/__init__.py +0 -0
- mx_bluesky/hyperion/external_interaction/nexus/nexus_utils.py +148 -0
- mx_bluesky/hyperion/external_interaction/nexus/write_nexus.py +114 -0
- mx_bluesky/hyperion/log.py +99 -0
- mx_bluesky/hyperion/parameters/__init__.py +2 -0
- mx_bluesky/hyperion/parameters/cli.py +68 -0
- mx_bluesky/{parameters → hyperion/parameters}/components.py +77 -24
- mx_bluesky/hyperion/parameters/constants.py +158 -0
- mx_bluesky/hyperion/parameters/gridscan.py +216 -0
- mx_bluesky/hyperion/parameters/rotation.py +160 -0
- mx_bluesky/hyperion/resources/panda/panda-gridscan.yaml +964 -0
- mx_bluesky/hyperion/tracing.py +28 -0
- mx_bluesky/hyperion/utils/context.py +84 -0
- mx_bluesky/hyperion/utils/utils.py +25 -0
- mx_bluesky/hyperion/utils/validation.py +196 -0
- mx_bluesky/jupyter_example.ipynb +3 -2
- {mx_bluesky-0.3.1.dist-info → mx_bluesky-1.1.0.dist-info}/METADATA +26 -11
- mx_bluesky-1.1.0.dist-info/RECORD +136 -0
- {mx_bluesky-0.3.1.dist-info → mx_bluesky-1.1.0.dist-info}/WHEEL +1 -1
- mx_bluesky-1.1.0.dist-info/entry_points.txt +8 -0
- mx_bluesky/i04/__init__.py +0 -3
- mx_bluesky/i24/serial/parameters/__init__.py +0 -15
- mx_bluesky/parameters/__init__.py +0 -31
- mx_bluesky-0.3.1.dist-info/RECORD +0 -67
- mx_bluesky-0.3.1.dist-info/entry_points.txt +0 -4
- /mx_bluesky/{i24 → beamlines}/__init__.py +0 -0
- /mx_bluesky/{i04 → beamlines/i04}/callbacks/murko_callback.py +0 -0
- /mx_bluesky/{i24/serial/extruder → beamlines/i24}/__init__.py +0 -0
- /mx_bluesky/{i24 → beamlines/i24}/serial/__init__.py +0 -0
- /mx_bluesky/{i24 → beamlines/i24}/serial/extruder/EX-gui-edm/microdrop_alignment.edl +0 -0
- /mx_bluesky/{i24/serial/fixed_target → beamlines/i24/serial/extruder}/__init__.py +0 -0
- /mx_bluesky/{i24 → beamlines/i24}/serial/fixed_target/FT-gui-edm/ME14E-GeneralPurpose.edl +0 -0
- /mx_bluesky/{i24 → beamlines/i24}/serial/fixed_target/FT-gui-edm/PMAC_Command.edl +0 -0
- /mx_bluesky/{i24 → beamlines/i24}/serial/fixed_target/FT-gui-edm/Shutter_Control.edl +0 -0
- /mx_bluesky/{i24 → beamlines/i24}/serial/fixed_target/FT-gui-edm/microdrop_alignment.edl +0 -0
- /mx_bluesky/{i24 → beamlines/i24}/serial/fixed_target/FT-gui-edm/nudgechip.edl +0 -0
- /mx_bluesky/{i24 → beamlines/i24}/serial/fixed_target/FT-gui-edm/short1-laser.png +0 -0
- /mx_bluesky/{i24 → beamlines/i24}/serial/fixed_target/FT-gui-edm/short2-laser.png +0 -0
- /mx_bluesky/{i24 → beamlines/i24}/serial/fixed_target/ft_utils.py +0 -0
- /mx_bluesky/{i24 → beamlines/i24}/serial/parameters/fixed_target/cs/cs_maker.json +0 -0
- /mx_bluesky/{i24 → beamlines/i24}/serial/parameters/fixed_target/cs/motor_direction.txt +0 -0
- /mx_bluesky/{i24 → beamlines/i24}/serial/parameters/fixed_target/pvar_files/minichip-oxford.pvar +0 -0
- /mx_bluesky/{i24 → beamlines/i24}/serial/parameters/fixed_target/pvar_files/oxford.pvar +0 -0
- /mx_bluesky/{i24 → beamlines/i24}/serial/run_extruder.sh +0 -0
- /mx_bluesky/{i24 → beamlines/i24}/serial/run_fixed_target.sh +0 -0
- /mx_bluesky/{i24 → beamlines/i24}/serial/run_ssx.sh +0 -0
- /mx_bluesky/{i24 → beamlines/i24}/serial/set_visit_directory.sh +0 -0
- /mx_bluesky/{i24 → beamlines/i24}/serial/setup_beamline/__init__.py +0 -0
- /mx_bluesky/{i24 → beamlines/i24}/serial/setup_beamline/ca.py +0 -0
- /mx_bluesky/{i24 → beamlines/i24}/serial/setup_beamline/pv.py +0 -0
- /mx_bluesky/{i24 → beamlines/i24}/serial/setup_beamline/setup_zebra_plans.py +0 -0
- /mx_bluesky/{i24 → beamlines/i24}/serial/start_blueapi.sh +0 -0
- {mx_bluesky-0.3.1.dist-info → mx_bluesky-1.1.0.dist-info}/LICENSE +0 -0
- {mx_bluesky-0.3.1.dist-info → mx_bluesky-1.1.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,463 @@
|
|
|
1
|
+
import dataclasses
|
|
2
|
+
from enum import Enum
|
|
3
|
+
|
|
4
|
+
import bluesky.plan_stubs as bps
|
|
5
|
+
import bluesky.preprocessors as bpp
|
|
6
|
+
import numpy as np
|
|
7
|
+
from blueapi.core import BlueskyContext
|
|
8
|
+
from dodal.devices.attenuator import Attenuator
|
|
9
|
+
from dodal.devices.xspress3.xspress3 import Xspress3
|
|
10
|
+
from dodal.devices.zebra_controlled_shutter import ZebraShutter, ZebraShutterState
|
|
11
|
+
|
|
12
|
+
from mx_bluesky.hyperion.log import LOGGER
|
|
13
|
+
from mx_bluesky.hyperion.utils.context import device_composite_from_context
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class AttenuationOptimisationFailedException(Exception):
|
|
17
|
+
pass
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class Direction(Enum):
|
|
21
|
+
POSITIVE = "positive"
|
|
22
|
+
NEGATIVE = "negative"
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@dataclasses.dataclass
|
|
26
|
+
class OptimizeAttenuationComposite:
|
|
27
|
+
"""All devices which are directly or indirectly required by this plan"""
|
|
28
|
+
|
|
29
|
+
attenuator: Attenuator
|
|
30
|
+
sample_shutter: ZebraShutter
|
|
31
|
+
xspress3mini: Xspress3
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def create_devices(context: BlueskyContext) -> OptimizeAttenuationComposite:
|
|
35
|
+
return device_composite_from_context(context, OptimizeAttenuationComposite)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def check_parameters(
|
|
39
|
+
target,
|
|
40
|
+
upper_count_limit,
|
|
41
|
+
lower_count_limit,
|
|
42
|
+
default_high_roi,
|
|
43
|
+
default_low_roi,
|
|
44
|
+
initial_transmission,
|
|
45
|
+
upper_transmission,
|
|
46
|
+
lower_transmission,
|
|
47
|
+
):
|
|
48
|
+
if target < lower_count_limit or target > upper_count_limit:
|
|
49
|
+
raise (
|
|
50
|
+
ValueError(
|
|
51
|
+
f"Target {target} is outside of lower and upper bounds: {lower_count_limit} to {upper_count_limit}"
|
|
52
|
+
)
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
if default_high_roi < default_low_roi:
|
|
56
|
+
raise ValueError(
|
|
57
|
+
f"Upper roi {default_high_roi} must be greater than lower roi {default_low_roi}"
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
if upper_transmission < lower_transmission:
|
|
61
|
+
raise ValueError(
|
|
62
|
+
f"Upper transmission limit {upper_transmission} must be greater than lower tranmission limit {lower_transmission}"
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
if not upper_transmission >= initial_transmission >= lower_transmission:
|
|
66
|
+
raise ValueError(
|
|
67
|
+
f"initial transmission {initial_transmission} is outside range {lower_transmission} - {upper_transmission}"
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def is_counts_within_target(total_count, lower_count_limit, upper_count_limit) -> bool:
|
|
72
|
+
if lower_count_limit <= total_count and total_count <= upper_count_limit:
|
|
73
|
+
return True
|
|
74
|
+
else:
|
|
75
|
+
return False
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def calculate_new_direction(direction: Direction, deadtime, deadtime_threshold):
|
|
79
|
+
if direction == Direction.POSITIVE:
|
|
80
|
+
if deadtime > deadtime_threshold:
|
|
81
|
+
direction = Direction.NEGATIVE
|
|
82
|
+
LOGGER.info(
|
|
83
|
+
"Found tranmission to go above deadtime threshold. Reducing transmission..."
|
|
84
|
+
)
|
|
85
|
+
return direction
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def deadtime_calc_new_transmission(
|
|
89
|
+
direction: Direction,
|
|
90
|
+
transmission: float,
|
|
91
|
+
increment: float,
|
|
92
|
+
upper_transmission_limit: float,
|
|
93
|
+
lower_transmission_limit: float,
|
|
94
|
+
) -> float:
|
|
95
|
+
"""Calculate the new transmission value based on the current direction and increment. Raise error if transmission is too low.
|
|
96
|
+
|
|
97
|
+
Args:
|
|
98
|
+
direction (Direction):
|
|
99
|
+
If positive, increase transmission by a factor of the increment. If negative, divide it
|
|
100
|
+
|
|
101
|
+
transmission (float):
|
|
102
|
+
Current transmission value
|
|
103
|
+
|
|
104
|
+
increment (float):
|
|
105
|
+
Factor to multiply or divide transmission by
|
|
106
|
+
|
|
107
|
+
upper_transmission_limit (float):
|
|
108
|
+
Maximum allowed transmission, in order to protect sample.
|
|
109
|
+
|
|
110
|
+
lower_transmission_limit (float):
|
|
111
|
+
Minimum expected transmission. Raise an error if transmission goes lower.
|
|
112
|
+
|
|
113
|
+
Raises:
|
|
114
|
+
AttenuationOptimisationFailedException:
|
|
115
|
+
This error is thrown if the transmission goes below the expected value or if the maximum cycles are reached
|
|
116
|
+
|
|
117
|
+
Returns:
|
|
118
|
+
transmission (float): Optimised transmission value
|
|
119
|
+
"""
|
|
120
|
+
if direction == Direction.POSITIVE:
|
|
121
|
+
transmission *= increment
|
|
122
|
+
if transmission > upper_transmission_limit:
|
|
123
|
+
transmission = upper_transmission_limit
|
|
124
|
+
else:
|
|
125
|
+
transmission /= increment
|
|
126
|
+
if transmission < lower_transmission_limit:
|
|
127
|
+
raise AttenuationOptimisationFailedException(
|
|
128
|
+
"Calculated transmission is below expected limit"
|
|
129
|
+
)
|
|
130
|
+
return transmission
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def do_device_optimise_iteration(
|
|
134
|
+
composite: OptimizeAttenuationComposite,
|
|
135
|
+
transmission,
|
|
136
|
+
):
|
|
137
|
+
def close_shutter():
|
|
138
|
+
yield from bps.abs_set(
|
|
139
|
+
composite.sample_shutter, ZebraShutterState.CLOSE, wait=True
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
@bpp.finalize_decorator(close_shutter)
|
|
143
|
+
def open_and_run():
|
|
144
|
+
"""Set transmission, set number of images on xspress3mini, arm xspress3mini"""
|
|
145
|
+
yield from bps.abs_set(
|
|
146
|
+
composite.attenuator, transmission, group="set_transmission"
|
|
147
|
+
)
|
|
148
|
+
yield from bps.abs_set(composite.xspress3mini.set_num_images, 1, wait=True)
|
|
149
|
+
yield from bps.abs_set(
|
|
150
|
+
composite.sample_shutter, ZebraShutterState.OPEN, wait=True
|
|
151
|
+
)
|
|
152
|
+
yield from bps.stage(composite.xspress3mini, wait=True)
|
|
153
|
+
yield from bps.unstage(composite.xspress3mini, wait=True)
|
|
154
|
+
|
|
155
|
+
yield from open_and_run()
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
def is_deadtime_optimised(
|
|
159
|
+
deadtime: float,
|
|
160
|
+
deadtime_threshold: float,
|
|
161
|
+
transmission: float,
|
|
162
|
+
upper_transmission_limit: float,
|
|
163
|
+
direction: Direction,
|
|
164
|
+
) -> bool:
|
|
165
|
+
if direction == Direction.POSITIVE:
|
|
166
|
+
if transmission == upper_transmission_limit:
|
|
167
|
+
LOGGER.warning(
|
|
168
|
+
f"Deadtime {deadtime} is above threshold {deadtime_threshold} at maximum transmission {upper_transmission_limit}. Using maximum transmission\
|
|
169
|
+
as optimised value."
|
|
170
|
+
)
|
|
171
|
+
return True
|
|
172
|
+
# Once direction is flipped and deadtime goes back above threshold, we consider attenuation to be optimised.
|
|
173
|
+
else:
|
|
174
|
+
if deadtime <= deadtime_threshold:
|
|
175
|
+
return True
|
|
176
|
+
return False
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def deadtime_optimisation(
|
|
180
|
+
composite: OptimizeAttenuationComposite,
|
|
181
|
+
transmission: float,
|
|
182
|
+
increment: float,
|
|
183
|
+
deadtime_threshold: float,
|
|
184
|
+
max_cycles: int,
|
|
185
|
+
upper_transmission_limit: float,
|
|
186
|
+
lower_transmission_limit: float,
|
|
187
|
+
):
|
|
188
|
+
"""Optimises the attenuation for the Xspress3Mini based on the detector deadtime
|
|
189
|
+
|
|
190
|
+
Deadtime is the time after each event during which the detector cannot record another event. This loop adjusts the transmission of the attenuator
|
|
191
|
+
and checks the deadtime until the percentage deadtime is below the accepted threshold. To protect the sample, the transmission has a maximum value
|
|
192
|
+
|
|
193
|
+
Here we use the percentage deadtime - the percentage of time to which the detector is unable to process events.
|
|
194
|
+
|
|
195
|
+
This algorithm gradually increases the transmission until the percentage deadtime goes beneath the specified threshold. It then increases
|
|
196
|
+
the transmission and stops when the deadtime goes above the threshold. A smaller increment will provide a better optimised value, but take more
|
|
197
|
+
cycles to complete.
|
|
198
|
+
|
|
199
|
+
Args:
|
|
200
|
+
attenuator: (Attenuator) Ophyd device
|
|
201
|
+
|
|
202
|
+
xspress3mini: (Xspress3Mini) Ophyd device
|
|
203
|
+
|
|
204
|
+
sample_shutter: (ZebraShutter) Ophyd_async device for the fast shutter
|
|
205
|
+
|
|
206
|
+
transmission: (float)
|
|
207
|
+
The initial transmission value to use for the optimising
|
|
208
|
+
|
|
209
|
+
increment: (float)
|
|
210
|
+
The factor to increase / decrease the transmission by each iteration
|
|
211
|
+
|
|
212
|
+
deadtime_threshold: (float)
|
|
213
|
+
The maximum acceptable percentage deadtime
|
|
214
|
+
|
|
215
|
+
max_cycles: (int)
|
|
216
|
+
The maximum number of iterations before an error is thrown
|
|
217
|
+
|
|
218
|
+
upper_transmission_limit (float):
|
|
219
|
+
Maximum allowed transmission, in order to protect sample.
|
|
220
|
+
|
|
221
|
+
lower_transmission_limit (float):
|
|
222
|
+
Minimum expected transmission. Raise an error if transmission goes lower.
|
|
223
|
+
|
|
224
|
+
Raises:
|
|
225
|
+
AttenuationOptimisationFailedException:
|
|
226
|
+
This error is thrown if the transmission goes below the expected value or the maximum cycles are reached
|
|
227
|
+
|
|
228
|
+
Returns:
|
|
229
|
+
optimised_transmission: (float)
|
|
230
|
+
The final transmission value which produces an acceptable deadtime
|
|
231
|
+
"""
|
|
232
|
+
|
|
233
|
+
direction = Direction.POSITIVE
|
|
234
|
+
LOGGER.info(f"Target deadtime is {deadtime_threshold}")
|
|
235
|
+
optimised_transmission: float = 0
|
|
236
|
+
for cycle in range(0, max_cycles):
|
|
237
|
+
yield from do_device_optimise_iteration(composite, transmission)
|
|
238
|
+
|
|
239
|
+
total_time = yield from bps.rd(composite.xspress3mini.channels[1].total_time)
|
|
240
|
+
reset_ticks = yield from bps.rd(composite.xspress3mini.channels[1].reset_ticks)
|
|
241
|
+
|
|
242
|
+
LOGGER.info(f"Current total time = {total_time}")
|
|
243
|
+
LOGGER.info(f"Current reset ticks = {reset_ticks}")
|
|
244
|
+
deadtime = 0
|
|
245
|
+
|
|
246
|
+
"""
|
|
247
|
+
The reset ticks PV stops ticking while the detector is unable to process events, so the absolute difference between the total time and the
|
|
248
|
+
reset ticks time gives the deadtime in unit time. Divide by total time to get it as a percentage.
|
|
249
|
+
"""
|
|
250
|
+
|
|
251
|
+
if total_time != reset_ticks:
|
|
252
|
+
deadtime = 1 - abs(total_time - reset_ticks) / (total_time)
|
|
253
|
+
|
|
254
|
+
LOGGER.info(f"Deadtime is now at {deadtime}")
|
|
255
|
+
|
|
256
|
+
# Check if new deadtime is OK
|
|
257
|
+
|
|
258
|
+
if is_deadtime_optimised(
|
|
259
|
+
deadtime,
|
|
260
|
+
deadtime_threshold,
|
|
261
|
+
transmission,
|
|
262
|
+
upper_transmission_limit,
|
|
263
|
+
direction,
|
|
264
|
+
):
|
|
265
|
+
optimised_transmission = transmission
|
|
266
|
+
break
|
|
267
|
+
|
|
268
|
+
if cycle == max_cycles - 1:
|
|
269
|
+
raise AttenuationOptimisationFailedException(
|
|
270
|
+
f"Unable to optimise attenuation after maximum cycles.\
|
|
271
|
+
Deadtime did not get lower than threshold: {deadtime_threshold} in maximum cycles {max_cycles}"
|
|
272
|
+
)
|
|
273
|
+
|
|
274
|
+
direction = calculate_new_direction(direction, deadtime, deadtime_threshold)
|
|
275
|
+
|
|
276
|
+
transmission = deadtime_calc_new_transmission(
|
|
277
|
+
direction,
|
|
278
|
+
transmission,
|
|
279
|
+
increment,
|
|
280
|
+
upper_transmission_limit,
|
|
281
|
+
lower_transmission_limit,
|
|
282
|
+
)
|
|
283
|
+
|
|
284
|
+
return optimised_transmission
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
def total_counts_optimisation(
|
|
288
|
+
composite: OptimizeAttenuationComposite,
|
|
289
|
+
transmission: float,
|
|
290
|
+
low_roi: int,
|
|
291
|
+
high_roi: int,
|
|
292
|
+
lower_count_limit: float,
|
|
293
|
+
upper_count_limit: float,
|
|
294
|
+
target_count: float,
|
|
295
|
+
max_cycles: int,
|
|
296
|
+
upper_transmission_limit: float,
|
|
297
|
+
lower_transmission_limit: float,
|
|
298
|
+
):
|
|
299
|
+
"""Optimises the attenuation for the Xspress3Mini based on the total counts
|
|
300
|
+
|
|
301
|
+
This loop adjusts the transmission of the attenuator and checks the total counts of the detector until the total counts as in the acceptable range,
|
|
302
|
+
defined by the lower and upper limit. To protect the sample, the transmission has a maximum value of 10%.
|
|
303
|
+
|
|
304
|
+
Args:
|
|
305
|
+
attenuator: (Attenuator) Ophyd device
|
|
306
|
+
|
|
307
|
+
xspress3mini: (Xspress3Mini) Ophyd device
|
|
308
|
+
|
|
309
|
+
sample_shutter: (ZebraShutter) Ophyd_async device for the fast shutter
|
|
310
|
+
|
|
311
|
+
transmission: (float)
|
|
312
|
+
The initial transmission value to use for the optimising
|
|
313
|
+
|
|
314
|
+
low_roi: (float)
|
|
315
|
+
Lower region of interest at which to include in the counts
|
|
316
|
+
|
|
317
|
+
high_roi: (float)
|
|
318
|
+
Upper region of interest at which to include in the counts
|
|
319
|
+
|
|
320
|
+
lower_count_limit: (float)
|
|
321
|
+
The lowest acceptable value for count
|
|
322
|
+
|
|
323
|
+
upper_count_limit: (float)
|
|
324
|
+
The highest acceptable value for count
|
|
325
|
+
|
|
326
|
+
target_count: (int)
|
|
327
|
+
The ideal number of target counts - used to calculate the transmission for the subsequent iteration.
|
|
328
|
+
|
|
329
|
+
max_cycles: (int)
|
|
330
|
+
The maximum number of iterations before an error is thrown
|
|
331
|
+
|
|
332
|
+
upper_transmission_limit: (float)
|
|
333
|
+
The maximum allowed value for the transmission
|
|
334
|
+
|
|
335
|
+
lower_transmission_limit: (float)
|
|
336
|
+
The minimum allowed value for the transmission
|
|
337
|
+
|
|
338
|
+
Returns:
|
|
339
|
+
optimised_transmission: (float)
|
|
340
|
+
The final transmission value which produces an acceptable total_count value
|
|
341
|
+
"""
|
|
342
|
+
|
|
343
|
+
LOGGER.info("Using total count optimisation")
|
|
344
|
+
optimised_transmission: float = 0
|
|
345
|
+
for cycle in range(0, max_cycles):
|
|
346
|
+
LOGGER.info(
|
|
347
|
+
f"Setting transmission to {transmission} for attenuation optimisation cycle {cycle}"
|
|
348
|
+
)
|
|
349
|
+
|
|
350
|
+
yield from do_device_optimise_iteration(composite, transmission)
|
|
351
|
+
|
|
352
|
+
data = np.array(
|
|
353
|
+
(yield from bps.rd(composite.xspress3mini.dt_corrected_latest_mca[1]))
|
|
354
|
+
)
|
|
355
|
+
total_count = sum(data[int(low_roi) : int(high_roi)])
|
|
356
|
+
LOGGER.info(f"Total count is {total_count}")
|
|
357
|
+
|
|
358
|
+
if is_counts_within_target(total_count, lower_count_limit, upper_count_limit):
|
|
359
|
+
optimised_transmission = transmission
|
|
360
|
+
LOGGER.info(
|
|
361
|
+
f"Total count is within accepted limits: {lower_count_limit}, {total_count}, {upper_count_limit}"
|
|
362
|
+
)
|
|
363
|
+
break
|
|
364
|
+
elif transmission == upper_transmission_limit:
|
|
365
|
+
LOGGER.warning(
|
|
366
|
+
f"Total count is not within limits: {lower_count_limit} <= {total_count} <= {upper_count_limit}\
|
|
367
|
+
after using maximum transmission {upper_transmission_limit}. Continuing\
|
|
368
|
+
with maximum transmission as optimised value..."
|
|
369
|
+
)
|
|
370
|
+
optimised_transmission = transmission
|
|
371
|
+
break
|
|
372
|
+
|
|
373
|
+
else:
|
|
374
|
+
transmission = (target_count / (total_count)) * transmission
|
|
375
|
+
if transmission > upper_transmission_limit:
|
|
376
|
+
transmission = upper_transmission_limit
|
|
377
|
+
elif transmission < lower_transmission_limit:
|
|
378
|
+
raise AttenuationOptimisationFailedException(
|
|
379
|
+
f"Transmission has gone below lower threshold {lower_transmission_limit}"
|
|
380
|
+
)
|
|
381
|
+
|
|
382
|
+
if cycle == max_cycles - 1:
|
|
383
|
+
raise AttenuationOptimisationFailedException(
|
|
384
|
+
f"Unable to optimise attenuation after maximum cycles.\
|
|
385
|
+
Total count is not within limits: {lower_count_limit} <= {total_count}\
|
|
386
|
+
<= {upper_count_limit}"
|
|
387
|
+
)
|
|
388
|
+
|
|
389
|
+
return optimised_transmission
|
|
390
|
+
|
|
391
|
+
|
|
392
|
+
def optimise_attenuation_plan(
|
|
393
|
+
composite: OptimizeAttenuationComposite,
|
|
394
|
+
collection_time=1, # Comes from self.parameters.acquisitionTime in fluorescence_spectrum.py
|
|
395
|
+
optimisation_type="deadtime",
|
|
396
|
+
low_roi=100,
|
|
397
|
+
high_roi=2048,
|
|
398
|
+
upper_transmission_limit=0.1,
|
|
399
|
+
lower_transmission_limit=1.0e-6,
|
|
400
|
+
initial_transmission=0.1,
|
|
401
|
+
target_count=20000,
|
|
402
|
+
lower_count_limit=20000,
|
|
403
|
+
upper_count_limit=50000,
|
|
404
|
+
max_cycles=10,
|
|
405
|
+
increment=2,
|
|
406
|
+
deadtime_threshold=0.002,
|
|
407
|
+
):
|
|
408
|
+
check_parameters(
|
|
409
|
+
target_count,
|
|
410
|
+
upper_count_limit,
|
|
411
|
+
lower_count_limit,
|
|
412
|
+
high_roi,
|
|
413
|
+
low_roi,
|
|
414
|
+
initial_transmission,
|
|
415
|
+
upper_transmission_limit,
|
|
416
|
+
lower_transmission_limit,
|
|
417
|
+
)
|
|
418
|
+
|
|
419
|
+
yield from bps.abs_set(
|
|
420
|
+
composite.xspress3mini.acquire_time, collection_time, wait=True
|
|
421
|
+
) # Don't necessarily need to wait here
|
|
422
|
+
optimised_transmission: float = 0
|
|
423
|
+
# Do the attenuation optimisation using count threshold
|
|
424
|
+
if optimisation_type == "total_counts":
|
|
425
|
+
LOGGER.info(
|
|
426
|
+
f"Starting Xspress3Mini total counts optimisation routine \nOptimisation will be performed across ROI channels {low_roi} - {high_roi}"
|
|
427
|
+
)
|
|
428
|
+
|
|
429
|
+
optimised_transmission = yield from total_counts_optimisation(
|
|
430
|
+
composite,
|
|
431
|
+
initial_transmission,
|
|
432
|
+
low_roi,
|
|
433
|
+
high_roi,
|
|
434
|
+
lower_count_limit,
|
|
435
|
+
upper_count_limit,
|
|
436
|
+
target_count,
|
|
437
|
+
max_cycles,
|
|
438
|
+
upper_transmission_limit,
|
|
439
|
+
lower_transmission_limit,
|
|
440
|
+
)
|
|
441
|
+
|
|
442
|
+
elif optimisation_type == "deadtime":
|
|
443
|
+
LOGGER.info(
|
|
444
|
+
f"Starting Xspress3Mini deadtime optimisation routine \nOptimisation will be performed across ROI channels {low_roi} - {high_roi}"
|
|
445
|
+
)
|
|
446
|
+
optimised_transmission = yield from deadtime_optimisation(
|
|
447
|
+
composite,
|
|
448
|
+
initial_transmission,
|
|
449
|
+
upper_transmission_limit,
|
|
450
|
+
lower_transmission_limit,
|
|
451
|
+
increment,
|
|
452
|
+
deadtime_threshold,
|
|
453
|
+
max_cycles,
|
|
454
|
+
)
|
|
455
|
+
|
|
456
|
+
yield from bps.abs_set(
|
|
457
|
+
composite.attenuator,
|
|
458
|
+
optimised_transmission,
|
|
459
|
+
group="set_transmission",
|
|
460
|
+
wait=True,
|
|
461
|
+
)
|
|
462
|
+
|
|
463
|
+
return optimised_transmission
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
|
|
5
|
+
from blueapi.core import BlueskyContext, MsgGenerator
|
|
6
|
+
from dodal.devices.eiger import EigerDetector
|
|
7
|
+
from dodal.devices.oav.oav_parameters import OAV_CONFIG_JSON, OAVParameters
|
|
8
|
+
|
|
9
|
+
from mx_bluesky.hyperion.device_setup_plans.manipulate_sample import move_phi_chi_omega
|
|
10
|
+
from mx_bluesky.hyperion.device_setup_plans.utils import (
|
|
11
|
+
start_preparing_data_collection_then_do_plan,
|
|
12
|
+
)
|
|
13
|
+
from mx_bluesky.hyperion.experiment_plans.grid_detect_then_xray_centre_plan import (
|
|
14
|
+
GridDetectThenXRayCentreComposite,
|
|
15
|
+
detect_grid_and_do_gridscan,
|
|
16
|
+
)
|
|
17
|
+
from mx_bluesky.hyperion.experiment_plans.oav_snapshot_plan import (
|
|
18
|
+
setup_beamline_for_OAV,
|
|
19
|
+
)
|
|
20
|
+
from mx_bluesky.hyperion.experiment_plans.pin_tip_centring_plan import (
|
|
21
|
+
PinTipCentringComposite,
|
|
22
|
+
pin_tip_centre_plan,
|
|
23
|
+
)
|
|
24
|
+
from mx_bluesky.hyperion.external_interaction.callbacks.xray_centre.ispyb_callback import (
|
|
25
|
+
ispyb_activation_wrapper,
|
|
26
|
+
)
|
|
27
|
+
from mx_bluesky.hyperion.log import LOGGER
|
|
28
|
+
from mx_bluesky.hyperion.parameters.constants import CONST
|
|
29
|
+
from mx_bluesky.hyperion.parameters.gridscan import (
|
|
30
|
+
GridScanWithEdgeDetect,
|
|
31
|
+
PinTipCentreThenXrayCentre,
|
|
32
|
+
)
|
|
33
|
+
from mx_bluesky.hyperion.utils.context import device_composite_from_context
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def create_devices(context: BlueskyContext) -> GridDetectThenXRayCentreComposite:
|
|
37
|
+
"""
|
|
38
|
+
GridDetectThenXRayCentreComposite contains all the devices we need, reuse that.
|
|
39
|
+
"""
|
|
40
|
+
return device_composite_from_context(context, GridDetectThenXRayCentreComposite)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def create_parameters_for_grid_detection(
|
|
44
|
+
pin_centre_parameters: PinTipCentreThenXrayCentre,
|
|
45
|
+
) -> GridScanWithEdgeDetect:
|
|
46
|
+
params_json = json.loads(pin_centre_parameters.model_dump_json())
|
|
47
|
+
del params_json["tip_offset_um"]
|
|
48
|
+
grid_detect_and_xray_centre = GridScanWithEdgeDetect(**params_json)
|
|
49
|
+
LOGGER.info(
|
|
50
|
+
f"Parameters for grid detect and xray centre: {grid_detect_and_xray_centre.model_dump_json(indent=2)}"
|
|
51
|
+
)
|
|
52
|
+
return grid_detect_and_xray_centre
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def pin_centre_then_xray_centre_plan(
|
|
56
|
+
composite: GridDetectThenXRayCentreComposite,
|
|
57
|
+
parameters: PinTipCentreThenXrayCentre,
|
|
58
|
+
oav_config_file: str = OAV_CONFIG_JSON,
|
|
59
|
+
):
|
|
60
|
+
"""Plan that perfoms a pin tip centre followed by an xray centre to completely
|
|
61
|
+
centre the sample"""
|
|
62
|
+
oav_config_file = parameters.oav_centring_file
|
|
63
|
+
|
|
64
|
+
pin_tip_centring_composite = PinTipCentringComposite(
|
|
65
|
+
oav=composite.oav,
|
|
66
|
+
smargon=composite.smargon,
|
|
67
|
+
backlight=composite.backlight,
|
|
68
|
+
pin_tip_detection=composite.pin_tip_detection,
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
def _pin_centre_then_xray_centre_plan():
|
|
72
|
+
yield from setup_beamline_for_OAV(
|
|
73
|
+
composite.smargon, composite.backlight, composite.aperture_scatterguard
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
yield from move_phi_chi_omega(
|
|
77
|
+
composite.smargon,
|
|
78
|
+
parameters.phi_start_deg,
|
|
79
|
+
parameters.chi_start_deg,
|
|
80
|
+
group=CONST.WAIT.READY_FOR_OAV,
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
yield from pin_tip_centre_plan(
|
|
84
|
+
pin_tip_centring_composite,
|
|
85
|
+
parameters.tip_offset_um,
|
|
86
|
+
oav_config_file,
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
grid_detect_params = create_parameters_for_grid_detection(parameters)
|
|
90
|
+
|
|
91
|
+
oav_params = OAVParameters("xrayCentring", oav_config_file)
|
|
92
|
+
|
|
93
|
+
yield from detect_grid_and_do_gridscan(
|
|
94
|
+
composite,
|
|
95
|
+
grid_detect_params,
|
|
96
|
+
oav_params,
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
yield from ispyb_activation_wrapper(_pin_centre_then_xray_centre_plan(), parameters)
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def pin_tip_centre_then_xray_centre(
|
|
103
|
+
composite: GridDetectThenXRayCentreComposite,
|
|
104
|
+
parameters: PinTipCentreThenXrayCentre,
|
|
105
|
+
oav_config_file: str = OAV_CONFIG_JSON,
|
|
106
|
+
) -> MsgGenerator:
|
|
107
|
+
"""Starts preparing for collection then performs the pin tip centre and xray centre"""
|
|
108
|
+
|
|
109
|
+
eiger: EigerDetector = composite.eiger
|
|
110
|
+
|
|
111
|
+
eiger.set_detector_parameters(parameters.detector_params)
|
|
112
|
+
|
|
113
|
+
return start_preparing_data_collection_then_do_plan(
|
|
114
|
+
eiger,
|
|
115
|
+
composite.detector_motion,
|
|
116
|
+
parameters.detector_params.detector_distance,
|
|
117
|
+
pin_centre_then_xray_centre_plan(composite, parameters, oav_config_file),
|
|
118
|
+
group=CONST.WAIT.GRID_READY_FOR_DC,
|
|
119
|
+
)
|