Commit Graph
81 Commits
Author SHA1 Message Date
kferjaoui 1503331100 update the CUDA clusterfinder notebook (new API)
Build on RHEL8 / build (push) Successful in 4m0s
Build on RHEL9 / build (push) Successful in 4m13s
Run tests using data on local RHEL8 / build (push) Successful in 4m47s
2026-09-24 07:56:26 +02:00
kferjaoui d9bead2a07 Port the frozen and CUDA finders to FastPedestal
Build on RHEL8 / build (push) Successful in 3m38s
Build on RHEL9 / build (push) Successful in 3m49s
Run tests using data on local RHEL8 / build (push) Successful in 4m46s
- main moved ClusterFinder to FastPedestal and dropped Pedestal's
  push_fast/get_sum/get_sum2, which ClusterFinderFrozen and both CUDA
  drivers still called.

- Move all three to FastPedestal. Its uniform sample count is what the
  CUDA kernel already assumes, and matching ClusterFinder keeps CPU and
  GPU warm-up identical, which ClusterFinderFrozen exists to verify.
  FastPedestal gains get_sum/get_sum2 to de-interleave its internal
  Entry{sum,sum2} for the CUDA upload.

- Also expose min_pedestal_samples on these constructors and factories,
  make the factories keyword-only after image_size. Same API in C++ and Python.
2026-09-23 15:54:16 +02:00
kferjaoui 7a8f918a56 Merge branch 'main' into testing/cuda_cf_api
Three files conflicted:

- ClusterFinderMT::collect(): both sides rewrote it. Keep this branch's
  version (f4bbbc9), which reads the stop flags before sweeping, over
  main's (afce91b), which still samples queue emptiness mid-condition.
  The staged splice had also lost a brace, swallowing popFront().
- python/aare/ClusterFinder.py: restore the CUDA API deleted by the
  resolution (_cuda_available, _CUDA_ALGORITHMS, ClusterFinderCUDA,
  find_cluster_views_batched_iter, ClusterFinderCUDAGraph), and main's
  min_pedestal_samples forwarding, dropped as an adjacent hunk.
- bind_ClusterVector.hpp: keep py::module_local() (133cedf) alongside
  main's new docstrings; without it _aare and _aare_cuda cannot both
  register Cluster/ClusterVector under pybind11 3.

Does not build on its own: ClusterFinderFrozen and the CUDA drivers
still call Pedestal APIs main removed. Fixed in the follow-up.
2026-09-23 15:39:54 +02:00
Erik Fröjdh 752ec38596 added template for making views (#377)
Build on RHEL9 / build (push) Successful in 3m4s
Build on RHEL8 / build (push) Successful in 3m32s
Run tests using data on local RHEL8 / build (push) Successful in 4m25s
Build on local RHEL8 / build (push) Successful in 3m8s
Cleaned up the helper functions by: 
- Added template for make_view (and new make_const_view) instead of the
copy paste implementation
- checks c_style and dimensions inside make_view
- test binding to be able to probe the checks in make_view

Call sites can be reviewed in a second PR since usage of const or
strictness of conversion depends...
2026-09-22 09:25:42 +02:00
Erik Fröjdhandmazzol_a 0dc72634a9 Reject raw files without padding if discard partial is not set (#374)
Build on RHEL9 / build (push) Successful in 3m3s
Build on RHEL8 / build (push) Successful in 3m25s
Run tests using data on local RHEL8 / build (push) Successful in 4m21s
Build on local RHEL8 / build (push) Successful in 3m5s
We have no code in aare to deal with RawFiles that have missing packets
if they are not padded to full size or frames with packet loss dropped.

Added: 
- check that we don't try to read files that are not supported
- python bindings for frame discard policy

at least we get the check in: 
closes #171

---------

Co-authored-by: AliceMazzoleni99 <alice.mazzoleni@psi.ch>
2026-09-18 17:06:11 +02:00
Erik Fröjdhandmazzol_a b558f1146b Fixed seek bug in CtbRawFile (#373)
Build on RHEL9 / build (push) Successful in 2m49s
Build on RHEL8 / build (push) Successful in 3m25s
Run tests using data on local RHEL8 / build (push) Successful in 4m27s
Build on local RHEL8 / build (push) Successful in 3m6s
- seek to the edge of a file + read_into would select the wrong file
- Visible for example in f.read_frame(index) in python

Added fix, release notes and a small test. 

closes #371

Co-authored-by: AliceMazzoleni99 <alice.mazzoleni@psi.ch>
2026-09-17 08:09:12 +02:00
Erik Fröjdh 8551281eb7 Cleaned up Pedestal (#370)
Build on RHEL9 / build (push) Successful in 2m52s
Build on RHEL8 / build (push) Successful in 3m33s
Run tests using data on local RHEL8 / build (push) Successful in 4m31s
Build on local RHEL8 / build (push) Successful in 3m4s
- renamed pedestal.hpp to bind_Pedestal.hpp for python bindings
- variance is now always calculated in double and made private
- cost correctness in a few places
- removed push_fast and explicit updates of mean from Pedestal. If
performance is needed use FastPedestal
- Pedestal no longer caches std
2026-09-14 16:27:53 +02:00
Erik Fröjdh b0875106e3 Improved ClusterFile API (#358)
Build on RHEL9 / build (push) Successful in 2m49s
Build on RHEL8 / build (push) Successful in 3m23s
Run tests using data on local RHEL8 / build (push) Successful in 4m23s
Build on local RHEL8 / build (push) Successful in 3m5s
Improved API for ClusterFile

- read_frame now returns a std::optional<ClusterVector> to differentiate
between an empty frame and end of file
- f.frames() produces an iterator over frames
- f.chunks() produces an iterator reading chunks of clusters
2026-09-14 16:01:25 +02:00
kferjaoui da3f563872 Add nsys probe scripts for ClusterFinderCUDA
Per-engine GPU time, duty cycle and roofline on the campaign configs
(3x3 cap 3000, 9x9 cap 1700, 1 and 4 streams, 20k frames).
2026-09-10 14:09:20 +02:00
mazzol_aandErik Fröjdh 86db375948 python repr for Eta, Cluster (#367)
Build on RHEL9 / build (push) Successful in 2m40s
Build on RHEL8 / build (push) Successful in 3m15s
Run tests using data on local RHEL8 / build (push) Successful in 4m15s
Build on local RHEL8 / build (push) Successful in 2m56s
- python string repr for Cluster and Eta
- Context manager for RawMasterFile 
- slice operator for ROI

---------

Co-authored-by: Erik Fröjdh <erik.frojdh@psi.ch>
2026-09-10 12:18:27 +02:00
kferjaouiandClaude Fable 5.1 272b483c6c CUDA: split the cluster finder into algorithm, stream context and driver
ClusterFinderCUDA mixed three things: which kernel runs and what device
state it needs, the per-stream buffers, and the batching pipeline. A
second algorithm meant a second copy of all of it. The pieces are now
separate and the driver is templated on the algorithm.

  cuda::FixedWindow  (clusterfinder_algo.cuh)  the fixed-size window
      search: DeviceState pointer bundle, launch(), and the pedestal
      transforms. Stateless; every pointer is supplied by the caller,
      so external code can drive the kernel on its own memory and
      stream without touching the driver.
  StreamContext<Algo>  one stream's buffers, RAII, non-movable.
  ClusterFinderCUDA<Algo>  the pipeline, unchanged in behaviour.

no_2x2_cluster moves to Cluster.hpp so the kernel and algorithm headers
no longer drag in ClusterFinder.hpp. The Python factory gains
algorithm="fixed_window"; memory stays internal.

Kernel SASS identical to before. Positions identical against
ClusterFinderFrozen for 3x3, 5x5, 9x9 (stream and graph finders). New
Catch2 test drives FixedWindow::launch on raw cudaMalloc buffers and
requires exact agreement with the driver.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-10 12:08:16 +02:00
Erik Fröjdh 3431bbe117 better error and update frames (#365)
Build on RHEL9 / build (push) Successful in 2m34s
Build on RHEL8 / build (push) Successful in 3m13s
Run tests using data on local RHEL8 / build (push) Failing after 4m14s
Build on local RHEL8 / build (push) Successful in 2m54s
- Updated error message to mention out of range and which file it
applies to
- If there are different number of frames in the subfiles, take the
minimum and print a warning

Closes: #363 
Closes: #362
2026-09-09 17:07:08 +02:00
Erik Fröjdh dd409cfe41 Fixed ClusterVector move, filtering, and improved documentation (#357)
Run tests using data on local RHEL8 / build (push) Failing after 4m6s
Build on local RHEL8 / build (push) Successful in 2m52s
Build on RHEL9 / build (push) Successful in 2m44s
Build on RHEL8 / build (push) Successful in 3m16s
- Default `ClusterVector` move operations simplifying the code and
fixing a bug.
- Fixed inconsistent frame number type (uint64/int32)
- Validate Python masks as one-dimensional, C-contiguous Boolean arrays,
handle empty masks safely, and reserve filtered storage based on the
selected cluster count.
- Align the C++ and Python API documentation with the implementation,
including concise `hitmap` and reduction documentation and a correctly
rendered constructor example.
2026-09-07 08:19:26 +02:00
kferjaoui f4bbbc9c76 Fix cluster loss on ClusterFinderMT shutdown
collect() cached queue emptiness across sweeps, so a worker's last
write could land after the final sweep but before shutdown was
observed, stranding that frame's clusters. Read the stop flags before
sweeping instead: once workers are joined, a sweep that moves nothing
proves the queues are drained.

Found via ClusterFinderCUDA.ipynb's drift plot, which jumped by one
frame's worth of clusters at the very end.
2026-09-03 17:26:47 +02:00
mazzol_aandErik Fröjdh 641ef047b5 Dev/filereading for disabled udp (#342)
Build on RHEL9 / build (push) Successful in 2m41s
Build on RHEL8 / build (push) Successful in 3m15s
Run tests using data on local RHEL8 / build (push) Failing after 4m13s
Build on local RHEL8 / build (push) Successful in 2m53s
- handles file reading of disabled udp ports 
- adds members disabled_udp_ports in master file as an optional 
- adds member diasbled_udp_port_types in master file as an optional
- Treats enabled udp ports as ROIs 
- merges/combines consecutive ROis into one 

### To discuss: 

- right now can only handle either ROI or disabled udp ports (disabled
udp ports has higher precedence)
- Should Frames in File be removed from Master File as value is nonsense
for disabled udp ports

---------

Co-authored-by: Erik Fröjdh <erik.frojdh@psi.ch>
2026-09-03 14:14:57 +02:00
kferjaoui 7870ccfc32 CUDA: expose in docs, drop dead headers
Build on RHEL8 / build (push) Successful in 4m12s
Build on RHEL9 / build (push) Successful in 4m44s
Run tests using data on local RHEL8 / build (push) Failing after 4m18s
Doxygen never read *.cuh and needs EXTENSION_MAPPING to parse it; add
both, plus C++/Python pages and AARE_CUDA build docs.

Delete ClusterFinderCUDA_old.hpp (declared the same class, polluting
the API page), utils/batch.hpp, and the deck. All unreferenced.

TODO:
- Frozen + CUDAGraph: bound in Python, headers not installed. Ship or drop?
- test_ClusterFinderCUDA.py guarded on _cuda_available()
- AARE_CUDA_ARCHITECTURES=native needs CMake 3.24, project min is 3.15
- make docs somewhere with sphinx+breathe; new .rst files never rendered
- fmt nvcc warnings: gone at -std=c++20 or with -isystem
2026-09-02 15:00:39 +02:00
Khalil Ferjaoui 1d963a778b Merge branch 'main' into feature/cuda_clusterfinder
Build on RHEL8 / build (push) Successful in 3m34s
Build on RHEL9 / build (push) Successful in 3m42s
Run tests using data on local RHEL8 / build (push) Failing after 4m46s
2026-09-02 14:18:37 +02:00
kferjaoui 9921944772 CUDA: zero-copy collection, pipelined slots, optional kernel timing
Build on RHEL8 / build (push) Successful in 3m34s
Build on RHEL9 / build (push) Successful in 3m35s
Run tests using data on local RHEL8 / build (push) Successful in 4m13s
Host-side pipeline work ported from the benchmark branch. The kernel is
unchanged, so cluster results are identical.

  - collect_view()/BatchView: read clusters in place from the pinned D2H
    buffer instead of copying one ClusterVector per frame. The view is
    released back to the finder, so keep nothing that borrows it.
  - reserve_output_slots(), chunk_size_for(): pre-pin the output slots and
    size a batch to fit one.
  - find_cluster_views_batched_iter(): submits chunk i+1 before collecting
    chunk i.
  - time_kernels, default off. avg_kernel_time_ms() returns NaN unless
    enabled, and always for the Graph variant.
  - n_streams defaults to 4 everywhere.

ClusterFinderCUDA.ipynb cut to CPU MT vs batched CUDA plus a cluster-set
diff, and it now reports peak clusters/frame.

Removed src/ClusterFinderCUDA{,_old}.test.cu and src/Makefile (early C++
benchmarks, never in the CMake build), the perf and Frozen-vs-CUDA
notebooks, and helper.py.
2026-09-02 12:20:38 +02:00
Erik Fröjdh eddb919328 FastPedestal + optimizations in the cluster finder (#341)
Build on RHEL9 / build (push) Successful in 2m42s
Build on RHEL8 / build (push) Successful in 3m21s
Run tests using data on local RHEL8 / build (push) Failing after 3m56s
Build on local RHEL8 / build (push) Successful in 2m54s
Optimized pedestal tracking with FastPedestal and performance improvements to the cluster finder. 
- ~2x faster hitting 14k FPS on benchmark dataset on 16 core threadripper pro


**Changes**
- No bounds check on interior of frame. (only within half of a cluster)
(~15% improvement)
- Cache threshold (std*n_rms) Biggest improvement comes from not
computing std for each access
- Cache pedestal subtracted frame
- Switched to FastPedestal which assumes we reached steady state

**Things tried but rejected:**
- Always store cluster values, commit on val==max (~15% drop in frame
rate)

**Options**
- using 16 bit clusters and 16 bit pedestal. (slows down the single
threaded case but allow us to reach 12k with multi threading)

**Other notes on performance**
- Passing in memory frames from python and doing nothing: 0.8us/frame
- Passing + pedestal subtraction: 28us/frame
- Passing + pedestal + cluster finding (no store): 652us/frame
- Passing + pedestal + cluster finding: 886 us/frame
2026-09-01 18:41:12 +02:00
Erik Fröjdh 85f9d0176a Dev/guess clusters (#355)
Build on RHEL9 / build (push) Successful in 2m40s
Build on RHEL8 / build (push) Successful in 3m17s
Run tests using data on local RHEL8 / build (push) Successful in 3m53s
Build on local RHEL8 / build (push) Successful in 2m47s
- exposing ClusterVector::empty in python
- added member guess_n_clusters
2026-08-25 09:15:38 +02:00
Erik Fröjdh 4f6b69fba6 Removing deprecated use of lmfit (#344)
Build on RHEL9 / build (push) Successful in 2m44s
Build on RHEL8 / build (push) Successful in 3m9s
Run tests using data on local RHEL8 / build (push) Successful in 3m58s
Build on local RHEL8 / build (push) Successful in 2m44s
Removed the lmfit based fitting and the old API for calling functions

closes #296
2026-08-14 10:31:07 +02:00
Erik Fröjdh e26db97b5c Prototype multi threaded file reader (#343)
Build on RHEL9 / build (push) Successful in 2m40s
Build on RHEL8 / build (push) Successful in 3m4s
Run tests using data on local RHEL8 / build (push) Successful in 3m51s
Build on local RHEL8 / build (push) Successful in 2m46s
- Reading files from nfs shares tops out at ~1GB/s for single threaded
reads.
- MultiThreadedFileReader uses our File wrapper to read a generic file
in parallel
- Placed in aare::experimental to show that it's not production ready
2026-08-11 15:28:54 +02:00
kferjaoui ce256dd30f Save changes 2026-08-06 14:01:23 +02:00
kferjaoui 3504d96336 ClusterFinderFrozen: CPU finder for CUDA correctness study
- Diagnostic twin of ClusterFinder: identical decisions, but the pedestal is
  frozen per frame (snapshot at frame start, updates deferred to frame end),
  matching the CUDA kernel's update model.
- Isolates pedestal-update timing as the sole remaining CPU/GPU mismatch.
- Adds class + bindings + factory, validation notebook, and helper utilities.
2026-08-03 11:59:27 +02:00
kferjaoui 156667efde Merge 'origin/main' into feature/cuda_clusterfinder 2026-07-21 15:21:49 +02:00
4aadb6f7f0 refactor: hide Minuit2 from aare's public API (#331)
Build on RHEL9 / build (push) Successful in 3m13s
Build on RHEL8 / build (push) Successful in 3m52s
Run tests using data on local RHEL8 / build (push) Successful in 3m56s
Build on local RHEL8 / build (push) Successful in 2m45s
- Move Chi2.hpp from include/aare/ to src/ (private)
- Pimpl on FitModel<Model>: MnUserParameters/MnStrategy behind opaque
src/FitModelImpl.hpp, no Minuit2 includes in public headers
- Move fit_pixel/fit_3d bodies to Fit.cpp with explicit instantiations
for all 8 models; drop FCN template param from public API
- CMake: aare::Minuit2 wrapped in $<BUILD_INTERFACE:...> (hidden from
exported targets, same pattern as lmfit), MINUIT2_INSTALL OFF, Chi2.hpp
removed from PUBLICHEADERS
- Update python bindings and benchmark callsites accordingly

---------

Co-authored-by: Erik Fröjdh <erik.frojdh@psi.ch>
Co-authored-by: Alice <alice.mazzoleni@psi.ch>
2026-07-02 16:04:22 +02:00
Erik Fröjdh b4b28fc9e0 len() and pedestal subtraction (#328)
Build on RHEL9 / build (push) Successful in 2m57s
Build on RHEL8 / build (push) Successful in 3m28s
Run tests using data on local RHEL8 / build (push) Successful in 4m0s
Build on local RHEL8 / build (push) Successful in 2m44s
Collection of small improvements in usability: 

- NDView works for large arrays
- Direct subtraction of Pedestal from np.array
- len() support for python bindings of files
2026-06-15 11:38:06 +02:00
mazzol_aandErik Fröjdh ee7503082d Dev/matterhorn decoder (#324)
Build on RHEL9 / build (push) Successful in 2m49s
Build on RHEL8 / build (push) Successful in 3m35s
Run tests using data on local RHEL8 / build (push) Successful in 4m1s
Build on local RHEL8 / build (push) Successful in 3m9s
- reshape image directly in decoder such that first dimension is num
counters
- take into account chip artefact in decoder.

---------

Co-authored-by: Erik Fröjdh <erik.frojdh@psi.ch>
2026-06-11 15:04:20 +02:00
kferjaoui 5922c73c07 feat(ClusterFinderCUDA): async submit_batch/collect API
Build on RHEL8 / build (push) Successful in 3m16s
Build on RHEL9 / build (push) Successful in 3m26s
Run tests using data on local RHEL8 / build (push) Successful in 9m42s
- Eliminate the ~200–300 µs inter-batch idle gap by allowing two batches
to be in-flight simultaneously:
  - submit_batch() enqueues H2D+kernel+D2H without blocking
  - collect() syncs via cudaEventSynchronize (not
  cudaStreamSynchronize) so a queued second batch runs uninterrupted.

- Two ping-pong output slots (NUM_SLOTS=2) with per-slot pinned buffers
and cudaEventDisableTiming sync events.
- find_clusters_batched() keeps its direct implementation.

* Measured: 0.026 -> 0.022 ms/frame (~18%).
2026-05-28 16:23:37 +02:00
kferjaoui 4c66802980 perf(ClusterFinderCUDA): FP32 device pedestal and bulk memcpy drain
Build on RHEL8 / build (push) Successful in 3m0s
Build on RHEL9 / build (push) Successful in 3m41s
Run tests using data on local RHEL8 / build (push) Successful in 3m47s
- Device pedestal arrays (mean/sum/sum2) are now float instead of
  double: halves global-memory bandwidth for pedestal reads/writes and
  eliminates FP64 arithmetic in the kernel (3.3x kernel speedup,
  15µs -> 4.6µs).

- Replace the per-cluster push_back loop in the D2H drain with a
  single resize()+memcpy().
2026-05-21 14:12:02 +02:00
Khalil Ferjaoui 52b5cf6b9f Feature/gauss+plateau (#312)
Build on RHEL9 / build (push) Successful in 2m27s
Build on RHEL8 / build (push) Successful in 3m1s
Run tests using data on local RHEL8 / build (push) Successful in 3m54s
Build on local RHEL8 / build (push) Successful in 2m36s
Adds three Minuit2-backed spectrum models to the Python-exposed fitting
API:

- `GaussianErfcPlateau`
- `GaussianChargeSharing`
- `GaussianChargeSharingKb`

Closes #297
2026-05-21 08:33:02 +02:00
kferjaoui 6a12e3de24 Refactor ClusterFinderCUDA
Build on RHEL9 / build (push) Successful in 3m22s
Build on RHEL8 / build (push) Successful in 3m28s
Run tests using data on local RHEL8 / build (push) Successful in 3m37s
Rework the multi-stream pipeline to eliminate per-frame sync barriers and
fix the D2H staging architecture.

Sync reduction:
- Replace one cudaStreamSynchronize per frame with one per stream per batch,
  cutting synchronisation calls from O(n_frames x n_streams) to O(n_streams)
- Introduce a unified per-frame D2H output layout [uint32_t count | clusters[max]]
  stored in a single class-level lazy-allocated pinned pool (h_output_pinned),
  replacing the per-stream separate cluster/count device buffers
- Move CUDA event pool from per-stream fixed-size to per-frame-slot lazy-allocated,
  enabling correct kernel timing across any batch size

Pinned H2D without CPU-side copy:
- Add register_input_buffer(ptr, bytes) / unregister_input_buffer() wrapping
  cudaHostRegister so callers can pin their existing batch buffer once; all
  find_clusters_batched() slices then transfer at DMA speed (~22 GB/s) instead
  of ~15 GB/s for pageable, with no extra memcpy or WC-memory penalty

Result (RTX 4090, 400x400 uint16, 3x3 clusters, batch=2000, 5 streams):
  Before: ~34 µs/frame  ->  After: ~28 µs/frame  (−18 %)
2026-05-18 16:30:13 +02:00
kferjaoui 41d5184e1b Fix ClusterVector move semantics 2026-05-06 11:30:59 +02:00
kferjaoui 88e0e8d678 Optimize CUDA cluster finder transfers and kernel hot path
Build on RHEL8 / build (push) Successful in 2m51s
Build on RHEL9 / build (push) Successful in 3m15s
Run tests using data on local RHEL8 / build (push) Successful in 3m47s
- Use per-stream pinned host staging buffers for truly async CUDA transfers.
- Avoid reserving full device capacity per result frame.
- Reduce kernel work by delaying cluster payload construction.
- Use squared comparisons and removing per-pixel sqrtf() ops.
2026-04-30 18:23:31 +02:00
kferjaoui 34e69a8065 Add per-frame kernel timing via CUDA events
Build on RHEL8 / build (push) Successful in 3m13s
Build on RHEL9 / build (push) Successful in 3m37s
Run tests using data on local RHEL8 / build (push) Successful in 3m51s
2026-04-28 13:09:25 +02:00
kferjaoui ac96d1f688 Implement mixed precision: f32 stencil, f64 pedestal
Build on RHEL8 / build (push) Successful in 2m53s
Build on RHEL9 / build (push) Successful in 3m15s
Run tests using data on local RHEL8 / build (push) Successful in 3m47s
- Stencil arithmetic and shared memory use float (COMPUTE_TYPE alias).
- Pedestal accumulation stays double to preserve variance accuracy.

Notes:
- On RTX 4090, FP32 throughput is ~64× higher than FP64, so moving
  stencil math to float improves performance.
- Using float also avoids shared memory bank conflicts: stride-18 maps
  to distinct banks for 32-bit values, but caused conflicts with 64-bit.
2026-04-27 14:56:40 +02:00
kferjaoui fddef977af Exclude notebooks from JSON check 2026-04-27 11:53:15 +02:00
mazzol_a 2736d975c5 Dev/enable custom etas (#305)
Build on local RHEL8 / build (push) Successful in 2m32s
Build on RHEL9 / build (push) Successful in 2m29s
Build on RHEL8 / build (push) Successful in 2m52s
Run tests using data on local RHEL8 / build (push) Successful in 3m49s
- Allowing the users more flexibility to play around with custom eta
functions without touching the c++ code

- passing vector of eta values to ``transform_eta_values`` 

```
from aare import Interpolator, ClusterVector, Etai, Cluster
import numpy as np 

def custom_eta(cluster_pixel_coordinate_x, cluster_pixel_coordinate_y, cluster_data):
    # dummy custom eta function that just returns the sum of the cluster data
    eta = Etai()
    eta.x = 0.1 # dummy x value
    eta.y = 0.1 # dummy y value
    eta.sum = np.sum(cluster_data) # sum of the cluster data as the "energy
    return eta

# Create a dummy eta distribution and bins
eta_distribution = np.zeros((10, 10, 1)) # dummy eta distribution
etax_bins = np.linspace(0, 1.0, 11)
etay_bins = np.linspace(0, 1.0, 11)
e_bins = np.array([0., 10.]) # dummy energy bins

# Create the interpolator
interpolator = Interpolator(eta_distribution, etax_bins, etay_bins, e_bins)

# Create a dummy cluster vector
cluster_vector = ClusterVector()
cluster_vector.push_back(Cluster(10, 5, np.ones(shape=9, dtype = np.int32)))
cluster_vector.push_back(Cluster(20, 10, np.ones(shape=9, dtype = np.int32)))

# Create dummy etas for the clusters
cluster_array = np.array(cluster_vector)
etas = np.array([custom_eta(cluster["x"], cluster["y"], cluster["data"]) for cluster in cluster_array])

# transform eta values to uniform coordinates 
uniform_coordinates = interpolator.transform_eta_values(etas)

# Interpolate to get the photon coordinates e.g. apply interpolation logic 
photon_coordinates_x = cluster_array["x"] + uniform_coordinates["x"] # add to pixel coordinate 
photon_coordinates_y = cluster_array["y"] + uniform_coordinates["y"] # add to pixel coordinate 

```
advantage: full control over interpolation logic, 
downside: inefficient quite some loops in python
- passing pre computed eta values to interpolate function 
```
Interpolator.interpolate(cluster_vector, etas) 
```
downside: less flexibility in interpolation logic. 
downside: People might misuse it instead of using interpolate directly
with a pre compiled eta function implemented in c++
2026-04-24 14:01:13 +02:00
Khalil Ferjaoui 7c91ce99c2 Merge branch 'main' into feature/cuda_clusterfinder
Build on RHEL8 / build (push) Successful in 3m15s
Build on RHEL9 / build (push) Successful in 3m39s
Run tests using data on local RHEL8 / build (push) Successful in 3m51s
2026-04-23 13:52:52 +02:00
kferjaoui e894bdac9b Add Python bindings for CUDA cluster finder
Build on RHEL8 / build (push) Successful in 2m50s
Build on RHEL9 / build (push) Successful in 2m57s
Run tests using data on local RHEL8 / build (push) Successful in 3m38s
- Add bind_ClusterFinderCUDA.hpp with pybind11 bindings for
  ClusterFinderCUDA
- Build CUDA bindings as separate _aare_cuda.so to avoid
  segfaults from mixing nvcc and gcc compiled code in the
  same shared object
- Re-export CUDA classes onto _aare in __init__.py so user
  code uses `from aare import ClusterFinderCUDA` regardless
  of which .so hosts the class
- Factory in ClusterFinder.py selects backend; RuntimeError
  if GPU requested on CPU-only build
- Update python/CMakeLists.txt: _aare_cuda module gated
  behind AARE_CUDA and AARE_PYTHON_BINDINGS
- Add validation notebook: ~20x speedup vs sequential ClusterFinder
2026-04-23 11:43:40 +02:00
mazzol_aandErik Fröjdh 6ff664f812 allow passing mask to clustervector (#304)
Build on RHEL9 / build (push) Successful in 2m24s
Build on RHEL8 / build (push) Successful in 2m54s
Run tests using data on local RHEL8 / build (push) Successful in 3m52s
Build on local RHEL8 / build (push) Successful in 2m34s
- passing mask to ClusterVector 
- creates a copy of the ClusterVector

Co-authored-by: Erik Fröjdh <erik.frojdh@psi.ch>
2026-04-17 17:13:02 +02:00
lunin_l 8f8173feb6 CI/CD: Integrate pre-commit hooks and GitHub Actions workflow (#303)
Build on RHEL8 / build (push) Successful in 2m48s
Build on RHEL9 / build (push) Successful in 3m8s
Run tests using data on local RHEL8 / build (push) Successful in 3m34s
Build on local RHEL8 / build (push) Successful in 2m24s
To improve codebase quality and reduce human error, this PR introduces
the pre-commit framework. This ensures that all code adheres to project
standards before it is even committed, maintaining a consistent style
and catching common mistakes early.

Key Changes:

- Code Formatting: Automated C++ formatting using clang-format (based on
the project's .clang-format file).
- Syntax Validation: Basic checks for file integrity and syntax.
- Spell Check: Automated scanning for typos in source code and comments.
- CMake Formatting: Standardization of CMakeLists.txt and .cmake
configuration files.
- GitHub Workflow: Added a CI action that validates every Pull Request
against the pre-commit configuration to ensure compliance.

The configuration includes a [ci] block to handle automated fixes within
the PR. Currently, this is disabled. If we want the CI to automatically
commit formatting fixes back to the PR branch, this can be toggled to
true in .pre-commit-config.yaml.

```yaml
ci:
  autofix_commit_msg: [pre-commit] auto fixes from pre-commit hooks
  autofix_prs: false
  autoupdate_schedule: monthly
```

The last large commit with the fit functions, for example, was not
formatted according to the clang-format rules. This PR would allow to
avoid similar mistakes in the future.

Python fomat with `ruff` for tests and sanitiser for `.ipynb` notebooks
can be added as well.
2026-04-14 11:52:23 +02:00
Erik FröjdhandKhalil Ferjaoui a25f5d2344 Access to parameter names and fast erf approx (#298)
Build on RHEL8 / build (push) Successful in 2m44s
Build on RHEL9 / build (push) Successful in 3m3s
Run tests using data on local RHEL8 / build (push) Successful in 3m39s
Build on local RHEL8 / build (push) Successful in 2m23s
- Set parameter starting values, limits or fix through name as well as
index
- Updated parameter names for the scurve
- Fast approximation to erf function (~10% speedup of fitting)

---------

Co-authored-by: Khalil Ferjaoui <khalilferjaoui@yahoo.fr>
2026-04-02 13:33:37 +02:00
Khalil FerjaouiandErik Fröjdh a6afa45b3b Feature/minuit2 wrapper (#279)
Build on RHEL8 / build (push) Successful in 3m6s
Build on RHEL9 / build (push) Successful in 3m20s
Run tests using data on local RHEL8 / build (push) Successful in 3m36s
Build on local RHEL8 / build (push) Successful in 2m21s
## Unified Minuit2 fitting framework with FitModel API

### Models (`Models.hpp`)
Consolidate all model structs (Gaussian, RisingScurve, FallingScurve)
into a
single header. Each model provides: `eval`, `eval_and_grad`, `is_valid`,
`estimate_par`, `compute_steps`, and `param_info` metadata. No Minuit2
dependency.

### Chi2 functors (`Chi2.hpp`)
Generic `Chi2Model1DGrad` (analytic gradient) templated on the model
struct.
Replaces the separate Chi2Gaussian, Chi2GaussianGradient,
Chi2Scurves, and Chi2ScurvesGradient headers.

### FitModel (`FitModel.hpp`)
Configuration object wrapping `MnUserParameters`, strategy, tolerance,
and
user-override tracking. User constraints (fixed parameters, start
values, limits)
always take precedence over automatic data-driven estimates.

### Fit functions (`Fit.hpp`)
- `fit_pixel<Model, FCN>(model, x, y, y_err)` -> single-pixel,
self-contained
- `fit_pixel<Model, FCN>(model, upar_local, x, y, y_err)` -> pre-cloned
upar for hot loops
- `fit_3d<Model, FCN>(model, x, y, y_err, ..., n_threads)` ->
row-parallel over pixel grid

### Python bindings
- `Pol1`, `Pol2`, `Gaussian`, `RisingScurve`, `FallingScurve` model
classes with
  `FixParameter`, `SetParLimits`, `SetParameter`, and properties for
  `max_calls`, `tolerance`, `compute_errors`
- Single `fit(model, x, y, y_err, n_threads)` dispatch replacing the old
`fit_gaus_minuit`, `fit_gaus_minuit_grad`, `fit_scurve_minuit_grad`,
etc.

### Benchmarks
- Updated `fit_benchmark.cpp` (Google Benchmark) to use the new FitModel
API
- Jupyter notebooks for 1D and 3D S-curve fitting (lmfit vs Minuit2
analytic)
- ~1.8x speedup over lmfit, near-linear thread scaling up to physical
core count

---------

Co-authored-by: Erik Fröjdh <erik.frojdh@psi.ch>
2026-03-30 09:12:23 +02:00
mazzol_aandErik Fröjdh d9ff73e8b2 added matterhorn transformation tests (#284)
- added matterhorn transformation tests

---------

Co-authored-by: Erik Fröjdh <erik.frojdh@psi.ch>
2026-03-17 16:54:06 +01:00
mazzol_a 2a3c121574 Fix/test (#278)
Build on RHEL8 / build (push) Successful in 2m29s
Build on RHEL9 / build (push) Successful in 2m40s
Build on local RHEL9 / build (push) Successful in 1m9s
Run tests using data on local RHEL8 / build (push) Has been cancelled
Build on local RHEL8 / build (push) Has been cancelled
2026-02-26 14:23:45 +01:00
mazzol_a 2139e5843c Dev/stuff from pyctbgui (#273)
Build on RHEL8 / build (push) Successful in 2m23s
Build on RHEL9 / build (push) Successful in 2m35s
Run tests using data on local RHEL8 / build (push) Failing after 3m19s
Matterhorn10 Transform 
some other Transformations from pyctbGUI 
added method get_reading_mode for easier error handling in decoders 


## TODO: 

- proper error handling for all other decoders 
- proper documentation for all other decoders 
- refactoring all other decoders to store hard coded values in a Struct
ChipSpecification
2026-02-19 16:12:44 +01:00
218f31ce60 Dev/multiple rois in aare (#263)
Build on RHEL8 / build (push) Successful in 2m23s
Build on RHEL9 / build (push) Successful in 2m32s
Run tests using data on local RHEL8 / build (push) Failing after 3m14s
Reading multiple ROI's for aare 

- read_frame, read_n etc throws for multiple ROIs
- new functions read_ROIs, read_n_ROIs 
-  read_roi_into (used for python bindings - to not copy) 

all these functions use get_frame or get_frame_into where one passes the
roi_index
## Refactoring:
- each roi keeps track of its subfiles that one has to open e.g.
subfiles can be opened several times
- refactored class DetectorGeometry - keep track of the updated module
geometries in new class ROIGeometry.
- ModuleGeometry updates based on ROI

## ROIGeometry: 
- stores number of modules overlapping with ROI and its indices
- size of ROI 

Note: only tested size of the resulting frames not the actual values

---------

Co-authored-by: Erik Fröjdh <erik.frojdh@psi.ch>
Co-authored-by: Erik Fröjdh <erik.frojdh@gmail.com>
2026-02-18 10:57:56 +01:00
mazzol_aandErik Fröjdh b77a576f72 Dev/automate tests using data (#267)
Build on RHEL8 / build (push) Successful in 2m13s
Build on RHEL9 / build (push) Successful in 2m37s
Run tests using data on local RHEL8 / build (push) Successful in 3m12s
- automatically run python tests 
- automatically run test using data files on local runner from gitea
- fixed some of the workflows

---------

Co-authored-by: Erik Fröjdh <erik.frojdh@psi.ch>
2026-01-20 17:20:48 +01:00
mazzol_a fb95e518b4 Dev/interpolation documentation (#255)
Build on RHEL8 / build (push) Successful in 3m5s
Build on RHEL9 / build (push) Successful in 3m21s
- added transform_eta_values for easier debugging more control for the
user
- updated Documentation
2025-12-16 13:06:28 +01:00