diff --git a/README.md b/README.md index 6c36775..119c3b0 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,10 @@ # aare Data analysis library for PSI hybrid detectors +## Documentation + +Detailed documentation including installation can be found in [Documentation](https://slsdetectorgroup.github.io/aare/) + ## Build and install diff --git a/RELEASE.md b/RELEASE.md index 7f7fed8..4b69ecd 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -1,5 +1,13 @@ # Release notes +### 2025.10.27 + +Features: + +- max_sum_2x2 including index of subcluster with highest energy is now available from Python API +- 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.10.1 Bugfixes: diff --git a/include/aare/CalculateEta.hpp b/include/aare/CalculateEta.hpp index 6df263a..93902fa 100644 --- a/include/aare/CalculateEta.hpp +++ b/include/aare/CalculateEta.hpp @@ -3,16 +3,10 @@ #include "aare/Cluster.hpp" #include "aare/ClusterVector.hpp" #include "aare/NDArray.hpp" +#include "aare/defs.hpp" namespace aare { -enum class corner : int { - cTopLeft = 0, - cTopRight = 1, - cBottomLeft = 2, - cBottomRight = 3 -}; - enum class pixel : int { pBottomLeft = 0, pBottom = 1, @@ -28,7 +22,7 @@ enum class pixel : int { template struct Eta2 { double x; double y; - int c{0}; + corner c{0}; T sum; }; @@ -66,11 +60,11 @@ calculate_eta2(const Cluster &cl) { (ClusterSizeX / 2) + (ClusterSizeY / 2) * ClusterSizeX; auto max_sum = cl.max_sum_2x2(); - eta.sum = max_sum.first; - int c = max_sum.second; + eta.sum = max_sum.sum; + corner c = max_sum.index; // subcluster top right from center - switch (static_cast(c)) { + switch (c) { case (corner::cTopLeft): if ((cl.data[cluster_center_index - 1] + cl.data[cluster_center_index]) != 0) diff --git a/include/aare/Cluster.hpp b/include/aare/Cluster.hpp index a4d2428..f0507d2 100755 --- a/include/aare/Cluster.hpp +++ b/include/aare/Cluster.hpp @@ -8,6 +8,7 @@ #pragma once +#include "defs.hpp" #include #include #include @@ -45,7 +46,7 @@ struct Cluster { * @return photon energy of subcluster, 2x2 subcluster index relative to * cluster center */ - std::pair max_sum_2x2() const { + Sum_index_pair max_sum_2x2() const { if constexpr (cluster_size_x == 3 && cluster_size_y == 3) { std::array sum_2x2_subclusters; @@ -56,9 +57,11 @@ struct Cluster { int index = std::max_element(sum_2x2_subclusters.begin(), sum_2x2_subclusters.end()) - sum_2x2_subclusters.begin(); - return std::make_pair(sum_2x2_subclusters[index], index); + return Sum_index_pair{sum_2x2_subclusters[index], + corner{index}}; } else if constexpr (cluster_size_x == 2 && cluster_size_y == 2) { - return std::make_pair(data[0] + data[1] + data[2] + data[3], 0); + return Sum_index_pair{ + data[0] + data[1] + data[2] + data[3], corner{0}}; } else { constexpr size_t cluster_center_index = (ClusterSizeX / 2) + (ClusterSizeY / 2) * ClusterSizeX; @@ -97,7 +100,8 @@ struct Cluster { int index = std::max_element(sum_2x2_subcluster.begin(), sum_2x2_subcluster.end()) - sum_2x2_subcluster.begin(); - return std::make_pair(sum_2x2_subcluster[index], index); + return Sum_index_pair{sum_2x2_subcluster[index], + corner{index}}; } } }; @@ -125,8 +129,8 @@ reduce_to_2x2(const Cluster &c) { (ClusterSizeX / 2) + (ClusterSizeY / 2) * ClusterSizeX; int16_t index_bottom_left_max_2x2_subcluster = - (int(index / (ClusterSizeX - 1))) * ClusterSizeX + - index % (ClusterSizeX - 1); + (int(static_cast(index) / (ClusterSizeX - 1))) * ClusterSizeX + + static_cast(index) % (ClusterSizeX - 1); result.x = c.x + (index_bottom_left_max_2x2_subcluster - cluster_center_index) % @@ -149,22 +153,22 @@ Cluster reduce_to_2x2(const Cluster &c) { auto [s, i] = c.max_sum_2x2(); switch (i) { - case 0: + case corner::cTopLeft: result.x = c.x - 1; result.y = c.y + 1; result.data = {c.data[0], c.data[1], c.data[3], c.data[4]}; break; - case 1: + case corner::cTopRight: result.x = c.x; result.y = c.y + 1; result.data = {c.data[1], c.data[2], c.data[4], c.data[5]}; break; - case 2: + case corner::cBottomLeft: result.x = c.x - 1; result.y = c.y; result.data = {c.data[3], c.data[4], c.data[6], c.data[7]}; break; - case 3: + case corner::cBottomRight: result.x = c.x; result.y = c.y; result.data = {c.data[4], c.data[5], c.data[7], c.data[8]}; diff --git a/include/aare/ClusterCollector.hpp b/include/aare/ClusterCollector.hpp index ae78a8e..a1cbcff 100644 --- a/include/aare/ClusterCollector.hpp +++ b/include/aare/ClusterCollector.hpp @@ -5,6 +5,7 @@ #include "aare/ClusterFinderMT.hpp" #include "aare/ClusterVector.hpp" #include "aare/ProducerConsumerQueue.hpp" +#include "aare/defs.hpp" namespace aare { diff --git a/include/aare/ClusterFile.hpp b/include/aare/ClusterFile.hpp index ccdace3..1295252 100644 --- a/include/aare/ClusterFile.hpp +++ b/include/aare/ClusterFile.hpp @@ -453,8 +453,8 @@ bool ClusterFile::is_selected(ClusterType &cl) { if (m_noise_map) { auto sum_1x1 = cl.data[cluster_center_index]; // central pixel - auto sum_2x2 = cl.max_sum_2x2().first; // highest sum of 2x2 subclusters - auto total_sum = cl.sum(); // sum of all pixels + auto sum_2x2 = cl.max_sum_2x2().sum; // highest sum of 2x2 subclusters + auto total_sum = cl.sum(); // sum of all pixels auto noise = (*m_noise_map)(cl.y, cl.x); // TODO! check if this is correct diff --git a/include/aare/ClusterVector.hpp b/include/aare/ClusterVector.hpp index 00f4d25..9da5392 100644 --- a/include/aare/ClusterVector.hpp +++ b/include/aare/ClusterVector.hpp @@ -86,15 +86,14 @@ class ClusterVector> { /** * @brief Sum the pixels in the 2x2 subcluster with the biggest pixel sum in * each cluster - * @return std::vector vector of sums for each cluster + * @return vector of sums index pairs for each cluster */ - std::vector sum_2x2() { - std::vector sums_2x2(m_data.size()); + std::vector> sum_2x2() { + std::vector> sums_2x2(m_data.size()); - std::transform(m_data.begin(), m_data.end(), sums_2x2.begin(), - [](const ClusterType &cluster) { - return cluster.max_sum_2x2().first; - }); + std::transform( + m_data.begin(), m_data.end(), sums_2x2.begin(), + [](const ClusterType &cluster) { return cluster.max_sum_2x2(); }); return sums_2x2; } diff --git a/include/aare/defs.hpp b/include/aare/defs.hpp index 7ae6269..041dbc9 100644 --- a/include/aare/defs.hpp +++ b/include/aare/defs.hpp @@ -331,6 +331,19 @@ enum DACIndex { SLOW_ADC_TEMP }; +// helper pair class to easily expose in python +template struct Sum_index_pair { + T1 sum; + T2 index; +}; + +enum class corner : int { + cTopLeft = 0, + cTopRight = 1, + cBottomLeft = 2, + cBottomRight = 3 +}; + enum class TimingMode { Auto, Trigger }; enum class FrameDiscardPolicy { NoDiscard, Discard, DiscardPartial }; diff --git a/python/src/bind_Cluster.hpp b/python/src/bind_Cluster.hpp index b6f4272..c672a95 100644 --- a/python/src/bind_Cluster.hpp +++ b/python/src/bind_Cluster.hpp @@ -58,7 +58,17 @@ void define_Cluster(py::module &m, const std::string &typestr) { &Cluster::x) .def_readonly("y", - &Cluster::y); + &Cluster::y) + + .def( + "max_sum_2x2", + [](Cluster &self) { + auto max_sum = self.max_sum_2x2(); + return py::make_tuple(max_sum.sum, + static_cast(max_sum.index)); + }, + 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 + )"); } template (self.sum()); return return_vector(vec); }) - .def("sum_2x2", - [](ClusterVector &self) { - auto *vec = new std::vector(self.sum_2x2()); - return return_vector(vec); - }) + .def( + "sum_2x2", + [](ClusterVector &self) { + auto *vec = new std::vector>( + self.sum_2x2()); + + return return_vector(vec); + }, + 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("item_size", &ClusterVector::item_size) .def_property_readonly("fmt", diff --git a/python/src/module.cpp b/python/src/module.cpp index 5de2fef..5005fc6 100644 --- a/python/src/module.cpp +++ b/python/src/module.cpp @@ -114,4 +114,11 @@ PYBIND11_MODULE(_aare, m) { reduce_to_3x3(m); reduce_to_3x3(m); reduce_to_3x3(m); + + using Sum_index_pair_d = Sum_index_pair; + PYBIND11_NUMPY_DTYPE(Sum_index_pair_d, sum, index); + using Sum_index_pair_f = Sum_index_pair; + PYBIND11_NUMPY_DTYPE(Sum_index_pair_f, sum, index); + using Sum_index_pair_i = Sum_index_pair; + PYBIND11_NUMPY_DTYPE(Sum_index_pair_i, sum, index); } diff --git a/python/tests/test_Cluster.py b/python/tests/test_Cluster.py index 6d2106d..578c97f 100644 --- a/python/tests/test_Cluster.py +++ b/python/tests/test_Cluster.py @@ -86,6 +86,17 @@ def test_calculate_eta(): assert eta2[1,0] == 0.5 assert eta2[1,1] == 0.4 #2/5 + +def test_max_sum(): + """Max 2x2 Sum""" + cluster = _aare.Cluster3x3i(5,5,np.array([1, 1, 1, 2, 3, 1, 2, 2, 1], dtype=np.int32)) + + max_sum = cluster.max_sum_2x2() + + assert max_sum[0] == 9 + assert max_sum[1] == 2 + + def test_cluster_finder(): """Test ClusterFinder""" diff --git a/python/tests/test_ClusterVector.py b/python/tests/test_ClusterVector.py index fa98e3f..5d99778 100644 --- a/python/tests/test_ClusterVector.py +++ b/python/tests/test_ClusterVector.py @@ -32,6 +32,18 @@ def test_push_back_on_cluster_vector(): assert arr[0]['y'] == 22 +def test_max_2x2_sum(): + """max_2x2_sum""" + cv = _aare.ClusterVector_Cluster3x3i() + cv.push_back(_aare.Cluster3x3i(19, 22, np.array([0,1,0,2,3,0,2,1,0], dtype=np.int32))) + cv.push_back(_aare.Cluster3x3i(19, 22, np.ones(9, dtype=np.int32))) + assert cv.size == 2 + max_2x2 = cv.sum_2x2() + assert max_2x2.size == 2 + assert max_2x2[0]["sum"] == 8 + assert max_2x2[0]["index"] == 2 + + def test_make_a_hitmap_from_cluster_vector(): cv = _aare.ClusterVector_Cluster3x3i() diff --git a/src/CalculateEta.test.cpp b/src/CalculateEta.test.cpp index 820ab44..f7cb304 100644 --- a/src/CalculateEta.test.cpp +++ b/src/CalculateEta.test.cpp @@ -21,22 +21,20 @@ using ClusterTypes = auto get_test_parameters() { return GENERATE( std::make_tuple(ClusterTypes{Cluster{0, 0, {1, 2, 3, 1}}}, - Eta2{2. / 3, 3. / 4, - static_cast(corner::cBottomLeft), 7}), + Eta2{2. / 3, 3. / 4, corner::cTopLeft, 7}), std::make_tuple( ClusterTypes{Cluster{0, 0, {1, 2, 3, 4, 5, 6, 1, 2, 7}}}, - Eta2{6. / 11, 2. / 7, static_cast(corner::cTopRight), - 20}), + Eta2{6. / 11, 2. / 7, corner::cBottomRight, 20}), std::make_tuple(ClusterTypes{Cluster{ - 0, 0, {1, 6, 7, 6, 5, 4, 3, 2, 1, 2, 8, 9, 8, - 1, 4, 5, 6, 7, 8, 4, 1, 1, 1, 1, 1}}}, - Eta2{8. / 17, 7. / 15, 9, 30}), + 0, 0, {1, 1, 1, 1, 1, 1, 1, 2, 1, 2, 1, 9, 8, + 1, 4, 1, 6, 7, 8, 1, 1, 1, 1, 1, 1}}}, + Eta2{8. / 17, 7. / 15, corner::cBottomLeft, 30}), std::make_tuple( ClusterTypes{Cluster{0, 0, {1, 4, 7, 2, 5, 6, 4, 3}}}, - Eta2{4. / 10, 4. / 11, 1, 21}), + Eta2{4. / 10, 4. / 11, corner::cTopLeft, 21}), std::make_tuple( ClusterTypes{Cluster{0, 0, {1, 3, 2, 3, 4, 2}}}, - Eta2{3. / 5, 2. / 5, 1, 11})); + Eta2{3. / 5, 2. / 5, corner::cBottomLeft, 11})); } TEST_CASE("compute_largest_2x2_subcluster", "[eta_calculation]") { @@ -91,7 +89,7 @@ TEST_CASE("Calculate eta2 for a 3x3 int32 cluster with the largest 2x2 sum in " // 30, 23, 5 auto eta = calculate_eta2(cl); - CHECK(eta.c == static_cast(corner::cBottomLeft)); + CHECK(eta.c == corner::cBottomLeft); CHECK(eta.x == 50.0 / (20 + 50)); // 4/(3+4) CHECK(eta.y == 50.0 / (23 + 50)); // 4/(1+4) CHECK(eta.sum == 30 + 23 + 20 + 50); @@ -120,7 +118,7 @@ TEST_CASE("Calculate eta2 for a 3x3 int32 cluster with the largest 2x2 sum in " // 8, 12, 5 auto eta = calculate_eta2(cl); - CHECK(eta.c == static_cast(corner::cTopLeft)); + CHECK(eta.c == corner::cTopLeft); CHECK(eta.x == 80. / (77 + 80)); // 4/(3+4) CHECK(eta.y == 91.0 / (91 + 80)); // 7/(7+4) CHECK(eta.sum == 77 + 80 + 82 + 91);