Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions doc/devices/dae.md
Original file line number Diff line number Diff line change
Expand Up @@ -191,13 +191,16 @@ Published signals:
### {py:obj}`PeriodSpecIntegralsReducer<ibex_bluesky_core.devices.simpledae.PeriodSpecIntegralsReducer>`

This reducer exposes the raw integrals of the configured detector and monitor spectra, as
numpy arrays. By itself, this reducer is not useful in a scan, but is useful for downstream
processing as performed by reflectometry detector-mapping alignment for
example.
numpy arrays. As a convenience, this reducer additionally publishes scalar sums of detector
and monitor intensities, and an 'intensity' (detector sum divided by monitor sum).

Published signals:
- `reducer.mon_integrals` - `numpy` array of integrated counts on each configured monitor pixel.
- `reducer.det_integrals` - `numpy` array of integrated counts on each configured detector pixel.
- `reducer.mon_sum` - scalar of integrated counts on each configured monitor.
- `reducer.det_sum` - scalar of integrated counts on each configured detector pixel.
- `reducer.intensity` - detector sum normalized by monitor sum.
- `reducer.intensity_stddev` - standard deviation of normalized intensity.

### {py:obj}`DSpacingMappingReducer<ibex_bluesky_core.devices.simpledae.DSpacingMappingReducer>`

Expand Down
36 changes: 31 additions & 5 deletions src/ibex_bluesky_core/devices/simpledae/_reducers.py
Original file line number Diff line number Diff line change
Expand Up @@ -321,12 +321,19 @@ def additional_readable_signals(self, dae: Dae) -> list[Device]:
class PeriodSpecIntegralsReducer(Reducer, StandardReadable):
"""A DAE Reducer which simultaneously exposes integrals of many spectra in the current period.

Two types of integrals are available: detectors and monitors. Other than defaults, their
behaviour is identical. No normalization is performed in this reducer - exactly how the
detector and monitor integrals are used is defined downstream.
Two types of integrals are available: detectors and monitors.

By itself, the data from this reducer is not suitable for use in a scan - but it provides
raw data which may be useful for further processing as part of callbacks (e.g. LiveDispatchers).
The exposed signals are:

- det_integrals (1-dimensional array)
- mon_integrals (1-dimensional array)

For convenience, three additional properties are also exposed:

- det_sum (scalar) - the sum of detector intensities across every detector.
- mon_sum (scalar) - the sum of monitor intensities across every monitor.
- intensity (scalar) - det_sum / mon_sum
- intensity_stddev (scalar) - det_sum / mon_sum
"""

def __init__(
Expand Down Expand Up @@ -354,6 +361,10 @@ def __init__(
self.mon_integrals, self._mon_integrals_setter = soft_signal_r_and_setter(
Array1D[np.int32], np.ndarray([], dtype=np.int32)
)
self.det_sum, self._det_sum_setter = soft_signal_r_and_setter(int, 0)
self.mon_sum, self._mon_sum_setter = soft_signal_r_and_setter(int, 0)
self.intensity, self._intensity_setter = soft_signal_r_and_setter(float, 0.0)
self.intensity_stddev, self._intensity_stddev_setter = soft_signal_r_and_setter(float, 0.0)

super().__init__(name="")

Expand Down Expand Up @@ -393,13 +404,28 @@ async def reduce_data(self, dae: Dae) -> None:
self._det_integrals_setter(det_integrals)
self._mon_integrals_setter(mon_integrals)

scalar_det_sum = det_integrals.sum()
scalar_mon_sum = mon_integrals.sum()
self._det_sum_setter(scalar_det_sum)
self._mon_sum_setter(scalar_mon_sum)

normalized = sc.scalar(
scalar_det_sum, variance=scalar_det_sum + VARIANCE_ADDITION, dtype="float64"
) / sc.scalar(scalar_mon_sum, variance=scalar_mon_sum + VARIANCE_ADDITION, dtype="float64")
self._intensity_setter(normalized.value)
self._intensity_stddev_setter(normalized.variance**0.5)

logger.info("reduction complete")

def additional_readable_signals(self, dae: Dae) -> list[Device]:
"""Publish interesting signals derived or used by this reducer."""
return [
self.mon_integrals,
self.det_integrals,
self.det_sum,
self.mon_sum,
self.intensity,
self.intensity_stddev,
]


Expand Down
8 changes: 8 additions & 0 deletions tests/devices/simpledae/test_reducers.py
Original file line number Diff line number Diff line change
Expand Up @@ -1012,6 +1012,14 @@ async def test_period_spec_integrals_reducer(
np.testing.assert_equal(await reducer.mon_integrals.get_value(), mon_integrals)
np.testing.assert_equal(await reducer.det_integrals.get_value(), det_integrals)

assert await reducer.mon_sum.get_value() == mon_integrals.sum()
assert await reducer.det_sum.get_value() == det_integrals.sum()

assert (
pytest.approx(await reducer.intensity.get_value())
== det_integrals.sum() / mon_integrals.sum()
)


def test_period_spec_integrals_reducer_publishes_signals(simpledae: SimpleDae):
reducer = PeriodSpecIntegralsReducer(detectors=np.array([]), monitors=np.array([]))
Expand Down