In this tutorial we will go through some of the most common ways of using pytac. The aim is to give you an understanding of the interface and how to find out what is available.
The central object in pytac is the lattice
. It holds the information about all of the elements in the accelerator.
All the data about the lattice and its elements is stored in CSV files inside the pytac repository. We use the load_csv
module to load the data and initialise a lattice
object; this is the normal starting point for using pytac.
The "ring mode" describes one configuration of the elements in the lattice. There is one set of CSV files for each ring mode. So when we load the lattice, we specify the ring mode we want to load.
At the time of writing the normal ring mode in use at Diamond is "DIAD", so let's load that.
First some required imports.
In [1]:
import sys, os
# Make the pytac package available from this subdirectory
sys.path.append(os.path.join(os.getcwd(), '..'))
import pytac
Initialize the DIAD mode. The import of the Cothread channel access library will allow us to get some live values from the Diamond accelerators.
In [2]:
import cothread
lattice = pytac.load_csv.load('DIAD')
The lattice object itself has some fields with its own properties:
In [3]:
lattice.get_fields()
Out[3]:
The name "live" is referring to the data source - Pytac can also be set up with additional data sources for simulation, but that isn't described here.
We can ask for the values of these fields. These commands will try to get the real values from the live machine (so won't work if you're not on a suitable Diamond network).
In [4]:
lattice.get_value("energy")
Out[4]:
In [5]:
lattice.get_value("beam_current")
Out[5]:
In [6]:
lattice.get_all_families()
Out[6]:
Let's get all the beam position monitors (BPMs). We do this by using get_elements which takes an argument for family name - in this case we use the family name "BPM".
In [7]:
bpms = lattice.get_elements('BPM')
print("Got {} BPMs".format(len(bpms)))
Let's look at what we can find out about a single BPM.
Each one has some fields:
In [8]:
one_bpm = bpms[0]
one_bpm.get_fields()
Out[8]:
The fields represent a property of the BPM that can change. For example, x and y are the measured positions.
In [9]:
one_bpm.get_value("x")
Out[9]:
In [10]:
one_bpm.get_device("x")
Out[10]:
The device
object knows the PV names for reading and writing the value of the field. Each field might have a "setpoint" or "readback" handle, which could be associated with different PV names.
You can use either strings or pytac constants to specify which handle to use.
In [11]:
readback_pv = one_bpm.get_pv_name("x_sofb_disabled", "readback")
same_readback_pv = one_bpm.get_pv_name("x_sofb_disabled", pytac.RB)
print(readback_pv, same_readback_pv)
Some fields are read-only, in which case there is no setpoint PV to get.
In [12]:
try:
one_bpm.get_pv_name("x_sofb_disabled", pytac.SP)
except Exception as e:
print(e)
It's not normally necessary to interact with the device
directly; you can do most things through methods of the element
or lattice
. E.g. element.get_value() above and lattice.get_element_pv_names
:
In [13]:
lattice.get_element_pv_names('BPM', 'y', 'readback')[:10]
Out[13]:
In [14]:
# Get a corrector magnet
corrector = lattice.get_elements("HSTR")[5]
# Request
corrector.get_value("x_kick", units=pytac.ENG)
Out[14]:
In order to get the unit itslef, we have to ask for the unitconv
object associated with the field.
In [15]:
corrector.get_unitconv("x_kick").eng_units
Out[15]:
This seems like a good time to talk about the names for the magnetic fields of magnets.
In accelerator physics we refer to the different components of magnetic fields as $a_n$ for vertical fields and $b_n$ for horizontal fields, where n is:
n | Field |
---|---|
0 | Dipole |
1 | Quadrupole |
2 | Sextupole |
... | ... |
These names are used for the field
s associated with magnet element
s in pytac.
For corrector magnets, although the corrector field acts like a dipole, it is given the name x_kick
or y_kick
so that it can be easily distinguished. An example of this is when several magnets are combined into the same element
. The following example shows an element which combines a corrector, a skew quadrupole and a sextupole.
In [16]:
an_element = lattice.get_elements("HSTR")[12]
print("Fields:", an_element.get_fields())
print("Families:", an_element.families)
lattice
To finish off for now, let's look at some more of the methods of the lattice
lattice.get_element_values
lets you get all the live values for a field from a while family of elements. E.g. the currents for the horizontal corrector magnets. There is also an analogous command lattice.set_element_values()
.
In [17]:
lattice.get_element_values("HSTR", "x_kick", "readback")
Out[17]:
s
position is the position of an element in metres around the ring.
There is a method to get the s
positions of all elements in a family:
In [18]:
lattice.get_family_s("BPM")[:10]
Out[18]:
In [ ]: