This commit is contained in:
froejdh_e 2024-12-11 16:27:36 +01:00
parent 60534add92
commit b3a9e9576b
8 changed files with 301 additions and 183 deletions

View File

@ -344,6 +344,7 @@ if(AARE_TESTS)
${CMAKE_CURRENT_SOURCE_DIR}/src/NDArray.test.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/NDView.test.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/ClusterFinder.test.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/ClusterVector.test.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Pedestal.test.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/NumpyFile.test.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/NumpyHelpers.test.cpp

View File

@ -29,7 +29,6 @@ version = '@PROJECT_VERSION@'
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = ['breathe',
'sphinx_rtd_theme',
'sphinx.ext.autodoc',
'sphinx.ext.napoleon',
]

View File

@ -0,0 +1,6 @@
ClusterVector
=============
.. doxygenclass:: aare::ClusterVector
:members:
:undoc-members:

View File

@ -46,6 +46,7 @@ AARE
Dtype
ClusterFinder
ClusterFile
ClusterVector
Pedestal
RawFile
RawSubFile

View File

@ -23,62 +23,83 @@ enum class eventType {
UNDEFINED_EVENT = -1 /** undefined */
};
template <typename FRAME_TYPE = uint16_t, typename PEDESTAL_TYPE = double, typename CT = int32_t>
template <typename FRAME_TYPE = uint16_t, typename PEDESTAL_TYPE = double,
typename CT = int32_t>
class ClusterFinder {
Shape<2> m_image_size;
const int m_cluster_sizeX;
const int m_cluster_sizeY;
const double m_threshold;
const double m_nSigma;
const double c2;
const double c3;
// const PEDESTAL_TYPE m_threshold;
const PEDESTAL_TYPE m_nSigma;
const PEDESTAL_TYPE c2;
const PEDESTAL_TYPE c3;
Pedestal<PEDESTAL_TYPE> m_pedestal;
ClusterVector<CT> m_clusters;
public:
/**
* @brief Construct a new ClusterFinder object
* @param image_size size of the image
* @param cluster_size size of the cluster (x, y)
* @param nSigma number of sigma above the pedestal to consider a photon
* @param capacity initial capacity of the cluster vector
*
*/
ClusterFinder(Shape<2> image_size, Shape<2> cluster_size,
double nSigma = 5.0, double threshold = 0.0)
PEDESTAL_TYPE nSigma = 5.0, size_t capacity = 1000000)
: m_image_size(image_size), m_cluster_sizeX(cluster_size[0]),
m_cluster_sizeY(cluster_size[1]), m_threshold(threshold),
m_cluster_sizeY(cluster_size[1]),
m_nSigma(nSigma),
c2(sqrt((m_cluster_sizeY + 1) / 2 * (m_cluster_sizeX + 1) / 2)),
c3(sqrt(m_cluster_sizeX * m_cluster_sizeY)),
m_pedestal(image_size[0], image_size[1]),
m_clusters(m_cluster_sizeX, m_cluster_sizeY, 1'000'000) {
// clusters = ClusterVector<CT>(m_cluster_sizeX, m_cluster_sizeY, 2000);
};
m_clusters(m_cluster_sizeX, m_cluster_sizeY, capacity) {};
void push_pedestal_frame(NDView<FRAME_TYPE, 2> frame) {
m_pedestal.push(frame);
}
NDArray<PEDESTAL_TYPE, 2> pedestal() { return m_pedestal.mean(); }
NDArray<PEDESTAL_TYPE, 2> noise() { return m_pedestal.std(); }
ClusterVector<CT> steal_clusters() {
/**
* @brief Move the clusters from the ClusterVector in the ClusterFinder to a
* new ClusterVector and return it.
* @param realloc_same_capacity if true the new ClusterVector will have the
* same capacity as the old one
*
*/
ClusterVector<CT> steal_clusters(bool realloc_same_capacity = false) {
ClusterVector<CT> tmp = std::move(m_clusters);
m_clusters = ClusterVector<CT>(m_cluster_sizeX, m_cluster_sizeY, 2000);
if (realloc_same_capacity)
m_clusters = ClusterVector<CT>(m_cluster_sizeX, m_cluster_sizeY,
tmp.capacity());
else
m_clusters = ClusterVector<CT>(m_cluster_sizeX, m_cluster_sizeY);
return tmp;
}
void
find_clusters(NDView<FRAME_TYPE, 2> frame) {
// // size_t capacity = 2000;
// // ClusterVector<CT> clusters(m_cluster_sizeX, m_cluster_sizeY, capacity);
eventType event_type = eventType::PEDESTAL;
void find_clusters(NDView<FRAME_TYPE, 2> frame) {
// // TODO! deal with even size clusters
// // currently 3,3 -> +/- 1
// // 4,4 -> +/- 2
short dy = m_cluster_sizeY / 2;
short dx = m_cluster_sizeX / 2;
std::vector<CT> cluster_data(m_cluster_sizeX * m_cluster_sizeY);
for (int iy = 0; iy < frame.shape(0); iy++) {
for (int ix = 0; ix < frame.shape(1); ix++) {
PEDESTAL_TYPE max = std::numeric_limits<FRAME_TYPE>::min();
PEDESTAL_TYPE total = 0;
// What can we short circuit here?
PEDESTAL_TYPE rms = m_pedestal.std(iy, ix);
PEDESTAL_TYPE value = (frame(iy, ix) - m_pedestal.mean(iy, ix));
if (value < -m_nSigma * rms)
continue; // NEGATIVE_PEDESTAL go to next pixel
// TODO! No pedestal update???
for (short ir = -dy; ir < dy + 1; ir++) {
for (short ic = -dx; ic < dx + 1; ic++) {
if (ix + ic >= 0 && ix + ic < frame.shape(1) &&
@ -92,159 +113,157 @@ class ClusterFinder {
}
}
}
PEDESTAL_TYPE rms = m_pedestal.std(iy, ix);
PEDESTAL_TYPE value = (frame(iy, ix) - m_pedestal.mean(iy, ix));
if (value < -m_nSigma * rms) {
continue; // NEGATIVE_PEDESTAL go to next pixel
// TODO! No pedestal update???
} else if (max > m_nSigma * rms) {
event_type = eventType::PHOTON;
if ((max > m_nSigma * rms)) {
if (value < max)
continue; // Not max go to the next pixel
// but also no pedestal update
} else if (total > c3 * m_nSigma * rms) {
event_type = eventType::PHOTON;
// pass
} else {
m_pedestal.push(iy, ix, frame(iy, ix));
continue; // It was a pedestal value nothing to store
}
// Store cluster
if (event_type == eventType::PHOTON && value >= max) {
event_type = eventType::PHOTON_MAX;
if (value == max) {
// Zero out the cluster data
std::fill(cluster_data.begin(), cluster_data.end(), 0);
short i = 0;
std::vector<CT> cluster_data(m_cluster_sizeX *
m_cluster_sizeY);
for (short ir = -dy; ir < dy + 1; ir++) {
for (short ic = -dx; ic < dx + 1; ic++) {
// Fill the cluster data since we have a photon to store
// It's worth redoing the look since most of the time we
// don't have a photon
int i = 0;
for (int ir = -dy; ir < dy + 1; ir++) {
for (int ic = -dx; ic < dx + 1; ic++) {
if (ix + ic >= 0 && ix + ic < frame.shape(1) &&
iy + ir >= 0 && iy + ir < frame.shape(0)) {
CT tmp =
static_cast<CT>(
frame(iy + ir, ix + ic)) -
static_cast<CT>(frame(iy + ir, ix + ic)) -
m_pedestal.mean(iy + ir, ix + ic);
cluster_data[i] = tmp; //Watch for out of bounds access
cluster_data[i] =
tmp; // Watch for out of bounds access
i++;
}
}
}
// Add the cluster to the output ClusterVector
m_clusters.push_back(
ix, iy,
reinterpret_cast<std::byte *>(cluster_data.data()));
}
}
}
// return clusters;
}
// template <typename FRAME_TYPE, typename PEDESTAL_TYPE>
std::vector<DynamicCluster>
find_clusters_with_threshold(NDView<FRAME_TYPE, 2> frame,
Pedestal<PEDESTAL_TYPE> &pedestal) {
assert(m_threshold > 0);
std::vector<DynamicCluster> clusters;
std::vector<std::vector<eventType>> eventMask;
for (int i = 0; i < frame.shape(0); i++) {
eventMask.push_back(std::vector<eventType>(frame.shape(1)));
}
double tthr, tthr1, tthr2;
// // template <typename FRAME_TYPE, typename PEDESTAL_TYPE>
// std::vector<DynamicCluster>
// find_clusters_with_threshold(NDView<FRAME_TYPE, 2> frame,
// Pedestal<PEDESTAL_TYPE> &pedestal) {
// assert(m_threshold > 0);
// std::vector<DynamicCluster> clusters;
// std::vector<std::vector<eventType>> eventMask;
// for (int i = 0; i < frame.shape(0); i++) {
// eventMask.push_back(std::vector<eventType>(frame.shape(1)));
// }
// double tthr, tthr1, tthr2;
NDArray<FRAME_TYPE, 2> rest({frame.shape(0), frame.shape(1)});
NDArray<int, 2> nph({frame.shape(0), frame.shape(1)});
// convert to n photons
// nph = (frame-pedestal.mean()+0.5*m_threshold)/m_threshold; // can be
// optimized with expression templates?
for (int iy = 0; iy < frame.shape(0); iy++) {
for (int ix = 0; ix < frame.shape(1); ix++) {
auto val = frame(iy, ix) - pedestal.mean(iy, ix);
nph(iy, ix) = (val + 0.5 * m_threshold) / m_threshold;
nph(iy, ix) = nph(iy, ix) < 0 ? 0 : nph(iy, ix);
rest(iy, ix) = val - nph(iy, ix) * m_threshold;
}
}
// iterate over frame pixels
for (int iy = 0; iy < frame.shape(0); iy++) {
for (int ix = 0; ix < frame.shape(1); ix++) {
eventMask[iy][ix] = eventType::PEDESTAL;
// initialize max and total
FRAME_TYPE max = std::numeric_limits<FRAME_TYPE>::min();
long double total = 0;
if (rest(iy, ix) <= 0.25 * m_threshold) {
pedestal.push(iy, ix, frame(iy, ix));
continue;
}
eventMask[iy][ix] = eventType::NEIGHBOUR;
// iterate over cluster pixels around the current pixel (ix,iy)
for (short ir = -(m_cluster_sizeY / 2);
ir < (m_cluster_sizeY / 2) + 1; ir++) {
for (short ic = -(m_cluster_sizeX / 2);
ic < (m_cluster_sizeX / 2) + 1; ic++) {
if (ix + ic >= 0 && ix + ic < frame.shape(1) &&
iy + ir >= 0 && iy + ir < frame.shape(0)) {
auto val = frame(iy + ir, ix + ic) -
pedestal.mean(iy + ir, ix + ic);
total += val;
if (val > max) {
max = val;
}
}
}
}
// NDArray<FRAME_TYPE, 2> rest({frame.shape(0), frame.shape(1)});
// NDArray<int, 2> nph({frame.shape(0), frame.shape(1)});
// // convert to n photons
// // nph = (frame-pedestal.mean()+0.5*m_threshold)/m_threshold; // can
// be
// // optimized with expression templates?
// for (int iy = 0; iy < frame.shape(0); iy++) {
// for (int ix = 0; ix < frame.shape(1); ix++) {
// auto val = frame(iy, ix) - pedestal.mean(iy, ix);
// nph(iy, ix) = (val + 0.5 * m_threshold) / m_threshold;
// nph(iy, ix) = nph(iy, ix) < 0 ? 0 : nph(iy, ix);
// rest(iy, ix) = val - nph(iy, ix) * m_threshold;
// }
// }
// // iterate over frame pixels
// for (int iy = 0; iy < frame.shape(0); iy++) {
// for (int ix = 0; ix < frame.shape(1); ix++) {
// eventMask[iy][ix] = eventType::PEDESTAL;
// // initialize max and total
// FRAME_TYPE max = std::numeric_limits<FRAME_TYPE>::min();
// long double total = 0;
// if (rest(iy, ix) <= 0.25 * m_threshold) {
// pedestal.push(iy, ix, frame(iy, ix));
// continue;
// }
// eventMask[iy][ix] = eventType::NEIGHBOUR;
// // iterate over cluster pixels around the current pixel
// (ix,iy) for (short ir = -(m_cluster_sizeY / 2);
// ir < (m_cluster_sizeY / 2) + 1; ir++) {
// for (short ic = -(m_cluster_sizeX / 2);
// ic < (m_cluster_sizeX / 2) + 1; ic++) {
// if (ix + ic >= 0 && ix + ic < frame.shape(1) &&
// iy + ir >= 0 && iy + ir < frame.shape(0)) {
// auto val = frame(iy + ir, ix + ic) -
// pedestal.mean(iy + ir, ix + ic);
// total += val;
// if (val > max) {
// max = val;
// }
// }
// }
// }
auto rms = pedestal.std(iy, ix);
if (m_nSigma == 0) {
tthr = m_threshold;
tthr1 = m_threshold;
tthr2 = m_threshold;
} else {
tthr = m_nSigma * rms;
tthr1 = m_nSigma * rms * c3;
tthr2 = m_nSigma * rms * c2;
// auto rms = pedestal.std(iy, ix);
// if (m_nSigma == 0) {
// tthr = m_threshold;
// tthr1 = m_threshold;
// tthr2 = m_threshold;
// } else {
// tthr = m_nSigma * rms;
// tthr1 = m_nSigma * rms * c3;
// tthr2 = m_nSigma * rms * c2;
if (m_threshold > 2 * tthr)
tthr = m_threshold - tthr;
if (m_threshold > 2 * tthr1)
tthr1 = tthr - tthr1;
if (m_threshold > 2 * tthr2)
tthr2 = tthr - tthr2;
}
if (total > tthr1 || max > tthr) {
eventMask[iy][ix] = eventType::PHOTON;
nph(iy, ix) += 1;
rest(iy, ix) -= m_threshold;
} else {
pedestal.push(iy, ix, frame(iy, ix));
continue;
}
if (eventMask[iy][ix] == eventType::PHOTON &&
frame(iy, ix) - pedestal.mean(iy, ix) >= max) {
eventMask[iy][ix] = eventType::PHOTON_MAX;
DynamicCluster cluster(m_cluster_sizeX, m_cluster_sizeY,
Dtype(typeid(FRAME_TYPE)));
cluster.x = ix;
cluster.y = iy;
short i = 0;
for (short ir = -(m_cluster_sizeY / 2);
ir < (m_cluster_sizeY / 2) + 1; ir++) {
for (short ic = -(m_cluster_sizeX / 2);
ic < (m_cluster_sizeX / 2) + 1; ic++) {
if (ix + ic >= 0 && ix + ic < frame.shape(1) &&
iy + ir >= 0 && iy + ir < frame.shape(0)) {
auto tmp = frame(iy + ir, ix + ic) -
pedestal.mean(iy + ir, ix + ic);
cluster.set<FRAME_TYPE>(i, tmp);
i++;
}
}
}
clusters.push_back(cluster);
}
}
}
return clusters;
}
// if (m_threshold > 2 * tthr)
// tthr = m_threshold - tthr;
// if (m_threshold > 2 * tthr1)
// tthr1 = tthr - tthr1;
// if (m_threshold > 2 * tthr2)
// tthr2 = tthr - tthr2;
// }
// if (total > tthr1 || max > tthr) {
// eventMask[iy][ix] = eventType::PHOTON;
// nph(iy, ix) += 1;
// rest(iy, ix) -= m_threshold;
// } else {
// pedestal.push(iy, ix, frame(iy, ix));
// continue;
// }
// if (eventMask[iy][ix] == eventType::PHOTON &&
// frame(iy, ix) - pedestal.mean(iy, ix) >= max) {
// eventMask[iy][ix] = eventType::PHOTON_MAX;
// DynamicCluster cluster(m_cluster_sizeX, m_cluster_sizeY,
// Dtype(typeid(FRAME_TYPE)));
// cluster.x = ix;
// cluster.y = iy;
// short i = 0;
// for (short ir = -(m_cluster_sizeY / 2);
// ir < (m_cluster_sizeY / 2) + 1; ir++) {
// for (short ic = -(m_cluster_sizeX / 2);
// ic < (m_cluster_sizeX / 2) + 1; ic++) {
// if (ix + ic >= 0 && ix + ic < frame.shape(1) &&
// iy + ir >= 0 && iy + ir < frame.shape(0)) {
// auto tmp = frame(iy + ir, ix + ic) -
// pedestal.mean(iy + ir, ix + ic);
// cluster.set<FRAME_TYPE>(i, tmp);
// i++;
// }
// }
// }
// clusters.push_back(cluster);
// }
// }
// }
// return clusters;
// }
};
} // namespace aare

View File

@ -2,9 +2,18 @@
#include <cstddef>
#include <cstdint>
#include <numeric>
#include <vector>
#include <fmt/core.h>
namespace aare {
/**
* @brief ClusterVector is a container for clusters of various sizes. It uses a
* contiguous memory buffer to store the clusters.
* @note push_back can invalidate pointers to elements in the container
* @tparam T data type of the pixels in the cluster
*/
template <typename T> class ClusterVector {
using value_type = T;
using coord_t = int16_t;
@ -24,6 +33,12 @@ template <typename T> class ClusterVector {
constexpr static char m_fmt_base[] = "=h:x:\nh:y:\n({},{}){}:data:" ;
public:
/**
* @brief Construct a new ClusterVector object
* @param cluster_size_x size of the cluster in x direction
* @param cluster_size_y size of the cluster in y direction
* @param capacity initial capacity of the buffer in number of clusters
*/
ClusterVector(coord_t cluster_size_x, coord_t cluster_size_y,
size_t capacity = 1024)
: m_cluster_size_x(cluster_size_x), m_cluster_size_y(cluster_size_y),
@ -31,8 +46,6 @@ template <typename T> class ClusterVector {
allocate_buffer(capacity);
}
~ClusterVector() {
fmt::print("~ClusterVector - size: {}, capacity: {}\n", m_size,
m_capacity);
delete[] m_data;
}
@ -63,13 +76,24 @@ template <typename T> class ClusterVector {
return *this;
}
/**
* @brief Reserve space for at least capacity clusters
* @param capacity number of clusters to reserve space for
* @note If capacity is less than the current capacity, the function does nothing.
*/
void reserve(size_t capacity) {
if (capacity > m_capacity) {
allocate_buffer(capacity);
}
}
// data better hold data of the right size!
/**
* @brief Add a cluster to the vector
* @param x x-coordinate of the cluster
* @param y y-coordinate of the cluster
* @param data pointer to the data of the cluster
* @warning The data pointer must point to a buffer of size cluster_size_x * cluster_size_y * sizeof(T)
*/
void push_back(coord_t x, coord_t y, const std::byte *data) {
if (m_size == m_capacity) {
allocate_buffer(m_capacity * 2);
@ -85,6 +109,10 @@ template <typename T> class ClusterVector {
m_size++;
}
/**
* @brief Sum the pixels in each cluster
* @return std::vector<T> vector of sums for each cluster
*/
std::vector<T> sum() {
std::vector<T> sums(m_size);
const size_t stride = element_offset();
@ -101,12 +129,23 @@ template <typename T> class ClusterVector {
}
size_t size() const { return m_size; }
size_t capacity() const { return m_capacity; }
/**
* @brief Return the offset in bytes for a single cluster
*/
size_t element_offset() const {
return sizeof(m_cluster_size_x) + sizeof(m_cluster_size_y) +
m_cluster_size_x * m_cluster_size_y * sizeof(T);
}
/**
* @brief Return the offset in bytes for the i-th cluster
*/
size_t element_offset(size_t i) const { return element_offset() * i; }
/**
* @brief Return a pointer to the i-th cluster
*/
std::byte *element_ptr(size_t i) { return m_data + element_offset(i); }
int16_t cluster_size_x() const { return m_cluster_size_x; }
@ -123,13 +162,12 @@ template <typename T> class ClusterVector {
private:
void allocate_buffer(size_t new_capacity) {
size_t num_bytes = element_offset() * new_capacity;
fmt::print(
"ClusterVector allocating {} elements for a total of {} bytes\n",
new_capacity, num_bytes);
std::byte *new_data = new std::byte[num_bytes]{};
std::copy(m_data, m_data + element_offset() * m_size, new_data);
delete[] m_data;
m_data = new_data;
m_capacity = new_capacity;
}
};
};
} // namespace aare

View File

@ -10,7 +10,7 @@
#include <pybind11/stl.h>
namespace py = pybind11;
using pd_type = double;
using pd_type = float;
template <typename T>
void define_cluster_vector(py::module &m, const std::string &typestr) {
@ -46,7 +46,9 @@ void define_cluster_vector(py::module &m, const std::string &typestr) {
void define_cluster_finder_bindings(py::module &m) {
py::class_<ClusterFinder<uint16_t, pd_type>>(m, "ClusterFinder")
.def(py::init<Shape<2>, Shape<2>>())
.def(py::init<Shape<2>, Shape<2>, pd_type, size_t>(), py::arg("image_size"),
py::arg("cluster_size"), py::arg("n_sigma") = 5.0,
py::arg("capacity") = 1'000'000)
.def("push_pedestal_frame",
[](ClusterFinder<uint16_t, pd_type> &self,
py::array_t<uint16_t> frame) {
@ -66,10 +68,10 @@ void define_cluster_finder_bindings(py::module &m) {
return return_image_data(arr);
})
.def("steal_clusters",
[](ClusterFinder<uint16_t, pd_type> &self) {
auto v = new ClusterVector<int>(self.steal_clusters());
[](ClusterFinder<uint16_t, pd_type> &self, bool realloc_same_capacity) {
auto v = new ClusterVector<int>(self.steal_clusters(realloc_same_capacity));
return v;
})
}, py::arg("realloc_same_capacity") = false)
.def("find_clusters",
[](ClusterFinder<uint16_t, pd_type> &self,
py::array_t<uint16_t> frame) {
@ -78,36 +80,11 @@ void define_cluster_finder_bindings(py::module &m) {
return;
});
m.def("hello", []() {
fmt::print("Hello from C++\n");
auto v = new ClusterVector<int>(3, 3);
int data[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
v->push_back(5, 30, reinterpret_cast<std::byte *>(data));
v->push_back(5, 55, reinterpret_cast<std::byte *>(data));
v->push_back(5, 20, reinterpret_cast<std::byte *>(data));
v->push_back(5, 30, reinterpret_cast<std::byte *>(data));
return v;
});
define_cluster_vector<int>(m, "i");
define_cluster_vector<double>(m, "d");
define_cluster_vector<float>(m, "f");
// py::class_<ClusterVector<int>>(m, "ClusterVector", py::buffer_protocol())
// .def(py::init<int, int>())
// .def("size", &ClusterVector<int>::size)
// .def("element_offset",
// py::overload_cast<>(&ClusterVector<int>::element_offset, py::const_))
// .def_buffer([](ClusterVector<int> &v) -> py::buffer_info {
// return py::buffer_info(
// v.data(), /* Pointer to buffer */
// v.element_offset(), /* Size of one scalar */
// fmt::format("h:x:\nh:y:\n({},{})i:data:", v.cluster_size_x(),
// v.cluster_size_y()), /* Format descriptor */ 1, /* Number of
// dimensions */ {v.size()}, /* Buffer dimensions */
// {v.element_offset()} /* Strides (in bytes) for each index */
// );
// });
py::class_<DynamicCluster>(m, "DynamicCluster", py::buffer_protocol())
.def(py::init<int, int, Dtype>())

View File

@ -0,0 +1,77 @@
#include <cstdint>
#include "aare/ClusterVector.hpp"
// #include <catch2/matchers/catch_matchers_floating_point.hpp>
#include <catch2/catch_test_macros.hpp>
using aare::ClusterVector;
TEST_CASE("ClusterVector 2x2 int32_t capacity 4, push back then read") {
struct Cluster_i2x2 {
int16_t x;
int16_t y;
int32_t data[4];
};
ClusterVector<int32_t> cv(2, 2, 4);
REQUIRE(cv.capacity() == 4);
REQUIRE(cv.size() == 0);
REQUIRE(cv.cluster_size_x() == 2);
REQUIRE(cv.cluster_size_y() == 2);
// int16_t, int16_t, 2x2 int32_t = 20 bytes
REQUIRE(cv.element_offset() == 20);
//Create a cluster and push back into the vector
Cluster_i2x2 c1 = {1, 2, {3, 4, 5, 6}};
cv.push_back(c1.x, c1.y, reinterpret_cast<std::byte*>(&c1.data[0]));
REQUIRE(cv.size() == 1);
REQUIRE(cv.capacity() == 4);
//Read the cluster back out using copy. TODO! Can we improve the API?
Cluster_i2x2 c2;
std::byte *ptr = cv.element_ptr(0);
std::copy(ptr, ptr + cv.element_offset(), reinterpret_cast<std::byte*>(&c2));
//Check that the data is the same
REQUIRE(c1.x == c2.x);
REQUIRE(c1.y == c2.y);
for(size_t i = 0; i < 4; i++) {
REQUIRE(c1.data[i] == c2.data[i]);
}
}
TEST_CASE("Summing 3x1 clusters of int64"){
struct Cluster_l3x1{
int16_t x;
int16_t y;
int64_t data[3];
};
ClusterVector<int64_t> cv(3, 1, 2);
REQUIRE(cv.capacity() == 2);
REQUIRE(cv.size() == 0);
REQUIRE(cv.cluster_size_x() == 3);
REQUIRE(cv.cluster_size_y() == 1);
//Create a cluster and push back into the vector
Cluster_l3x1 c1 = {1, 2, {3, 4, 5}};
cv.push_back(c1.x, c1.y, reinterpret_cast<std::byte*>(&c1.data[0]));
REQUIRE(cv.capacity() == 2);
REQUIRE(cv.size() == 1);
Cluster_l3x1 c2 = {6, 7, {8, 9, 10}};
cv.push_back(c2.x, c2.y, reinterpret_cast<std::byte*>(&c2.data[0]));
REQUIRE(cv.capacity() == 2);
REQUIRE(cv.size() == 2);
Cluster_l3x1 c3 = {11, 12, {13, 14, 15}};
cv.push_back(c3.x, c3.y, reinterpret_cast<std::byte*>(&c3.data[0]));
REQUIRE(cv.capacity() == 4);
REQUIRE(cv.size() == 3);
auto sums = cv.sum();
REQUIRE(sums.size() == 3);
REQUIRE(sums[0] == 12);
REQUIRE(sums[1] == 27);
REQUIRE(sums[2] == 42);
}