Skip to content

Commit

Permalink
[TASK] added cavity, power converters, frequency
Browse files Browse the repository at this point in the history
  • Loading branch information
PierreSchnizer committed Feb 7, 2025
1 parent 6088ea6 commit 5700e1f
Show file tree
Hide file tree
Showing 7 changed files with 538 additions and 135 deletions.
83 changes: 65 additions & 18 deletions src/dt4acc/core/accelerators/element_proxies.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,10 @@ def estimate_shift(element, eps=1e-8):
return shift


def manipulate_kick(kick_angles: Tuple[float, float], kick_x = None, kick_y = None) -> Tuple[float, float]:
kick_angles = kick_angles.copy()
def manipulate_kick(
kick_angles: Tuple[float, float], kick_x=None, kick_y=None
) -> Tuple[float, float]:
kick_angles = kick_angles.copy()
if kick_x is not None:
kick_angles[0] = kick_x
if kick_y is not None:
Expand Down Expand Up @@ -90,7 +92,7 @@ async def update_shift(self, *, dx=None, dy=None):
"""
assert dx is not None or dy is not None, "Either dx or dy must be provided"

element, = self._obj
(element,) = self._obj
shift = estimate_shift(element)

dx = dx if dx is not None else shift[0]
Expand All @@ -100,7 +102,7 @@ async def update_shift(self, *, dx=None, dy=None):
shift_elem(element, dx, dy)

# look what really happened
element, = self._obj
(element,) = self._obj
dxr, _, dyr, _, _, _ = estimate_shift(element)
pass

Expand All @@ -119,7 +121,7 @@ async def update(self, property_id: str, value, element_data):
if value is not None:
assert np.isfinite(value), "Value must be finite"

element, = self._obj
(element,) = self._obj
method_name = f"set_{property_id}"

if method_name == "set_x":
Expand All @@ -129,12 +131,19 @@ async def update(self, property_id: str, value, element_data):
elif method_name == "set_roll":
await self.update_roll(roll=value)
elif method_name == "set_im":
val = value * element_data.hw2phys
element_type = str(element).split('\n')[0]
if 'Sextupole' in element_type:
element.update(H=val)
elif 'Quadrupole' in element_type:
element.update(K=val)
raise AssertionError("should not end up here")
# val = value * element_data.hw2phys
# element_type = str(element).split('\n')[0]
elif method_name == "set_main_strength":
element_type = element.__class__.__name__
if "Sextupole" in element_type:
element.update(H=value)
elif "Quadrupole" in element_type:
element.update(K=value)
else:
raise NotImplementedError(
f"Don't know how to set main strength for element {element_type}"
)
elif method_name == "set_freq":
element.update(Frequency=value * 1000)
elif method_name in ["set_rdbk", "set_K"]:
Expand All @@ -144,24 +153,57 @@ async def update(self, property_id: str, value, element_data):
elif method_name == "set_y_kick":
element.update(KickAngle=manipulate_kick(element.KickAngle, kick_y=value))
elif method_name == "set_frequency":
raise AssertionError("Cavity control not yet declared as functional, have a look to the line below")
# should be ok for AT
element.update(Frequency=value)
# raise AssertionError("Cavity control not yet declared as functional, have a look to the line below")
else:
method = getattr(element, method_name)
await method(value)

await self.on_update_finished.trigger(None)

def peek(self, property_id: str) -> float:
element, = self._obj
if property_id in ["K", "H", "main_strength"]:
return self.peek_main_strength(property_id)
elif property_id in ["x_kick", "y_kick"]:
return self.peek_kick(property_id)
elif property_id in ["frequency"]:
return self.peek_frequency()
else:
raise NotImplementedError(
f"handling property {property_id} not (yet) implemented"
)

def peek_frequency(self):
(element,) = self._obj
return element.Frequency

def peek_main_strength(self, property_id: str):
(element,) = self._obj
element_type = element.__class__.__name__
assert property_id == "main_strength"
if element_type == "Quadrupole":
assert property_id in ["K", "main_strength"]
return element.K
elif element_type == "Sextupole":
if property_id not in ["H", "main_strength"]:
raise AssertionError(
f"Not handling {property_id} for element {element_type}"
)
return element.H
else:
raise NotImplementedError
raise NotImplementedError(
f"main strength not implemented for element {element_type}"
)

def peek_kick(self, property_id: str):
(element,) = self._obj
lut = dict(x_kick=0, y_kick=1)
try:
idx = lut[property_id]
except KeyError as ke:
raise AssertionError(f"Did not expect kick {property_id}")
return element.KickAngle[idx]


class AddOnElementProxy(ElementProxy):
"""
Expand Down Expand Up @@ -191,7 +233,10 @@ class KickAngleCorrectorProxy(AddOnElementProxy):
"""

def __init__(self, obj, *, correction_plane, **kwargs):
assert correction_plane in ["horizontal", "vertical"], "Invalid correction plane"
assert correction_plane in [
"horizontal",
"vertical",
], "Invalid correction plane"
self.correction_planes = correction_plane
super().__init__(*obj, **kwargs)

Expand All @@ -206,12 +251,14 @@ async def update_kick(self, *, kick_x=None, kick_y=None, element_data):
Todo: review if this code is still neede
"""
element, = self._obj
(element,) = self._obj
if kick_x is not None:
kick_x = kick_x * element_data.hw2phys
if kick_y is not None:
kick_y = kick_y * element_data.hw2phys
element.update(KickAngle=manipulate_kick(self._obj.KickAngle, kick_x=kick_x, kick_y=kick_y))
element.update(
KickAngle=manipulate_kick(self._obj.KickAngle, kick_x=kick_x, kick_y=kick_y)
)

async def update(self, property_id: str, value, element_data):
"""
Expand Down
80 changes: 71 additions & 9 deletions src/dt4acc/core/command.py
Original file line number Diff line number Diff line change
@@ -1,26 +1,78 @@
from typing import Sequence

from bact_twin_architecture.data_model.command import Command, BehaviourOnError
from bact_twin_architecture.data_model.identifiers import (
LatticeElementPropertyID,
DevicePropertyID,
ConversionID,
)
from bact_twin_architecture.interfaces.command_rewritter import CommandRewriterBase
from bact_twin_architecture.interfaces.liaison_manager import LiaisonManagerBase
from bact_twin_architecture.interfaces.translator_service import TranslatorServiceBase

from .accelerators.accelerator_manager import AcceleratorManager
from .update_context_manager import UpdateContext
from ..custom_epics.ioc.liasion_translation_manager import TranslatorService


class UpdateManager:
"""Handle update requests from the device view
Treats the incoming requests as commands to be rewritten and delivered to the
machine
Supports peeking into the engine
"""
def __init__(self, command_rewritter: CommandRewriterBase, acc_mgr: AcceleratorManager):

def __init__(
self,
*,
command_rewritter: CommandRewriterBase,
liaison_manager: LiaisonManagerBase,
translator_service: TranslatorServiceBase,
acc_mgr: AcceleratorManager,
):
self.command_rewritter = command_rewritter
self.liaison_manager = liaison_manager
self.translator_service = translator_service
self.acc_mgr = acc_mgr

def peek(self, lat_elem, property_name) -> object:
def device_value_from_peeking_engine(
self, dev_prop: DevicePropertyID
) -> Sequence[float]:
"""
Todo:
review interface
place it in correct layer
How to handle that many values can be returned
"""
Todo: remove layer violation
if dev_prop.device_name[:3].upper() == "CAV":
pass
lat_props = self.liaison_manager.inverse(dev_prop)
if lat_props is None:
raise AssertionError(
f"{self.liaison_manager.__class__.__name__} does not know {dev_prop}"
)

def convert(lat_prop):
translator = self.translator_service.get(
ConversionID(lattice_property_id=lat_prop, device_property_id=dev_prop)
)
val = self.peek_engine(lat_prop)
return translator.forward(val)

values = [convert(lat_prop) for lat_prop in lat_props]
return values

def peek_engine(self, lat_elem_prop: LatticeElementPropertyID) -> object:
"""peek into underlaying engine to get value
Todo:
resolve layring violation
"""
proxy = self.acc_mgr.accelerator.proxy_factory.get(lat_elem)
val = proxy.peek(property_id=property_name)
proxy = self.acc_mgr.accelerator.proxy_factory.get(lat_elem_prop.element_name)
val = proxy.peek(property_id=lat_elem_prop.property)
return val

async def update(self, *, device_id, property_name, value=None, element=None):
Expand All @@ -34,11 +86,21 @@ async def update(self, *, device_id, property_name, value=None, element=None):
# this argument shall be removed
assert element is None
# update context manager: currently here as the async io comm stops at first exception
with UpdateContext(element_id=device_id, property_name=property_name, value=value, element=element,
kwargs=dict()):
with UpdateContext(
element_id=device_id,
property_name=property_name,
value=value,
element=element,
kwargs=dict(),
):
cmds = self.command_rewritter.inverse(
Command(id=device_id, property=property_name, value=value, behaviour_on_error=BehaviourOnError.stop
))
Command(
id=device_id,
property=property_name,
value=value,
behaviour_on_error=BehaviourOnError.stop,
)
)
cmds
for cmd in cmds:
# Todo: simplify the code down here ...
Expand Down
5 changes: 4 additions & 1 deletion src/dt4acc/core/interfaces/accelerator_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,14 @@ class AcceleratorInterface(metaclass=ABCMeta):
Todo:
Derive from a list interface
"""

@abstractmethod
def get_element(self, element_id) -> ElementInterface:
"""
Review if derived classes use async implementations
"""
pass

@abstractmethod
def __init__(self, *args, **kwargs):
pass

2 changes: 2 additions & 0 deletions src/dt4acc/custom_epics/ioc/handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
liasion_manager=lm,
translation_service=tm
),
liaison_manager=lm,
translator_service=tm,
acc_mgr=setup_accelerator()
)

Expand Down
Loading

0 comments on commit 5700e1f

Please # to comment.