Skip to content

Acquisition chain

Used by a Scan object, the AcquisitionChain object is a tree describing triggering relations between acquisition objects.

Screenshot

Note

  • An AcquisitionChain can only be run once by a scan.
  • It’s sometimes needed to control the state of devices like shutter, multiplexer, or detector cover. Those devices should be controlled with Preset objects.

Building the chain

The AcquisitionChain tree is built by inserting acquisition objects into the chain. Inserted acquisition objects must inherit from AcquisitionMaster or AcquisitionSlave.

Screenshot

Acquisition objects are inserted using the add(parent_obj, child_obj) method of the AcquisitionChain object, where parent_obj is the object under which child_obj object should be inserted.

If parent_obj is not already in the chain, it will be added as a top master. If child_obj is not provided, parent_obj is added as a top master.

Build a chain for a simple loop scan

from bliss.scanning import chain
from bliss.scanning.acquisition.timer import SoftwareTimerMaster
from bliss.scanning.acquisition.counter import SamplingCounterAcquisitionSlave

acq_chain = chain.AcquisitionChain()

timer = SoftwareTimerMaster(1, npoints=2)
counter_device = SamplingCounterAcquisitionSlave(diode, count_time=1, npoints=2)

acq_chain.add(timer, counter_device)

print(acq_chain._tree)

Out [4]:
acquisition chain
└── timer
    └── simulation_diode_sampling_controller

Run the scan using the chain

from bliss.scanning.scan import Scan

s = Scan(acq_chain, name='simple loop', scan_info={}, save=False)
s.run()

s.get_data()

Out [12]: {'elapsed_time': array([0. , 1.0063]),
        'diode': array([11.59349, -6.2747])}

Build a chain for a one-axis step-by-step scan

from bliss.scanning import chain
from bliss.scanning.acquisition.timer import SoftwareTimerMaster
from bliss.scanning.acquisition.counter import SamplingCounterAcquisitionSlave

from bliss.scanning.acquisition.motor import LinearStepTriggerMaster

acq_chain = chain.AcquisitionChain()

motor_master = LinearStepTriggerMaster(2,robz,0,1)
timer = SoftwareTimerMaster(1, npoints=2)
counter_device = SamplingCounterAcquisitionSlave(diode, count_time=1, npoints=2)

acq_chain.add(motor_master, timer)
acq_chain.add(timer, counter_device)

print(acq_chain._tree)

Out [12]:
acquisition chain
└── axis
    └── timer
        └── simulation_diode_sampling_controller

See custom scans section for more advanced acquisition chain examples

Display chain tree

A print() on the tree property of the acquisition chain object, displays the acquisition tree.

Print the acquisition chain tree

DEMO [1]: s = ascan(robz, 0, 1, 10, 0.1, diode2, diode3, run=False)
DEMO [2]: print(s.acq_chain.tree)
acquisition chain
└── axis
    └── timer
        └── simulation_diode_sampling_controller

Scan iterations

During the scanning procedure iterations, specific methods are called on each acquisition object composing the acquisition chain (see AcquisitionChainIter).

The calls can be made down-stream (from top-master to slaves) or up-stream (from slaves to top-master).

One method calls can be sequential or parallelized over chain objects.

At each scan iteration, these acquisition object methods are called:

  • acq_prepare: device preparation (up-stream)
  • acq_start: starts acquisition on device (up-stream)
  • acq_wait_ready: block until device is ready for next scan iteration (down-stream)

At the beginning of a scan, apply_parameters (up-stream) and acq_wait_ready (down-stream) are also called on chain objects.

At the end of the scan acq_stop is called once per acquisition object (down-stream).

A master acquisition object may call trigger_slaves in its trigger method. If so, this will call the acq_trigger method of its children nodes if children objects are using a trigger_type == TRIGGER_MODE_ENUM.SOFTWARE.

Screenshot

Monitor iterations calls

Acquisition object methods called during scan iterations can be displayed using the bliss.scans logger.

Monitor acquisition objects methods called during a scan

DEMO [1]: s = loopscan(2,1,diode,diode2,run=False)
DEMO [2]: debugon('bliss.scans')
DEMO [3]: s.run()
DEBUG 29,288 Scan: Start timer.wait_ready
DEBUG 29,289 Scan: End timer.wait_ready Took 0.000449s
DEBUG 29,289 Scan: Start diode.wait_ready
DEBUG 29,289 Scan: Start simulation_diode_controller.wait_ready
DEBUG 29,290 Scan: End diode.wait_ready Took 0.000545s
DEBUG 29,290 Scan: End simulation_diode_controller.wait_ready Took 0.000495s

Scan 11 Wed Mar 06 24 2019 /tmp/scans/test_session/data.h5
                                test_session user = seb
loopscan 2 1

        #         dt[s]         diode        diode2
DEBUG 29,314 Scan: Start simulation_diode_controller.prepare
DEBUG 29,314 Scan: End simulation_diode_controller.prepare Took 0.000120s
DEBUG 29,314 Scan: Start diode.prepare
DEBUG 29,314 Scan: End diode.prepare Took 0.000040s
DEBUG 29,314 Scan: Start timer.prepare
DEBUG 29,315 Scan: End timer.prepare Took 0.000033s
DEBUG 29,315 Scan: Start simulation_diode_controller.start
DEBUG 29,315 Scan: End simulation_diode_controller.start Took 0.000536s
DEBUG 29,315 Scan: Start diode.start
DEBUG 29,316 Scan: End diode.start Took 0.000395s
DEBUG 29,316 Scan: Start timer.start
DEBUG 29,316 Scan: Start timer.trigger_slaves
DEBUG 29,316 Scan: End timer.trigger_slaves Took 0.000090s
DEBUG 29,317 Scan: Start diode.trigger
DEBUG 29,317 Scan: End diode.trigger Took 0.000062s
DEBUG 29,317 Scan: Start simulation_diode_controller.trigger
DEBUG 29,317 Scan: End simulation_diode_controller.trigger Took 0.000037s
DEBUG 29,318 Scan: Start timer.wait_slaves
DEBUG 29,318 Scan: End timer.wait_slaves Took 0.000249s
DEBUG 30,319 Scan: End timer.start Took 1.002655s
DEBUG 30,320 Scan: Start timer.wait_ready
DEBUG 30,321 Scan: End timer.wait_ready Took 0.000566s
DEBUG 30,322 Scan: Start diode.wait_ready
DEBUG 30,322 Scan: Start simulation_diode_controller.wait_ready
DEBUG 30,323 Scan: End diode.wait_ready Took 0.001143s
DEBUG 30,323 Scan: End simulation_diode_controller.wait_ready Took 0.001143s
DEBUG 30,324 Scan: Start timer.prepare
DEBUG 30,325 Scan: End timer.prepare Took 0.000302s
DEBUG 30,326 Scan: Start timer.start
DEBUG 30,327 Scan: Start timer.trigger_slaves
DEBUG 30,327 Scan: End timer.trigger_slaves Took 0.000420s
DEBUG 30,328 Scan: Start diode.trigger
DEBUG 30,329 Scan: End diode.trigger Took 0.000344s
DEBUG 30,329 Scan: Start simulation_diode_controller.trigger
DEBUG 30,329 Scan: End simulation_diode_controller.trigger Took 0.000291s
DEBUG 30,332 Scan: Start timer.wait_slaves
DEBUG 30,334 Scan: End timer.wait_slaves Took 0.001394s
        0             0       3.52747      -2.57778
DEBUG 31,329 Scan: End timer.start Took 1.002674s
DEBUG 31,331 Scan: Start timer.wait_ready
DEBUG 31,331 Scan: End timer.wait_ready Took 0.000565s
DEBUG 31,332 Scan: Start diode.wait_ready
DEBUG 31,332 Scan: Start simulation_diode_controller.wait_ready
DEBUG 31,339 Scan: End simulation_diode_controller.wait_ready Took 0.006308s
DEBUG 31,339 Scan: End diode.wait_ready Took 0.007317s
DEBUG 31,340 Scan: Start timer.wait_slaves
DEBUG 31,340 Scan: End timer.wait_slaves Took 0.000504s
        1       1.01025      -1.74157       10.4382
DEBUG 31,349 Scan: Start timer.stop
DEBUG 31,350 Scan: End timer.stop Took 0.000574s
DEBUG 31,351 Scan: Start diode.stop
DEBUG 31,352 Scan: End diode.stop Took 0.000701s
DEBUG 31,352 Scan: Start simulation_diode_controller.stop
DEBUG 31,352 Scan: End simulation_diode_controller.stop Took 0.000295s
DEBUG 31,353 Scan: Start timer.wait_slaves
DEBUG 31,354 Scan: End timer.wait_slaves Took 0.000611s

Took 0:00:06.935785