dls-dodal 1.61.0__py3-none-any.whl → 1.63.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.
- {dls_dodal-1.61.0.dist-info → dls_dodal-1.63.0.dist-info}/METADATA +1 -1
- {dls_dodal-1.61.0.dist-info → dls_dodal-1.63.0.dist-info}/RECORD +81 -73
- dls_dodal-1.63.0.dist-info/entry_points.txt +3 -0
- dodal/_version.py +2 -2
- dodal/beamlines/__init__.py +1 -0
- dodal/beamlines/adsim.py +5 -3
- dodal/beamlines/b21.py +3 -1
- dodal/beamlines/i02_2.py +32 -0
- dodal/beamlines/i03.py +9 -0
- dodal/beamlines/i04.py +1 -1
- dodal/beamlines/i09.py +10 -3
- dodal/beamlines/i09_1.py +9 -3
- dodal/beamlines/i10.py +7 -69
- dodal/beamlines/i10_1.py +35 -0
- dodal/beamlines/i10_optics.py +205 -0
- dodal/beamlines/i15_1.py +5 -5
- dodal/beamlines/i17.py +50 -1
- dodal/beamlines/i18.py +15 -9
- dodal/beamlines/i19_1.py +3 -3
- dodal/beamlines/i19_2.py +12 -2
- dodal/beamlines/i19_optics.py +4 -1
- dodal/beamlines/i24.py +3 -3
- dodal/cli.py +4 -4
- dodal/common/visit.py +4 -4
- dodal/devices/aperturescatterguard.py +6 -4
- dodal/devices/apple2_undulator.py +211 -114
- dodal/devices/attenuator/filter_selections.py +6 -6
- dodal/devices/common_dcm.py +62 -15
- dodal/devices/controllers.py +8 -6
- dodal/devices/current_amplifiers/femto.py +4 -4
- dodal/devices/current_amplifiers/sr570.py +3 -3
- dodal/devices/fast_grid_scan.py +97 -21
- dodal/devices/fast_shutter.py +69 -0
- dodal/devices/i02_1/fast_grid_scan.py +1 -1
- dodal/devices/i02_2/__init__.py +0 -0
- dodal/devices/i03/dcm.py +4 -2
- dodal/devices/i04/murko_results.py +35 -14
- dodal/devices/i09/__init__.py +1 -2
- dodal/devices/i10/__init__.py +29 -0
- dodal/devices/i10/diagnostics.py +37 -5
- dodal/devices/i10/i10_apple2.py +125 -229
- dodal/devices/i10/slits.py +38 -6
- dodal/devices/i15/dcm.py +6 -44
- dodal/devices/i17/__init__.py +0 -0
- dodal/devices/i17/i17_apple2.py +51 -0
- dodal/devices/i19/access_controlled/__init__.py +0 -0
- dodal/devices/i19/{shutter.py → access_controlled/shutter.py} +7 -4
- dodal/devices/i19/mapt_configuration.py +38 -0
- dodal/devices/i19/pin_col_stages.py +170 -0
- dodal/devices/i22/dcm.py +2 -2
- dodal/devices/i24/dcm.py +2 -2
- dodal/devices/oav/oav_detector.py +1 -1
- dodal/devices/oav/oav_parameters.py +4 -4
- dodal/devices/oav/oav_to_redis_forwarder.py +4 -4
- dodal/devices/oav/pin_image_recognition/__init__.py +3 -3
- dodal/devices/oav/pin_image_recognition/utils.py +1 -1
- dodal/devices/oav/snapshots/snapshot.py +1 -1
- dodal/devices/oav/snapshots/snapshot_image_processing.py +12 -12
- dodal/devices/oav/snapshots/snapshot_with_grid.py +1 -1
- dodal/devices/oav/utils.py +2 -2
- dodal/devices/pgm.py +3 -3
- dodal/devices/robot.py +5 -5
- dodal/devices/tetramm.py +9 -5
- dodal/devices/thawer.py +0 -4
- dodal/devices/v2f.py +2 -2
- dodal/devices/zebra/zebra_constants_mapping.py +2 -2
- dodal/devices/zocalo/__init__.py +4 -4
- dodal/devices/zocalo/zocalo_results.py +4 -4
- dodal/log.py +9 -9
- dodal/plan_stubs/motor_utils.py +4 -4
- dodal/plans/configure_arm_trigger_and_disarm_detector.py +29 -7
- dodal/plans/save_panda.py +7 -7
- dodal/plans/verify_undulator_gap.py +2 -2
- dls_dodal-1.61.0.dist-info/entry_points.txt +0 -3
- dodal/beamlines/i10-1.py +0 -25
- dodal/devices/i09/dcm.py +0 -26
- {dls_dodal-1.61.0.dist-info → dls_dodal-1.63.0.dist-info}/WHEEL +0 -0
- {dls_dodal-1.61.0.dist-info → dls_dodal-1.63.0.dist-info}/licenses/LICENSE +0 -0
- {dls_dodal-1.61.0.dist-info → dls_dodal-1.63.0.dist-info}/top_level.txt +0 -0
- /dodal/devices/areadetector/plugins/{CAM.py → cam.py} +0 -0
- /dodal/devices/areadetector/plugins/{MJPG.py → mjpg.py} +0 -0
- /dodal/devices/i18/{KBMirror.py → kb_mirror.py} +0 -0
- /dodal/devices/i19/{blueapi_device.py → access_controlled/blueapi_device.py} +0 -0
- /dodal/devices/i19/{hutch_access.py → access_controlled/hutch_access.py} +0 -0
dodal/devices/fast_grid_scan.py
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
+
import asyncio
|
|
1
2
|
from abc import ABC, abstractmethod
|
|
2
3
|
from typing import Generic, TypeVar
|
|
3
4
|
|
|
4
5
|
import numpy as np
|
|
5
|
-
from bluesky.plan_stubs import
|
|
6
|
-
from bluesky.protocols import Flyable
|
|
6
|
+
from bluesky.plan_stubs import prepare
|
|
7
|
+
from bluesky.protocols import Flyable, Preparable
|
|
7
8
|
from numpy import ndarray
|
|
8
9
|
from ophyd_async.core import (
|
|
9
10
|
AsyncStatus,
|
|
@@ -13,6 +14,7 @@ from ophyd_async.core import (
|
|
|
13
14
|
SignalRW,
|
|
14
15
|
StandardReadable,
|
|
15
16
|
derived_signal_r,
|
|
17
|
+
set_and_wait_for_value,
|
|
16
18
|
soft_signal_r_and_setter,
|
|
17
19
|
wait_for_value,
|
|
18
20
|
)
|
|
@@ -29,6 +31,10 @@ from dodal.log import LOGGER
|
|
|
29
31
|
from dodal.parameters.experiment_parameter_base import AbstractExperimentWithBeamParams
|
|
30
32
|
|
|
31
33
|
|
|
34
|
+
class GridScanInvalidError(RuntimeError):
|
|
35
|
+
"""Raised when the gridscan parameters are not valid."""
|
|
36
|
+
|
|
37
|
+
|
|
32
38
|
@dataclass
|
|
33
39
|
class GridAxis:
|
|
34
40
|
start: float
|
|
@@ -144,7 +150,7 @@ class GridScanParamsThreeD(GridScanParamsCommon):
|
|
|
144
150
|
return GridAxis(self.z2_start_mm, self.z_step_size_mm, self.z_steps)
|
|
145
151
|
|
|
146
152
|
|
|
147
|
-
ParamType = TypeVar("ParamType", bound=GridScanParamsCommon
|
|
153
|
+
ParamType = TypeVar("ParamType", bound=GridScanParamsCommon)
|
|
148
154
|
|
|
149
155
|
|
|
150
156
|
class WithDwellTime(BaseModel):
|
|
@@ -190,7 +196,9 @@ class MotionProgram(Device):
|
|
|
190
196
|
self.program_number = soft_signal_r_and_setter(float, -1)[0]
|
|
191
197
|
|
|
192
198
|
|
|
193
|
-
class FastGridScanCommon(
|
|
199
|
+
class FastGridScanCommon(
|
|
200
|
+
StandardReadable, Flyable, ABC, Preparable, Generic[ParamType]
|
|
201
|
+
):
|
|
194
202
|
"""Device containing the minimal signals for a general fast grid scan.
|
|
195
203
|
|
|
196
204
|
When the motion program is started, the goniometer will move in a snake-like grid trajectory,
|
|
@@ -231,8 +239,9 @@ class FastGridScanCommon(StandardReadable, Flyable, ABC, Generic[ParamType]):
|
|
|
231
239
|
self.KICKOFF_TIMEOUT: float = 5.0
|
|
232
240
|
|
|
233
241
|
self.COMPLETE_STATUS: float = 60.0
|
|
242
|
+
self.VALIDITY_CHECK_TIMEOUT = 0.5
|
|
234
243
|
|
|
235
|
-
self.
|
|
244
|
+
self._movable_params: dict[str, Signal] = {
|
|
236
245
|
"x_steps": self.x_steps,
|
|
237
246
|
"y_steps": self.y_steps,
|
|
238
247
|
"x_step_size_mm": self.x_step_size,
|
|
@@ -284,6 +293,45 @@ class FastGridScanCommon(StandardReadable, Flyable, ABC, Generic[ParamType]):
|
|
|
284
293
|
self, motion_controller_prefix: str
|
|
285
294
|
) -> MotionProgram: ...
|
|
286
295
|
|
|
296
|
+
@AsyncStatus.wrap
|
|
297
|
+
async def prepare(self, value: ParamType):
|
|
298
|
+
"""
|
|
299
|
+
Submit the gridscan parameters to the device for validation prior to
|
|
300
|
+
gridscan kickoff
|
|
301
|
+
Args:
|
|
302
|
+
value: the gridscan parameters
|
|
303
|
+
|
|
304
|
+
Raises:
|
|
305
|
+
GridScanInvalidError: if the gridscan parameters were not valid
|
|
306
|
+
"""
|
|
307
|
+
set_statuses = []
|
|
308
|
+
|
|
309
|
+
LOGGER.info("Applying gridscan parameters...")
|
|
310
|
+
# Create arguments for bps.mv
|
|
311
|
+
for key, signal in self._movable_params.items():
|
|
312
|
+
param_value = value.__dict__[key]
|
|
313
|
+
set_statuses.append(await set_and_wait_for_value(signal, param_value)) # type: ignore
|
|
314
|
+
|
|
315
|
+
# Counter should always start at 0
|
|
316
|
+
set_statuses.append(await set_and_wait_for_value(self.position_counter, 0))
|
|
317
|
+
|
|
318
|
+
LOGGER.info("Gridscan parameters applied, waiting for sets to complete...")
|
|
319
|
+
|
|
320
|
+
# wait for parameter sets to complete
|
|
321
|
+
await asyncio.gather(*set_statuses)
|
|
322
|
+
|
|
323
|
+
LOGGER.info("Sets confirmed, waiting for validity checks to pass...")
|
|
324
|
+
try:
|
|
325
|
+
await wait_for_value(
|
|
326
|
+
self.scan_invalid, 0.0, timeout=self.VALIDITY_CHECK_TIMEOUT
|
|
327
|
+
)
|
|
328
|
+
except TimeoutError as e:
|
|
329
|
+
raise GridScanInvalidError(
|
|
330
|
+
f"Gridscan parameters not validated after {self.VALIDITY_CHECK_TIMEOUT}s"
|
|
331
|
+
) from e
|
|
332
|
+
|
|
333
|
+
LOGGER.info("Gridscan validity confirmed, gridscan is now prepared.")
|
|
334
|
+
|
|
287
335
|
|
|
288
336
|
class FastGridScanThreeD(FastGridScanCommon[ParamType]):
|
|
289
337
|
"""Device for standard 3D FGS.
|
|
@@ -309,10 +357,10 @@ class FastGridScanThreeD(FastGridScanCommon[ParamType]):
|
|
|
309
357
|
|
|
310
358
|
super().__init__(full_prefix, prefix, name)
|
|
311
359
|
|
|
312
|
-
self.
|
|
313
|
-
self.
|
|
314
|
-
self.
|
|
315
|
-
self.
|
|
360
|
+
self._movable_params["z_step_size_mm"] = self.z_step_size
|
|
361
|
+
self._movable_params["z2_start_mm"] = self.z2_start
|
|
362
|
+
self._movable_params["y2_start_mm"] = self.y2_start
|
|
363
|
+
self._movable_params["z_steps"] = self.z_steps
|
|
316
364
|
|
|
317
365
|
def _create_expected_images_signal(self):
|
|
318
366
|
return derived_signal_r(
|
|
@@ -329,7 +377,35 @@ class FastGridScanThreeD(FastGridScanCommon[ParamType]):
|
|
|
329
377
|
return first_grid + second_grid
|
|
330
378
|
|
|
331
379
|
def _create_scan_invalid_signal(self, prefix: str) -> SignalR[float]:
|
|
332
|
-
|
|
380
|
+
self.x_scan_valid = epics_signal_r(float, f"{prefix}X_SCAN_VALID")
|
|
381
|
+
self.y_scan_valid = epics_signal_r(float, f"{prefix}Y_SCAN_VALID")
|
|
382
|
+
self.z_scan_valid = epics_signal_r(float, f"{prefix}Z_SCAN_VALID")
|
|
383
|
+
self.device_scan_invalid = epics_signal_r(float, f"{prefix}SCAN_INVALID")
|
|
384
|
+
|
|
385
|
+
def compute_derived_value(
|
|
386
|
+
x_scan_valid: float,
|
|
387
|
+
y_scan_valid: float,
|
|
388
|
+
z_scan_valid: float,
|
|
389
|
+
device_scan_invalid: float,
|
|
390
|
+
) -> float:
|
|
391
|
+
return (
|
|
392
|
+
1.0
|
|
393
|
+
if not (
|
|
394
|
+
x_scan_valid
|
|
395
|
+
and y_scan_valid
|
|
396
|
+
and z_scan_valid
|
|
397
|
+
and not device_scan_invalid
|
|
398
|
+
)
|
|
399
|
+
else 0.0
|
|
400
|
+
)
|
|
401
|
+
|
|
402
|
+
return derived_signal_r(
|
|
403
|
+
compute_derived_value,
|
|
404
|
+
x_scan_valid=self.x_scan_valid,
|
|
405
|
+
y_scan_valid=self.y_scan_valid,
|
|
406
|
+
z_scan_valid=self.z_scan_valid,
|
|
407
|
+
device_scan_invalid=self.device_scan_invalid,
|
|
408
|
+
)
|
|
333
409
|
|
|
334
410
|
def _create_motion_program(self, motion_controller_prefix: str):
|
|
335
411
|
return MotionProgram(motion_controller_prefix)
|
|
@@ -349,7 +425,7 @@ class ZebraFastGridScanThreeD(FastGridScanThreeD[ZebraGridScanParamsThreeD]):
|
|
|
349
425
|
self.dwell_time_ms = epics_signal_rw_rbv(float, f"{full_prefix}DWELL_TIME")
|
|
350
426
|
self.x_counter = epics_signal_r(int, f"{full_prefix}X_COUNTER")
|
|
351
427
|
super().__init__(prefix, infix, name)
|
|
352
|
-
self.
|
|
428
|
+
self._movable_params["dwell_time_ms"] = self.dwell_time_ms
|
|
353
429
|
|
|
354
430
|
def _create_position_counter(self, prefix: str):
|
|
355
431
|
return epics_signal_rw(
|
|
@@ -380,20 +456,20 @@ class PandAFastGridScan(FastGridScanThreeD[PandAGridScanParams]):
|
|
|
380
456
|
)
|
|
381
457
|
super().__init__(prefix, infix, name)
|
|
382
458
|
|
|
383
|
-
self.
|
|
459
|
+
self._movable_params["run_up_distance_mm"] = self.run_up_distance_mm
|
|
384
460
|
|
|
385
461
|
def _create_position_counter(self, prefix: str):
|
|
386
462
|
return epics_signal_rw(int, f"{prefix}Y_COUNTER")
|
|
387
463
|
|
|
388
464
|
|
|
389
465
|
def set_fast_grid_scan_params(scan: FastGridScanCommon[ParamType], params: ParamType):
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
# Counter should always start at 0
|
|
397
|
-
to_move.extend([scan.position_counter, 0])
|
|
466
|
+
"""
|
|
467
|
+
Apply the fast grid scan parameters to the grid scan device and validate them
|
|
468
|
+
Args:
|
|
469
|
+
scan: The fast grid scan device
|
|
470
|
+
params: The parameters to set
|
|
398
471
|
|
|
399
|
-
|
|
472
|
+
Raises:
|
|
473
|
+
GridScanInvalidError: if the grid scan parameters are not valid
|
|
474
|
+
"""
|
|
475
|
+
yield from prepare(scan, params, wait=True)
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
from typing import TypeVar
|
|
2
|
+
|
|
3
|
+
from bluesky.protocols import Movable
|
|
4
|
+
from ophyd_async.core import (
|
|
5
|
+
AsyncStatus,
|
|
6
|
+
EnumTypes,
|
|
7
|
+
StandardReadable,
|
|
8
|
+
)
|
|
9
|
+
from ophyd_async.epics.core import epics_signal_rw
|
|
10
|
+
|
|
11
|
+
StrictEnumT = TypeVar("StrictEnumT", bound=EnumTypes)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class GenericFastShutter(StandardReadable, Movable[StrictEnumT]):
|
|
15
|
+
"""
|
|
16
|
+
Basic enum device specialised for a fast shutter with configured open_state and
|
|
17
|
+
close_state so it is generic enough to be used with any device or plan without
|
|
18
|
+
knowing the specific enum to use.
|
|
19
|
+
|
|
20
|
+
For example:
|
|
21
|
+
await shutter.set(shutter.open_state)
|
|
22
|
+
await shutter.set(shutter.close_state)
|
|
23
|
+
OR
|
|
24
|
+
run_engine(bps.mv(shutter, shutter.open_state))
|
|
25
|
+
run_engine(bps.mv(shutter, shutter.close_state))
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
def __init__(
|
|
29
|
+
self,
|
|
30
|
+
pv: str,
|
|
31
|
+
open_state: StrictEnumT,
|
|
32
|
+
close_state: StrictEnumT,
|
|
33
|
+
name: str = "",
|
|
34
|
+
):
|
|
35
|
+
"""
|
|
36
|
+
Arguments:
|
|
37
|
+
pv: The pv to connect to the shutter device.
|
|
38
|
+
open_state: The enum value that corresponds with opening the shutter.
|
|
39
|
+
close_state: The enum value that corresponds with closing the shutter.
|
|
40
|
+
"""
|
|
41
|
+
self.open_state = open_state
|
|
42
|
+
self.close_state = close_state
|
|
43
|
+
with self.add_children_as_readables():
|
|
44
|
+
self.state = epics_signal_rw(type(self.open_state), pv)
|
|
45
|
+
super().__init__(name)
|
|
46
|
+
|
|
47
|
+
@AsyncStatus.wrap
|
|
48
|
+
async def set(self, value: StrictEnumT) -> None:
|
|
49
|
+
await self.state.set(value)
|
|
50
|
+
|
|
51
|
+
async def is_open(self) -> bool:
|
|
52
|
+
"""
|
|
53
|
+
Checks to see if shutter is in open_state. Should not be used directly inside a
|
|
54
|
+
plan. A user should use the following instead in a plan:
|
|
55
|
+
|
|
56
|
+
from bluesky import plan_stubs as bps
|
|
57
|
+
is_open = yield from bps.rd(shutter.state) == shutter.open_state
|
|
58
|
+
"""
|
|
59
|
+
return await self.state.get_value() == self.open_state
|
|
60
|
+
|
|
61
|
+
async def is_closed(self) -> bool:
|
|
62
|
+
"""
|
|
63
|
+
Checks to see if shutter is in close_state. Should not be used directly inside a
|
|
64
|
+
plan. A user should use the following instead in a plan:
|
|
65
|
+
|
|
66
|
+
from bluesky import plan_stubs as bps
|
|
67
|
+
is_closed = yield from bps.rd(shutter.state) == shutter.close_state
|
|
68
|
+
"""
|
|
69
|
+
return await self.state.get_value() == self.close_state
|
|
@@ -35,7 +35,7 @@ class ZebraFastGridScanTwoD(FastGridScanCommon[ZebraGridScanParamsTwoD]):
|
|
|
35
35
|
# See https://github.com/DiamondLightSource/mx-bluesky/issues/1203
|
|
36
36
|
self.dwell_time_ms = epics_signal_rw_rbv(float, f"{full_prefix}EXPOSURE_TIME")
|
|
37
37
|
|
|
38
|
-
self.
|
|
38
|
+
self._movable_params["dwell_time_ms"] = self.dwell_time_ms
|
|
39
39
|
|
|
40
40
|
def _create_expected_images_signal(self):
|
|
41
41
|
return derived_signal_r(
|
|
File without changes
|
dodal/devices/i03/dcm.py
CHANGED
|
@@ -9,13 +9,15 @@ from dodal.common.crystal_metadata import (
|
|
|
9
9
|
make_crystal_metadata_from_material,
|
|
10
10
|
)
|
|
11
11
|
from dodal.devices.common_dcm import (
|
|
12
|
-
|
|
12
|
+
DoubleCrystalMonochromatorWithDSpacing,
|
|
13
13
|
PitchAndRollCrystal,
|
|
14
14
|
StationaryCrystal,
|
|
15
15
|
)
|
|
16
16
|
|
|
17
17
|
|
|
18
|
-
class DCM(
|
|
18
|
+
class DCM(
|
|
19
|
+
DoubleCrystalMonochromatorWithDSpacing[PitchAndRollCrystal, StationaryCrystal]
|
|
20
|
+
):
|
|
19
21
|
"""
|
|
20
22
|
A double crystal monochromator (DCM), used to select the energy of the beam.
|
|
21
23
|
|
|
@@ -43,7 +43,7 @@ class Coord(Enum):
|
|
|
43
43
|
|
|
44
44
|
@dataclass
|
|
45
45
|
class MurkoResult:
|
|
46
|
-
|
|
46
|
+
chosen_point_px: tuple[int, int]
|
|
47
47
|
x_dist_mm: float
|
|
48
48
|
y_dist_mm: float
|
|
49
49
|
omega: float
|
|
@@ -51,7 +51,7 @@ class MurkoResult:
|
|
|
51
51
|
metadata: MurkoMetadata
|
|
52
52
|
|
|
53
53
|
|
|
54
|
-
class
|
|
54
|
+
class NoResultsFoundError(ValueError):
|
|
55
55
|
pass
|
|
56
56
|
|
|
57
57
|
|
|
@@ -73,6 +73,7 @@ class MurkoResultsDevice(StandardReadable, Triggerable, Stageable):
|
|
|
73
73
|
|
|
74
74
|
TIMEOUT_S = 2
|
|
75
75
|
PERCENTAGE_TO_USE = 25
|
|
76
|
+
LEFTMOST_PIXEL_TO_USE = 10
|
|
76
77
|
NUMBER_OF_WRONG_RESULTS_TO_LOG = 5
|
|
77
78
|
|
|
78
79
|
def __init__(
|
|
@@ -129,7 +130,7 @@ class MurkoResultsDevice(StandardReadable, Triggerable, Stageable):
|
|
|
129
130
|
await self.process_batch(message, sample_id)
|
|
130
131
|
|
|
131
132
|
if not self._results:
|
|
132
|
-
raise
|
|
133
|
+
raise NoResultsFoundError("No results retrieved from Murko")
|
|
133
134
|
|
|
134
135
|
for result in self._results:
|
|
135
136
|
LOGGER.debug(result)
|
|
@@ -186,16 +187,16 @@ class MurkoResultsDevice(StandardReadable, Triggerable, Stageable):
|
|
|
186
187
|
else:
|
|
187
188
|
shape = result["original_shape"] # Dimensions of image in pixels
|
|
188
189
|
# Murko returns coords as y, x
|
|
189
|
-
|
|
190
|
+
chosen_point_px = (coords[1] * shape[1], coords[0] * shape[0])
|
|
190
191
|
|
|
191
192
|
beam_dist_px = calculate_beam_distance(
|
|
192
193
|
(metadata["beam_centre_i"], metadata["beam_centre_j"]),
|
|
193
|
-
|
|
194
|
-
|
|
194
|
+
chosen_point_px[0],
|
|
195
|
+
chosen_point_px[1],
|
|
195
196
|
)
|
|
196
197
|
self._results.append(
|
|
197
198
|
MurkoResult(
|
|
198
|
-
|
|
199
|
+
chosen_point_px=chosen_point_px,
|
|
199
200
|
x_dist_mm=beam_dist_px[0] * metadata["microns_per_x_pixel"] / 1000,
|
|
200
201
|
y_dist_mm=beam_dist_px[1] * metadata["microns_per_y_pixel"] / 1000,
|
|
201
202
|
omega=omega,
|
|
@@ -209,10 +210,27 @@ class MurkoResultsDevice(StandardReadable, Triggerable, Stageable):
|
|
|
209
210
|
"""Whilst murko is not fully trained it often gives us poor results.
|
|
210
211
|
When it is wrong it usually picks up the base of the pin, rather than the tip,
|
|
211
212
|
meaning that by keeping only a percentage of the results with the smallest X we
|
|
212
|
-
remove many of the outliers.
|
|
213
|
+
remove many of the outliers. Murko also occasionally picks a point in the bottom
|
|
214
|
+
left corner, which can be removed by filtering results with a small x pixel.
|
|
213
215
|
"""
|
|
216
|
+
|
|
214
217
|
LOGGER.info(f"Number of results before filtering: {len(self._results)}")
|
|
215
|
-
sorted_results = sorted(self._results, key=lambda item: item.
|
|
218
|
+
sorted_results = sorted(self._results, key=lambda item: item.chosen_point_px[0])
|
|
219
|
+
|
|
220
|
+
results_without_tiny_x = [
|
|
221
|
+
result
|
|
222
|
+
for result in sorted_results
|
|
223
|
+
if result.chosen_point_px[0] >= self.LEFTMOST_PIXEL_TO_USE
|
|
224
|
+
]
|
|
225
|
+
result_uuids_with_tiny_x = [
|
|
226
|
+
result.uuid
|
|
227
|
+
for result in sorted_results
|
|
228
|
+
if result not in results_without_tiny_x
|
|
229
|
+
]
|
|
230
|
+
|
|
231
|
+
LOGGER.info(
|
|
232
|
+
f"Results with tiny x have been removed: {result_uuids_with_tiny_x}"
|
|
233
|
+
)
|
|
216
234
|
|
|
217
235
|
worst_results = [
|
|
218
236
|
r.uuid for r in sorted_results[-self.NUMBER_OF_WRONG_RESULTS_TO_LOG :]
|
|
@@ -221,14 +239,17 @@ class MurkoResultsDevice(StandardReadable, Triggerable, Stageable):
|
|
|
221
239
|
LOGGER.info(
|
|
222
240
|
f"Worst {self.NUMBER_OF_WRONG_RESULTS_TO_LOG} murko results were {worst_results}"
|
|
223
241
|
)
|
|
242
|
+
|
|
224
243
|
cutoff = max(1, int(len(sorted_results) * self.PERCENTAGE_TO_USE / 100))
|
|
225
|
-
|
|
226
|
-
|
|
244
|
+
cutoff = min(cutoff, len(results_without_tiny_x))
|
|
245
|
+
|
|
246
|
+
best_x = results_without_tiny_x[:cutoff]
|
|
227
247
|
|
|
228
|
-
|
|
248
|
+
for result in sorted_results:
|
|
249
|
+
result.metadata["used_for_centring"] = result in best_x
|
|
229
250
|
|
|
230
|
-
LOGGER.info(f"Number of results after filtering: {len(
|
|
231
|
-
return
|
|
251
|
+
LOGGER.info(f"Number of results after filtering: {len(best_x)}")
|
|
252
|
+
return best_x
|
|
232
253
|
|
|
233
254
|
|
|
234
255
|
def get_yz_least_squares(vertical_dists: list, omegas: list) -> tuple[float, float]:
|
dodal/devices/i09/__init__.py
CHANGED
dodal/devices/i10/__init__.py
CHANGED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
from .diagnostics import (
|
|
2
|
+
I10Diagnostic,
|
|
3
|
+
I10Diagnostic5ADet,
|
|
4
|
+
I10JDiagnostic,
|
|
5
|
+
I10PneumaticStage,
|
|
6
|
+
I10SharedDiagnostic,
|
|
7
|
+
)
|
|
8
|
+
from .mirrors import PiezoMirror
|
|
9
|
+
from .slits import (
|
|
10
|
+
I10JSlits,
|
|
11
|
+
I10SharedSlits,
|
|
12
|
+
I10SharedSlitsDrainCurrent,
|
|
13
|
+
I10Slits,
|
|
14
|
+
I10SlitsDrainCurrent,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
__all__ = [
|
|
18
|
+
"I10Diagnostic",
|
|
19
|
+
"I10Diagnostic5ADet",
|
|
20
|
+
"I10PneumaticStage",
|
|
21
|
+
"PiezoMirror",
|
|
22
|
+
"I10Slits",
|
|
23
|
+
"I10SlitsDrainCurrent",
|
|
24
|
+
"I10SharedDiagnostic",
|
|
25
|
+
"I10SharedSlits",
|
|
26
|
+
"I10JSlits",
|
|
27
|
+
"I10SharedSlitsDrainCurrent",
|
|
28
|
+
"I10JDiagnostic",
|
|
29
|
+
]
|
dodal/devices/i10/diagnostics.py
CHANGED
|
@@ -28,7 +28,7 @@ class D3Position(StrictEnum):
|
|
|
28
28
|
GRID = "Grid"
|
|
29
29
|
|
|
30
30
|
|
|
31
|
-
class
|
|
31
|
+
class CellPosition(StrictEnum):
|
|
32
32
|
CELL_IN = "Cell In"
|
|
33
33
|
CELL_OUT = "Cell Out"
|
|
34
34
|
|
|
@@ -68,6 +68,21 @@ class InStateReadBackTable(StrictEnum):
|
|
|
68
68
|
OUT_OF_BEAM = "Out of Beam"
|
|
69
69
|
|
|
70
70
|
|
|
71
|
+
class D2jPosition(StrictEnum):
|
|
72
|
+
OUT_OF_THE_BEAM = "Out of the beam"
|
|
73
|
+
DIODE = "Diode"
|
|
74
|
+
BLADE = "Blade"
|
|
75
|
+
LA = "La ref"
|
|
76
|
+
GD = "Gd ref"
|
|
77
|
+
YB = "Yb ref"
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
class D3jPosition(StrictEnum):
|
|
81
|
+
OUT_OF_THE_BEAM = "Out of the beam"
|
|
82
|
+
DIODE_IN = "Diode In"
|
|
83
|
+
DIAMOND_WINDOW = "Diamond window"
|
|
84
|
+
|
|
85
|
+
|
|
71
86
|
class I10PneumaticStage(StandardReadable):
|
|
72
87
|
"""Pneumatic stage only has two real positions in or out.
|
|
73
88
|
Use for fluorescent screen which can be insert into the x-ray beam.
|
|
@@ -138,9 +153,7 @@ class FullDiagnostic(Device):
|
|
|
138
153
|
super().__init__(name)
|
|
139
154
|
|
|
140
155
|
|
|
141
|
-
class
|
|
142
|
-
"""Collection of all the diagnostic stage on i10."""
|
|
143
|
-
|
|
156
|
+
class I10SharedDiagnostic(Device):
|
|
144
157
|
def __init__(self, prefix, name: str = "") -> None:
|
|
145
158
|
self.d1 = ScreenCam(prefix=prefix + "PHDGN-01:")
|
|
146
159
|
self.d2 = ScreenCam(prefix=prefix + "PHDGN-02:")
|
|
@@ -149,8 +162,15 @@ class I10Diagnostic(Device):
|
|
|
149
162
|
positioner_enum=D3Position,
|
|
150
163
|
positioner_suffix="DET:X",
|
|
151
164
|
)
|
|
165
|
+
super().__init__(name)
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
class I10Diagnostic(Device):
|
|
169
|
+
"""Collection of all the diagnostic stage on i10."""
|
|
170
|
+
|
|
171
|
+
def __init__(self, prefix, name: str = "") -> None:
|
|
152
172
|
self.d4 = ScreenCam(prefix=prefix + "PHDGN-04:")
|
|
153
|
-
self.d5 = create_positioner(
|
|
173
|
+
self.d5 = create_positioner(CellPosition, f"{prefix}IONC-01:Y")
|
|
154
174
|
self.d5A = create_positioner(D5APosition, f"{prefix}PHDGN-06:DET:X")
|
|
155
175
|
self.d6 = FullDiagnostic(f"{prefix}PHDGN-05:", D6Position, "DET:X")
|
|
156
176
|
self.d7 = create_positioner(D7Position, f"{prefix}PHDGN-07:Y")
|
|
@@ -158,6 +178,18 @@ class I10Diagnostic(Device):
|
|
|
158
178
|
super().__init__(name)
|
|
159
179
|
|
|
160
180
|
|
|
181
|
+
class I10JDiagnostic(Device):
|
|
182
|
+
"""Collection of all the diagnostic stage on i10-1."""
|
|
183
|
+
|
|
184
|
+
def __init__(self, prefix, name: str = "") -> None:
|
|
185
|
+
self.dj1 = ScreenCam(prefix=prefix + "PHDGN-01:")
|
|
186
|
+
self.dj2 = create_positioner(CellPosition, f"{prefix}IONC-01:Y")
|
|
187
|
+
self.dj2A = create_positioner(D2jPosition, f"{prefix}PHDGN-03:DET:X")
|
|
188
|
+
self.dj3 = FullDiagnostic(f"{prefix}PHDGN-02:", D3jPosition, "DET:X")
|
|
189
|
+
|
|
190
|
+
super().__init__(name)
|
|
191
|
+
|
|
192
|
+
|
|
161
193
|
class I10Diagnostic5ADet(Device):
|
|
162
194
|
"""Diagnostic 5a detection with drain current and photo diode"""
|
|
163
195
|
|