From 85f9d0176a455cc9daa04f5aba441ab4fe815707 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20Fr=C3=B6jdh?= Date: Tue, 25 Aug 2026 09:15:38 +0200 Subject: [PATCH] Dev/guess clusters (#355) - exposing ClusterVector::empty in python - added member guess_n_clusters --- RELEASE.md | 2 ++ include/aare/ClusterFile.hpp | 13 +++++++++++++ python/src/bind_ClusterFile.hpp | 4 +++- python/src/bind_ClusterVector.hpp | 3 ++- python/tests/test_ClusterFile.py | 4 +++- python/tests/test_ClusterVector.py | 4 +++- src/ClusterFile.test.cpp | 4 ++++ 7 files changed, 30 insertions(+), 4 deletions(-) diff --git a/RELEASE.md b/RELEASE.md index 8d647df4..638b8b3b 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -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 diff --git a/include/aare/ClusterFile.hpp b/include/aare/ClusterFile.hpp index 885d4b45..4e416c23 100644 --- a/include/aare/ClusterFile.hpp +++ b/include/aare/ClusterFile.hpp @@ -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 diff --git a/python/src/bind_ClusterFile.hpp b/python/src/bind_ClusterFile.hpp index fbdd1909..0831d64f 100644 --- a/python/src/bind_ClusterFile.hpp +++ b/python/src/bind_ClusterFile.hpp @@ -47,6 +47,8 @@ void define_ClusterFile(py::module &m, const std::string &typestr) { }) .def("set_roi", &ClusterFile::set_roi, py::arg("roi")) .def("tell", &ClusterFile::tell) + .def("estimate_n_clusters", + &ClusterFile::estimate_n_clusters) .def( "set_noise_map", [](ClusterFile &self, py::array_t noise_map) { @@ -82,4 +84,4 @@ void define_ClusterFile(py::module &m, const std::string &typestr) { }); } -#pragma GCC diagnostic pop \ No newline at end of file +#pragma GCC diagnostic pop diff --git a/python/src/bind_ClusterVector.hpp b/python/src/bind_ClusterVector.hpp index 254f96a9..a3d59f57 100644 --- a/python/src/bind_ClusterVector.hpp +++ b/python/src/bind_ClusterVector.hpp @@ -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::size) + .def("empty", &ClusterVector::empty) .def("item_size", &ClusterVector::item_size) .def_property_readonly("fmt", [typestr](ClusterVector &self) { @@ -169,4 +170,4 @@ void define_3x3_reduction(py::module &m) { py::arg("clustervector")); } -#pragma GCC diagnostic pop \ No newline at end of file +#pragma GCC diagnostic pop diff --git a/python/tests/test_ClusterFile.py b/python/tests/test_ClusterFile.py index 5e032009..3e2fb81d 100644 --- a/python/tests/test_ClusterFile.py +++ b/python/tests/test_ClusterFile.py @@ -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 \ No newline at end of file + assert hist_aare == hist_py diff --git a/python/tests/test_ClusterVector.py b/python/tests/test_ClusterVector.py index 0ae96c3e..30f934fa 100644 --- a/python/tests/test_ClusterVector.py +++ b/python/tests/test_ClusterVector.py @@ -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() \ No newline at end of file + assert (cv_masked_array[0]["data"] == np.ones((3,3),dtype=np.int32)).all() diff --git a/src/ClusterFile.test.cpp b/src/ClusterFile.test.cpp index 78ba3e61..15caf6bb 100644 --- a/src/ClusterFile.test.cpp +++ b/src/ClusterFile.test.cpp @@ -17,6 +17,8 @@ TEST_CASE("Read one frame from a cluster file", "[.with-data]") { REQUIRE(std::filesystem::exists(fpath)); ClusterFile> 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 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);