Epics Applications using PyEpics

Table Of Contents

Previous topic

Using StepScan

Next topic

Using the StepScan GUI

This Page

Spec-like Scans with StepScan

Spec is a commonly used program for collecting data at synchrotrons, as it can not only communicate with Motors and Detectors, but can also work in the geometries needed to run diffractometers, and includes a convenient macro language. Spec is not without limitations, and a reasonable goal of StepScan would be to replace some uses of Spec. Of course, Python can replace the macro language, and Epics Channel Access can control Motor and Detectors.

To better enable a transition of some scanning away from Spec and toward Python, StepScan has a spec_emulator module that provides Spec-like functionality, with scanning methods ascan(), dscan(), a2scan(), d2scan(), lup(), and mesh() that closely match the Spec routines of the same name.

Setting up and Configuring the spec emulation layer

To emulate Spec, one creates a SpecScan object:

>>> from epicsapps.stepscan import SpecScan
>>> spec = SpecScan()

This creates an empty SpecScan object, which does not have any configuration information about Motors and Detectors to use during a scan. Motors can be added to the configuration with:

>>> spec.add_motors(x='XXX:m1', y='XXX:m2', theta='XXX:m3')

where you can add as many label=PV_NAME keyword arguments as you like. Later on, when scanning, you'll use the labels to mean the underlying PVs. As with all of StepScan, the PVs are not required to be motors.

We'll also need to add some detectors. StepScan supports many kinds of detectors, but for simple scans with point-detectors, an Epics Scaler record usually suffices, so we'll start with that:

>>> spec.add_detector('XXX:scaler1', kind='scaler', nchan=8, use_calc=False)

By saying kind='scaler', StepScan will know what to use for a Trigger, how to set the dwell time, and what Counters to count -- all the channels 1 through 8 that have a non-blank name will be collected. There is also the option to set use_calc=True to use the calc record associated with the Scan Record, which can be used to support offset values and simple calculations.

As with other parts of StepScan, we can add extra PVs -- values to be recorded at the start of each scan, with a list of (description, pvname) tuples:

>>> spec.add_extra_pvs((('Ring Current', 'S:SRcurrentAI.VAL'),
                        ('Ring Lifetime', 'S:SRlifeTimeHrsCC.VAL')))

We haven't yet set the name of the output file. This is still a work-in-progress, but currently, the Spec file is not emulated in detail, but an ASCII file is written to for each scan. We can change this anytime with:

>>> spec.filename = 'myoutput.dat'

At this point, we have a fully configured (if minimal) Spec emulator, and can start running scans. It is likely

Using the spec emulation layer

To perform a simple absolute scan, we can use ascan(), which would be as simple as:

>>> spec.ascan('x', 0, 0.5, 51, 0.5)

which scans Motor 'x' between 0 and 0.5 in 21 steps, counting for 0.5 second per point.

Other supported scan routines are dscan(), which is like ascan() but with start and stop positions relative to the current position:

>>> spec.dscan('x', -0.1, 0.1, 41, 0.25)

The routines a2scan() and d2scan() simultaneously moves 2 Motors (in absolute and relative coordinates, respectively):

>>> spec.a2scan('x', 0, 1.0, 'y', 0, 0.2, 21, 0.5)
>>> spec.d2scan('x', -0.5, 0.5, 'y', -0.1, 0.1, 21, 0.5)

That is, these move along a line in 'x'-'y' space.

The routines a3scan() and d3scan() simultaneously moves 3 Motors (in absolute and relative coordinates, respectively):

>>> spec.a3scan('x', 0, 1.0, 'y', 0, 0.2, 'theta', 10.0, 10.1, 21, 0.5)

Finally, mesh() will make a 2-dimensional mesh, or map:

>>> spec.mesh('x', 10, 11, 21, theta', 10.0, 10.1, 11,  0.25)

This will make a 21 x 11 pixel map, moving 'x' between 10 and 11 for each 'theta' value, so that 'theta' scans slowly.