diff --git a/RELEASE.md b/RELEASE.md index cefe4de..708f4ba 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -9,6 +9,7 @@ - setter and getter for nSigma for ClusterFinder ``aare.ClusterFinder().nSigma = 2``, ``aare.ClusterFinderMT().set_nSigma(2)`` - mask opeartor for ClusterVector ``masked_clustervector = aare.ClusterVector()(mask)`` - passing pre computed eta values to ``aare.Interpolator.interpolate`` alongside clusters +- Added ``PixelHistogram`` and ``PedestalTrackingPixelHistogram`` ### Bugfixes: diff --git a/docs/src/index.rst b/docs/src/index.rst index 834c490..8b9af28 100644 --- a/docs/src/index.rst +++ b/docs/src/index.rst @@ -28,6 +28,7 @@ AARE pycalibration python/cluster/index python/file/index + python/histogram/index pyFit @@ -63,4 +64,4 @@ AARE Philosophy Workflow - Tests \ No newline at end of file + Tests diff --git a/docs/src/python/histogram/index.rst b/docs/src/python/histogram/index.rst new file mode 100644 index 0000000..5ca8b33 --- /dev/null +++ b/docs/src/python/histogram/index.rst @@ -0,0 +1,9 @@ +Histogram +========= + +.. toctree:: + :caption: Histogram + :maxdepth: 1 + + pyPixelHistogram + pyPedestalTrackingPixelHistogram diff --git a/docs/src/python/histogram/pyPedestalTrackingPixelHistogram.rst b/docs/src/python/histogram/pyPedestalTrackingPixelHistogram.rst new file mode 100644 index 0000000..18d438c --- /dev/null +++ b/docs/src/python/histogram/pyPedestalTrackingPixelHistogram.rst @@ -0,0 +1,24 @@ +PedestalTrackingPixelHistogram +============================== + +.. warning:: + + ``PedestalTrackingPixelHistogram`` is specifically designed for use in the Jungfrau calibration + pipeline. Make sure you understand the behaviour before using it in other contexts. + +``PedestalTrackingPixelHistogram`` accumulates a pixel-wise histogram of +``frame - pedestal`` residuals while maintaining a running per-pixel pedestal +estimate. + +Use ``push_pedestal_no_update()`` to seed the pedestal estimate, then +``update_mean()`` before submitting frames with ``fill_async()``. Pending +asynchronous fills are drained by ``flush()``, and snapshot methods such as +``values()`` and ``pedestal_mean()`` return numpy arrays. + +.. py:currentmodule:: aare + +.. autoclass:: PedestalTrackingPixelHistogram + :members: + :undoc-members: + :show-inheritance: + :inherited-members: diff --git a/docs/src/python/histogram/pyPixelHistogram.rst b/docs/src/python/histogram/pyPixelHistogram.rst new file mode 100644 index 0000000..2705732 --- /dev/null +++ b/docs/src/python/histogram/pyPixelHistogram.rst @@ -0,0 +1,41 @@ +PixelHistogram +============== + +``PixelHistogram`` accumulates one histogram per detector pixel from 2D +``float64`` images. The public ``PixelHistogram`` name is an alias for +``PixelHistogram_d``, which stores bin counts as ``float64``. + +.. note:: + + ``PixelHistogram`` is designed for fast filling from 2D images, utilizing multiple threads, + and contiguous storage of a specific type. No over/underflow bins are provided. For generic use cases consider + boost-histogram. (https://boost-histogram.readthedocs.io/en/latest/) + + +Storage-specific variants are also available when a smaller or integer count +type is preferred: + +* ``PixelHistogram_d``: ``float64`` storage +* ``PixelHistogram_f``: ``float32`` storage +* ``PixelHistogram_u64``: ``uint64`` storage +* ``PixelHistogram_u32``: ``uint32`` storage +* ``PixelHistogram_u16``: ``uint16`` storage +* ``PixelHistogram_u8``: ``uint8`` storage + +.. py:currentmodule:: aare + +.. autoclass:: PixelHistogram + :members: + :undoc-members: + :show-inheritance: + :inherited-members: + +Showing API for PixelHistogram_d, all variant share the same API. + +.. autoclass:: aare._aare.PixelHistogram_d + :members: + :undoc-members: + :show-inheritance: + :inherited-members: + :noindex: + diff --git a/src/hist/PedestalTrackingPixelHistogram.cpp b/src/hist/PedestalTrackingPixelHistogram.cpp index 93a7521..b444cef 100644 --- a/src/hist/PedestalTrackingPixelHistogram.cpp +++ b/src/hist/PedestalTrackingPixelHistogram.cpp @@ -235,38 +235,64 @@ void PedestalTrackingPixelHistogram::worker_loop(int thread_id) { // Histogram the pedestal-subtracted residual AND, for pixels // whose residual is consistent with noise // (|residual| < n_sigma * cached_std), feed the raw value - // back into the pedestal shard. With n_sigma == 0 the gate - // never fires, recovering plain histogram-only behaviour - // (modulo the per-pixel gate evaluation). The [xmin, xmax) - // histogram gate lives inside PixelHistogramImpl::fill. - auto &my_std = partial_std_[thread_id]; + // back into the pedestal shard. With n_sigma <= 0, use a + // histogram-only hot path that skips the per-pixel pedestal + // tracking gate entirely. The [xmin, xmax) histogram gate + // lives inside PixelHistogramImpl::fill. const auto n_sigma = n_sigma_.load(std::memory_order_relaxed); - for (const auto &frame : *images) { - const auto cols = frame.shape(1); - for (int local_row = 0; local_row < local_rows; ++local_row) { - const auto row = - static_cast(first_row + local_row); - for (ssize_t col = 0; col < cols; ++col) { - const FrameType raw = frame(row, col); - const AxisType val = - static_cast(raw) - - static_cast(my_pedestal.mean( - static_cast(local_row), - static_cast(col))); - my_hist.fill_unchecked(local_row, static_cast(col), - val); - const AxisType sigma = my_std(local_row, col); - if (sigma > AxisType{0.0} && - std::abs(static_cast(val)) < - n_sigma * sigma) { - my_pedestal.template push( - static_cast(local_row), - static_cast(col), raw); + if (n_sigma <= AxisType{0.0}) { + // Fill without pedestal tracking. + for (const auto &frame : *images) { + const auto cols = frame.shape(1); + for (int local_row = 0; local_row < local_rows; + ++local_row) { + const auto row = + static_cast(first_row + local_row); + for (ssize_t col = 0; col < cols; ++col) { + const FrameType raw = frame(row, col); + const AxisType val = + static_cast(raw) - + static_cast(my_pedestal.mean( + static_cast(local_row), + static_cast(col))); + my_hist.fill_unchecked(local_row, + static_cast(col), val); } } } + break; + } else { + // Do pedestal tracking. Duplicated code for clean hot path. + + auto &my_std = partial_std_[thread_id]; + for (const auto &frame : *images) { + const auto cols = frame.shape(1); + for (int local_row = 0; local_row < local_rows; + ++local_row) { + const auto row = + static_cast(first_row + local_row); + for (ssize_t col = 0; col < cols; ++col) { + const FrameType raw = frame(row, col); + const AxisType val = + static_cast(raw) - + static_cast(my_pedestal.mean( + static_cast(local_row), + static_cast(col))); + my_hist.fill_unchecked(local_row, + static_cast(col), val); + const AxisType sigma = my_std(local_row, col); + if (sigma > AxisType{0.0} && + std::abs(static_cast(val)) < + n_sigma * sigma) { + my_pedestal.template push( + static_cast(local_row), + static_cast(col), raw); + } + } + } + } + break; } - break; } }