Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
115 changes: 106 additions & 9 deletions pyaml/accelerator.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

from .arrays.array import ArrayConfig
from .common.element import Element, __pyaml_repr__
from .common.exception import PyAMLConfigException
from .common.exception import PyAMLConfigException, PyAMLException
from .common.holders.element_holder import ElementHolder
from .configuration import ConfigurationManager, UnsupportedConfigurationRootError
from .configuration.factory import Factory
Expand Down Expand Up @@ -73,6 +73,8 @@ class Accelerator(DynamicValidation):
Set the number of bucket.
add_device(config, ignore_external=False)
Dynamically add a device to this accelerator.
add(element)
Bind and register an element or tool in every mode.
post_init()
Method triggered after all initialisations are done
get_description()
Expand Down Expand Up @@ -208,6 +210,7 @@ def set_energy(self, E: float):
Energy value to set in eV
"""
self._set_properties("_set_energy", E)
self._energy = E

def set_mcf(self, alphac: float):
"""
Expand All @@ -219,6 +222,7 @@ def set_mcf(self, alphac: float):
Moment compaction factor
"""
self._set_properties("_set_mcf", alphac)
self._alphac = alphac

def set_harmonic_number(self, h: int):
"""
Expand All @@ -230,6 +234,106 @@ def set_harmonic_number(self, h: int):
Number of bucket
"""
self._set_properties("_set_harmonic", h)
self._harmonic_number = h

def add(self, element: Element) -> None:
"""
Bind and register an element or tool in every mode.

Use the same backend binding as configured devices, including any
backend-specific mode restrictions. Newly registered instances receive
the current accelerator properties and run their post-initialization
hooks. Existing elements are not reinitialized. If binding or
initialization fails, all holder element stores are restored to
their previous state.

Parameters
----------
element : Element
Element to add, including tuning and measurement tools, magnets,
BPMs, RF plants, and diagnostics.

Raises
------
TypeError
If the supplied object is not an Element.
PyAMLException
If the element's name is already registered in any mode, or
backend binding or initialization fails. Binding and initialization
errors include the element and mode names and preserve the original
exception as their cause.

Notes
-----
Binding normally creates a separate attached instance in each mode,
leaving the supplied element reusable. Access these instances through
their mode's holders or by name, for example ``sr.design["ALTUNE"]``.

Examples
--------
.. code-block:: python

tune = Tune(
"ALTUNE",
quad_array_name="QCORR",
response_matrix="respmat.json",
betatron_tune_name="BETATRON_TUNE2",
)
sr.add(tune)
print(sr.design.tool.get("ALTUNE").tune_monitor)
print(sr.live.tool.get("ALTUNE").tune_monitor)
"""
if not isinstance(element, Element):
raise TypeError(f"Accelerator.add() expects an Element, got {type(element).__name__}")

holders = (*self._simulators.values(), *self._controls.values())
for holder in holders:
if element.name in holder._ALL:
raise PyAMLException(f"Duplicate element {element.name} in mode {holder.name()}")

snapshots = [
(store, store.copy())
for holder in holders
for store in (
holder._ALL,
holder._MAGNETS,
holder._CFM_MAGNETS,
holder._SERIALIZED_MAGNETS,
holder._BPMS,
holder._RFPLANT,
holder._RFTRANSMITTER,
holder._DIAG,
holder._TOOLS,
)
]
added: list[tuple[ElementHolder, Element]] = []
try:
for holder in holders:
mode_name = holder.name()
existing_names = set(holder._ALL)
holder.fill_device([element])
added.extend((holder, instance) for name, instance in holder._ALL.items() if name not in existing_names)

for holder, instance in added:
mode_name = holder.name()
if self._energy is not None:
instance.set_energy(self._energy)
if self._alphac is not None:
instance.set_mcf(self._alphac)
if self._harmonic_number is not None:
instance.set_harmonic(self._harmonic_number)
for holder, instance in added:
mode_name = holder.name()
instance.post_init()
except Exception as err:
for store, snapshot in snapshots:
store.clear()
store.update(snapshot)
raise PyAMLException(f"Failed to add element {element.name} in mode {mode_name}: {err}") from err

if self._devices is None:
self._devices = []
self._devices.append(element)

def add_device(self, config: dict, ignore_external=False):
"""
Expand All @@ -250,14 +354,7 @@ def add_device(self, config: dict, ignore_external=False):
"Invalid device type, Element or sub classes of Element expected " + f"but got {dev.__class__.__name__}"
)

self._devices.append(dev)
if self._controls is not None:
for c in self._controls:
c.fill_device([dev])

if self._simulators is not None:
for s in self._simulators:
s.fill_device([dev])
self.add(dev)

def post_init(self):
"""
Expand Down
Loading
Loading