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 fields associated with magnet elements 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)
    
    
latticeTo 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 [ ]: