Skip to content

Commit af1430f

Browse files
Villtordeir17846
andauthored
I09-1 switch to config_server for hard undulator lut (#1798)
* switch to config_server for lut * cast to np.ndarray * overwrite file * fix mock side effect * fix lint * small update to test failing certificate in daq-server * change lut to lut_provider tp avoid call to daq-server in CI * fix tests * switch to GenericLookupTable * pin version daq-server to >=1.1.2 * merge main branch * fix tests * clean up functions, move logic get_file_content logic out of them * rework classes * add more docstrings * more docstrings * docstring * fix updated daq-config-server module * small typo --------- Co-authored-by: eir17846 <victor.rogalev@diamond.ac.uk>
1 parent bb55ae6 commit af1430f

File tree

7 files changed

+258
-132
lines changed

7 files changed

+258
-132
lines changed

src/dodal/beamlines/i09_1_shared.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,21 @@
1+
from daq_config_server import ConfigClient
2+
3+
from dodal.common.beamlines.beamline_utils import get_config_client, set_config_client
14
from dodal.device_manager import DeviceManager
25
from dodal.devices.beamlines.i09_1_shared import (
36
HardEnergy,
47
HardInsertionDeviceEnergy,
58
calculate_energy_i09_hu,
69
calculate_gap_i09_hu,
710
)
11+
from dodal.devices.beamlines.i09_1_shared.hard_energy import (
12+
HardEnergy,
13+
HardInsertionDeviceEnergy,
14+
)
15+
from dodal.devices.beamlines.i09_1_shared.hard_undulator_functions import (
16+
calculate_energy_i09_hu,
17+
calculate_gap_i09_hu,
18+
)
819
from dodal.devices.common_dcm import (
920
DoubleCrystalMonochromatorWithDSpacing,
1021
PitchAndRollCrystal,
@@ -19,6 +30,9 @@
1930

2031
devices = DeviceManager()
2132

33+
set_config_client(ConfigClient())
34+
LOOK_UPTABLE_FILE = "/dls_sw/i09-1/software/gda/workspace_git/gda-diamond.git/configurations/i09-1-shared/lookupTables/IIDCalibrationTable.txt"
35+
2236

2337
@devices.factory()
2438
def psi1() -> HutchShutter:
@@ -51,7 +65,8 @@ def iidenergy(
5165
return HardInsertionDeviceEnergy(
5266
undulator_order=ienergy_order,
5367
undulator=iid,
54-
lut={}, # ToDo https://github.com/DiamondLightSource/sm-bluesky/issues/239
68+
config_server=get_config_client(),
69+
filepath=LOOK_UPTABLE_FILE,
5570
gap_to_energy_func=calculate_energy_i09_hu,
5671
energy_to_gap_func=calculate_gap_i09_hu,
5772
)

src/dodal/devices/beamlines/i09_1_shared/__init__.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,10 @@
22
from .hard_undulator_functions import (
33
calculate_energy_i09_hu,
44
calculate_gap_i09_hu,
5-
get_hu_lut_as_dict,
65
)
76

87
__all__ = [
98
"calculate_gap_i09_hu",
10-
"get_hu_lut_as_dict",
119
"calculate_energy_i09_hu",
1210
"HardInsertionDeviceEnergy",
1311
"HardEnergy",

src/dodal/devices/beamlines/i09_1_shared/hard_energy.py

Lines changed: 65 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
from asyncio import gather
2-
from collections.abc import Callable
2+
from typing import Protocol
33

44
from bluesky.protocols import Locatable, Location, Movable
5-
from numpy import ndarray
5+
from daq_config_server import ConfigClient
6+
from daq_config_server.models.lookup_tables import GenericLookupTable
67
from ophyd_async.core import (
78
AsyncStatus,
89
Reference,
@@ -12,33 +13,56 @@
1213
soft_signal_rw,
1314
)
1415

15-
from dodal.devices.beamlines.i09_1_shared.hard_undulator_functions import (
16-
MAX_ENERGY_COLUMN,
17-
MIN_ENERGY_COLUMN,
18-
)
1916
from dodal.devices.common_dcm import DoubleCrystalMonochromatorBase
2017
from dodal.devices.undulator import UndulatorInMm, UndulatorOrder
2118

2219

20+
class EnergyGapConvertor(Protocol):
21+
def __call__(
22+
self, look_up_table: GenericLookupTable, value: float, order: int
23+
) -> float:
24+
"""Protocol for a function to provide value conversion using lookup table."""
25+
...
26+
27+
2328
class HardInsertionDeviceEnergy(StandardReadable, Movable[float]):
24-
"""Compound device to link hard x-ray undulator gap and order to photon energy.
29+
"""Compound device to control insertion device energy.
30+
31+
This device link hard x-ray undulator gap and order to the required photon energy.
2532
Setting the energy adjusts the undulator gap accordingly.
33+
34+
Attributes:
35+
energy_demand (SignalRW[float]): The energy value that the user wants to set.
36+
energy (SignalRW[float]): The actual energy of the insertion device.
2637
"""
2738

2839
def __init__(
2940
self,
3041
undulator_order: UndulatorOrder,
3142
undulator: UndulatorInMm,
32-
lut: dict[int, ndarray],
33-
gap_to_energy_func: Callable[..., float],
34-
energy_to_gap_func: Callable[..., float],
43+
config_server: ConfigClient,
44+
filepath: str,
45+
gap_to_energy_func: EnergyGapConvertor,
46+
energy_to_gap_func: EnergyGapConvertor,
3547
name: str = "",
3648
) -> None:
37-
self._lut = lut
38-
self.gap_to_energy_func = gap_to_energy_func
39-
self.energy_to_gap_func = energy_to_gap_func
49+
"""Initialize the HardInsertionDeviceEnergy device.
50+
51+
Args:
52+
undulator_order (UndulatorOrder): undulator order device.
53+
undulator (UndulatorInMm): undulator device for gap control.
54+
config_server (ConfigServer): Config server client to retrieve the lookup table.
55+
filepath (str): File path to the lookup table on the config server.
56+
gap_to_energy_func (EnergyGapConvertor): Function to convert gap to energy using the lookup table.
57+
energy_to_gap_func (EnergyGapConvertor): Function to convert energy to gap using the lookup table.
58+
name (str, optional): Name for the device. Defaults to empty string.
59+
"""
4060
self._undulator_order_ref = Reference(undulator_order)
4161
self._undulator_ref = Reference(undulator)
62+
self._config_server = config_server
63+
self._filepath = filepath
64+
self._gap_to_energy_func = gap_to_energy_func
65+
self._energy_to_gap_func = energy_to_gap_func
4266

4367
self.add_readables([undulator_order, undulator.current_gap])
4468
with self.add_children_as_readables(StandardReadableFormat.HINTED_SIGNAL):
@@ -53,37 +77,40 @@ def __init__(
5377
super().__init__(name=name)
5478

5579
def _read_energy(self, current_gap: float, current_order: int) -> float:
56-
return self.gap_to_energy_func(
57-
gap=current_gap,
58-
look_up_table=self._lut,
59-
order=current_order,
80+
_lookup_table = self.get_look_up_table()
81+
return self._gap_to_energy_func(
82+
look_up_table=_lookup_table, value=current_gap, order=current_order
6083
)
6184

62-
async def _set_energy(self, energy: float) -> None:
85+
async def _set_energy(self, value: float) -> None:
6386
current_order = await self._undulator_order_ref().value.get_value()
64-
min_energy, max_energy = self._lut[current_order][
65-
MIN_ENERGY_COLUMN : MAX_ENERGY_COLUMN + 1
66-
]
67-
if not (min_energy <= energy <= max_energy):
68-
raise ValueError(
69-
f"Requested energy {energy} keV is out of range for harmonic {current_order}: "
70-
f"[{min_energy}, {max_energy}] keV"
71-
)
87+
_lookup_table = self.get_look_up_table()
88+
target_gap = self._energy_to_gap_func(_lookup_table, value, current_order)
89+
await self._undulator_ref().set(target_gap)
7290

73-
target_gap = self.energy_to_gap_func(
74-
photon_energy_kev=energy, look_up_table=self._lut, order=current_order
91+
def get_look_up_table(self) -> GenericLookupTable:
92+
self._lut: GenericLookupTable = self._config_server.get_file_contents(
93+
self._filepath,
94+
desired_return_type=GenericLookupTable,
95+
reset_cached_result=True,
7596
)
76-
await self._undulator_ref().set(target_gap)
97+
return self._lut
7798

7899
@AsyncStatus.wrap
79100
async def set(self, value: float) -> None:
101+
"""Update energy demand and set energy to a given value in keV.
102+
103+
Args:
104+
value (float): Energy in keV.
105+
"""
80106
self.energy_demand.set(value)
81107
await self.energy.set(value)
82108

83109

84110
class HardEnergy(StandardReadable, Locatable[float]):
85-
"""Energy compound device that provides combined change of both DCM energy and
86-
undulator gap accordingly.
111+
"""Compound energy device.
112+
113+
This device changes both monochromator and insertion device energy.
87114
"""
88115

89116
def __init__(
@@ -92,6 +119,13 @@ def __init__(
92119
undulator_energy: HardInsertionDeviceEnergy,
93120
name: str = "",
94121
) -> None:
122+
"""Initialize the HardEnergy device.
123+
124+
Args:
125+
dcm (DoubleCrystalMonochromatorBase): Double crystal monochromator device.
126+
undulator_energy (HardInsertionDeviceEnergy): Hard insertion device control.
127+
name (str, optional): name for the device. Defaults to empty.
128+
"""
95129
self._dcm_ref = Reference(dcm)
96130
self._undulator_energy_ref = Reference(undulator_energy)
97131
self.add_readables([undulator_energy, dcm.energy_in_keV])

0 commit comments

Comments
 (0)