Live/Design: control modes#
With pyAML it is possible to use the same commands either:
live: on the real accelerator
design: in simulations
errors: in simulations with errors (and for several seeds)
live (with a different prefix/host): in a virtual control system (same as in point 1. for those laboratories that have a simulated control system)
Those are called “control modes” and may be used
at a global script level (for testing in simulations and then using in real accelerator measurements with a simple switch/comment)
anywhere in the script
Using pyAML on a real accelerator
import os
import numpy as np
from pyaml.accelerator import Accelerator
instantiate a pyAML accelerator
SR: Accelerator = Accelerator.load("../../tests/config/EBSTune.yaml")
---------------------------------------------------------------------------
PyAMLConfigException Traceback (most recent call last)
Cell In[2], line 1
----> 1 SR: Accelerator = Accelerator.load("../../tests/config/EBSTune.yaml")
File ~/checkouts/readthedocs.org/user_builds/pyaml/envs/latest/lib/python3.13/site-packages/pyaml/accelerator.py:304, in Accelerator.load(filename, include_locations, ignore_external)
299 except UnsupportedConfigurationRootError as ex:
300 raise PyAMLConfigException(
301 "Accelerator.load() expects a 'pyaml.accelerator' root configuration. "
302 "Use the factory APIs to build sub-elements directly."
303 ) from ex
--> 304 return manager.build(ignore_external=ignore_external)
File ~/checkouts/readthedocs.org/user_builds/pyaml/envs/latest/lib/python3.13/site-packages/pyaml/configuration/manager.py:496, in ConfigurationManager.build(self, ignore_external)
494 ROOT.set(self._build_root)
495 snapshot = ConfigurationManager.strip_runtime_internal_metadata(self._snapshot(include_internal_metadata=True))
--> 496 return Accelerator.from_dict(snapshot, ignore_external=ignore_external)
File ~/checkouts/readthedocs.org/user_builds/pyaml/envs/latest/lib/python3.13/site-packages/pyaml/accelerator.py:275, in Accelerator.from_dict(config_dict, ignore_external)
273 # Ensure factory is clean before building a new accelerator
274 Factory.clear()
--> 275 return Factory.build(config_dict, ignore_external)
File ~/checkouts/readthedocs.org/user_builds/pyaml/envs/latest/lib/python3.13/site-packages/pyaml/configuration/factory.py:557, in PyAMLFactory.build(self, data, ignore_external)
554 return self._build_list(data, ignore_external)
556 elif isinstance(data, dict):
--> 557 data = self._build_dict(data, ignore_external)
558 if data is None:
559 return None
File ~/checkouts/readthedocs.org/user_builds/pyaml/envs/latest/lib/python3.13/site-packages/pyaml/configuration/factory.py:401, in PyAMLFactory._build_dict(self, data, ignore_external)
398 # Do not recurse dict defined in ConfigModel
399 # pydantic use TypedDict not usable with isinstance
400 if not self._is_plain_dict_type(field_type):
--> 401 result[key] = self.build(value, ignore_external)
403 return result
File ~/checkouts/readthedocs.org/user_builds/pyaml/envs/latest/lib/python3.13/site-packages/pyaml/configuration/factory.py:554, in PyAMLFactory.build(self, data, ignore_external)
551 raise PyAMLConfigException(f"Unexpected element found. 'dict' or 'list' expected but got '{type(data).__name__}'")
553 if isinstance(data, list):
--> 554 return self._build_list(data, ignore_external)
556 elif isinstance(data, dict):
557 data = self._build_dict(data, ignore_external)
File ~/checkouts/readthedocs.org/user_builds/pyaml/envs/latest/lib/python3.13/site-packages/pyaml/configuration/factory.py:362, in PyAMLFactory._build_list(self, items, ignore_external)
357 def _build_list(self, items, ignore_external: bool):
358 """
359 Recursively build all nested objects contained in a list.
360 """
--> 362 return [self.build(item, ignore_external) if isinstance(item, (dict, list)) else item for item in items]
File ~/checkouts/readthedocs.org/user_builds/pyaml/envs/latest/lib/python3.13/site-packages/pyaml/configuration/factory.py:557, in PyAMLFactory.build(self, data, ignore_external)
554 return self._build_list(data, ignore_external)
556 elif isinstance(data, dict):
--> 557 data = self._build_dict(data, ignore_external)
558 if data is None:
559 return None
File ~/checkouts/readthedocs.org/user_builds/pyaml/envs/latest/lib/python3.13/site-packages/pyaml/configuration/factory.py:382, in PyAMLFactory._build_dict(self, data, ignore_external)
373 def _build_dict(self, data: dict, ignore_external: bool):
374 """
375 Recursively build nested objects referenced by a configuration
376 dictionary.
(...) 379 recursive build process and are left unchanged.
380 """
--> 382 build_info = resolve_build_info(data, ignore_external)
384 if build_info is None:
385 return None
File ~/checkouts/readthedocs.org/user_builds/pyaml/envs/latest/lib/python3.13/site-packages/pyaml/configuration/factory.py:238, in resolve_build_info(data, ignore_external)
236 if ignore_external:
237 return None
--> 238 raise PyAMLConfigException(
239 f"Module referenced in {module_str} cannot be found: {module_ex} {location_str} while"
240 ) from None
242 # Get the object class name
243 if class_str is None:
PyAMLConfigException: Module referenced in tango.pyaml.controlsystem cannot be found: No module named 'tango' while
Switch between live and design
Access some magnet families defined in the configuration is simple. One can use sr.live for live control system or sr.design for the simulator part. They provide identical interface.
sr = SR.design # simulations
# sr = SR.live # act on real accelerator
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
Cell In[3], line 1
----> 1 sr = SR.design # simulations
2 # sr = SR.live # act on real accelerator
NameError: name 'SR' is not defined
quadForTune = sr.get_magnets("QForTune")
tune_device = sr.get_betatron_tune_monitor("BETATRON_TUNE")
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
Cell In[4], line 1
----> 1 quadForTune = sr.get_magnets("QForTune")
2
3 tune_device = sr.get_betatron_tune_monitor("BETATRON_TUNE")
NameError: name 'sr' is not defined
For example a tune response matrix is evaluated below. This script is used identically for computation of theoretical values or to measure the response directly on the accelerator.
# Build tune response matrix
initial_tune = tune_device.tune.get()
print(f"Tune via pyAML interface for design mode {initial_tune}")
tunemat = np.zeros((len(quadForTune), 2))
for idx, m in enumerate(quadForTune):
str = m.strength.get()
m.strength.set(str + 1e-4)
dq = tune_device.tune.get() - initial_tune
tunemat[idx] = dq * 1e4
m.strength.set(str)
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
Cell In[5], line 2
1 # Build tune response matrix
----> 2 initial_tune = tune_device.tune.get()
3 print(f"Tune via pyAML interface for design mode {initial_tune}")
4 tunemat = np.zeros((len(quadForTune), 2))
5
NameError: name 'tune_device' is not defined
At any point in the code it is always possible to still access any of the control modes
SR.design.get_lattice().disable_6d()
tune_design = SR.design.get_lattice().get_tune()
print(f"Tune directly from the lattice {tune_design}")
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
Cell In[6], line 1
----> 1 SR.design.get_lattice().disable_6d()
2 tune_design = SR.design.get_lattice().get_tune()
3
4 print(f"Tune directly from the lattice {tune_design}")
NameError: name 'SR' is not defined