mx-bluesky 1.2.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.
Files changed (105) hide show
  1. mx_bluesky/__init__.py +8 -3
  2. mx_bluesky/__main__.py +12 -7
  3. mx_bluesky/_version.py +2 -2
  4. mx_bluesky/beamlines/i04/callbacks/murko_callback.py +14 -4
  5. mx_bluesky/beamlines/i04/redis_to_murko_forwarder.py +178 -0
  6. mx_bluesky/beamlines/i04/thawing_plan.py +49 -11
  7. mx_bluesky/beamlines/i24/serial/__init__.py +3 -0
  8. mx_bluesky/beamlines/i24/serial/dcid.py +143 -171
  9. mx_bluesky/beamlines/i24/serial/extruder/EX-gui-edm/DiamondExtruder-I24-py3v1.edl +1 -1
  10. mx_bluesky/beamlines/i24/serial/extruder/i24ssx_Extruder_Collect_py3v2.py +121 -110
  11. mx_bluesky/beamlines/i24/serial/fixed_target/FT-gui-edm/DiamondChipI24-py3v1.edl +3 -6
  12. mx_bluesky/beamlines/i24/serial/fixed_target/ft_utils.py +0 -1
  13. mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_Chip_Collect_py3v1.py +164 -169
  14. mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_Chip_Manager_py3v1.py +149 -225
  15. mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_Chip_StartUp_py3v1.py +7 -216
  16. mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_moveonclick.py +18 -17
  17. mx_bluesky/beamlines/i24/serial/log.py +58 -49
  18. mx_bluesky/beamlines/i24/serial/parameters/__init__.py +4 -0
  19. mx_bluesky/beamlines/i24/serial/parameters/constants.py +6 -1
  20. mx_bluesky/beamlines/i24/serial/parameters/experiment_parameters.py +42 -15
  21. mx_bluesky/beamlines/i24/serial/parameters/fixed_target/cs/cs_maker.json +3 -3
  22. mx_bluesky/beamlines/i24/serial/run_extruder.sh +30 -5
  23. mx_bluesky/beamlines/i24/serial/run_fixed_target.sh +30 -5
  24. mx_bluesky/beamlines/i24/serial/run_serial.py +24 -8
  25. mx_bluesky/beamlines/i24/serial/setup_beamline/ca.py +0 -2
  26. mx_bluesky/beamlines/i24/serial/setup_beamline/pv.py +2 -0
  27. mx_bluesky/beamlines/i24/serial/setup_beamline/setup_beamline.py +104 -82
  28. mx_bluesky/beamlines/i24/serial/setup_beamline/setup_detector.py +9 -20
  29. mx_bluesky/beamlines/i24/serial/setup_beamline/setup_zebra_plans.py +26 -28
  30. mx_bluesky/beamlines/i24/serial/write_nexus.py +74 -72
  31. mx_bluesky/common/__init__.py +0 -0
  32. mx_bluesky/common/device_setup_plans/read_hardware_for_setup.py +14 -0
  33. mx_bluesky/common/external_interaction/config_server.py +46 -0
  34. mx_bluesky/common/parameters/components.py +258 -0
  35. mx_bluesky/common/parameters/constants.py +143 -0
  36. mx_bluesky/common/parameters/gridscan.py +94 -0
  37. mx_bluesky/common/parameters/robot_load.py +16 -0
  38. mx_bluesky/common/plans/__init__.py +1 -0
  39. mx_bluesky/common/plans/do_fgs.py +121 -0
  40. mx_bluesky/common/utils/log.py +118 -0
  41. mx_bluesky/{hyperion → common/utils}/tracing.py +2 -2
  42. mx_bluesky/hyperion/__main__.py +13 -10
  43. mx_bluesky/hyperion/device_setup_plans/dcm_pitch_roll_mirror_adjuster.py +47 -52
  44. mx_bluesky/hyperion/device_setup_plans/read_hardware_for_setup.py +6 -12
  45. mx_bluesky/hyperion/device_setup_plans/setup_oav.py +6 -12
  46. mx_bluesky/hyperion/device_setup_plans/setup_panda.py +5 -6
  47. mx_bluesky/hyperion/device_setup_plans/setup_zebra.py +49 -18
  48. mx_bluesky/hyperion/device_setup_plans/smargon.py +9 -9
  49. mx_bluesky/hyperion/device_setup_plans/utils.py +2 -2
  50. mx_bluesky/hyperion/device_setup_plans/xbpm_feedback.py +4 -4
  51. mx_bluesky/hyperion/exceptions.py +13 -1
  52. mx_bluesky/hyperion/experiment_plans/__init__.py +4 -0
  53. mx_bluesky/hyperion/experiment_plans/change_aperture_then_move_plan.py +83 -0
  54. mx_bluesky/hyperion/experiment_plans/common/xrc_result.py +47 -0
  55. mx_bluesky/hyperion/experiment_plans/experiment_registry.py +9 -9
  56. mx_bluesky/hyperion/experiment_plans/flyscan_xray_centre_plan.py +147 -169
  57. mx_bluesky/hyperion/experiment_plans/grid_detect_then_xray_centre_plan.py +48 -22
  58. mx_bluesky/hyperion/experiment_plans/load_centre_collect_full_plan.py +75 -9
  59. mx_bluesky/hyperion/experiment_plans/oav_grid_detection_plan.py +21 -20
  60. mx_bluesky/hyperion/experiment_plans/oav_snapshot_plan.py +9 -6
  61. mx_bluesky/hyperion/experiment_plans/optimise_attenuation_plan.py +2 -2
  62. mx_bluesky/hyperion/experiment_plans/pin_centre_then_xray_centre_plan.py +40 -21
  63. mx_bluesky/hyperion/experiment_plans/pin_tip_centring_plan.py +22 -22
  64. mx_bluesky/hyperion/experiment_plans/robot_load_and_change_energy.py +43 -39
  65. mx_bluesky/hyperion/experiment_plans/robot_load_then_centre_plan.py +69 -18
  66. mx_bluesky/hyperion/experiment_plans/rotation_scan_plan.py +17 -7
  67. mx_bluesky/hyperion/experiment_plans/set_energy_plan.py +13 -13
  68. mx_bluesky/hyperion/external_interaction/callbacks/__init__.py +0 -4
  69. mx_bluesky/hyperion/external_interaction/callbacks/__main__.py +5 -2
  70. mx_bluesky/hyperion/external_interaction/callbacks/common/abstract_event.py +66 -0
  71. mx_bluesky/hyperion/external_interaction/callbacks/common/callback_util.py +5 -0
  72. mx_bluesky/hyperion/external_interaction/callbacks/common/ispyb_mapping.py +1 -1
  73. mx_bluesky/hyperion/external_interaction/callbacks/grid_detection_callback.py +30 -25
  74. mx_bluesky/hyperion/external_interaction/callbacks/ispyb_callback_base.py +29 -12
  75. mx_bluesky/hyperion/external_interaction/callbacks/log_uid_tag_callback.py +1 -1
  76. mx_bluesky/hyperion/external_interaction/callbacks/robot_load/ispyb_callback.py +19 -11
  77. mx_bluesky/hyperion/external_interaction/callbacks/rotation/ispyb_callback.py +7 -4
  78. mx_bluesky/hyperion/external_interaction/callbacks/rotation/nexus_callback.py +5 -3
  79. mx_bluesky/hyperion/external_interaction/callbacks/sample_handling/__init__.py +0 -0
  80. mx_bluesky/hyperion/external_interaction/callbacks/sample_handling/sample_handling_callback.py +84 -0
  81. mx_bluesky/hyperion/external_interaction/callbacks/xray_centre/ispyb_callback.py +38 -27
  82. mx_bluesky/hyperion/external_interaction/callbacks/xray_centre/nexus_callback.py +5 -4
  83. mx_bluesky/hyperion/external_interaction/config_server.py +11 -28
  84. mx_bluesky/hyperion/external_interaction/exceptions.py +0 -9
  85. mx_bluesky/hyperion/external_interaction/ispyb/exp_eye_store.py +65 -15
  86. mx_bluesky/hyperion/external_interaction/ispyb/ispyb_store.py +1 -1
  87. mx_bluesky/hyperion/external_interaction/nexus/nexus_utils.py +2 -2
  88. mx_bluesky/hyperion/external_interaction/nexus/write_nexus.py +1 -1
  89. mx_bluesky/hyperion/log.py +0 -84
  90. mx_bluesky/hyperion/parameters/components.py +4 -251
  91. mx_bluesky/hyperion/parameters/constants.py +22 -119
  92. mx_bluesky/hyperion/parameters/gridscan.py +35 -74
  93. mx_bluesky/hyperion/parameters/load_centre_collect.py +16 -11
  94. mx_bluesky/hyperion/parameters/rotation.py +23 -10
  95. mx_bluesky/hyperion/utils/utils.py +17 -0
  96. mx_bluesky/hyperion/utils/validation.py +5 -6
  97. {mx_bluesky-1.2.0.dist-info → mx_bluesky-1.4.1.dist-info}/METADATA +36 -33
  98. {mx_bluesky-1.2.0.dist-info → mx_bluesky-1.4.1.dist-info}/RECORD +102 -89
  99. {mx_bluesky-1.2.0.dist-info → mx_bluesky-1.4.1.dist-info}/WHEEL +1 -1
  100. mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_Chip_Mapping_py3v1.py +0 -161
  101. mx_bluesky/example.py +0 -19
  102. mx_bluesky/hyperion/parameters/robot_load.py +0 -16
  103. {mx_bluesky-1.2.0.dist-info → mx_bluesky-1.4.1.dist-info}/LICENSE +0 -0
  104. {mx_bluesky-1.2.0.dist-info → mx_bluesky-1.4.1.dist-info}/entry_points.txt +0 -0
  105. {mx_bluesky-1.2.0.dist-info → mx_bluesky-1.4.1.dist-info}/top_level.txt +0 -0
@@ -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
- TEST_MODE = os.environ.get("HYPERION_TEST_MODE")
9
-
10
-
11
- @dataclass(frozen=True)
12
- class SimConstants:
13
- BEAMLINE = "BL03S"
14
- INSERTION_PREFIX = "SR03S"
15
- ZOCALO_ENV = "dev_artemis"
16
- # this one is for unit tests
17
- ISPYB_CONFIG = "tests/test_data/test_config.cfg"
18
- # this one is for system tests
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
- @dataclass(frozen=True)
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,21 @@ 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 = _test_oav_file if TEST_MODE else _live_oav_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
- THAWING_TIME = 20
118
- USE_CPU_AND_GPU_ZOCALO = False
30
+ SET_STUB_OFFSETS = False
31
+
32
+ # Turns on GPU processing for zocalo and logs a comparison between GPU and CPU-
33
+ # processed results. GPU results never used in analysis for now
34
+ COMPARE_CPU_AND_GPU_ZOCALO = False
119
35
 
120
36
 
121
37
  @dataclass(frozen=True)
122
38
  class HyperionConstants:
39
+ DESCRIPTORS = DocDescriptorNames()
40
+ TRIGGER = TriggerConstants()
41
+ ZOCALO_ENV = EnvironmentConstants.ZOCALO_ENV
123
42
  HARDWARE = HardwareConstants()
124
43
  I03 = I03Constants()
125
44
  PARAM = ExperimentParamConstants()
@@ -136,23 +55,7 @@ class HyperionConstants:
136
55
  )
137
56
  GRAYLOG_PORT = 12232
138
57
  PARAMETER_SCHEMA_DIRECTORY = "src/hyperion/parameters/schemas/"
139
- ZOCALO_ENV = "dev_artemis" if TEST_MODE else "artemis"
58
+ LOG_FILE_NAME = "hyperion.log"
140
59
 
141
60
 
142
61
  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"
@@ -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
  )
@@ -14,36 +11,22 @@ from pydantic import Field, PrivateAttr
14
11
  from scanspec.core import Path as ScanPath
15
12
  from scanspec.specs import Line, Static
16
13
 
17
- from mx_bluesky.hyperion.parameters.components import (
18
- DiffractionExperimentWithSample,
19
- IspybExperimentType,
20
- OptionalGonioAngleStarts,
14
+ from mx_bluesky.common.parameters.components import (
21
15
  SplitScan,
22
- WithOavCentring,
23
16
  WithOptionalEnergyChange,
24
- WithScan,
25
- XyzStarts,
17
+ WithPandaGridScan,
18
+ )
19
+ from mx_bluesky.common.parameters.gridscan import (
20
+ GridCommon,
21
+ SpecifiedGrid,
26
22
  )
23
+ from mx_bluesky.hyperion.parameters.components import WithHyperionFeatures
27
24
  from mx_bluesky.hyperion.parameters.constants import CONST, I03Constants
28
- from mx_bluesky.hyperion.parameters.robot_load import RobotLoadAndEnergyChange
29
25
 
30
26
 
31
- class GridCommon(
32
- DiffractionExperimentWithSample, OptionalGonioAngleStarts, WithOavCentring
33
- ):
34
- grid_width_um: float = Field(default=CONST.PARAM.GRIDSCAN.WIDTH_UM)
35
- exposure_time_s: float = Field(default=CONST.PARAM.GRIDSCAN.EXPOSURE_TIME_S)
36
- use_roi_mode: bool = Field(default=CONST.PARAM.GRIDSCAN.USE_ROI)
37
- panda_runup_distance_mm: float = Field(
38
- default=CONST.HARDWARE.PANDA_FGS_RUN_UP_DEFAULT
39
- )
40
- use_panda: bool = Field(default=CONST.I03.USE_PANDA_FOR_GRIDSCAN)
41
- use_gpu: bool = Field(default=CONST.I03.USE_GPU_FOR_GRIDSCAN_ANALYSIS)
42
- use_cpu_and_gpu_zocalo: bool = Field(default=CONST.I03.USE_CPU_AND_GPU_ZOCALO)
43
- ispyb_experiment_type: IspybExperimentType = Field(
44
- default=IspybExperimentType.GRIDSCAN_3D
45
- )
46
- 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"""
47
30
 
48
31
  @property
49
32
  def detector_params(self):
@@ -57,7 +40,6 @@ class GridCommon(
57
40
  assert (
58
41
  self.detector_distance_mm is not None
59
42
  ), "Detector distance must be filled before generating DetectorParams"
60
- os.makedirs(self.storage_directory, exist_ok=True)
61
43
  return DetectorParams(
62
44
  detector_size_constants=I03Constants.DETECTOR,
63
45
  expected_energy_ev=self.demand_energy_ev,
@@ -72,40 +54,18 @@ class GridCommon(
72
54
  use_roi_mode=self.use_roi_mode,
73
55
  det_dist_to_beam_converter_path=self.det_dist_to_beam_converter_path,
74
56
  trigger_mode=self.trigger_mode,
75
- enable_dev_shm=self.use_gpu,
57
+ enable_dev_shm=self.features.compare_cpu_and_gpu_zocalo,
76
58
  **optional_args,
77
59
  )
78
60
 
79
61
 
80
- class GridScanWithEdgeDetect(GridCommon): ...
81
-
82
-
83
- class PinTipCentreThenXrayCentre(GridCommon):
84
- tip_offset_um: float = 0
85
-
86
-
87
- class RobotLoadThenCentre(GridCommon):
88
- thawing_time: float = Field(default=CONST.I03.THAWING_TIME)
89
-
90
- def robot_load_params(self):
91
- my_params = self.model_dump()
92
- return RobotLoadAndEnergyChange(**my_params)
93
-
94
- def pin_centre_then_xray_centre_params(self):
95
- my_params = self.model_dump()
96
- del my_params["thawing_time"]
97
- return PinTipCentreThenXrayCentre(**my_params)
98
-
99
-
100
- class SpecifiedGridScan(GridCommon, XyzStarts, WithScan):
101
- """A specified grid scan is one which has defined values for the start position,
102
- grid and box sizes, etc., as opposed to parameters for a plan which will create
103
- those parameters at some point (e.g. through optical pin detection)."""
104
-
105
- ...
106
-
107
-
108
- class ThreeDGridScan(SpecifiedGridScan, SplitScan, WithOptionalEnergyChange):
62
+ class HyperionThreeDGridScan(
63
+ HyperionGridCommon,
64
+ SpecifiedGrid,
65
+ SplitScan,
66
+ WithOptionalEnergyChange,
67
+ WithPandaGridScan,
68
+ ):
109
69
  """Parameters representing a so-called 3D grid scan, which consists of doing a
110
70
  gridscan in X and Y, followed by one in X and Z."""
111
71
 
@@ -127,14 +87,14 @@ class ThreeDGridScan(SpecifiedGridScan, SplitScan, WithOptionalEnergyChange):
127
87
  x_steps=self.x_steps,
128
88
  y_steps=self.y_steps,
129
89
  z_steps=self.z_steps,
130
- x_step_size=self.x_step_size_um,
131
- y_step_size=self.y_step_size_um,
132
- z_step_size=self.z_step_size_um,
133
- x_start=self.x_start_um,
134
- y1_start=self.y_start_um,
135
- z1_start=self.z_start_um,
136
- y2_start=self.y2_start_um,
137
- z2_start=self.z2_start_um,
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,
138
98
  set_stub_offsets=self.features.set_stub_offsets,
139
99
  dwell_time_ms=self.exposure_time_s * 1000,
140
100
  transmission_fraction=self.transmission_frac,
@@ -143,6 +103,7 @@ class ThreeDGridScan(SpecifiedGridScan, SplitScan, WithOptionalEnergyChange):
143
103
  @property
144
104
  def panda_FGS_params(self) -> PandAGridScanParams:
145
105
  if self.y_steps % 2 and self.z_steps > 0:
106
+ # See https://github.com/DiamondLightSource/hyperion/issues/1118 for explanation
146
107
  raise OddYStepsException(
147
108
  "The number of Y steps must be even for a PandA gridscan"
148
109
  )
@@ -150,14 +111,14 @@ class ThreeDGridScan(SpecifiedGridScan, SplitScan, WithOptionalEnergyChange):
150
111
  x_steps=self.x_steps,
151
112
  y_steps=self.y_steps,
152
113
  z_steps=self.z_steps,
153
- x_step_size=self.x_step_size_um,
154
- y_step_size=self.y_step_size_um,
155
- z_step_size=self.z_step_size_um,
156
- x_start=self.x_start_um,
157
- y1_start=self.y_start_um,
158
- z1_start=self.z_start_um,
159
- y2_start=self.y2_start_um,
160
- z2_start=self.z2_start_um,
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,
161
122
  set_stub_offsets=self.features.set_stub_offsets,
162
123
  run_up_distance_mm=self.panda_runup_distance_mm,
163
124
  transmission_fraction=self.transmission_frac,
@@ -2,12 +2,13 @@ from typing import TypeVar
2
2
 
3
3
  from pydantic import BaseModel, model_validator
4
4
 
5
- from mx_bluesky.hyperion.parameters.components import (
6
- HyperionParameters,
5
+ from mx_bluesky.common.parameters.components import (
6
+ MxBlueskyParameters,
7
+ WithCentreSelection,
7
8
  WithSample,
8
9
  WithVisit,
9
10
  )
10
- from mx_bluesky.hyperion.parameters.gridscan import (
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, key: str, t: type[T]) -> T:
19
- values = dict(parent_context)
20
- values |= values[key]
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(HyperionParameters, WithVisit, WithSample):
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(HyperionParameters, WithVisit, WithSample):
41
44
  disallowed_keys == set()
42
45
  ), f"Unexpected fields found in LoadCentreCollect {disallowed_keys}"
43
46
 
44
- values["robot_load_then_centre"] = construct_from_values(
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
- values["multi_rotation_scan"] = construct_from_values(
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
@@ -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.hyperion.parameters.components import (
20
+ from mx_bluesky.common.parameters.components import (
21
21
  DiffractionExperimentWithSample,
22
22
  IspybExperimentType,
23
23
  OptionalGonioAngleStarts,
@@ -47,7 +47,9 @@ class RotationExperiment(DiffractionExperimentWithSample):
47
47
  default=IspybExperimentType.ROTATION
48
48
  )
49
49
 
50
- def _detector_params(self, omega_start_deg: float):
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=self.num_images,
70
- num_triggers=1,
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,12 +120,16 @@ class MultiRotationScan(RotationExperiment, SplitScan):
115
120
 
116
121
  def _single_rotation_scan(self, scan: RotationScanPerSweep) -> RotationScan:
117
122
  # self has everything from RotationExperiment
118
- params = self.model_dump()
119
- del params["rotation_scans"]
123
+ allowed_keys = RotationScan.model_fields.keys() # type: ignore # mypy doesn't recognise this as a property...
124
+ params_dump = self.model_dump()
120
125
  # provided `scan` has everything from RotationScanPerSweep
121
- params.update(scan.model_dump())
126
+ scan_dump = scan.model_dump()
127
+ rotation_scan_kv_pairs = {
128
+ k: v for k, v in (params_dump | scan_dump).items() if k in allowed_keys
129
+ }
122
130
  # together they have everything for RotationScan
123
- return RotationScan(**params)
131
+ rotation_scan = RotationScan(**rotation_scan_kv_pairs)
132
+ return rotation_scan
124
133
 
125
134
  @model_validator(mode="after")
126
135
  @classmethod
@@ -152,8 +161,12 @@ class MultiRotationScan(RotationExperiment, SplitScan):
152
161
  return list(accumulate([0, *self._num_images_per_scan()]))
153
162
 
154
163
  @property
155
- def detector_params(self):
156
- return self._detector_params(self.rotation_scans[0].omega_start_deg)
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
+ )
157
170
 
158
171
  @property
159
172
  def ispyb_params(self): # pyright: ignore
@@ -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 OAVConfigParams
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/devices/unit_tests/test_display.configuration"
27
- ZOOM_LEVELS_XML = "tests/devices/unit_tests/test_jCameraManZoomLevels.xml"
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=OAVConfigParams(
98
- zoom_params_file=ZOOM_LEVELS_XML, display_config=DISPLAY_CONFIGURATION
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,
@@ -1,9 +1,9 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: mx-bluesky
3
- Version: 1.2.0
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: Apache 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: annotated-types
216
+ Requires-Dist: annotated_types
217
217
  Requires-Dist: caproto
218
218
  Requires-Dist: fastapi[all]
219
219
  Requires-Dist: flask-restful
@@ -226,43 +226,46 @@ Requires-Dist: opencv-python
226
226
  Requires-Dist: opentelemetry-distro
227
227
  Requires-Dist: opentelemetry-exporter-otlp
228
228
  Requires-Dist: pydantic
229
+ Requires-Dist: pydantic-extra-types
229
230
  Requires-Dist: pyepics
230
231
  Requires-Dist: pyzmq
231
232
  Requires-Dist: requests
232
233
  Requires-Dist: scanspec
233
234
  Requires-Dist: scipy
234
235
  Requires-Dist: semver
235
- Requires-Dist: blueapi >=0.5.0
236
- Requires-Dist: daq-config-server >=0.1.1
237
- Requires-Dist: ophyd ==1.9.0
238
- Requires-Dist: ophyd-async >=0.3a5
239
- Requires-Dist: bluesky >=1.13.0a4
240
- Requires-Dist: dls-dodal ==1.33.0
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
241
243
  Provides-Extra: dev
242
- Requires-Dist: black ; extra == 'dev'
243
- Requires-Dist: build ; extra == 'dev'
244
- Requires-Dist: diff-cover ; extra == 'dev'
245
- Requires-Dist: GitPython ; extra == 'dev'
246
- Requires-Dist: ipython ; extra == 'dev'
247
- Requires-Dist: mypy ; extra == 'dev'
248
- Requires-Dist: myst-parser ; extra == 'dev'
249
- Requires-Dist: pipdeptree ; extra == 'dev'
250
- Requires-Dist: pre-commit ; extra == 'dev'
251
- Requires-Dist: pydata-sphinx-theme >=0.12 ; extra == 'dev'
252
- Requires-Dist: pyright ; extra == 'dev'
253
- Requires-Dist: pytest-asyncio ; extra == 'dev'
254
- Requires-Dist: pytest-cov ; extra == 'dev'
255
- Requires-Dist: pytest-random-order ; extra == 'dev'
256
- Requires-Dist: pytest ; extra == 'dev'
257
- Requires-Dist: ruff ; extra == 'dev'
258
- Requires-Dist: sphinx-autobuild ; extra == 'dev'
259
- Requires-Dist: sphinx-copybutton ; extra == 'dev'
260
- Requires-Dist: sphinxcontrib-plantuml ; extra == 'dev'
261
- Requires-Dist: sphinx-design ; extra == 'dev'
262
- Requires-Dist: tox-direct ; extra == 'dev'
263
- Requires-Dist: tox ; extra == 'dev'
264
- Requires-Dist: types-mock ; extra == 'dev'
265
- Requires-Dist: types-requests ; extra == 'dev'
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"
266
269
 
267
270
  mx-bluesky
268
271
  ===========================