Skip to content

Scan

Scan is the top level object of the scan engine and it’s the one who perform the scan according to the acquisition chain passed as argument when calling the run method. It saves and publish acquisition data according to the scan saving configuration.

Scan life cycle

Scan states

A normal scan will follow the steps bellow:

  • A new scan is created with the state IDLE until run is called
  • During the PREPARING, controllers are setup and metadata are collected
  • During STARTING the data acquisition is processed
  • During STOPPING metadata at the termination of the scan are collected
  • Finally the scan ending with DONE state

At any time a scan can also be interrupted. As result the ending state will be one of:

  • KILLED, caused by an error during the scan
  • USER_ABORT caused by an abort request from the user (commonly after the use of a CTRL-C keys)

Scan data

During or after a scan, scan data can be retrieved by calling get_data(). This method will return data as a dictionary with the key as the counter name and a numpy array with the associated data.

DEMO [1]: s = loopscan(2,0.1,diode)

Scan 3 Wed Mar 06 11:47 /scans/test_session/data.h5 test_session user = seb
loopscan 2 0.1

           #         dt[s]         diode
           0             0       2.77778
           1      0.107661            26

Took 0:00:03.523786

DEMO [2]: s.get_data().keys()
 Out [2]: dict_keys(['timer:elapsed_time', 'timer:epoch',
                     'simulation_diode_controller:diode'])

# retrieved with counter instance
DEMO [4]: s.get_data()[diode]
 Out [4]: array([ 2.77777778, 26. ])

# retrieved with short name (when there is no name collision)
DEMO [5]: s.get_data()['diode']
 Out [5]: array([ 2.77777778, 26. ])

# or with fullname
DEMO [5]: s.get_data()['simulation_diode_controller:diode']
 Out [5]: array([ 2.77777778, 26. ])

Alignment functions

There is a group of function that returns motor position in fonction of value of counter passed as argument. Available functions are:

  • fwhm(): return the full width half maximum
  • cen(): return the center of the fwhm and fwhm
  • com(): return the center of mass
  • peak(): return the motor position at the counter maximum

An other function group prefixed by goto_ move directly the motor to the calculated position.

Example: goto_cen() moves the motor to the center of the fwhm. This group of function draw a marker in Flint at the final motor position.

Function where() displays the current scan motor position.

Examples

DEMO [1]: s = ascan(robz,0,1,10,0.1,counter)

Scan 7 Wed Mar 06 12:45 /scans/test_session/data.h5 test_session user = seb
ascan robz 0 1 10 0.1

           #         dt[s]          robz   autoCounter
           0             0             0     0.0526115
           1      0.205485        0.1111     0.0731554
           2       0.40605        0.2222      0.107844
           3      0.606267        0.3333       1.07414
           4      0.800804        0.4444       3.49636
           5       1.00134        0.5556       3.43307
           6       1.20344        0.6667       1.04448
           7       1.40291        0.7778      0.147452
           8       1.60499        0.8889      0.035737
           9       1.80242             1     0.0652613

DEMO [2]: robz.position # position of robz at the end of scan
 Out [2]: 1.0

DEMO [3]: s.cen(counter)
 Out [3]: (0.494053, 0.425117)

DEMO [4]: s.goto_cen(counter) # will move to 0.494053

DEMO [5]: robz.position # position after the `goto_cen`
 Out [5]: 0.4941

Profiling

Scans can be profiled using a tracing profiler such as yappi:

DEMO [1]: import yappi; yappi.set_context_backend("greenlet")
DEMO [2]: yappi.set_clock_type("cpu")
DEMO [3]: yappi.start(); fscan_eh.ftimescan.run(); yappi.stop()

<scan runs>

DEMO [4]: yappi.get_func_stats().print_all()

Clock type: CPU
Ordered by: totaltime, desc

name                                  ncall  tsub      ttot      tavg
..279 _MusstAcquisitionSlave.reading  1      0.000000  2.272000  2.272000
.. _MusstAcquisitionSlave._send_data  473    0.012000  2.248000  0.004753
..s/common/greenlet_utils.py:66 func  2289   0.004000  2.216000  0.000968
..gevent/_socket3.py:447 socket.recv  7170   2.096000  2.148000  0.000300
..lers/musst.py:500 id31musst.putget  1435   0.008000  2.128000  0.001483
..kages/louie/dispatcher.py:303 send  711..  0.088000  1.856000  0.000261
..llers/musst.py:744 id31musst.STATE  954    0.004000  1.844000  0.001933

For statistics on metadata collection, use the built-in metadata_profiling() function:

DEMO [5]: metadata_profiling()

==============================================================
                        USER META DATA
==============================================================

      name      |    category    |metadata gathering time (ms)
----------------|----------------|----------------------------
  device_info   |  nexuswriter   |           6.1351
   @NX_class    |   instrument   |           0.0023
   @NX_class    |   technique    |           0.0017


==============================================================
                    CONTROLLERS META DATA
==============================================================

      name      |    category    |metadata gathering time (ms)
----------------|----------------|----------------------------
  positioners   |  positioners   |           0.0482
   @NX_class    |   instrument   |           0.0025

Debugging

A tracing mechanism can be activated to debug and profile scans. Use:

  • debugon("scan") to activate scan statistics in session logging file
  • debugon("scan_stats") to activate scan statistics on screen and in session logging file
  • debugon("scan_chain") to activate scan profiling and statistics on screen and in session logging file
  • debugoff("scan") to de-activate any of the 3 above
  • debugon("bliss.scanning.chain") to activate low-level scan chain tasks profiling in session logging file