From 47af6092fa1efbd8a7140fb696b58aceb7033426 Mon Sep 17 00:00:00 2001 From: Filip Leonarski Date: Mon, 8 Dec 2025 11:12:59 +0100 Subject: [PATCH] StatusVector: Always use float as underlying storage --- common/CMakeLists.txt | 2 +- common/StatusVector.cpp | 242 +++++++++++++++++++++++++++ common/StatusVector.h | 290 +++------------------------------ receiver/JFJochReceiverPlots.h | 60 +++---- tests/StatusVectorTest.cpp | 26 +-- 5 files changed, 309 insertions(+), 311 deletions(-) create mode 100644 common/StatusVector.cpp diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index 5fd87876..188b0c5a 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -41,7 +41,7 @@ ADD_LIBRARY(JFJochCommon STATIC Definitions.h ThreadSafeFIFO.h DiffractionSpot.cpp DiffractionSpot.h - StatusVector.h + StatusVector.cpp StatusVector.h SpotToSave.h NetworkAddressConvert.h NetworkAddressConvert.cpp DetectorGeometry.h diff --git a/common/StatusVector.cpp b/common/StatusVector.cpp new file mode 100644 index 00000000..84c68f18 --- /dev/null +++ b/common/StatusVector.cpp @@ -0,0 +1,242 @@ +// SPDX-FileCopyrightText: 2024 Filip Leonarski, Paul Scherrer Institute +// SPDX-License-Identifier: GPL-3.0-only + + +#include "StatusVector.h" + +void StatusVector::Clear() { + std::unique_lock ul(m); + content.clear(); + + mean = NAN; + count = 0; + sum = 0; +} + +void StatusVector::AddElement(uint32_t id, std::optional val) { + if (val.has_value()) + AddElement(id, val.value()); +} + +void StatusVector::AddElement(uint32_t id, float val) { + std::unique_lock ul(m); + if (id >= content.size()) { + content.resize(id + 1, NAN); + } + content[id] = val; + + sum += val; + count += 1; + mean = sum / count; +} + +std::optional StatusVector::GetElement(uint32_t id) const { + std::unique_lock ul(m); + if (id < content.size() && std::isfinite(content.at(id))) + return content.at(id); + return {}; +} + +size_t StatusVector::GetImageNumber() const { + return content.size(); +} + +bool StatusVector::empty() const { + return count == 0; +} + +int32_t StatusVector::GetActualBinning(int32_t bin_size) const { + if (content.size() < bin_size) + return 1; + return bin_size; +} + +[[nodiscard]] std::vector StatusVector::ExportArray(float def_value) const { + std::unique_lock ul(m); + std::vector ret(content.size(), def_value); + for (int i = 0; i < content.size(); i++) { + if (std::isfinite(content[i])) + ret[i] = content[i]; + } + return ret; +} + +[[nodiscard]] float StatusVector::Mean() const { + return mean; +} + +MultiLinePlotStruct StatusVector::GetMeanPerBin(int32_t bin_size, float x_start, float x_incr, + const std::optional &fill_value) const { + std::unique_lock ul(m); + + MultiLinePlotStruct ret; + + if (bin_size <= 0) + throw JFJochException(JFJochExceptionCategory::ArrayOutOfBounds, + "Bin number must be greater than zero"); + + if (!content.empty()) { + size_t elems; + + if (content.size() < bin_size) { + // don't bin if less samples than bin size + bin_size = 1; + elems = content.size(); + } else + elems = content.size() / bin_size + ((content.size() % bin_size > 0) ? 1 : 0); + + ret.x.reserve(elems); + ret.y.reserve(elems); + + if (bin_size == 1) { + for (int i = 0; i < content.size(); i++) { + if (std::isfinite(content[i])) { + ret.x.push_back(x_start + x_incr * i); + ret.y.push_back(content[i]); + } else if (fill_value) { + ret.x.push_back(x_start + x_incr * i); + ret.y.push_back(fill_value.value()); + } + } + } else { + for (int bin = 0; bin < elems; bin++) { + double sum_bin = 0; + int64_t count_bin = 0; + + for (int i = bin * bin_size; (i < (bin + 1) * bin_size) && (i < content.size()); i++) { + if (std::isfinite(content[i])) { + sum_bin += 1.0 * content[i]; + count_bin += 1.0; + } + } + + float bin_x = static_cast(bin_size) * (bin + 0.5f); + + if (count_bin > 0) { + ret.x.push_back(x_start + x_incr * bin_x); + ret.y.push_back(static_cast(sum_bin / static_cast(count_bin))); + } else if (fill_value) { + ret.x.push_back(x_start + x_incr * bin_x); + ret.y.push_back(fill_value.value()); + } + } + } + } + return ret; +} + +MultiLinePlotStruct StatusVector::GetMaxPerBin(int32_t bin_size, float x_start, float x_incr, + const std::optional &fill_value) const { + std::unique_lock ul(m); + + MultiLinePlotStruct ret; + + if (bin_size <= 0) + throw JFJochException(JFJochExceptionCategory::ArrayOutOfBounds, + "Bin number must be greater than zero"); + + size_t elems = content.size() / bin_size + ((content.size() % bin_size > 0) ? 1 : 0); + + if (!content.empty()) { + ret.x.reserve(elems); + ret.y.reserve(elems); + + if (bin_size == 1) { + for (int i = 0; i < content.size(); i++) { + if (std::isfinite(content[i])) { + ret.x.push_back(x_start + x_incr * i); + ret.y.push_back(content[i]); + } else if (fill_value) { + ret.x.push_back(x_start + x_incr * i); + ret.y.push_back(fill_value.value()); + } + } + } else { + for (int bin = 0; bin < elems; bin++) { + float max_in_bin = 0; + bool max_bin_set = false; + + for (int i = bin * bin_size; (i < (bin + 1) * bin_size) && (i < content.size()); i++) { + if (std::isfinite(content[i]) && (content[i] > max_in_bin || !max_bin_set)) { + max_bin_set = true; + max_in_bin = content[i]; + } + } + + float bin_x = static_cast(bin_size) * (bin + 0.5f); + + if (max_bin_set) { + ret.x.push_back(x_start + x_incr * bin_x); + ret.y.push_back(max_in_bin); + } else if (fill_value) { + ret.x.push_back(x_start + x_incr * bin_x); + ret.y.push_back(fill_value.value()); + } + } + } + } + return ret; +} + +MultiLinePlot StatusVector::GetMeanPlot(int64_t bin_size, float x_start, float x_incr, + const std::optional &fill_value) const { + MultiLinePlot ret; + ret.AddPlot(GetMeanPerBin(bin_size, x_start, x_incr, fill_value)); + return ret; +} + +MultiLinePlot StatusVector::GetMaxPlot(int64_t bin_size, float x_start, float x_incr, + const std::optional &fill_value) const { + MultiLinePlot ret; + ret.AddPlot(GetMaxPerBin(bin_size, x_start, x_incr, fill_value)); + return ret; +} + +void StatusMultiVector::Clear() { + std::unique_lock ul(m); + status.clear(); +} + +void StatusMultiVector::AddElement(const std::string &s, uint32_t id, float val) { + std::unique_lock ul(m); + if (!status.contains(s)) + status[s] = std::make_unique(); + status[s]->AddElement(id, val); +} + +void StatusMultiVector::AddElement(const std::string &s, uint32_t id, std::optional val) { + // no need to lock, as AddElement(string, u32, T) has lock already + if (val.has_value()) + AddElement(s, id, val.value()); +} + +MultiLinePlotStruct StatusMultiVector::GetMeanPerBin(const std::string &in_key, int64_t bin_size, float x_start, + float x_incr, + const std::optional &fill_value) const { + MultiLinePlotStruct ret{}; + for (const auto &[key, value]: status) { + if (key == in_key) { + ret = value->GetMeanPerBin(bin_size, x_start, x_incr, fill_value); + ret.title = key; + } + } + return ret; +} + +MultiLinePlot StatusMultiVector::GetMeanPlot(int64_t bin_size, float x_start, float x_incr, + const std::optional &fill_value) const { + MultiLinePlot ret; + for (const auto &[key, value]: status) { + auto tmp = value->GetMeanPerBin(bin_size, x_start, x_incr, fill_value); + tmp.title = key; + ret.AddPlot(tmp); + } + return ret; +} + +std::vector StatusMultiVector::ExportArray(const std::string &s, float def_value) const { + auto iter = status.find(s); + if (iter == status.end() || !iter->second) + return {}; + return iter->second->ExportArray(def_value); +} diff --git a/common/StatusVector.h b/common/StatusVector.h index 550c4cae..79fe79da 100644 --- a/common/StatusVector.h +++ b/common/StatusVector.h @@ -14,290 +14,46 @@ #include "JFJochException.h" #include "MultiLinePlot.h" -template class StatusVector { +class StatusVector { mutable std::mutex m; - std::vector content; - std::vector present; + std::vector content; float mean = NAN; size_t count = 0; double sum = 0; public: - void Clear() { - std::unique_lock ul(m); - content.clear(); - present.clear(); - - mean = NAN; - count = 0; - sum = 0; - } - - void AddElement(uint32_t id, std::optional val) { - if (val.has_value()) - AddElement(id, val.value()); - } - - void AddElement(uint32_t id, T val) { - std::unique_lock ul(m); - if (id >= content.size()) { - content.resize(id + 1); - present.resize(id + 1); - } - content[id] = val; - present[id] = 1; - - sum += val; - count += 1; - mean = sum / count; - } - - std::optional GetElement(uint32_t id) const { - std::unique_lock ul(m); - if (id < content.size() && (present.at(id) > 0)) - return content.at(id); - return {}; - } - - void ImportArray(std::vector &in_content, - std::vector &in_present) { - std::unique_lock ul(m); - if (in_content.size() != in_present.size()) - throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, - "Content and presence arrays must be equal in size"); - - content = in_content; - present = in_present; - - sum = 0; - count = 0; - - for (int i = 0; i < content.size(); i++) { - if (present[i]) { - sum += content[i]; - count += 1; - } - } - - if (count > 0) - mean = sum / count; - else - mean = 0; - } - - size_t GetImageNumber() const { - return content.size(); - } - - bool empty() const { - return count == 0; - } - - int32_t GetActualBinning(int32_t bin_size) const { - if (content.size() < bin_size) - return 1; - return bin_size; - } - - - void ImportArray(std::vector &in_content) { - std::vector in_present(in_content.size(), 1); - ImportArray(in_content, in_present); - } - - [[nodiscard]] std::vector ExportArray(T def_value) const { - std::unique_lock ul(m); - std::vector ret(content.size(), def_value); - for (int i = 0; i < content.size(); i++) { - if (present[i]) - ret[i] = content[i]; - } - return ret; - } - - [[nodiscard]] float Mean() const { - return mean; - } - + void Clear(); + void AddElement(uint32_t id, std::optional val); + void AddElement(uint32_t id, float val); + std::optional GetElement(uint32_t id) const; + size_t GetImageNumber() const; + bool empty() const; + int32_t GetActualBinning(int32_t bin_size) const; + [[nodiscard]] std::vector ExportArray(float def_value) const; + [[nodiscard]] float Mean() const; MultiLinePlotStruct GetMeanPerBin(int32_t bin_size, float x_start, float x_incr, - const std::optional &fill_value = {}) const { - std::unique_lock ul(m); - - MultiLinePlotStruct ret; - - if (bin_size <= 0) - throw JFJochException(JFJochExceptionCategory::ArrayOutOfBounds, - "Bin number must be greater than zero"); - - if (!content.empty()) { - size_t elems; - - if (content.size() < bin_size) { - // don't bin if less samples than bin size - bin_size = 1; - elems = content.size(); - } else - elems = content.size() / bin_size + ((content.size() % bin_size > 0) ? 1 : 0); - - ret.x.reserve(elems); - ret.y.reserve(elems); - - if (bin_size == 1) { - for (int i = 0; i < content.size(); i++) { - if (present[i] && std::isfinite(content[i])) { - ret.x.push_back(x_start + x_incr * i); - ret.y.push_back(content[i]); - } else if (fill_value) { - ret.x.push_back(x_start + x_incr * i); - ret.y.push_back(fill_value.value()); - } - } - } else { - for (int bin = 0; bin < elems; bin++) { - double sum_bin = 0; - int64_t count_bin = 0; - - for (int i = bin * bin_size; (i < (bin + 1) * bin_size) && (i < content.size()); i++) { - if (present[i] && std::isfinite(content[i])) { - sum_bin += 1.0 * content[i]; - count_bin += 1.0; - } - } - - float bin_x = static_cast(bin_size) * (bin + 0.5f); - - if (count_bin > 0) { - ret.x.push_back(x_start + x_incr * bin_x); - ret.y.push_back(static_cast(sum_bin / static_cast(count_bin))); - } else if (fill_value) { - ret.x.push_back(x_start + x_incr * bin_x); - ret.y.push_back(fill_value.value()); - } - } - } - } - return ret; - } - + const std::optional &fill_value = {}) const; [[nodiscard]] MultiLinePlotStruct GetMaxPerBin(int32_t bin_size, float x_start, float x_incr, - const std::optional &fill_value = {}) const { - std::unique_lock ul(m); - - MultiLinePlotStruct ret; - - if (bin_size <= 0) - throw JFJochException(JFJochExceptionCategory::ArrayOutOfBounds, - "Bin number must be greater than zero"); - - size_t elems = content.size() / bin_size + ((content.size() % bin_size > 0) ? 1 : 0); - - if (!content.empty()) { - ret.x.reserve(elems); - ret.y.reserve(elems); - - if (bin_size == 1) { - for (int i = 0; i < content.size(); i++) { - if (present[i] && std::isfinite(content[i])) { - ret.x.push_back(x_start + x_incr * i); - ret.y.push_back(content[i]); - } else if (fill_value) { - ret.x.push_back(x_start + x_incr * i); - ret.y.push_back(fill_value.value()); - } - } - } else { - for (int bin = 0; bin < elems; bin++) { - float max_in_bin = 0; - bool max_bin_set = false; - - for (int i = bin * bin_size; (i < (bin + 1) * bin_size) && (i < content.size()); i++) { - if (present[i] && std::isfinite(content[i]) && (content[i] > max_in_bin || !max_bin_set)) { - max_bin_set = true; - max_in_bin = content[i]; - } - } - - float bin_x = static_cast(bin_size) * (bin + 0.5f); - - if (max_bin_set) { - ret.x.push_back(x_start + x_incr * bin_x); - ret.y.push_back(max_in_bin); - } else if (fill_value) { - ret.x.push_back(x_start + x_incr * bin_x); - ret.y.push_back(fill_value.value()); - } - } - } - } - return ret; - } - + const std::optional &fill_value = {}) const; MultiLinePlot GetMeanPlot(int64_t bin_size, float x_start, float x_incr, - const std::optional &fill_value = {}) const { - MultiLinePlot ret; - ret.AddPlot(GetMeanPerBin(bin_size, x_start, x_incr, fill_value)); - return ret; - } - + const std::optional &fill_value = {}) const; MultiLinePlot GetMaxPlot(int64_t bin_size, float x_start, float x_incr, - const std::optional &fill_value = {}) const { - MultiLinePlot ret; - ret.AddPlot(GetMaxPerBin(bin_size, x_start, x_incr, fill_value)); - return ret; - } + const std::optional &fill_value = {}) const; }; -template class StatusMultiVector { +class StatusMultiVector { std::mutex m; - std::map>> status; + std::map> status; public: - void Clear() { - std::unique_lock ul(m); - status.clear(); - } - - void AddElement(const std::string& s, uint32_t id, T val) { - std::unique_lock ul(m); - if (!status.contains(s)) - status[s] = std::make_unique>(); - status[s]->AddElement(id, val); - } - - void AddElement(const std::string& s, uint32_t id, std::optional val) { - // no need to lock, as AddElement(string, u32, T) has lock already - if (val.has_value()) - AddElement(s, id, val.value()); - } + void Clear(); + void AddElement(const std::string& s, uint32_t id, float val); + void AddElement(const std::string& s, uint32_t id, std::optional val); [[nodiscard]] MultiLinePlotStruct GetMeanPerBin(const std::string& in_key, int64_t bin_size, float x_start, float x_incr, - const std::optional &fill_value = {}) const { - MultiLinePlotStruct ret{}; - for (const auto &[key, value]: status) { - if (key == in_key) { - ret = value->GetMeanPerBin(bin_size, x_start, x_incr, fill_value); - ret.title = key; - } - } - return ret; - } - + const std::optional &fill_value = {}) const; [[nodiscard]] MultiLinePlot GetMeanPlot(int64_t bin_size, float x_start, float x_incr, - const std::optional &fill_value = {}) const { - MultiLinePlot ret; - for (const auto &[key, value]: status) { - auto tmp = value->GetMeanPerBin(bin_size, x_start, x_incr, fill_value); - tmp.title = key; - ret.AddPlot(tmp); - } - return ret; - } - - [[nodiscard]] std::vector ExportArray(const std::string& s, T def_value) const { - auto iter = status.find(s); - if (iter == status.end() || !iter->second) - return {}; - return iter->second->ExportArray(def_value); - } + const std::optional &fill_value = {}) const; + [[nodiscard]] std::vector ExportArray(const std::string& s, float def_value) const; }; #endif //JUNGFRAUJOCH_STATUSVECTOR_H diff --git a/receiver/JFJochReceiverPlots.h b/receiver/JFJochReceiverPlots.h index f95ceddc..c79eaa5b 100644 --- a/receiver/JFJochReceiverPlots.h +++ b/receiver/JFJochReceiverPlots.h @@ -23,39 +23,39 @@ class JFJochReceiverPlots { AutoIncrVector xfel_pulse_id; AutoIncrVector xfel_event_code; - StatusVector bkg_estimate; - StatusVector spot_count; - StatusVector spot_count_low_res; - StatusVector spot_count_indexed; - StatusVector spot_count_ice; + StatusVector bkg_estimate; + StatusVector spot_count; + StatusVector spot_count_low_res; + StatusVector spot_count_indexed; + StatusVector spot_count_ice; - StatusVector indexing_solution; - StatusMultiVector indexing_unit_cell_len; - StatusMultiVector indexing_unit_cell_angle; - StatusVector error_pixels; - StatusVector saturated_pixels; - StatusVector strong_pixels; - StatusVector receiver_delay; - StatusVector receiver_free_send_buf; - StatusVector image_collection_efficiency; - StatusVector packets_received; - StatusVector max_value; - StatusVector resolution_estimate; - StatusMultiVector roi_sum; - StatusMultiVector roi_max_count; - StatusMultiVector roi_pixels; - StatusMultiVector roi_x; - StatusMultiVector roi_y; - StatusMultiVector roi_mean; - StatusVector indexing_time; - StatusVector profile_radius; - StatusVector b_factor; - StatusVector pixel_sum; + StatusVector indexing_solution; + StatusMultiVector indexing_unit_cell_len; + StatusMultiVector indexing_unit_cell_angle; + StatusVector error_pixels; + StatusVector saturated_pixels; + StatusVector strong_pixels; + StatusVector receiver_delay; + StatusVector receiver_free_send_buf; + StatusVector image_collection_efficiency; + StatusVector packets_received; + StatusVector max_value; + StatusVector resolution_estimate; + StatusMultiVector roi_sum; + StatusMultiVector roi_max_count; + StatusMultiVector roi_pixels; + StatusMultiVector roi_x; + StatusMultiVector roi_y; + StatusMultiVector roi_mean; + StatusVector indexing_time; + StatusVector profile_radius; + StatusVector b_factor; + StatusVector pixel_sum; - StatusVector beam_center_x; - StatusVector beam_center_y; + StatusVector beam_center_x; + StatusVector beam_center_y; - StatusVector processing_time; + StatusVector processing_time; public: void Setup(const DiffractionExperiment& experiment, const AzimuthalIntegration& mapping); diff --git a/tests/StatusVectorTest.cpp b/tests/StatusVectorTest.cpp index d830868d..f6a17c2c 100644 --- a/tests/StatusVectorTest.cpp +++ b/tests/StatusVectorTest.cpp @@ -5,7 +5,7 @@ #include "../common/StatusVector.h" TEST_CASE("StatusVector_GetMeanPerBin","[StatusVector]") { - StatusVector status_vector; + StatusVector status_vector; status_vector.AddElement(5, 11); status_vector.AddElement(2000, 45); @@ -56,7 +56,7 @@ TEST_CASE("StatusVector_GetMeanPerBin","[StatusVector]") { } TEST_CASE("StatusVector_GetMeanPerBin_Start_Incr","[StatusVector]") { - StatusVector status_vector; + StatusVector status_vector; status_vector.AddElement(5, 11); status_vector.AddElement(2000, 45); @@ -97,7 +97,7 @@ TEST_CASE("StatusVector_GetMeanPerBin_Start_Incr","[StatusVector]") { } TEST_CASE("StatusVector_GetMaxPerBin_Start_Incr","[StatusVector]") { - StatusVector status_vector; + StatusVector status_vector; status_vector.AddElement(5, 11); status_vector.AddElement(2000, 45); @@ -123,7 +123,7 @@ TEST_CASE("StatusVector_GetMaxPerBin_Start_Incr","[StatusVector]") { } TEST_CASE("StatusVector_GetMeanPerBin_Fill","[StatusVector]") { - StatusVector status_vector; + StatusVector status_vector; status_vector.AddElement(5, 11); status_vector.AddElement(2000, 45); @@ -171,7 +171,7 @@ TEST_CASE("StatusVector_GetMeanPerBin_Fill","[StatusVector]") { } TEST_CASE("StatusVector_Mean","[StatusVector]") { - StatusVector status_vector; + StatusVector status_vector; status_vector.AddElement(5, 0.045f); status_vector.AddElement(2000, .010f); @@ -182,7 +182,7 @@ TEST_CASE("StatusVector_Mean","[StatusVector]") { } TEST_CASE("StatusVector_Mean_LargeNum","[StatusVector]") { - StatusVector status_vector; + StatusVector status_vector; for (int i = 0; i < 100000; i++) status_vector.AddElement(i, 0.045f); @@ -190,7 +190,7 @@ TEST_CASE("StatusVector_Mean_LargeNum","[StatusVector]") { } TEST_CASE("StatusVector_GetMaxPerBin","[StatusVector]") { - StatusVector status_vector; + StatusVector status_vector; status_vector.AddElement(5, 11); status_vector.AddElement(2000, 45); @@ -228,7 +228,7 @@ TEST_CASE("StatusVector_GetMaxPerBin","[StatusVector]") { } TEST_CASE("StatusVector_ExportArray","[StatusVector]") { - StatusVector status_vector; + StatusVector status_vector; status_vector.AddElement(5, 11); status_vector.AddElement(7, 45); @@ -246,7 +246,7 @@ TEST_CASE("StatusVector_ExportArray","[StatusVector]") { } TEST_CASE("StatusVector_Plot_OneBin","[StatusVector]") { - StatusVector status_vector; + StatusVector status_vector; status_vector.AddElement(5, 11); status_vector.AddElement(7, 12); @@ -261,7 +261,7 @@ TEST_CASE("StatusVector_Plot_OneBin","[StatusVector]") { } TEST_CASE("StatusVector_Plot_NoBinning","[StatusVector]") { - StatusVector status_vector; + StatusVector status_vector; status_vector.AddElement(5, 11); auto plot_out = status_vector.GetMaxPlot(1, 0.0, 1.0); @@ -273,7 +273,7 @@ TEST_CASE("StatusVector_Plot_NoBinning","[StatusVector]") { } TEST_CASE("StatusMultiVector","[StatusMultiVector]") { - StatusMultiVector status_vector; + StatusMultiVector status_vector; status_vector.AddElement("plot1", 0, 4); status_vector.AddElement("plot1", 1, 3); status_vector.AddElement("plot2", 0, 5); @@ -295,7 +295,7 @@ TEST_CASE("StatusMultiVector","[StatusMultiVector]") { } TEST_CASE("StatusVector_Clear","[StatusVector]") { - StatusVector status_vector; + StatusVector status_vector; status_vector.AddElement(5, 800); status_vector.AddElement(8, 1000); REQUIRE(status_vector.Mean() == 900); @@ -316,7 +316,7 @@ TEST_CASE("StatusVector_Clear","[StatusVector]") { } TEST_CASE("StatusVector_GetElement","[StatusVector]") { - StatusVector status_vector; + StatusVector status_vector; status_vector.AddElement(5, 800); status_vector.AddElement(8, 1000);