Merge branch 'main' into dev/strixels/remap_simple
Build on RHEL9 / build (push) Successful in 2m39s
Build on RHEL8 / build (push) Successful in 3m23s
Run tests using data on local RHEL8 / build (push) Successful in 4m0s

This commit is contained in:
2026-08-26 17:37:41 +02:00
committed by GitHub
12 changed files with 98 additions and 12 deletions
+4 -1
View File
@@ -4,6 +4,8 @@
### API Changes:
- 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``, and ``fit_scurve2`` APIs. Use ``Gaussian``, ``Pol1``,
``RisingScurve``, or ``FallingScurve`` and call ``model.fit(...)`` (or
@@ -11,6 +13,8 @@
- Removed the legacy ``gaus``, ``pol1``, ``scurve``, and ``scurve2`` function
evaluators. Model objects are callable and provide the replacement, for
example ``Gaussian()(x, par)``.
- ``NDView<T, Ndim>`` now converts to ``NDView<const T, Ndim>``;
``expand4to8bit`` and ``expand24to32bit`` accept const input views.
### Bugfixes:
- Fixed broken reading of old (pre reordering) Moench03
@@ -165,4 +169,3 @@ dhanya.thattil@psi.ch
+13
View File
@@ -144,6 +144,19 @@ class ClusterFile {
*/
size_t chunk_size() const { return m_chunk_size; }
/**
* @brief Estimate the number of clusters in the file from its size
*
* Frame headers are included in the estimate, so the result may be slightly
* larger than the actual number of clusters. The file position is not
* changed.
*
* @throws std::runtime_error if the file is not opened for reading
*/
size_t estimate_n_clusters() const {
return std::filesystem::file_size(m_filename) / sizeof(ClusterType);
}
/**
* @brief Set the region of interest to use when reading
* clusters. If set only clusters within the ROI will be
+10 -1
View File
@@ -12,6 +12,7 @@
#include <iostream>
#include <numeric>
#include <stdexcept>
#include <type_traits>
#include <vector>
namespace aare {
@@ -92,6 +93,12 @@ class NDView : public ArrayExpr<NDView<T, Ndim>, Ndim> {
: buffer_(buffer), strides_(c_strides<Ndim>(shape)), shape_(shape),
size_(num_elements(shape)) {}
template <typename U, std::enable_if_t<
std::is_convertible_v<U (*)[], T (*)[]>, int> = 0>
NDView(const NDView<U, Ndim> &other) noexcept
: buffer_(other.buffer_), strides_(other.strides_),
shape_(other.shape_), size_(other.size_) {}
template <typename... Ix>
std::enable_if_t<sizeof...(Ix) == Ndim, T &> operator()(Ix... index) {
return buffer_[element_offset(strides_, index...)];
@@ -216,6 +223,8 @@ class NDView : public ArrayExpr<NDView<T, Ndim>, Ndim> {
}
private:
template <typename, ssize_t> friend class NDView;
T *buffer_{nullptr};
std::array<ssize_t, Ndim> strides_{};
std::array<ssize_t, Ndim> shape_{};
@@ -263,4 +272,4 @@ template <typename T> NDView<T, 1> make_view(std::vector<T> &vec) {
return NDView<T, 1>(vec.data(), {static_cast<ssize_t>(vec.size())});
}
} // namespace aare
} // namespace aare
+2 -2
View File
@@ -35,7 +35,7 @@ uint32_t mask32to24bits(uint32_t input, BitOffset offset = {});
* @param offset Offset within the first byte to where the data starts (0-7
* bits)
*/
void expand24to32bit(NDView<uint8_t, 1> input, NDView<uint32_t, 1> output,
void expand24to32bit(NDView<const uint8_t, 1> input, NDView<uint32_t, 1> output,
BitOffset offset = {});
/**
@@ -43,7 +43,7 @@ void expand24to32bit(NDView<uint8_t, 1> input, NDView<uint32_t, 1> output,
* @param input input buffer with 4 bit values packed into 8 bit
* @param output output buffer with 8 bit values
*/
void expand4to8bit(NDView<uint8_t, 1> input, NDView<uint8_t, 1> output);
void expand4to8bit(NDView<const uint8_t, 1> input, NDView<uint8_t, 1> output);
/**
* @brief Apply custom weights to a 16-bit input value. Will sum up
+3 -1
View File
@@ -47,6 +47,8 @@ void define_ClusterFile(py::module &m, const std::string &typestr) {
})
.def("set_roi", &ClusterFile<ClusterType>::set_roi, py::arg("roi"))
.def("tell", &ClusterFile<ClusterType>::tell)
.def("estimate_n_clusters",
&ClusterFile<ClusterType>::estimate_n_clusters)
.def(
"set_noise_map",
[](ClusterFile<ClusterType> &self, py::array_t<int32_t> noise_map) {
@@ -82,4 +84,4 @@ void define_ClusterFile(py::module &m, const std::string &typestr) {
});
}
#pragma GCC diagnostic pop
#pragma GCC diagnostic pop
+2 -1
View File
@@ -72,6 +72,7 @@ void define_ClusterVector(py::module &m, const std::string &typestr) {
R"(calculates sum of 2x2 subcluster with highest energy and index relative to cluster center 0: top_left, 1: top_right, 2: bottom_left, 3: bottom_right
)")
.def_property_readonly("size", &ClusterVector<ClusterType>::size)
.def("empty", &ClusterVector<ClusterType>::empty)
.def("item_size", &ClusterVector<ClusterType>::item_size)
.def_property_readonly("fmt",
[typestr](ClusterVector<ClusterType> &self) {
@@ -169,4 +170,4 @@ void define_3x3_reduction(py::module &m) {
py::arg("clustervector"));
}
#pragma GCC diagnostic pop
#pragma GCC diagnostic pop
+3 -1
View File
@@ -14,6 +14,8 @@ from conftest import test_data_path
def test_cluster_file(test_data_path):
"""Test ClusterFile"""
f = ClusterFile(test_data_path / "clust/single_frame_97_clustrers.clust")
assert f.estimate_n_clusters() == 97
assert f.tell() == 0
cv = f.read_clusters(10) #conversion does not work
@@ -62,4 +64,4 @@ def test_read_clusters_and_fill_histogram(test_data_path):
hist_py = pickle.load(f)
#Compare the two histograms
assert hist_aare == hist_py
assert hist_aare == hist_py
+3 -1
View File
@@ -16,6 +16,7 @@ def test_create_cluster_vector():
assert cv.cluster_size_x == 3
assert cv.cluster_size_y == 3
assert cv.size == 0
assert cv.empty()
def test_push_back_on_cluster_vector():
@@ -27,6 +28,7 @@ def test_push_back_on_cluster_vector():
cluster = _aare.Cluster2x2i(19, 22, np.ones(4, dtype=np.int32))
cv.push_back(cluster)
assert cv.size == 1
assert not cv.empty()
arr = np.array(cv, copy=False)
assert arr[0]['x'] == 19
@@ -127,4 +129,4 @@ def test_masking():
assert cv_masked_array[0]["x"] == 1
assert cv_masked_array[0]["y"] == 2
assert (cv_masked_array[0]["data"] == np.ones((3,3),dtype=np.int32)).all()
assert (cv_masked_array[0]["data"] == np.ones((3,3),dtype=np.int32)).all()
+4
View File
@@ -17,6 +17,8 @@ TEST_CASE("Read one frame from a cluster file", "[.with-data]") {
REQUIRE(std::filesystem::exists(fpath));
ClusterFile<Cluster<int32_t, 3, 3>> f(fpath);
CHECK(f.estimate_n_clusters() == 97);
CHECK(f.tell() == 0);
auto clusters = f.read_frame();
CHECK(clusters.size() == 97);
CHECK(clusters.frame_number() == 135);
@@ -250,6 +252,8 @@ TEST_CASE("Read cluster from multiple frame file", "[.with-data]") {
SECTION("Read clusters from both frames") {
ClusterFile<ClusterType> f(fpath);
CHECK(f.estimate_n_clusters() == 8);
CHECK(f.tell() == 0);
auto clusters = f.read_clusters(2);
REQUIRE(clusters.size() == 2);
REQUIRE(clusters.frame_number() == 0);
+16 -1
View File
@@ -5,12 +5,16 @@
#include <cstddef>
#include <iostream>
#include <numeric>
#include <type_traits>
#include <vector>
using aare::NDView;
using aare::num_elements;
using aare::Shape;
static_assert(std::is_convertible_v<NDView<int, 2>, NDView<const int, 2>>);
static_assert(!std::is_convertible_v<NDView<const int, 2>, NDView<int, 2>>);
TEST_CASE("Calculate size from a shape") {
Shape<3> shape{2, 3, 4};
REQUIRE(num_elements(shape) == 24);
@@ -88,6 +92,17 @@ TEST_CASE("Element reference 1D with a const NDView") {
}
}
TEST_CASE("Convert a mutable NDView to a const NDView") {
std::vector<int> vec{1, 2, 3, 4};
NDView<int, 2> mutable_view(vec.data(), Shape<2>{2, 2});
NDView<const int, 2> const_view = mutable_view;
REQUIRE(const_view.data() == mutable_view.data());
REQUIRE(const_view.shape() == mutable_view.shape());
REQUIRE(const_view.strides() == mutable_view.strides());
}
TEST_CASE("Element reference 2D") {
std::vector<int> vec(12);
std::iota(vec.begin(), vec.end(), 0);
@@ -279,4 +294,4 @@ TEST_CASE("NDView over byte") {
auto v = aare::make_view(buf);
REQUIRE(v.shape()[0] == 5);
REQUIRE(v[0] == std::byte{0});
}
}
+2 -2
View File
@@ -144,7 +144,7 @@ uint32_t mask32to24bits(uint32_t input, BitOffset offset) {
return (input >> offset.value()) & mask24bits;
}
void expand4to8bit(NDView<uint8_t, 1> input, NDView<uint8_t, 1> output) {
void expand4to8bit(NDView<const uint8_t, 1> input, NDView<uint8_t, 1> output) {
if (2 * input.size() != output.size())
throw std::runtime_error(
@@ -162,7 +162,7 @@ void expand4to8bit(NDView<uint8_t, 1> input, NDView<uint8_t, 1> output) {
}
}
void expand24to32bit(NDView<uint8_t, 1> input, NDView<uint32_t, 1> output,
void expand24to32bit(NDView<const uint8_t, 1> input, NDView<uint32_t, 1> output,
BitOffset bit_offset) {
ssize_t bytes_per_channel = 3; // 24bit
+36 -1
View File
@@ -154,6 +154,20 @@ TEST_CASE("Expand container with 24 bit data to 32") {
}
}
TEST_CASE("Expand 24 bit values to 32 bit values from a const buffer") {
const uint8_t buffer[] = {
0x0F, 0x00, 0x00, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF,
};
aare::NDView<const uint8_t, 1> input(buffer, {9});
aare::NDArray<uint32_t, 1> out({3});
aare::expand24to32bit(input, out.view());
CHECK(out(0) == 0xF);
CHECK(out(1) == 0xFF);
CHECK(out(2) == 0xFFFFFF);
}
TEST_CASE("Expand 4 bit values packed into 8 bit to 8 bit values") {
{
uint8_t buffer[] = {
@@ -172,4 +186,25 @@ TEST_CASE("Expand 4 bit values packed into 8 bit to 8 bit values") {
CHECK(out(i) == expected_output[i]);
}
}
}
}
TEST_CASE("Expand 4 bit values packed into 8 bit to 8 bit values from a const "
"buffer") {
{
const uint8_t buffer[] = {
0x00, 0xF0, 0xFF, 0x00, 0xF0, 0xFF,
};
aare::NDView<const uint8_t, 1> input(&buffer[0], {6});
aare::NDArray<uint8_t, 1> out({12});
aare::expand4to8bit(input, out.view());
uint8_t expected_output[] = {
0x0, 0x0, 0x0, 0xF, 0xF, 0xF,
0x0, 0x0, 0x0, 0xF, 0xF, 0xF}; // assuming little endian
for (size_t i = 0; i < 12; ++i) {
CHECK(out(i) == expected_output[i]);
}
}
}