# 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 = Ready
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=)
```

More info about Measurement groups here.