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.

Loading the lattice

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]:
{'live': ['energy',
  'tune_y',
  'tune_x',
  's_position',
  'emittance_x',
  'emittance_y',
  'beam_current']}

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]:
3000000000.0

In [5]:
lattice.get_value("beam_current")


Out[5]:
296.6981619696345

Families, elements and fields

The elements in the lattice are grouped by families, and this is the most common way to choose some to access. We can list the available families:


In [6]:
lattice.get_all_families()


Out[6]:
{'AP',
 'BB',
 'BBVMXL',
 'BBVMXS',
 'BEND',
 'BPM',
 'BPM10',
 'BUMP',
 'BUMPSS',
 'D054BA',
 'D054BAL',
 'D09_1',
 'D09_10',
 'D09_12',
 'D09_13',
 'D09_14',
 'D09_2',
 'D09_3',
 'D09_5',
 'D09_6',
 'D09_7',
 'D09_8',
 'D09_9',
 'D104BA0',
 'D104BA0R',
 'D104BA1',
 'D104BAA',
 'D104BAB',
 'D104BAC',
 'D104BAD',
 'D104BAML',
 'D104BAMR',
 'D104BAR',
 'D10_1',
 'D10_2',
 'D10_3',
 'D10_4',
 'D10_5',
 'D10_6',
 'D10_7',
 'D10_8',
 'D10_9',
 'D13_1',
 'D13_10',
 'D13_12',
 'D13_13',
 'D13_14',
 'D13_2',
 'D13_3',
 'D13_5',
 'D13_6',
 'D13_7',
 'D13_8',
 'D13_9',
 'D154BAL',
 'D154BAR',
 'D1A',
 'D1Aa',
 'D1Ab',
 'D1D2',
 'D1M4BA',
 'D1M4BAL1',
 'D1M4BAL2',
 'D1S4BA',
 'D1X',
 'D1Y',
 'D1YAD1YA',
 'D1YB',
 'D20_1',
 'D20_2',
 'D2A',
 'D2B',
 'D2B4BA',
 'D2XL',
 'D2XR',
 'D2YA',
 'D2YB',
 'D2YC',
 'D3A',
 'D3Aa',
 'D3Ab',
 'D3B',
 'D3B4BA',
 'D3B4BAR',
 'D4A',
 'D4Aa',
 'D4Ab',
 'D4B',
 'D4B4BA0',
 'D4Ba',
 'D4Bb',
 'D5B',
 'D6B',
 'D6B4BA0',
 'D6Ba',
 'D6Bb',
 'DBPM',
 'DHS4BA',
 'DHS4BAL1',
 'DHS4BAL2',
 'DHS4BAR1',
 'DHS4BAR2',
 'DHS4BAR3',
 'DI05',
 'DK4BAS20',
 'DK4BAS21',
 'DK4BAS22',
 'DK4BAS23',
 'DLB4BAL',
 'DLB4BAR',
 'DLBM4BA0',
 'DLBM4BA1',
 'DLBM4BAL1',
 'DLBM4BAL2',
 'DMID4BAL1',
 'DMID4BAL2',
 'DMID4BAR1',
 'DMID4BAR2',
 'DRBM4BA2',
 'DRBM4BAL1',
 'DRBM4BAL2',
 'DRBM4BAR1',
 'DRBM4BAR2',
 'DRIFT',
 'DRIFT_DRIFT_S2A',
 'DRIFT_S2A',
 'DSEXT',
 'Drift',
 'HCHICA',
 'HSTR',
 'HTRIM',
 'HU64',
 'KD1',
 'MPW12',
 'MPW15',
 'MPW45',
 'PAD',
 'Q1AB',
 'Q1AD',
 'Q1B',
 'Q1BE',
 'Q1D',
 'Q2AB',
 'Q2AD',
 'Q2B',
 'Q2BE',
 'Q2D',
 'Q3B',
 'Q3D',
 'Q3E',
 'Q4E',
 'QM09',
 'QM13',
 'QUAD',
 'RF',
 'S1A',
 'S1B',
 'S1BE',
 'S1C',
 'S1D',
 'S2A',
 'S2B',
 'S2BE',
 'S2C',
 'S2D',
 'S3E',
 'S4E',
 'SEXT',
 'SPACER',
 'SQUAD',
 'TEMPDRIFT',
 'U21',
 'U23a',
 'U23b',
 'U27',
 'VSTR',
 'VTRIM',
 'shim',
 'source'}

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)))


Got 173 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]:
{'live': ['y_sofb_disabled',
  'enabled',
  'y',
  'x',
  'x_fofb_disabled',
  'x_sofb_disabled',
  'y_fofb_disabled']}

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]:
0.047219

Devices

Each field has a device object associated with it, which knows how to set and get the value.


In [10]:
one_bpm.get_device("x")


Out[10]:
<pytac.device.EpicsDevice at 0x7f4290a71f10>

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)


('SR01C-PC-HBPM-01:SLOW:DISABLED', 'SR01C-PC-HBPM-01:SLOW:DISABLED')

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)


Device SR01C-DI-EBPM-01 has no setpoint PV.

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]:
['SR01C-DI-EBPM-01:SA:Y',
 'SR01C-DI-EBPM-02:SA:Y',
 'SR01C-DI-EBPM-03:SA:Y',
 'SR01C-DI-EBPM-04:SA:Y',
 'SR01C-DI-EBPM-05:SA:Y',
 'SR01C-DI-EBPM-06:SA:Y',
 'SR01C-DI-EBPM-07:SA:Y',
 'SR02C-DI-EBPM-01:SA:Y',
 'SR02C-DI-EBPM-02:SA:Y',
 'SR02C-DI-EBPM-03:SA:Y']

Unit conversions

Many fields can be represented in either engineering units or physics units. For example, for a magnet field, the physics unit would be the field strength and the engineering unit would be the current applied by the magnet power supply controller.


In [14]:
# Get a corrector magnet
corrector = lattice.get_elements("HSTR")[5]
# Request
corrector.get_value("x_kick", units=pytac.ENG)


Out[14]:
-3.0552401542663574

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]:
'A'

Magnet fields

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)


('Fields:', {'live': ['h_fofb_disabled', 'h_sofb_disabled', 'v_fofb_disabled', 'a1', 'x_kick', 'v_sofb_disabled', 'b2', 'y_kick']})
('Families:', set(['S4E', 'SQUAD', 'SEXT', 'VSTR', 'HSTR']))

Other methods of the 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]:
[-0.24839822947978973,
 0.7639292478561401,
 -0.4572945237159729,
 -0.1370551735162735,
 0.6560376882553101,
 -3.0552401542663574,
 3.0576119422912598,
 0.6859914660453796,
 -0.8835821747779846,
 0.37336450815200806,
 -0.397186279296875,
 -0.3592968285083771,
 1.5479310750961304,
 -0.2497788667678833,
 -0.3833305537700653,
 0.04267336428165436,
 0.387008398771286,
 2.083509922027588,
 -2.213555335998535,
 2.316075086593628,
 -1.2140284776687622,
 0.4225691556930542,
 -0.3863433301448822,
 0.1559593677520752,
 2.3147804737091064,
 -0.2917431592941284,
 -1.003007173538208,
 0.5435793995857239,
 -0.21308128535747528,
 0.30097678303718567,
 0.42939627170562744,
 2.6342201232910156,
 -3.1828463077545166,
 1.8283201456069946,
 -0.6628888845443726,
 0.1967700868844986,
 -0.15570154786109924,
 -0.08124255388975143,
 -0.09838544577360153,
 1.190570592880249,
 -0.9167197346687317,
 -1.0333651304244995,
 0.20847293734550476,
 -0.26976802945137024,
 0.3439919054508209,
 1.0685083866119385,
 -0.26988404989242554,
 -0.2225686013698578,
 0.48204877972602844,
 0.4525769352912903,
 -0.32029491662979126,
 0.011995009146630764,
 -0.072536900639534,
 -0.7957542538642883,
 1.8488221168518066,
 -1.8276989459991455,
 2.074706554412842,
 -0.6961939930915833,
 1.660825252532959,
 2.037513256072998,
 -1.9940599203109741,
 1.2870264053344727,
 1.1285380125045776,
 -3.115408420562744,
 -0.6366449594497681,
 0.6396395564079285,
 0.1271335333585739,
 0.5475709438323975,
 -1.7670093774795532,
 1.6561113595962524,
 -1.3666211366653442,
 -1.5977840423583984,
 0.5970470905303955,
 0.5569626688957214,
 -0.061939679086208344,
 -2.2024006843566895,
 1.2349647283554077,
 -1.1097846031188965,
 0.21210838854312897,
 -0.9944986701011658,
 1.2641316652297974,
 -0.4769899547100067,
 0.9592297673225403,
 -1.6858210563659668,
 -0.542770266532898,
 0.5003483891487122,
 0.20877952873706818,
 -6.958314418792725,
 1.8476916551589966,
 -1.3825221061706543,
 -0.3401913046836853,
 0.03160438686609268,
 1.2040449380874634,
 -1.0675880908966064,
 1.0701864957809448,
 0.6834375858306885,
 -1.2668933868408203,
 -0.07783783972263336,
 -0.39933741092681885,
 1.5456103086471558,
 -0.3358473777770996,
 0.1989932656288147,
 -0.20524908602237701,
 0.1039108857512474,
 0.5607045292854309,
 -0.6076611280441284,
 0.6814919114112854,
 -1.242967128753662,
 0.1817963421344757,
 -0.6134481430053711,
 1.304316759109497,
 -1.8899571895599365,
 3.616405963897705,
 -4.558992385864258,
 0.129770427942276,
 0.07601836323738098,
 0.10492631047964096,
 0.4832989573478699,
 0.9280765652656555,
 -2.44914174079895,
 2.768791913986206,
 -1.5745543241500854,
 0.8972594141960144,
 -0.5179362297058105,
 0.3355884850025177,
 -1.5213912725448608,
 1.8749045133590698,
 -1.0240871906280518,
 -0.010050266981124878,
 -0.20131799578666687,
 0.532402515411377,
 -0.4071364104747772,
 -2.65728759765625,
 3.989853858947754,
 -3.6086249351501465,
 -0.3485717475414276,
 0.2619762420654297,
 0.13016314804553986,
 0.08971194922924042,
 0.22305065393447876,
 -0.17202238738536835,
 0.0707668885588646,
 -0.03778775408864021,
 -0.031766898930072784,
 0.14996331930160522,
 0.04967852309346199,
 -2.149498701095581,
 1.9415940046310425,
 -1.6292657852172852,
 -0.6792949438095093,
 0.6019706726074219,
 0.510526180267334,
 -0.009603044018149376,
 -2.642376184463501,
 2.5861854553222656,
 -3.7314720153808594,
 -0.18924900889396667,
 0.4994497001171112,
 0.33791583776474,
 -0.35369110107421875,
 -1.1990420818328857,
 2.3036105632781982,
 -2.647069215774536,
 -0.655612587928772,
 0.9581286311149597,
 0.14957129955291748,
 -1.030884027481079,
 2.295074939727783,
 -0.5442541241645813,
 -1.0026730298995972,
 0.33420810103416443,
 -0.2033674269914627]

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]:
[4.38,
 8.806500000000002,
 11.374000000000002,
 12.559000000000005,
 14.942500000000006,
 18.005000000000003,
 21.270000000000003,
 26.93,
 30.360759,
 32.076129]

In [ ]: