dls-dodal 1.69.0__py3-none-any.whl → 2.0.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.
Files changed (282) hide show
  1. {dls_dodal-1.69.0.dist-info → dls_dodal-2.0.0.dist-info}/METADATA +1 -1
  2. dls_dodal-2.0.0.dist-info/RECORD +354 -0
  3. {dls_dodal-1.69.0.dist-info → dls_dodal-2.0.0.dist-info}/WHEEL +1 -1
  4. dodal/_version.py +2 -2
  5. dodal/beamlines/__init__.py +10 -17
  6. dodal/beamlines/adsim.py +17 -17
  7. dodal/beamlines/b01_1.py +11 -0
  8. dodal/beamlines/b07.py +17 -21
  9. dodal/beamlines/b07_1.py +20 -22
  10. dodal/beamlines/b07_shared.py +12 -0
  11. dodal/beamlines/b16.py +1 -1
  12. dodal/beamlines/b21.py +15 -6
  13. dodal/beamlines/i02_1.py +3 -3
  14. dodal/beamlines/i02_2.py +1 -1
  15. dodal/beamlines/i03.py +4 -4
  16. dodal/beamlines/i04.py +16 -8
  17. dodal/beamlines/i05.py +7 -45
  18. dodal/beamlines/i05_1.py +4 -13
  19. dodal/beamlines/i05_shared.py +51 -0
  20. dodal/beamlines/i06_1.py +7 -5
  21. dodal/beamlines/{i06.py → i06_shared.py} +25 -14
  22. dodal/beamlines/i07.py +14 -16
  23. dodal/beamlines/i09.py +54 -51
  24. dodal/beamlines/i09_1.py +25 -64
  25. dodal/beamlines/i09_1_shared.py +61 -0
  26. dodal/beamlines/i09_2.py +6 -100
  27. dodal/beamlines/i09_2_shared.py +110 -0
  28. dodal/beamlines/i10.py +60 -54
  29. dodal/beamlines/i10_1.py +99 -10
  30. dodal/beamlines/{i10_optics.py → i10_shared.py} +80 -66
  31. dodal/beamlines/i11.py +31 -18
  32. dodal/beamlines/i13_1.py +1 -1
  33. dodal/beamlines/i15.py +6 -6
  34. dodal/beamlines/i15_1.py +6 -6
  35. dodal/beamlines/i17.py +37 -28
  36. dodal/beamlines/i18.py +3 -4
  37. dodal/beamlines/i19_1.py +95 -34
  38. dodal/beamlines/i19_2.py +68 -52
  39. dodal/beamlines/i19_optics.py +26 -13
  40. dodal/beamlines/i20_1.py +6 -14
  41. dodal/beamlines/i21.py +35 -28
  42. dodal/beamlines/i22.py +19 -4
  43. dodal/beamlines/i23.py +1 -2
  44. dodal/beamlines/i24.py +11 -10
  45. dodal/beamlines/k07.py +99 -5
  46. dodal/beamlines/p38.py +3 -3
  47. dodal/beamlines/p60.py +28 -17
  48. dodal/beamlines/p99.py +16 -15
  49. dodal/beamlines/training_rig.py +20 -12
  50. dodal/cli.py +36 -2
  51. dodal/common/beamlines/beamline_parameters.py +2 -1
  52. dodal/common/beamlines/beamline_utils.py +11 -9
  53. dodal/common/beamlines/commissioning_mode.py +6 -3
  54. dodal/common/coordination.py +12 -14
  55. dodal/common/crystal_metadata.py +5 -8
  56. dodal/common/device_utils.py +4 -3
  57. dodal/common/maths.py +28 -40
  58. dodal/common/udc_directory_provider.py +13 -8
  59. dodal/common/visit.py +18 -21
  60. dodal/common/watcher_utils.py +13 -12
  61. dodal/device_manager.py +94 -54
  62. dodal/devices/aperturescatterguard.py +26 -27
  63. dodal/devices/areadetector/plugins/cam.py +1 -3
  64. dodal/devices/areadetector/plugins/mjpg.py +6 -5
  65. dodal/devices/attenuator/attenuator.py +12 -11
  66. dodal/devices/beamlines/b07/__init__.py +3 -0
  67. dodal/devices/{b07_1 → beamlines/b07_1}/__init__.py +2 -2
  68. dodal/devices/{b07_1 → beamlines/b07_1}/ccmc.py +5 -10
  69. dodal/devices/{b16 → beamlines/b16}/detector.py +2 -3
  70. dodal/devices/{i02_1 → beamlines/i02_1}/fast_grid_scan.py +2 -3
  71. dodal/devices/{i02_1 → beamlines/i02_1}/sample_motors.py +1 -1
  72. dodal/devices/{i03 → beamlines/i03}/beamsize.py +11 -7
  73. dodal/devices/{i03 → beamlines/i03}/dcm.py +1 -2
  74. dodal/devices/{i03 → beamlines/i03}/undulator_dcm.py +4 -5
  75. dodal/devices/beamlines/i04/beam_centre.py +151 -0
  76. dodal/devices/{i04 → beamlines/i04}/beamsize.py +11 -7
  77. dodal/devices/{i04 → beamlines/i04}/murko_results.py +5 -5
  78. dodal/devices/{i04 → beamlines/i04}/transfocator.py +10 -15
  79. dodal/devices/beamlines/i05/__init__.py +3 -0
  80. dodal/devices/beamlines/i06_shared/__init__.py +3 -0
  81. dodal/devices/beamlines/i06_shared/i06_enum.py +7 -0
  82. dodal/devices/{i07 → beamlines/i07}/dcm.py +2 -3
  83. dodal/devices/{i07 → beamlines/i07}/id.py +8 -9
  84. dodal/devices/beamlines/i09/__init__.py +3 -0
  85. dodal/devices/{i09_1_shared → beamlines/i09_1_shared}/hard_energy.py +5 -6
  86. dodal/devices/{i09_1_shared → beamlines/i09_1_shared}/hard_undulator_functions.py +19 -16
  87. dodal/devices/{i10 → beamlines/i10}/diagnostics.py +4 -3
  88. dodal/devices/{i10 → beamlines/i10}/i10_apple2.py +31 -45
  89. dodal/devices/{i10 → beamlines/i10}/rasor/rasor_current_amp.py +1 -24
  90. dodal/devices/{i10 → beamlines/i10}/rasor/rasor_motors.py +2 -2
  91. dodal/devices/{i10 → beamlines/i10}/slits.py +5 -3
  92. dodal/devices/beamlines/i10_1/__init__.py +9 -0
  93. dodal/devices/beamlines/i10_1/electromagnet/magnet.py +16 -0
  94. dodal/devices/beamlines/i10_1/electromagnet/stages.py +14 -0
  95. dodal/devices/beamlines/i10_1/scaler_cards.py +13 -0
  96. dodal/devices/{i11 → beamlines/i11}/cyberstar_blower.py +1 -1
  97. dodal/devices/{i11 → beamlines/i11}/diff_stages.py +4 -6
  98. dodal/devices/{i11 → beamlines/i11}/mythen.py +3 -4
  99. dodal/devices/{i11 → beamlines/i11}/nx100robot.py +6 -6
  100. dodal/devices/{i11 → beamlines/i11}/spinner.py +1 -1
  101. dodal/devices/{i13_1 → beamlines/i13_1}/merlin.py +1 -1
  102. dodal/devices/{i15 → beamlines/i15}/dcm.py +1 -2
  103. dodal/devices/{i15 → beamlines/i15}/focussing_mirror.py +5 -5
  104. dodal/devices/{i15 → beamlines/i15}/jack.py +2 -2
  105. dodal/devices/{i15 → beamlines/i15}/multilayer_mirror.py +1 -1
  106. dodal/devices/{i17 → beamlines/i17}/i17_apple2.py +10 -16
  107. dodal/devices/{i18 → beamlines/i18}/diode.py +1 -1
  108. dodal/devices/{i19 → beamlines/i19}/access_controlled/attenuator_motor_squad.py +12 -8
  109. dodal/devices/{i19 → beamlines/i19}/access_controlled/blueapi_device.py +16 -15
  110. dodal/devices/beamlines/i19/access_controlled/piezo_control.py +72 -0
  111. dodal/devices/{i19 → beamlines/i19}/access_controlled/shutter.py +11 -9
  112. dodal/devices/{i19 → beamlines/i19}/backlight.py +3 -1
  113. dodal/devices/{i19 → beamlines/i19}/mapt_configuration.py +2 -1
  114. dodal/devices/{i19 → beamlines/i19}/pin_col_stages.py +11 -8
  115. dodal/devices/beamlines/i19/pin_tip.py +32 -0
  116. dodal/devices/beamlines/i21/__init__.py +3 -0
  117. dodal/devices/{i22 → beamlines/i22}/dcm.py +1 -2
  118. dodal/devices/{i22 → beamlines/i22}/fswitch.py +1 -3
  119. dodal/devices/{i22 → beamlines/i22}/nxsas.py +5 -4
  120. dodal/devices/{i24 → beamlines/i24}/beam_center.py +1 -1
  121. dodal/devices/{i24 → beamlines/i24}/beamstop.py +2 -2
  122. dodal/devices/{i24 → beamlines/i24}/commissioning_jungfrau.py +3 -2
  123. dodal/devices/{i24 → beamlines/i24}/dcm.py +1 -3
  124. dodal/devices/{i24 → beamlines/i24}/dual_backlight.py +3 -3
  125. dodal/devices/{i24 → beamlines/i24}/pmac.py +9 -7
  126. dodal/devices/{p60 → beamlines/p60}/lab_xray_source.py +1 -1
  127. dodal/devices/beamlines/p99/__init__.py +0 -0
  128. dodal/devices/{p99 → beamlines/p99}/andor2_point.py +11 -15
  129. dodal/devices/bimorph_mirror.py +22 -20
  130. dodal/devices/collimation_table.py +3 -2
  131. dodal/devices/common_dcm.py +30 -20
  132. dodal/devices/controllers.py +2 -2
  133. dodal/devices/cryostream.py +8 -0
  134. dodal/devices/current_amplifiers/current_amplifier.py +16 -18
  135. dodal/devices/current_amplifiers/current_amplifier_detector.py +9 -10
  136. dodal/devices/current_amplifiers/femto.py +8 -9
  137. dodal/devices/current_amplifiers/sr570.py +16 -16
  138. dodal/devices/current_amplifiers/struck_scaler_counter.py +5 -5
  139. dodal/devices/detector/det_resolution.py +9 -8
  140. dodal/devices/detector/detector.py +4 -2
  141. dodal/devices/diamond_filter.py +3 -4
  142. dodal/devices/eiger.py +3 -3
  143. dodal/devices/eiger_odin.py +1 -1
  144. dodal/devices/electron_analyser/base/base_controller.py +13 -13
  145. dodal/devices/electron_analyser/base/base_detector.py +15 -20
  146. dodal/devices/electron_analyser/base/base_driver_io.py +39 -46
  147. dodal/devices/electron_analyser/base/base_region.py +27 -30
  148. dodal/devices/electron_analyser/base/base_util.py +18 -16
  149. dodal/devices/electron_analyser/base/energy_sources.py +13 -19
  150. dodal/devices/eurotherm.py +3 -2
  151. dodal/devices/fast_grid_scan.py +31 -34
  152. dodal/devices/fast_shutter.py +24 -21
  153. dodal/devices/flux.py +1 -1
  154. dodal/devices/focusing_mirror.py +29 -11
  155. dodal/devices/hutch_shutter.py +6 -6
  156. dodal/devices/insertion_device/__init__.py +8 -0
  157. dodal/devices/insertion_device/apple2_controller.py +51 -60
  158. dodal/devices/insertion_device/apple2_undulator.py +40 -64
  159. dodal/devices/insertion_device/apple_knot_controller.py +222 -0
  160. dodal/devices/insertion_device/energy.py +100 -27
  161. dodal/devices/insertion_device/energy_motor_lookup.py +20 -27
  162. dodal/devices/insertion_device/lookup_table_models.py +45 -50
  163. dodal/devices/insertion_device/polarisation.py +1 -1
  164. dodal/devices/ipin.py +1 -1
  165. dodal/devices/linkam3.py +7 -5
  166. dodal/devices/motors.py +107 -19
  167. dodal/devices/mx_phase1/beamstop.py +2 -4
  168. dodal/devices/oav/oav_calculations.py +20 -13
  169. dodal/devices/oav/oav_detector.py +28 -23
  170. dodal/devices/oav/oav_parameters.py +4 -9
  171. dodal/devices/oav/oav_to_redis_forwarder.py +22 -18
  172. dodal/devices/oav/pin_image_recognition/__init__.py +4 -6
  173. dodal/devices/oav/pin_image_recognition/manual_test.py +1 -2
  174. dodal/devices/oav/pin_image_recognition/utils.py +30 -32
  175. dodal/devices/oav/snapshots/grid_overlay.py +10 -9
  176. dodal/devices/oav/snapshots/snapshot_image_processing.py +15 -13
  177. dodal/devices/oav/utils.py +9 -12
  178. dodal/devices/p45.py +3 -9
  179. dodal/devices/pgm.py +8 -14
  180. dodal/devices/pressure_jump_cell.py +93 -32
  181. dodal/devices/qbpm.py +1 -3
  182. dodal/devices/robot.py +12 -4
  183. dodal/devices/s4_slit_gaps.py +1 -1
  184. dodal/devices/selectable_source.py +5 -2
  185. dodal/devices/slits.py +2 -5
  186. dodal/devices/smargon.py +2 -3
  187. dodal/devices/temperture_controller/lakeshore/lakeshore.py +38 -64
  188. dodal/devices/temperture_controller/lakeshore/lakeshore_io.py +21 -35
  189. dodal/devices/tetramm.py +7 -7
  190. dodal/devices/turbo_slit.py +8 -7
  191. dodal/devices/undulator.py +42 -56
  192. dodal/devices/util/adjuster_plans.py +2 -3
  193. dodal/devices/util/epics_util.py +10 -10
  194. dodal/devices/util/lookup_tables.py +17 -18
  195. dodal/devices/v2f.py +2 -3
  196. dodal/devices/watsonmarlow323_pump.py +1 -1
  197. dodal/devices/xbpm_feedback.py +3 -2
  198. dodal/devices/xspress3/xspress3.py +8 -11
  199. dodal/devices/xspress3/xspress3_channel.py +3 -6
  200. dodal/devices/zebra/zebra.py +6 -7
  201. dodal/devices/zebra/zebra_constants_mapping.py +11 -7
  202. dodal/devices/zebra/zebra_controlled_shutter.py +2 -1
  203. dodal/devices/zocalo/zocalo_interaction.py +14 -14
  204. dodal/devices/zocalo/zocalo_results.py +33 -33
  205. dodal/log.py +23 -20
  206. dodal/plan_stubs/check_topup.py +15 -15
  207. dodal/plan_stubs/data_session.py +6 -6
  208. dodal/plan_stubs/motor_utils.py +22 -18
  209. dodal/plan_stubs/pressure_jump_cell.py +18 -0
  210. dodal/plan_stubs/wrapped.py +40 -55
  211. dodal/plans/bimorph.py +63 -52
  212. dodal/plans/device_setup_plans/__init__.py +5 -0
  213. dodal/plans/device_setup_plans/setup_pin_tip_params.py +63 -0
  214. dodal/plans/preprocessors/verify_undulator_gap.py +10 -8
  215. dodal/plans/spec_path.py +3 -5
  216. dodal/plans/verify_undulator_gap.py +1 -2
  217. dodal/plans/wrapped.py +4 -3
  218. dodal/testing/electron_analyser/device_factory.py +5 -7
  219. dodal/testing/fixtures/devices/apple2.py +38 -0
  220. dodal/testing/fixtures/run_engine.py +3 -7
  221. dodal/testing/fixtures/utils.py +1 -2
  222. dodal/utils.py +60 -58
  223. dls_dodal-1.69.0.dist-info/RECORD +0 -338
  224. dodal/beamline_specific_utils/i05_shared.py +0 -14
  225. dodal/devices/b07/__init__.py +0 -3
  226. dodal/devices/i04/beam_centre.py +0 -84
  227. dodal/devices/i05/__init__.py +0 -3
  228. dodal/devices/i09/__init__.py +0 -3
  229. dodal/devices/i21/__init__.py +0 -5
  230. {dls_dodal-1.69.0.dist-info → dls_dodal-2.0.0.dist-info}/entry_points.txt +0 -0
  231. {dls_dodal-1.69.0.dist-info → dls_dodal-2.0.0.dist-info}/licenses/LICENSE +0 -0
  232. {dls_dodal-1.69.0.dist-info → dls_dodal-2.0.0.dist-info}/top_level.txt +0 -0
  233. /dodal/{beamline_specific_utils → devices/beamlines}/__init__.py +0 -0
  234. /dodal/devices/{b07 → beamlines/b07}/enums.py +0 -0
  235. /dodal/devices/{b07_1 → beamlines/b07_1}/enums.py +0 -0
  236. /dodal/devices/{b16 → beamlines/b16}/__init__.py +0 -0
  237. /dodal/devices/{i02_1 → beamlines/i02_1}/__init__.py +0 -0
  238. /dodal/devices/{i02_2 → beamlines/i02_2}/__init__.py +0 -0
  239. /dodal/devices/{i03 → beamlines/i03}/__init__.py +0 -0
  240. /dodal/devices/{i03 → beamlines/i03}/constants.py +0 -0
  241. /dodal/devices/{i04 → beamlines/i04}/__init__.py +0 -0
  242. /dodal/devices/{i04 → beamlines/i04}/constants.py +0 -0
  243. /dodal/devices/{i04 → beamlines/i04}/max_pixel.py +0 -0
  244. /dodal/devices/{i05 → beamlines/i05}/enums.py +0 -0
  245. /dodal/devices/{i07 → beamlines/i07}/__init__.py +0 -0
  246. /dodal/devices/{i09 → beamlines/i09}/enums.py +0 -0
  247. /dodal/devices/{i09_1 → beamlines/i09_1}/__init__.py +0 -0
  248. /dodal/devices/{i09_1 → beamlines/i09_1}/enums.py +0 -0
  249. /dodal/devices/{i09_1_shared → beamlines/i09_1_shared}/__init__.py +0 -0
  250. /dodal/devices/{i09_2_shared → beamlines/i09_2_shared}/__init__.py +0 -0
  251. /dodal/devices/{i09_2_shared → beamlines/i09_2_shared}/i09_apple2.py +0 -0
  252. /dodal/devices/{i10 → beamlines/i10}/__init__.py +0 -0
  253. /dodal/devices/{i10 → beamlines/i10}/i10_setting_data.py +0 -0
  254. /dodal/devices/{i10 → beamlines/i10}/mirrors.py +0 -0
  255. /dodal/devices/{i10 → beamlines/i10}/rasor/__init__.py +0 -0
  256. /dodal/devices/{i10 → beamlines/i10}/rasor/rasor_scaler_cards.py +0 -0
  257. /dodal/devices/{i11 → beamlines/i10_1/electromagnet}/__init__.py +0 -0
  258. /dodal/devices/{i13_1 → beamlines/i11}/__init__.py +0 -0
  259. /dodal/devices/{i15 → beamlines/i13_1}/__init__.py +0 -0
  260. /dodal/devices/{i13_1 → beamlines/i13_1}/merlin_controller.py +0 -0
  261. /dodal/devices/{i17 → beamlines/i15}/__init__.py +0 -0
  262. /dodal/devices/{i15 → beamlines/i15}/laue.py +0 -0
  263. /dodal/devices/{i15 → beamlines/i15}/motors.py +0 -0
  264. /dodal/devices/{i15 → beamlines/i15}/rail.py +0 -0
  265. /dodal/devices/{i18 → beamlines/i17}/__init__.py +0 -0
  266. /dodal/devices/{i19 → beamlines/i18}/__init__.py +0 -0
  267. /dodal/devices/{i18 → beamlines/i18}/kb_mirror.py +0 -0
  268. /dodal/devices/{i19/access_controlled → beamlines/i19}/__init__.py +0 -0
  269. /dodal/devices/{i20_1 → beamlines/i19/access_controlled}/__init__.py +0 -0
  270. /dodal/devices/{i19 → beamlines/i19}/access_controlled/hutch_access.py +0 -0
  271. /dodal/devices/{i19 → beamlines/i19}/beamstop.py +0 -0
  272. /dodal/devices/{i19 → beamlines/i19}/diffractometer.py +0 -0
  273. /dodal/devices/{i22 → beamlines/i20_1}/__init__.py +0 -0
  274. /dodal/devices/{i21 → beamlines/i21}/enums.py +0 -0
  275. /dodal/devices/{i24 → beamlines/i22}/__init__.py +0 -0
  276. /dodal/devices/{p99 → beamlines/i24}/__init__.py +0 -0
  277. /dodal/devices/{i24 → beamlines/i24}/aperture.py +0 -0
  278. /dodal/devices/{i24 → beamlines/i24}/focus_mirrors.py +0 -0
  279. /dodal/devices/{i24 → beamlines/i24}/vgonio.py +0 -0
  280. /dodal/devices/{p60 → beamlines/p60}/__init__.py +0 -0
  281. /dodal/devices/{p60 → beamlines/p60}/enums.py +0 -0
  282. /dodal/devices/{p99 → beamlines/p99}/sample_stage.py +0 -0
@@ -25,7 +25,8 @@ class MJPG(StandardReadable, Triggerable, ABC):
25
25
  """The MJPG areadetector plugin creates an MJPG video stream of the camera's output.
26
26
 
27
27
  This devices uses that stream to grab images. When it is triggered it will send the
28
- latest image from the stream to the `post_processing` method for child classes to handle.
28
+ latest image from the stream to the `post_processing` method for child classes to
29
+ handle.
29
30
  """
30
31
 
31
32
  def __init__(
@@ -51,9 +52,9 @@ class MJPG(StandardReadable, Triggerable, ABC):
51
52
  super().__init__(name)
52
53
 
53
54
  async def _save_image(self, image: Image.Image):
54
- """A helper function to save a given image to the path supplied by the \
55
- directory and filename signals. The full resultant path is put on the \
56
- last_saved_path signal
55
+ """A helper function to save a given image to the path supplied by the
56
+ directory and filename signals. The full resultant path is put on the
57
+ last_saved_path signal.
57
58
  """
58
59
  filename_str = await self.filename.get_value()
59
60
  directory_str = await self.directory.get_value()
@@ -74,7 +75,7 @@ class MJPG(StandardReadable, Triggerable, ABC):
74
75
  """This takes a snapshot image from the MJPG stream and send it to the
75
76
  post_processing method, expected to be implemented by a child of this class.
76
77
 
77
- It is the responsibility of the child class to save any resulting images by \
78
+ It is the responsibility of the child class to save any resulting images by
78
79
  calling _save_image.
79
80
  """
80
81
  url_str = await self.url.get_value()
@@ -37,13 +37,15 @@ class ReadOnlyAttenuator(StandardReadable):
37
37
 
38
38
  class BinaryFilterAttenuator(ReadOnlyAttenuator, Movable[float]):
39
39
  """The attenuator will insert filters into the beam to reduce its transmission.
40
- In this attenuator, each filter can be in one of two states: IN or OUT
40
+ In this attenuator, each filter can be in one of two states: IN or OUT.
41
41
 
42
42
  This device should be set with:
43
+
43
44
  yield from bps.set(attenuator, desired_transmission)
44
45
 
45
46
  Where desired_transmission is fraction e.g. 0-1. When the actual_transmission is
46
- read from the device it is also fractional"""
47
+ read from the device it is also fractional
48
+ """
47
49
 
48
50
  def __init__(self, prefix: str, num_filters: int, name: str = ""):
49
51
  self._calculated_filter_states: DeviceVector[SignalR[int]] = DeviceVector(
@@ -72,11 +74,10 @@ class BinaryFilterAttenuator(ReadOnlyAttenuator, Movable[float]):
72
74
  async def set(self, value: float):
73
75
  """Set the transmission to the fractional (0-1) value given.
74
76
 
75
- The attenuator IOC will then insert filters to reach the desired transmission for
76
- the current beamline energy, the set will only complete when they have all been
77
- applied.
77
+ The attenuator IOC will then insert filters to reach the desired transmission
78
+ for the current beamline energy, the set will only complete when they have all
79
+ been applied.
78
80
  """
79
-
80
81
  LOGGER.debug("Using current energy ")
81
82
  await self._use_current_energy.trigger()
82
83
  LOGGER.info(f"Setting desired transmission to {value}")
@@ -110,7 +111,8 @@ ENUM_ATTENUATOR_SETTLE_TIME_S = 0.15
110
111
  class EnumFilterAttenuator(ReadOnlyAttenuator, Movable[float]):
111
112
  """The attenuator will insert filters into the beam to reduce its transmission.
112
113
 
113
- This device is currently working, but feature incomplete. See https://github.com/DiamondLightSource/dodal/issues/972
114
+ This device is currently working, but feature incomplete. See
115
+ https://github.com/DiamondLightSource/dodal/issues/972
114
116
 
115
117
  In this attenuator, the state of a filter corresponds to the selected material,
116
118
  e.g Ag50, in contrast to being either 'IN' or 'OUT'; see BinaryFilterAttenuator.
@@ -141,11 +143,10 @@ class EnumFilterAttenuator(ReadOnlyAttenuator, Movable[float]):
141
143
  async def set(self, value: float):
142
144
  """Set the transmission to the fractional (0-1) value given.
143
145
 
144
- The attenuator IOC will then insert filters to reach the desired transmission for
145
- the current beamline energy, the set will only complete when they have all been
146
- applied.
146
+ The attenuator IOC will then insert filters to reach the desired transmission
147
+ for the current beamline energy, the set will only complete when they have all
148
+ been applied.
147
149
  """
148
-
149
150
  # auto move should normally be on, but check here incase it was manually turned off
150
151
  await self._auto_move_on_desired_transmission_set.set(YesNo.YES)
151
152
 
@@ -0,0 +1,3 @@
1
+ from dodal.devices.beamlines.b07.enums import Grating, LensMode, PsuMode
2
+
3
+ __all__ = ["Grating", "LensMode", "PsuMode"]
@@ -1,8 +1,8 @@
1
- from dodal.devices.b07_1.ccmc import (
1
+ from dodal.devices.beamlines.b07_1.ccmc import (
2
2
  ChannelCutMonochromator,
3
3
  ChannelCutMonochromatorPositions,
4
4
  )
5
- from dodal.devices.b07_1.enums import Grating, LensMode
5
+ from dodal.devices.beamlines.b07_1.enums import Grating, LensMode
6
6
 
7
7
  __all__ = [
8
8
  "Grating",
@@ -25,13 +25,16 @@ error_message = "Can not get energy value in eV from ccmc position: "
25
25
  class ChannelCutMonochromator(
26
26
  StandardReadable, Movable[ChannelCutMonochromatorPositions]
27
27
  ):
28
- """
29
- Device to move the channel cut monochromator (ccmc). CCMC has three
28
+ """Device to move the channel cut monochromator (ccmc). CCMC has three
30
29
  choices of crystal (Xtal for short). Setting energy is by means of a
31
30
  multi-positioner. The positions are named after the nominal energies of the
32
31
  crystals. To change energy select one of the crystals from the list.
33
32
  This causes the Y motor to move that crystal into the beam and other
34
33
  motors have to align the angles correctly.
34
+
35
+ Args:
36
+ prefix (str): Beamline specific part of the PV.
37
+ name (str, optional): Name of the device.
35
38
  """
36
39
 
37
40
  def __init__(
@@ -39,14 +42,6 @@ class ChannelCutMonochromator(
39
42
  prefix: str,
40
43
  name: str = "",
41
44
  ) -> None:
42
- """
43
- Parameters
44
- ----------
45
- prefix:
46
- Beamline specific part of the PV
47
- name:
48
- Name of the device
49
- """
50
45
  with self.add_children_as_readables():
51
46
  # crystal motors
52
47
  self._xyz = XYZStage(prefix)
@@ -10,10 +10,9 @@ from dodal.devices.controllers import ConstantDeadTimeController
10
10
 
11
11
 
12
12
  def software_triggered_tiff_area_detector(prefix: str, deadtime: float = 0.0):
13
- """
14
- Wrapper for AreaDetector with fixed dead time (defaulted to 0)
13
+ """Wrapper for AreaDetector with fixed dead time (defaulted to 0)
15
14
  and a TIFF file writer.
16
- Most detectors in B16 could be configured like this
15
+ Most detectors in B16 could be configured like this.
17
16
  """
18
17
  return AreaDetector(
19
18
  writer=ADTIFFWriter.with_io(
@@ -11,8 +11,7 @@ from dodal.log import LOGGER
11
11
 
12
12
 
13
13
  class ZebraGridScanParamsTwoD(GridScanParamsCommon, WithDwellTime):
14
- """
15
- Params for 2D Zebra FGS. Adds on the dwell time, which is really the time
14
+ """Params for 2D Zebra FGS. Adds on the dwell time, which is really the time
16
15
  between trigger positions.
17
16
  """
18
17
 
@@ -23,7 +22,7 @@ class ZebraFastGridScanTwoD(FastGridScanCommon[ZebraGridScanParamsTwoD]):
23
22
  - No Z steps, Z step sizes, or Y2 start positions, or Z2 start
24
23
  - No scan valid PV - see https://github.com/DiamondLightSource/mx-bluesky/issues/1203
25
24
  - No program_number - see https://github.com/DiamondLightSource/mx-bluesky/issues/1203
26
- """
25
+ """ # noqa D415
27
26
 
28
27
  def __init__(
29
28
  self, prefix: str, motion_controller_prefix: str, name: str = ""
@@ -3,7 +3,7 @@ from ophyd_async.epics.motor import Motor
3
3
 
4
4
 
5
5
  class SampleMotors(StandardReadable):
6
- """Virtual Smaract motors on i02-1 (VMXm)"""
6
+ """Virtual Smaract motors on i02-1 (VMXm)."""
7
7
 
8
8
  def __init__(
9
9
  self,
@@ -1,11 +1,15 @@
1
1
  from ophyd_async.core import Reference, derived_signal_r
2
2
 
3
3
  from dodal.devices.aperturescatterguard import ApertureScatterguard
4
+ from dodal.devices.beamlines.i03.constants import BeamsizeConstants
4
5
  from dodal.devices.beamsize.beamsize import BeamsizeBase
5
- from dodal.devices.i03.constants import BeamsizeConstants
6
6
 
7
7
 
8
8
  class Beamsize(BeamsizeBase):
9
+ """Device that calculates the size of the beam by taking the minimum of the beam
10
+ dimensions and the aperture scatterguard diameter.
11
+ """
12
+
9
13
  def __init__(self, aperture_scatterguard: ApertureScatterguard, name=""):
10
14
  super().__init__(name=name)
11
15
  self._aperture_scatterguard_ref = Reference(aperture_scatterguard)
@@ -13,23 +17,23 @@ class Beamsize(BeamsizeBase):
13
17
  with self.add_children_as_readables():
14
18
  self.x_um = derived_signal_r(
15
19
  self._get_beamsize_x,
16
- aperture_radius=self._aperture_scatterguard_ref().radius,
20
+ aperture_diameter=self._aperture_scatterguard_ref().diameter,
17
21
  derived_units="µm",
18
22
  )
19
23
  self.y_um = derived_signal_r(
20
24
  self._get_beamsize_y,
21
- aperture_radius=self._aperture_scatterguard_ref().radius,
25
+ aperture_diameter=self._aperture_scatterguard_ref().diameter,
22
26
  derived_units="µm",
23
27
  )
24
28
 
25
29
  def _get_beamsize_x(
26
30
  self,
27
- aperture_radius: float,
31
+ aperture_diameter: float,
28
32
  ) -> float:
29
- return min(aperture_radius, BeamsizeConstants.BEAM_WIDTH_UM)
33
+ return min(aperture_diameter, BeamsizeConstants.BEAM_WIDTH_UM)
30
34
 
31
35
  def _get_beamsize_y(
32
36
  self,
33
- aperture_radius: float,
37
+ aperture_diameter: float,
34
38
  ) -> float:
35
- return min(aperture_radius, BeamsizeConstants.BEAM_HEIGHT_UM)
39
+ return min(aperture_diameter, BeamsizeConstants.BEAM_HEIGHT_UM)
@@ -18,8 +18,7 @@ from dodal.devices.common_dcm import (
18
18
  class DCM(
19
19
  DoubleCrystalMonochromatorWithDSpacing[PitchAndRollCrystal, StationaryCrystal]
20
20
  ):
21
- """
22
- A double crystal monochromator (DCM), used to select the energy of the beam.
21
+ """A double crystal monochromator (DCM), used to select the energy of the beam.
23
22
 
24
23
  perp describes the gap between the 2 DCM crystals which has to change as you alter
25
24
  the angle to select the requested energy.
@@ -4,7 +4,7 @@ from bluesky.protocols import Movable
4
4
  from ophyd_async.core import AsyncStatus, Reference, StandardReadable
5
5
 
6
6
  from dodal.common.beamlines.beamline_parameters import get_beamline_parameters
7
- from dodal.devices.i03.dcm import DCM
7
+ from dodal.devices.beamlines.i03.dcm import DCM
8
8
  from dodal.devices.undulator import UndulatorInKeV
9
9
  from dodal.log import LOGGER
10
10
 
@@ -12,9 +12,8 @@ ENERGY_TIMEOUT_S: float = 30.0
12
12
 
13
13
 
14
14
  class UndulatorDCM(StandardReadable, Movable[float]):
15
- """
16
- Composite device to handle changing beamline energies, wraps the Undulator and the
17
- DCM. The DCM has a motor which controls the beam energy, when it moves, the
15
+ """Composite device to handle changing beamline energies, wraps the Undulator and
16
+ the DCM. The DCM has a motor which controls the beam energy, when it moves, the
18
17
  Undulator gap may also have to change to enable emission at the new energy.
19
18
  The relationship between the two motor motor positions is provided via a lookup
20
19
  table.
@@ -24,7 +23,7 @@ class UndulatorDCM(StandardReadable, Movable[float]):
24
23
  a comprehensive way to set beam energy.
25
24
 
26
25
  This class will be removed in the future. Use the separate Undulator and DCM devices
27
- instead. See https://github.com/DiamondLightSource/dodal/issues/1092
26
+ instead. See https://github.com/DiamondLightSource/dodal/issues/1092.
28
27
  """
29
28
 
30
29
  DCM_PERP_TOLERANCE = 0.01
@@ -0,0 +1,151 @@
1
+ import math
2
+
3
+ import cv2
4
+ import numpy as np
5
+ from bluesky.protocols import Triggerable
6
+ from ophyd_async.core import (
7
+ AsyncStatus,
8
+ StandardReadable,
9
+ soft_signal_r_and_setter,
10
+ soft_signal_rw,
11
+ )
12
+ from ophyd_async.epics.core import (
13
+ epics_signal_r,
14
+ )
15
+
16
+ from dodal.devices.oav.utils import convert_to_gray_and_blur
17
+ from dodal.log import LOGGER
18
+
19
+ # Constant was chosen from trial and error with test images
20
+ ADDITIONAL_BINARY_THRESH = 20
21
+
22
+
23
+ def convert_image_to_binary(image: np.ndarray):
24
+ """Creates a binary image from OAV image array data.
25
+
26
+ Pixels of the input image are converted to one of two values (a high and a low value).
27
+ Otsu's method is used for automatic thresholding.
28
+ See https://docs.opencv.org/4.x/d7/d4d/tutorial_py_thresholding.html.
29
+ The threshold is increased by ADDITIONAL_BINARY_THRESH in order to get more of
30
+ the centre of the beam.
31
+ """
32
+ max_pixel_value = 255
33
+
34
+ blurred_image = convert_to_gray_and_blur(image)
35
+
36
+ threshold_value, _ = cv2.threshold(
37
+ blurred_image, 0, max_pixel_value, cv2.THRESH_BINARY + cv2.THRESH_OTSU
38
+ )
39
+
40
+ # Adjusting because the inner beam is less noisy compared to the outer
41
+ threshold_value += ADDITIONAL_BINARY_THRESH
42
+
43
+ _, thresholded_image = cv2.threshold(
44
+ blurred_image, threshold_value, max_pixel_value, cv2.THRESH_BINARY
45
+ )
46
+
47
+ LOGGER.info(f"Image binarised with threshold of {threshold_value}")
48
+ return thresholded_image
49
+
50
+
51
+ def round_half_up(x):
52
+ return int(math.floor(x + 0.5))
53
+
54
+
55
+ def get_roi(
56
+ image_arr: np.ndarray,
57
+ centre_x: int,
58
+ centre_y: int,
59
+ box_width: int = 200,
60
+ box_height: int = 200,
61
+ ) -> tuple[np.ndarray, tuple[int, int], tuple[int, int]]:
62
+ """Creates an ROI image array from a full screen image array, given a centre for the
63
+ ROI image and a width and height of the ROI box. Note that if the centre of the ROI
64
+ box is close to the edge of the full screen image, the box may be smaller than the
65
+ width and height provided, as the ROI will be trimmed to fit inside the full screen
66
+ image.
67
+
68
+ Args:
69
+ image_arr (np.ndarray): The full screen image array.
70
+ centre_x (int): The x coordinate of the centre of the ROI box.
71
+ centre_y (int): The y coordinate of the centre of the ROI box.
72
+ box_width (int, optional): The width of the ROI box. Defaults to 200.
73
+ box_height (int, optional): The height of the ROI box. Defaults to 200.
74
+
75
+ Returns:
76
+ tuple[np.ndarray, tuple[int, int], tuple[int, int]]: The ROI array, and (x, y)
77
+ coordinates of the top left and bottom right corners of the ROI box.
78
+ """
79
+ height, width = image_arr.shape[:2]
80
+ x_dist = (box_width) / 2
81
+ y_dist = (box_height) / 2
82
+
83
+ # Clip coordinates to stay within bounds
84
+ x_min = max(round_half_up(centre_x - x_dist), 0)
85
+ x_max = min(round_half_up(centre_x + x_dist), width) - 1
86
+ y_min = max(round_half_up(centre_y - y_dist), 0)
87
+ y_max = min(round_half_up(centre_y + y_dist), height) - 1
88
+
89
+ roi_arr = image_arr[y_min : y_max + 1, x_min : x_max + 1]
90
+
91
+ return roi_arr, (x_min, y_min), (x_max, y_max)
92
+
93
+
94
+ class CentreEllipseMethod(StandardReadable, Triggerable):
95
+ """Upon triggering, fits an ellipse to a binary image from the area detector defined
96
+ by the prefix.
97
+
98
+ This is used, in conjunction with a scintillator, to determine the centre of the
99
+ beam on the image.
100
+ """
101
+
102
+ def __init__(self, prefix: str, overlay_channel: int = 1, name: str = ""):
103
+ self.oav_array_signal = epics_signal_r(np.ndarray, f"pva://{prefix}PVA:ARRAY")
104
+
105
+ self.center_x_val, self._center_x_val_setter = soft_signal_r_and_setter(float)
106
+ self.center_y_val, self._center_y_val_setter = soft_signal_r_and_setter(float)
107
+
108
+ self.current_centre_x = epics_signal_r(
109
+ int, f"{prefix}OVER:{overlay_channel}:CenterX"
110
+ )
111
+ self.current_centre_y = epics_signal_r(
112
+ int, f"{prefix}OVER:{overlay_channel}:CenterY"
113
+ )
114
+
115
+ self.roi_box_size = soft_signal_rw(int, 300)
116
+
117
+ super().__init__(name)
118
+
119
+ def _fit_ellipse(self, binary_img: cv2.typing.MatLike) -> cv2.typing.RotatedRect:
120
+ contours, _ = cv2.findContours(
121
+ binary_img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE
122
+ )
123
+ if not contours:
124
+ raise ValueError("No contours found in image.")
125
+
126
+ largest_contour = max(contours, key=cv2.contourArea)
127
+ if len(largest_contour) < 5:
128
+ raise ValueError(
129
+ f"Not enough points to fit an ellipse. Found {largest_contour} points and need at least 5."
130
+ )
131
+
132
+ return cv2.fitEllipse(largest_contour)
133
+
134
+ @AsyncStatus.wrap
135
+ async def trigger(self):
136
+ array_data = await self.oav_array_signal.get_value()
137
+ current_x = await self.current_centre_x.get_value()
138
+ current_y = await self.current_centre_y.get_value()
139
+ roi_box_size = await self.roi_box_size.get_value()
140
+
141
+ roi_data, top_left_corner, _ = get_roi(
142
+ array_data, current_x, current_y, roi_box_size, roi_box_size
143
+ )
144
+
145
+ roi_binary = convert_image_to_binary(roi_data)
146
+ ellipse_fit = self._fit_ellipse(roi_binary)
147
+ roi_centre_x = ellipse_fit[0][0]
148
+ roi_centre_y = ellipse_fit[0][1]
149
+ # convert back to full screen image coords and set beam centre
150
+ self._center_x_val_setter(roi_centre_x + top_left_corner[0])
151
+ self._center_y_val_setter(roi_centre_y + top_left_corner[1])
@@ -1,11 +1,15 @@
1
1
  from ophyd_async.core import Reference, derived_signal_r
2
2
 
3
3
  from dodal.devices.aperturescatterguard import ApertureScatterguard
4
+ from dodal.devices.beamlines.i04.transfocator import Transfocator
4
5
  from dodal.devices.beamsize.beamsize import BeamsizeBase
5
- from dodal.devices.i04.transfocator import Transfocator
6
6
 
7
7
 
8
8
  class Beamsize(BeamsizeBase):
9
+ """Device that calculates the size of the beam by taking the minimum of the
10
+ transfocator size and the aperture scatterguard diameter.
11
+ """
12
+
9
13
  def __init__(
10
14
  self,
11
15
  transfocator: Transfocator,
@@ -20,26 +24,26 @@ class Beamsize(BeamsizeBase):
20
24
  self.x_um = derived_signal_r(
21
25
  self._get_beamsize_x,
22
26
  transfocator_size_x=self._transfocator_ref().current_horizontal_size_rbv,
23
- aperture_radius=self._aperture_scatterguard_ref().radius,
27
+ aperture_diameter=self._aperture_scatterguard_ref().diameter,
24
28
  derived_units="µm",
25
29
  )
26
30
  self.y_um = derived_signal_r(
27
31
  self._get_beamsize_y,
28
32
  transfocator_size_y=self._transfocator_ref().current_vertical_size_rbv,
29
- aperture_radius=self._aperture_scatterguard_ref().radius,
33
+ aperture_diameter=self._aperture_scatterguard_ref().diameter,
30
34
  derived_units="µm",
31
35
  )
32
36
 
33
37
  def _get_beamsize_x(
34
38
  self,
35
39
  transfocator_size_x: float,
36
- aperture_radius: float,
40
+ aperture_diameter: float,
37
41
  ) -> float:
38
- return min(transfocator_size_x, aperture_radius)
42
+ return min(transfocator_size_x, aperture_diameter)
39
43
 
40
44
  def _get_beamsize_y(
41
45
  self,
42
46
  transfocator_size_y: float,
43
- aperture_radius: float,
47
+ aperture_diameter: float,
44
48
  ) -> float:
45
- return min(transfocator_size_y, aperture_radius)
49
+ return min(transfocator_size_y, aperture_diameter)
@@ -15,7 +15,7 @@ from ophyd_async.core import (
15
15
  )
16
16
  from redis.asyncio import ConnectionError, StrictRedis
17
17
 
18
- from dodal.devices.i04.constants import RedisConstants
18
+ from dodal.devices.beamlines.i04.constants import RedisConstants
19
19
  from dodal.devices.oav.oav_calculations import (
20
20
  calculate_beam_distance,
21
21
  )
@@ -238,7 +238,6 @@ class MurkoResultsDevice(StandardReadable, Triggerable, Stageable):
238
238
  remove many of the outliers. Murko also occasionally picks a point in the bottom
239
239
  left corner, which can be removed by filtering results with a small x pixel.
240
240
  """
241
-
242
241
  LOGGER.info(f"Number of results before filtering: {len(self._results)}")
243
242
  sorted_results = sorted(self._results, key=lambda item: item.chosen_point_px[0])
244
243
 
@@ -278,15 +277,16 @@ class MurkoResultsDevice(StandardReadable, Triggerable, Stageable):
278
277
 
279
278
 
280
279
  def get_yz_least_squares(vertical_dists: list, omegas: list) -> tuple[float, float]:
281
- """Get the least squares solution for y and z from the vertical distances and omega angles.
280
+ """Get the least squares solution for y and z from the vertical distances and omega
281
+ angles.
282
282
 
283
283
  Args:
284
- v_dists (list): List of vertical distances from beam centre. Any units
284
+ vertical_dists (list): List of vertical distances from beam centre. Any units.
285
285
  omegas (list): List of omega angles in degrees.
286
286
 
287
287
  Returns:
288
288
  tuple[float, float]: y, z distances from centre, in whichever units
289
- v_dists came as.
289
+ v_dists came as.
290
290
  """
291
291
  thetas = np.radians(omegas)
292
292
  matrix = np.column_stack([np.cos(thetas), -np.sin(thetas)])
@@ -3,7 +3,6 @@ import asyncio
3
3
  from ophyd_async.core import (
4
4
  AsyncStatus,
5
5
  StandardReadable,
6
- observe_value,
7
6
  wait_for_value,
8
7
  )
9
8
  from ophyd_async.epics.core import epics_signal_r, epics_signal_rw
@@ -49,24 +48,20 @@ class Transfocator(StandardReadable):
49
48
  @AsyncStatus.wrap
50
49
  async def set(self, value: float):
51
50
  """To set the beamsize on the transfocator we must:
52
- 1. Set the beamsize in the calculator part of the transfocator
53
- 2. Get the predicted number of lenses needed from this calculator
54
- 3. Enter this back into the device
55
- 4. Start the device moving
56
- 5. Wait for the start_rbv goes high and low again
51
+ 1. Set the beamsize in the calculator part of the transfocator.
52
+ 2. Get the predicted number of lenses needed from this calculator.
53
+ 3. Enter this back into the device.
54
+ 4. Start the device moving.
55
+ 5. Wait for the start_rbv goes high and low again.
57
56
  """
58
57
  LOGGER.info(f"Transfocator setting {value} beamsize")
59
58
 
60
- # Logic in the IOC calculates _num_lenses_calc_rbv when _vert_size_calc_sp changes
61
-
62
- # Register an observer before setting _vert_size_calc_sp to ensure we don't miss changes
63
- num_lenses_calc_iterator = observe_value(
64
- self._num_lenses_calc_rbv, timeout=self.TIMEOUT
65
- )
66
-
67
- await anext(num_lenses_calc_iterator)
68
59
  await self._vert_size_calc_sp.set(value)
69
- calc_lenses = await anext(num_lenses_calc_iterator)
60
+ # Logic in the IOC calculates _num_lenses_calc_rbv when _vert_size_calc_sp changes,
61
+ # but this isn't instant so we need a short sleep until
62
+ # https://jira.diamond.ac.uk/browse/I04-1100
63
+ await asyncio.sleep(0.1)
64
+ calc_lenses = await self._num_lenses_calc_rbv.get_value()
70
65
 
71
66
  async with periodic_reminder(
72
67
  f"Waiting for transfocator to insert {calc_lenses} into beam"
@@ -0,0 +1,3 @@
1
+ from dodal.devices.beamlines.i05.enums import Grating
2
+
3
+ __all__ = ["Grating"]
@@ -0,0 +1,3 @@
1
+ from .i06_enum import I06Grating
2
+
3
+ __all__ = ["I06Grating"]
@@ -0,0 +1,7 @@
1
+ from ophyd_async.core import StrictEnum
2
+
3
+
4
+ class I06Grating(StrictEnum):
5
+ GRATING_150 = "150 lines/mm"
6
+ GRATING_400 = "400 lines/mm"
7
+ GRATING_1200 = "1200 lines/mm"
@@ -9,9 +9,8 @@ from dodal.devices.common_dcm import (
9
9
 
10
10
 
11
11
  class DCM(DoubleCrystalMonochromator[PitchAndRollCrystal, StationaryCrystal]):
12
- """
13
- Device for i07's DCM, including temperature monitors and vertical motor which were
14
- included in GDA.
12
+ """Device for i07's DCM, including temperature monitors and vertical motor which
13
+ were included in GDA.
15
14
  """
16
15
 
17
16
  def __init__(
@@ -5,34 +5,33 @@ from dodal.devices.util.lookup_tables import energy_distance_table
5
5
 
6
6
 
7
7
  class InsertionDevice(UndulatorInKeV):
8
- """
9
- Insertion device for i07 including beamline-specific energy-gap lookup behaviour
8
+ """Insertion device for i07 including beamline-specific energy-gap lookup
9
+ behaviour.
10
10
  """
11
11
 
12
12
  def __init__(
13
13
  self,
14
- name: str,
15
14
  prefix: str,
16
15
  harmonic: UndulatorOrder,
17
16
  id_gap_lookup_table_path: str = "/dls_sw/i07/software/gda/config/lookupTables/"
18
17
  + "IIDCalibrationTable.txt",
19
- ) -> None:
18
+ name: str = "",
19
+ ):
20
20
  super().__init__(prefix, id_gap_lookup_table_path, name=name)
21
21
  self.harmonic = harmonic
22
22
 
23
23
  async def _get_gap_to_match_energy(self, energy_kev: float) -> float:
24
- """
25
- i07's energy scans remain on a particular harmonic while changing energy. The
24
+ """i07's energy scans remain on a particular harmonic while changing energy. The
26
25
  calibration table has one row for each harmonic, row contains max and min
27
26
  energies and their corresponding ID gaps. The requested energy is used to
28
27
  interpolate between these values, assuming a linear relationship on the relevant
29
28
  scale.
30
29
  """
31
- energy_to_distance_table: np.ndarray = await energy_distance_table(
30
+ energy_to_distance_table = await energy_distance_table(
32
31
  self.id_gap_lookup_table_path, comments="#", skiprows=2
33
32
  )
34
- harmonic_value: int = await self.harmonic.value.get_value()
33
+ harmonic_value = await self.harmonic.value.get_value()
35
34
 
36
- row: np.ndarray = energy_to_distance_table[harmonic_value - 1, :]
35
+ row = energy_to_distance_table[harmonic_value - 1, :]
37
36
  gap = np.interp(energy_kev, [row[1], row[2]], [row[3], row[4]])
38
37
  return gap
@@ -0,0 +1,3 @@
1
+ from dodal.devices.beamlines.i09.enums import Grating, LensMode, PassEnergy, PsuMode
2
+
3
+ __all__ = ["Grating", "LensMode", "PsuMode", "PassEnergy"]