# Counters¶

In Bliss, a counter is a python object that represents an experimental parameter which can be measured during a scan.

Counters are passed to the Scan object in order to select the experimental parameters to measure or in other words, the data that will be produced.

Note

More about Counters and CounterControllers implementation can be found in the developers corner

• A counter is identified by a name
• A counter stores information about the shape (dimension) of the associated data (0D, 1D, 2D).
• A counter stores information about the type of the associated data (float, int, …).
• A counter is always attached to a CounterController which knows where and how to read the counter data: data = controller.read( counter )
• A conversion function can be attached to the counter.
• A unit can be specified for the associated data.

For example, a counter can be:

• a scalar parameter like a temperature (0D)
• a spectrum of a multi-channel analyzer (1D)
• an image produced by a camera (2D)
• statistics of a ROI in an image (N x 0D)

## Sampling counters¶

Sampling counters are designed for the measurement of instantaneous values.

A sample counter has a mode property to choose between different sampling behaviors.

The different available modes are:

• SINGLE: performs a single measurement at the beginning of the counting time.
• LAST: returns the last measurement at the end of the counting time.
• MEAN: performs as many samples as possible and computes the averaged value.
• STATS: in addition to MEAN, produces the usual statistics (mean, N, std, var, min, max, p2v)
• SAMPLES: in addition to MEAN, produces individual samples as 1D array.
• INTEGRATE: produces the MEAN multiplied by the counting time.

Example of usage:

In   [9]: diode
Out  [9]: 'diode counter info:
counter type = sampling
sampling mode = MEAN
fullname = simulation_diode_sampling_controller:diode
unit = None

In  [10]: diode.mode
Out [10]: <SamplingMode.SINGLE: 4>

In  [11]: diode.mode='MEAN'
In  [12]: diode.mode
Out [12]: <SamplingMode.MEAN: 1>

In  [13]: ct(1.0, diode)
diode =   12.75 (       12.75/s)

In  [14]: diode.statistics
Out [14]: Stat.(mean=12.75, N=92, std=55.95, var=3130.40, min=-99.0, max=98.0)


### Sampling counter statistics¶

Sampling counters read as many samples as possible from the connected hardware in the specified counting time and return, amongst others, an average value (default mode, see below for details). Additionally, some basic statistics of the sampling process are calculated on the fly, which are accessible after the count through the .statistics property.

DEMO [1]: diode.mode
Out [1]: <SamplingMode.MEAN: 1>

DEMO [2]: ct(1,diode)

diode = 8.03225806451613 ( 8.03225806451613/s)

DEMO [3]: diode.statistics
Out [3]: SamplingCounterStatistics( mean=8.032, N=93,
std=55.96,  var=3132.16,
min=-98.0,  max=100.0,
p2v=198.0,  count_time=1,
timestamp='2019-07-26 10:13:25')


Values available in SamplingCounterStatistics are

• mean: Mean value $\bar x = \frac {\sum_{j=1}^n x_j}{n}$
• var: Variance $\sigma^2 = \displaystyle\frac {\sum_{i=1}^n (x_i - \bar x)^2}{n}$
• std: Standard deviation $\sigma = \sqrt{\sigma^2}$
• min: Minimum value $x_{min}$
• max: Maxium value $x_{max}$
• p2v: Peak to valley $x_{max}-x_{min}$

To avoid storing individual sample values temporarily, the statistics are calculated in a rolling fashion using Welford’s online algorithm. Internally, the sum of squares of differences from the current mean $M_{2,n} = \sum_{i=1}^n (x_i - \bar x_n)^2$, is calculated iteratively via $M_{2,n} = M_{2,n-1} + (x_n - \bar x_{n-1})(x_n - \bar x_n)$. Based on $M_{2,n}$ the variance is derived as $\sigma^2_n = \frac{M_{2,n}}{n}$.

### Sampling counter modes¶

At the end of the counting process, the sampling counter modes are used to specify which value(s) should be published (to hdf5 file and database).

The available modes can be found in bliss.common.measurement.SamplingMode:

DEMO [1]: from bliss.common.measurement import SamplingMode
DEMO [2]: list(SamplingMode)
Out [3]: [<SamplingMode.MEAN: 1>,
<SamplingMode.STATS: 2>,
<SamplingMode.SAMPLES: 3>,
<SamplingMode.SINGLE: 4>,
<SamplingMode.LAST: 5>,
<SamplingMode.INTEGRATE: 6>]
<SamplingMode.INTEGRATE_STATS: 7>]


#### MEAN¶

The default mode is MEAN which returns the mean (average) value of all samples, which have been read during the counting time.

#### INTEGRATE¶

in addition to SamplingMode.MEAN the nominal counting time is taken into account. This way a counter in the mode SamplingMode.INTEGRATE returns the equivalent of the sum of all samples normalized by the counting time. A use case for this mode is for example the reading of a diode, that should yield a value approximately proportional to the number of photons that hit the diode during the counting time.

#### STATS¶

publish all the values as calculated for the sampling counter statistics (see above) into the hdf5 file and the redis database.

#### INTEGRATE_STATS¶

equivalent to SamplingMode.STATS, but for counters that should behave as described in SamplingMode.INTEGRATE yielding statistics in additional channels.

#### SINGLE¶

A counter in this mode publishes only the first sample read from the device, discarding any further samples. If possible (i.e. there is no counter in any other mode on the same AquisitionDevice) only one sample will be read.

#### LAST¶

A counter in this mode publishes only the last sample discarding any further samples.

#### SAMPLES¶

SAMPLES mode is different from other modes in the sense that in addition to SamplingMode.MEAN, it generates an additional 1d dataset containing the individual samples in a counting period and also publishes it. It can e.g. be used to do some more complex statistical analysis of the measured values or, as basis for any CalcCounter, that can be used to extract derived quantities from the original dataset. Following is an example for a CalcCounter, that returns the median:

from bliss.common.measurement import CalcCounter
from bliss.scanning.acquisition.calc import CalcHook
import numpy
class Median(CalcHook):
def compute(self,sender,data_dict):
if "_samples" in sender.name:
return {"median":numpy.median(data_dict[sender.name])}
medi = CalcCounter('median',Median(),diode9)
diode9.mode = "SAMPLES"

DEMO [2]: ct(.1,medi)
Out [2]: Scan(number=224, name=ct, path=<no saving>)

dt[s] =          0.0 (         0.0/s)
diode9 = 19.333333333333332 ( 193.33333333333331/s)
median =         -7.0 (       -70.0/s)


## Integrating counters¶

Integrating counters are designed for time integrated measurements and do not offer any special counting modes. They are bound to a “Time Master” controller which propagates its counting time to the integrating counters that depend on it.

DEMO [49]: lima1
Out [49]: Simulator - Generator (Simulator) - Lima Simulator

Image:
bin = [1 1]
flip = [False False]
height = 1024
roi = <0,0> <1024 x 1024>
rotation = rotation_enum.NONE
sizes = [   0    4 1024 1024]
type = Bpp32
width = 1024

Acquisition:
expo_time = 0.1
mode = mode_enum.SINGLE
nb_frames = 1
status_fault_error = No error
trigger_mode = trigger_mode_enum.INTERNAL_TRIGGER_MULTI

ROI Counters: default
Name  ROI (<X, Y> <W x H>)
----  ------------------
r1    <0, 0> <100 x 200>

BPM Counters:
acq_time, intensity, x, y, fwhm_x, fwhm_y


## Calculation counters¶

A CalcCounterController takes multiple counters as inputs and produces multiple calculated counters as outputs.

One calculated counter is defined by a name and a function that computes a new value from the values of the input counters.

The calculation counters can be used in a scan as standard counters and the dependencies on the input counters will be automatically managed.

- plugin: bliss
module: expression_based_calc
class: ExpressionCalcCounterController
name: calc_diodes

inputs:
- counter: $diode1 tags: d1 - counter:$diode2
tags: d2

- counter: $diode3 tags: d3 - counter:$diode4
tags: d4

constants:
m : 0.5
n : 0.8

outputs:
- name: intensity
expression:  (d1 + d2 + d3 + d4 )

- name: cen_x
expression:  m*(d2-d1) + m*(d4-d3)

- name: cen_y
expression:  m*(d3-d1) + m*(d4-d2)


## Conversion function¶

In order to transform the value of a single counter, a ligther procedure than calculation counters can be used. A conversion function can be dynamicaly added to a counter.

Example usable in a session or in its setup:

# Invert sign of a keithley counter:
kdiag.conversion_function = lambda x : -x

# Multiply cc12 counter by 3:
cc12.conversion_function = lambda x : 3*x


## Counters list¶

The lscnt() command displays all the counters that are currently available in the Bliss session.

It shows the counters names, their associated controllers and the shape of the associated data.

The fullname attribute of a counter is the concatenation of the counter name and its controller name.

## Measurement groups¶

As it is not convenient to count on all counters of the session during a scan, Bliss provides the MeasurementGroup object.

A measurement group is a sub-set of counters which can be defined through the configuration files.

Several measurement groups can be defined and one can be chosen as the default.

Counters can be added/removed or enabled/disabled on the fly while in a Bliss session.

IN  [52]: MG1
OUT [52]: MeasurementGroup: MG1 (state='default')
- Existing states : 'default'

Enabled                       Disabled
----------------------------  -------------------------------------------
simu_diode_controller:diode   heater:heater_counter
simu_diode_controller:diode2  sample_regulation:sample_regulation_setpoint
simu_diode_controller:diode3  thermo_sample:thermo_sample_counter

IN [53]: ct(1.0)

Tue Feb 18 17:04:36 2020

diode =       1.6125 (      1.6125/s)
diode2 =       7.5125 (      7.5125/s)
diode3 =          8.5 (         8.5/s)

Out [53]: Scan(number=6, name=ct, path=)
`