Create and Load Configuration#

This guide shows how to write a pyAML configuration and load it into an Accelerator using a minimal example.

Tip

Read Configuration Structure and Syntax which explains the concepts and ideas behind the configuration before you start.

The configuration can be written as a text file in YAML or JSON or as a dictionary.

Note

Tools are available to help writing the configuration. See Tools That Help Writing the Configuration for the options.

The Rule to Remember#

The configuration consists of a set of items which tells pyAML which objects to build when loading the configuration.

Important

Each item of the configuration names a Python class in its class field. Every other field is an argument of the constructor of that class with the same name as the field and the value to pass to the constructor. When a constructor argument is an object, its value in the configuration is a nested item with its own class field.

Writing and loading the configuration is the equivalent of writing the Python code that creates the objects yourself. The configuration just allows pyAML to create the objects for you.

Finding the Accepted Fields#

Before writing an item, look up the constructor parameters of the class you want pyAML to build an object of. This can be done in several ways:

  • Read the API documentation of the class

  • Use help() in Python since this shows the signature of the constructor and description of each parameter

For example:

from pyaml.bpm.bpm import BPM

help(BPM)
# class BPM(...)
#  |  BPM(name: str, lattice_names: str | None = None, description: str | None = None,
#  |      x_pos: str | None = None, y_pos: str | None = None, ...)
#  |
#  |  Parameters
#  |  ----------
#  |  name : str
#  |      Name of the BPM.
#  |  x_pos : str | None, optional
#  |      Device catalog key for the horizontal beam position.
#  |  ...

Parameters with a default value is optional and can be left out of the configuration if you wish.

Write Configuration as a Text File#

Here an example is shown for how to create the configuration in a YAML file. The steps are similar if using JSON. The steps below build a small but complete configuration.

Create a file, for example accelerator.yaml, with any text editor. If you want the editor to suggest the fields, you can use VS Code together with a JSON Schema. See Use JSON Schema in VS Code for instructions.

1. The Accelerator#

The root item is the Accelerator. Its required arguments are facility, machine and energy:

class: pyaml.accelerator.Accelerator
facility: my_facility
machine: storage_ring
energy: 1.0e9

2. The Control Modes#

Add the control modes as lists in simulators and controls. Their name is also the name used to access them, for example accelerator.design, accelerator.live etc.

A simulator needs the path to a lattice file. All formats that can be loaded by pyAT works. If you use the JSON format, you need to use the ${path:...} resolver to avoid the lattice being loaded as if it was a configuration file.

simulators:
  - class: pyaml.lattice.simulator.Simulator
    name: design
    lattice: ${path:lattice.json}

A control system is given by the class of the bindings you use. The arguments depend on the bindings and catalog type you decide to use. The catalog describes how the keys used by the devices map to control-system signals. See Control System Catalogs for the different types of catalogs.

For example for pyaml-cs-oa using a dynamic catalog for TANGO:

controls:
  - class: pyaml_cs_oa.controlsystem.OphydAsyncControlSystem
    name: live
    backend: tango

If you only want to use the simulator, you can leave out controls entirely.

3. The Devices#

Add the elements of the machine in devices. For a magnet, the model argument is an object (the magnet model, which also handles the unit conversion), so it is written as a nested item.

The strings given to physics, x_pos and y_pos are keys looked up in the catalog of the control system.

devices:
  - class: pyaml.magnet.quadrupole.Quadrupole
    name: QF_001
    model:
      class: pyaml.magnet.identity_model.IdentityMagnetModel
      unit: 1/m
      physics: AN01-AR/EM-QP/QF.01/magnetic_strength
  - class: pyaml.magnet.quadrupole.Quadrupole
    name: QD_001
    model:
      class: pyaml.magnet.identity_model.IdentityMagnetModel
      unit: 1/m
      physics: AN01-AR/EM-QP/QD.01/magnetic_strength
  - class: pyaml.bpm.bpm.BPM
    name: BPM_001
    x_pos: AN01-AR/DG-EPOS/BPM.01/x
    y_pos: AN01-AR/DG-EPOS/BPM.01/y

By default, the name of an element is also the name of the element in the lattice of the simulator. If you want to use a different name in pyAML, use lattice_names to map between pyAML and the lattice.

4. The Arrays#

Group elements in named arrays in arrays. Element names can contain wildcards:

arrays:
  - class: pyaml.arrays.magnet.Magnet
    name: Quadrupoles
    elements:
      - QF_001
      - QD_001
  - class: pyaml.arrays.bpm.BPM
    name: BPMs
    elements:
      - BPM_*

Complete File#

Putting it all together:

class: pyaml.accelerator.Accelerator
facility: my_facility
machine: storage_ring
energy: 1.0e9
simulators:
  - class: pyaml.lattice.simulator.Simulator
    name: design
    lattice: ${path:lattice.json}
controls:
  - class: pyaml_cs_oa.controlsystem.OphydAsyncControlSystem
    name: live
    backend: tango
devices:
  - class: pyaml.magnet.quadrupole.Quadrupole
    name: QF_001
    model:
      class: pyaml.magnet.identity_model.IdentityMagnetModel
      unit: 1/m
      physics: AN01-AR/EM-QP/QF.01/magnetic_strength
  - class: pyaml.magnet.quadrupole.Quadrupole
    name: QD_001
    model:
      class: pyaml.magnet.identity_model.IdentityMagnetModel
      unit: 1/m
      physics: AN01-AR/EM-QP/QD.01/magnetic_strength
  - class: pyaml.bpm.bpm.BPM
    name: BPM_001
    x_pos: AN01-AR/DG-EPOS/BPM.01/x
    y_pos: AN01-AR/DG-EPOS/BPM.01/y
arrays:
  - class: pyaml.arrays.magnet.Magnet
    name: Quadrupoles
    elements:
      - QF_001
      - QD_001
  - class: pyaml.arrays.bpm.BPM
    name: BPMs
    elements:
      - BPM_*

Split the Configuration into Several Files#

For a real machine the configuration becomes long. When the configuration is loaded from a file, any string value ending with .yaml, .yml or .json is replaced by the content of that file. Inside a list, if the file contains a list, its items are added to the parent list. For example, move the quadrupoles to devices/quadrupoles.yaml:

# devices/quadrupoles.yaml
- class: pyaml.magnet.quadrupole.Quadrupole
  name: QF_001
  model:
    class: pyaml.magnet.identity_model.IdentityMagnetModel
    unit: 1/m
    physics: AN01-AR/EM-QP/QF.01/magnetic_strength
- class: pyaml.magnet.quadrupole.Quadrupole
  name: QD_001
  # ...

and refer to it from the main file:

devices:
  - devices/quadrupoles.yaml
  # ...

Values can also come from environment variables with ${env:NAME}. See Resolvers for all options.

Use Your Own Classes#

The configuration is not limited to pyAML classes. Any class that can be imported can be used in the configuration, for example a magnet model specific to your facility:

# my_facility/models.py
from pyaml.magnet.model import MagnetModel

class MyMagnetModel(MagnetModel):
    def __init__(self, power_supply: str, calibration: float):
        ...
model:
  class: my_facility.models.MyMagnetModel
  power_supply: PS/QF/01
  calibration: 1.02

Load the Configuration#

Set the configuration root, which is the directory used to resolve relative paths, then load the file with Accelerator.load().

from pyaml.configuration import ROOT
from pyaml.accelerator import Accelerator

ROOT.set("/path/to/configuration")
accelerator = Accelerator.load("accelerator.yaml")

accelerator.design.magnets.get("Quadrupoles").strengths.get()

If the control-system bindings are not installed or you only want to use the simulator, add ignore_external=True and the controls section is skipped without having to remove it from the configuration.

The configuration can also be loaded as a nested dictionary with Accelerator.from_dict(). This also allows to write the configuration directly as a dictionary instead of a text file if you prefer.

To validate the whole configuration before any object is created, add validate=True. See Validate Configuration for details.

See the API documentation for the Accelerator for all options.