dls-dodal 1.68.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.
- {dls_dodal-1.68.0.dist-info → dls_dodal-2.0.0.dist-info}/METADATA +1 -31
- dls_dodal-2.0.0.dist-info/RECORD +354 -0
- {dls_dodal-1.68.0.dist-info → dls_dodal-2.0.0.dist-info}/WHEEL +1 -1
- dodal/_version.py +2 -2
- dodal/beamlines/__init__.py +10 -17
- dodal/beamlines/adsim.py +40 -33
- dodal/beamlines/b01_1.py +11 -0
- dodal/beamlines/b07.py +17 -21
- dodal/beamlines/b07_1.py +20 -22
- dodal/beamlines/b07_shared.py +12 -0
- dodal/beamlines/b16.py +1 -1
- dodal/beamlines/b21.py +15 -6
- dodal/beamlines/i02_1.py +17 -45
- dodal/beamlines/i02_2.py +6 -12
- dodal/beamlines/i03.py +8 -5
- dodal/beamlines/i03_supervisor.py +19 -0
- dodal/beamlines/i04.py +87 -184
- dodal/beamlines/i05.py +9 -39
- dodal/beamlines/i05_1.py +4 -13
- dodal/beamlines/i05_shared.py +51 -0
- dodal/beamlines/i06_1.py +26 -0
- dodal/beamlines/{i06.py → i06_shared.py} +25 -14
- dodal/beamlines/i07.py +14 -16
- dodal/beamlines/i09.py +76 -29
- dodal/beamlines/i09_1.py +25 -56
- dodal/beamlines/i09_1_shared.py +61 -0
- dodal/beamlines/i09_2.py +6 -100
- dodal/beamlines/i09_2_shared.py +110 -0
- dodal/beamlines/i10.py +60 -54
- dodal/beamlines/i10_1.py +99 -10
- dodal/beamlines/{i10_optics.py → i10_shared.py} +80 -66
- dodal/beamlines/i11.py +31 -18
- dodal/beamlines/i13_1.py +1 -1
- dodal/beamlines/i15.py +6 -6
- dodal/beamlines/i15_1.py +6 -6
- dodal/beamlines/i16.py +11 -0
- dodal/beamlines/i17.py +37 -28
- dodal/beamlines/i18.py +3 -4
- dodal/beamlines/i19_1.py +95 -34
- dodal/beamlines/i19_2.py +68 -52
- dodal/beamlines/i19_optics.py +26 -13
- dodal/beamlines/i20_1.py +17 -11
- dodal/beamlines/i21.py +44 -29
- dodal/beamlines/i22.py +19 -4
- dodal/beamlines/i23.py +20 -27
- dodal/beamlines/i24.py +64 -113
- dodal/beamlines/k07.py +99 -5
- dodal/beamlines/p38.py +3 -3
- dodal/beamlines/p60.py +35 -14
- dodal/beamlines/p99.py +16 -15
- dodal/beamlines/training_rig.py +20 -12
- dodal/cli.py +36 -2
- dodal/common/__init__.py +2 -1
- dodal/common/beamlines/beamline_parameters.py +2 -1
- dodal/common/beamlines/beamline_utils.py +11 -9
- dodal/common/beamlines/commissioning_mode.py +6 -3
- dodal/common/coordination.py +12 -14
- dodal/common/crystal_metadata.py +5 -8
- dodal/common/device_utils.py +4 -3
- dodal/common/maths.py +87 -19
- dodal/common/udc_directory_provider.py +13 -8
- dodal/common/visit.py +18 -21
- dodal/common/watcher_utils.py +13 -12
- dodal/device_manager.py +94 -54
- dodal/devices/aperturescatterguard.py +26 -27
- dodal/devices/areadetector/plugins/cam.py +1 -3
- dodal/devices/areadetector/plugins/mjpg.py +6 -5
- dodal/devices/attenuator/attenuator.py +12 -11
- dodal/devices/beamlines/b07/__init__.py +3 -0
- dodal/devices/{b07_1 → beamlines/b07_1}/__init__.py +2 -2
- dodal/devices/{b07_1 → beamlines/b07_1}/ccmc.py +5 -10
- dodal/devices/{b16 → beamlines/b16}/detector.py +2 -3
- dodal/devices/{i02_1 → beamlines/i02_1}/fast_grid_scan.py +2 -3
- dodal/devices/{i02_1 → beamlines/i02_1}/sample_motors.py +1 -1
- dodal/devices/{i03 → beamlines/i03}/beamsize.py +11 -7
- dodal/devices/{i03 → beamlines/i03}/dcm.py +1 -2
- dodal/devices/{i03 → beamlines/i03}/undulator_dcm.py +4 -5
- dodal/devices/beamlines/i04/beam_centre.py +151 -0
- dodal/devices/{i04 → beamlines/i04}/beamsize.py +11 -7
- dodal/devices/beamlines/i04/max_pixel.py +25 -0
- dodal/devices/{i04 → beamlines/i04}/murko_results.py +23 -8
- dodal/devices/{i04 → beamlines/i04}/transfocator.py +10 -15
- dodal/devices/beamlines/i05/__init__.py +3 -0
- dodal/devices/beamlines/i06_shared/__init__.py +3 -0
- dodal/devices/beamlines/i06_shared/i06_enum.py +7 -0
- dodal/devices/{i07 → beamlines/i07}/dcm.py +2 -3
- dodal/devices/{i07 → beamlines/i07}/id.py +8 -9
- dodal/devices/beamlines/i09/__init__.py +3 -0
- dodal/devices/{i09_1_shared → beamlines/i09_1_shared}/hard_energy.py +5 -6
- dodal/devices/{i09_1_shared → beamlines/i09_1_shared}/hard_undulator_functions.py +19 -16
- dodal/devices/{i10 → beamlines/i10}/diagnostics.py +4 -3
- dodal/devices/{i10 → beamlines/i10}/i10_apple2.py +37 -51
- dodal/devices/{i10 → beamlines/i10}/rasor/rasor_current_amp.py +1 -24
- dodal/devices/{i10 → beamlines/i10}/rasor/rasor_motors.py +2 -2
- dodal/devices/{i10 → beamlines/i10}/slits.py +5 -3
- dodal/devices/beamlines/i10_1/__init__.py +9 -0
- dodal/devices/beamlines/i10_1/electromagnet/magnet.py +16 -0
- dodal/devices/beamlines/i10_1/electromagnet/stages.py +14 -0
- dodal/devices/beamlines/i10_1/scaler_cards.py +13 -0
- dodal/devices/{i11 → beamlines/i11}/cyberstar_blower.py +1 -1
- dodal/devices/{i11 → beamlines/i11}/diff_stages.py +4 -6
- dodal/devices/{i11 → beamlines/i11}/mythen.py +3 -4
- dodal/devices/{i11 → beamlines/i11}/nx100robot.py +6 -6
- dodal/devices/{i11 → beamlines/i11}/spinner.py +1 -1
- dodal/devices/{i13_1 → beamlines/i13_1}/merlin.py +1 -1
- dodal/devices/{i15 → beamlines/i15}/dcm.py +1 -2
- dodal/devices/{i15 → beamlines/i15}/focussing_mirror.py +5 -5
- dodal/devices/{i15 → beamlines/i15}/jack.py +2 -2
- dodal/devices/{i15 → beamlines/i15}/multilayer_mirror.py +1 -1
- dodal/devices/{i17 → beamlines/i17}/i17_apple2.py +16 -22
- dodal/devices/{i18 → beamlines/i18}/diode.py +1 -1
- dodal/devices/{i19 → beamlines/i19}/access_controlled/attenuator_motor_squad.py +12 -8
- dodal/devices/{i19 → beamlines/i19}/access_controlled/blueapi_device.py +16 -15
- dodal/devices/beamlines/i19/access_controlled/piezo_control.py +72 -0
- dodal/devices/{i19 → beamlines/i19}/access_controlled/shutter.py +11 -9
- dodal/devices/{i19 → beamlines/i19}/backlight.py +3 -1
- dodal/devices/{i19 → beamlines/i19}/mapt_configuration.py +2 -1
- dodal/devices/{i19 → beamlines/i19}/pin_col_stages.py +11 -8
- dodal/devices/beamlines/i19/pin_tip.py +32 -0
- dodal/devices/beamlines/i21/__init__.py +3 -0
- dodal/devices/{i22 → beamlines/i22}/dcm.py +1 -2
- dodal/devices/{i22 → beamlines/i22}/fswitch.py +1 -3
- dodal/devices/{i22 → beamlines/i22}/nxsas.py +5 -4
- dodal/devices/{i24 → beamlines/i24}/beam_center.py +1 -1
- dodal/devices/{i24 → beamlines/i24}/beamstop.py +2 -2
- dodal/devices/{i24 → beamlines/i24}/commissioning_jungfrau.py +12 -12
- dodal/devices/{i24 → beamlines/i24}/dcm.py +1 -3
- dodal/devices/{i24 → beamlines/i24}/dual_backlight.py +3 -3
- dodal/devices/{i24 → beamlines/i24}/pmac.py +9 -7
- dodal/devices/{p60 → beamlines/p60}/lab_xray_source.py +1 -1
- dodal/devices/beamlines/p99/__init__.py +0 -0
- dodal/devices/{p99 → beamlines/p99}/andor2_point.py +11 -15
- dodal/devices/bimorph_mirror.py +22 -20
- dodal/devices/collimation_table.py +3 -2
- dodal/devices/common_dcm.py +30 -20
- dodal/devices/controllers.py +2 -2
- dodal/devices/cryostream.py +8 -0
- dodal/devices/current_amplifiers/current_amplifier.py +16 -18
- dodal/devices/current_amplifiers/current_amplifier_detector.py +9 -10
- dodal/devices/current_amplifiers/femto.py +8 -9
- dodal/devices/current_amplifiers/sr570.py +16 -16
- dodal/devices/current_amplifiers/struck_scaler_counter.py +5 -5
- dodal/devices/detector/det_resolution.py +9 -8
- dodal/devices/detector/detector.py +4 -2
- dodal/devices/diamond_filter.py +3 -4
- dodal/devices/eiger.py +32 -17
- dodal/devices/eiger_odin.py +1 -1
- dodal/devices/electron_analyser/base/__init__.py +3 -3
- dodal/devices/electron_analyser/base/base_controller.py +32 -21
- dodal/devices/electron_analyser/base/base_detector.py +15 -20
- dodal/devices/electron_analyser/base/base_driver_io.py +39 -46
- dodal/devices/electron_analyser/base/base_enums.py +0 -5
- dodal/devices/electron_analyser/base/base_region.py +29 -31
- dodal/devices/electron_analyser/base/base_util.py +18 -16
- dodal/devices/electron_analyser/base/energy_sources.py +35 -40
- dodal/devices/electron_analyser/specs/specs_detector.py +7 -6
- dodal/devices/electron_analyser/vgscienta/vgscienta_detector.py +7 -6
- dodal/devices/eurotherm.py +3 -2
- dodal/devices/fast_grid_scan.py +31 -34
- dodal/devices/fast_shutter.py +125 -39
- dodal/devices/flux.py +1 -1
- dodal/devices/focusing_mirror.py +29 -11
- dodal/devices/hutch_shutter.py +6 -6
- dodal/devices/insertion_device/__init__.py +20 -8
- dodal/devices/insertion_device/apple2_controller.py +371 -0
- dodal/devices/insertion_device/apple2_undulator.py +184 -587
- dodal/devices/insertion_device/apple_knot_controller.py +222 -0
- dodal/devices/insertion_device/energy.py +161 -0
- dodal/devices/insertion_device/energy_motor_lookup.py +21 -28
- dodal/devices/insertion_device/lookup_table_models.py +47 -52
- dodal/devices/insertion_device/polarisation.py +36 -0
- dodal/devices/ipin.py +1 -1
- dodal/devices/linkam3.py +7 -5
- dodal/devices/motors.py +107 -19
- dodal/devices/mx_phase1/beamstop.py +2 -4
- dodal/devices/oav/oav_calculations.py +20 -13
- dodal/devices/oav/oav_detector.py +92 -22
- dodal/devices/oav/oav_parameters.py +4 -9
- dodal/devices/oav/oav_to_redis_forwarder.py +22 -18
- dodal/devices/oav/pin_image_recognition/__init__.py +4 -6
- dodal/devices/oav/pin_image_recognition/manual_test.py +1 -2
- dodal/devices/oav/pin_image_recognition/utils.py +30 -32
- dodal/devices/oav/snapshots/grid_overlay.py +10 -9
- dodal/devices/oav/snapshots/snapshot_image_processing.py +15 -13
- dodal/devices/oav/utils.py +20 -6
- dodal/devices/p45.py +3 -9
- dodal/devices/pgm.py +8 -14
- dodal/devices/pressure_jump_cell.py +93 -32
- dodal/devices/qbpm.py +1 -3
- dodal/devices/robot.py +45 -20
- dodal/devices/s4_slit_gaps.py +1 -1
- dodal/devices/selectable_source.py +41 -0
- dodal/devices/slits.py +2 -5
- dodal/devices/smargon.py +2 -3
- dodal/devices/temperture_controller/lakeshore/lakeshore.py +38 -64
- dodal/devices/temperture_controller/lakeshore/lakeshore_io.py +21 -35
- dodal/devices/tetramm.py +7 -7
- dodal/devices/turbo_slit.py +8 -7
- dodal/devices/undulator.py +42 -56
- dodal/devices/util/adjuster_plans.py +2 -3
- dodal/devices/util/epics_util.py +10 -10
- dodal/devices/util/lookup_tables.py +17 -18
- dodal/devices/v2f.py +2 -3
- dodal/devices/watsonmarlow323_pump.py +1 -1
- dodal/devices/xbpm_feedback.py +3 -2
- dodal/devices/xspress3/xspress3.py +8 -11
- dodal/devices/xspress3/xspress3_channel.py +3 -6
- dodal/devices/zebra/zebra.py +21 -7
- dodal/devices/zebra/zebra_constants_mapping.py +12 -7
- dodal/devices/zebra/zebra_controlled_shutter.py +2 -1
- dodal/devices/zocalo/zocalo_interaction.py +14 -14
- dodal/devices/zocalo/zocalo_results.py +33 -33
- dodal/log.py +23 -20
- dodal/plan_stubs/check_topup.py +15 -15
- dodal/plan_stubs/data_session.py +6 -6
- dodal/plan_stubs/motor_utils.py +22 -18
- dodal/plan_stubs/pressure_jump_cell.py +18 -0
- dodal/plan_stubs/wrapped.py +40 -55
- dodal/plans/bimorph.py +63 -52
- dodal/plans/configure_arm_trigger_and_disarm_detector.py +0 -1
- dodal/plans/device_setup_plans/__init__.py +5 -0
- dodal/plans/device_setup_plans/setup_pin_tip_params.py +63 -0
- dodal/plans/preprocessors/verify_undulator_gap.py +10 -8
- dodal/plans/spec_path.py +3 -5
- dodal/plans/verify_undulator_gap.py +1 -2
- dodal/plans/wrapped.py +4 -3
- dodal/testing/__init__.py +0 -0
- dodal/testing/electron_analyser/device_factory.py +5 -7
- dodal/testing/fixtures/devices/apple2.py +38 -0
- dodal/testing/fixtures/run_engine.py +3 -7
- dodal/testing/fixtures/utils.py +1 -2
- dodal/utils.py +60 -58
- dls_dodal-1.68.0.dist-info/RECORD +0 -330
- dodal/beamline_specific_utils/i05_shared.py +0 -14
- dodal/devices/b07/__init__.py +0 -3
- dodal/devices/i04/max_pixel.py +0 -38
- dodal/devices/i05/__init__.py +0 -3
- dodal/devices/i09/__init__.py +0 -3
- dodal/devices/i21/__init__.py +0 -5
- {dls_dodal-1.68.0.dist-info → dls_dodal-2.0.0.dist-info}/entry_points.txt +0 -0
- {dls_dodal-1.68.0.dist-info → dls_dodal-2.0.0.dist-info}/licenses/LICENSE +0 -0
- {dls_dodal-1.68.0.dist-info → dls_dodal-2.0.0.dist-info}/top_level.txt +0 -0
- /dodal/{beamline_specific_utils → devices/beamlines}/__init__.py +0 -0
- /dodal/devices/{b07 → beamlines/b07}/enums.py +0 -0
- /dodal/devices/{b07_1 → beamlines/b07_1}/enums.py +0 -0
- /dodal/devices/{b16 → beamlines/b16}/__init__.py +0 -0
- /dodal/devices/{i02_1 → beamlines/i02_1}/__init__.py +0 -0
- /dodal/devices/{i02_2 → beamlines/i02_2}/__init__.py +0 -0
- /dodal/devices/{i03 → beamlines/i03}/__init__.py +0 -0
- /dodal/devices/{i03 → beamlines/i03}/constants.py +0 -0
- /dodal/devices/{i04 → beamlines/i04}/__init__.py +0 -0
- /dodal/devices/{i04 → beamlines/i04}/constants.py +0 -0
- /dodal/devices/{i05 → beamlines/i05}/enums.py +0 -0
- /dodal/devices/{i07 → beamlines/i07}/__init__.py +0 -0
- /dodal/devices/{i09 → beamlines/i09}/enums.py +0 -0
- /dodal/devices/{i09_1 → beamlines/i09_1}/__init__.py +0 -0
- /dodal/devices/{i09_1 → beamlines/i09_1}/enums.py +0 -0
- /dodal/devices/{i09_1_shared → beamlines/i09_1_shared}/__init__.py +0 -0
- /dodal/devices/{i09_2_shared → beamlines/i09_2_shared}/__init__.py +0 -0
- /dodal/devices/{i09_2_shared → beamlines/i09_2_shared}/i09_apple2.py +0 -0
- /dodal/devices/{i10 → beamlines/i10}/__init__.py +0 -0
- /dodal/devices/{i10 → beamlines/i10}/i10_setting_data.py +0 -0
- /dodal/devices/{i10 → beamlines/i10}/mirrors.py +0 -0
- /dodal/devices/{i10 → beamlines/i10}/rasor/__init__.py +0 -0
- /dodal/devices/{i10 → beamlines/i10}/rasor/rasor_scaler_cards.py +0 -0
- /dodal/devices/{i11 → beamlines/i10_1/electromagnet}/__init__.py +0 -0
- /dodal/devices/{i13_1 → beamlines/i11}/__init__.py +0 -0
- /dodal/devices/{i15 → beamlines/i13_1}/__init__.py +0 -0
- /dodal/devices/{i13_1 → beamlines/i13_1}/merlin_controller.py +0 -0
- /dodal/devices/{i17 → beamlines/i15}/__init__.py +0 -0
- /dodal/devices/{i15 → beamlines/i15}/laue.py +0 -0
- /dodal/devices/{i15 → beamlines/i15}/motors.py +0 -0
- /dodal/devices/{i15 → beamlines/i15}/rail.py +0 -0
- /dodal/devices/{i18 → beamlines/i17}/__init__.py +0 -0
- /dodal/devices/{i19 → beamlines/i18}/__init__.py +0 -0
- /dodal/devices/{i18 → beamlines/i18}/kb_mirror.py +0 -0
- /dodal/devices/{i19/access_controlled → beamlines/i19}/__init__.py +0 -0
- /dodal/devices/{i20_1 → beamlines/i19/access_controlled}/__init__.py +0 -0
- /dodal/devices/{i19 → beamlines/i19}/access_controlled/hutch_access.py +0 -0
- /dodal/devices/{i19 → beamlines/i19}/beamstop.py +0 -0
- /dodal/devices/{i19 → beamlines/i19}/diffractometer.py +0 -0
- /dodal/devices/{i22 → beamlines/i20_1}/__init__.py +0 -0
- /dodal/devices/{i21 → beamlines/i21}/enums.py +0 -0
- /dodal/devices/{i24 → beamlines/i22}/__init__.py +0 -0
- /dodal/devices/{p99 → beamlines/i24}/__init__.py +0 -0
- /dodal/devices/{i24 → beamlines/i24}/aperture.py +0 -0
- /dodal/devices/{i24 → beamlines/i24}/focus_mirrors.py +0 -0
- /dodal/devices/{i24 → beamlines/i24}/vgonio.py +0 -0
- /dodal/devices/{p60 → beamlines/p60}/__init__.py +0 -0
- /dodal/devices/{p60 → beamlines/p60}/enums.py +0 -0
- /dodal/devices/{p99 → beamlines/p99}/sample_stage.py +0 -0
- /dodal/devices/insertion_device/{id_enum.py → enum.py} +0 -0
|
@@ -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 = ""
|
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
31
|
+
aperture_diameter: float,
|
|
28
32
|
) -> float:
|
|
29
|
-
return min(
|
|
33
|
+
return min(aperture_diameter, BeamsizeConstants.BEAM_WIDTH_UM)
|
|
30
34
|
|
|
31
35
|
def _get_beamsize_y(
|
|
32
36
|
self,
|
|
33
|
-
|
|
37
|
+
aperture_diameter: float,
|
|
34
38
|
) -> float:
|
|
35
|
-
return min(
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
40
|
+
aperture_diameter: float,
|
|
37
41
|
) -> float:
|
|
38
|
-
return min(transfocator_size_x,
|
|
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
|
-
|
|
47
|
+
aperture_diameter: float,
|
|
44
48
|
) -> float:
|
|
45
|
-
return min(transfocator_size_y,
|
|
49
|
+
return min(transfocator_size_y, aperture_diameter)
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
from bluesky.protocols import Triggerable
|
|
3
|
+
from ophyd_async.core import AsyncStatus, StandardReadable, soft_signal_r_and_setter
|
|
4
|
+
from ophyd_async.epics.core import (
|
|
5
|
+
epics_signal_r,
|
|
6
|
+
)
|
|
7
|
+
|
|
8
|
+
from dodal.devices.oav.utils import convert_to_gray_and_blur
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class MaxPixel(StandardReadable, Triggerable):
|
|
12
|
+
"""Gets the max pixel (brightest pixel) from the image after some image processing."""
|
|
13
|
+
|
|
14
|
+
def __init__(self, prefix: str, name: str = "") -> None:
|
|
15
|
+
self.array_data = epics_signal_r(np.ndarray, f"pva://{prefix}PVA:ARRAY")
|
|
16
|
+
self.max_pixel_val, self._max_val_setter = soft_signal_r_and_setter(float)
|
|
17
|
+
super().__init__(name)
|
|
18
|
+
|
|
19
|
+
@AsyncStatus.wrap
|
|
20
|
+
async def trigger(self):
|
|
21
|
+
img_data = await self.array_data.get_value()
|
|
22
|
+
arr = convert_to_gray_and_blur(img_data)
|
|
23
|
+
max_val = float(np.max(arr))
|
|
24
|
+
assert isinstance(max_val, float)
|
|
25
|
+
self._max_val_setter(max_val)
|
|
@@ -13,9 +13,9 @@ from ophyd_async.core import (
|
|
|
13
13
|
soft_signal_r_and_setter,
|
|
14
14
|
soft_signal_rw,
|
|
15
15
|
)
|
|
16
|
-
from redis.asyncio import StrictRedis
|
|
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
|
)
|
|
@@ -103,13 +103,25 @@ class MurkoResultsDevice(StandardReadable, Triggerable, Stageable):
|
|
|
103
103
|
self.z_mm, self._z_mm_setter = soft_signal_r_and_setter(float)
|
|
104
104
|
super().__init__(name=name)
|
|
105
105
|
|
|
106
|
+
async def _check_redis_connection(self):
|
|
107
|
+
try:
|
|
108
|
+
await self.redis_client.ping() # type: ignore
|
|
109
|
+
return True
|
|
110
|
+
except ConnectionError:
|
|
111
|
+
LOGGER.warning(
|
|
112
|
+
f"Failed to connect to redis: {self.redis_client}. Murko results device will not trigger"
|
|
113
|
+
)
|
|
114
|
+
return False
|
|
115
|
+
|
|
106
116
|
def _reset(self):
|
|
107
117
|
self._last_omega = None
|
|
108
118
|
self._results: list[MurkoResult] = []
|
|
109
119
|
|
|
110
120
|
@AsyncStatus.wrap
|
|
111
121
|
async def stage(self):
|
|
112
|
-
await self.
|
|
122
|
+
self.redis_connected = await self._check_redis_connection()
|
|
123
|
+
if self.redis_connected:
|
|
124
|
+
await self.pubsub.subscribe("murko-results")
|
|
113
125
|
self._x_mm_setter(0)
|
|
114
126
|
self._y_mm_setter(0)
|
|
115
127
|
self._z_mm_setter(0)
|
|
@@ -117,10 +129,13 @@ class MurkoResultsDevice(StandardReadable, Triggerable, Stageable):
|
|
|
117
129
|
@AsyncStatus.wrap
|
|
118
130
|
async def unstage(self):
|
|
119
131
|
self._reset()
|
|
120
|
-
|
|
132
|
+
if self.redis_connected:
|
|
133
|
+
await self.pubsub.unsubscribe()
|
|
121
134
|
|
|
122
135
|
@AsyncStatus.wrap
|
|
123
136
|
async def trigger(self):
|
|
137
|
+
if not self.redis_connected:
|
|
138
|
+
return
|
|
124
139
|
sample_id = await self.sample_id.get_value()
|
|
125
140
|
t_last_result = time.time()
|
|
126
141
|
while True:
|
|
@@ -223,7 +238,6 @@ class MurkoResultsDevice(StandardReadable, Triggerable, Stageable):
|
|
|
223
238
|
remove many of the outliers. Murko also occasionally picks a point in the bottom
|
|
224
239
|
left corner, which can be removed by filtering results with a small x pixel.
|
|
225
240
|
"""
|
|
226
|
-
|
|
227
241
|
LOGGER.info(f"Number of results before filtering: {len(self._results)}")
|
|
228
242
|
sorted_results = sorted(self._results, key=lambda item: item.chosen_point_px[0])
|
|
229
243
|
|
|
@@ -263,15 +277,16 @@ class MurkoResultsDevice(StandardReadable, Triggerable, Stageable):
|
|
|
263
277
|
|
|
264
278
|
|
|
265
279
|
def get_yz_least_squares(vertical_dists: list, omegas: list) -> tuple[float, float]:
|
|
266
|
-
"""Get the least squares solution for y and z from the vertical distances and omega
|
|
280
|
+
"""Get the least squares solution for y and z from the vertical distances and omega
|
|
281
|
+
angles.
|
|
267
282
|
|
|
268
283
|
Args:
|
|
269
|
-
|
|
284
|
+
vertical_dists (list): List of vertical distances from beam centre. Any units.
|
|
270
285
|
omegas (list): List of omega angles in degrees.
|
|
271
286
|
|
|
272
287
|
Returns:
|
|
273
288
|
tuple[float, float]: y, z distances from centre, in whichever units
|
|
274
|
-
|
|
289
|
+
v_dists came as.
|
|
275
290
|
"""
|
|
276
291
|
thetas = np.radians(omegas)
|
|
277
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
|
-
|
|
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"
|
|
@@ -9,9 +9,8 @@ from dodal.devices.common_dcm import (
|
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
class DCM(DoubleCrystalMonochromator[PitchAndRollCrystal, StationaryCrystal]):
|
|
12
|
-
"""
|
|
13
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
33
|
+
harmonic_value = await self.harmonic.value.get_value()
|
|
35
34
|
|
|
36
|
-
row
|
|
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
|
|
@@ -12,17 +12,16 @@ from ophyd_async.core import (
|
|
|
12
12
|
soft_signal_rw,
|
|
13
13
|
)
|
|
14
14
|
|
|
15
|
-
from dodal.devices.
|
|
16
|
-
from dodal.devices.i09_1_shared.hard_undulator_functions import (
|
|
15
|
+
from dodal.devices.beamlines.i09_1_shared.hard_undulator_functions import (
|
|
17
16
|
MAX_ENERGY_COLUMN,
|
|
18
17
|
MIN_ENERGY_COLUMN,
|
|
19
18
|
)
|
|
19
|
+
from dodal.devices.common_dcm import DoubleCrystalMonochromatorBase
|
|
20
20
|
from dodal.devices.undulator import UndulatorInMm, UndulatorOrder
|
|
21
21
|
|
|
22
22
|
|
|
23
23
|
class HardInsertionDeviceEnergy(StandardReadable, Movable[float]):
|
|
24
|
-
"""
|
|
25
|
-
Compound device to link hard x-ray undulator gap and order to photon energy.
|
|
24
|
+
"""Compound device to link hard x-ray undulator gap and order to photon energy.
|
|
26
25
|
Setting the energy adjusts the undulator gap accordingly.
|
|
27
26
|
"""
|
|
28
27
|
|
|
@@ -83,8 +82,8 @@ class HardInsertionDeviceEnergy(StandardReadable, Movable[float]):
|
|
|
83
82
|
|
|
84
83
|
|
|
85
84
|
class HardEnergy(StandardReadable, Locatable[float]):
|
|
86
|
-
"""
|
|
87
|
-
|
|
85
|
+
"""Energy compound device that provides combined change of both DCM energy and
|
|
86
|
+
undulator gap accordingly.
|
|
88
87
|
"""
|
|
89
88
|
|
|
90
89
|
def __init__(
|