Catch2 v3 is ~100 small translation units that together took 58.5 s of summed compile time in a Release build, more than aare_core itself. Batched into unity sources of 16 files they take 13 s in 10 steps. The test executable itself is unchanged. Clean Release build with tests and bindings: 28 s -> 23 s on 10 cores; all 277 test cases pass, both through run_tests and ctest.
23 KiB
Release notes
Next
New Features:
- Added the Python
xytype withrowandcolfields, enabling access toRawFile.geometry,RawMasterFile.detector_layout, andRawMasterFile.udp_interfaces_per_module. It unpacks asrow, col. - Added Windows wheels (
win_amd64, Python 3.12 to 3.14) to the wheel build, so releases publish them to PyPI alongside the Linux and macOS wheels.
Build:
- libzmq is only fetched or searched for with
-DAARE_ZMQ=ON(default OFF) and is then available asaare::zmq. Nothing in aare links it yet, and skipping it removes about 8 s from every CMake configure. - Minuit2, Catch2, and libzmq when enabled, are compiled as unity builds (about 4-5x less compile time), and the fetched dependencies are shallow clones, which makes a first configure faster and the build directory smaller.
- The Python extension is compiled from one translation unit per binding area instead of a single file, so the bindings build in parallel and editing one binding header recompiles only its unit. The module's contents and registration order are unchanged.
- The Python extension is no longer link-time optimized by default. The hot
loops are header templates compiled at
-O3inside the extension and LTO cannot reach the staticaare_core, so it only added a 20 s single-threaded link and made the binary 15 % smaller. LTO follows the standard CMake property now:set_property(TARGET _aare PROPERTY INTERPROCEDURAL_OPTIMIZATION ON)or-DCMAKE_INTERPROCEDURAL_OPTIMIZATION=ONrestores it.
2026.9.30
New Features:
- Added
pedestal_readytoClusterFinderandClusterFinderMTin C++ and Python to check whether enough pedestal frames have been pushed to find clusters. - Added the Python
FrameDiscardPolicyenum withNoDiscard,Discard, andDiscardPartial, enabling access toRawMasterFile.frame_discard_policy. - Added the Python
Pedestalfactory withdtypeselection, matchingFastPedestaland defaulting tofloat64output. - Added
FastPedestalin C++ and Python for per-pixel running mean and population standard deviation. It supports exponentially - Added
ClusterFile.frames()andClusterFile.chunks()in C++ and Python for iteration from the current file position. Frames preserve empty frames and frame numbers; chunks support an optional positive size override. Python's default file iteration continues to yield chunks. - Added
FastPedestalin C++ and Python for per-pixel running mean, population variance, and standard deviation. It supports exponentially weighted updates, initialization from files, direct subtraction from NumPy arrays, andfloat64,float32, andint16output types. - Added the
Pedestal_i16Python binding alongsideFastPedestal_d,FastPedestal_f, andFastPedestal_i16. PedestalTrackingPixelHistogram.fill_from_file()now uses parallel, double-buffered file reading and acceptsreader_threadsandreader_chunk_sizetuning parameters.- Added the
AARE_TUNE_LOCALCMake option to build with-march=nativeand-mtune=nativewhen supported. Binaries built with this option are specific to the local CPU and may not be portable. - Added string representator in python for Cluster and Eta
- Added roi slice method in python for easy slicing of numpy arrays
array[roi.slice()]. - added context manager for
aare.RawMasterFile - Added a configurable minimizer for the fit models, selected with the
minimizerconstructor argument or property in Python, orFitModel::SetMinimizerin C++.Minimizer.Migradremains the default.Minimizer.Fumiliis Minuit2's Gauss-Newton minimizer, which uses the analytic model derivatives and needs far fewer function evaluations.Minimizer.LevenbergMarquardtis a built-in, dependency-free Levenberg-Marquardt solver with the analytic Jacobian; it reflects steps at parameter limits and fits data cubes without per-pixel allocations. A fit that stalls away from a minimum, or whose width collapses below the spacing of the scan points, is reported as failed (all zeros) rather than returned as a result, so noisy or signal-free pixels may fail. Withcompute_errors, Fumili and LevenbergMarquardt report parameter errors from their linearised covariance instead of running Hesse. Pixels for which Fumili does not reach a valid minimum, which Minuit2's implementation cannot once a two-sided limit becomes active, are refitted with Migrad. Minimizer.LevenbergMarquardtis about twice as fast for every model: it evaluates the Jacobian at the trial point instead of re-evaluating accepted points, accumulates the normal equations with fixed-size loops, needs a single factorisation per iteration, and the erf-based models compute one exponential per point instead of two. Results are unchanged, except thatGaussianChargeSharingandGaussianChargeSharingKbnow evaluate the erf from the Gaussian's exponential, a rounding-level change. One iteration costs one model evaluation of themax_callsbudget, so pixels that used to exhaust the default budget of 100 may now converge.- Added
Minimizer.VarPro, a built-in variable projection solver. Every bundled model now declares the parameters it is linear in (linear_parandbasis_and_gradin C++, checked against the model derivatives by the tests). Each trial point solves those exactly and the Levenberg-Marquardt iteration runs over the nonlinear parameters only, so the fit needs fewer evaluations and does not depend on start values of the linear parameters. When the basis functions are nearly collinear, as for a polynomial on a narrow range far from x = 0, one more pass over the data refines the linear solution to the precision of the other minimizers. Limits on linear parameters, pixels that do not converge and models without the separable structure fall back toLevenbergMarquardt. Parameter errors are the same Gauss-Newton estimates. The Gaussian, plateau, charge-sharing and S-curve models also provide their basis functions for all scan points at once (basis_columns), a loop that compiles to vector instructions with the newmodel::fast_exp;evalandeval_and_grad, and with them the other minimizers, are unchanged.Minimizer.Migradremains the default. NDArray/NDViewexpressions now support scalar operands (2 * a + b / 4) and can be assigned to an existingNDArray, reusing its buffer.
API Changes:
-
The C++
NDArrayview constructor andNDArray::copy_from()takeNDView<const T, Ndim>. Mutable views still convert implicitly, so existing callers are unaffected, and read-only views can now be copied. -
Added C++ overloads of
adc_sar_05_06_07_08decode64to16,adc_sar_05_decode64to16andadc_sar_04_decode64to16that take the packed samples asNDView<const uint8_t, 2>. Words are assembled withmemcpy, so the buffer does not need to be 8-byte aligned. The existingNDView<uint64_t, 2>overloads are unchanged. -
NDArray+ - * /with a scalar now returns a lazy expression instead of anNDArrayand no longer converts the scalar to the element type. -
FitModelis now plain data without a pimpl (C++ users need to rebuild); it gainedstrategy(),lower_limit(),upper_limit()andvalue()accessors and lostimpl(). Bad parameter indices raisestd::out_of_range(IndexErrorin Python) andSetParLimitsrejectslo >= hi. The models inModels.hppno longer provide the Minuit specificcompute_stepsandcompute_rangeshelpers. -
Fit behaviour common to all minimizers: user start and fixed values are applied before the validity check and free start values are clamped into their limits; one-sided limits are open-ended for
LevenbergMarquardtwhile the Minuit2 minimizers use a wide two-sided range as before; withcompute_errors, fixed parameters and parameters ending on a limit report an error of 0 (fixed parameters previously reported 1.0 with Minuit2). -
FastPedestalvariance is now a privatedoubleintermediate. Removed the C++variance()/variance_unchecked()APIs and Pythonvar(). Standard deviation is calculated before conversion to the output type, avoiding overflow of intermediate variance forint16output. Negative variance from floating-point roundoff is clamped to zero. -
Pedestalnow always accumulates sums and sums of squares indouble, likeFastPedestal. Mean and standard deviation output types are unchanged. Removed the C++get_sum()/get_sum2()getters and Pythonsum/sum2properties; internal sums are no longer exposed. -
Removed the public
Pedestal.variance()andcached_std()APIs and C++update_std(). Usestd()to calculate the current population standard deviation; variance is now an internal implementation detail. -
Added
ClusterFile::read_frame(ClusterVector&)for allocation-reusing C++ reads. It returnsfalseat a clean end of file. The value-returning C++ overload now returnsstd::optional<ClusterVector<ClusterType>>and Pythonread_frame()returnsNoneat end of file. Incomplete frames still raise an error. -
Added an explicit boolean conversion to the C++
FilePtrtype. -
Removed the public C++
ClusterFile::openmethod. Construct a newClusterFileto reopen a file or change its mode. -
ClusterFindernow usesFastPedestal. It must receive 1000 pedestal frames before cluster finding;find_clusters()raises an error until initialization is complete. Addedupdate_threshold()to recompute the per-pixel detection thresholds. -
Added the
queue_depthconstructor argument toClusterFinderMTto configure the number of preallocated frame buffers per worker thread. -
Exposed
ClusterVector.empty()in the Python API. -
Added ClusterVector.estimate_n_clusters
-
Removed the lmfit dependency and the legacy
fit_gaus,fit_pol1,fit_scurve, andfit_scurve2APIs. UseGaussian,Pol1,RisingScurve, orFallingScurveand callmodel.fit(...)(orfit(model, ...)) instead. -
Removed the legacy
gaus,pol1,scurve, andscurve2function evaluators. Model objects are callable and provide the replacement, for exampleGaussian()(x, par). -
NDView<T, Ndim>now converts toNDView<const T, Ndim>;expand4to8bitandexpand24to32bitaccept const input views. -
RawMasterFile::geometry()is deprecetad and returns full detector geometry information including module geometry. UseRawMasterFile::module_layout()to get num_modules in x an y -
RawMasterFile::rois()always returns a list of rois (no optional). Per default it returns a list of one ROI element spawing the entire detector -
TimingMode::Autochanged toTimingMode::AUTO_TIMING,TimingMode::Triggerchanged toTimingMode::TRIGGER_EXPOSURE
Bugfixes:
-
Fitting a 3D data cube in Python without
y_errnow returnspar_errwhencompute_errorsis set, as fitting a single pixel already did. -
Samples with a zero error are ignored by every minimizer, as documented, also when they hold NaN. They are now removed before the start values are estimated; previously a masked NaN at a sample used by the estimate, such as the first or last point of
Pol1, made even Migrad fail. A pixel with fewer remaining samples than free parameters is reported as failed. -
Fitting a single pixel checks that
x,yandy_errhave the same size and raisesRuntimeErrorotherwise. Previously a shorteryory_errwas read past its end. -
Setting nSigma on
ClusterFinderandClusterFinderMTbefore the pedestal is ready no longer raises an error. The new value is used when the threshold is initialized. PreviouslyClusterFinderMT.set_nSigma()also left the worker threads with different nSigma values. -
The Python
Clusterconstructors validate that the data array holds exactly one value per pixel and raiseValueErrorotherwise. Previously a longer array wrote past the end of the cluster data. -
The Python CTB decoding helpers
adc_sar_*decode64to16,apply_custom_weights,expand24to32bit,expand4to8bitanddecode_my302validate their input throughmake_view. They require C-contiguous arrays of the documented rank and reject other dtypes withTypeErrorinstead of converting a copy. The ADC SAR decoders no longer reinterpret the byte buffer as aligned 64-bit words; a row length that is not a multiple of 8 bytes raisesValueErrorinstead of dropping the trailing bytes. Read-only arrays are accepted where the input is only read.decode_my302raisesValueErrorinstead ofRuntimeErrorfor a wrong input size. -
Mismatched operators inhibited vectorization in gcc of NDArray math operators
-
NDArray/NDViewmath operators and expressions were not vectorized foruint8_tand 64 bit integers, up to 30x slower than a plain loop. -
Removed the move constructor and move assignment of
ProducerConsumerQueue. They left the moved-from queue with a null buffer, crashing its destructor if it still held elements, and leaked the target's buffer on assignment. The queue is now non-movable, as in the upstream folly implementation; hold it in aunique_ptrto move it around. -
ProducerConsumerQueuerejects sizes below two withstd::invalid_argumentin all build types. The previous assertion was compiled out of Release builds, so a zero-size queue overflowed its buffer on the first write. -
Corrected the license metadata for vendored third-party code.
ProducerConsumerQueue.hppis taggedApache-2.0like upstream folly andNumpyHelpers.cppis taggedMITlike upstream libnpy. The MPL 2.0, Apache 2.0, and MIT texts ship inLICENSES/, and both the conda package and the Python wheel declareMPL-2.0 AND Apache-2.0 AND MITwith all three license files. Building the wheel now requires scikit-build-core 0.11 or newer. -
The wheel and conda package now ship
THIRD-PARTY-NOTICES.txtand the LGPL 2.1 text for the libraries compiled into the extension: Minuit2 (LGPL-2.1-or-later), {fmt} and nlohmann/json (MIT), and pybind11 (BSD-3-Clause). Their license expressions include these licenses. Minuit2 is now fetched at tagv6-40-02instead ofmaster, and libzmq atv4.3.5(MPL-2.0) instead ofv4.3.4(LGPL-3.0 with a static-linking exception). -
RawFileandFilereject raw files with frame padding disabled unless the frame discard policy isdiscardpartial. The constructor reports the master path before opening data subfiles. Legacy.rawmaster files now parse the frame discard policy so unpaddeddiscardpartialfiles remain readable. -
Fixed
CtbRawFile.read_frame(index)and reads afterseek(index)at subfile boundaries skipping a subfile, returning the wrong frame or raisingSubfile index out of range. -
Pedestalreports mismatched frame shapes with exceptions in all push overloads, including Debug builds, instead of aborting on assertions. -
Python
Pedestalconstructors reject negative dimensions and sample counts instead of converting them to large unsigned values. -
Pedestalclamps negative variance from floating-point roundoff to zero, preventing NaN standard deviations for nearly constant inputs. -
Python
Pedestal.push()now requires C-contiguousuint16frames without implicit conversion. Bothpush()andpush_with_threshold()validate that frames and thresholds are two-dimensional before constructing views, preventing incorrect results from unsupported array layouts or ranks. -
Python
ClusterFinder.push_pedestal_frame()andfind_clusters()now require C-contiguousuint16frames without implicit conversion, raisingTypeErrorinstead of silently copying and casting the input. The frame argument can also be passed by keyword asframe. -
Fixed a leaked empty
ClusterVectorat the end of PythonClusterFileiteration. Chunk iteration now rejects a zero chunk size. -
ClusterFile::read_clustersand Python iteration now report incomplete frame headers and cluster records instead of treating truncated files as a clean end of file, with or without ROI or noise filtering. -
ClusterFile::write_framenow reports incomplete writes instead of silently continuing with a truncated file. -
Gain-map application now checks the complete cluster footprint, preventing out-of-bounds access for cluster sizes larger than 3x3.
-
RawFilenow derives its frame count from the shortest selected raw subfile series across all ROIs. Frame-number reads use the same bounds, and Pythonlen(reader)returns the adjusted count. A warning is printed when subfile counts differ or their minimum differs from the recorded master count. The expected frame count is not used and master metadata is preserved. Warning-level logging is now enabled in non-verbose builds. -
Raw frame, batch, ROI, and frame-number read errors now include the attempted frame index and file path in C++ and Python, preserving subfile error details. Subfile errors omit redundant master-file context and C++ source locations; out-of-range errors include the available frame count. Synchronization errors identify the last raw data file for the affected module. Top-level frame bounds errors state the total frame count and that indices are zero-based.
-
Fixed
ClusterVectormove operations to transfer storage instead of copying every cluster. -
Validate that
ClusterVectormasks are one-dimensional, C-contiguous Boolean arrays. -
Preserve signed
ClusterVectorframe numbers when filtering or reducing cluster dimensions. -
Fixed broken reading of old (pre reordering) Moench03
-
Supports reading all timing modes supported in slsDetectorPackage (auto, trigger, gating, burst_trigger, trigger_gating)
2026.7.2
New Features:
- Added a new Minuit2-based fitting framework for
Gaussian,RisingScurve,FallingScurve,Pol1andPol2models. - 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.interpolatealongside clusters - Added
PixelHistogramandPedestalTrackingPixelHistogram aare.transfrom.Matterhorn10Transformhandles counter artefact in chip. Mind that enabling only one counter or three counters or enabling the wromg two counters e.g. 0,1 will still lead to erreneous data.aare.transfrom.Matterhorn10Transformreshapes data such that first dimension is number of counters- Added support for len() for files. Returns the number of frames
- Added support for direct subtraction of Pedestal from numpy array
- Added support to read files with disabled udp ports
Bugfixes:
- Fixed
split_task(first, last, n_threads)so task ranges now correctly respect thefirstoffset. Previously, non-zero starting indices could generate incorrect subranges. - Fixed overflow issue causing failed allocations for NDArrays abouve ~2GB
- Fixed libfmt failures due to consteval when building with C++20
2026.3.17
New Features:
- Decoding transceiver data from Matterhorn10
transformed_data = aare.transform.Matterhorn10Transform(num_counters=2, dynamic_range=16)(data) - Expanding 24 to 32 bit data
aare._aare.expand24to32bit(data, offset=4) - Decoding digital data from Mythen 302
transformed_data = aare.transform.Mythen302Transform(offset=4)(data) - added
aare.Interpolator.transform_eta_values. Function transforms\eta-values to uniform spatial coordinates. Should only be used for easier debugging. - New
to_string,string_to - Added exptime and period members to RawMasterFile including decoding
- Removed redundant
arr.value(ix,iy...)on NDArray usearr(ix,iy...) - Removed Print/Print_some/Print_all form NDArray (operator
<<still works) - Added const* version of .data()
- reading multiple ROI's supported for aare.
- Use
aare.RawFile.read_roi(roi_index=0)to read a specific ROI for the current frame - Use
aare.RawFile.read_rois()to read multiple ROIs for the current frame - Use
aare.RawFile.read_n_with_roi(num_frames = 2, roi_index = 0)to read multiple frames for a specific ROI. - Note
read_frameandread_nis not supported for multiple ROI's.
- Use
- Building conda/pypi pkgs for python 3.14. Removing 3.11 builds.
Bugfixes:
- multi threaded cluster finder doesnt drop frames if queues are full
- Round before casting in the cluster finder to avoid biasing clusters by truncating
2025.11.21
New Features:
-
Added SPDX-License-Identifier: MPL-2.0 to source files
-
Calculate Eta3 supports all cluster types
-
interpolation class supports using cross eta3x3 and eta3x3 on full cluster as well as eta2x2 on full cluster
-
interpolation class has option to calculate the rosenblatt transform
-
reduction operations to reduce Clusters of general size to 2x2 or 3x3 clusters
-
max_sum_2x2including index of subcluster with highest energy is now available from Python API -
interpolation supports bilinear interpolation of eta values for more fine grained transformed uniform coordinates
-
Interpolation is documented
-
Added tell to ClusterFile. Returns position in bytes for debugging
Resolved Features:
- calculate_eta coincides with theoretical definition
Bugfixes:
- eta calculation assumes correct photon center
- eta transformation to uniform coordinates starts at 0
- Bug in interpolation
- File supports reading new master json file format (multiple ROI's not supported yet)
API Changes:
- ClusterFinder for 2x2 Cluster disabled
- eta stores corner as enum class cTopLeft, cTopRight, BottomLeft, cBottomRight indicating 2x2 subcluster with largest energy relative to cluster center
- max_sum_2x2 returns corner as index
2025.8.22
Features:
- Apply calibration works in G0 if passes a 2D calibration and pedestal
- count pixels that switch
- calculate pedestal (also g0 version)
- NDArray::view() needs an lvalue to reduce issues with the view outliving the array
Bugfixes:
- Now using glibc 2.17 in conda builds (was using the host)
- Fixed shifted pixels in clusters close to the edge of a frame
2025.7.18
Features:
- Cluster finder now works with 5x5, 7x7 and 9x9 clusters
- Added ClusterVector::empty() member
- Added apply_calibration function for Jungfrau data
Bugfixes:
- Fixed reading RawFiles with ROI fully excluding some sub files.
- Decoding of MH02 files placed the pixels in wrong position
- Removed unused file: ClusterFile.cpp
2025.5.22
Features:
- Added scurve fitting
Bugfixes:
- Fixed crash when opening raw files with large number of data files
Download, Documentation & Support
Download
The Source Code: https://github.com/slsdetectorgroup/aare
Documentation
Documentation including installation details: https://github.com/slsdetectorgroup/aare
Support
erik.frojdh@psi.ch
alice.mazzoleni@psi.ch
dhanya.thattil@psi.ch