From c74ce5d8e315dcde7776f0f79e2e5af378e9f25a Mon Sep 17 00:00:00 2001 From: Chen Zhang Date: Fri, 16 Aug 2024 14:31:10 -0400 Subject: [PATCH 01/38] add example config --- sophiread/SophireadCLI/src/user_config.cpp | 2 ++ sophiread/resources/config/example_config.json | 15 +++++++++++++++ 2 files changed, 17 insertions(+) create mode 100644 sophiread/resources/config/example_config.json diff --git a/sophiread/SophireadCLI/src/user_config.cpp b/sophiread/SophireadCLI/src/user_config.cpp index 3a5c3f0..75f344b 100644 --- a/sophiread/SophireadCLI/src/user_config.cpp +++ b/sophiread/SophireadCLI/src/user_config.cpp @@ -20,6 +20,8 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ +#pragma once + #include "user_config.h" #include diff --git a/sophiread/resources/config/example_config.json b/sophiread/resources/config/example_config.json new file mode 100644 index 0000000..e4be83c --- /dev/null +++ b/sophiread/resources/config/example_config.json @@ -0,0 +1,15 @@ +{ + "abs": { + "radius": 5.0, + "min_cluster_size": 1, + "spider_time_range": 75 + }, + "tof_imaging": { + "bin_edges": [0, 1000, 2000, 3000, 16700], + "uniform_bins": { + "start": 0, + "end": 16700, + "num_bins": 1500 + } + } +} \ No newline at end of file From 057362382cb5a0cc701160b73874f677ddb71385 Mon Sep 17 00:00:00 2001 From: Chen Zhang Date: Fri, 16 Aug 2024 15:05:16 -0400 Subject: [PATCH 02/38] add new dep for json handling --- sophiread/environment_linux.yml | 1 + sophiread/environment_mac.yml | 1 + 2 files changed, 2 insertions(+) diff --git a/sophiread/environment_linux.yml b/sophiread/environment_linux.yml index 01570bf..8cb9f08 100644 --- a/sophiread/environment_linux.yml +++ b/sophiread/environment_linux.yml @@ -14,6 +14,7 @@ dependencies: - cereal - tbb-devel - spdlog + - nlohmann_json # Not Windows, OpenGL implementation: diff --git a/sophiread/environment_mac.yml b/sophiread/environment_mac.yml index a0e71ae..c5a6bb9 100644 --- a/sophiread/environment_mac.yml +++ b/sophiread/environment_mac.yml @@ -13,6 +13,7 @@ dependencies: - eigen - tbb-devel - spdlog + - nlohmann_json # Not Windows, OpenGL implementation: # - mesalib>=18.0.0 From 79a9fc912406833568776b3813338e4fc5fa1c2e Mon Sep 17 00:00:00 2001 From: Chen Zhang Date: Fri, 16 Aug 2024 16:36:04 -0400 Subject: [PATCH 03/38] add json config parser --- .../SophireadCLI/include/json_config_parser.h | 42 ++++++++++ .../SophireadCLI/src/json_config_parser.cpp | 76 +++++++++++++++++++ sophiread/SophireadCLI/src/user_config.cpp | 2 - .../tests/test_json_config_parser.cpp | 66 ++++++++++++++++ 4 files changed, 184 insertions(+), 2 deletions(-) create mode 100644 sophiread/SophireadCLI/include/json_config_parser.h create mode 100644 sophiread/SophireadCLI/src/json_config_parser.cpp create mode 100644 sophiread/SophireadCLI/tests/test_json_config_parser.cpp diff --git a/sophiread/SophireadCLI/include/json_config_parser.h b/sophiread/SophireadCLI/include/json_config_parser.h new file mode 100644 index 0000000..11a9704 --- /dev/null +++ b/sophiread/SophireadCLI/include/json_config_parser.h @@ -0,0 +1,42 @@ +/** + * @file json_config_parser.h + * @author Chen Zhang (zhangc@orn.gov) + * @brief Class to store user-defined configuration (JSON) for clustering algorithms. + * @version 0.1 + * @date 2024-08-16 + * + * @copyright Copyright (c) 2024 + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once + +#include +#include +#include + +class JSONConfigParser { +public: + static JSONConfigParser fromFile(const std::string& filepath); + + double getABSRadius() const; + unsigned long int getABSMinClusterSize() const; + unsigned long int getABSSpiderTimeRange() const; + std::vector getTOFBinEdges() const; + + std::string toString() const; + +private: + JSONConfigParser(const nlohmann::json& config); + nlohmann::json m_config; +}; \ No newline at end of file diff --git a/sophiread/SophireadCLI/src/json_config_parser.cpp b/sophiread/SophireadCLI/src/json_config_parser.cpp new file mode 100644 index 0000000..fc91cc3 --- /dev/null +++ b/sophiread/SophireadCLI/src/json_config_parser.cpp @@ -0,0 +1,76 @@ +/** + * @file json_config_parser.cpp + * @author Chen Zhang (zhangc@orn.gov) + * @brief Class to store user-defined configuration (JSON) for clustering algorithms. + * @version 0.1 + * @date 2024-08-16 + * + * @copyright Copyright (c) 2024 + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include "json_config_parser.h" +#include +#include + +JSONConfigParser JSONConfigParser::fromFile(const std::string& filepath) { + std::ifstream file(filepath); + if (!file.is_open()) { + throw std::runtime_error("Failed to open configuration file: " + filepath); + } + + nlohmann::json config; + try { + file >> config; + } catch (const nlohmann::json::exception& e) { + throw std::runtime_error("Error parsing JSON file: " + std::string(e.what())); + } + + return JSONConfigParser(config); +} + +JSONConfigParser::JSONConfigParser(const nlohmann::json& config) : m_config(config) {} + +double JSONConfigParser::getABSRadius() const { + return m_config["abs"]["radius"].get(); +} + +unsigned long int JSONConfigParser::getABSMinClusterSize() const { + return m_config["abs"]["min_cluster_size"].get(); +} + +unsigned long int JSONConfigParser::getABSSpiderTimeRange() const { + return m_config["abs"]["spider_time_range"].get(); +} + +std::vector JSONConfigParser::getTOFBinEdges() const { + const auto& tof = m_config["tof_imaging"]; + if (tof.contains("bin_edges")) { + return tof["bin_edges"].get>(); + } else if (tof.contains("uniform_bins")) { + const auto& uniform = tof["uniform_bins"]; + double start = uniform["start"].get(); + double end = uniform["end"].get(); + int num_bins = uniform["num_bins"].get(); + std::vector edges(num_bins + 1); + for (int i = 0; i <= num_bins; ++i) { + edges[i] = start + (end - start) * i / num_bins; + } + return edges; + } + throw std::runtime_error("TOF bin edges not specified in configuration"); +} + +std::string JSONConfigParser::toString() const { + return m_config.dump(2); +} \ No newline at end of file diff --git a/sophiread/SophireadCLI/src/user_config.cpp b/sophiread/SophireadCLI/src/user_config.cpp index 75f344b..3a5c3f0 100644 --- a/sophiread/SophireadCLI/src/user_config.cpp +++ b/sophiread/SophireadCLI/src/user_config.cpp @@ -20,8 +20,6 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -#pragma once - #include "user_config.h" #include diff --git a/sophiread/SophireadCLI/tests/test_json_config_parser.cpp b/sophiread/SophireadCLI/tests/test_json_config_parser.cpp new file mode 100644 index 0000000..4e14ef9 --- /dev/null +++ b/sophiread/SophireadCLI/tests/test_json_config_parser.cpp @@ -0,0 +1,66 @@ +/** + * @file test_json_config_parser.cpp + * @author Chen Zhang (zhangc@orn.gov) + * @brief Unit tests for JSONConfigParser class. + * @date 2024-08-16 + * + * @copyright Copyright (c) 2024 + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include "json_config_parser.h" +#include +#include + +class JSONConfigParserTest : public ::testing::Test { +protected: + void SetUp() override { + std::ofstream config_file("test_config.json"); + config_file << R"({ + "abs": { + "radius": 5.0, + "min_cluster_size": 1, + "spider_time_range": 75 + }, + "tof_imaging": { + "uniform_bins": { + "start": 0, + "end": 16700, + "num_bins": 1500 + } + } + })"; + config_file.close(); + } + + void TearDown() override { + std::remove("test_config.json"); + } +}; + +TEST_F(JSONConfigParserTest, ParsesConfigCorrectly) { + auto config = JSONConfigParser::fromFile("test_config.json"); + + EXPECT_DOUBLE_EQ(config.getABSRadius(), 5.0); + EXPECT_EQ(config.getABSMinClusterSize(), 1); + EXPECT_EQ(config.getABSSpiderTimeRange(), 75); + + auto bin_edges = config.getTOFBinEdges(); + EXPECT_EQ(bin_edges.size(), 1501); + EXPECT_DOUBLE_EQ(bin_edges.front(), 0); + EXPECT_DOUBLE_EQ(bin_edges.back(), 16700); +} + +TEST_F(JSONConfigParserTest, ThrowsOnMissingFile) { + EXPECT_THROW(JSONConfigParser::fromFile("non_existent.json"), std::runtime_error); +} From 308c651714bc35a31a4ba842c85eab25a0aba853 Mon Sep 17 00:00:00 2001 From: Chen Zhang Date: Fri, 16 Aug 2024 16:36:18 -0400 Subject: [PATCH 04/38] refactor CMake and include new targets --- sophiread/CMakeLists.txt | 60 +++++-- sophiread/FastSophiread/CMakeLists.txt | 227 +++++++------------------ sophiread/SophireadCLI/CMakeLists.txt | 38 +++-- 3 files changed, 133 insertions(+), 192 deletions(-) diff --git a/sophiread/CMakeLists.txt b/sophiread/CMakeLists.txt index dc258fe..3bf2a93 100644 --- a/sophiread/CMakeLists.txt +++ b/sophiread/CMakeLists.txt @@ -1,12 +1,22 @@ +# Sophiread CMakeLists.txt cmake_minimum_required(VERSION 3.20) execute_process(COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/scripts/version.sh -s print WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} - OUTPUT_VARIABLE SOPHIREAD_VERSION) + OUTPUT_VARIABLE SOPHIREAD_VERSION + RESULT_VARIABLE VERSION_RESULT + OUTPUT_STRIP_TRAILING_WHITESPACE +) +if(NOT VERSION_RESULT EQUAL 0) + message(FATAL_ERROR "Failed to determine version") +endif() project("Sophiread" VERSION ${SOPHIREAD_VERSION}) -# set(CMAKE_BUILD_TYPE DEBUG) +if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE Release) +endif() + set(CMAKE_CXX_STANDARD 20) # set(CMAKE_AUTOMOC ON) # for meta object compiler, needed for Qt5 @@ -15,29 +25,36 @@ find_package(Eigen3 REQUIRED) find_package(spdlog REQUIRED) find_package(HDF5 REQUIRED COMPONENTS CXX) find_package(GTest REQUIRED) +find_package(nlohmann_json 3.2.0 REQUIRED) +find_package(TBB REQUIRED) # find_package(Qt5 COMPONENTS Widgets REQUIRED) -include_directories(${HDF5_INCLUDE_DIRS}) - -link_directories( - $ENV{CONDA_PREFIX}/lib -) - # Testing setup enable_testing() include(GoogleTest) -file(COPY ${CMAKE_SOURCE_DIR}/resources/data DESTINATION ${CMAKE_BINARY_DIR}) -# Add compiler flags -add_compile_options( - -O3 - -std=c++20 - -march=native - -ffast-math - -pthread - -Wall - -Wextra +# Copy resources +add_custom_command( + OUTPUT ${CMAKE_BINARY_DIR}/data + COMMAND ${CMAKE_COMMAND} -E copy_directory + ${CMAKE_SOURCE_DIR}/resources/data + ${CMAKE_BINARY_DIR}/data + DEPENDS ${CMAKE_SOURCE_DIR}/resources/data ) +add_custom_target(copy_resources ALL DEPENDS ${CMAKE_BINARY_DIR}/data) + +# Add compiler flags +if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") + add_compile_options( + -O3 + -std=c++20 + -march=native + -ffast-math + -pthread + -Wall + -Wextra + ) +endif() # Add Sophiread library # NOTE: this will take care of the testing as well @@ -65,6 +82,13 @@ if(DOXYGEN_FOUND) COMMENT "Generating API documentation with Doxygen" VERBATIM ) + if(APPLE) + set(OPEN_COMMAND open) + elseif(UNIX) + set(OPEN_COMMAND xdg-open) + elseif(WIN32) + set(OPEN_COMMAND start) + endif() add_custom_command(TARGET docs POST_BUILD COMMAND ${CMAKE_COMMAND} -E chdir ${CMAKE_BINARY_DIR}/docs/html diff --git a/sophiread/FastSophiread/CMakeLists.txt b/sophiread/FastSophiread/CMakeLists.txt index 19353c2..57d5fe7 100644 --- a/sophiread/FastSophiread/CMakeLists.txt +++ b/sophiread/FastSophiread/CMakeLists.txt @@ -1,15 +1,4 @@ -# Config SophireadLib -include_directories( - include - $ENV{CONDA_PREFIX}/include - ${EIGEN3_INCLUDE_DIR} - ${HDF5_INCLUDE_DIRS} -) - -link_directories( - $ENV{CONDA_PREFIX}/lib -) - +# Config FastSophireadLib set(SRC_FAST_FILES src/abs.cpp src/centroid.cpp @@ -21,164 +10,78 @@ set(SRC_FAST_FILES # ------------- SophireadLibFast -------------- # add_library(FastSophiread ${SRC_FAST_FILES}) - -# ----------------- TESTS ----------------- # -# DiskIO Tests -add_executable( - SophireadTests_diskio - tests/test_disk_io.cpp -) -target_link_libraries( - SophireadTests_diskio - FastSophiread - tbb - spdlog::spdlog - GTest::GTest - GTest::Main - ${HDF5_LIBRARIES} -) -gtest_discover_tests(SophireadTests_diskio) -# Hit Test -add_executable( - SophireadTests_hit - tests/test_hit.cpp -) -target_link_libraries( - SophireadTests_hit - FastSophiread - pthread - tbb - spdlog::spdlog - GTest::GTest - GTest::Main -) -gtest_discover_tests(SophireadTests_hit) -# TPX3 Test -add_executable( - SophireadTests_tpx3 - tests/test_tpx3.cpp -) -target_link_libraries( - SophireadTests_tpx3 - FastSophiread - pthread - tbb - spdlog::spdlog - GTest::GTest - GTest::Main - ${HDF5_LIBRARIES} -) -gtest_discover_tests(SophireadTests_tpx3) -# ABS Test -add_executable( - SophireadTests_abs - tests/test_abs.cpp -) -target_link_libraries( - SophireadTests_abs - FastSophiread - pthread - tbb - spdlog::spdlog - GTest::GTest - GTest::Main -) -gtest_discover_tests(SophireadTests_abs) -# Centroid Test -add_executable( - SophireadTests_centroid - tests/test_centroid.cpp -) -target_link_libraries( - SophireadTests_centroid - FastSophiread - pthread - tbb - spdlog::spdlog - GTest::GTest - GTest::Main -) -gtest_discover_tests(SophireadTests_centroid) -# Fast Gaussian Test -add_executable( - SophireadTests_fastgaussian - tests/test_fastgaussian.cpp +target_include_directories(FastSophiread + PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/include + $ENV{CONDA_PREFIX}/include + ${EIGEN3_INCLUDE_DIR} + ${HDF5_INCLUDE_DIRS} ) -target_link_libraries( - SophireadTests_fastgaussian - FastSophiread - pthread - tbb - spdlog::spdlog - GTest::GTest - GTest::Main +target_link_directories(FastSophiread + PRIVATE + $ENV{CONDA_PREFIX}/lib ) -gtest_discover_tests(SophireadTests_fastgaussian) + +# ----------------- TESTS ----------------- # +function(add_sophiread_test test_name) + add_executable(${test_name} tests/test_${test_name}.cpp) + target_link_libraries(${test_name} + PRIVATE + FastSophiread + pthread + TBB::tbb + spdlog::spdlog + GTest::GTest + GTest::Main + ${ARGN} + ) + gtest_discover_tests(${test_name}) +endfunction() +# Add tests +add_sophiread_test(disk_io ${HDF5_LIBRARIES}) +add_sophiread_test(hit) +add_sophiread_test(tpx3 ${HDF5_LIBRARIES}) +add_sophiread_test(abs) +add_sophiread_test(centroid) +add_sophiread_test(fastgaussian) + # ------------------ Benchmarks ------------------ # -# Raw2Hits Benchmark -add_executable( - SophireadBenchmarks_raw2hits - benchmarks/benchmark_raw2hits.cpp -) -target_link_libraries( - SophireadBenchmarks_raw2hits - FastSophiread - pthread - tbb - spdlog::spdlog - ${HDF5_LIBRARIES} -) -add_custom_command(TARGET SophireadBenchmarks_raw2hits POST_BUILD - COMMAND ${CMAKE_COMMAND} -E create_symlink - ${PROJECT_BINARY_DIR}/FastSophiread/SophireadBenchmarks_raw2hits - ${PROJECT_BINARY_DIR}/FastSophireadBenchmarks_raw2hits.app -) -# Hits2Events Benchmark -add_executable( - SophireadBenchmarks_hits2events - benchmarks/benchmark_hits2events.cpp -) -target_link_libraries( - SophireadBenchmarks_hits2events - FastSophiread - pthread - tbb - spdlog::spdlog -) -add_custom_command(TARGET SophireadBenchmarks_hits2events POST_BUILD - COMMAND ${CMAKE_COMMAND} -E create_symlink - ${PROJECT_BINARY_DIR}/FastSophiread/SophireadBenchmarks_hits2events - ${PROJECT_BINARY_DIR}/FastSophireadBenchmarks_hits2events.app -) -# Raw2Events Benchmark -add_executable( - SophireadBenchmarks_raw2events - benchmarks/benchmark_raw2events.cpp -) -target_link_libraries( - SophireadBenchmarks_raw2events - FastSophiread - pthread - tbb - spdlog::spdlog - ${HDF5_LIBRARIES} -) -add_custom_command(TARGET SophireadBenchmarks_raw2events POST_BUILD - COMMAND ${CMAKE_COMMAND} -E create_symlink - ${PROJECT_BINARY_DIR}/FastSophiread/SophireadBenchmarks_raw2events - ${PROJECT_BINARY_DIR}/FastSophireadBenchmarks_raw2events.app -) -# CLI -add_executable( - FastSophireadBenchmarksCLI +# Define a function to add benchmark targets +function(add_sophiread_benchmark NAME) + set(TARGET_NAME SophireadBenchmarks_${NAME}) + add_executable(${TARGET_NAME} benchmarks/benchmark_${NAME}.cpp) + + target_link_libraries(${TARGET_NAME} + PRIVATE + FastSophiread + pthread + TBB::tbb + spdlog::spdlog + ${ARGN} # Additional libraries passed as arguments + ) + + add_custom_command(TARGET ${TARGET_NAME} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E create_symlink + ${PROJECT_BINARY_DIR}/FastSophiread/${TARGET_NAME} + ${PROJECT_BINARY_DIR}/FastSophireadBenchmarks_${NAME}.app + ) +endfunction() + +# Add benchmarks +add_sophiread_benchmark(raw2hits ${HDF5_LIBRARIES}) +add_sophiread_benchmark(hits2events) +add_sophiread_benchmark(raw2events ${HDF5_LIBRARIES}) + +# ----------------- CLI ----------------- # +add_executable(FastSophireadBenchmarksCLI benchmarks/benchmark_mmap.cpp ) -target_link_libraries( - FastSophireadBenchmarksCLI +target_link_libraries(FastSophireadBenchmarksCLI + PRIVATE FastSophiread pthread - tbb + TBB::tbb spdlog::spdlog ${HDF5_LIBRARIES} ) diff --git a/sophiread/SophireadCLI/CMakeLists.txt b/sophiread/SophireadCLI/CMakeLists.txt index c809c4e..5ef3757 100644 --- a/sophiread/SophireadCLI/CMakeLists.txt +++ b/sophiread/SophireadCLI/CMakeLists.txt @@ -7,6 +7,7 @@ include_directories( # Configure the commandline application set(SRC_FILES src/user_config.cpp + src/json_config_parser.cpp src/sophiread.cpp ) @@ -15,12 +16,12 @@ add_executable(Sophiread ${SRC_FILES} ) set_target_properties(Sophiread PROPERTIES VERSION ${PROJECT_VERSION}) -target_link_libraries( - Sophiread - FastSophiread - tbb - spdlog::spdlog - ${HDF5_LIBRARIES} +target_link_libraries(Sophiread + PRIVATE FastSophiread + PRIVATE TBB::tbb + PRIVATE spdlog::spdlog + PRIVATE nlohmann_json::nlohmann_json + PRIVATE ${HDF5_LIBRARIES} ) # ----------------- TESTS ----------------- # @@ -30,14 +31,27 @@ add_executable( tests/test_user_config.cpp src/user_config.cpp ) -target_link_libraries( - UserConfigTest - FastSophiread - spdlog::spdlog - GTest::GTest - GTest::Main +target_link_libraries(UserConfigTest + PRIVATE FastSophiread + PRIVATE spdlog::spdlog + PRIVATE GTest::GTest + PRIVATE GTest::Main ) gtest_discover_tests(UserConfigTest) +# Json config test +add_executable( + JsonConfigParserTest + tests/test_json_config_parser.cpp + src/json_config_parser.cpp +) +target_link_libraries(JsonConfigParserTest + PRIVATE FastSophiread + PRIVATE spdlog::spdlog + PRIVATE GTest::GTest + PRIVATE GTest::Main + PRIVATE nlohmann_json::nlohmann_json +) +gtest_discover_tests(JsonConfigParserTest) # ----------------- SYMLINK ----------------- # # symlink the executable to the build directory From 4d0c28b469042adf767470ac6df7e118f96fb571 Mon Sep 17 00:00:00 2001 From: Chen Zhang Date: Sat, 17 Aug 2024 21:12:19 -0400 Subject: [PATCH 05/38] use interface design --- sophiread/SophireadCLI/include/iconfig.h | 37 ++++++ sophiread/SophireadCLI/include/tof_binning.h | 56 +++++++++ sophiread/SophireadCLI/include/user_config.h | 37 +++--- sophiread/SophireadCLI/src/sophiread.cpp | 2 +- sophiread/SophireadCLI/src/user_config.cpp | 33 +++++- .../SophireadCLI/tests/test_user_config.cpp | 107 +++++++++++++++--- 6 files changed, 233 insertions(+), 39 deletions(-) create mode 100644 sophiread/SophireadCLI/include/iconfig.h create mode 100644 sophiread/SophireadCLI/include/tof_binning.h diff --git a/sophiread/SophireadCLI/include/iconfig.h b/sophiread/SophireadCLI/include/iconfig.h new file mode 100644 index 0000000..a9ef181 --- /dev/null +++ b/sophiread/SophireadCLI/include/iconfig.h @@ -0,0 +1,37 @@ +/** + * @file iconfig.h + * @author Chen Zhang (zhangc@orn.gov) + * @brief Config interface + * @version 0.1 + * @date 2024-08-16 + * + * @copyright Copyright (c) 2024 + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once + +#include +#include + +class IConfig { +public: + virtual ~IConfig() = default; + + virtual double getABSRadius() const = 0; + virtual unsigned long int getABSMinClusterSize() const = 0; + virtual unsigned long int getABSSpiderTimeRange() const = 0; + virtual std::vector getTOFBinEdges() const = 0; + + virtual std::string toString() const = 0; +}; \ No newline at end of file diff --git a/sophiread/SophireadCLI/include/tof_binning.h b/sophiread/SophireadCLI/include/tof_binning.h new file mode 100644 index 0000000..ea9dd36 --- /dev/null +++ b/sophiread/SophireadCLI/include/tof_binning.h @@ -0,0 +1,56 @@ +/** + * @file tof_binning.h + * @author Chen Zhang (zhangc@orn.gov) + * @brief TOF binning + * @version 0.1 + * @date 2024-08-16 + * + * @copyright Copyright (c) 2024 + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once + +#include +#include + +struct TOFBinning { + std::optional num_bins; + std::optional tof_max; + std::vector custom_edges; + + // Default constructor + TOFBinning() : num_bins(1500), tof_max(16.7e-3) {} + + bool isUniform() const { + return num_bins.has_value() && tof_max.has_value() && custom_edges.empty(); + } + + bool isCustom() const { + return !custom_edges.empty(); + } + + std::vector getBinEdges() const { + if (isCustom()) { + return custom_edges; + } + + int bins = num_bins.value_or(1500); + double max = tof_max.value_or(16.7e-3); + std::vector edges(bins + 1); + for (int i = 0; i <= bins; ++i) { + edges[i] = max * i / bins; + } + return edges; + } +}; \ No newline at end of file diff --git a/sophiread/SophireadCLI/include/user_config.h b/sophiread/SophireadCLI/include/user_config.h index 3113ad7..104e4da 100644 --- a/sophiread/SophireadCLI/include/user_config.h +++ b/sophiread/SophireadCLI/include/user_config.h @@ -23,35 +23,34 @@ #pragma once #include +#include "iconfig.h" +#include "tof_binning.h" -class UserConfig { +class UserConfig : public IConfig { public: - UserConfig(){}; - UserConfig(const double abs_radius, unsigned long int abs_min_cluster_size, unsigned long int abs_spider_time_range) - : m_abs_radius(abs_radius), - m_abs_min_cluster_size(abs_min_cluster_size), - m_abs_spider_time_range(abs_spider_time_range){}; + UserConfig(); + UserConfig(double abs_radius, unsigned long int abs_min_cluster_size, unsigned long int abs_spider_time_range); - double getABSRadius() const { return m_abs_radius; }; - void setABSRadius(const double abs_radius) { m_abs_radius = abs_radius; }; + double getABSRadius() const override { return m_abs_radius; } + void setABSRadius(double abs_radius) { m_abs_radius = abs_radius; } - unsigned long int getABSMinClusterSize() const { return m_abs_min_cluster_size; }; - void setABSMinClusterSize(const unsigned long int abs_min_cluster_size) { - m_abs_min_cluster_size = abs_min_cluster_size; - }; + unsigned long int getABSMinClusterSize() const override { return m_abs_min_cluster_size; } + void setABSMinClusterSize(unsigned long int abs_min_cluster_size) { m_abs_min_cluster_size = abs_min_cluster_size; } - unsigned long int getABSSpidertimeRange() const { return m_abs_spider_time_range; }; - void setABSSpidertimeRange(const unsigned long int abs_spider_time_range) { - m_abs_spider_time_range = abs_spider_time_range; - }; + unsigned long int getABSSpiderTimeRange() const override { return m_abs_spider_time_range; } + void setABSSpiderTimeRange(unsigned long int abs_spider_time_range) { m_abs_spider_time_range = abs_spider_time_range; } - std::string toString() const; + std::vector getTOFBinEdges() const override { return m_tof_binning.getBinEdges(); } + void setTOFBinning(const TOFBinning& tof_binning) { m_tof_binning = tof_binning; } + void setCustomTOFBinEdges(const std::vector& edges) { m_tof_binning.custom_edges = edges; } + + std::string toString() const override; private: - // ABS members (see abs.h for details) double m_abs_radius = 5.0; unsigned long int m_abs_min_cluster_size = 1; unsigned long int m_abs_spider_time_range = 75; + TOFBinning m_tof_binning; }; -UserConfig parseUserDefinedConfigurationFile(const std::string& filepath); \ No newline at end of file +UserConfig parseUserDefinedConfigurationFile(const std::string& filepath); diff --git a/sophiread/SophireadCLI/src/sophiread.cpp b/sophiread/SophireadCLI/src/sophiread.cpp index 1ca517b..0a296c8 100644 --- a/sophiread/SophireadCLI/src/sophiread.cpp +++ b/sophiread/SophireadCLI/src/sophiread.cpp @@ -80,7 +80,7 @@ void timedProcessing(std::vector &batches, const std::vector &raw_da tbb::parallel_for(tbb::blocked_range(0, batches.size()), [&](const tbb::blocked_range &r) { // Define ABS algorithm with user-defined parameters for each thread auto abs_alg_mt = - std::make_unique(config.getABSRadius(), config.getABSMinClusterSize(), config.getABSSpidertimeRange()); + std::make_unique(config.getABSRadius(), config.getABSMinClusterSize(), config.getABSSpiderTimeRange()); for (size_t i = r.begin(); i != r.end(); ++i) { auto &tpx3 = batches[i]; diff --git a/sophiread/SophireadCLI/src/user_config.cpp b/sophiread/SophireadCLI/src/user_config.cpp index 3a5c3f0..786e958 100644 --- a/sophiread/SophireadCLI/src/user_config.cpp +++ b/sophiread/SophireadCLI/src/user_config.cpp @@ -28,16 +28,32 @@ #include #include +UserConfig::UserConfig() : m_abs_radius(5.0), m_abs_min_cluster_size(1), m_abs_spider_time_range(75), m_tof_binning() {} + +UserConfig::UserConfig(double abs_radius, unsigned long int abs_min_cluster_size, unsigned long int abs_spider_time_range) + : m_abs_radius(abs_radius), m_abs_min_cluster_size(abs_min_cluster_size), m_abs_spider_time_range(abs_spider_time_range), m_tof_binning() {} + /** * @brief Helper function to convert a user configuration to a string for console output. * - * @return std::string + * @return std::string User configuration as a string */ std::string UserConfig::toString() const { std::stringstream ss; - ss << "ABS: radius=" << m_abs_radius << ", min_cluster_size=" << m_abs_min_cluster_size + ss << "ABS: radius=" << m_abs_radius + << ", min_cluster_size=" << m_abs_min_cluster_size << ", spider_time_range=" << m_abs_spider_time_range; + // Add TOF binning information + if (m_tof_binning.isUniform()) { + ss << ", TOF bins=" << m_tof_binning.num_bins.value_or(0) + << ", TOF max=" << (m_tof_binning.tof_max.value_or(0.0) * 1000) << " ms"; // Convert to milliseconds + } else if (m_tof_binning.isCustom()) { + ss << ", Custom TOF binning with " << m_tof_binning.custom_edges.size() - 1 << " bins"; + } else { + ss << ", TOF binning not set"; + } + return ss.str(); } @@ -45,7 +61,7 @@ std::string UserConfig::toString() const { * @brief Parse the user-defined configuration file and return a UserConfig object. * * @param[in] filepath - * @return UserConfig + * @return UserConfig User-defined configuration */ UserConfig parseUserDefinedConfigurationFile(const std::string& filepath) { // Check if the file exists @@ -87,8 +103,15 @@ UserConfig parseUserDefinedConfigurationFile(const std::string& filepath) { } else if (name == "spider_time_range") { int value; ss >> value; - user_config.setABSSpidertimeRange(value); - } else { + user_config.setABSSpiderTimeRange(value); + } else if (name == "tof_bins") { + int value; + ss >> value; + } else if (name == "tof_max") { + double value; + ss >> value; + } + else { spdlog::warn("Unknown parameter {} in the user-defined configuration file.", name); } } diff --git a/sophiread/SophireadCLI/tests/test_user_config.cpp b/sophiread/SophireadCLI/tests/test_user_config.cpp index 436eda1..4a24f55 100644 --- a/sophiread/SophireadCLI/tests/test_user_config.cpp +++ b/sophiread/SophireadCLI/tests/test_user_config.cpp @@ -20,16 +20,89 @@ * along with this program. If not, see . */ #include - #include - #include "user_config.h" +#include "tof_binning.h" + +// Test default constructor +TEST(UserConfigTest, DefaultConstructor) { + UserConfig config; + EXPECT_DOUBLE_EQ(config.getABSRadius(), 5.0); + EXPECT_EQ(config.getABSMinClusterSize(), 1); + EXPECT_EQ(config.getABSSpiderTimeRange(), 75); + + auto tof_edges = config.getTOFBinEdges(); + EXPECT_EQ(tof_edges.size(), 1501); // 1500 bins + 1 + EXPECT_DOUBLE_EQ(tof_edges.front(), 0.0); + EXPECT_DOUBLE_EQ(tof_edges.back(), 16.7e-3); +} + +// Test parameterized constructor +TEST(UserConfigTest, ParameterizedConstructor) { + UserConfig config(10.0, 5, 100); + EXPECT_DOUBLE_EQ(config.getABSRadius(), 10.0); + EXPECT_EQ(config.getABSMinClusterSize(), 5); + EXPECT_EQ(config.getABSSpiderTimeRange(), 100); + + // TOF binning should still be default + auto tof_edges = config.getTOFBinEdges(); + EXPECT_EQ(tof_edges.size(), 1501); + EXPECT_DOUBLE_EQ(tof_edges.front(), 0.0); + EXPECT_DOUBLE_EQ(tof_edges.back(), 16.7e-3); +} + +// Test setters +TEST(UserConfigTest, Setters) { + UserConfig config; + config.setABSRadius(15.0); + config.setABSMinClusterSize(10); + config.setABSSpiderTimeRange(150); + + EXPECT_DOUBLE_EQ(config.getABSRadius(), 15.0); + EXPECT_EQ(config.getABSMinClusterSize(), 10); + EXPECT_EQ(config.getABSSpiderTimeRange(), 150); +} + +// Test TOF binning setter +TEST(UserConfigTest, TOFBinningSetter) { + UserConfig config; + TOFBinning custom_binning; + custom_binning.num_bins = 1000; + custom_binning.tof_max = 20000.0; + config.setTOFBinning(custom_binning); + + auto tof_edges = config.getTOFBinEdges(); + EXPECT_EQ(tof_edges.size(), 1001); // 1000 bins + 1 + EXPECT_DOUBLE_EQ(tof_edges.front(), 0.0); + EXPECT_DOUBLE_EQ(tof_edges.back(), 20000.0); +} -// Test toString method of UserConfig class +// Test custom TOF bin edges +TEST(UserConfigTest, CustomTOFBinEdges) { + UserConfig config; + std::vector custom_edges = {0.0, 100.0, 200.0, 300.0, 400.0}; + config.setCustomTOFBinEdges(custom_edges); + + auto tof_edges = config.getTOFBinEdges(); + EXPECT_EQ(tof_edges.size(), 5); + EXPECT_DOUBLE_EQ(tof_edges[0], 0.0); + EXPECT_DOUBLE_EQ(tof_edges[1], 100.0); + EXPECT_DOUBLE_EQ(tof_edges[2], 200.0); + EXPECT_DOUBLE_EQ(tof_edges[3], 300.0); + EXPECT_DOUBLE_EQ(tof_edges[4], 400.0); +} + +// Test toString method TEST(UserConfigTest, ToStringMethod) { - UserConfig config(20.0, 30, 500000); - std::string expected = "ABS: radius=20, min_cluster_size=30, spider_time_range=500000"; - ASSERT_EQ(config.toString(), expected); + UserConfig config(20.0, 30, 500); + std::string result = config.toString(); + // print the result + std::cout << result << std::endl; + EXPECT_TRUE(result.find("radius=20") != std::string::npos); + EXPECT_TRUE(result.find("min_cluster_size=30") != std::string::npos); + EXPECT_TRUE(result.find("spider_time_range=500") != std::string::npos); + EXPECT_TRUE(result.find("TOF bins=1500") != std::string::npos); + EXPECT_TRUE(result.find("TOF max=16.7 ms") != std::string::npos); } // Test parsing a valid configuration file @@ -39,13 +112,19 @@ TEST(UserConfigTest, ParseValidConfigurationFile) { testFile << "# ABS\n"; testFile << "abs_radius 20.0\n"; testFile << "abs_min_cluster_size 30\n"; - testFile << "spider_time_range 500000\n"; + testFile << "spider_time_range 500\n"; testFile.close(); UserConfig config = parseUserDefinedConfigurationFile("testConfig.txt"); - ASSERT_DOUBLE_EQ(config.getABSRadius(), 20.0); - ASSERT_EQ(config.getABSMinClusterSize(), 30); - ASSERT_EQ(config.getABSSpidertimeRange(), 500000); + EXPECT_DOUBLE_EQ(config.getABSRadius(), 20.0); + EXPECT_EQ(config.getABSMinClusterSize(), 30); + EXPECT_EQ(config.getABSSpiderTimeRange(), 500); + + // TOF binning should still be default + auto tof_edges = config.getTOFBinEdges(); + EXPECT_EQ(tof_edges.size(), 1501); + EXPECT_DOUBLE_EQ(tof_edges.front(), 0.0); + EXPECT_DOUBLE_EQ(tof_edges.back(), 16.7e-3); // Cleanup std::remove("testConfig.txt"); @@ -61,9 +140,9 @@ TEST(UserConfigTest, ParseInvalidConfigurationFile) { // It should ignore the unknown parameter and use the default value instead UserConfig config = parseUserDefinedConfigurationFile("testInvalidConfig.txt"); - ASSERT_DOUBLE_EQ(config.getABSRadius(), 5.0); // Default value - ASSERT_EQ(config.getABSMinClusterSize(), 1); // Default value - ASSERT_EQ(config.getABSSpidertimeRange(), 75); // Default value + EXPECT_DOUBLE_EQ(config.getABSRadius(), 5.0); // Default value + EXPECT_EQ(config.getABSMinClusterSize(), 1); // Default value + EXPECT_EQ(config.getABSSpiderTimeRange(), 75); // Default value // Cleanup std::remove("testInvalidConfig.txt"); @@ -72,4 +151,4 @@ TEST(UserConfigTest, ParseInvalidConfigurationFile) { int main(int argc, char **argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); -} +} \ No newline at end of file From 5597fb28771bb8bf4982053894bdcd1cf1d09d12 Mon Sep 17 00:00:00 2001 From: Chen Zhang Date: Sat, 17 Aug 2024 21:30:08 -0400 Subject: [PATCH 06/38] refactor the json parser --- .../SophireadCLI/include/json_config_parser.h | 26 ++++-- .../SophireadCLI/src/json_config_parser.cpp | 86 +++++++++++++----- sophiread/SophireadCLI/src/user_config.cpp | 6 ++ .../tests/test_json_config_parser.cpp | 89 ++++++++++++++++--- .../resources/config/example_config.json | 10 +-- 5 files changed, 173 insertions(+), 44 deletions(-) diff --git a/sophiread/SophireadCLI/include/json_config_parser.h b/sophiread/SophireadCLI/include/json_config_parser.h index 11a9704..af8981a 100644 --- a/sophiread/SophireadCLI/include/json_config_parser.h +++ b/sophiread/SophireadCLI/include/json_config_parser.h @@ -24,19 +24,31 @@ #include #include #include +#include "iconfig.h" +#include "tof_binning.h" -class JSONConfigParser { +class JSONConfigParser : public IConfig { public: static JSONConfigParser fromFile(const std::string& filepath); - double getABSRadius() const; - unsigned long int getABSMinClusterSize() const; - unsigned long int getABSSpiderTimeRange() const; - std::vector getTOFBinEdges() const; + double getABSRadius() const override; + unsigned long int getABSMinClusterSize() const override; + unsigned long int getABSSpiderTimeRange() const override; + std::vector getTOFBinEdges() const override; - std::string toString() const; + std::string toString() const override; private: JSONConfigParser(const nlohmann::json& config); nlohmann::json m_config; -}; \ No newline at end of file + TOFBinning m_tof_binning; + + void parseTOFBinning(); + + // Default values + static constexpr double DEFAULT_ABS_RADIUS = 5.0; + static constexpr unsigned long int DEFAULT_ABS_MIN_CLUSTER_SIZE = 1; + static constexpr unsigned long int DEFAULT_ABS_SPIDER_TIME_RANGE = 75; + static constexpr int DEFAULT_TOF_BINS = 1500; + static constexpr double DEFAULT_TOF_MAX = 16.7e-3; // 16.7 milliseconds +}; diff --git a/sophiread/SophireadCLI/src/json_config_parser.cpp b/sophiread/SophireadCLI/src/json_config_parser.cpp index fc91cc3..7a0ff25 100644 --- a/sophiread/SophireadCLI/src/json_config_parser.cpp +++ b/sophiread/SophireadCLI/src/json_config_parser.cpp @@ -23,6 +23,11 @@ #include #include +/** + * @brief Build JSONConfigParser from a given file + * @param filepath Path to the JSON configuration file + * @return JSONConfigParser + */ JSONConfigParser JSONConfigParser::fromFile(const std::string& filepath) { std::ifstream file(filepath); if (!file.is_open()) { @@ -39,38 +44,79 @@ JSONConfigParser JSONConfigParser::fromFile(const std::string& filepath) { return JSONConfigParser(config); } -JSONConfigParser::JSONConfigParser(const nlohmann::json& config) : m_config(config) {} +/** + * @brief Construct a new JSONConfigParser::JSONConfigParser object + * @param config JSON configuration object + */ +JSONConfigParser::JSONConfigParser(const nlohmann::json& config) : m_config(config) { + parseTOFBinning(); +} +/** + * @brief Get the ABS Radius + * @return double + */ double JSONConfigParser::getABSRadius() const { - return m_config["abs"]["radius"].get(); + return m_config.value("/abs/radius"_json_pointer, DEFAULT_ABS_RADIUS); } +/** + * @brief Get the ABS Min Cluster Size + * @return unsigned long int + */ unsigned long int JSONConfigParser::getABSMinClusterSize() const { - return m_config["abs"]["min_cluster_size"].get(); + return m_config.value("/abs/min_cluster_size"_json_pointer, DEFAULT_ABS_MIN_CLUSTER_SIZE); } +/** + * @brief Get the ABS Spider Time Range + * @return unsigned long int + */ unsigned long int JSONConfigParser::getABSSpiderTimeRange() const { - return m_config["abs"]["spider_time_range"].get(); + return m_config.value("/abs/spider_time_range"_json_pointer, DEFAULT_ABS_SPIDER_TIME_RANGE); } +/** + * @brief Get the TOF Bin Edges + * @return std::vector + */ std::vector JSONConfigParser::getTOFBinEdges() const { - const auto& tof = m_config["tof_imaging"]; - if (tof.contains("bin_edges")) { - return tof["bin_edges"].get>(); - } else if (tof.contains("uniform_bins")) { - const auto& uniform = tof["uniform_bins"]; - double start = uniform["start"].get(); - double end = uniform["end"].get(); - int num_bins = uniform["num_bins"].get(); - std::vector edges(num_bins + 1); - for (int i = 0; i <= num_bins; ++i) { - edges[i] = start + (end - start) * i / num_bins; - } - return edges; + return m_tof_binning.getBinEdges(); +} + +/** + * @brief Parse the TOF binning configuration + */ +void JSONConfigParser::parseTOFBinning() { + if (m_config.contains("/tof_imaging/bin_edges"_json_pointer)) { + m_tof_binning.custom_edges = m_config["/tof_imaging/bin_edges"_json_pointer].get>(); + } else if (m_config.contains("/tof_imaging/uniform_bins"_json_pointer)) { + const auto& uniform = m_config["/tof_imaging/uniform_bins"_json_pointer]; + m_tof_binning.num_bins = uniform.value("num_bins", DEFAULT_TOF_BINS); + m_tof_binning.tof_max = uniform.value("end", DEFAULT_TOF_MAX); + } else { + // Use default values + m_tof_binning.num_bins = DEFAULT_TOF_BINS; + m_tof_binning.tof_max = DEFAULT_TOF_MAX; } - throw std::runtime_error("TOF bin edges not specified in configuration"); } +/** + * @brief Get a string representation of the configuration + * @return std::string + */ std::string JSONConfigParser::toString() const { - return m_config.dump(2); -} \ No newline at end of file + std::stringstream ss; + ss << "ABS: radius=" << getABSRadius() + << ", min_cluster_size=" << getABSMinClusterSize() + << ", spider_time_range=" << getABSSpiderTimeRange(); + + if (m_tof_binning.isCustom()) { + ss << ", Custom TOF binning with " << m_tof_binning.custom_edges.size() - 1 << " bins"; + } else { + ss << ", TOF bins=" << m_tof_binning.num_bins.value_or(DEFAULT_TOF_BINS) + << ", TOF max=" << (m_tof_binning.tof_max.value_or(DEFAULT_TOF_MAX) * 1000) << " ms"; + } + + return ss.str(); +} diff --git a/sophiread/SophireadCLI/src/user_config.cpp b/sophiread/SophireadCLI/src/user_config.cpp index 786e958..3f9003b 100644 --- a/sophiread/SophireadCLI/src/user_config.cpp +++ b/sophiread/SophireadCLI/src/user_config.cpp @@ -28,8 +28,14 @@ #include #include +/** + * @brief Construct a new UserConfig object with default values. + */ UserConfig::UserConfig() : m_abs_radius(5.0), m_abs_min_cluster_size(1), m_abs_spider_time_range(75), m_tof_binning() {} +/** + * @brief Construct a new UserConfig object with user-defined values. + */ UserConfig::UserConfig(double abs_radius, unsigned long int abs_min_cluster_size, unsigned long int abs_spider_time_range) : m_abs_radius(abs_radius), m_abs_min_cluster_size(abs_min_cluster_size), m_abs_spider_time_range(abs_spider_time_range), m_tof_binning() {} diff --git a/sophiread/SophireadCLI/tests/test_json_config_parser.cpp b/sophiread/SophireadCLI/tests/test_json_config_parser.cpp index 4e14ef9..50fb045 100644 --- a/sophiread/SophireadCLI/tests/test_json_config_parser.cpp +++ b/sophiread/SophireadCLI/tests/test_json_config_parser.cpp @@ -21,46 +21,111 @@ #include "json_config_parser.h" #include #include +#include class JSONConfigParserTest : public ::testing::Test { protected: void SetUp() override { - std::ofstream config_file("test_config.json"); + // This setup will be used for the uniform binning test + std::ofstream config_file("test_config_uniform.json"); config_file << R"({ "abs": { - "radius": 5.0, - "min_cluster_size": 1, - "spider_time_range": 75 + "radius": 6.0, + "min_cluster_size": 2, + "spider_time_range": 80 }, "tof_imaging": { "uniform_bins": { - "start": 0, - "end": 16700, - "num_bins": 1500 + "num_bins": 1000, + "end": 0.0167 } } })"; config_file.close(); + + // Setup for custom binning test + std::ofstream config_file_custom("test_config_custom.json"); + config_file_custom << R"({ + "abs": { + "radius": 7.0, + "min_cluster_size": 3, + "spider_time_range": 85 + }, + "tof_imaging": { + "bin_edges": [0, 0.001, 0.002, 0.005, 0.01, 0.0167] + } + })"; + config_file_custom.close(); + + // Setup for default values test + std::ofstream config_file_default("test_config_default.json"); + config_file_default << R"({ + "abs": {} + })"; + config_file_default.close(); } void TearDown() override { - std::remove("test_config.json"); + std::remove("test_config_uniform.json"); + std::remove("test_config_custom.json"); + std::remove("test_config_default.json"); } }; -TEST_F(JSONConfigParserTest, ParsesConfigCorrectly) { - auto config = JSONConfigParser::fromFile("test_config.json"); +TEST_F(JSONConfigParserTest, ParsesUniformConfigCorrectly) { + auto config = JSONConfigParser::fromFile("test_config_uniform.json"); + + EXPECT_DOUBLE_EQ(config.getABSRadius(), 6.0); + EXPECT_EQ(config.getABSMinClusterSize(), 2); + EXPECT_EQ(config.getABSSpiderTimeRange(), 80); + + auto bin_edges = config.getTOFBinEdges(); + EXPECT_EQ(bin_edges.size(), 1001); // 1000 bins + 1 + EXPECT_DOUBLE_EQ(bin_edges.front(), 0); + EXPECT_DOUBLE_EQ(bin_edges.back(), 0.0167); +} + +TEST_F(JSONConfigParserTest, ParsesCustomConfigCorrectly) { + auto config = JSONConfigParser::fromFile("test_config_custom.json"); + + EXPECT_DOUBLE_EQ(config.getABSRadius(), 7.0); + EXPECT_EQ(config.getABSMinClusterSize(), 3); + EXPECT_EQ(config.getABSSpiderTimeRange(), 85); + + auto bin_edges = config.getTOFBinEdges(); + EXPECT_EQ(bin_edges.size(), 6); + EXPECT_DOUBLE_EQ(bin_edges[0], 0); + EXPECT_DOUBLE_EQ(bin_edges[1], 0.001); + EXPECT_DOUBLE_EQ(bin_edges[2], 0.002); + EXPECT_DOUBLE_EQ(bin_edges[3], 0.005); + EXPECT_DOUBLE_EQ(bin_edges[4], 0.01); + EXPECT_DOUBLE_EQ(bin_edges[5], 0.0167); +} + +TEST_F(JSONConfigParserTest, UsesDefaultValuesCorrectly) { + auto config = JSONConfigParser::fromFile("test_config_default.json"); EXPECT_DOUBLE_EQ(config.getABSRadius(), 5.0); EXPECT_EQ(config.getABSMinClusterSize(), 1); EXPECT_EQ(config.getABSSpiderTimeRange(), 75); auto bin_edges = config.getTOFBinEdges(); - EXPECT_EQ(bin_edges.size(), 1501); + EXPECT_EQ(bin_edges.size(), 1501); // 1500 bins + 1 EXPECT_DOUBLE_EQ(bin_edges.front(), 0); - EXPECT_DOUBLE_EQ(bin_edges.back(), 16700); + EXPECT_DOUBLE_EQ(bin_edges.back(), 0.0167); } TEST_F(JSONConfigParserTest, ThrowsOnMissingFile) { EXPECT_THROW(JSONConfigParser::fromFile("non_existent.json"), std::runtime_error); } + +TEST_F(JSONConfigParserTest, ToStringMethodWorksCorrectly) { + auto config = JSONConfigParser::fromFile("test_config_uniform.json"); + std::string result = config.toString(); + + EXPECT_TRUE(result.find("radius=6") != std::string::npos); + EXPECT_TRUE(result.find("min_cluster_size=2") != std::string::npos); + EXPECT_TRUE(result.find("spider_time_range=80") != std::string::npos); + EXPECT_TRUE(result.find("TOF bins=1000") != std::string::npos); + EXPECT_TRUE(result.find("TOF max=16.7 ms") != std::string::npos); +} diff --git a/sophiread/resources/config/example_config.json b/sophiread/resources/config/example_config.json index e4be83c..74c5254 100644 --- a/sophiread/resources/config/example_config.json +++ b/sophiread/resources/config/example_config.json @@ -5,11 +5,11 @@ "spider_time_range": 75 }, "tof_imaging": { - "bin_edges": [0, 1000, 2000, 3000, 16700], "uniform_bins": { - "start": 0, - "end": 16700, - "num_bins": 1500 - } + "num_bins": 1500, + "end": 0.0167 + }, + "bin_edges": [0, 0.001, 0.002, 0.005, 0.01, 0.0167], + "_comment": "use either uniform_bins or bin_edges, otherwise bin_edges will be used" } } \ No newline at end of file From f22bff5b045d191b5cbc6d4e49c6974cadde6e86 Mon Sep 17 00:00:00 2001 From: Chen Zhang Date: Sat, 17 Aug 2024 21:42:05 -0400 Subject: [PATCH 07/38] update CLI to allow new config --- sophiread/SophireadCLI/src/sophiread.cpp | 35 ++++++++++++++----- .../{config => data}/example_config.json | 0 2 files changed, 27 insertions(+), 8 deletions(-) rename sophiread/resources/{config => data}/example_config.json (100%) diff --git a/sophiread/SophireadCLI/src/sophiread.cpp b/sophiread/SophireadCLI/src/sophiread.cpp index 0a296c8..544536d 100644 --- a/sophiread/SophireadCLI/src/sophiread.cpp +++ b/sophiread/SophireadCLI/src/sophiread.cpp @@ -11,11 +11,13 @@ #include #include #include +#include #include "abs.h" #include "disk_io.h" #include "tpx3_fast.h" #include "user_config.h" +#include "json_config_parser.h" /** * @brief Timed read raw data to char vector. @@ -75,7 +77,7 @@ void timedLocateTimeStamp(std::vector &batches, const std::vector &r * @param[in] rawdata * @param[in] config */ -void timedProcessing(std::vector &batches, const std::vector &raw_data, const UserConfig &config) { +void timedProcessing(std::vector &batches, const std::vector &raw_data, const IConfig &config) { auto start = std::chrono::high_resolution_clock::now(); tbb::parallel_for(tbb::blocked_range(0, batches.size()), [&](const tbb::blocked_range &r) { // Define ABS algorithm with user-defined parameters for each thread @@ -146,7 +148,7 @@ int main(int argc, char *argv[]) { std::string in_tpx3; std::string out_hits; std::string out_events; - std::string user_defined_params; + std::string config_file; bool verbose = false; int opt; @@ -167,7 +169,7 @@ int main(int argc, char *argv[]) { out_events = optarg; break; case 'u': // user-defined params - user_defined_params = optarg; + config_file = optarg; break; case 'v': verbose = true; @@ -178,10 +180,26 @@ int main(int argc, char *argv[]) { } } - // If provided user-defined params, parse it - UserConfig config; - if (!user_defined_params.empty()) { - config = parseUserDefinedConfigurationFile(user_defined_params); + // Determine config file type and parse accordingly + std::unique_ptr config; + if (!config_file.empty()) { + std::string extension = std::filesystem::path(config_file).extension().string(); + if (extension == ".json") { + try { + config = std::make_unique(JSONConfigParser::fromFile(config_file)); + spdlog::info("Using JSON configuration file: {}", config_file); + } catch (const std::exception& e) { + spdlog::error("Error parsing JSON configuration file: {}", e.what()); + return 1; + } + } else { + spdlog::warn("Deprecated configuration format detected. Please switch to JSON format in future."); + spdlog::warn("Support for the old format will be removed in version 4.x"); + config = std::make_unique(parseUserDefinedConfigurationFile(config_file)); + } + } else { + spdlog::info("No configuration file provided. Using default values."); + config = std::make_unique(); } // recap @@ -189,6 +207,7 @@ int main(int argc, char *argv[]) { spdlog::info("Input file: {}", in_tpx3); spdlog::info("Output hits file: {}", out_hits); spdlog::info("Output events file: {}", out_events); + spdlog::info("Configuration: {}", config->toString()); } // read raw data @@ -203,7 +222,7 @@ int main(int argc, char *argv[]) { auto raw_data = timedReadDataToCharVec(in_tpx3); auto batches = timedFindTPX3H(raw_data); timedLocateTimeStamp(batches, raw_data); - timedProcessing(batches, raw_data, config); + timedProcessing(batches, raw_data, *config); auto end = std::chrono::high_resolution_clock::now(); auto elapsed = std::chrono::duration_cast(end - start).count(); spdlog::info("Total processing time: {} s", elapsed / 1e6); diff --git a/sophiread/resources/config/example_config.json b/sophiread/resources/data/example_config.json similarity index 100% rename from sophiread/resources/config/example_config.json rename to sophiread/resources/data/example_config.json From 0669b10ca8f11c72842a092c536df4538e0e9287 Mon Sep 17 00:00:00 2001 From: Chen Zhang Date: Mon, 19 Aug 2024 10:42:00 -0400 Subject: [PATCH 08/38] proper tof range --- sophiread/SophireadCLI/include/tof_binning.h | 2 +- sophiread/SophireadCLI/src/sophiread.cpp | 36 ++++++++++++++++++- .../SophireadCLI/tests/test_user_config.cpp | 8 ++--- 3 files changed, 40 insertions(+), 6 deletions(-) diff --git a/sophiread/SophireadCLI/include/tof_binning.h b/sophiread/SophireadCLI/include/tof_binning.h index ea9dd36..f27f244 100644 --- a/sophiread/SophireadCLI/include/tof_binning.h +++ b/sophiread/SophireadCLI/include/tof_binning.h @@ -30,7 +30,7 @@ struct TOFBinning { std::vector custom_edges; // Default constructor - TOFBinning() : num_bins(1500), tof_max(16.7e-3) {} + TOFBinning() : num_bins(1500), tof_max(1.0/60) {} bool isUniform() const { return num_bins.has_value() && tof_max.has_value() && custom_edges.empty(); diff --git a/sophiread/SophireadCLI/src/sophiread.cpp b/sophiread/SophireadCLI/src/sophiread.cpp index 544536d..62bdffa 100644 --- a/sophiread/SophireadCLI/src/sophiread.cpp +++ b/sophiread/SophireadCLI/src/sophiread.cpp @@ -7,6 +7,7 @@ #include #include +#include #include #include #include @@ -121,6 +122,12 @@ void timedSaveHitsToHDF5(const std::string &out_hits, std::vector &batches spdlog::info("Save hits to HDF5: {} s", elapsed / 1e6); } +/** + * @brief Timed save events to HDF5. + * + * @param[in] out_events + * @param[in] batches + */ void timedSaveEventsToHDF5(const std::string &out_events, std::vector &batches) { auto start = std::chrono::high_resolution_clock::now(); // move all events into a single vector @@ -136,6 +143,24 @@ void timedSaveEventsToHDF5(const std::string &out_events, std::vector &bat spdlog::info("Save events to HDF5: {} s", elapsed / 1e6); } +/** + * @brief Timed save TOF imaging to TIFF. + * + * @param[in] out_tof_imaging + * @param[in] batches + * @param[in] tof_bin_edges + */ +void timedSaveTOFImagingToTIFF(const std::string& out_tof_imaging, std::vector& batches, const std::vector& tof_bin_edges) { + auto start = std::chrono::high_resolution_clock::now(); + + // Implementation for TOF imaging + // TODO: Implement TOF imaging logic here + + auto end = std::chrono::high_resolution_clock::now(); + auto elapsed = std::chrono::duration_cast(end - start).count(); + spdlog::info("Save TOF imaging to TIFF: {} s", elapsed / 1e6); +} + /** * @brief Main function. * @@ -149,6 +174,7 @@ int main(int argc, char *argv[]) { std::string out_hits; std::string out_events; std::string config_file; + std::string out_tof_imaging; bool verbose = false; int opt; @@ -157,7 +183,7 @@ int main(int argc, char *argv[]) { " [-E output_event_HDF5] " + " [-u user_defined_params]" + " [-v]"; // parse command line arguments - while ((opt = getopt(argc, argv, "i:H:E:u:v")) != -1) { + while ((opt = getopt(argc, argv, "i:H:E:T:u:v")) != -1) { switch (opt) { case 'i': // input file in_tpx3 = optarg; @@ -168,6 +194,9 @@ int main(int argc, char *argv[]) { case 'E': // output event file out_events = optarg; break; + case 'T': // output TOF imaging files (TIFF) + out_tof_imaging = optarg; + break; case 'u': // user-defined params config_file = optarg; break; @@ -240,5 +269,10 @@ int main(int argc, char *argv[]) { timedSaveEventsToHDF5(out_events, batches); } + // Save TOF imaging to TIFF files + if (!out_tof_imaging.empty()) { + timedSaveTOFImagingToTIFF(out_tof_imaging, batches, config->getTOFBinEdges()); + } + return 0; } diff --git a/sophiread/SophireadCLI/tests/test_user_config.cpp b/sophiread/SophireadCLI/tests/test_user_config.cpp index 4a24f55..8c75d35 100644 --- a/sophiread/SophireadCLI/tests/test_user_config.cpp +++ b/sophiread/SophireadCLI/tests/test_user_config.cpp @@ -34,7 +34,7 @@ TEST(UserConfigTest, DefaultConstructor) { auto tof_edges = config.getTOFBinEdges(); EXPECT_EQ(tof_edges.size(), 1501); // 1500 bins + 1 EXPECT_DOUBLE_EQ(tof_edges.front(), 0.0); - EXPECT_DOUBLE_EQ(tof_edges.back(), 16.7e-3); + EXPECT_DOUBLE_EQ(tof_edges.back(), 1.0/60); } // Test parameterized constructor @@ -48,7 +48,7 @@ TEST(UserConfigTest, ParameterizedConstructor) { auto tof_edges = config.getTOFBinEdges(); EXPECT_EQ(tof_edges.size(), 1501); EXPECT_DOUBLE_EQ(tof_edges.front(), 0.0); - EXPECT_DOUBLE_EQ(tof_edges.back(), 16.7e-3); + EXPECT_DOUBLE_EQ(tof_edges.back(), 1.0/60); } // Test setters @@ -102,7 +102,7 @@ TEST(UserConfigTest, ToStringMethod) { EXPECT_TRUE(result.find("min_cluster_size=30") != std::string::npos); EXPECT_TRUE(result.find("spider_time_range=500") != std::string::npos); EXPECT_TRUE(result.find("TOF bins=1500") != std::string::npos); - EXPECT_TRUE(result.find("TOF max=16.7 ms") != std::string::npos); + EXPECT_TRUE(result.find("TOF max=16.6667 ms") != std::string::npos); } // Test parsing a valid configuration file @@ -124,7 +124,7 @@ TEST(UserConfigTest, ParseValidConfigurationFile) { auto tof_edges = config.getTOFBinEdges(); EXPECT_EQ(tof_edges.size(), 1501); EXPECT_DOUBLE_EQ(tof_edges.front(), 0.0); - EXPECT_DOUBLE_EQ(tof_edges.back(), 16.7e-3); + EXPECT_DOUBLE_EQ(tof_edges.back(), 1.0/60); // Cleanup std::remove("testConfig.txt"); From 0c6d36d0964251a2d08a1b0c943360a0687b6919 Mon Sep 17 00:00:00 2001 From: Chen Zhang Date: Mon, 19 Aug 2024 11:04:14 -0400 Subject: [PATCH 09/38] fix output unit --- sophiread/FastSophiread/include/neutron.h | 1 + sophiread/FastSophiread/src/disk_io.cpp | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/sophiread/FastSophiread/include/neutron.h b/sophiread/FastSophiread/include/neutron.h index 54fc5b2..b565110 100644 --- a/sophiread/FastSophiread/include/neutron.h +++ b/sophiread/FastSophiread/include/neutron.h @@ -34,6 +34,7 @@ class Neutron { double getTOT() const { return m_tot; } double getTOF() const { return m_tof; }; double getTOF_ns() const { return m_tof * m_scale_to_ns_40mhz; }; + double getTOT_ns() const { return m_tot * m_scale_to_ns_40mhz; }; int getNHits() const { return m_nHits; }; std::string toString() const { diff --git a/sophiread/FastSophiread/src/disk_io.cpp b/sophiread/FastSophiread/src/disk_io.cpp index be45c0f..bb9a475 100644 --- a/sophiread/FastSophiread/src/disk_io.cpp +++ b/sophiread/FastSophiread/src/disk_io.cpp @@ -348,8 +348,8 @@ void saveOrAppendNeutronToHDF5(const std::string &out_file_name, ForwardIterator { {"x", [](const Neutron &neutron) { return neutron.getX(); }}, {"y", [](const Neutron &neutron) { return neutron.getY(); }}, - {"tof", [](const Neutron &neutron) { return neutron.getTOF(); }}, - {"tot", [](const Neutron &neutron) { return neutron.getTOT(); }}, + {"tof_ns", [](const Neutron &neutron) { return neutron.getTOF_ns(); }}, + {"tot_ns", [](const Neutron &neutron) { return neutron.getTOT_ns(); }}, {"nHits", [](const Neutron &neutron) { return neutron.getNHits(); }}, }, append); From 08902204c8ee6b2eb4b9d02bdf64f92ce1b04004 Mon Sep 17 00:00:00 2001 From: Chen Zhang Date: Mon, 19 Aug 2024 12:04:21 -0400 Subject: [PATCH 10/38] add super resolution option --- sophiread/SophireadCLI/include/json_config_parser.h | 2 ++ sophiread/SophireadCLI/include/tof_binning.h | 2 +- sophiread/SophireadCLI/src/json_config_parser.cpp | 6 ++++++ sophiread/SophireadCLI/src/user_config.cpp | 2 +- .../SophireadCLI/tests/test_json_config_parser.cpp | 10 +++++++++- sophiread/resources/data/example_config.json | 3 ++- 6 files changed, 21 insertions(+), 4 deletions(-) diff --git a/sophiread/SophireadCLI/include/json_config_parser.h b/sophiread/SophireadCLI/include/json_config_parser.h index af8981a..b9c0031 100644 --- a/sophiread/SophireadCLI/include/json_config_parser.h +++ b/sophiread/SophireadCLI/include/json_config_parser.h @@ -35,6 +35,7 @@ class JSONConfigParser : public IConfig { unsigned long int getABSMinClusterSize() const override; unsigned long int getABSSpiderTimeRange() const override; std::vector getTOFBinEdges() const override; + double getSuperResolution() const; std::string toString() const override; @@ -51,4 +52,5 @@ class JSONConfigParser : public IConfig { static constexpr unsigned long int DEFAULT_ABS_SPIDER_TIME_RANGE = 75; static constexpr int DEFAULT_TOF_BINS = 1500; static constexpr double DEFAULT_TOF_MAX = 16.7e-3; // 16.7 milliseconds + static constexpr double DEFAULT_SUPER_RESOLUTION = 1.0; }; diff --git a/sophiread/SophireadCLI/include/tof_binning.h b/sophiread/SophireadCLI/include/tof_binning.h index f27f244..b79167e 100644 --- a/sophiread/SophireadCLI/include/tof_binning.h +++ b/sophiread/SophireadCLI/include/tof_binning.h @@ -46,7 +46,7 @@ struct TOFBinning { } int bins = num_bins.value_or(1500); - double max = tof_max.value_or(16.7e-3); + double max = tof_max.value_or(1.0/60); std::vector edges(bins + 1); for (int i = 0; i <= bins; ++i) { edges[i] = max * i / bins; diff --git a/sophiread/SophireadCLI/src/json_config_parser.cpp b/sophiread/SophireadCLI/src/json_config_parser.cpp index 7a0ff25..f2fa01e 100644 --- a/sophiread/SophireadCLI/src/json_config_parser.cpp +++ b/sophiread/SophireadCLI/src/json_config_parser.cpp @@ -84,6 +84,10 @@ std::vector JSONConfigParser::getTOFBinEdges() const { return m_tof_binning.getBinEdges(); } +double JSONConfigParser::getSuperResolution() const { + return m_config.value("/tof_imaging/super_resolution"_json_pointer, DEFAULT_SUPER_RESOLUTION); +} + /** * @brief Parse the TOF binning configuration */ @@ -118,5 +122,7 @@ std::string JSONConfigParser::toString() const { << ", TOF max=" << (m_tof_binning.tof_max.value_or(DEFAULT_TOF_MAX) * 1000) << " ms"; } + ss << ", Super Resolution=" << getSuperResolution(); + return ss.str(); } diff --git a/sophiread/SophireadCLI/src/user_config.cpp b/sophiread/SophireadCLI/src/user_config.cpp index 3f9003b..251ce11 100644 --- a/sophiread/SophireadCLI/src/user_config.cpp +++ b/sophiread/SophireadCLI/src/user_config.cpp @@ -34,7 +34,7 @@ UserConfig::UserConfig() : m_abs_radius(5.0), m_abs_min_cluster_size(1), m_abs_spider_time_range(75), m_tof_binning() {} /** - * @brief Construct a new UserConfig object with user-defined values. + * @brief Construct a new UserConfig object with user-defined values */ UserConfig::UserConfig(double abs_radius, unsigned long int abs_min_cluster_size, unsigned long int abs_spider_time_range) : m_abs_radius(abs_radius), m_abs_min_cluster_size(abs_min_cluster_size), m_abs_spider_time_range(abs_spider_time_range), m_tof_binning() {} diff --git a/sophiread/SophireadCLI/tests/test_json_config_parser.cpp b/sophiread/SophireadCLI/tests/test_json_config_parser.cpp index 50fb045..6c70f5e 100644 --- a/sophiread/SophireadCLI/tests/test_json_config_parser.cpp +++ b/sophiread/SophireadCLI/tests/test_json_config_parser.cpp @@ -38,7 +38,8 @@ class JSONConfigParserTest : public ::testing::Test { "uniform_bins": { "num_bins": 1000, "end": 0.0167 - } + }, + "super_resolution": 2.0 } })"; config_file.close(); @@ -72,6 +73,11 @@ class JSONConfigParserTest : public ::testing::Test { } }; +TEST_F(JSONConfigParserTest, ParsesSuperResolutionCorrectly) { + auto config = JSONConfigParser::fromFile("test_config_uniform.json"); + EXPECT_DOUBLE_EQ(config.getSuperResolution(), 2.0); +} + TEST_F(JSONConfigParserTest, ParsesUniformConfigCorrectly) { auto config = JSONConfigParser::fromFile("test_config_uniform.json"); @@ -113,6 +119,7 @@ TEST_F(JSONConfigParserTest, UsesDefaultValuesCorrectly) { EXPECT_EQ(bin_edges.size(), 1501); // 1500 bins + 1 EXPECT_DOUBLE_EQ(bin_edges.front(), 0); EXPECT_DOUBLE_EQ(bin_edges.back(), 0.0167); + EXPECT_DOUBLE_EQ(config.getSuperResolution(), 1.0); } TEST_F(JSONConfigParserTest, ThrowsOnMissingFile) { @@ -128,4 +135,5 @@ TEST_F(JSONConfigParserTest, ToStringMethodWorksCorrectly) { EXPECT_TRUE(result.find("spider_time_range=80") != std::string::npos); EXPECT_TRUE(result.find("TOF bins=1000") != std::string::npos); EXPECT_TRUE(result.find("TOF max=16.7 ms") != std::string::npos); + EXPECT_TRUE(result.find("Super Resolution=2") != std::string::npos); } diff --git a/sophiread/resources/data/example_config.json b/sophiread/resources/data/example_config.json index 74c5254..90acc4a 100644 --- a/sophiread/resources/data/example_config.json +++ b/sophiread/resources/data/example_config.json @@ -9,7 +9,8 @@ "num_bins": 1500, "end": 0.0167 }, - "bin_edges": [0, 0.001, 0.002, 0.005, 0.01, 0.0167], + "super_resolution": 2.0, + "_bin_edges": [0, 0.001, 0.002, 0.005, 0.01, 0.0167], "_comment": "use either uniform_bins or bin_edges, otherwise bin_edges will be used" } } \ No newline at end of file From 6b3c14709f8744b52a7c198a2433596ac90e2e95 Mon Sep 17 00:00:00 2001 From: Chen Zhang Date: Mon, 19 Aug 2024 14:35:35 -0400 Subject: [PATCH 11/38] add 2D histogram --- sophiread/SophireadCLI/include/iconfig.h | 1 + .../SophireadCLI/include/json_config_parser.h | 3 +- sophiread/SophireadCLI/include/user_config.h | 5 + .../SophireadCLI/src/json_config_parser.cpp | 21 +++ sophiread/SophireadCLI/src/sophiread.cpp | 153 ++++++++++++++++-- sophiread/SophireadCLI/src/user_config.cpp | 6 +- 6 files changed, 173 insertions(+), 16 deletions(-) diff --git a/sophiread/SophireadCLI/include/iconfig.h b/sophiread/SophireadCLI/include/iconfig.h index a9ef181..a9b4120 100644 --- a/sophiread/SophireadCLI/include/iconfig.h +++ b/sophiread/SophireadCLI/include/iconfig.h @@ -32,6 +32,7 @@ class IConfig { virtual unsigned long int getABSMinClusterSize() const = 0; virtual unsigned long int getABSSpiderTimeRange() const = 0; virtual std::vector getTOFBinEdges() const = 0; + virtual double getSuperResolution() const = 0; virtual std::string toString() const = 0; }; \ No newline at end of file diff --git a/sophiread/SophireadCLI/include/json_config_parser.h b/sophiread/SophireadCLI/include/json_config_parser.h index b9c0031..f1e1dd2 100644 --- a/sophiread/SophireadCLI/include/json_config_parser.h +++ b/sophiread/SophireadCLI/include/json_config_parser.h @@ -29,13 +29,14 @@ class JSONConfigParser : public IConfig { public: + static JSONConfigParser createDefault(); static JSONConfigParser fromFile(const std::string& filepath); double getABSRadius() const override; unsigned long int getABSMinClusterSize() const override; unsigned long int getABSSpiderTimeRange() const override; std::vector getTOFBinEdges() const override; - double getSuperResolution() const; + double getSuperResolution() const override; std::string toString() const override; diff --git a/sophiread/SophireadCLI/include/user_config.h b/sophiread/SophireadCLI/include/user_config.h index 104e4da..5e8238e 100644 --- a/sophiread/SophireadCLI/include/user_config.h +++ b/sophiread/SophireadCLI/include/user_config.h @@ -44,6 +44,10 @@ class UserConfig : public IConfig { void setTOFBinning(const TOFBinning& tof_binning) { m_tof_binning = tof_binning; } void setCustomTOFBinEdges(const std::vector& edges) { m_tof_binning.custom_edges = edges; } + // no super resolution for old config format + double getSuperResolution() const override {return m_super_resolution; } + void setSuperResolution(double super_resolution) {m_super_resolution = super_resolution; } + std::string toString() const override; private: @@ -51,6 +55,7 @@ class UserConfig : public IConfig { unsigned long int m_abs_min_cluster_size = 1; unsigned long int m_abs_spider_time_range = 75; TOFBinning m_tof_binning; + double m_super_resolution = 1.0; }; UserConfig parseUserDefinedConfigurationFile(const std::string& filepath); diff --git a/sophiread/SophireadCLI/src/json_config_parser.cpp b/sophiread/SophireadCLI/src/json_config_parser.cpp index f2fa01e..e2bef14 100644 --- a/sophiread/SophireadCLI/src/json_config_parser.cpp +++ b/sophiread/SophireadCLI/src/json_config_parser.cpp @@ -23,6 +23,27 @@ #include #include + +JSONConfigParser JSONConfigParser::createDefault() { + nlohmann::json default_config = { + {"abs", { + {"radius", DEFAULT_ABS_RADIUS}, + {"min_cluster_size", DEFAULT_ABS_MIN_CLUSTER_SIZE}, + {"spider_time_range", DEFAULT_ABS_SPIDER_TIME_RANGE} + }}, + {"tof_imaging", { + {"uniform_bins", { + {"num_bins", DEFAULT_TOF_BINS}, + {"end", DEFAULT_TOF_MAX} + }}, + {"super_resolution", DEFAULT_SUPER_RESOLUTION} + }} + }; + + return JSONConfigParser(default_config); +} + + /** * @brief Build JSONConfigParser from a given file * @param filepath Path to the JSON configuration file diff --git a/sophiread/SophireadCLI/src/sophiread.cpp b/sophiread/SophireadCLI/src/sophiread.cpp index 62bdffa..ab87411 100644 --- a/sophiread/SophireadCLI/src/sophiread.cpp +++ b/sophiread/SophireadCLI/src/sophiread.cpp @@ -1,7 +1,24 @@ /** + * @file sophiread.cpp + * @author Chen Zhang (zhangc@orn.gov) + * @author Su-Ann Chong (chongs@ornl.gov) * @brief CLI for reading Timepix3 raw data and parse it into neutron event * files and a tiff image (for visual inspection). + * @date 2024-08-19 * + * @copyright Copyright (c) 2024 + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . */ #include #include @@ -143,6 +160,73 @@ void timedSaveEventsToHDF5(const std::string &out_events, std::vector &bat spdlog::info("Save events to HDF5: {} s", elapsed / 1e6); } +std::vector>> timedCreateTOFImages( + const std::vector& batches, + double super_resolution, + const std::vector& tof_bin_edges) { + + auto start = std::chrono::high_resolution_clock::now(); + + // Sanity checks + if (tof_bin_edges.size() < 2) { + spdlog::error("Invalid TOF bin edges: at least 2 edges are required"); + return {}; + } + + // Initialize the TOF images container + std::vector>> tof_images(tof_bin_edges.size() - 1); + + // Calculate the dimensions of each 2D histogram based on super_resolution + // one chip: 0-255 pixel pos + // gap: 5 + // total: 0-255 + 5 + 0-255 -> 517 (TPX3@VENUS only) + int dim_x = static_cast(517 * super_resolution); + int dim_y = static_cast(517 * super_resolution); + + spdlog::debug("Creating TOF images with dimensions: {} x {}", dim_x, dim_y); + + // Initialize each TOF bin's 2D histogram + for (auto& tof_image : tof_images) { + tof_image.resize(dim_y, std::vector(dim_x, 0)); + } + + // Process neutrons from all batches + size_t total_neutrons = 0; + size_t binned_neutrons = 0; + + for (const auto& batch : batches) { + for (const auto& neutron : batch.neutrons) { + total_neutrons++; + double tof_ns = neutron.getTOF_ns(); + + // Find the correct TOF bin + // NOTE: tof_bin_edges are in sec, and tof_ns are in nano secs + auto it = std::lower_bound(tof_bin_edges.begin(), tof_bin_edges.end(), tof_ns/1e9); + if (it != tof_bin_edges.begin()) { + size_t bin_index = std::distance(tof_bin_edges.begin(), it) - 1; + + // Calculate the x and y indices in the 2D histogram + int x = static_cast(neutron.getX() * super_resolution); + int y = static_cast(neutron.getY() * super_resolution); + + // Ensure x and y are within bounds + if (x >= 0 && x < dim_x && y >= 0 && y < dim_y) { + // Increment the count in the appropriate bin and position + tof_images[bin_index][y][x]++; + binned_neutrons++; + } + } + } + } + + auto end = std::chrono::high_resolution_clock::now(); + auto elapsed = std::chrono::duration_cast(end - start).count(); + spdlog::info("TOF image creation time: {} s", elapsed / 1e6); + spdlog::info("Total neutrons: {}, Binned neutrons: {}", total_neutrons, binned_neutrons); + + return tof_images; +} + /** * @brief Timed save TOF imaging to TIFF. * @@ -150,7 +234,10 @@ void timedSaveEventsToHDF5(const std::string &out_events, std::vector &bat * @param[in] batches * @param[in] tof_bin_edges */ -void timedSaveTOFImagingToTIFF(const std::string& out_tof_imaging, std::vector& batches, const std::vector& tof_bin_edges) { +void timedSaveTOFImagingToTIFF( + const std::string& out_tof_imaging, + const std::vector>>& tof_images, + const std::string& tof_filename_base) { auto start = std::chrono::high_resolution_clock::now(); // Implementation for TOF imaging @@ -169,21 +256,32 @@ void timedSaveTOFImagingToTIFF(const std::string& out_tof_imaging, std::vector(parseUserDefinedConfigurationFile(config_file)); } } else { - spdlog::info("No configuration file provided. Using default values."); - config = std::make_unique(); + spdlog::info("No configuration file provided. Using default JSON configuration."); + config = std::make_unique(JSONConfigParser::createDefault()); } // recap if (verbose) { + spdlog::set_level(spdlog::level::debug); + spdlog::debug("Debug logging enabled"); spdlog::info("Input file: {}", in_tpx3); spdlog::info("Output hits file: {}", out_hits); spdlog::info("Output events file: {}", out_events); spdlog::info("Configuration: {}", config->toString()); } - // read raw data + // ------------- + // sanity check + // ------------- + // 1. Does the TPX3 exists? if (in_tpx3.empty()) { spdlog::error("Error: no input file specified."); spdlog::error(help_msg); return 1; } + // 2. Do we need to create a folder for the TOF Imaging + if (!out_tof_imaging.empty()) { + if (!std::filesystem::exists(out_tof_imaging)) { + std::filesystem::create_directories(out_tof_imaging); + } + } - // process file to hits + // -------- + // process + // -------- + + // raw data --> hits --> neutrons auto start = std::chrono::high_resolution_clock::now(); auto raw_data = timedReadDataToCharVec(in_tpx3); auto batches = timedFindTPX3H(raw_data); @@ -255,10 +371,20 @@ int main(int argc, char *argv[]) { auto end = std::chrono::high_resolution_clock::now(); auto elapsed = std::chrono::duration_cast(end - start).count(); spdlog::info("Total processing time: {} s", elapsed / 1e6); - // release memory of raw data std::vector().swap(raw_data); + // neutrons --2D hist--> TOF images + std::vector>> tof_images; + if (!out_tof_imaging.empty()) { + spdlog::debug("start creating tof images"); + tof_images = timedCreateTOFImages(batches, config->getSuperResolution(), config->getTOFBinEdges()); + } + + // ------------- + // Save to Disk + // ------------- + // save hits to HDF5 file if (!out_hits.empty()) { timedSaveHitsToHDF5(out_hits, batches); @@ -271,7 +397,8 @@ int main(int argc, char *argv[]) { // Save TOF imaging to TIFF files if (!out_tof_imaging.empty()) { - timedSaveTOFImagingToTIFF(out_tof_imaging, batches, config->getTOFBinEdges()); + // TODO + timedSaveTOFImagingToTIFF(out_tof_imaging, tof_images, tof_filename_base); } return 0; diff --git a/sophiread/SophireadCLI/src/user_config.cpp b/sophiread/SophireadCLI/src/user_config.cpp index 251ce11..e2fd5e5 100644 --- a/sophiread/SophireadCLI/src/user_config.cpp +++ b/sophiread/SophireadCLI/src/user_config.cpp @@ -31,13 +31,13 @@ /** * @brief Construct a new UserConfig object with default values. */ -UserConfig::UserConfig() : m_abs_radius(5.0), m_abs_min_cluster_size(1), m_abs_spider_time_range(75), m_tof_binning() {} +UserConfig::UserConfig() : m_abs_radius(5.0), m_abs_min_cluster_size(1), m_abs_spider_time_range(75), m_tof_binning(), m_super_resolution(1.0) {} /** * @brief Construct a new UserConfig object with user-defined values */ UserConfig::UserConfig(double abs_radius, unsigned long int abs_min_cluster_size, unsigned long int abs_spider_time_range) - : m_abs_radius(abs_radius), m_abs_min_cluster_size(abs_min_cluster_size), m_abs_spider_time_range(abs_spider_time_range), m_tof_binning() {} + : m_abs_radius(abs_radius), m_abs_min_cluster_size(abs_min_cluster_size), m_abs_spider_time_range(abs_spider_time_range), m_tof_binning(), m_super_resolution(1.0) {} /** * @brief Helper function to convert a user configuration to a string for console output. @@ -60,6 +60,8 @@ std::string UserConfig::toString() const { ss << ", TOF binning not set"; } + ss << ", Super Resolution=" << m_super_resolution; + return ss.str(); } From 1b275f1fff3fed2415a154347ba16f8519060a3b Mon Sep 17 00:00:00 2001 From: Chen Zhang Date: Mon, 19 Aug 2024 17:02:21 -0400 Subject: [PATCH 12/38] add skeleton for writer --- sophiread/CMakeLists.txt | 9 +++++- sophiread/SophireadCLI/CMakeLists.txt | 14 +++++---- sophiread/SophireadCLI/src/sophiread.cpp | 38 +++++++++++++++++------- 3 files changed, 44 insertions(+), 17 deletions(-) diff --git a/sophiread/CMakeLists.txt b/sophiread/CMakeLists.txt index 3bf2a93..db0e729 100644 --- a/sophiread/CMakeLists.txt +++ b/sophiread/CMakeLists.txt @@ -13,6 +13,11 @@ endif() project("Sophiread" VERSION ${SOPHIREAD_VERSION}) +# This is to avoid accidentally using Homebrew header/lib instead of the one from micromamba +if(DEFINED ENV{CONDA_PREFIX}) + set(CMAKE_PREFIX_PATH $ENV{CONDA_PREFIX} ${CMAKE_PREFIX_PATH}) +endif() + if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE Release) endif() @@ -22,11 +27,13 @@ set(CMAKE_CXX_STANDARD 20) # Dependencies find_package(Eigen3 REQUIRED) -find_package(spdlog REQUIRED) find_package(HDF5 REQUIRED COMPONENTS CXX) find_package(GTest REQUIRED) find_package(nlohmann_json 3.2.0 REQUIRED) find_package(TBB REQUIRED) +find_package(TIFF REQUIRED) +find_package(spdlog 1.8.0 REQUIRED) +find_package(fmt 7.0.0 REQUIRED) # find_package(Qt5 COMPONENTS Widgets REQUIRED) # Testing setup diff --git a/sophiread/SophireadCLI/CMakeLists.txt b/sophiread/SophireadCLI/CMakeLists.txt index 5ef3757..6fa30ca 100644 --- a/sophiread/SophireadCLI/CMakeLists.txt +++ b/sophiread/SophireadCLI/CMakeLists.txt @@ -2,6 +2,7 @@ include_directories( include ${PROJECT_SOURCE_DIR}/FastSophiread/include + ${TIFF_INCLUDE_DIRS} ) # Configure the commandline application @@ -17,11 +18,14 @@ add_executable(Sophiread ) set_target_properties(Sophiread PROPERTIES VERSION ${PROJECT_VERSION}) target_link_libraries(Sophiread - PRIVATE FastSophiread - PRIVATE TBB::tbb - PRIVATE spdlog::spdlog - PRIVATE nlohmann_json::nlohmann_json - PRIVATE ${HDF5_LIBRARIES} + PRIVATE + FastSophiread + TBB::tbb + spdlog::spdlog + fmt::fmt + nlohmann_json::nlohmann_json + ${HDF5_LIBRARIES} + ${TIFF_LIBRARIES} ) # ----------------- TESTS ----------------- # diff --git a/sophiread/SophireadCLI/src/sophiread.cpp b/sophiread/SophireadCLI/src/sophiread.cpp index ab87411..4754c64 100644 --- a/sophiread/SophireadCLI/src/sophiread.cpp +++ b/sophiread/SophireadCLI/src/sophiread.cpp @@ -237,15 +237,37 @@ std::vector>> timedCreateTOFImages( void timedSaveTOFImagingToTIFF( const std::string& out_tof_imaging, const std::vector>>& tof_images, - const std::string& tof_filename_base) { + const std::vector& tof_bin_edges, + const std::string& tof_filename_base) +{ auto start = std::chrono::high_resolution_clock::now(); - // Implementation for TOF imaging - // TODO: Implement TOF imaging logic here + // 1. Create output directory if it doesn't exist + if (!std::filesystem::exists(out_tof_imaging)) { + std::filesystem::create_directories(out_tof_imaging); + spdlog::info("Created output directory: {}", out_tof_imaging); + } + + // 2. Initialize vector for spectral data + std::vector spectral_counts(tof_images.size(), 0); + + // 3. Iterate through each TOF bin and save TIFF files + for (size_t bin = 0; bin < tof_images.size(); ++bin) { + // Construct filename + std::string filename = fmt::format("{}/{}_bin_{:04d}.tiff", out_tof_imaging, tof_filename_base, bin + 1); + + // TODO: Write or update TIFF file + + // TODO: Accumulate counts for spectral file + } + + // 4. Write spectral file + std::string spectral_filename = fmt::format("{}/spectral.txt", out_tof_imaging); + // TODO: Write spectral data to file auto end = std::chrono::high_resolution_clock::now(); - auto elapsed = std::chrono::duration_cast(end - start).count(); - spdlog::info("Save TOF imaging to TIFF: {} s", elapsed / 1e6); + auto duration = std::chrono::duration_cast(end - start); + spdlog::info("TIFF and spectral file writing completed in {} ms", duration.count()); } /** @@ -351,12 +373,6 @@ int main(int argc, char *argv[]) { spdlog::error(help_msg); return 1; } - // 2. Do we need to create a folder for the TOF Imaging - if (!out_tof_imaging.empty()) { - if (!std::filesystem::exists(out_tof_imaging)) { - std::filesystem::create_directories(out_tof_imaging); - } - } // -------- // process From 5609e528ab3fc74381c279952eee3b2d56533a52 Mon Sep 17 00:00:00 2001 From: Chen Zhang Date: Mon, 19 Aug 2024 17:04:34 -0400 Subject: [PATCH 13/38] draft implementation --- sophiread/SophireadCLI/src/sophiread.cpp | 46 +++++++++++++++++++++--- 1 file changed, 42 insertions(+), 4 deletions(-) diff --git a/sophiread/SophireadCLI/src/sophiread.cpp b/sophiread/SophireadCLI/src/sophiread.cpp index 4754c64..301a48d 100644 --- a/sophiread/SophireadCLI/src/sophiread.cpp +++ b/sophiread/SophireadCLI/src/sophiread.cpp @@ -256,14 +256,52 @@ void timedSaveTOFImagingToTIFF( // Construct filename std::string filename = fmt::format("{}/{}_bin_{:04d}.tiff", out_tof_imaging, tof_filename_base, bin + 1); - // TODO: Write or update TIFF file + // Write or update TIFF file + // TODO: accumulating mode + TIFF* tif = TIFFOpen(filename.c_str(), "w"); + if (tif) { + uint32_t width = tof_images[bin][0].size(); + uint32_t height = tof_images[bin].size(); + + TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, width); + TIFFSetField(tif, TIFFTAG_IMAGELENGTH, height); + TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 1); + TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 32); + TIFFSetField(tif, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT); + TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG); + TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK); + + for (uint32_t row = 0; row < height; ++row) { + TIFFWriteScanline(tif, const_cast(tof_images[bin][row].data()), row); + } + + TIFFClose(tif); + spdlog::debug("Wrote TIFF file: {}", filename); + } else { + spdlog::error("Failed to open TIFF file for writing: {}", filename); + } - // TODO: Accumulate counts for spectral file + // Accumulate counts for spectral file + spectral_counts[bin] = std::accumulate(tof_images[bin].begin(), tof_images[bin].end(), 0ULL, + [](unsigned long long sum, const std::vector& row) { + return sum + std::accumulate(row.begin(), row.end(), 0ULL); + }); } // 4. Write spectral file std::string spectral_filename = fmt::format("{}/spectral.txt", out_tof_imaging); - // TODO: Write spectral data to file + // Write spectral data to file + std::ofstream spectral_file(spectral_filename); + if (spectral_file.is_open()) { + spectral_file << "Upper_Edge(s)\tCounts\n"; + for (size_t bin = 0; bin < tof_bin_edges.size() - 1; ++bin) { + spectral_file << tof_bin_edges[bin + 1] << "\t" << spectral_counts[bin] << "\n"; + } + spectral_file.close(); + spdlog::info("Wrote spectral file: {}", spectral_filename); + } else { + spdlog::error("Failed to open spectral file for writing: {}", spectral_filename); + } auto end = std::chrono::high_resolution_clock::now(); auto duration = std::chrono::duration_cast(end - start); @@ -414,7 +452,7 @@ int main(int argc, char *argv[]) { // Save TOF imaging to TIFF files if (!out_tof_imaging.empty()) { // TODO - timedSaveTOFImagingToTIFF(out_tof_imaging, tof_images, tof_filename_base); + timedSaveTOFImagingToTIFF(out_tof_imaging, tof_images, config->getTOFBinEdges(), tof_filename_base); } return 0; From 339ac0d92ebbb21a501f3b92ee34a80a3ea51132 Mon Sep 17 00:00:00 2001 From: Chen Zhang Date: Tue, 20 Aug 2024 10:37:02 -0400 Subject: [PATCH 14/38] enalbe accumulating mode --- sophiread/SophireadCLI/src/sophiread.cpp | 40 +++++++++++++++++++++--- 1 file changed, 36 insertions(+), 4 deletions(-) diff --git a/sophiread/SophireadCLI/src/sophiread.cpp b/sophiread/SophireadCLI/src/sophiread.cpp index 301a48d..47aa8d4 100644 --- a/sophiread/SophireadCLI/src/sophiread.cpp +++ b/sophiread/SophireadCLI/src/sophiread.cpp @@ -255,9 +255,42 @@ void timedSaveTOFImagingToTIFF( for (size_t bin = 0; bin < tof_images.size(); ++bin) { // Construct filename std::string filename = fmt::format("{}/{}_bin_{:04d}.tiff", out_tof_imaging, tof_filename_base, bin + 1); + + // prepare container and fill with current hist2d + uint32_t width = tof_images[bin][0].size(); + uint32_t height = tof_images[bin].size(); + std::vector> accumulated_image = tof_images[bin]; + + // check if file already exist + if (std::filesystem::exists(filename)) { + TIFF* existing_tif = TIFFOpen(filename.c_str(), "r"); + if (existing_tif) { + uint32_t existing_width, existing_height; + TIFFGetField(existing_tif, TIFFTAG_IMAGEWIDTH, &existing_width); + TIFFGetField(existing_tif, TIFFTAG_IMAGELENGTH, &existing_height); + + if (existing_width == width && existing_height == height) { + // Dimensions match, proceed with accumulation + for (uint32_t row = 0; row < height; ++row) { + std::vector scanline(width); + TIFFReadScanline(existing_tif, scanline.data(), row); + for (uint32_t col = 0; col < width; ++col) { + accumulated_image[row][col] += scanline[col]; + } + } + spdlog::debug("Accumulated counts for existing file: {}", filename); + } else { + spdlog::error("Dimension mismatch for file: {}. Expected {}x{}, got {}x{}. Overwriting.", + filename, width, height, existing_width, existing_height); + } + TIFFClose(existing_tif); + } else { + spdlog::error("Failed to open existing TIFF file for reading: {}", filename); + } + } + // Write or update TIFF file - // TODO: accumulating mode TIFF* tif = TIFFOpen(filename.c_str(), "w"); if (tif) { uint32_t width = tof_images[bin][0].size(); @@ -272,7 +305,7 @@ void timedSaveTOFImagingToTIFF( TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK); for (uint32_t row = 0; row < height; ++row) { - TIFFWriteScanline(tif, const_cast(tof_images[bin][row].data()), row); + TIFFWriteScanline(tif, accumulated_image[row].data(), row); } TIFFClose(tif); @@ -282,7 +315,7 @@ void timedSaveTOFImagingToTIFF( } // Accumulate counts for spectral file - spectral_counts[bin] = std::accumulate(tof_images[bin].begin(), tof_images[bin].end(), 0ULL, + spectral_counts[bin] = std::accumulate(accumulated_image.begin(), accumulated_image.end(), 0ULL, [](unsigned long long sum, const std::vector& row) { return sum + std::accumulate(row.begin(), row.end(), 0ULL); }); @@ -451,7 +484,6 @@ int main(int argc, char *argv[]) { // Save TOF imaging to TIFF files if (!out_tof_imaging.empty()) { - // TODO timedSaveTOFImagingToTIFF(out_tof_imaging, tof_images, config->getTOFBinEdges(), tof_filename_base); } From dc602acc19910ff5a8295232b5090806d6ffa9c1 Mon Sep 17 00:00:00 2001 From: Chen Zhang Date: Tue, 20 Aug 2024 11:04:41 -0400 Subject: [PATCH 15/38] add new arg for hits to tof img --- sophiread/SophireadCLI/src/sophiread.cpp | 51 ++++++++++++++---------- 1 file changed, 30 insertions(+), 21 deletions(-) diff --git a/sophiread/SophireadCLI/src/sophiread.cpp b/sophiread/SophireadCLI/src/sophiread.cpp index 47aa8d4..200db6f 100644 --- a/sophiread/SophireadCLI/src/sophiread.cpp +++ b/sophiread/SophireadCLI/src/sophiread.cpp @@ -359,22 +359,24 @@ int main(int argc, char *argv[]) { std::string out_tof_imaging; std::string tof_filename_base = "tof_image"; bool verbose = false; + std::string tof_mode = "neutron"; // Default to neutron-based TOF imaging int opt; // help message string std::string help_msg = - "Usage: " + + "Usage:\n" + std::string(argv[0]) + - " [-i input_tpx3] " + - " [-H output_hits_HDF5] " + - " [-E output_event_HDF5] " + - " [-u user_defined_params]" + - " [-T tof_imaging_folder]" + - " [-f tof_filename_base]" + - " [-v]"; + "\n\t[-i input_tpx3] " + + "\n\t[-H output_hits_HDF5] " + + "\n\t[-E output_event_HDF5] " + + "\n\t[-u user_defined_params]" + + "\n\t[-T tof_imaging_folder]" + + "\n\t[-f tof_filename_base]" + + "\n\t[-m tof_mode(hit or neutron)]" + + "\n\t[-v]"; // parse command line arguments - while ((opt = getopt(argc, argv, "i:H:E:T:f:u:v")) != -1) { + while ((opt = getopt(argc, argv, "i:H:E:T:f:u:m:v")) != -1) { switch (opt) { case 'i': // input file in_tpx3 = optarg; @@ -394,6 +396,9 @@ int main(int argc, char *argv[]) { case 'f': // TOF filename base tof_filename_base = optarg; break; + case 'm': + tof_mode = optarg; + break; case 'v': verbose = true; break; @@ -403,7 +408,10 @@ int main(int argc, char *argv[]) { } } - // Determine config file type and parse accordingly + // ------------- + // sanity check + // ------------- + // Check 1: determine config file type and parse accordingly std::unique_ptr config; if (!config_file.empty()) { std::string extension = std::filesystem::path(config_file).extension().string(); @@ -424,7 +432,17 @@ int main(int argc, char *argv[]) { spdlog::info("No configuration file provided. Using default JSON configuration."); config = std::make_unique(JSONConfigParser::createDefault()); } - + // Check 2: does the TPX3 exists? + if (in_tpx3.empty()) { + spdlog::error("Error: no input file specified."); + spdlog::error(help_msg); + return 1; + } + // Check 3: model valid + if (tof_mode != "neutron" && tof_mode != "hit") { + spdlog::error("Invalid TOF mode specified. Use 'neutron' or 'hit'."); + return 1; + } // recap if (verbose) { spdlog::set_level(spdlog::level::debug); @@ -433,16 +451,7 @@ int main(int argc, char *argv[]) { spdlog::info("Output hits file: {}", out_hits); spdlog::info("Output events file: {}", out_events); spdlog::info("Configuration: {}", config->toString()); - } - - // ------------- - // sanity check - // ------------- - // 1. Does the TPX3 exists? - if (in_tpx3.empty()) { - spdlog::error("Error: no input file specified."); - spdlog::error(help_msg); - return 1; + spdlog::info("TOF imaging mode: {}", tof_mode); } // -------- From c46c8420b13bb4a612c2be05c8e06927c85a4f47 Mon Sep 17 00:00:00 2001 From: Chen Zhang Date: Tue, 20 Aug 2024 12:45:14 -0400 Subject: [PATCH 16/38] allow both hit and neutron tiff img mode --- sophiread/FastSophiread/include/hit.h | 9 +- .../FastSophiread/include/iposition_tof.h | 30 +++++ sophiread/FastSophiread/include/neutron.h | 9 +- sophiread/SophireadCLI/src/sophiread.cpp | 109 +++++++++++++----- 4 files changed, 129 insertions(+), 28 deletions(-) create mode 100644 sophiread/FastSophiread/include/iposition_tof.h diff --git a/sophiread/FastSophiread/include/hit.h b/sophiread/FastSophiread/include/hit.h index 1850689..af8fa9e 100644 --- a/sophiread/FastSophiread/include/hit.h +++ b/sophiread/FastSophiread/include/hit.h @@ -24,7 +24,9 @@ #include #include -class Hit { +#include "iposition_tof.h" + +class Hit : public IPositionTOF { public: // default constructor Hit() : m_x(0), m_y(0), m_tot(0), m_toa(0), m_ftoa(0), m_tof(0), m_spidertime(0){}; @@ -77,6 +79,11 @@ class Hit { return ss.str(); }; + // Implement the interface methods + double iGetX() const override { return static_cast(getX()); } + double iGetY() const override { return static_cast(getY()); } + double iGetTOF_ns() const override { return getTOF_ns(); } + private: // raw packet directly read from tpx3. int m_x, m_y; // pixel coordinates diff --git a/sophiread/FastSophiread/include/iposition_tof.h b/sophiread/FastSophiread/include/iposition_tof.h new file mode 100644 index 0000000..6f09783 --- /dev/null +++ b/sophiread/FastSophiread/include/iposition_tof.h @@ -0,0 +1,30 @@ +/** + * @file iposition_tof.h + * @author Chen Zhang (zhangc@orn.gov) + * @brief Interface for neutron and hit + * @version 0.1 + * @date 2024-08-20 + * + * @copyright Copyright (c) 2024 + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once + +class IPositionTOF { +public: + virtual ~IPositionTOF() = default; + virtual double iGetX() const = 0; + virtual double iGetY() const = 0; + virtual double iGetTOF_ns() const = 0; +}; diff --git a/sophiread/FastSophiread/include/neutron.h b/sophiread/FastSophiread/include/neutron.h index b565110..f39e43f 100644 --- a/sophiread/FastSophiread/include/neutron.h +++ b/sophiread/FastSophiread/include/neutron.h @@ -24,7 +24,9 @@ #include #include -class Neutron { +#include "iposition_tof.h" + +class Neutron : public IPositionTOF { public: Neutron(const double x, const double y, const double tof, const double tot, const int nHits) : m_x(x), m_y(y), m_tof(tof), m_tot(tot), m_nHits(nHits){}; @@ -43,6 +45,11 @@ class Neutron { return ss.str(); }; + // Implement the interface methods + double iGetX() const override { return getX(); } + double iGetY() const override { return getY(); } + double iGetTOF_ns() const override { return getTOF_ns(); } + private: double m_x, m_y; // pixel coordinates double m_tof; // time of flight diff --git a/sophiread/SophireadCLI/src/sophiread.cpp b/sophiread/SophireadCLI/src/sophiread.cpp index 200db6f..7812dd6 100644 --- a/sophiread/SophireadCLI/src/sophiread.cpp +++ b/sophiread/SophireadCLI/src/sophiread.cpp @@ -160,21 +160,37 @@ void timedSaveEventsToHDF5(const std::string &out_events, std::vector &bat spdlog::info("Save events to HDF5: {} s", elapsed / 1e6); } +/** + * @brief Timed create TOF images. + * + * This function creates time-of-flight (TOF) images based on the provided batches of TPX3 data. The TOF images are 2D histograms that represent the distribution of neutron events in space for different TOF bins. The function takes into account the super resolution, TOF bin edges, and the mode of operation (hit or neutron) to generate the TOF images. + * + * @param[in] batches The vector of TPX3 batches containing the neutron events. + * @param[in] super_resolution The super resolution factor used to calculate the dimensions of each 2D histogram. + * @param[in] tof_bin_edges The vector of TOF bin edges used to determine the TOF bin for each neutron event. + * @param[in] mode The mode of operation, either "hit" or "neutron", which determines the type of events to process. + * @return std::vector>> The vector of TOF images, where each TOF image is a 2D histogram representing the distribution of neutron events in space for a specific TOF bin. + */ std::vector>> timedCreateTOFImages( const std::vector& batches, double super_resolution, - const std::vector& tof_bin_edges) { + const std::vector& tof_bin_edges, + const std::string& mode) { auto start = std::chrono::high_resolution_clock::now(); + // Initialize the TOF images container + std::vector>> tof_images(tof_bin_edges.size() - 1); + // Sanity checks if (tof_bin_edges.size() < 2) { spdlog::error("Invalid TOF bin edges: at least 2 edges are required"); return {}; } - - // Initialize the TOF images container - std::vector>> tof_images(tof_bin_edges.size() - 1); + if (batches.empty()) { + spdlog::error("No batches to process"); + return tof_images; + } // Calculate the dimensions of each 2D histogram based on super_resolution // one chip: 0-255 pixel pos @@ -184,6 +200,10 @@ std::vector>> timedCreateTOFImages( int dim_y = static_cast(517 * super_resolution); spdlog::debug("Creating TOF images with dimensions: {} x {}", dim_x, dim_y); + spdlog::debug("tof_bin_edges size: {}", tof_bin_edges.size()); + if (!tof_bin_edges.empty()) { + spdlog::debug("First bin edge: {}, Last bin edge: {}", tof_bin_edges.front(), tof_bin_edges.back()); + } // Initialize each TOF bin's 2D histogram for (auto& tof_image : tof_images) { @@ -191,29 +211,51 @@ std::vector>> timedCreateTOFImages( } // Process neutrons from all batches - size_t total_neutrons = 0; - size_t binned_neutrons = 0; - - for (const auto& batch : batches) { - for (const auto& neutron : batch.neutrons) { - total_neutrons++; - double tof_ns = neutron.getTOF_ns(); - + size_t total_entries = 0; + size_t binned_entries = 0; + + for (size_t batch_index = 0; batch_index < batches.size(); ++batch_index) { + const auto& batch = batches[batch_index]; + spdlog::debug("Processing batch {}", batch_index); + + std::vector entries; + if (mode == "hit") { + entries.reserve(batch.hits.size()); + for (const auto& hit : batch.hits) { + entries.push_back(static_cast(&hit)); + } + } else { + entries.reserve(batch.neutrons.size()); + for (const auto& neutron : batch.neutrons) { + entries.push_back(static_cast(&neutron)); + } + } + + if (entries.empty()) { + spdlog::debug("Batch {} is empty", batch_index); + continue; + } + + for (const auto& entry : entries) { + total_entries++; + double tof_ns = entry->iGetTOF_ns(); + // Find the correct TOF bin // NOTE: tof_bin_edges are in sec, and tof_ns are in nano secs + spdlog::debug("tof_ns: {}, tof_ns/1e9: {}", tof_ns, tof_ns/1e9); auto it = std::lower_bound(tof_bin_edges.begin(), tof_bin_edges.end(), tof_ns/1e9); if (it != tof_bin_edges.begin()) { size_t bin_index = std::distance(tof_bin_edges.begin(), it) - 1; // Calculate the x and y indices in the 2D histogram - int x = static_cast(neutron.getX() * super_resolution); - int y = static_cast(neutron.getY() * super_resolution); + int x = static_cast(entry->iGetX() * super_resolution); + int y = static_cast(entry->iGetY() * super_resolution); // Ensure x and y are within bounds if (x >= 0 && x < dim_x && y >= 0 && y < dim_y) { // Increment the count in the appropriate bin and position tof_images[bin_index][y][x]++; - binned_neutrons++; + binned_entries++; } } } @@ -222,7 +264,7 @@ std::vector>> timedCreateTOFImages( auto end = std::chrono::high_resolution_clock::now(); auto elapsed = std::chrono::duration_cast(end - start).count(); spdlog::info("TOF image creation time: {} s", elapsed / 1e6); - spdlog::info("Total neutrons: {}, Binned neutrons: {}", total_neutrons, binned_neutrons); + spdlog::info("Total entries: {}, Binned entries: {}", total_entries, binned_entries); return tof_images; } @@ -358,8 +400,9 @@ int main(int argc, char *argv[]) { std::string config_file; std::string out_tof_imaging; std::string tof_filename_base = "tof_image"; - bool verbose = false; std::string tof_mode = "neutron"; // Default to neutron-based TOF imaging + bool debug_logging = false; + bool verbose = false; int opt; // help message string @@ -373,10 +416,11 @@ int main(int argc, char *argv[]) { "\n\t[-T tof_imaging_folder]" + "\n\t[-f tof_filename_base]" + "\n\t[-m tof_mode(hit or neutron)]" + + "\n\t[-d]" + "\n\t[-v]"; // parse command line arguments - while ((opt = getopt(argc, argv, "i:H:E:T:f:u:m:v")) != -1) { + while ((opt = getopt(argc, argv, "i:H:E:T:f:u:m:dv")) != -1) { switch (opt) { case 'i': // input file in_tpx3 = optarg; @@ -399,6 +443,9 @@ int main(int argc, char *argv[]) { case 'm': tof_mode = optarg; break; + case 'd': + debug_logging = true; + break; case 'v': verbose = true; break; @@ -407,6 +454,16 @@ int main(int argc, char *argv[]) { return 1; } } + + // Set Logging + if (debug_logging) { + spdlog::set_level(spdlog::level::debug); + spdlog::debug("Debug logging enabled"); + } else if (verbose) { + spdlog::set_level(spdlog::level::info); + } else { + spdlog::set_level(spdlog::level::warn); + } // ------------- // sanity check @@ -445,13 +502,13 @@ int main(int argc, char *argv[]) { } // recap if (verbose) { - spdlog::set_level(spdlog::level::debug); - spdlog::debug("Debug logging enabled"); - spdlog::info("Input file: {}", in_tpx3); - spdlog::info("Output hits file: {}", out_hits); - spdlog::info("Output events file: {}", out_events); - spdlog::info("Configuration: {}", config->toString()); - spdlog::info("TOF imaging mode: {}", tof_mode); + spdlog::info("Recap input:"); + spdlog::info("\tInput file: {}", in_tpx3); + spdlog::info("\tOutput hits file: {}", out_hits); + spdlog::info("\tOutput events file: {}", out_events); + spdlog::info("\tConfiguration: {}", config->toString()); + spdlog::info("\tTOF imaging mode: {}", tof_mode); + spdlog::info("\tDebug logging: {}", debug_logging ? "Enabled" : "Disabled"); } // -------- @@ -474,7 +531,7 @@ int main(int argc, char *argv[]) { std::vector>> tof_images; if (!out_tof_imaging.empty()) { spdlog::debug("start creating tof images"); - tof_images = timedCreateTOFImages(batches, config->getSuperResolution(), config->getTOFBinEdges()); + tof_images = timedCreateTOFImages(batches, config->getSuperResolution(), config->getTOFBinEdges(), tof_mode); } // ------------- From c0d256861014913c47a1d188f20ef7360a9ba954 Mon Sep 17 00:00:00 2001 From: Chen Zhang Date: Tue, 20 Aug 2024 16:03:03 -0400 Subject: [PATCH 17/38] tbb tiff writing --- sophiread/SophireadCLI/src/sophiread.cpp | 138 ++++++++++++----------- 1 file changed, 70 insertions(+), 68 deletions(-) diff --git a/sophiread/SophireadCLI/src/sophiread.cpp b/sophiread/SophireadCLI/src/sophiread.cpp index 7812dd6..3ca9d49 100644 --- a/sophiread/SophireadCLI/src/sophiread.cpp +++ b/sophiread/SophireadCLI/src/sophiread.cpp @@ -294,74 +294,76 @@ void timedSaveTOFImagingToTIFF( std::vector spectral_counts(tof_images.size(), 0); // 3. Iterate through each TOF bin and save TIFF files - for (size_t bin = 0; bin < tof_images.size(); ++bin) { - // Construct filename - std::string filename = fmt::format("{}/{}_bin_{:04d}.tiff", out_tof_imaging, tof_filename_base, bin + 1); - - // prepare container and fill with current hist2d - uint32_t width = tof_images[bin][0].size(); - uint32_t height = tof_images[bin].size(); - std::vector> accumulated_image = tof_images[bin]; - - // check if file already exist - if (std::filesystem::exists(filename)) { - TIFF* existing_tif = TIFFOpen(filename.c_str(), "r"); - if (existing_tif) { - uint32_t existing_width, existing_height; - TIFFGetField(existing_tif, TIFFTAG_IMAGEWIDTH, &existing_width); - TIFFGetField(existing_tif, TIFFTAG_IMAGELENGTH, &existing_height); - - if (existing_width == width && existing_height == height) { - // Dimensions match, proceed with accumulation - for (uint32_t row = 0; row < height; ++row) { - std::vector scanline(width); - TIFFReadScanline(existing_tif, scanline.data(), row); - for (uint32_t col = 0; col < width; ++col) { - accumulated_image[row][col] += scanline[col]; - } - } - spdlog::debug("Accumulated counts for existing file: {}", filename); - } else { - spdlog::error("Dimension mismatch for file: {}. Expected {}x{}, got {}x{}. Overwriting.", - filename, width, height, existing_width, existing_height); - } - TIFFClose(existing_tif); - } else { - spdlog::error("Failed to open existing TIFF file for reading: {}", filename); - } - } - - - // Write or update TIFF file - TIFF* tif = TIFFOpen(filename.c_str(), "w"); - if (tif) { - uint32_t width = tof_images[bin][0].size(); - uint32_t height = tof_images[bin].size(); - - TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, width); - TIFFSetField(tif, TIFFTAG_IMAGELENGTH, height); - TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 1); - TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 32); - TIFFSetField(tif, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT); - TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG); - TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK); - - for (uint32_t row = 0; row < height; ++row) { - TIFFWriteScanline(tif, accumulated_image[row].data(), row); - } - - TIFFClose(tif); - spdlog::debug("Wrote TIFF file: {}", filename); - } else { - spdlog::error("Failed to open TIFF file for writing: {}", filename); - } - - // Accumulate counts for spectral file - spectral_counts[bin] = std::accumulate(accumulated_image.begin(), accumulated_image.end(), 0ULL, - [](unsigned long long sum, const std::vector& row) { - return sum + std::accumulate(row.begin(), row.end(), 0ULL); - }); - } + tbb::parallel_for(tbb::blocked_range(0, tof_images.size()), [&](const tbb::blocked_range& range) { + for (size_t bin = range.begin(); bin < range.end(); ++bin) { + // Construct filename + std::string filename = fmt::format("{}/{}_bin_{:04d}.tiff", out_tof_imaging, tof_filename_base, bin + 1); + + // prepare container and fill with current hist2d + uint32_t width = tof_images[bin][0].size(); + uint32_t height = tof_images[bin].size(); + std::vector> accumulated_image = tof_images[bin]; + + // check if file already exist + if (std::filesystem::exists(filename)) { + TIFF* existing_tif = TIFFOpen(filename.c_str(), "r"); + if (existing_tif) { + uint32_t existing_width, existing_height; + TIFFGetField(existing_tif, TIFFTAG_IMAGEWIDTH, &existing_width); + TIFFGetField(existing_tif, TIFFTAG_IMAGELENGTH, &existing_height); + + if (existing_width == width && existing_height == height) { + // Dimensions match, proceed with accumulation + for (uint32_t row = 0; row < height; ++row) { + std::vector scanline(width); + TIFFReadScanline(existing_tif, scanline.data(), row); + for (uint32_t col = 0; col < width; ++col) { + accumulated_image[row][col] += scanline[col]; + } + } + spdlog::debug("Accumulated counts for existing file: {}", filename); + } else { + spdlog::error("Dimension mismatch for file: {}. Expected {}x{}, got {}x{}. Overwriting.", + filename, width, height, existing_width, existing_height); + } + TIFFClose(existing_tif); + } else { + spdlog::error("Failed to open existing TIFF file for reading: {}", filename); + } + } + + + // Write or update TIFF file + TIFF* tif = TIFFOpen(filename.c_str(), "w"); + if (tif) { + uint32_t width = tof_images[bin][0].size(); + uint32_t height = tof_images[bin].size(); + + TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, width); + TIFFSetField(tif, TIFFTAG_IMAGELENGTH, height); + TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 1); + TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 32); + TIFFSetField(tif, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT); + TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG); + TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK); + + for (uint32_t row = 0; row < height; ++row) { + TIFFWriteScanline(tif, accumulated_image[row].data(), row); + } + + TIFFClose(tif); + spdlog::debug("Wrote TIFF file: {}", filename); + } else { + spdlog::error("Failed to open TIFF file for writing: {}", filename); + } + + // Accumulate counts for spectral file + spectral_counts[bin] = std::accumulate(accumulated_image.begin(), accumulated_image.end(), 0ULL, + [](unsigned long long sum, const std::vector& row) { + return sum + std::accumulate(row.begin(), row.end(), 0ULL); + }); + } + }); // 4. Write spectral file std::string spectral_filename = fmt::format("{}/spectral.txt", out_tof_imaging); From ad52e495f1fc93fb559be5b2b40208e2f10ac4d1 Mon Sep 17 00:00:00 2001 From: Chen Zhang Date: Wed, 21 Aug 2024 09:27:33 -0400 Subject: [PATCH 18/38] update docs --- sophiread/SophireadCLI/src/sophiread.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/sophiread/SophireadCLI/src/sophiread.cpp b/sophiread/SophireadCLI/src/sophiread.cpp index 3ca9d49..e2de665 100644 --- a/sophiread/SophireadCLI/src/sophiread.cpp +++ b/sophiread/SophireadCLI/src/sophiread.cpp @@ -275,6 +275,7 @@ std::vector>> timedCreateTOFImages( * @param[in] out_tof_imaging * @param[in] batches * @param[in] tof_bin_edges + * @param[in] tof_filename_base */ void timedSaveTOFImagingToTIFF( const std::string& out_tof_imaging, @@ -332,7 +333,6 @@ void timedSaveTOFImagingToTIFF( } } - // Write or update TIFF file TIFF* tif = TIFFOpen(filename.c_str(), "w"); if (tif) { @@ -442,7 +442,7 @@ int main(int argc, char *argv[]) { case 'f': // TOF filename base tof_filename_base = optarg; break; - case 'm': + case 'm': // mode for TOF imaging, neutron: neutrons->tiff; hit: hit->tiff tof_mode = optarg; break; case 'd': @@ -516,7 +516,6 @@ int main(int argc, char *argv[]) { // -------- // process // -------- - // raw data --> hits --> neutrons auto start = std::chrono::high_resolution_clock::now(); auto raw_data = timedReadDataToCharVec(in_tpx3); @@ -529,7 +528,9 @@ int main(int argc, char *argv[]) { // release memory of raw data std::vector().swap(raw_data); - // neutrons --2D hist--> TOF images + // neutrons/hits --2D hist--> TOF images + // NOTE: since x,y are int from hit, so the super resolution will produce checked image, + // so users should keep sr at 1 for hit->tiff std::vector>> tof_images; if (!out_tof_imaging.empty()) { spdlog::debug("start creating tof images"); @@ -539,7 +540,6 @@ int main(int argc, char *argv[]) { // ------------- // Save to Disk // ------------- - // save hits to HDF5 file if (!out_hits.empty()) { timedSaveHitsToHDF5(out_hits, batches); From 00a7e5a05229646a2ecda9803b7b42fac75f7a6e Mon Sep 17 00:00:00 2001 From: Chen Zhang Date: Wed, 21 Aug 2024 11:37:13 -0400 Subject: [PATCH 19/38] separating cli core and app --- sophiread/SophireadCLI/CMakeLists.txt | 40 +- .../SophireadCLI/include/sophiread_core.h | 51 ++ sophiread/SophireadCLI/src/sophiread.cpp | 701 +++++++++--------- sophiread/SophireadCLI/src/sophiread_core.cpp | 402 ++++++++++ 4 files changed, 835 insertions(+), 359 deletions(-) create mode 100644 sophiread/SophireadCLI/include/sophiread_core.h create mode 100644 sophiread/SophireadCLI/src/sophiread_core.cpp diff --git a/sophiread/SophireadCLI/CMakeLists.txt b/sophiread/SophireadCLI/CMakeLists.txt index 6fa30ca..a8d8029 100644 --- a/sophiread/SophireadCLI/CMakeLists.txt +++ b/sophiread/SophireadCLI/CMakeLists.txt @@ -9,6 +9,7 @@ include_directories( set(SRC_FILES src/user_config.cpp src/json_config_parser.cpp + src/sophiread_core.cpp src/sophiread.cpp ) @@ -36,10 +37,11 @@ add_executable( src/user_config.cpp ) target_link_libraries(UserConfigTest - PRIVATE FastSophiread - PRIVATE spdlog::spdlog - PRIVATE GTest::GTest - PRIVATE GTest::Main + PRIVATE + FastSophiread + spdlog::spdlog + GTest::GTest + GTest::Main ) gtest_discover_tests(UserConfigTest) # Json config test @@ -49,13 +51,33 @@ add_executable( src/json_config_parser.cpp ) target_link_libraries(JsonConfigParserTest - PRIVATE FastSophiread - PRIVATE spdlog::spdlog - PRIVATE GTest::GTest - PRIVATE GTest::Main - PRIVATE nlohmann_json::nlohmann_json + PRIVATE + FastSophiread + spdlog::spdlog + GTest::GTest + GTest::Main + nlohmann_json::nlohmann_json ) gtest_discover_tests(JsonConfigParserTest) +# core test +# add_executable( +# SophireadCoreTest +# tests/test_sophiread_core.cpp +# src/sophiread_core.cpp +# src/json_config_parser.cpp +# ) +# target_link_libraries(SophireadCoreTest +# PRIVATE +# FastSophiread +# spdlog::spdlog +# GTest::GTest +# GTest::Main +# nlohmann_json::nlohmann_json +# ${HDF5_LIBRARIES} +# ${TIFF_LIBRARIES} +# TBB::tbb +# ) +# gtest_discover_tests(SophireadCoreTest) # ----------------- SYMLINK ----------------- # # symlink the executable to the build directory diff --git a/sophiread/SophireadCLI/include/sophiread_core.h b/sophiread/SophireadCLI/include/sophiread_core.h new file mode 100644 index 0000000..97983e7 --- /dev/null +++ b/sophiread/SophireadCLI/include/sophiread_core.h @@ -0,0 +1,51 @@ +/** + * @file sophiread_core.h + * @author Chen Zhang (zhangc@orn.gov) + * @brief Core functions for CLI application + * @version 0.1 + * @date 2024-08-21 + * + * @copyright Copyright (c) 2024 + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once + +#include +#include +#include "abs.h" +#include "tpx3_fast.h" +#include "iconfig.h" + +namespace sophiread { + +std::vector timedReadDataToCharVec(const std::string& in_tpx3); +std::vector timedFindTPX3H(const std::vector& rawdata); +template + std::vector timedFindTPX3H(ForwardIter begin, ForwardIter end, std::size_t& consumed); +void timedLocateTimeStamp(std::vector& batches, const std::vector& rawdata); +void timedProcessing(std::vector& batches, const std::vector& raw_data, const IConfig& config); +void timedSaveHitsToHDF5(const std::string& out_hits, std::vector& batches); +void timedSaveEventsToHDF5(const std::string& out_events, std::vector& batches); +std::vector>> timedCreateTOFImages( + const std::vector& batches, + double super_resolution, + const std::vector& tof_bin_edges, + const std::string& mode); +void timedSaveTOFImagingToTIFF( + const std::string& out_tof_imaging, + const std::vector>>& tof_images, + const std::vector& tof_bin_edges, + const std::string& tof_filename_base); + +} // namespace sophiread diff --git a/sophiread/SophireadCLI/src/sophiread.cpp b/sophiread/SophireadCLI/src/sophiread.cpp index e2de665..4712a06 100644 --- a/sophiread/SophireadCLI/src/sophiread.cpp +++ b/sophiread/SophireadCLI/src/sophiread.cpp @@ -36,354 +36,355 @@ #include "tpx3_fast.h" #include "user_config.h" #include "json_config_parser.h" - -/** - * @brief Timed read raw data to char vector. - * - * @param[in] in_tpx3 - * @return std::vector - */ -std::vector timedReadDataToCharVec(const std::string &in_tpx3) { - auto start = std::chrono::high_resolution_clock::now(); - auto raw_data = readTPX3RawToCharVec(in_tpx3); - auto end = std::chrono::high_resolution_clock::now(); - auto elapsed = std::chrono::duration_cast(end - start).count(); - spdlog::info("Read raw data: {} s", elapsed / 1e6); - - return raw_data; -} - -/** - * @brief Timed find TPX3H. - * - * @param[in] rawdata - * @return std::vector - */ -std::vector timedFindTPX3H(const std::vector &rawdata) { - auto start = std::chrono::high_resolution_clock::now(); - auto batches = findTPX3H(rawdata); - auto end = std::chrono::high_resolution_clock::now(); - auto elapsed = std::chrono::duration_cast(end - start).count(); - spdlog::info("Locate all headers: {} s", elapsed / 1e6); - - return batches; -} - -/** - * @brief Timed locate timestamp. - * - * @param[in, out] batches - * @param[in] rawdata - */ -void timedLocateTimeStamp(std::vector &batches, const std::vector &rawdata) { - auto start = std::chrono::high_resolution_clock::now(); - unsigned long tdc_timestamp = 0; - unsigned long long gdc_timestamp = 0; - unsigned long timer_lsb32 = 0; - for (auto &tpx3 : batches) { - updateTimestamp(tpx3, rawdata, tdc_timestamp, gdc_timestamp, timer_lsb32); - } - auto end = std::chrono::high_resolution_clock::now(); - auto elapsed = std::chrono::duration_cast(end - start).count(); - spdlog::info("Locate all timestamps: {} s", elapsed / 1e6); -} - -/** - * @brief Timed hits extraction and clustering via multi-threading. - * - * @param[in, out] batches - * @param[in] rawdata - * @param[in] config - */ -void timedProcessing(std::vector &batches, const std::vector &raw_data, const IConfig &config) { - auto start = std::chrono::high_resolution_clock::now(); - tbb::parallel_for(tbb::blocked_range(0, batches.size()), [&](const tbb::blocked_range &r) { - // Define ABS algorithm with user-defined parameters for each thread - auto abs_alg_mt = - std::make_unique(config.getABSRadius(), config.getABSMinClusterSize(), config.getABSSpiderTimeRange()); - - for (size_t i = r.begin(); i != r.end(); ++i) { - auto &tpx3 = batches[i]; - extractHits(tpx3, raw_data); - - abs_alg_mt->reset(); - abs_alg_mt->set_method("centroid"); - abs_alg_mt->fit(tpx3.hits); - - tpx3.neutrons = abs_alg_mt->get_events(tpx3.hits); - } - }); - auto end = std::chrono::high_resolution_clock::now(); - auto elapsed = std::chrono::duration_cast(end - start).count(); - spdlog::info("Process all hits -> neutrons: {} s", elapsed / 1e6); -} - -/** - * @brief Timed save hits to HDF5. - * - * @param[in] out_hits - * @param[in] hits - */ -void timedSaveHitsToHDF5(const std::string &out_hits, std::vector &batches) { - auto start = std::chrono::high_resolution_clock::now(); - // move all hits into a single vector - std::vector hits; - for (const auto &tpx3 : batches) { - auto tpx3_hits = tpx3.hits; - hits.insert(hits.end(), tpx3_hits.begin(), tpx3_hits.end()); - } - // save hits to HDF5 file - saveHitsToHDF5(out_hits, hits); - auto end = std::chrono::high_resolution_clock::now(); - auto elapsed = std::chrono::duration_cast(end - start).count(); - spdlog::info("Save hits to HDF5: {} s", elapsed / 1e6); -} - -/** - * @brief Timed save events to HDF5. - * - * @param[in] out_events - * @param[in] batches - */ -void timedSaveEventsToHDF5(const std::string &out_events, std::vector &batches) { - auto start = std::chrono::high_resolution_clock::now(); - // move all events into a single vector - std::vector events; - for (const auto &tpx3 : batches) { - auto tpx3_events = tpx3.neutrons; - events.insert(events.end(), tpx3_events.begin(), tpx3_events.end()); - } - // save events to HDF5 file - saveNeutronToHDF5(out_events, events); - auto end = std::chrono::high_resolution_clock::now(); - auto elapsed = std::chrono::duration_cast(end - start).count(); - spdlog::info("Save events to HDF5: {} s", elapsed / 1e6); -} - -/** - * @brief Timed create TOF images. - * - * This function creates time-of-flight (TOF) images based on the provided batches of TPX3 data. The TOF images are 2D histograms that represent the distribution of neutron events in space for different TOF bins. The function takes into account the super resolution, TOF bin edges, and the mode of operation (hit or neutron) to generate the TOF images. - * - * @param[in] batches The vector of TPX3 batches containing the neutron events. - * @param[in] super_resolution The super resolution factor used to calculate the dimensions of each 2D histogram. - * @param[in] tof_bin_edges The vector of TOF bin edges used to determine the TOF bin for each neutron event. - * @param[in] mode The mode of operation, either "hit" or "neutron", which determines the type of events to process. - * @return std::vector>> The vector of TOF images, where each TOF image is a 2D histogram representing the distribution of neutron events in space for a specific TOF bin. - */ -std::vector>> timedCreateTOFImages( - const std::vector& batches, - double super_resolution, - const std::vector& tof_bin_edges, - const std::string& mode) { +#include "sophiread_core.h" + +// /** +// * @brief Timed read raw data to char vector. +// * +// * @param[in] in_tpx3 +// * @return std::vector +// */ +// std::vector timedReadDataToCharVec(const std::string &in_tpx3) { +// auto start = std::chrono::high_resolution_clock::now(); +// auto raw_data = readTPX3RawToCharVec(in_tpx3); +// auto end = std::chrono::high_resolution_clock::now(); +// auto elapsed = std::chrono::duration_cast(end - start).count(); +// spdlog::info("Read raw data: {} s", elapsed / 1e6); + +// return raw_data; +// } + +// /** +// * @brief Timed find TPX3H. +// * +// * @param[in] rawdata +// * @return std::vector +// */ +// std::vector timedFindTPX3H(const std::vector &rawdata) { +// auto start = std::chrono::high_resolution_clock::now(); +// auto batches = findTPX3H(rawdata); +// auto end = std::chrono::high_resolution_clock::now(); +// auto elapsed = std::chrono::duration_cast(end - start).count(); +// spdlog::info("Locate all headers: {} s", elapsed / 1e6); + +// return batches; +// } + +// /** +// * @brief Timed locate timestamp. +// * +// * @param[in, out] batches +// * @param[in] rawdata +// */ +// void timedLocateTimeStamp(std::vector &batches, const std::vector &rawdata) { +// auto start = std::chrono::high_resolution_clock::now(); +// unsigned long tdc_timestamp = 0; +// unsigned long long gdc_timestamp = 0; +// unsigned long timer_lsb32 = 0; +// for (auto &tpx3 : batches) { +// updateTimestamp(tpx3, rawdata, tdc_timestamp, gdc_timestamp, timer_lsb32); +// } +// auto end = std::chrono::high_resolution_clock::now(); +// auto elapsed = std::chrono::duration_cast(end - start).count(); +// spdlog::info("Locate all timestamps: {} s", elapsed / 1e6); +// } + +// /** +// * @brief Timed hits extraction and clustering via multi-threading. +// * +// * @param[in, out] batches +// * @param[in] rawdata +// * @param[in] config +// */ +// void timedProcessing(std::vector &batches, const std::vector &raw_data, const IConfig &config) { +// auto start = std::chrono::high_resolution_clock::now(); +// tbb::parallel_for(tbb::blocked_range(0, batches.size()), [&](const tbb::blocked_range &r) { +// // Define ABS algorithm with user-defined parameters for each thread +// auto abs_alg_mt = +// std::make_unique(config.getABSRadius(), config.getABSMinClusterSize(), config.getABSSpiderTimeRange()); + +// for (size_t i = r.begin(); i != r.end(); ++i) { +// auto &tpx3 = batches[i]; +// extractHits(tpx3, raw_data); + +// abs_alg_mt->reset(); +// abs_alg_mt->set_method("centroid"); +// abs_alg_mt->fit(tpx3.hits); + +// tpx3.neutrons = abs_alg_mt->get_events(tpx3.hits); +// } +// }); +// auto end = std::chrono::high_resolution_clock::now(); +// auto elapsed = std::chrono::duration_cast(end - start).count(); +// spdlog::info("Process all hits -> neutrons: {} s", elapsed / 1e6); +// } + +// /** +// * @brief Timed save hits to HDF5. +// * +// * @param[in] out_hits +// * @param[in] hits +// */ +// void timedSaveHitsToHDF5(const std::string &out_hits, std::vector &batches) { +// auto start = std::chrono::high_resolution_clock::now(); +// // move all hits into a single vector +// std::vector hits; +// for (const auto &tpx3 : batches) { +// auto tpx3_hits = tpx3.hits; +// hits.insert(hits.end(), tpx3_hits.begin(), tpx3_hits.end()); +// } +// // save hits to HDF5 file +// saveHitsToHDF5(out_hits, hits); +// auto end = std::chrono::high_resolution_clock::now(); +// auto elapsed = std::chrono::duration_cast(end - start).count(); +// spdlog::info("Save hits to HDF5: {} s", elapsed / 1e6); +// } + +// /** +// * @brief Timed save events to HDF5. +// * +// * @param[in] out_events +// * @param[in] batches +// */ +// void timedSaveEventsToHDF5(const std::string &out_events, std::vector &batches) { +// auto start = std::chrono::high_resolution_clock::now(); +// // move all events into a single vector +// std::vector events; +// for (const auto &tpx3 : batches) { +// auto tpx3_events = tpx3.neutrons; +// events.insert(events.end(), tpx3_events.begin(), tpx3_events.end()); +// } +// // save events to HDF5 file +// saveNeutronToHDF5(out_events, events); +// auto end = std::chrono::high_resolution_clock::now(); +// auto elapsed = std::chrono::duration_cast(end - start).count(); +// spdlog::info("Save events to HDF5: {} s", elapsed / 1e6); +// } + +// /** +// * @brief Timed create TOF images. +// * +// * This function creates time-of-flight (TOF) images based on the provided batches of TPX3 data. The TOF images are 2D histograms that represent the distribution of neutron events in space for different TOF bins. The function takes into account the super resolution, TOF bin edges, and the mode of operation (hit or neutron) to generate the TOF images. +// * +// * @param[in] batches The vector of TPX3 batches containing the neutron events. +// * @param[in] super_resolution The super resolution factor used to calculate the dimensions of each 2D histogram. +// * @param[in] tof_bin_edges The vector of TOF bin edges used to determine the TOF bin for each neutron event. +// * @param[in] mode The mode of operation, either "hit" or "neutron", which determines the type of events to process. +// * @return std::vector>> The vector of TOF images, where each TOF image is a 2D histogram representing the distribution of neutron events in space for a specific TOF bin. +// */ +// std::vector>> timedCreateTOFImages( +// const std::vector& batches, +// double super_resolution, +// const std::vector& tof_bin_edges, +// const std::string& mode) { - auto start = std::chrono::high_resolution_clock::now(); - - // Initialize the TOF images container - std::vector>> tof_images(tof_bin_edges.size() - 1); - - // Sanity checks - if (tof_bin_edges.size() < 2) { - spdlog::error("Invalid TOF bin edges: at least 2 edges are required"); - return {}; - } - if (batches.empty()) { - spdlog::error("No batches to process"); - return tof_images; - } +// auto start = std::chrono::high_resolution_clock::now(); + +// // Initialize the TOF images container +// std::vector>> tof_images(tof_bin_edges.size() - 1); + +// // Sanity checks +// if (tof_bin_edges.size() < 2) { +// spdlog::error("Invalid TOF bin edges: at least 2 edges are required"); +// return {}; +// } +// if (batches.empty()) { +// spdlog::error("No batches to process"); +// return tof_images; +// } - // Calculate the dimensions of each 2D histogram based on super_resolution - // one chip: 0-255 pixel pos - // gap: 5 - // total: 0-255 + 5 + 0-255 -> 517 (TPX3@VENUS only) - int dim_x = static_cast(517 * super_resolution); - int dim_y = static_cast(517 * super_resolution); - - spdlog::debug("Creating TOF images with dimensions: {} x {}", dim_x, dim_y); - spdlog::debug("tof_bin_edges size: {}", tof_bin_edges.size()); - if (!tof_bin_edges.empty()) { - spdlog::debug("First bin edge: {}, Last bin edge: {}", tof_bin_edges.front(), tof_bin_edges.back()); - } +// // Calculate the dimensions of each 2D histogram based on super_resolution +// // one chip: 0-255 pixel pos +// // gap: 5 +// // total: 0-255 + 5 + 0-255 -> 517 (TPX3@VENUS only) +// int dim_x = static_cast(517 * super_resolution); +// int dim_y = static_cast(517 * super_resolution); + +// spdlog::debug("Creating TOF images with dimensions: {} x {}", dim_x, dim_y); +// spdlog::debug("tof_bin_edges size: {}", tof_bin_edges.size()); +// if (!tof_bin_edges.empty()) { +// spdlog::debug("First bin edge: {}, Last bin edge: {}", tof_bin_edges.front(), tof_bin_edges.back()); +// } - // Initialize each TOF bin's 2D histogram - for (auto& tof_image : tof_images) { - tof_image.resize(dim_y, std::vector(dim_x, 0)); - } - - // Process neutrons from all batches - size_t total_entries = 0; - size_t binned_entries = 0; - - for (size_t batch_index = 0; batch_index < batches.size(); ++batch_index) { - const auto& batch = batches[batch_index]; - spdlog::debug("Processing batch {}", batch_index); - - std::vector entries; - if (mode == "hit") { - entries.reserve(batch.hits.size()); - for (const auto& hit : batch.hits) { - entries.push_back(static_cast(&hit)); - } - } else { - entries.reserve(batch.neutrons.size()); - for (const auto& neutron : batch.neutrons) { - entries.push_back(static_cast(&neutron)); - } - } +// // Initialize each TOF bin's 2D histogram +// for (auto& tof_image : tof_images) { +// tof_image.resize(dim_y, std::vector(dim_x, 0)); +// } + +// // Process neutrons from all batches +// size_t total_entries = 0; +// size_t binned_entries = 0; + +// for (size_t batch_index = 0; batch_index < batches.size(); ++batch_index) { +// const auto& batch = batches[batch_index]; +// spdlog::debug("Processing batch {}", batch_index); + +// std::vector entries; +// if (mode == "hit") { +// entries.reserve(batch.hits.size()); +// for (const auto& hit : batch.hits) { +// entries.push_back(static_cast(&hit)); +// } +// } else { +// entries.reserve(batch.neutrons.size()); +// for (const auto& neutron : batch.neutrons) { +// entries.push_back(static_cast(&neutron)); +// } +// } - if (entries.empty()) { - spdlog::debug("Batch {} is empty", batch_index); - continue; - } - - for (const auto& entry : entries) { - total_entries++; - double tof_ns = entry->iGetTOF_ns(); - - // Find the correct TOF bin - // NOTE: tof_bin_edges are in sec, and tof_ns are in nano secs - spdlog::debug("tof_ns: {}, tof_ns/1e9: {}", tof_ns, tof_ns/1e9); - auto it = std::lower_bound(tof_bin_edges.begin(), tof_bin_edges.end(), tof_ns/1e9); - if (it != tof_bin_edges.begin()) { - size_t bin_index = std::distance(tof_bin_edges.begin(), it) - 1; +// if (entries.empty()) { +// spdlog::debug("Batch {} is empty", batch_index); +// continue; +// } + +// for (const auto& entry : entries) { +// total_entries++; +// double tof_ns = entry->iGetTOF_ns(); + +// // Find the correct TOF bin +// // NOTE: tof_bin_edges are in sec, and tof_ns are in nano secs +// spdlog::debug("tof_ns: {}, tof_ns/1e9: {}", tof_ns, tof_ns/1e9); +// auto it = std::lower_bound(tof_bin_edges.begin(), tof_bin_edges.end(), tof_ns/1e9); +// if (it != tof_bin_edges.begin()) { +// size_t bin_index = std::distance(tof_bin_edges.begin(), it) - 1; - // Calculate the x and y indices in the 2D histogram - int x = static_cast(entry->iGetX() * super_resolution); - int y = static_cast(entry->iGetY() * super_resolution); +// // Calculate the x and y indices in the 2D histogram +// int x = static_cast(entry->iGetX() * super_resolution); +// int y = static_cast(entry->iGetY() * super_resolution); - // Ensure x and y are within bounds - if (x >= 0 && x < dim_x && y >= 0 && y < dim_y) { - // Increment the count in the appropriate bin and position - tof_images[bin_index][y][x]++; - binned_entries++; - } - } - } - } - - auto end = std::chrono::high_resolution_clock::now(); - auto elapsed = std::chrono::duration_cast(end - start).count(); - spdlog::info("TOF image creation time: {} s", elapsed / 1e6); - spdlog::info("Total entries: {}, Binned entries: {}", total_entries, binned_entries); - - return tof_images; -} - -/** - * @brief Timed save TOF imaging to TIFF. - * - * @param[in] out_tof_imaging - * @param[in] batches - * @param[in] tof_bin_edges - * @param[in] tof_filename_base - */ -void timedSaveTOFImagingToTIFF( - const std::string& out_tof_imaging, - const std::vector>>& tof_images, - const std::vector& tof_bin_edges, - const std::string& tof_filename_base) -{ - auto start = std::chrono::high_resolution_clock::now(); - - // 1. Create output directory if it doesn't exist - if (!std::filesystem::exists(out_tof_imaging)) { - std::filesystem::create_directories(out_tof_imaging); - spdlog::info("Created output directory: {}", out_tof_imaging); - } - - // 2. Initialize vector for spectral data - std::vector spectral_counts(tof_images.size(), 0); - - // 3. Iterate through each TOF bin and save TIFF files - tbb::parallel_for(tbb::blocked_range(0, tof_images.size()), [&](const tbb::blocked_range& range) { - for (size_t bin = range.begin(); bin < range.end(); ++bin) { - // Construct filename - std::string filename = fmt::format("{}/{}_bin_{:04d}.tiff", out_tof_imaging, tof_filename_base, bin + 1); - - // prepare container and fill with current hist2d - uint32_t width = tof_images[bin][0].size(); - uint32_t height = tof_images[bin].size(); - std::vector> accumulated_image = tof_images[bin]; - - // check if file already exist - if (std::filesystem::exists(filename)) { - TIFF* existing_tif = TIFFOpen(filename.c_str(), "r"); - if (existing_tif) { - uint32_t existing_width, existing_height; - TIFFGetField(existing_tif, TIFFTAG_IMAGEWIDTH, &existing_width); - TIFFGetField(existing_tif, TIFFTAG_IMAGELENGTH, &existing_height); - - if (existing_width == width && existing_height == height) { - // Dimensions match, proceed with accumulation - for (uint32_t row = 0; row < height; ++row) { - std::vector scanline(width); - TIFFReadScanline(existing_tif, scanline.data(), row); - for (uint32_t col = 0; col < width; ++col) { - accumulated_image[row][col] += scanline[col]; - } - } - spdlog::debug("Accumulated counts for existing file: {}", filename); - } else { - spdlog::error("Dimension mismatch for file: {}. Expected {}x{}, got {}x{}. Overwriting.", - filename, width, height, existing_width, existing_height); - } - TIFFClose(existing_tif); - } else { - spdlog::error("Failed to open existing TIFF file for reading: {}", filename); - } - } - - // Write or update TIFF file - TIFF* tif = TIFFOpen(filename.c_str(), "w"); - if (tif) { - uint32_t width = tof_images[bin][0].size(); - uint32_t height = tof_images[bin].size(); - - TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, width); - TIFFSetField(tif, TIFFTAG_IMAGELENGTH, height); - TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 1); - TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 32); - TIFFSetField(tif, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT); - TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG); - TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK); - - for (uint32_t row = 0; row < height; ++row) { - TIFFWriteScanline(tif, accumulated_image[row].data(), row); - } - - TIFFClose(tif); - spdlog::debug("Wrote TIFF file: {}", filename); - } else { - spdlog::error("Failed to open TIFF file for writing: {}", filename); - } - - // Accumulate counts for spectral file - spectral_counts[bin] = std::accumulate(accumulated_image.begin(), accumulated_image.end(), 0ULL, - [](unsigned long long sum, const std::vector& row) { - return sum + std::accumulate(row.begin(), row.end(), 0ULL); - }); - } - }); - - // 4. Write spectral file - std::string spectral_filename = fmt::format("{}/spectral.txt", out_tof_imaging); - // Write spectral data to file - std::ofstream spectral_file(spectral_filename); - if (spectral_file.is_open()) { - spectral_file << "Upper_Edge(s)\tCounts\n"; - for (size_t bin = 0; bin < tof_bin_edges.size() - 1; ++bin) { - spectral_file << tof_bin_edges[bin + 1] << "\t" << spectral_counts[bin] << "\n"; - } - spectral_file.close(); - spdlog::info("Wrote spectral file: {}", spectral_filename); - } else { - spdlog::error("Failed to open spectral file for writing: {}", spectral_filename); - } - - auto end = std::chrono::high_resolution_clock::now(); - auto duration = std::chrono::duration_cast(end - start); - spdlog::info("TIFF and spectral file writing completed in {} ms", duration.count()); -} +// // Ensure x and y are within bounds +// if (x >= 0 && x < dim_x && y >= 0 && y < dim_y) { +// // Increment the count in the appropriate bin and position +// tof_images[bin_index][y][x]++; +// binned_entries++; +// } +// } +// } +// } + +// auto end = std::chrono::high_resolution_clock::now(); +// auto elapsed = std::chrono::duration_cast(end - start).count(); +// spdlog::info("TOF image creation time: {} s", elapsed / 1e6); +// spdlog::info("Total entries: {}, Binned entries: {}", total_entries, binned_entries); + +// return tof_images; +// } + +// /** +// * @brief Timed save TOF imaging to TIFF. +// * +// * @param[in] out_tof_imaging +// * @param[in] batches +// * @param[in] tof_bin_edges +// * @param[in] tof_filename_base +// */ +// void timedSaveTOFImagingToTIFF( +// const std::string& out_tof_imaging, +// const std::vector>>& tof_images, +// const std::vector& tof_bin_edges, +// const std::string& tof_filename_base) +// { +// auto start = std::chrono::high_resolution_clock::now(); + +// // 1. Create output directory if it doesn't exist +// if (!std::filesystem::exists(out_tof_imaging)) { +// std::filesystem::create_directories(out_tof_imaging); +// spdlog::info("Created output directory: {}", out_tof_imaging); +// } + +// // 2. Initialize vector for spectral data +// std::vector spectral_counts(tof_images.size(), 0); + +// // 3. Iterate through each TOF bin and save TIFF files +// tbb::parallel_for(tbb::blocked_range(0, tof_images.size()), [&](const tbb::blocked_range& range) { +// for (size_t bin = range.begin(); bin < range.end(); ++bin) { +// // Construct filename +// std::string filename = fmt::format("{}/{}_bin_{:04d}.tiff", out_tof_imaging, tof_filename_base, bin + 1); + +// // prepare container and fill with current hist2d +// uint32_t width = tof_images[bin][0].size(); +// uint32_t height = tof_images[bin].size(); +// std::vector> accumulated_image = tof_images[bin]; + +// // check if file already exist +// if (std::filesystem::exists(filename)) { +// TIFF* existing_tif = TIFFOpen(filename.c_str(), "r"); +// if (existing_tif) { +// uint32_t existing_width, existing_height; +// TIFFGetField(existing_tif, TIFFTAG_IMAGEWIDTH, &existing_width); +// TIFFGetField(existing_tif, TIFFTAG_IMAGELENGTH, &existing_height); + +// if (existing_width == width && existing_height == height) { +// // Dimensions match, proceed with accumulation +// for (uint32_t row = 0; row < height; ++row) { +// std::vector scanline(width); +// TIFFReadScanline(existing_tif, scanline.data(), row); +// for (uint32_t col = 0; col < width; ++col) { +// accumulated_image[row][col] += scanline[col]; +// } +// } +// spdlog::debug("Accumulated counts for existing file: {}", filename); +// } else { +// spdlog::error("Dimension mismatch for file: {}. Expected {}x{}, got {}x{}. Overwriting.", +// filename, width, height, existing_width, existing_height); +// } +// TIFFClose(existing_tif); +// } else { +// spdlog::error("Failed to open existing TIFF file for reading: {}", filename); +// } +// } + +// // Write or update TIFF file +// TIFF* tif = TIFFOpen(filename.c_str(), "w"); +// if (tif) { +// uint32_t width = tof_images[bin][0].size(); +// uint32_t height = tof_images[bin].size(); + +// TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, width); +// TIFFSetField(tif, TIFFTAG_IMAGELENGTH, height); +// TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 1); +// TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 32); +// TIFFSetField(tif, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT); +// TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG); +// TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK); + +// for (uint32_t row = 0; row < height; ++row) { +// TIFFWriteScanline(tif, accumulated_image[row].data(), row); +// } + +// TIFFClose(tif); +// spdlog::debug("Wrote TIFF file: {}", filename); +// } else { +// spdlog::error("Failed to open TIFF file for writing: {}", filename); +// } + +// // Accumulate counts for spectral file +// spectral_counts[bin] = std::accumulate(accumulated_image.begin(), accumulated_image.end(), 0ULL, +// [](unsigned long long sum, const std::vector& row) { +// return sum + std::accumulate(row.begin(), row.end(), 0ULL); +// }); +// } +// }); + +// // 4. Write spectral file +// std::string spectral_filename = fmt::format("{}/spectral.txt", out_tof_imaging); +// // Write spectral data to file +// std::ofstream spectral_file(spectral_filename); +// if (spectral_file.is_open()) { +// spectral_file << "Upper_Edge(s)\tCounts\n"; +// for (size_t bin = 0; bin < tof_bin_edges.size() - 1; ++bin) { +// spectral_file << tof_bin_edges[bin + 1] << "\t" << spectral_counts[bin] << "\n"; +// } +// spectral_file.close(); +// spdlog::info("Wrote spectral file: {}", spectral_filename); +// } else { +// spdlog::error("Failed to open spectral file for writing: {}", spectral_filename); +// } + +// auto end = std::chrono::high_resolution_clock::now(); +// auto duration = std::chrono::duration_cast(end - start); +// spdlog::info("TIFF and spectral file writing completed in {} ms", duration.count()); +// } /** * @brief Main function. @@ -518,10 +519,10 @@ int main(int argc, char *argv[]) { // -------- // raw data --> hits --> neutrons auto start = std::chrono::high_resolution_clock::now(); - auto raw_data = timedReadDataToCharVec(in_tpx3); - auto batches = timedFindTPX3H(raw_data); - timedLocateTimeStamp(batches, raw_data); - timedProcessing(batches, raw_data, *config); + auto raw_data = sophiread::timedReadDataToCharVec(in_tpx3); + auto batches = sophiread::timedFindTPX3H(raw_data); + sophiread::timedLocateTimeStamp(batches, raw_data); + sophiread::timedProcessing(batches, raw_data, *config); auto end = std::chrono::high_resolution_clock::now(); auto elapsed = std::chrono::duration_cast(end - start).count(); spdlog::info("Total processing time: {} s", elapsed / 1e6); @@ -534,7 +535,7 @@ int main(int argc, char *argv[]) { std::vector>> tof_images; if (!out_tof_imaging.empty()) { spdlog::debug("start creating tof images"); - tof_images = timedCreateTOFImages(batches, config->getSuperResolution(), config->getTOFBinEdges(), tof_mode); + tof_images = sophiread::timedCreateTOFImages(batches, config->getSuperResolution(), config->getTOFBinEdges(), tof_mode); } // ------------- @@ -542,17 +543,17 @@ int main(int argc, char *argv[]) { // ------------- // save hits to HDF5 file if (!out_hits.empty()) { - timedSaveHitsToHDF5(out_hits, batches); + sophiread::timedSaveHitsToHDF5(out_hits, batches); } // save events to HDF5 file if (!out_events.empty()) { - timedSaveEventsToHDF5(out_events, batches); + sophiread::timedSaveEventsToHDF5(out_events, batches); } // Save TOF imaging to TIFF files if (!out_tof_imaging.empty()) { - timedSaveTOFImagingToTIFF(out_tof_imaging, tof_images, config->getTOFBinEdges(), tof_filename_base); + sophiread::timedSaveTOFImagingToTIFF(out_tof_imaging, tof_images, config->getTOFBinEdges(), tof_filename_base); } return 0; diff --git a/sophiread/SophireadCLI/src/sophiread_core.cpp b/sophiread/SophireadCLI/src/sophiread_core.cpp new file mode 100644 index 0000000..080cdde --- /dev/null +++ b/sophiread/SophireadCLI/src/sophiread_core.cpp @@ -0,0 +1,402 @@ +/** + * @file sophiread_core.cpp + * @author Chen Zhang (zhangc@orn.gov) + * @author Su-Ann Chong (chongs@ornl.gov) + * @brief CLI for reading Timepix3 raw data and parse it into neutron event + * files and a tiff image (for visual inspection). + * @date 2024-08-21 + * + * @copyright Copyright (c) 2024 + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include +#include +#include +#include +#include + +#include "disk_io.h" +#include "sophiread_core.h" + +namespace sophiread { + +/** + * @brief Timed read raw data to char vector. + * + * @param[in] in_tpx3 + * @return std::vector + */ +std::vector timedReadDataToCharVec(const std::string &in_tpx3) { + auto start = std::chrono::high_resolution_clock::now(); + auto raw_data = readTPX3RawToCharVec(in_tpx3); + auto end = std::chrono::high_resolution_clock::now(); + auto elapsed = std::chrono::duration_cast(end - start).count(); + spdlog::info("Read raw data: {} s", elapsed / 1e6); + + return raw_data; +} + +/** + * @brief Timed find TPX3H. + * + * @param[in] rawdata + * @return std::vector + */ +std::vector timedFindTPX3H(const std::vector &rawdata) { + auto start = std::chrono::high_resolution_clock::now(); + auto batches = findTPX3H(rawdata); + auto end = std::chrono::high_resolution_clock::now(); + auto elapsed = std::chrono::duration_cast(end - start).count(); + spdlog::info("Locate all headers: {} s", elapsed / 1e6); + + return batches; +} + +/** + * @brief Timed find TPX3H with iterator syntax + * + * @tparam ForwardIter + * @param begin + * @param end + * @param consumed + * @return std::vector + */ +template +std::vector timedFindTPX3H(ForwardIter begin, ForwardIter end, std::size_t& consumed) { + auto start = std::chrono::high_resolution_clock::now(); + auto batches = findTPX3H(begin, end, consumed); + auto end_time = std::chrono::high_resolution_clock::now(); + auto elapsed = std::chrono::duration_cast(end_time - start).count(); + spdlog::info("Locate all headers: {} s", elapsed / 1e6); + return batches; +} + +/** + * @brief Timed locate timestamp. + * + * @param[in, out] batches + * @param[in] rawdata + */ +void timedLocateTimeStamp(std::vector &batches, const std::vector &rawdata) { + auto start = std::chrono::high_resolution_clock::now(); + unsigned long tdc_timestamp = 0; + unsigned long long gdc_timestamp = 0; + unsigned long timer_lsb32 = 0; + for (auto &tpx3 : batches) { + updateTimestamp(tpx3, rawdata, tdc_timestamp, gdc_timestamp, timer_lsb32); + } + auto end = std::chrono::high_resolution_clock::now(); + auto elapsed = std::chrono::duration_cast(end - start).count(); + spdlog::info("Locate all timestamps: {} s", elapsed / 1e6); +} + +/** + * @brief Timed hits extraction and clustering via multi-threading. + * + * @param[in, out] batches + * @param[in] rawdata + * @param[in] config + */ +void timedProcessing(std::vector &batches, const std::vector &raw_data, const IConfig &config) { + auto start = std::chrono::high_resolution_clock::now(); + tbb::parallel_for(tbb::blocked_range(0, batches.size()), [&](const tbb::blocked_range &r) { + // Define ABS algorithm with user-defined parameters for each thread + auto abs_alg_mt = + std::make_unique(config.getABSRadius(), config.getABSMinClusterSize(), config.getABSSpiderTimeRange()); + + for (size_t i = r.begin(); i != r.end(); ++i) { + auto &tpx3 = batches[i]; + extractHits(tpx3, raw_data); + + abs_alg_mt->reset(); + abs_alg_mt->set_method("centroid"); + abs_alg_mt->fit(tpx3.hits); + + tpx3.neutrons = abs_alg_mt->get_events(tpx3.hits); + } + }); + auto end = std::chrono::high_resolution_clock::now(); + auto elapsed = std::chrono::duration_cast(end - start).count(); + spdlog::info("Process all hits -> neutrons: {} s", elapsed / 1e6); +} + +/** + * @brief Timed save hits to HDF5. + * + * @param[in] out_hits + * @param[in] hits + */ +void timedSaveHitsToHDF5(const std::string &out_hits, std::vector &batches) { + auto start = std::chrono::high_resolution_clock::now(); + // move all hits into a single vector + std::vector hits; + for (const auto &tpx3 : batches) { + auto tpx3_hits = tpx3.hits; + hits.insert(hits.end(), tpx3_hits.begin(), tpx3_hits.end()); + } + // save hits to HDF5 file + saveHitsToHDF5(out_hits, hits); + auto end = std::chrono::high_resolution_clock::now(); + auto elapsed = std::chrono::duration_cast(end - start).count(); + spdlog::info("Save hits to HDF5: {} s", elapsed / 1e6); +} + +/** + * @brief Timed save events to HDF5. + * + * @param[in] out_events + * @param[in] batches + */ +void timedSaveEventsToHDF5(const std::string &out_events, std::vector &batches) { + auto start = std::chrono::high_resolution_clock::now(); + // move all events into a single vector + std::vector events; + for (const auto &tpx3 : batches) { + auto tpx3_events = tpx3.neutrons; + events.insert(events.end(), tpx3_events.begin(), tpx3_events.end()); + } + // save events to HDF5 file + saveNeutronToHDF5(out_events, events); + auto end = std::chrono::high_resolution_clock::now(); + auto elapsed = std::chrono::duration_cast(end - start).count(); + spdlog::info("Save events to HDF5: {} s", elapsed / 1e6); +} + +/** + * @brief Timed create TOF images. + * + * This function creates time-of-flight (TOF) images based on the provided batches of TPX3 data. The TOF images are 2D histograms that represent the distribution of neutron events in space for different TOF bins. The function takes into account the super resolution, TOF bin edges, and the mode of operation (hit or neutron) to generate the TOF images. + * + * @param[in] batches The vector of TPX3 batches containing the neutron events. + * @param[in] super_resolution The super resolution factor used to calculate the dimensions of each 2D histogram. + * @param[in] tof_bin_edges The vector of TOF bin edges used to determine the TOF bin for each neutron event. + * @param[in] mode The mode of operation, either "hit" or "neutron", which determines the type of events to process. + * @return std::vector>> The vector of TOF images, where each TOF image is a 2D histogram representing the distribution of neutron events in space for a specific TOF bin. + */ +std::vector>> timedCreateTOFImages( + const std::vector& batches, + double super_resolution, + const std::vector& tof_bin_edges, + const std::string& mode) { + + auto start = std::chrono::high_resolution_clock::now(); + + // Initialize the TOF images container + std::vector>> tof_images(tof_bin_edges.size() - 1); + + // Sanity checks + if (tof_bin_edges.size() < 2) { + spdlog::error("Invalid TOF bin edges: at least 2 edges are required"); + return {}; + } + if (batches.empty()) { + spdlog::error("No batches to process"); + return tof_images; + } + + // Calculate the dimensions of each 2D histogram based on super_resolution + // one chip: 0-255 pixel pos + // gap: 5 + // total: 0-255 + 5 + 0-255 -> 517 (TPX3@VENUS only) + int dim_x = static_cast(517 * super_resolution); + int dim_y = static_cast(517 * super_resolution); + + spdlog::debug("Creating TOF images with dimensions: {} x {}", dim_x, dim_y); + spdlog::debug("tof_bin_edges size: {}", tof_bin_edges.size()); + if (!tof_bin_edges.empty()) { + spdlog::debug("First bin edge: {}, Last bin edge: {}", tof_bin_edges.front(), tof_bin_edges.back()); + } + + // Initialize each TOF bin's 2D histogram + for (auto& tof_image : tof_images) { + tof_image.resize(dim_y, std::vector(dim_x, 0)); + } + + // Process neutrons from all batches + size_t total_entries = 0; + size_t binned_entries = 0; + + for (size_t batch_index = 0; batch_index < batches.size(); ++batch_index) { + const auto& batch = batches[batch_index]; + spdlog::debug("Processing batch {}", batch_index); + + std::vector entries; + if (mode == "hit") { + entries.reserve(batch.hits.size()); + for (const auto& hit : batch.hits) { + entries.push_back(static_cast(&hit)); + } + } else { + entries.reserve(batch.neutrons.size()); + for (const auto& neutron : batch.neutrons) { + entries.push_back(static_cast(&neutron)); + } + } + + if (entries.empty()) { + spdlog::debug("Batch {} is empty", batch_index); + continue; + } + + for (const auto& entry : entries) { + total_entries++; + double tof_ns = entry->iGetTOF_ns(); + + // Find the correct TOF bin + // NOTE: tof_bin_edges are in sec, and tof_ns are in nano secs + spdlog::debug("tof_ns: {}, tof_ns/1e9: {}", tof_ns, tof_ns/1e9); + auto it = std::lower_bound(tof_bin_edges.begin(), tof_bin_edges.end(), tof_ns/1e9); + if (it != tof_bin_edges.begin()) { + size_t bin_index = std::distance(tof_bin_edges.begin(), it) - 1; + + // Calculate the x and y indices in the 2D histogram + int x = static_cast(entry->iGetX() * super_resolution); + int y = static_cast(entry->iGetY() * super_resolution); + + // Ensure x and y are within bounds + if (x >= 0 && x < dim_x && y >= 0 && y < dim_y) { + // Increment the count in the appropriate bin and position + tof_images[bin_index][y][x]++; + binned_entries++; + } + } + } + } + + auto end = std::chrono::high_resolution_clock::now(); + auto elapsed = std::chrono::duration_cast(end - start).count(); + spdlog::info("TOF image creation time: {} s", elapsed / 1e6); + spdlog::info("Total entries: {}, Binned entries: {}", total_entries, binned_entries); + + return tof_images; +} + +/** + * @brief Timed save TOF imaging to TIFF. + * + * @param[in] out_tof_imaging + * @param[in] batches + * @param[in] tof_bin_edges + * @param[in] tof_filename_base + */ +void timedSaveTOFImagingToTIFF( + const std::string& out_tof_imaging, + const std::vector>>& tof_images, + const std::vector& tof_bin_edges, + const std::string& tof_filename_base) +{ + auto start = std::chrono::high_resolution_clock::now(); + + // 1. Create output directory if it doesn't exist + if (!std::filesystem::exists(out_tof_imaging)) { + std::filesystem::create_directories(out_tof_imaging); + spdlog::info("Created output directory: {}", out_tof_imaging); + } + + // 2. Initialize vector for spectral data + std::vector spectral_counts(tof_images.size(), 0); + + // 3. Iterate through each TOF bin and save TIFF files + tbb::parallel_for(tbb::blocked_range(0, tof_images.size()), [&](const tbb::blocked_range& range) { + for (size_t bin = range.begin(); bin < range.end(); ++bin) { + // Construct filename + std::string filename = fmt::format("{}/{}_bin_{:04d}.tiff", out_tof_imaging, tof_filename_base, bin + 1); + + // prepare container and fill with current hist2d + uint32_t width = tof_images[bin][0].size(); + uint32_t height = tof_images[bin].size(); + std::vector> accumulated_image = tof_images[bin]; + + // check if file already exist + if (std::filesystem::exists(filename)) { + TIFF* existing_tif = TIFFOpen(filename.c_str(), "r"); + if (existing_tif) { + uint32_t existing_width, existing_height; + TIFFGetField(existing_tif, TIFFTAG_IMAGEWIDTH, &existing_width); + TIFFGetField(existing_tif, TIFFTAG_IMAGELENGTH, &existing_height); + + if (existing_width == width && existing_height == height) { + // Dimensions match, proceed with accumulation + for (uint32_t row = 0; row < height; ++row) { + std::vector scanline(width); + TIFFReadScanline(existing_tif, scanline.data(), row); + for (uint32_t col = 0; col < width; ++col) { + accumulated_image[row][col] += scanline[col]; + } + } + spdlog::debug("Accumulated counts for existing file: {}", filename); + } else { + spdlog::error("Dimension mismatch for file: {}. Expected {}x{}, got {}x{}. Overwriting.", + filename, width, height, existing_width, existing_height); + } + TIFFClose(existing_tif); + } else { + spdlog::error("Failed to open existing TIFF file for reading: {}", filename); + } + } + + // Write or update TIFF file + TIFF* tif = TIFFOpen(filename.c_str(), "w"); + if (tif) { + uint32_t width = tof_images[bin][0].size(); + uint32_t height = tof_images[bin].size(); + + TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, width); + TIFFSetField(tif, TIFFTAG_IMAGELENGTH, height); + TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 1); + TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 32); + TIFFSetField(tif, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT); + TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG); + TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK); + + for (uint32_t row = 0; row < height; ++row) { + TIFFWriteScanline(tif, accumulated_image[row].data(), row); + } + + TIFFClose(tif); + spdlog::debug("Wrote TIFF file: {}", filename); + } else { + spdlog::error("Failed to open TIFF file for writing: {}", filename); + } + + // Accumulate counts for spectral file + spectral_counts[bin] = std::accumulate(accumulated_image.begin(), accumulated_image.end(), 0ULL, + [](unsigned long long sum, const std::vector& row) { + return sum + std::accumulate(row.begin(), row.end(), 0ULL); + }); + } + }); + + // 4. Write spectral file + std::string spectral_filename = fmt::format("{}/spectral.txt", out_tof_imaging); + // Write spectral data to file + std::ofstream spectral_file(spectral_filename); + if (spectral_file.is_open()) { + spectral_file << "Upper_Edge(s)\tCounts\n"; + for (size_t bin = 0; bin < tof_bin_edges.size() - 1; ++bin) { + spectral_file << tof_bin_edges[bin + 1] << "\t" << spectral_counts[bin] << "\n"; + } + spectral_file.close(); + spdlog::info("Wrote spectral file: {}", spectral_filename); + } else { + spdlog::error("Failed to open spectral file for writing: {}", spectral_filename); + } + + auto end = std::chrono::high_resolution_clock::now(); + auto duration = std::chrono::duration_cast(end - start); + spdlog::info("TIFF and spectral file writing completed in {} ms", duration.count()); +} + +} // namespace sophiread \ No newline at end of file From 6efe643f03e40af899d89cd9ef616bf06e3a32d7 Mon Sep 17 00:00:00 2001 From: Chen Zhang Date: Wed, 21 Aug 2024 12:08:03 -0400 Subject: [PATCH 20/38] add unit test --- sophiread/SophireadCLI/CMakeLists.txt | 36 ++--- .../SophireadCLI/include/sophiread_core.h | 3 - sophiread/SophireadCLI/src/sophiread_core.cpp | 20 --- .../tests/test_sophiread_core.cpp | 151 ++++++++++++++++++ 4 files changed, 169 insertions(+), 41 deletions(-) create mode 100644 sophiread/SophireadCLI/tests/test_sophiread_core.cpp diff --git a/sophiread/SophireadCLI/CMakeLists.txt b/sophiread/SophireadCLI/CMakeLists.txt index a8d8029..854bfbd 100644 --- a/sophiread/SophireadCLI/CMakeLists.txt +++ b/sophiread/SophireadCLI/CMakeLists.txt @@ -60,24 +60,24 @@ target_link_libraries(JsonConfigParserTest ) gtest_discover_tests(JsonConfigParserTest) # core test -# add_executable( -# SophireadCoreTest -# tests/test_sophiread_core.cpp -# src/sophiread_core.cpp -# src/json_config_parser.cpp -# ) -# target_link_libraries(SophireadCoreTest -# PRIVATE -# FastSophiread -# spdlog::spdlog -# GTest::GTest -# GTest::Main -# nlohmann_json::nlohmann_json -# ${HDF5_LIBRARIES} -# ${TIFF_LIBRARIES} -# TBB::tbb -# ) -# gtest_discover_tests(SophireadCoreTest) +add_executable( + SophireadCoreTest + tests/test_sophiread_core.cpp + src/sophiread_core.cpp + src/json_config_parser.cpp +) +target_link_libraries(SophireadCoreTest + PRIVATE + FastSophiread + spdlog::spdlog + GTest::GTest + GTest::Main + nlohmann_json::nlohmann_json + ${HDF5_LIBRARIES} + ${TIFF_LIBRARIES} + TBB::tbb +) +gtest_discover_tests(SophireadCoreTest) # ----------------- SYMLINK ----------------- # # symlink the executable to the build directory diff --git a/sophiread/SophireadCLI/include/sophiread_core.h b/sophiread/SophireadCLI/include/sophiread_core.h index 97983e7..b90c991 100644 --- a/sophiread/SophireadCLI/include/sophiread_core.h +++ b/sophiread/SophireadCLI/include/sophiread_core.h @@ -31,8 +31,6 @@ namespace sophiread { std::vector timedReadDataToCharVec(const std::string& in_tpx3); std::vector timedFindTPX3H(const std::vector& rawdata); -template - std::vector timedFindTPX3H(ForwardIter begin, ForwardIter end, std::size_t& consumed); void timedLocateTimeStamp(std::vector& batches, const std::vector& rawdata); void timedProcessing(std::vector& batches, const std::vector& raw_data, const IConfig& config); void timedSaveHitsToHDF5(const std::string& out_hits, std::vector& batches); @@ -47,5 +45,4 @@ void timedSaveTOFImagingToTIFF( const std::vector>>& tof_images, const std::vector& tof_bin_edges, const std::string& tof_filename_base); - } // namespace sophiread diff --git a/sophiread/SophireadCLI/src/sophiread_core.cpp b/sophiread/SophireadCLI/src/sophiread_core.cpp index 080cdde..9758fbf 100644 --- a/sophiread/SophireadCLI/src/sophiread_core.cpp +++ b/sophiread/SophireadCLI/src/sophiread_core.cpp @@ -26,7 +26,6 @@ #include #include #include - #include "disk_io.h" #include "sophiread_core.h" @@ -64,25 +63,6 @@ std::vector timedFindTPX3H(const std::vector &rawdata) { return batches; } -/** - * @brief Timed find TPX3H with iterator syntax - * - * @tparam ForwardIter - * @param begin - * @param end - * @param consumed - * @return std::vector - */ -template -std::vector timedFindTPX3H(ForwardIter begin, ForwardIter end, std::size_t& consumed) { - auto start = std::chrono::high_resolution_clock::now(); - auto batches = findTPX3H(begin, end, consumed); - auto end_time = std::chrono::high_resolution_clock::now(); - auto elapsed = std::chrono::duration_cast(end_time - start).count(); - spdlog::info("Locate all headers: {} s", elapsed / 1e6); - return batches; -} - /** * @brief Timed locate timestamp. * diff --git a/sophiread/SophireadCLI/tests/test_sophiread_core.cpp b/sophiread/SophireadCLI/tests/test_sophiread_core.cpp new file mode 100644 index 0000000..8344d9c --- /dev/null +++ b/sophiread/SophireadCLI/tests/test_sophiread_core.cpp @@ -0,0 +1,151 @@ +/** + * @file: test_sophiread_core.cpp + * @author: Chen Zhang (zhangc@orn.gov) + * @brief: Unit tests for the Sophiread Core module. + * @date: 2024-08-21 + * + * @copyright Copyright (c) 2024 + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include +#include +#include "sophiread_core.h" +#include "json_config_parser.h" + +class SophireadCoreTest : public ::testing::Test { +protected: + std::vector generateMockTPX3Data(int num_packets = 10) { + std::vector data; + + for (int i = 0; i < num_packets; ++i) { + // Header packet + data.push_back('T'); + data.push_back('P'); + data.push_back('X'); + data.push_back('3'); + data.push_back(0); // chip_layout_type + data.push_back(0); // some random data + data.push_back(8); // data_packet_size (low byte) + data.push_back(0); // data_packet_size (high byte) + + // Data packet (8 bytes) + for (int j = 0; j < 8; ++j) { + data.push_back(0); + } + } + return data; + } + + std::vector generateMockTPX3Batches(int num_batches = 2, int hits_per_batch = 5) { + std::vector batches; + for (int i = 0; i < num_batches; ++i) { + TPX3 batch(i * 100, hits_per_batch, i % 3); // index, num_packets, chip_layout_type + + // Add mock hits + for (int j = 0; j < hits_per_batch; ++j) { + char mock_packet[8] = {0}; // Mock packet data + batch.emplace_back(mock_packet, 1000 + j, 2000 + j); + } + + // Add mock neutrons (derived from hits) + for (const auto& hit : batch.hits) { + batch.neutrons.emplace_back( + hit.getX(), hit.getY(), + hit.getTOF(), hit.getTOT(), + 1 // nHits, assume 1 hit per neutron for simplicity + ); + } + + batches.push_back(std::move(batch)); + } + return batches; + } + + void SetUp() override { + // Create a small test TPX3 file + auto test_data = generateMockTPX3Data(100); + std::ofstream test_file("test.tpx3", std::ios::binary); + test_file.write(test_data.data(), test_data.size()); + test_file.close(); + } + + void TearDown() override { + // Remove the test file + std::filesystem::remove("test.tpx3"); + } +}; + +TEST_F(SophireadCoreTest, TimedReadDataToCharVec) { + auto data = sophiread::timedReadDataToCharVec("test.tpx3"); + EXPECT_EQ(data.size(), 1600); // 100 * (8 + 8) bytes +} + +TEST_F(SophireadCoreTest, TimedFindTPX3H) { + auto rawdata = generateMockTPX3Data(100); + auto batches = sophiread::timedFindTPX3H(rawdata); + EXPECT_EQ(batches.size(), 100); +} + +TEST_F(SophireadCoreTest, TimedLocateTimeStamp) { + std::vector raw_data(8000, 'T'); // Simulating TPX3 data + std::vector batches = { TPX3(0, 10, 0) }; // Create a dummy TPX3 batch + sophiread::timedLocateTimeStamp(batches, raw_data); + // Add assertions based on expected behavior +} + +TEST_F(SophireadCoreTest, TimedProcessing) { + std::vector raw_data(8000, 'T'); // Simulating TPX3 data + std::vector batches = { TPX3(0, 10, 0) }; // Create a dummy TPX3 batch + JSONConfigParser config = JSONConfigParser::createDefault(); + sophiread::timedProcessing(batches, raw_data, config); + // Add assertions based on expected behavior +} + +TEST_F(SophireadCoreTest, TimedSaveHitsToHDF5) { + std::vector batches = generateMockTPX3Batches(2, 5); // Create a dummy TPX3 batch + sophiread::timedSaveHitsToHDF5("test_hits.h5", batches); + EXPECT_TRUE(std::filesystem::exists("test_hits.h5")); + std::filesystem::remove("test_hits.h5"); +} + +TEST_F(SophireadCoreTest, TimedSaveEventsToHDF5) { + std::vector batches = generateMockTPX3Batches(2, 5); // Create a dummy TPX3 batch + sophiread::timedSaveEventsToHDF5("test_events.h5", batches); + EXPECT_TRUE(std::filesystem::exists("test_events.h5")); + std::filesystem::remove("test_events.h5"); +} + +TEST_F(SophireadCoreTest, TimedCreateTOFImages) { + std::vector batches = generateMockTPX3Batches(2, 5); // Create a dummy TPX3 batch + std::vector tof_bin_edges = {0.0, 0.1, 0.2, 0.3}; + auto images = sophiread::timedCreateTOFImages(batches, 1.0, tof_bin_edges, "neutron"); + EXPECT_EQ(images.size(), 3); // 3 bins +} + +TEST_F(SophireadCoreTest, TimedSaveTOFImagingToTIFF) { + std::vector>> tof_images(3, std::vector>(10, std::vector(10, 1))); + std::vector tof_bin_edges = {0.0, 0.1, 0.2, 0.3}; + sophiread::timedSaveTOFImagingToTIFF("test_tof", tof_images, tof_bin_edges, "test"); + EXPECT_TRUE(std::filesystem::exists("test_tof/test_bin_0001.tiff")); + EXPECT_TRUE(std::filesystem::exists("test_tof/test_bin_0002.tiff")); + EXPECT_TRUE(std::filesystem::exists("test_tof/test_bin_0003.tiff")); + EXPECT_TRUE(std::filesystem::exists("test_tof/spectral.txt")); + std::filesystem::remove_all("test_tof"); +} + +int main(int argc, char **argv) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} \ No newline at end of file From b9ef9e2eb0bb821d5a733c25d2e3f0fbdf7c3f3c Mon Sep 17 00:00:00 2001 From: Chen Zhang Date: Wed, 21 Aug 2024 12:52:32 -0400 Subject: [PATCH 21/38] remove outdated code --- sophiread/SophireadCLI/src/sophiread.cpp | 348 ----------------------- 1 file changed, 348 deletions(-) diff --git a/sophiread/SophireadCLI/src/sophiread.cpp b/sophiread/SophireadCLI/src/sophiread.cpp index 4712a06..7aaa542 100644 --- a/sophiread/SophireadCLI/src/sophiread.cpp +++ b/sophiread/SophireadCLI/src/sophiread.cpp @@ -38,354 +38,6 @@ #include "json_config_parser.h" #include "sophiread_core.h" -// /** -// * @brief Timed read raw data to char vector. -// * -// * @param[in] in_tpx3 -// * @return std::vector -// */ -// std::vector timedReadDataToCharVec(const std::string &in_tpx3) { -// auto start = std::chrono::high_resolution_clock::now(); -// auto raw_data = readTPX3RawToCharVec(in_tpx3); -// auto end = std::chrono::high_resolution_clock::now(); -// auto elapsed = std::chrono::duration_cast(end - start).count(); -// spdlog::info("Read raw data: {} s", elapsed / 1e6); - -// return raw_data; -// } - -// /** -// * @brief Timed find TPX3H. -// * -// * @param[in] rawdata -// * @return std::vector -// */ -// std::vector timedFindTPX3H(const std::vector &rawdata) { -// auto start = std::chrono::high_resolution_clock::now(); -// auto batches = findTPX3H(rawdata); -// auto end = std::chrono::high_resolution_clock::now(); -// auto elapsed = std::chrono::duration_cast(end - start).count(); -// spdlog::info("Locate all headers: {} s", elapsed / 1e6); - -// return batches; -// } - -// /** -// * @brief Timed locate timestamp. -// * -// * @param[in, out] batches -// * @param[in] rawdata -// */ -// void timedLocateTimeStamp(std::vector &batches, const std::vector &rawdata) { -// auto start = std::chrono::high_resolution_clock::now(); -// unsigned long tdc_timestamp = 0; -// unsigned long long gdc_timestamp = 0; -// unsigned long timer_lsb32 = 0; -// for (auto &tpx3 : batches) { -// updateTimestamp(tpx3, rawdata, tdc_timestamp, gdc_timestamp, timer_lsb32); -// } -// auto end = std::chrono::high_resolution_clock::now(); -// auto elapsed = std::chrono::duration_cast(end - start).count(); -// spdlog::info("Locate all timestamps: {} s", elapsed / 1e6); -// } - -// /** -// * @brief Timed hits extraction and clustering via multi-threading. -// * -// * @param[in, out] batches -// * @param[in] rawdata -// * @param[in] config -// */ -// void timedProcessing(std::vector &batches, const std::vector &raw_data, const IConfig &config) { -// auto start = std::chrono::high_resolution_clock::now(); -// tbb::parallel_for(tbb::blocked_range(0, batches.size()), [&](const tbb::blocked_range &r) { -// // Define ABS algorithm with user-defined parameters for each thread -// auto abs_alg_mt = -// std::make_unique(config.getABSRadius(), config.getABSMinClusterSize(), config.getABSSpiderTimeRange()); - -// for (size_t i = r.begin(); i != r.end(); ++i) { -// auto &tpx3 = batches[i]; -// extractHits(tpx3, raw_data); - -// abs_alg_mt->reset(); -// abs_alg_mt->set_method("centroid"); -// abs_alg_mt->fit(tpx3.hits); - -// tpx3.neutrons = abs_alg_mt->get_events(tpx3.hits); -// } -// }); -// auto end = std::chrono::high_resolution_clock::now(); -// auto elapsed = std::chrono::duration_cast(end - start).count(); -// spdlog::info("Process all hits -> neutrons: {} s", elapsed / 1e6); -// } - -// /** -// * @brief Timed save hits to HDF5. -// * -// * @param[in] out_hits -// * @param[in] hits -// */ -// void timedSaveHitsToHDF5(const std::string &out_hits, std::vector &batches) { -// auto start = std::chrono::high_resolution_clock::now(); -// // move all hits into a single vector -// std::vector hits; -// for (const auto &tpx3 : batches) { -// auto tpx3_hits = tpx3.hits; -// hits.insert(hits.end(), tpx3_hits.begin(), tpx3_hits.end()); -// } -// // save hits to HDF5 file -// saveHitsToHDF5(out_hits, hits); -// auto end = std::chrono::high_resolution_clock::now(); -// auto elapsed = std::chrono::duration_cast(end - start).count(); -// spdlog::info("Save hits to HDF5: {} s", elapsed / 1e6); -// } - -// /** -// * @brief Timed save events to HDF5. -// * -// * @param[in] out_events -// * @param[in] batches -// */ -// void timedSaveEventsToHDF5(const std::string &out_events, std::vector &batches) { -// auto start = std::chrono::high_resolution_clock::now(); -// // move all events into a single vector -// std::vector events; -// for (const auto &tpx3 : batches) { -// auto tpx3_events = tpx3.neutrons; -// events.insert(events.end(), tpx3_events.begin(), tpx3_events.end()); -// } -// // save events to HDF5 file -// saveNeutronToHDF5(out_events, events); -// auto end = std::chrono::high_resolution_clock::now(); -// auto elapsed = std::chrono::duration_cast(end - start).count(); -// spdlog::info("Save events to HDF5: {} s", elapsed / 1e6); -// } - -// /** -// * @brief Timed create TOF images. -// * -// * This function creates time-of-flight (TOF) images based on the provided batches of TPX3 data. The TOF images are 2D histograms that represent the distribution of neutron events in space for different TOF bins. The function takes into account the super resolution, TOF bin edges, and the mode of operation (hit or neutron) to generate the TOF images. -// * -// * @param[in] batches The vector of TPX3 batches containing the neutron events. -// * @param[in] super_resolution The super resolution factor used to calculate the dimensions of each 2D histogram. -// * @param[in] tof_bin_edges The vector of TOF bin edges used to determine the TOF bin for each neutron event. -// * @param[in] mode The mode of operation, either "hit" or "neutron", which determines the type of events to process. -// * @return std::vector>> The vector of TOF images, where each TOF image is a 2D histogram representing the distribution of neutron events in space for a specific TOF bin. -// */ -// std::vector>> timedCreateTOFImages( -// const std::vector& batches, -// double super_resolution, -// const std::vector& tof_bin_edges, -// const std::string& mode) { - -// auto start = std::chrono::high_resolution_clock::now(); - -// // Initialize the TOF images container -// std::vector>> tof_images(tof_bin_edges.size() - 1); - -// // Sanity checks -// if (tof_bin_edges.size() < 2) { -// spdlog::error("Invalid TOF bin edges: at least 2 edges are required"); -// return {}; -// } -// if (batches.empty()) { -// spdlog::error("No batches to process"); -// return tof_images; -// } - -// // Calculate the dimensions of each 2D histogram based on super_resolution -// // one chip: 0-255 pixel pos -// // gap: 5 -// // total: 0-255 + 5 + 0-255 -> 517 (TPX3@VENUS only) -// int dim_x = static_cast(517 * super_resolution); -// int dim_y = static_cast(517 * super_resolution); - -// spdlog::debug("Creating TOF images with dimensions: {} x {}", dim_x, dim_y); -// spdlog::debug("tof_bin_edges size: {}", tof_bin_edges.size()); -// if (!tof_bin_edges.empty()) { -// spdlog::debug("First bin edge: {}, Last bin edge: {}", tof_bin_edges.front(), tof_bin_edges.back()); -// } - -// // Initialize each TOF bin's 2D histogram -// for (auto& tof_image : tof_images) { -// tof_image.resize(dim_y, std::vector(dim_x, 0)); -// } - -// // Process neutrons from all batches -// size_t total_entries = 0; -// size_t binned_entries = 0; - -// for (size_t batch_index = 0; batch_index < batches.size(); ++batch_index) { -// const auto& batch = batches[batch_index]; -// spdlog::debug("Processing batch {}", batch_index); - -// std::vector entries; -// if (mode == "hit") { -// entries.reserve(batch.hits.size()); -// for (const auto& hit : batch.hits) { -// entries.push_back(static_cast(&hit)); -// } -// } else { -// entries.reserve(batch.neutrons.size()); -// for (const auto& neutron : batch.neutrons) { -// entries.push_back(static_cast(&neutron)); -// } -// } - -// if (entries.empty()) { -// spdlog::debug("Batch {} is empty", batch_index); -// continue; -// } - -// for (const auto& entry : entries) { -// total_entries++; -// double tof_ns = entry->iGetTOF_ns(); - -// // Find the correct TOF bin -// // NOTE: tof_bin_edges are in sec, and tof_ns are in nano secs -// spdlog::debug("tof_ns: {}, tof_ns/1e9: {}", tof_ns, tof_ns/1e9); -// auto it = std::lower_bound(tof_bin_edges.begin(), tof_bin_edges.end(), tof_ns/1e9); -// if (it != tof_bin_edges.begin()) { -// size_t bin_index = std::distance(tof_bin_edges.begin(), it) - 1; - -// // Calculate the x and y indices in the 2D histogram -// int x = static_cast(entry->iGetX() * super_resolution); -// int y = static_cast(entry->iGetY() * super_resolution); - -// // Ensure x and y are within bounds -// if (x >= 0 && x < dim_x && y >= 0 && y < dim_y) { -// // Increment the count in the appropriate bin and position -// tof_images[bin_index][y][x]++; -// binned_entries++; -// } -// } -// } -// } - -// auto end = std::chrono::high_resolution_clock::now(); -// auto elapsed = std::chrono::duration_cast(end - start).count(); -// spdlog::info("TOF image creation time: {} s", elapsed / 1e6); -// spdlog::info("Total entries: {}, Binned entries: {}", total_entries, binned_entries); - -// return tof_images; -// } - -// /** -// * @brief Timed save TOF imaging to TIFF. -// * -// * @param[in] out_tof_imaging -// * @param[in] batches -// * @param[in] tof_bin_edges -// * @param[in] tof_filename_base -// */ -// void timedSaveTOFImagingToTIFF( -// const std::string& out_tof_imaging, -// const std::vector>>& tof_images, -// const std::vector& tof_bin_edges, -// const std::string& tof_filename_base) -// { -// auto start = std::chrono::high_resolution_clock::now(); - -// // 1. Create output directory if it doesn't exist -// if (!std::filesystem::exists(out_tof_imaging)) { -// std::filesystem::create_directories(out_tof_imaging); -// spdlog::info("Created output directory: {}", out_tof_imaging); -// } - -// // 2. Initialize vector for spectral data -// std::vector spectral_counts(tof_images.size(), 0); - -// // 3. Iterate through each TOF bin and save TIFF files -// tbb::parallel_for(tbb::blocked_range(0, tof_images.size()), [&](const tbb::blocked_range& range) { -// for (size_t bin = range.begin(); bin < range.end(); ++bin) { -// // Construct filename -// std::string filename = fmt::format("{}/{}_bin_{:04d}.tiff", out_tof_imaging, tof_filename_base, bin + 1); - -// // prepare container and fill with current hist2d -// uint32_t width = tof_images[bin][0].size(); -// uint32_t height = tof_images[bin].size(); -// std::vector> accumulated_image = tof_images[bin]; - -// // check if file already exist -// if (std::filesystem::exists(filename)) { -// TIFF* existing_tif = TIFFOpen(filename.c_str(), "r"); -// if (existing_tif) { -// uint32_t existing_width, existing_height; -// TIFFGetField(existing_tif, TIFFTAG_IMAGEWIDTH, &existing_width); -// TIFFGetField(existing_tif, TIFFTAG_IMAGELENGTH, &existing_height); - -// if (existing_width == width && existing_height == height) { -// // Dimensions match, proceed with accumulation -// for (uint32_t row = 0; row < height; ++row) { -// std::vector scanline(width); -// TIFFReadScanline(existing_tif, scanline.data(), row); -// for (uint32_t col = 0; col < width; ++col) { -// accumulated_image[row][col] += scanline[col]; -// } -// } -// spdlog::debug("Accumulated counts for existing file: {}", filename); -// } else { -// spdlog::error("Dimension mismatch for file: {}. Expected {}x{}, got {}x{}. Overwriting.", -// filename, width, height, existing_width, existing_height); -// } -// TIFFClose(existing_tif); -// } else { -// spdlog::error("Failed to open existing TIFF file for reading: {}", filename); -// } -// } - -// // Write or update TIFF file -// TIFF* tif = TIFFOpen(filename.c_str(), "w"); -// if (tif) { -// uint32_t width = tof_images[bin][0].size(); -// uint32_t height = tof_images[bin].size(); - -// TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, width); -// TIFFSetField(tif, TIFFTAG_IMAGELENGTH, height); -// TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 1); -// TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 32); -// TIFFSetField(tif, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT); -// TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG); -// TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK); - -// for (uint32_t row = 0; row < height; ++row) { -// TIFFWriteScanline(tif, accumulated_image[row].data(), row); -// } - -// TIFFClose(tif); -// spdlog::debug("Wrote TIFF file: {}", filename); -// } else { -// spdlog::error("Failed to open TIFF file for writing: {}", filename); -// } - -// // Accumulate counts for spectral file -// spectral_counts[bin] = std::accumulate(accumulated_image.begin(), accumulated_image.end(), 0ULL, -// [](unsigned long long sum, const std::vector& row) { -// return sum + std::accumulate(row.begin(), row.end(), 0ULL); -// }); -// } -// }); - -// // 4. Write spectral file -// std::string spectral_filename = fmt::format("{}/spectral.txt", out_tof_imaging); -// // Write spectral data to file -// std::ofstream spectral_file(spectral_filename); -// if (spectral_file.is_open()) { -// spectral_file << "Upper_Edge(s)\tCounts\n"; -// for (size_t bin = 0; bin < tof_bin_edges.size() - 1; ++bin) { -// spectral_file << tof_bin_edges[bin + 1] << "\t" << spectral_counts[bin] << "\n"; -// } -// spectral_file.close(); -// spdlog::info("Wrote spectral file: {}", spectral_filename); -// } else { -// spdlog::error("Failed to open spectral file for writing: {}", spectral_filename); -// } - -// auto end = std::chrono::high_resolution_clock::now(); -// auto duration = std::chrono::duration_cast(end - start); -// spdlog::info("TIFF and spectral file writing completed in {} ms", duration.count()); -// } - /** * @brief Main function. * From 4e0035ca3c43b24b55be37a51408df379038b54d Mon Sep 17 00:00:00 2001 From: Chen Zhang Date: Wed, 21 Aug 2024 13:29:12 -0400 Subject: [PATCH 22/38] add semi auto reducer --- sophiread/SophireadCLI/CMakeLists.txt | 38 +++- .../SophireadCLI/src/venus_auto_reducer.cpp | 211 ++++++++++++++++++ 2 files changed, 240 insertions(+), 9 deletions(-) create mode 100644 sophiread/SophireadCLI/src/venus_auto_reducer.cpp diff --git a/sophiread/SophireadCLI/CMakeLists.txt b/sophiread/SophireadCLI/CMakeLists.txt index 854bfbd..cd23498 100644 --- a/sophiread/SophireadCLI/CMakeLists.txt +++ b/sophiread/SophireadCLI/CMakeLists.txt @@ -10,23 +10,38 @@ set(SRC_FILES src/user_config.cpp src/json_config_parser.cpp src/sophiread_core.cpp - src/sophiread.cpp ) # ----------------- CLI APPLICATION ----------------- # add_executable(Sophiread ${SRC_FILES} + src/sophiread.cpp ) set_target_properties(Sophiread PROPERTIES VERSION ${PROJECT_VERSION}) target_link_libraries(Sophiread PRIVATE - FastSophiread - TBB::tbb - spdlog::spdlog - fmt::fmt - nlohmann_json::nlohmann_json - ${HDF5_LIBRARIES} - ${TIFF_LIBRARIES} + FastSophiread + TBB::tbb + spdlog::spdlog + fmt::fmt + nlohmann_json::nlohmann_json + ${HDF5_LIBRARIES} + ${TIFF_LIBRARIES} +) +# +add_executable(venus_auto_reducer +${SRC_FILES} + src/venus_auto_reducer.cpp +) +target_link_libraries(venus_auto_reducer + PRIVATE + FastSophiread + TBB::tbb + spdlog::spdlog + fmt::fmt + nlohmann_json::nlohmann_json + ${HDF5_LIBRARIES} + ${TIFF_LIBRARIES} ) # ----------------- TESTS ----------------- # @@ -82,10 +97,15 @@ gtest_discover_tests(SophireadCoreTest) # ----------------- SYMLINK ----------------- # # symlink the executable to the build directory add_custom_command(TARGET Sophiread POST_BUILD - COMMAND ${CMAKE_COMMAND} -E create_symlink + COMMAND ${CMAKE_COMMAND} -E create_symlink ${PROJECT_BINARY_DIR}/SophireadCLI/Sophiread ${PROJECT_BINARY_DIR}/Sophiread ) +add_custom_command(TARGET venus_auto_reducer POST_BUILD + COMMAND ${CMAKE_COMMAND} -E create_symlink + ${PROJECT_BINARY_DIR}/SophireadCLI/venus_auto_reducer + ${PROJECT_BINARY_DIR}/venus_auto_reducer +) # ----------------- INSTALL ----------------- # if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) diff --git a/sophiread/SophireadCLI/src/venus_auto_reducer.cpp b/sophiread/SophireadCLI/src/venus_auto_reducer.cpp new file mode 100644 index 0000000..6053272 --- /dev/null +++ b/sophiread/SophireadCLI/src/venus_auto_reducer.cpp @@ -0,0 +1,211 @@ +/** + * @file venus_auto_reducer.cpp + * @author Chen Zhang (zhangc@orn.gov) + * @brief Auto reducer demo application for VENUS@SNS + * @version 0.1 + * @date 2024-08-21 + * + * @copyright Copyright (c) 2024 + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include "sophiread_core.h" +#include "json_config_parser.h" +#include +#include +namespace fs = std::filesystem; + +struct ProgramOptions { + std::string input_dir; + std::string output_dir; + std::string config_file; + std::string tiff_base = "tof_image"; + std::string tof_mode = "neutron"; + bool verbose = false; + bool debug = false; +}; + +void print_usage(const char* program_name) { + spdlog::info("Usage: {} -I -o [-u ] [-f ] [-m ] [-v]", program_name); + spdlog::info("Options:"); + spdlog::info(" -I Input directory with TPX3 files"); + spdlog::info(" -o Output directory for TIFF files"); + spdlog::info(" -u User configuration JSON file (optional)"); + spdlog::info(" -f Base name for TIFF files (default: tof_image)"); + spdlog::info(" -m TOF mode: 'hit' or 'neutron' (default: neutron)"); + spdlog::info(" -d Debug output"); + spdlog::info(" -v Verbose output"); +} + +ProgramOptions parse_arguments(int argc, char* argv[]) { + ProgramOptions options; + int opt; + + while ((opt = getopt(argc, argv, "I:o:u:f:m:dv")) != -1) { + switch (opt) { + case 'I': + options.input_dir = optarg; + break; + case 'o': + options.output_dir = optarg; + break; + case 'u': + options.config_file = optarg; + break; + case 'f': + options.tiff_base = optarg; + break; + case 'm': + options.tof_mode = optarg; + break; + case 'd': + options.debug = true; + case 'v': + options.verbose = true; + break; + default: + print_usage(argv[0]); + throw std::runtime_error("Invalid argument"); + } + } + + // Validate required arguments + if (options.input_dir.empty() || options.output_dir.empty()) { + print_usage(argv[0]); + throw std::runtime_error("Missing required arguments"); + } + + // Validate tof_mode + if (options.tof_mode != "hit" && options.tof_mode != "neutron") { + throw std::runtime_error("Invalid TOF mode. Use 'hit' or 'neutron'."); + } + + return options; +} + +/** + * @brief Process all existing tpx3 files + * + * @param[in] input_dir + * @param[in] output_dir + * @param[in] tiff_base + * @param[in] tof_mode + * @param[in] config + */ +void process_existing_files(const std::string& input_dir, const std::string& output_dir, + const std::string& tiff_base, const std::string& tof_mode, + const IConfig& config) { + spdlog::info("Processing existing files in {}", input_dir); + + for (const auto& entry : fs::directory_iterator(input_dir)) { + if (entry.is_regular_file() && entry.path().extension() == ".tpx3") { + spdlog::info("Processing file: {}", entry.path().string()); + + try { + // Read the TPX3 file + auto raw_data = sophiread::timedReadDataToCharVec(entry.path().string()); + + // Find TPX3 headers + auto batches = sophiread::timedFindTPX3H(raw_data); + + // Process the data + sophiread::timedLocateTimeStamp(batches, raw_data); + sophiread::timedProcessing(batches, raw_data, config); + + // Generate output file name + std::string output_file = fs::path(output_dir) / (tiff_base + "_bin_xxxx.tiff"); + + // Create TOF images + auto tof_images = sophiread::timedCreateTOFImages(batches, config.getSuperResolution(), config.getTOFBinEdges(), tof_mode); + + // Save TOF images + sophiread::timedSaveTOFImagingToTIFF(output_dir, tof_images, config.getTOFBinEdges(), tiff_base); + + spdlog::info("Processed and saved: {}", output_file); + } catch (const std::exception& e) { + spdlog::error("Error processing file {}: {}", entry.path().string(), e.what()); + } + } + } + + spdlog::info("Finished processing existing files"); +} + +// Function to monitor directory for new files +// void monitor_directory(const std::string& input_dir, const std::string& output_dir, +// const std::string& tiff_base, const std::string& tof_mode, +// const IConfig& config, std::atomic& should_continue) { +// // TODO: Implement directory monitoring and file processing +// } + +int main(int argc, char* argv[]) { + try { + ProgramOptions options = parse_arguments(argc, argv); + + // Set logging level based on verbose flag + if (options.debug){ + spdlog::set_level(spdlog::level::debug); + spdlog::debug("Debug logging enabled"); + } else if (options.verbose) { + spdlog::set_level(spdlog::level::info); + } else { + spdlog::set_level(spdlog::level::warn); + } + + spdlog::info("Input directory: {}", options.input_dir); + spdlog::info("Output directory: {}", options.output_dir); + spdlog::info("Config file: {}", options.config_file); + spdlog::info("TIFF base name: {}", options.tiff_base); + spdlog::info("TOF mode: {}", options.tof_mode); + + // Load configuration + std::unique_ptr config; + if (!options.config_file.empty()) { + spdlog::info("Config file: {}", options.config_file); + config = std::make_unique(JSONConfigParser::fromFile(options.config_file)); + } else { + spdlog::info("Using default configuration"); + config = std::make_unique(JSONConfigParser::createDefault()); + } + + spdlog::info("Configuration: {}", config->toString()); + + // Process existing files + process_existing_files(options.input_dir, options.output_dir, options.tiff_base, options.tof_mode, *config); + + // Start monitoring for new files + // std::atomic should_continue{true}; + // std::thread monitor_thread(monitor_directory, std::ref(options.input_dir), + // std::ref(options.output_dir), std::ref(options.tiff_base), + // std::ref(options.tof_mode), std::ref(config), + // std::ref(should_continue)); + + // // TODO: Implement graceful shutdown mechanism + + // monitor_thread.join(); + + } catch (const std::exception& e) { + spdlog::error("Error: {}", e.what()); + return 1; + } + + return 0; +} \ No newline at end of file From 6e52980321b2f5f4868845f74a412b25d7379534 Mon Sep 17 00:00:00 2001 From: Chen Zhang Date: Wed, 21 Aug 2024 14:01:14 -0400 Subject: [PATCH 23/38] track processed files --- .../SophireadCLI/src/venus_auto_reducer.cpp | 102 ++++++++++++++++-- 1 file changed, 95 insertions(+), 7 deletions(-) diff --git a/sophiread/SophireadCLI/src/venus_auto_reducer.cpp b/sophiread/SophireadCLI/src/venus_auto_reducer.cpp index 6053272..738dbf0 100644 --- a/sophiread/SophireadCLI/src/venus_auto_reducer.cpp +++ b/sophiread/SophireadCLI/src/venus_auto_reducer.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #include "sophiread_core.h" #include "json_config_parser.h" #include @@ -109,12 +110,15 @@ ProgramOptions parse_arguments(int argc, char* argv[]) { * @param[in] tiff_base * @param[in] tof_mode * @param[in] config + * @param[in, out] processed_files */ void process_existing_files(const std::string& input_dir, const std::string& output_dir, const std::string& tiff_base, const std::string& tof_mode, - const IConfig& config) { + const IConfig& config, std::unordered_set& processed_files) { spdlog::info("Processing existing files in {}", input_dir); - + + // NOTE: we need to process files sequentially as we are accumulating the counts to the + // same set of tiff files in the output folder for (const auto& entry : fs::directory_iterator(input_dir)) { if (entry.is_regular_file() && entry.path().extension() == ".tpx3") { spdlog::info("Processing file: {}", entry.path().string()); @@ -140,20 +144,103 @@ void process_existing_files(const std::string& input_dir, const std::string& out sophiread::timedSaveTOFImagingToTIFF(output_dir, tof_images, config.getTOFBinEdges(), tiff_base); spdlog::info("Processed and saved: {}", output_file); + + // record processed file + processed_files.insert(entry.path().stem().string()); } catch (const std::exception& e) { spdlog::error("Error processing file {}: {}", entry.path().string(), e.what()); } } } - - spdlog::info("Finished processing existing files"); + + // Create a string to hold the formatted output + std::ostringstream oss; + oss << "Processed files: ["; + + // Loop through the set and concatenate elements + for (auto it = processed_files.begin(); it != processed_files.end(); ++it) { + if (it != processed_files.begin()) { + oss << ", "; // Add a comma before each element except the first + } + oss << *it; + } + oss << "]"; + spdlog::info(oss.str()); } // Function to monitor directory for new files -// void monitor_directory(const std::string& input_dir, const std::string& output_dir, +// void monitor_directory(const std::string& input_dir, const std::string& output_dir, // const std::string& tiff_base, const std::string& tof_mode, // const IConfig& config, std::atomic& should_continue) { -// // TODO: Implement directory monitoring and file processing +// std::unordered_set processed_files; + +// // First, add all existing processed files to the set +// for (const auto& entry : fs::directory_iterator(input_dir)) { +// if (entry.is_regular_file() && entry.path().extension() == ".tiff") { +// std::string base_name = entry.path().stem().string(); +// // Remove the tiff_base suffix to get the original file name +// size_t pos = base_name.rfind("_" + tiff_base); +// if (pos != std::string::npos) { +// base_name = base_name.substr(0, pos); +// } +// processed_files.insert(base_name + ".tpx3"); +// } +// } + +// while (should_continue) { +// for (const auto& entry : fs::directory_iterator(input_dir)) { +// if (entry.is_regular_file() && entry.path().extension() == ".tpx3") { +// std::string file_name = entry.path().filename().string(); + +// // Check if file has already been processed +// if (processed_files.find(file_name) != processed_files.end()) { +// continue; +// } + +// // Simple file locking mechanism: check if file size is changing +// auto initial_size = fs::file_size(entry.path()); +// std::this_thread::sleep_for(std::chrono::seconds(1)); +// if (fs::file_size(entry.path()) != initial_size) { +// spdlog::debug("File {} is still being written. Skipping for now.", file_name); +// continue; +// } + +// spdlog::info("Processing new file: {}", file_name); + +// try { +// // Read the TPX3 file +// auto raw_data = sophiread::timedReadDataToCharVec(entry.path().string()); + +// // Find TPX3 headers +// auto batches = sophiread::timedFindTPX3H(raw_data); + +// // Process the data +// sophiread::timedLocateTimeStamp(batches, raw_data); +// sophiread::timedProcessing(batches, raw_data, config); + +// // Generate output file name +// std::string output_file = fs::path(output_dir) / (entry.path().stem().string() + "_" + tiff_base + ".tiff"); + +// // Create TOF images +// auto tof_images = sophiread::timedCreateTOFImages(batches, config.getSuperResolution(), config.getTOFBinEdges(), tof_mode); + +// // Save TOF images +// sophiread::timedSaveTOFImagingToTIFF(output_dir, tof_images, config.getTOFBinEdges(), entry.path().stem().string() + "_" + tiff_base); + +// spdlog::info("Processed and saved: {}", output_file); + +// // Add to processed files set +// processed_files.insert(file_name); + +// } catch (const std::exception& e) { +// spdlog::error("Error processing file {}: {}", file_name, e.what()); +// } +// } +// } + +// // Sleep for a short time before checking again +// std::this_thread::sleep_for(std::chrono::seconds(5)); +// } // } int main(int argc, char* argv[]) { @@ -189,7 +276,8 @@ int main(int argc, char* argv[]) { spdlog::info("Configuration: {}", config->toString()); // Process existing files - process_existing_files(options.input_dir, options.output_dir, options.tiff_base, options.tof_mode, *config); + std::unordered_set processed_files; + process_existing_files(options.input_dir, options.output_dir, options.tiff_base, options.tof_mode, *config, processed_files); // Start monitoring for new files // std::atomic should_continue{true}; From 1bcbb6395d435a75546ebe4741e498ee8a7c1b7d Mon Sep 17 00:00:00 2001 From: Chen Zhang Date: Wed, 21 Aug 2024 15:19:12 -0400 Subject: [PATCH 24/38] refactor cmd arg parser to be consistent --- sophiread/SophireadCLI/src/sophiread.cpp | 310 +++++++++--------- .../SophireadCLI/src/venus_auto_reducer.cpp | 8 +- 2 files changed, 153 insertions(+), 165 deletions(-) diff --git a/sophiread/SophireadCLI/src/sophiread.cpp b/sophiread/SophireadCLI/src/sophiread.cpp index 7aaa542..46445a0 100644 --- a/sophiread/SophireadCLI/src/sophiread.cpp +++ b/sophiread/SophireadCLI/src/sophiread.cpp @@ -38,6 +38,85 @@ #include "json_config_parser.h" #include "sophiread_core.h" +struct ProgramOptions { + std::string input_tpx3; + std::string output_hits; + std::string output_events; + std::string config_file; + std::string output_tof_imaging; + std::string tof_filename_base = "tof_image"; + std::string tof_mode = "neutron"; + bool debug_logging = false; + bool verbose = false; +}; + +void print_usage(const char* program_name) { + spdlog::info("Usage: {} -i -H -E [-u ] [-T ] [-f ] [-m ] [-d] [-v]", program_name); + spdlog::info("Options:"); + spdlog::info(" -i Input TPX3 file"); + spdlog::info(" -H Output hits HDF5 file"); + spdlog::info(" -E Output events HDF5 file"); + spdlog::info(" -u User configuration JSON file (optional)"); + spdlog::info(" -T Output folder for TIFF TOF images (optional)"); + spdlog::info(" -f Base name for TIFF files (default: tof_image)"); + spdlog::info(" -m TOF mode: 'hit' or 'neutron' (default: neutron)"); + spdlog::info(" -d Enable debug logging"); + spdlog::info(" -v Enable verbose logging"); +} + +ProgramOptions parse_arguments(int argc, char* argv[]) { + ProgramOptions options; + int opt; + + while ((opt = getopt(argc, argv, "i:H:E:u:T:f:m:dv")) != -1) { + switch (opt) { + case 'i': + options.input_tpx3 = optarg; + break; + case 'H': + options.output_hits = optarg; + break; + case 'E': + options.output_events = optarg; + break; + case 'u': + options.config_file = optarg; + break; + case 'T': + options.output_tof_imaging = optarg; + break; + case 'f': + options.tof_filename_base = optarg; + break; + case 'm': + options.tof_mode = optarg; + break; + case 'd': + options.debug_logging = true; + break; + case 'v': + options.verbose = true; + break; + default: + print_usage(argv[0]); + throw std::runtime_error("Invalid argument"); + } + } + + // Validate required arguments + if (options.input_tpx3.empty()) { + print_usage(argv[0]); + throw std::runtime_error("Missing required arguments"); + } + + // Validate TOF mode + if (options.tof_mode != "hit" && options.tof_mode != "neutron") { + throw std::runtime_error("Invalid TOF mode. Use 'hit' or 'neutron'."); + } + + return options; +} + /** * @brief Main function. * @@ -46,167 +125,76 @@ * @return int */ int main(int argc, char *argv[]) { - // ---------------------------------- - // processing command line arguments - // ---------------------------------- - std::string in_tpx3; - std::string out_hits; - std::string out_events; - std::string config_file; - std::string out_tof_imaging; - std::string tof_filename_base = "tof_image"; - std::string tof_mode = "neutron"; // Default to neutron-based TOF imaging - bool debug_logging = false; - bool verbose = false; - int opt; - - // help message string - std::string help_msg = - "Usage:\n" + - std::string(argv[0]) + - "\n\t[-i input_tpx3] " + - "\n\t[-H output_hits_HDF5] " + - "\n\t[-E output_event_HDF5] " + - "\n\t[-u user_defined_params]" + - "\n\t[-T tof_imaging_folder]" + - "\n\t[-f tof_filename_base]" + - "\n\t[-m tof_mode(hit or neutron)]" + - "\n\t[-d]" + - "\n\t[-v]"; - - // parse command line arguments - while ((opt = getopt(argc, argv, "i:H:E:T:f:u:m:dv")) != -1) { - switch (opt) { - case 'i': // input file - in_tpx3 = optarg; - break; - case 'H': // output hits file (HDF5) - out_hits = optarg; - break; - case 'E': // output event file - out_events = optarg; - break; - case 'u': // user-defined params - config_file = optarg; - break; - case 'T': // output TOF imaging folder - out_tof_imaging = optarg; - break; - case 'f': // TOF filename base - tof_filename_base = optarg; - break; - case 'm': // mode for TOF imaging, neutron: neutrons->tiff; hit: hit->tiff - tof_mode = optarg; - break; - case 'd': - debug_logging = true; - break; - case 'v': - verbose = true; - break; - default: - spdlog::error(help_msg); - return 1; - } - } - - // Set Logging - if (debug_logging) { - spdlog::set_level(spdlog::level::debug); - spdlog::debug("Debug logging enabled"); - } else if (verbose) { - spdlog::set_level(spdlog::level::info); - } else { - spdlog::set_level(spdlog::level::warn); - } - - // ------------- - // sanity check - // ------------- - // Check 1: determine config file type and parse accordingly - std::unique_ptr config; - if (!config_file.empty()) { - std::string extension = std::filesystem::path(config_file).extension().string(); - if (extension == ".json") { - try { - config = std::make_unique(JSONConfigParser::fromFile(config_file)); - spdlog::info("Using JSON configuration file: {}", config_file); - } catch (const std::exception& e) { - spdlog::error("Error parsing JSON configuration file: {}", e.what()); + try { + ProgramOptions options = parse_arguments(argc, argv); + + // Set logging level based on debug and verbose flags + if (options.debug_logging) { + spdlog::set_level(spdlog::level::debug); + spdlog::debug("Debug logging enabled"); + } else if (options.verbose) { + spdlog::set_level(spdlog::level::info); + } else { + spdlog::set_level(spdlog::level::warn); + } + + spdlog::info("Input file: {}", options.input_tpx3); + spdlog::info("Output hits file: {}", options.output_hits); + spdlog::info("Output events file: {}", options.output_events); + spdlog::info("Configuration file: {}", options.config_file); + spdlog::info("TOF imaging folder: {}", options.output_tof_imaging); + spdlog::info("TOF filename base: {}", options.tof_filename_base); + spdlog::info("TOF mode: {}", options.tof_mode); + + // Load configuration + std::unique_ptr config; + if (!options.config_file.empty()) { + std::string extension = std::filesystem::path(options.config_file).extension().string(); + if (extension == ".json") { + config = std::make_unique(JSONConfigParser::fromFile(options.config_file)); + } else { + spdlog::warn("Deprecated configuration format detected. Please switch to JSON format."); + config = std::make_unique(parseUserDefinedConfigurationFile(options.config_file)); + } + } else { + spdlog::info("No configuration file provided. Using default JSON configuration."); + config = std::make_unique(JSONConfigParser::createDefault()); + } + + spdlog::info("Configuration: {}", config->toString()); + + // Process raw data + auto start = std::chrono::high_resolution_clock::now(); + auto raw_data = sophiread::timedReadDataToCharVec(options.input_tpx3); + auto batches = sophiread::timedFindTPX3H(raw_data); + sophiread::timedLocateTimeStamp(batches, raw_data); + sophiread::timedProcessing(batches, raw_data, *config); + auto end = std::chrono::high_resolution_clock::now(); + auto elapsed = std::chrono::duration_cast(end - start).count(); + spdlog::info("Total processing time: {} s", elapsed / 1e6); + + // Release memory of raw data + std::vector().swap(raw_data); + + // Generate and save TOF images + if (!options.output_tof_imaging.empty()) { + auto tof_images = sophiread::timedCreateTOFImages(batches, config->getSuperResolution(), config->getTOFBinEdges(), options.tof_mode); + sophiread::timedSaveTOFImagingToTIFF(options.output_tof_imaging, tof_images, config->getTOFBinEdges(), options.tof_filename_base); + } + + // Save hits and events + if (!options.output_hits.empty()) { + sophiread::timedSaveHitsToHDF5(options.output_hits, batches); + } + + if (!options.output_events.empty()) { + sophiread::timedSaveEventsToHDF5(options.output_events, batches); + } + + } catch (const std::exception& e) { + spdlog::error("Error: {}", e.what()); return 1; - } - } else { - spdlog::warn("Deprecated configuration format detected. Please switch to JSON format in future."); - spdlog::warn("Support for the old format will be removed in version 4.x"); - config = std::make_unique(parseUserDefinedConfigurationFile(config_file)); } - } else { - spdlog::info("No configuration file provided. Using default JSON configuration."); - config = std::make_unique(JSONConfigParser::createDefault()); - } - // Check 2: does the TPX3 exists? - if (in_tpx3.empty()) { - spdlog::error("Error: no input file specified."); - spdlog::error(help_msg); - return 1; - } - // Check 3: model valid - if (tof_mode != "neutron" && tof_mode != "hit") { - spdlog::error("Invalid TOF mode specified. Use 'neutron' or 'hit'."); - return 1; - } - // recap - if (verbose) { - spdlog::info("Recap input:"); - spdlog::info("\tInput file: {}", in_tpx3); - spdlog::info("\tOutput hits file: {}", out_hits); - spdlog::info("\tOutput events file: {}", out_events); - spdlog::info("\tConfiguration: {}", config->toString()); - spdlog::info("\tTOF imaging mode: {}", tof_mode); - spdlog::info("\tDebug logging: {}", debug_logging ? "Enabled" : "Disabled"); - } - - // -------- - // process - // -------- - // raw data --> hits --> neutrons - auto start = std::chrono::high_resolution_clock::now(); - auto raw_data = sophiread::timedReadDataToCharVec(in_tpx3); - auto batches = sophiread::timedFindTPX3H(raw_data); - sophiread::timedLocateTimeStamp(batches, raw_data); - sophiread::timedProcessing(batches, raw_data, *config); - auto end = std::chrono::high_resolution_clock::now(); - auto elapsed = std::chrono::duration_cast(end - start).count(); - spdlog::info("Total processing time: {} s", elapsed / 1e6); - // release memory of raw data - std::vector().swap(raw_data); - - // neutrons/hits --2D hist--> TOF images - // NOTE: since x,y are int from hit, so the super resolution will produce checked image, - // so users should keep sr at 1 for hit->tiff - std::vector>> tof_images; - if (!out_tof_imaging.empty()) { - spdlog::debug("start creating tof images"); - tof_images = sophiread::timedCreateTOFImages(batches, config->getSuperResolution(), config->getTOFBinEdges(), tof_mode); - } - - // ------------- - // Save to Disk - // ------------- - // save hits to HDF5 file - if (!out_hits.empty()) { - sophiread::timedSaveHitsToHDF5(out_hits, batches); - } - - // save events to HDF5 file - if (!out_events.empty()) { - sophiread::timedSaveEventsToHDF5(out_events, batches); - } - - // Save TOF imaging to TIFF files - if (!out_tof_imaging.empty()) { - sophiread::timedSaveTOFImagingToTIFF(out_tof_imaging, tof_images, config->getTOFBinEdges(), tof_filename_base); - } - - return 0; + + return 0; } diff --git a/sophiread/SophireadCLI/src/venus_auto_reducer.cpp b/sophiread/SophireadCLI/src/venus_auto_reducer.cpp index 738dbf0..5cad4fa 100644 --- a/sophiread/SophireadCLI/src/venus_auto_reducer.cpp +++ b/sophiread/SophireadCLI/src/venus_auto_reducer.cpp @@ -45,9 +45,9 @@ struct ProgramOptions { }; void print_usage(const char* program_name) { - spdlog::info("Usage: {} -I -o [-u ] [-f ] [-m ] [-v]", program_name); + spdlog::info("Usage: {} -i -o [-u ] [-f ] [-m ] [-v]", program_name); spdlog::info("Options:"); - spdlog::info(" -I Input directory with TPX3 files"); + spdlog::info(" -i Input directory with TPX3 files"); spdlog::info(" -o Output directory for TIFF files"); spdlog::info(" -u User configuration JSON file (optional)"); spdlog::info(" -f Base name for TIFF files (default: tof_image)"); @@ -60,9 +60,9 @@ ProgramOptions parse_arguments(int argc, char* argv[]) { ProgramOptions options; int opt; - while ((opt = getopt(argc, argv, "I:o:u:f:m:dv")) != -1) { + while ((opt = getopt(argc, argv, "i:o:u:f:m:dv")) != -1) { switch (opt) { - case 'I': + case 'i': options.input_dir = optarg; break; case 'o': From e8e0aef4d3c446f95e70848e8feef6598d022d7e Mon Sep 17 00:00:00 2001 From: Chen Zhang Date: Wed, 21 Aug 2024 15:42:56 -0400 Subject: [PATCH 25/38] add auto monitor capability --- sophiread/SophireadCLI/src/sophiread.cpp | 12 +- .../SophireadCLI/src/venus_auto_reducer.cpp | 129 ++++++------------ 2 files changed, 49 insertions(+), 92 deletions(-) diff --git a/sophiread/SophireadCLI/src/sophiread.cpp b/sophiread/SophireadCLI/src/sophiread.cpp index 46445a0..5b76066 100644 --- a/sophiread/SophireadCLI/src/sophiread.cpp +++ b/sophiread/SophireadCLI/src/sophiread.cpp @@ -53,15 +53,15 @@ struct ProgramOptions { void print_usage(const char* program_name) { spdlog::info("Usage: {} -i -H -E [-u ] [-T ] [-f ] [-m ] [-d] [-v]", program_name); spdlog::info("Options:"); - spdlog::info(" -i Input TPX3 file"); - spdlog::info(" -H Output hits HDF5 file"); - spdlog::info(" -E Output events HDF5 file"); - spdlog::info(" -u User configuration JSON file (optional)"); + spdlog::info(" -i Input TPX3 file"); + spdlog::info(" -H Output hits HDF5 file"); + spdlog::info(" -E Output events HDF5 file"); + spdlog::info(" -u User configuration JSON file (optional)"); spdlog::info(" -T Output folder for TIFF TOF images (optional)"); spdlog::info(" -f Base name for TIFF files (default: tof_image)"); spdlog::info(" -m TOF mode: 'hit' or 'neutron' (default: neutron)"); - spdlog::info(" -d Enable debug logging"); - spdlog::info(" -v Enable verbose logging"); + spdlog::info(" -d Enable debug logging"); + spdlog::info(" -v Enable verbose logging"); } ProgramOptions parse_arguments(int argc, char* argv[]) { diff --git a/sophiread/SophireadCLI/src/venus_auto_reducer.cpp b/sophiread/SophireadCLI/src/venus_auto_reducer.cpp index 5cad4fa..445999c 100644 --- a/sophiread/SophireadCLI/src/venus_auto_reducer.cpp +++ b/sophiread/SophireadCLI/src/venus_auto_reducer.cpp @@ -40,18 +40,20 @@ struct ProgramOptions { std::string config_file; std::string tiff_base = "tof_image"; std::string tof_mode = "neutron"; + int check_interval = 5; bool verbose = false; bool debug = false; }; void print_usage(const char* program_name) { - spdlog::info("Usage: {} -i -o [-u ] [-f ] [-m ] [-v]", program_name); + spdlog::info("Usage: {} -i -o [-u ] [-f ] [-m ] [-c ] [-v] [-d]", program_name); spdlog::info("Options:"); spdlog::info(" -i Input directory with TPX3 files"); spdlog::info(" -o Output directory for TIFF files"); spdlog::info(" -u User configuration JSON file (optional)"); spdlog::info(" -f Base name for TIFF files (default: tof_image)"); spdlog::info(" -m TOF mode: 'hit' or 'neutron' (default: neutron)"); + spdlog::info(" -c Check interval in seconds (default: 5)"); spdlog::info(" -d Debug output"); spdlog::info(" -v Verbose output"); } @@ -60,7 +62,7 @@ ProgramOptions parse_arguments(int argc, char* argv[]) { ProgramOptions options; int opt; - while ((opt = getopt(argc, argv, "i:o:u:f:m:dv")) != -1) { + while ((opt = getopt(argc, argv, "i:o:u:f:m:c:dv")) != -1) { switch (opt) { case 'i': options.input_dir = optarg; @@ -77,6 +79,9 @@ ProgramOptions parse_arguments(int argc, char* argv[]) { case 'm': options.tof_mode = optarg; break; + case 'c': + options.check_interval = std::stoi(optarg); + break; case 'd': options.debug = true; case 'v': @@ -99,6 +104,11 @@ ProgramOptions parse_arguments(int argc, char* argv[]) { throw std::runtime_error("Invalid TOF mode. Use 'hit' or 'neutron'."); } + // Validate check_interval + if (options.check_interval <= 0) { + throw std::runtime_error("Check interval must be a positive integer."); + } + return options; } @@ -121,8 +131,15 @@ void process_existing_files(const std::string& input_dir, const std::string& out // same set of tiff files in the output folder for (const auto& entry : fs::directory_iterator(input_dir)) { if (entry.is_regular_file() && entry.path().extension() == ".tpx3") { + std::string filename = entry.path().stem().string(); + + if (processed_files.find(filename) != processed_files.end()) { + spdlog::debug("Skipping already processed file: {}", filename); + continue; + } + spdlog::info("Processing file: {}", entry.path().string()); - + try { // Read the TPX3 file auto raw_data = sophiread::timedReadDataToCharVec(entry.path().string()); @@ -168,80 +185,30 @@ void process_existing_files(const std::string& input_dir, const std::string& out spdlog::info(oss.str()); } -// Function to monitor directory for new files -// void monitor_directory(const std::string& input_dir, const std::string& output_dir, -// const std::string& tiff_base, const std::string& tof_mode, -// const IConfig& config, std::atomic& should_continue) { -// std::unordered_set processed_files; - -// // First, add all existing processed files to the set -// for (const auto& entry : fs::directory_iterator(input_dir)) { -// if (entry.is_regular_file() && entry.path().extension() == ".tiff") { -// std::string base_name = entry.path().stem().string(); -// // Remove the tiff_base suffix to get the original file name -// size_t pos = base_name.rfind("_" + tiff_base); -// if (pos != std::string::npos) { -// base_name = base_name.substr(0, pos); -// } -// processed_files.insert(base_name + ".tpx3"); -// } -// } - -// while (should_continue) { -// for (const auto& entry : fs::directory_iterator(input_dir)) { -// if (entry.is_regular_file() && entry.path().extension() == ".tpx3") { -// std::string file_name = entry.path().filename().string(); - -// // Check if file has already been processed -// if (processed_files.find(file_name) != processed_files.end()) { -// continue; -// } - -// // Simple file locking mechanism: check if file size is changing -// auto initial_size = fs::file_size(entry.path()); -// std::this_thread::sleep_for(std::chrono::seconds(1)); -// if (fs::file_size(entry.path()) != initial_size) { -// spdlog::debug("File {} is still being written. Skipping for now.", file_name); -// continue; -// } +void monitor_directory(const std::string& input_dir, const std::string& output_dir, + const std::string& tiff_base, const std::string& tof_mode, + const IConfig& config, std::unordered_set& processed_files, + int check_interval) { + spdlog::info("Starting directory monitoring: {}", input_dir); + spdlog::info("Check interval: {} seconds", check_interval); -// spdlog::info("Processing new file: {}", file_name); - -// try { -// // Read the TPX3 file -// auto raw_data = sophiread::timedReadDataToCharVec(entry.path().string()); - -// // Find TPX3 headers -// auto batches = sophiread::timedFindTPX3H(raw_data); - -// // Process the data -// sophiread::timedLocateTimeStamp(batches, raw_data); -// sophiread::timedProcessing(batches, raw_data, config); - -// // Generate output file name -// std::string output_file = fs::path(output_dir) / (entry.path().stem().string() + "_" + tiff_base + ".tiff"); - -// // Create TOF images -// auto tof_images = sophiread::timedCreateTOFImages(batches, config.getSuperResolution(), config.getTOFBinEdges(), tof_mode); - -// // Save TOF images -// sophiread::timedSaveTOFImagingToTIFF(output_dir, tof_images, config.getTOFBinEdges(), entry.path().stem().string() + "_" + tiff_base); - -// spdlog::info("Processed and saved: {}", output_file); - -// // Add to processed files set -// processed_files.insert(file_name); + while (true) { + // Check for *.nxs.h5 file + for (const auto& entry : fs::directory_iterator(input_dir)) { + if (entry.is_regular_file() && entry.path().extension() == ".h5" && + entry.path().stem().extension() == ".nxs") { + spdlog::info("Found *.nxs.h5 file. Stopping monitoring."); + return; + } + } -// } catch (const std::exception& e) { -// spdlog::error("Error processing file {}: {}", file_name, e.what()); -// } -// } -// } + // Process any new files + process_existing_files(input_dir, output_dir, tiff_base, tof_mode, config, processed_files); -// // Sleep for a short time before checking again -// std::this_thread::sleep_for(std::chrono::seconds(5)); -// } -// } + // Wait before next check + std::this_thread::sleep_for(std::chrono::seconds(check_interval)); + } +} int main(int argc, char* argv[]) { try { @@ -277,18 +244,8 @@ int main(int argc, char* argv[]) { // Process existing files std::unordered_set processed_files; - process_existing_files(options.input_dir, options.output_dir, options.tiff_base, options.tof_mode, *config, processed_files); - - // Start monitoring for new files - // std::atomic should_continue{true}; - // std::thread monitor_thread(monitor_directory, std::ref(options.input_dir), - // std::ref(options.output_dir), std::ref(options.tiff_base), - // std::ref(options.tof_mode), std::ref(config), - // std::ref(should_continue)); - - // // TODO: Implement graceful shutdown mechanism - - // monitor_thread.join(); + monitor_directory(options.input_dir, options.output_dir, options.tiff_base, + options.tof_mode, *config, processed_files, options.check_interval); } catch (const std::exception& e) { spdlog::error("Error: {}", e.what()); From c896e36cbea90530d03e680f26adb057fd6df487 Mon Sep 17 00:00:00 2001 From: Chen Zhang Date: Thu, 22 Aug 2024 08:32:51 -0400 Subject: [PATCH 26/38] change spectra file format to match tpx2 --- sophiread/SophireadCLI/src/sophiread_core.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sophiread/SophireadCLI/src/sophiread_core.cpp b/sophiread/SophireadCLI/src/sophiread_core.cpp index 9758fbf..9d22b06 100644 --- a/sophiread/SophireadCLI/src/sophiread_core.cpp +++ b/sophiread/SophireadCLI/src/sophiread_core.cpp @@ -360,13 +360,13 @@ void timedSaveTOFImagingToTIFF( }); // 4. Write spectral file - std::string spectral_filename = fmt::format("{}/spectral.txt", out_tof_imaging); + std::string spectral_filename = fmt::format("{}/{}_Spectra.txt", out_tof_imaging, tof_filename_base); // Write spectral data to file std::ofstream spectral_file(spectral_filename); if (spectral_file.is_open()) { - spectral_file << "Upper_Edge(s)\tCounts\n"; + spectral_file << "shutter_time(s),counts\n"; for (size_t bin = 0; bin < tof_bin_edges.size() - 1; ++bin) { - spectral_file << tof_bin_edges[bin + 1] << "\t" << spectral_counts[bin] << "\n"; + spectral_file << tof_bin_edges[bin + 1] << "," << spectral_counts[bin] << "\n"; } spectral_file.close(); spdlog::info("Wrote spectral file: {}", spectral_filename); From a3ec7c7c53c04bdb7bb930863dc7bf753110a164 Mon Sep 17 00:00:00 2001 From: Chen Zhang Date: Thu, 22 Aug 2024 08:34:10 -0400 Subject: [PATCH 27/38] fix typos --- sophiread/SophireadCLI/src/sophiread_core.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sophiread/SophireadCLI/src/sophiread_core.cpp b/sophiread/SophireadCLI/src/sophiread_core.cpp index 9d22b06..0b11e83 100644 --- a/sophiread/SophireadCLI/src/sophiread_core.cpp +++ b/sophiread/SophireadCLI/src/sophiread_core.cpp @@ -364,19 +364,19 @@ void timedSaveTOFImagingToTIFF( // Write spectral data to file std::ofstream spectral_file(spectral_filename); if (spectral_file.is_open()) { - spectral_file << "shutter_time(s),counts\n"; + spectral_file << "shutter_time,counts\n"; for (size_t bin = 0; bin < tof_bin_edges.size() - 1; ++bin) { spectral_file << tof_bin_edges[bin + 1] << "," << spectral_counts[bin] << "\n"; } spectral_file.close(); spdlog::info("Wrote spectral file: {}", spectral_filename); } else { - spdlog::error("Failed to open spectral file for writing: {}", spectral_filename); + spdlog::error("Failed to open spectra file for writing: {}", spectral_filename); } auto end = std::chrono::high_resolution_clock::now(); auto duration = std::chrono::duration_cast(end - start); - spdlog::info("TIFF and spectral file writing completed in {} ms", duration.count()); + spdlog::info("TIFF and spectra file writing completed in {} ms", duration.count()); } } // namespace sophiread \ No newline at end of file From 47689399a77a966d26921d084e3ee98a1d0cb037 Mon Sep 17 00:00:00 2001 From: Chen Zhang Date: Thu, 22 Aug 2024 08:40:45 -0400 Subject: [PATCH 28/38] fix failing unit test --- sophiread/SophireadCLI/tests/test_sophiread_core.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sophiread/SophireadCLI/tests/test_sophiread_core.cpp b/sophiread/SophireadCLI/tests/test_sophiread_core.cpp index 8344d9c..cd634cf 100644 --- a/sophiread/SophireadCLI/tests/test_sophiread_core.cpp +++ b/sophiread/SophireadCLI/tests/test_sophiread_core.cpp @@ -141,7 +141,7 @@ TEST_F(SophireadCoreTest, TimedSaveTOFImagingToTIFF) { EXPECT_TRUE(std::filesystem::exists("test_tof/test_bin_0001.tiff")); EXPECT_TRUE(std::filesystem::exists("test_tof/test_bin_0002.tiff")); EXPECT_TRUE(std::filesystem::exists("test_tof/test_bin_0003.tiff")); - EXPECT_TRUE(std::filesystem::exists("test_tof/spectral.txt")); + EXPECT_TRUE(std::filesystem::exists("test_tof/test_Spectra.txt")); std::filesystem::remove_all("test_tof"); } From c31e21a8d4788725e3a918e0e2181693648a5847 Mon Sep 17 00:00:00 2001 From: Chen Zhang Date: Thu, 22 Aug 2024 09:45:28 -0400 Subject: [PATCH 29/38] update readme --- sophiread/README.md | 70 +++++++++++++++++++++------------------------ 1 file changed, 33 insertions(+), 37 deletions(-) diff --git a/sophiread/README.md b/sophiread/README.md index 0739f89..49c678a 100644 --- a/sophiread/README.md +++ b/sophiread/README.md @@ -1,19 +1,13 @@ -Sophiread ---------- +# Sophiread -Sophiread is a simple, fast, and extensible toolkit for reading and processing -raw data from the Timepix3 chip. +Sophiread is a simple, fast, and extensible toolkit for reading and processing raw data (`*.tpx3`) from the Timepix3 chip. It provides both command line (CLI) and graphical user interface (GUI) interfaces. -Sophiread is written in C++ and Qt (with `qwt` extension). -The streaming data client, `SophireadStream`, is using a 3rd party library, [readerwriterqueue](https://github.com/cameron314/readerwriterqueue) -to read and process the raw data packet concurrently. +> As of 2024-08-22, the GUI application is still undergoing development to use the new fast sophiread backend, only command line applications are available. -How to build ------------- +## How to build -Sophiread is built using `cmake` and `make` under a sandboxed environment with -`conda` (see [conda](https://conda.io/docs/)). +Sophiread is built using `cmake` and `make` under a sandboxed environment with `conda` (see [conda](https://conda.io/docs/)). The following steps have been tested under MaxOS and Linux, and it is in theory possible to build it under Windows. - Install `conda` or equivalent pacakge manager such as `miniconda`, `mamba`, `micromamba`, etc. @@ -58,48 +52,50 @@ The following steps have been tested under MaxOS and Linux, and it is in theory - `sophiread--Linux.sh`: an installer for Linux - For Mac users with m-series chip, please make the following adjustment: - - Create env with `CONDA_SUBDIR=osx-64 conda create -f environment_mac.yml` - - Currently `mlpack` does not have a `arm64` package from conda, so we need to fallback to x86-64 using Rosetta 2 under the hood. - Install [MacTex](https://www.tug.org/mactex/) before building the documentation. - - __DO NOT__ install `mlpack` from homebrew. - - `mlpack` from homebrew can lead to linking error when building the DBSCAN object. -Use the CLI ------------ +## Use the CLI The CLI is a simple command line interface to read and process raw data from the Timepix3 chip. It is a single executable file, `Sophiread`, which can be found in the `build` directory. The current version of the CLI supports the following input arguments: ```bash -Sophiread [-i input_tpx3] [-u user_defined_params_list] [-H output_hits_HDF5] [-E output_event_HDF5] [-v] +Sophiread -i -H -E [-u ] [-T ] [-f ] [-m ] [-d] [-v] ``` -- `-i`: input raw Timepix3 data file. (Mandatory) -- `-u`: parse user-defined parameter list for clustering algorithsm. Please refer to the `user_defined_params.txt` template for reference. (Optional) -- `-H`: output processed hits with corresponding cluster labels as HDF5 archive. (Optional) -- `-E`: output processed neutron events as HDF5 archive. (Optional) -- `-v`: verbose mode. (Optional) +- `-i `: Input TPX3 file +- `-H `: Output hits HDF5 file +- `-E `: Output events HDF5 file +- `-u `: User configuration JSON file (optional) +- `-T `: Output folder for TIFF TOF images (optional) +- `-f `: Base name for TIFF files (default: tof_image) +- `-m `: TOF mode: 'hit' or 'neutron' (default: neutron) +- `-d`: Enable debug logging +- `-v`: Enable verbose logging -Alternatively, you can use `SophireadStream` to process the raw data packet concurrently. -From the user perspective, it is the same as `Sophiread` but it is much faster for larger data set and has a better memory management. -Unlike `Sophiread`, `SophireadStream` has a much simpler interface: +One **important** thing to check before using this software is that you need to check your chip layout before using it. +By default, `Sophiread` is assuming the detector has a `2x2` layout with a 5 pixel gap between chips. +Each chip has `512x512` pixels. +If your chip has different spec, you will need to modify the source code to make it work for your detector. + +A temporary auto reduction code binary is also available for the commission of [VENUS](https://neutrons.ornl.gov/venus), `venus_auto_reducer`: ```bash -SophireadStream +venus_auto_reducer -i -o [-u ] [-f ] [-m ] [-c ] [-v] [-d] ``` -Use the GUI ------------ - -The GUI is a graphical user interface to read and process raw data from the Timepix3 chip. -It is a single executable file, `SophireadGUI`, which can be found in the `build` directory. -Once the GUI is launched, you can open a raw data file by clicking the `Load Data` button on the top left corner. -The GUI will process the data and display the clustered neutron events in the 2D histogram. +- `-i `: Input directory with TPX3 files +- `-o `: Output directory for TIFF files +- `-u `: User configuration JSON file (optional) +- `-f `: Base name for TIFF files (default: tof_image) +- `-m `: TOF mode: 'hit' or 'neutron' (default: neutron) +- `-c `: Check interval in seconds (default: 5) +- `-d`: Debug output +- `-v`: Verbose output -Important note --------------- +## Important note -The raw data file is a binary file with a specific format, so it is not recommended to open it with a text editor. +The raw data file is a binary file with a specific format, please **DO NOT** try to open it with a text editor as it can corrupt the bytes inside. Additionally, super-pixeling (also known as super resolution) is used to increase the spatial resolution of the data, i.e. bumping the `512x512` native resolution to `4028x4028`. This is done by splitting each pixel into 8x8 sub-pixels via peak fitting. From dcabc22b8b7b8b5800f44b74cc781ff7f4420d9e Mon Sep 17 00:00:00 2001 From: Chen Zhang Date: Thu, 22 Aug 2024 09:56:17 -0400 Subject: [PATCH 30/38] clang-format --- sophiread/CMakeLists.txt | 9 + .../benchmarks/benchmark_mmap.cpp | 330 +++++++------- sophiread/FastSophiread/include/abs.h | 2 +- sophiread/FastSophiread/include/centroid.h | 4 +- sophiread/FastSophiread/include/disk_io.h | 6 +- .../FastSophiread/include/fastgaussian.h | 4 +- sophiread/FastSophiread/include/hit.h | 6 +- .../FastSophiread/include/iposition_tof.h | 12 +- sophiread/FastSophiread/include/neutron.h | 2 +- sophiread/FastSophiread/include/tpx3_fast.h | 8 +- sophiread/FastSophiread/src/disk_io.cpp | 38 +- sophiread/FastSophiread/src/hit.cpp | 7 +- sophiread/FastSophiread/src/tpx3_fast.cpp | 25 +- sophiread/FastSophiread/tests/test_tpx3.cpp | 74 ++-- sophiread/SophireadCLI/include/iconfig.h | 16 +- .../SophireadCLI/include/json_config_parser.h | 55 +-- .../SophireadCLI/include/sophiread_core.h | 22 +- sophiread/SophireadCLI/include/tof_binning.h | 44 +- sophiread/SophireadCLI/include/user_config.h | 9 +- .../SophireadCLI/src/json_config_parser.cpp | 118 +++-- sophiread/SophireadCLI/src/sophiread.cpp | 285 ++++++------ sophiread/SophireadCLI/src/sophiread_core.cpp | 411 +++++++++--------- sophiread/SophireadCLI/src/user_config.cpp | 22 +- .../SophireadCLI/src/venus_auto_reducer.cpp | 396 ++++++++--------- .../tests/test_json_config_parser.cpp | 144 +++--- .../tests/test_sophiread_core.cpp | 189 ++++---- .../SophireadCLI/tests/test_user_config.cpp | 20 +- sophiread/SophireadGUI/src/mainwindow.cpp | 18 +- .../SophireadLib/benchmarks/benchmark_abs.cpp | 14 +- .../benchmarks/benchmark_abs_thread.cpp | 12 +- sophiread/SophireadLib/include/abs.h | 19 +- sophiread/SophireadLib/include/centroid.h | 2 +- sophiread/SophireadLib/include/clustering.h | 3 +- sophiread/SophireadLib/include/dbscan.h | 26 +- sophiread/SophireadLib/include/fastgaussian.h | 2 +- sophiread/SophireadLib/include/tpx3.h | 97 ++--- sophiread/SophireadLib/src/abs.cpp | 13 +- sophiread/SophireadLib/src/centroid.cpp | 2 +- sophiread/SophireadLib/src/dbscan.cpp | 126 ++---- sophiread/SophireadLib/src/fastgaussian.cpp | 10 +- sophiread/SophireadLib/src/tpx3.cpp | 105 ++--- .../tests/generate_fake_tpx3_data.cpp | 380 ++++++++-------- .../SophireadLib/tests/test_clustering.cpp | 17 +- .../SophireadLib/tests/test_peakfitting.cpp | 30 +- sophiread/SophireadLib/tests/test_tpx3.cpp | 91 ++-- .../src/sophiread_stream.cpp | 11 +- sophiread/include/version.h | 4 +- sophiread/resources/manufacture/tpx3cam.cpp | 364 ++++++++-------- 48 files changed, 1729 insertions(+), 1875 deletions(-) diff --git a/sophiread/CMakeLists.txt b/sophiread/CMakeLists.txt index db0e729..584436a 100644 --- a/sophiread/CMakeLists.txt +++ b/sophiread/CMakeLists.txt @@ -103,3 +103,12 @@ if(DOXYGEN_FOUND) index.html ) endif(DOXYGEN_FOUND) + +# --------------- Clang format --------------# +file(GLOB_RECURSE ALL_CXX_SOURCE_FILES *.cpp *.hpp *.h *.c) +add_custom_target( + format + COMMAND clang-format -i ${ALL_CXX_SOURCE_FILES} + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + COMMENT "Running clang-format" +) diff --git a/sophiread/FastSophiread/benchmarks/benchmark_mmap.cpp b/sophiread/FastSophiread/benchmarks/benchmark_mmap.cpp index 34f613e..e935b39 100644 --- a/sophiread/FastSophiread/benchmarks/benchmark_mmap.cpp +++ b/sophiread/FastSophiread/benchmarks/benchmark_mmap.cpp @@ -21,22 +21,22 @@ */ #include +#include // for std::numeric_limits<> #include #include -#include // for std::numeric_limits<> +#include "abs.h" #include "disk_io.h" #include "hit.h" #include "spdlog/spdlog.h" -#include "abs.h" #include "tbb/tbb.h" #include "tpx3_fast.h" // from SophireadLib/include/tpx3.cpp #include + #include "neutron.h" -void saveEventsToHDF5(const std::string out_file_name, - const std::vector& events); +void saveEventsToHDF5(const std::string out_file_name, const std::vector& events); /** * @brief Save events to HDF5 file. * @@ -46,11 +46,11 @@ void saveEventsToHDF5(const std::string out_file_name, * * TODO: There is another program Sophiread that also writes out events. Make sure that those match up. * Better still, use common output writing routines for both programs. - * + * * @param out_file_name: output file name. * @param events: neutron events to be saved. */ -void saveEventsToHDF5(const std::string out_file_name, const std::vector &events) { +void saveEventsToHDF5(const std::string out_file_name, const std::vector& events) { // sanity check if (events.size() == 0) return; @@ -65,44 +65,41 @@ void saveEventsToHDF5(const std::string out_file_name, const std::vector x(events.size()); - std::transform(events.begin(), events.end(), x.begin(), [](const Neutron &event) { return event.getX(); }); + std::transform(events.begin(), events.end(), x.begin(), [](const Neutron& event) { return event.getX(); }); H5::DataSet x_dataset = group.createDataSet("x", float_type, dataspace); x_dataset.write(x.data(), float_type); // -- write y std::vector y(events.size()); - std::transform(events.begin(), events.end(), y.begin(), [](const Neutron &event) { return event.getY(); }); + std::transform(events.begin(), events.end(), y.begin(), [](const Neutron& event) { return event.getY(); }); H5::DataSet y_dataset = group.createDataSet("y", float_type, dataspace); y_dataset.write(y.data(), float_type); // -- write TOF_ns std::vector tof_ns(events.size()); - std::transform(events.begin(), events.end(), tof_ns.begin(), - [](const Neutron &event) { return event.getTOF_ns(); }); + std::transform(events.begin(), events.end(), tof_ns.begin(), [](const Neutron& event) { return event.getTOF_ns(); }); H5::DataSet tof_ns_dataset = group.createDataSet("tof", float_type, dataspace); tof_ns_dataset.write(tof_ns.data(), float_type); // -- write Nhits std::vector nhits(events.size()); - std::transform(events.begin(), events.end(), nhits.begin(), - [](const Neutron &event) { return event.getNHits(); }); + std::transform(events.begin(), events.end(), nhits.begin(), [](const Neutron& event) { return event.getNHits(); }); H5::DataSet nhits_dataset = group.createDataSet("nHits", int_type, dataspace); nhits_dataset.write(nhits.data(), int_type); // -- write TOT std::vector tot(events.size()); - std::transform(events.begin(), events.end(), tot.begin(), [](const Neutron &event) { return event.getTOT(); }); + std::transform(events.begin(), events.end(), tot.begin(), [](const Neutron& event) { return event.getTOT(); }); H5::DataSet tot_dataset = group.createDataSet("tot", float_type, dataspace); tot_dataset.write(tot.data(), float_type); // -- close file out_file.close(); } -void saveEventsToCSV( const std::string out_file_name, - const std::vector& events); +void saveEventsToCSV(const std::string out_file_name, const std::vector& events); /** * @brief Save events to CSV file. * * @param out_file_name: output file name. * @param events: neutron events to be saved. */ -void saveEventsToCSV(const std::string out_file_name, const std::vector &events) { +void saveEventsToCSV(const std::string out_file_name, const std::vector& events) { // sanity check if (events.size() == 0) return; @@ -119,28 +116,23 @@ void saveEventsToCSV(const std::string out_file_name, const std::vector file << "X,Y,TOF (ns),Nhits, TOT\n"; // write events - for (auto & event : events) { - file << - event.getX() << "," << - event.getY() << "," << - event.getTOF_ns() << "," << - event.getNHits() << "," << - event.getTOT() << "\n"; + for (auto& event : events) { + file << event.getX() << "," << event.getY() << "," << event.getTOF_ns() << "," << event.getNHits() << "," + << event.getTOT() << "\n"; } // -- close file file.close(); } -void saveEventsToBIN( const std::string out_file_name, - const std::vector& events); +void saveEventsToBIN(const std::string out_file_name, const std::vector& events); /** * @brief Save events to BIN file. * * @param out_file_name: output file name. * @param events: neutron events to be saved. */ -void saveEventsToBIN(const std::string out_file_name, const std::vector &events) { +void saveEventsToBIN(const std::string out_file_name, const std::vector& events) { // sanity check if (events.size() == 0) return; @@ -155,10 +147,9 @@ void saveEventsToBIN(const std::string out_file_name, const std::vector } // write events - for (auto & event : events) - { - std::uint16_t x = (std::uint16_t)(event.getX() * std::numeric_limits::max()/512.0); - std::uint16_t y = (std::uint16_t)(event.getY() * std::numeric_limits::max()/512.0); + for (auto& event : events) { + std::uint16_t x = (std::uint16_t)(event.getX() * std::numeric_limits::max() / 512.0); + std::uint16_t y = (std::uint16_t)(event.getY() * std::numeric_limits::max() / 512.0); std::uint64_t tof = (std::uint64_t)event.getTOF_ns(); std::uint16_t nhits = (std::uint16_t)event.getNHits(); std::uint16_t tot = (std::uint16_t)event.getTOT(); @@ -171,16 +162,15 @@ void saveEventsToBIN(const std::string out_file_name, const std::vector } // https://stackoverflow.com/questions/874134/find-out-if-string-ends-with-another-string-in-c -bool endsWith (std::string const &fullString, std::string const &ending) { - if (fullString.length() >= ending.length()) { - return (0 == fullString.compare (fullString.length() - ending.length(), ending.length(), ending)); - } else { - return false; - } +bool endsWith(std::string const& fullString, std::string const& ending) { + if (fullString.length() >= ending.length()) { + return (0 == fullString.compare(fullString.length() - ending.length(), ending.length(), ending)); + } else { + return false; + } } -int main(int argc, char* argv[]) -{ +int main(int argc, char* argv[]) { if (argc < 2) { spdlog::critical("Usage: {} [ [options]]", argv[0]); return 1; @@ -198,23 +188,22 @@ int main(int argc, char* argv[]) bool use_tgdc = false; bool debug = false; float pulse_rate = 0.0; - if( argc>3 ) - { - use_tbb = strstr(argv[3],"tbb")!=NULL; - use_mmap = strstr(argv[3],"mmap")!=NULL; - use_tgdc = strstr(argv[3],"tgdc")!=NULL; - debug = strstr(argv[3],"debug")!=NULL; - - // select pulse rate - pulse_rate = strstr(argv[3],"1hz") ? 1.0 : pulse_rate; - pulse_rate = strstr(argv[3],"10hz") ? 10.0 : pulse_rate; - pulse_rate = strstr(argv[3],"15hz") ? 15.0 : pulse_rate; - pulse_rate = strstr(argv[3],"30hz") ? 30.0 : pulse_rate; - pulse_rate = strstr(argv[3],"45hz") ? 45.0 : pulse_rate; - pulse_rate = strstr(argv[3],"60hz") ? 60.0 : pulse_rate; + if (argc > 3) { + use_tbb = strstr(argv[3], "tbb") != NULL; + use_mmap = strstr(argv[3], "mmap") != NULL; + use_tgdc = strstr(argv[3], "tgdc") != NULL; + debug = strstr(argv[3], "debug") != NULL; + + // select pulse rate + pulse_rate = strstr(argv[3], "1hz") ? 1.0 : pulse_rate; + pulse_rate = strstr(argv[3], "10hz") ? 10.0 : pulse_rate; + pulse_rate = strstr(argv[3], "15hz") ? 15.0 : pulse_rate; + pulse_rate = strstr(argv[3], "30hz") ? 30.0 : pulse_rate; + pulse_rate = strstr(argv[3], "45hz") ? 45.0 : pulse_rate; + pulse_rate = strstr(argv[3], "60hz") ? 60.0 : pulse_rate; } - if( debug ) spdlog::set_level(spdlog::level::debug); + if (debug) spdlog::set_level(spdlog::level::debug); // report statistics size_t n_hits = 0; @@ -223,15 +212,7 @@ int main(int argc, char* argv[]) // manage timers (for statistics) // but see also NOTES section of https://en.cppreference.com/w/cpp/chrono/high_resolution_clock - enum { - TOTAL = 0, - RAW_DATA, - BATCHES, - EVENTS, - GATHER, - OUTPUT, - NUM_TIMERS - }; + enum { TOTAL = 0, RAW_DATA, BATCHES, EVENTS, GATHER, OUTPUT, NUM_TIMERS }; struct { std::chrono::time_point begin; std::chrono::time_point end; @@ -247,7 +228,8 @@ int main(int argc, char* argv[]) timer[TOTAL].begin = timer[RAW_DATA].begin = std::chrono::high_resolution_clock::now(); auto raw_data = use_mmap ? mmapTPX3RawToMapInfo(in_tpx3) : readTPX3RawToMapInfo(in_tpx3); timer[RAW_DATA].end = std::chrono::high_resolution_clock::now(); - timer[RAW_DATA].accumulated = static_cast(std::chrono::duration_cast(timer[RAW_DATA].end - timer[RAW_DATA].begin).count()); + timer[RAW_DATA].accumulated = static_cast( + std::chrono::duration_cast(timer[RAW_DATA].end - timer[RAW_DATA].begin).count()); size_t raw_data_consumed = 0; unsigned long tdc_timestamp = 0; @@ -260,17 +242,15 @@ int main(int argc, char* argv[]) spdlog::debug("@{:p}, {}", raw_data.map, raw_data.max); - if ( raw_data.map == NULL ) - { + if (raw_data.map == NULL) { spdlog::error("Insufficient memory: {}", in_tpx3); exit(EXIT_FAILURE); } -// NB: processing large memory-mapped files requires a restriction -// on the amount of raw data that is treated by the algorithm + // NB: processing large memory-mapped files requires a restriction + // on the amount of raw data that is treated by the algorithm - if( use_tbb ) - { + if (use_tbb) { spdlog::debug("Processing tbb..."); method_events = "TBB Parallel"; } else { @@ -281,120 +261,121 @@ int main(int argc, char* argv[]) std::cerr << std::endl; std::cerr.precision(2); -while (raw_data_consumed < raw_data.max) { - - // manage partial batches - size_t consumed = 0; - char *raw_data_ptr = raw_data.map + raw_data_consumed; - size_t raw_data_size = raw_data.max - raw_data_consumed; - - //spdlog::debug("raw_data: {}/{} ({:.2}%)", raw_data_consumed, raw_data.max, static_cast(raw_data_consumed)*100.0/static_cast(raw_data.max)); - std::cerr << "\rraw_data: " - << raw_data_consumed << "/" << raw_data.max - << " (" << static_cast(raw_data_consumed)*100.0/static_cast(raw_data.max) << "%)"; - - timer[BATCHES].begin = std::chrono::high_resolution_clock::now(); - auto batches = findTPX3H(raw_data_ptr, raw_data_size, consumed); - if (use_tgdc) - { - // extract tdc and gdc timestamps from the batches read so far - for (auto& tpx3 : batches) { - updateTimestamp(tpx3, raw_data_ptr, consumed, tdc_timestamp, gdc_timestamp, timer_lsb32); + while (raw_data_consumed < raw_data.max) { + // manage partial batches + size_t consumed = 0; + char* raw_data_ptr = raw_data.map + raw_data_consumed; + size_t raw_data_size = raw_data.max - raw_data_consumed; + + // spdlog::debug("raw_data: {}/{} ({:.2}%)", raw_data_consumed, raw_data.max, + // static_cast(raw_data_consumed)*100.0/static_cast(raw_data.max)); + std::cerr << "\rraw_data: " << raw_data_consumed << "/" << raw_data.max << " (" + << static_cast(raw_data_consumed) * 100.0 / static_cast(raw_data.max) << "%)"; + + timer[BATCHES].begin = std::chrono::high_resolution_clock::now(); + auto batches = findTPX3H(raw_data_ptr, raw_data_size, consumed); + if (use_tgdc) { + // extract tdc and gdc timestamps from the batches read so far + for (auto& tpx3 : batches) { + updateTimestamp(tpx3, raw_data_ptr, consumed, tdc_timestamp, gdc_timestamp, timer_lsb32); + } } - } - timer[BATCHES].end = timer[EVENTS].begin = std::chrono::high_resolution_clock::now(); - - if( use_tbb ) - { - // https://github.com/jjallaire/TBB/blob/master/inst/examples/parallel-vector-sum.cpp - struct ComputeEvents { - // source vector(s) - char *input; - std::size_t range; - std::vector& batch; - - // output vector-of-vector-of-events - tbb::concurrent_vector> output; - - // standard and splitting constructor - ComputeEvents(char *input, std::size_t range, std::vector& batch) : input(input), range(range), batch(batch), output() {} - ComputeEvents(ComputeEvents& body, tbb::split) : input(body.input), range(body.range), batch(body.batch), output() {} - - // generate just the range of hits - void operator()(const tbb::blocked_range& r) { - auto abs_alg_mt = std::make_unique(5.0, 1, 75); - for (size_t i = r.begin(); i != r.end(); ++i) { - TPX3& _tpx3 = batch[i]; - extractHits(_tpx3, input, range); - abs_alg_mt->reset(); - abs_alg_mt->fit(_tpx3.hits); - output.push_back(abs_alg_mt->get_events(_tpx3.hits)); + timer[BATCHES].end = timer[EVENTS].begin = std::chrono::high_resolution_clock::now(); + + if (use_tbb) { + // https://github.com/jjallaire/TBB/blob/master/inst/examples/parallel-vector-sum.cpp + struct ComputeEvents { + // source vector(s) + char* input; + std::size_t range; + std::vector& batch; + + // output vector-of-vector-of-events + tbb::concurrent_vector> output; + + // standard and splitting constructor + ComputeEvents(char* input, std::size_t range, std::vector& batch) + : input(input), range(range), batch(batch), output() {} + ComputeEvents(ComputeEvents& body, tbb::split) + : input(body.input), range(body.range), batch(body.batch), output() {} + + // generate just the range of hits + void operator()(const tbb::blocked_range& r) { + auto abs_alg_mt = std::make_unique(5.0, 1, 75); + for (size_t i = r.begin(); i != r.end(); ++i) { + TPX3& _tpx3 = batch[i]; + extractHits(_tpx3, input, range); + abs_alg_mt->reset(); + abs_alg_mt->fit(_tpx3.hits); + output.push_back(abs_alg_mt->get_events(_tpx3.hits)); + } } - } - // join vectors - void join(ComputeEvents& rhs) { - for (auto& a : rhs.output) { - output.push_back(a); + // join vectors + void join(ComputeEvents& rhs) { + for (auto& a : rhs.output) { + output.push_back(a); + } } - } - }; + }; - ComputeEvents compute(raw_data_ptr, consumed, batches); - tbb::parallel_reduce(tbb::blocked_range(0, batches.size()), compute); - // transfer vectors - for (auto& a : compute.output) { - events.push_back(a); - } - } else { - auto abs_alg = std::make_unique(5.0, 1, 75); - for (auto& _tpx3 : batches) { - extractHits(_tpx3, raw_data_ptr, consumed); - abs_alg->reset(); - abs_alg->fit(_tpx3.hits); - events.push_back(abs_alg->get_events(_tpx3.hits)); + ComputeEvents compute(raw_data_ptr, consumed, batches); + tbb::parallel_reduce(tbb::blocked_range(0, batches.size()), compute); + // transfer vectors + for (auto& a : compute.output) { + events.push_back(a); + } + } else { + auto abs_alg = std::make_unique(5.0, 1, 75); + for (auto& _tpx3 : batches) { + extractHits(_tpx3, raw_data_ptr, consumed); + abs_alg->reset(); + abs_alg->fit(_tpx3.hits); + events.push_back(abs_alg->get_events(_tpx3.hits)); + } } - } - timer[EVENTS].end = std::chrono::high_resolution_clock::now(); + timer[EVENTS].end = std::chrono::high_resolution_clock::now(); - // report statistics - for (const auto& tpx3 : batches) { - n_hits += tpx3.hits.size(); - } - // sanity check: hit.getTOF() should be smaller than 666,667 clock, which is - // equivalent to 16.67 ms - if ( pulse_rate > 0.0 ) { + // report statistics for (const auto& tpx3 : batches) { - for (const auto& hit : tpx3.hits) { - auto tof_ms = hit.getTOF_ns() * 1e-6; - if (tof_ms > (1.0/pulse_rate)+1e-6) { - spdlog::debug("TOF: {} ms", tof_ms); - n_bad_hits++; + n_hits += tpx3.hits.size(); + } + // sanity check: hit.getTOF() should be smaller than 666,667 clock, which is + // equivalent to 16.67 ms + if (pulse_rate > 0.0) { + for (const auto& tpx3 : batches) { + for (const auto& hit : tpx3.hits) { + auto tof_ms = hit.getTOF_ns() * 1e-6; + if (tof_ms > (1.0 / pulse_rate) + 1e-6) { + spdlog::debug("TOF: {} ms", tof_ms); + n_bad_hits++; + } } } } - } - for (const auto& e : events) { - n_events += e.size(); - } + for (const auto& e : events) { + n_events += e.size(); + } - // manage iterations on partial raw_data - raw_data_consumed += consumed; - timer[BATCHES].accumulated += static_cast(std::chrono::duration_cast(timer[BATCHES].end - timer[BATCHES].begin).count()); - timer[EVENTS].accumulated += static_cast(std::chrono::duration_cast(timer[EVENTS].end - timer[EVENTS].begin).count()); -} + // manage iterations on partial raw_data + raw_data_consumed += consumed; + timer[BATCHES].accumulated += static_cast( + std::chrono::duration_cast(timer[BATCHES].end - timer[BATCHES].begin).count()); + timer[EVENTS].accumulated += static_cast( + std::chrono::duration_cast(timer[EVENTS].end - timer[EVENTS].begin).count()); + } std::cerr << std::endl; timer[TOTAL].end = std::chrono::high_resolution_clock::now(); - timer[TOTAL].accumulated += static_cast(std::chrono::duration_cast(timer[TOTAL].end - timer[TOTAL].begin).count()); + timer[TOTAL].accumulated += static_cast( + std::chrono::duration_cast(timer[TOTAL].end - timer[TOTAL].begin).count()); spdlog::info("Number of hits: {}", n_hits); spdlog::info("Number of events: {}", n_events); - if (n_hits > 0) - { - spdlog::info("bad/total hit ratio: {:.2f}%", (n_bad_hits*100.0)/n_hits); + if (n_hits > 0) { + spdlog::info("bad/total hit ratio: {:.2f}%", (n_bad_hits * 100.0) / n_hits); auto speed_raw_data = n_hits / (timer[RAW_DATA].accumulated / 1e6); auto speed_batches = n_hits / (timer[BATCHES].accumulated / 1e6); @@ -408,8 +389,7 @@ while (raw_data_consumed < raw_data.max) { // write output // save events to file - if( !no_output && n_events > 0 ) - { + if (!no_output && n_events > 0) { spdlog::debug("Writing output: {}", out_dat); // NB: events is a vector-of-vectors-of-events that we transfrom into a vector-of-events @@ -428,14 +408,22 @@ while (raw_data_consumed < raw_data.max) { std::string bin_ext = ".bin"; std::string h5_ext = ".h5"; - if (endsWith(out_dat, csv_ext)) saveEventsToCSV(out_dat, v); - else if (endsWith(out_dat, bin_ext)) saveEventsToBIN(out_dat, v); - else if (endsWith(out_dat, h5_ext)) saveEventsToHDF5(out_dat, v); - else { spdlog::debug("Unhanded extention (.bin, .csv or .h5 are known)"); no_output = true; } + if (endsWith(out_dat, csv_ext)) + saveEventsToCSV(out_dat, v); + else if (endsWith(out_dat, bin_ext)) + saveEventsToBIN(out_dat, v); + else if (endsWith(out_dat, h5_ext)) + saveEventsToHDF5(out_dat, v); + else { + spdlog::debug("Unhanded extention (.bin, .csv or .h5 are known)"); + no_output = true; + } timer[OUTPUT].end = std::chrono::high_resolution_clock::now(); - timer[GATHER].accumulated += static_cast(std::chrono::duration_cast(timer[GATHER].end - timer[GATHER].begin).count()); - timer[OUTPUT].accumulated += static_cast(std::chrono::duration_cast(timer[OUTPUT].end - timer[OUTPUT].begin).count()); + timer[GATHER].accumulated += static_cast( + std::chrono::duration_cast(timer[GATHER].end - timer[GATHER].begin).count()); + timer[OUTPUT].accumulated += static_cast( + std::chrono::duration_cast(timer[OUTPUT].end - timer[OUTPUT].begin).count()); auto speed_gather = n_events / (timer[GATHER].accumulated / 1e6); auto speed_output = n_events / (timer[OUTPUT].accumulated / 1e6); spdlog::info("{:s} speed: {:& data); void set_method(std::string method) { m_method = method; } void reset() { clusterLabels_.clear(); } diff --git a/sophiread/FastSophiread/include/centroid.h b/sophiread/FastSophiread/include/centroid.h index 331c287..603ce76 100644 --- a/sophiread/FastSophiread/include/centroid.h +++ b/sophiread/FastSophiread/include/centroid.h @@ -36,9 +36,9 @@ */ class Centroid : public PeakFittingAlgorithm { public: - Centroid(bool weighted_by_tot = true) : m_weighted_by_tot(weighted_by_tot){}; + Centroid(bool weighted_by_tot = true) : m_weighted_by_tot(weighted_by_tot) {}; Centroid(bool weighted_by_tot, double super_resolution_factor) - : m_weighted_by_tot(weighted_by_tot), m_super_resolution_factor(super_resolution_factor){}; + : m_weighted_by_tot(weighted_by_tot), m_super_resolution_factor(super_resolution_factor) {}; void set_weighted_by_tot(bool weighted_by_tot) { m_weighted_by_tot = weighted_by_tot; } diff --git a/sophiread/FastSophiread/include/disk_io.h b/sophiread/FastSophiread/include/disk_io.h index 8c3f889..e1b1ca0 100644 --- a/sophiread/FastSophiread/include/disk_io.h +++ b/sophiread/FastSophiread/include/disk_io.h @@ -34,9 +34,9 @@ std::vector readTPX3RawToCharVec(const std::string& tpx3file); typedef struct mapinfo { - int fd; - char *map; - size_t max; + int fd; + char* map; + size_t max; } mapinfo_t; mapinfo_t readTPX3RawToMapInfo(const std::string& tpx3file); diff --git a/sophiread/FastSophiread/include/fastgaussian.h b/sophiread/FastSophiread/include/fastgaussian.h index fd477d9..bcb26c1 100644 --- a/sophiread/FastSophiread/include/fastgaussian.h +++ b/sophiread/FastSophiread/include/fastgaussian.h @@ -29,8 +29,8 @@ */ class FastGaussian : public PeakFittingAlgorithm { public: - FastGaussian(){}; - FastGaussian(double super_resolution_factor) : m_super_resolution_factor(super_resolution_factor){}; + FastGaussian() {}; + FastGaussian(double super_resolution_factor) : m_super_resolution_factor(super_resolution_factor) {}; void set_super_resolution_factor(double super_resolution_factor) { m_super_resolution_factor = super_resolution_factor; diff --git a/sophiread/FastSophiread/include/hit.h b/sophiread/FastSophiread/include/hit.h index af8fa9e..a6ac129 100644 --- a/sophiread/FastSophiread/include/hit.h +++ b/sophiread/FastSophiread/include/hit.h @@ -29,7 +29,7 @@ class Hit : public IPositionTOF { public: // default constructor - Hit() : m_x(0), m_y(0), m_tot(0), m_toa(0), m_ftoa(0), m_tof(0), m_spidertime(0){}; + Hit() : m_x(0), m_y(0), m_tot(0), m_toa(0), m_ftoa(0), m_tof(0), m_spidertime(0) {}; // copy constructor Hit(const Hit& hit) : m_x(hit.m_x), @@ -38,10 +38,10 @@ class Hit : public IPositionTOF { m_toa(hit.m_toa), m_ftoa(hit.m_ftoa), m_tof(hit.m_tof), - m_spidertime(hit.m_spidertime){}; + m_spidertime(hit.m_spidertime) {}; Hit(int x, int y, int tot, int toa, int ftoa, unsigned int tof, unsigned long long spidertime) - : m_x(x), m_y(y), m_tot(tot), m_toa(toa), m_ftoa(ftoa), m_tof(tof), m_spidertime(spidertime){}; + : m_x(x), m_y(y), m_tot(tot), m_toa(toa), m_ftoa(ftoa), m_tof(tof), m_spidertime(spidertime) {}; // special constructor that directly parse the raw packet from tpx3 // into a hit diff --git a/sophiread/FastSophiread/include/iposition_tof.h b/sophiread/FastSophiread/include/iposition_tof.h index 6f09783..55fda3a 100644 --- a/sophiread/FastSophiread/include/iposition_tof.h +++ b/sophiread/FastSophiread/include/iposition_tof.h @@ -4,7 +4,7 @@ * @brief Interface for neutron and hit * @version 0.1 * @date 2024-08-20 - * + * * @copyright Copyright (c) 2024 * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -22,9 +22,9 @@ #pragma once class IPositionTOF { -public: - virtual ~IPositionTOF() = default; - virtual double iGetX() const = 0; - virtual double iGetY() const = 0; - virtual double iGetTOF_ns() const = 0; + public: + virtual ~IPositionTOF() = default; + virtual double iGetX() const = 0; + virtual double iGetY() const = 0; + virtual double iGetTOF_ns() const = 0; }; diff --git a/sophiread/FastSophiread/include/neutron.h b/sophiread/FastSophiread/include/neutron.h index f39e43f..ab77dfe 100644 --- a/sophiread/FastSophiread/include/neutron.h +++ b/sophiread/FastSophiread/include/neutron.h @@ -29,7 +29,7 @@ class Neutron : public IPositionTOF { public: Neutron(const double x, const double y, const double tof, const double tot, const int nHits) - : m_x(x), m_y(y), m_tof(tof), m_tot(tot), m_nHits(nHits){}; + : m_x(x), m_y(y), m_tof(tof), m_tot(tot), m_nHits(nHits) {}; double getX() const { return m_x; }; double getY() const { return m_y; }; diff --git a/sophiread/FastSophiread/include/tpx3_fast.h b/sophiread/FastSophiread/include/tpx3_fast.h index 47734b7..37093b1 100644 --- a/sophiread/FastSophiread/include/tpx3_fast.h +++ b/sophiread/FastSophiread/include/tpx3_fast.h @@ -33,10 +33,10 @@ * @note Each TPX3 dataset batch comes from a single sub-chip */ struct TPX3 { - std::size_t index; // index of the dataset batch in the raw charater array - const int num_packets; // number of packets in the dataset batch (time packet and data packet) - const int chip_layout_type; // data source (sub-chip ID) - std::vector hits; // hits extracted from the dataset batch + std::size_t index; // index of the dataset batch in the raw charater array + const int num_packets; // number of packets in the dataset batch (time packet and data packet) + const int chip_layout_type; // data source (sub-chip ID) + std::vector hits; // hits extracted from the dataset batch std::vector neutrons; // neutrons from clustering hits unsigned long tdc_timestamp; // starting tdc timestamp of the dataset batch diff --git a/sophiread/FastSophiread/src/disk_io.cpp b/sophiread/FastSophiread/src/disk_io.cpp index bb9a475..0c4aa4e 100644 --- a/sophiread/FastSophiread/src/disk_io.cpp +++ b/sophiread/FastSophiread/src/disk_io.cpp @@ -31,7 +31,7 @@ * @param[in] tpx3file * @return std::vector */ -std::vector readTPX3RawToCharVec(const std::string& tpx3file) { +std::vector readTPX3RawToCharVec(const std::string &tpx3file) { // Open the file std::ifstream file(tpx3file, std::ios::binary | std::ios::ate); @@ -64,9 +64,8 @@ std::vector readTPX3RawToCharVec(const std::string& tpx3file) { * @param tpx3file * @return mapinfo_t that defines { char *, and size_t } */ -mapinfo_t readTPX3RawToMapInfo(const std::string& tpx3file) -{ - mapinfo_t info = { -1, NULL, 0 }; +mapinfo_t readTPX3RawToMapInfo(const std::string &tpx3file) { + mapinfo_t info = {-1, NULL, 0}; // Open the file std::ifstream file(tpx3file, std::ios::binary | std::ios::ate); @@ -87,8 +86,10 @@ mapinfo_t readTPX3RawToMapInfo(const std::string& tpx3file) info.map = reinterpret_cast(malloc(info.max)); // Read the data from file and store it in the vector - if (info.map == NULL) info.max = 0; - else file.read(info.map, info.max); + if (info.map == NULL) + info.max = 0; + else + file.read(info.map, info.max); // Close the file file.close(); @@ -97,10 +98,10 @@ mapinfo_t readTPX3RawToMapInfo(const std::string& tpx3file) } // for mmap support +#include #include -#include #include -#include +#include #include /** @@ -109,9 +110,8 @@ mapinfo_t readTPX3RawToMapInfo(const std::string& tpx3file) * @param tpx3file * @return mapinfo_t that defines { char *, and size_t } */ -mapinfo_t mmapTPX3RawToMapInfo(const std::string& tpx3file) -{ - mapinfo_t info = { -1, NULL, 0 }; +mapinfo_t mmapTPX3RawToMapInfo(const std::string &tpx3file) { + mapinfo_t info = {-1, NULL, 0}; std::ifstream file(tpx3file, std::ios::binary); if (!file.is_open()) { @@ -119,17 +119,21 @@ mapinfo_t mmapTPX3RawToMapInfo(const std::string& tpx3file) exit(EXIT_FAILURE); } - info.fd = open(tpx3file.c_str(),O_RDWR,0666); + info.fd = open(tpx3file.c_str(), O_RDWR, 0666); - if( info.fd == -1 ) { perror(tpx3file.c_str()); exit(EXIT_FAILURE); } - info.max = lseek(info.fd,0,SEEK_END); // determine the sizeof the file - info.map = reinterpret_cast(mmap(0, info.max, PROT_READ|PROT_WRITE, MAP_SHARED, info.fd, 0)); + if (info.fd == -1) { + perror(tpx3file.c_str()); + exit(EXIT_FAILURE); + } + info.max = lseek(info.fd, 0, SEEK_END); // determine the sizeof the file + info.map = reinterpret_cast(mmap(0, info.max, PROT_READ | PROT_WRITE, MAP_SHARED, info.fd, 0)); // https://lemire.me/blog/2012/06/26/which-is-fastest-read-fread-ifstream-or-mmap/ // says to add MAP_POPULATE to make it mmap() faster... // TODO: Test this return info; - // https://stackoverflow.com/questions/26569217/do-i-have-to-munmap-a-mmap-file ( consensus is that you do not need to do it ) + // https://stackoverflow.com/questions/26569217/do-i-have-to-munmap-a-mmap-file ( consensus is that you do not need to + // do it ) } /** @@ -138,7 +142,7 @@ mapinfo_t mmapTPX3RawToMapInfo(const std::string& tpx3file) * @param[in] originalFileName * @return std::string */ -std::string generateFileNameWithMicroTimestamp(const std::string& originalFileName) { +std::string generateFileNameWithMicroTimestamp(const std::string &originalFileName) { auto now = std::chrono::high_resolution_clock::now(); auto seconds = std::chrono::time_point_cast(now); auto micros = std::chrono::duration_cast(now - seconds); diff --git a/sophiread/FastSophiread/src/hit.cpp b/sophiread/FastSophiread/src/hit.cpp index aad67a3..f12a139 100644 --- a/sophiread/FastSophiread/src/hit.cpp +++ b/sophiread/FastSophiread/src/hit.cpp @@ -21,6 +21,7 @@ * along with this program. If not, see . */ #include "hit.h" + #include /** @@ -62,16 +63,16 @@ Hit::Hit(const char *packet, const unsigned long long TDC_timestamp, const unsig m_spidertime = (SPDR_MSB18 << 30) & 0xFFFFC0000000; m_spidertime = m_spidertime | spidertime; - // additional check to make sure rollover of spidertime is correct + // additional check to make sure rollover of spidertime is correct // 4e7 is roughly 1 second in the units of 25 ns // 1073741824 is 2^30 (in units of 25 ns) - if ((m_spidertime-GDC_timestamp)>=4e7){ + if ((m_spidertime - GDC_timestamp) >= 4e7) { m_spidertime -= 1073741824; } // tof calculation m_tof = m_spidertime - TDC_timestamp; - while (m_tof*25E-6 > 16.67){ + while (m_tof * 25E-6 > 16.67) { m_tof -= 666667; } diff --git a/sophiread/FastSophiread/src/tpx3_fast.cpp b/sophiread/FastSophiread/src/tpx3_fast.cpp index cfa8188..3487bc7 100644 --- a/sophiread/FastSophiread/src/tpx3_fast.cpp +++ b/sophiread/FastSophiread/src/tpx3_fast.cpp @@ -25,20 +25,19 @@ #include #include -#define MAX_BATCH_LEN 100000 // enough to process suann_socket_background_serval32.tpx3 without rollover +#define MAX_BATCH_LEN 100000 // enough to process suann_socket_background_serval32.tpx3 without rollover #ifdef MAX_BATCH_LEN -#include #include +#include // allow MAX_BATCH_LEN to come from the environment long unsigned int _get_max_batch_len(void) { - if (const char* env_p = std::getenv("MAX_BATCH_LEN")) { - auto max_batch_len = std::strtoul(env_p, NULL, 0); - // no conversion produce 0 - if (max_batch_len != 0 ) - return max_batch_len; - } - return MAX_BATCH_LEN; + if (const char *env_p = std::getenv("MAX_BATCH_LEN")) { + auto max_batch_len = std::strtoul(env_p, NULL, 0); + // no conversion produce 0 + if (max_batch_len != 0) return max_batch_len; + } + return MAX_BATCH_LEN; } #endif @@ -143,9 +142,7 @@ std::vector findTPX3H(const std::vector &raw_bytes) { * @param[in] size * @return std::vector */ -std::vector findTPX3H(char *raw_bytes, std::size_t size) { - return findTPX3H(raw_bytes, raw_bytes + size); -} +std::vector findTPX3H(char *raw_bytes, std::size_t size) { return findTPX3H(raw_bytes, raw_bytes + size); } /** * @brief Locate all TPX3H (chip dataset) in the raw data. @@ -154,7 +151,7 @@ std::vector findTPX3H(char *raw_bytes, std::size_t size) { * @param[out] consumed * @return std::vector */ -std::vector findTPX3H(const std::vector &raw_bytes, std::size_t& consumed) { +std::vector findTPX3H(const std::vector &raw_bytes, std::size_t &consumed) { return findTPX3H(raw_bytes.cbegin(), raw_bytes.cend(), consumed); } @@ -166,7 +163,7 @@ std::vector findTPX3H(const std::vector &raw_bytes, std::size_t& con * @param[out] consumed * @return std::vector */ -std::vector findTPX3H(char *raw_bytes, std::size_t size, std::size_t& consumed) { +std::vector findTPX3H(char *raw_bytes, std::size_t size, std::size_t &consumed) { return findTPX3H(raw_bytes, raw_bytes + size, consumed); } diff --git a/sophiread/FastSophiread/tests/test_tpx3.cpp b/sophiread/FastSophiread/tests/test_tpx3.cpp index 86900dc..2a1f166 100644 --- a/sophiread/FastSophiread/tests/test_tpx3.cpp +++ b/sophiread/FastSophiread/tests/test_tpx3.cpp @@ -223,10 +223,12 @@ TEST(TPX3FuncTest, TestExtractHits_mmap) { TEST(TPX3FuncTest, TestExtractHits_large) { // memory map the testing raw data - auto mapdata = mmapTPX3RawToMapInfo("../data/HV2700_1500_500_THLrel_274_sophy_chopper_60Hz_4.1mm_aperture_siemen_star_120s_000000.tpx3"); - const int n_hits_reference = 5303344; // HV2700_1500_500_THLrel_274_sophy_chopper_60Hz_4.1mm_aperture_siemen_star_120s_000000.tpx3 - //auto mapdata = mmapTPX3RawToMapInfo("../data/suann_socket_background_serval32.tpx3"); - //const int n_hits_reference = 98533; // suann_socket_background_serval32.tpx3 + auto mapdata = mmapTPX3RawToMapInfo( + "../data/HV2700_1500_500_THLrel_274_sophy_chopper_60Hz_4.1mm_aperture_siemen_star_120s_000000.tpx3"); + const int n_hits_reference = + 5303344; // HV2700_1500_500_THLrel_274_sophy_chopper_60Hz_4.1mm_aperture_siemen_star_120s_000000.tpx3 + // auto mapdata = mmapTPX3RawToMapInfo("../data/suann_socket_background_serval32.tpx3"); + // const int n_hits_reference = 98533; // suann_socket_background_serval32.tpx3 size_t raw_data_consumed = 0; size_t n_hits = 0; // NB: these track timestamps; if they are reset improperly, the system keeps searching and gets the wrong answers! @@ -234,50 +236,48 @@ TEST(TPX3FuncTest, TestExtractHits_large) { unsigned long long int gdc_timestamp = 0; unsigned long timer_lsb32 = 0; - while (raw_data_consumed < mapdata.max) { + while (raw_data_consumed < mapdata.max) { + // manage partial batches + size_t consumed = 0; + char* raw_data_ptr = mapdata.map + raw_data_consumed; + size_t raw_data_size = mapdata.max - raw_data_consumed; - // manage partial batches - size_t consumed = 0; - char *raw_data_ptr = mapdata.map + raw_data_consumed; - size_t raw_data_size = mapdata.max - raw_data_consumed; + // locate all headers + auto batches = findTPX3H(raw_data_ptr, raw_data_size, consumed); - // locate all headers - auto batches = findTPX3H(raw_data_ptr, raw_data_size, consumed); - - // locate gdc and tdc - for (auto& tpx3 : batches) { - updateTimestamp(tpx3, raw_data_ptr, consumed, tdc_timestamp, gdc_timestamp, timer_lsb32); - } + // locate gdc and tdc + for (auto& tpx3 : batches) { + updateTimestamp(tpx3, raw_data_ptr, consumed, tdc_timestamp, gdc_timestamp, timer_lsb32); + } - // extract hits - for (auto& tpx3 : batches) { - extractHits(tpx3, raw_data_ptr, consumed); - } + // extract hits + for (auto& tpx3 : batches) { + extractHits(tpx3, raw_data_ptr, consumed); + } - // count all hits - for (const auto& tpx3 : batches) { - auto hits = tpx3.hits; - n_hits += hits.size(); - } + // count all hits + for (const auto& tpx3 : batches) { + auto hits = tpx3.hits; + n_hits += hits.size(); + } - // make sure no tof is above 16.67 ms - for (const auto& tpx3 : batches) { - auto hits = tpx3.hits; - for (const auto& hit : hits) { - auto tof_ms = hit.getTOF_ns() * 1e-6; - EXPECT_LT(tof_ms, 16670); + // make sure no tof is above 16.67 ms + for (const auto& tpx3 : batches) { + auto hits = tpx3.hits; + for (const auto& hit : hits) { + auto tof_ms = hit.getTOF_ns() * 1e-6; + EXPECT_LT(tof_ms, 16670); + } } - } - // manage iterations on partial raw_data - raw_data_consumed += consumed; - } + // manage iterations on partial raw_data + raw_data_consumed += consumed; + } EXPECT_EQ(n_hits, n_hits_reference); - } -int main(int argc, char **argv) { +int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/sophiread/SophireadCLI/include/iconfig.h b/sophiread/SophireadCLI/include/iconfig.h index a9b4120..092eeb5 100644 --- a/sophiread/SophireadCLI/include/iconfig.h +++ b/sophiread/SophireadCLI/include/iconfig.h @@ -25,14 +25,14 @@ #include class IConfig { -public: - virtual ~IConfig() = default; + public: + virtual ~IConfig() = default; - virtual double getABSRadius() const = 0; - virtual unsigned long int getABSMinClusterSize() const = 0; - virtual unsigned long int getABSSpiderTimeRange() const = 0; - virtual std::vector getTOFBinEdges() const = 0; - virtual double getSuperResolution() const = 0; + virtual double getABSRadius() const = 0; + virtual unsigned long int getABSMinClusterSize() const = 0; + virtual unsigned long int getABSSpiderTimeRange() const = 0; + virtual std::vector getTOFBinEdges() const = 0; + virtual double getSuperResolution() const = 0; - virtual std::string toString() const = 0; + virtual std::string toString() const = 0; }; \ No newline at end of file diff --git a/sophiread/SophireadCLI/include/json_config_parser.h b/sophiread/SophireadCLI/include/json_config_parser.h index f1e1dd2..7023bab 100644 --- a/sophiread/SophireadCLI/include/json_config_parser.h +++ b/sophiread/SophireadCLI/include/json_config_parser.h @@ -21,37 +21,38 @@ */ #pragma once +#include #include #include -#include + #include "iconfig.h" #include "tof_binning.h" class JSONConfigParser : public IConfig { -public: - static JSONConfigParser createDefault(); - static JSONConfigParser fromFile(const std::string& filepath); - - double getABSRadius() const override; - unsigned long int getABSMinClusterSize() const override; - unsigned long int getABSSpiderTimeRange() const override; - std::vector getTOFBinEdges() const override; - double getSuperResolution() const override; - - std::string toString() const override; - -private: - JSONConfigParser(const nlohmann::json& config); - nlohmann::json m_config; - TOFBinning m_tof_binning; - - void parseTOFBinning(); - - // Default values - static constexpr double DEFAULT_ABS_RADIUS = 5.0; - static constexpr unsigned long int DEFAULT_ABS_MIN_CLUSTER_SIZE = 1; - static constexpr unsigned long int DEFAULT_ABS_SPIDER_TIME_RANGE = 75; - static constexpr int DEFAULT_TOF_BINS = 1500; - static constexpr double DEFAULT_TOF_MAX = 16.7e-3; // 16.7 milliseconds - static constexpr double DEFAULT_SUPER_RESOLUTION = 1.0; + public: + static JSONConfigParser createDefault(); + static JSONConfigParser fromFile(const std::string& filepath); + + double getABSRadius() const override; + unsigned long int getABSMinClusterSize() const override; + unsigned long int getABSSpiderTimeRange() const override; + std::vector getTOFBinEdges() const override; + double getSuperResolution() const override; + + std::string toString() const override; + + private: + JSONConfigParser(const nlohmann::json& config); + nlohmann::json m_config; + TOFBinning m_tof_binning; + + void parseTOFBinning(); + + // Default values + static constexpr double DEFAULT_ABS_RADIUS = 5.0; + static constexpr unsigned long int DEFAULT_ABS_MIN_CLUSTER_SIZE = 1; + static constexpr unsigned long int DEFAULT_ABS_SPIDER_TIME_RANGE = 75; + static constexpr int DEFAULT_TOF_BINS = 1500; + static constexpr double DEFAULT_TOF_MAX = 16.7e-3; // 16.7 milliseconds + static constexpr double DEFAULT_SUPER_RESOLUTION = 1.0; }; diff --git a/sophiread/SophireadCLI/include/sophiread_core.h b/sophiread/SophireadCLI/include/sophiread_core.h index b90c991..c73bd2c 100644 --- a/sophiread/SophireadCLI/include/sophiread_core.h +++ b/sophiread/SophireadCLI/include/sophiread_core.h @@ -23,9 +23,10 @@ #include #include + #include "abs.h" -#include "tpx3_fast.h" #include "iconfig.h" +#include "tpx3_fast.h" namespace sophiread { @@ -35,14 +36,11 @@ void timedLocateTimeStamp(std::vector& batches, const std::vector& r void timedProcessing(std::vector& batches, const std::vector& raw_data, const IConfig& config); void timedSaveHitsToHDF5(const std::string& out_hits, std::vector& batches); void timedSaveEventsToHDF5(const std::string& out_events, std::vector& batches); -std::vector>> timedCreateTOFImages( - const std::vector& batches, - double super_resolution, - const std::vector& tof_bin_edges, - const std::string& mode); -void timedSaveTOFImagingToTIFF( - const std::string& out_tof_imaging, - const std::vector>>& tof_images, - const std::vector& tof_bin_edges, - const std::string& tof_filename_base); -} // namespace sophiread +std::vector>> timedCreateTOFImages(const std::vector& batches, + double super_resolution, + const std::vector& tof_bin_edges, + const std::string& mode); +void timedSaveTOFImagingToTIFF(const std::string& out_tof_imaging, + const std::vector>>& tof_images, + const std::vector& tof_bin_edges, const std::string& tof_filename_base); +} // namespace sophiread diff --git a/sophiread/SophireadCLI/include/tof_binning.h b/sophiread/SophireadCLI/include/tof_binning.h index b79167e..f96c21f 100644 --- a/sophiread/SophireadCLI/include/tof_binning.h +++ b/sophiread/SophireadCLI/include/tof_binning.h @@ -4,7 +4,7 @@ * @brief TOF binning * @version 0.1 * @date 2024-08-16 - * + * * @copyright Copyright (c) 2024 * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -21,36 +21,32 @@ */ #pragma once -#include #include +#include struct TOFBinning { - std::optional num_bins; - std::optional tof_max; - std::vector custom_edges; + std::optional num_bins; + std::optional tof_max; + std::vector custom_edges; - // Default constructor - TOFBinning() : num_bins(1500), tof_max(1.0/60) {} + // Default constructor + TOFBinning() : num_bins(1500), tof_max(1.0 / 60) {} - bool isUniform() const { - return num_bins.has_value() && tof_max.has_value() && custom_edges.empty(); - } + bool isUniform() const { return num_bins.has_value() && tof_max.has_value() && custom_edges.empty(); } + + bool isCustom() const { return !custom_edges.empty(); } - bool isCustom() const { - return !custom_edges.empty(); + std::vector getBinEdges() const { + if (isCustom()) { + return custom_edges; } - std::vector getBinEdges() const { - if (isCustom()) { - return custom_edges; - } - - int bins = num_bins.value_or(1500); - double max = tof_max.value_or(1.0/60); - std::vector edges(bins + 1); - for (int i = 0; i <= bins; ++i) { - edges[i] = max * i / bins; - } - return edges; + int bins = num_bins.value_or(1500); + double max = tof_max.value_or(1.0 / 60); + std::vector edges(bins + 1); + for (int i = 0; i <= bins; ++i) { + edges[i] = max * i / bins; } + return edges; + } }; \ No newline at end of file diff --git a/sophiread/SophireadCLI/include/user_config.h b/sophiread/SophireadCLI/include/user_config.h index 5e8238e..3ab877c 100644 --- a/sophiread/SophireadCLI/include/user_config.h +++ b/sophiread/SophireadCLI/include/user_config.h @@ -23,6 +23,7 @@ #pragma once #include + #include "iconfig.h" #include "tof_binning.h" @@ -38,15 +39,17 @@ class UserConfig : public IConfig { void setABSMinClusterSize(unsigned long int abs_min_cluster_size) { m_abs_min_cluster_size = abs_min_cluster_size; } unsigned long int getABSSpiderTimeRange() const override { return m_abs_spider_time_range; } - void setABSSpiderTimeRange(unsigned long int abs_spider_time_range) { m_abs_spider_time_range = abs_spider_time_range; } + void setABSSpiderTimeRange(unsigned long int abs_spider_time_range) { + m_abs_spider_time_range = abs_spider_time_range; + } std::vector getTOFBinEdges() const override { return m_tof_binning.getBinEdges(); } void setTOFBinning(const TOFBinning& tof_binning) { m_tof_binning = tof_binning; } void setCustomTOFBinEdges(const std::vector& edges) { m_tof_binning.custom_edges = edges; } // no super resolution for old config format - double getSuperResolution() const override {return m_super_resolution; } - void setSuperResolution(double super_resolution) {m_super_resolution = super_resolution; } + double getSuperResolution() const override { return m_super_resolution; } + void setSuperResolution(double super_resolution) { m_super_resolution = super_resolution; } std::string toString() const override; diff --git a/sophiread/SophireadCLI/src/json_config_parser.cpp b/sophiread/SophireadCLI/src/json_config_parser.cpp index e2bef14..1591f92 100644 --- a/sophiread/SophireadCLI/src/json_config_parser.cpp +++ b/sophiread/SophireadCLI/src/json_config_parser.cpp @@ -20,73 +20,62 @@ * along with this program. If not, see . */ #include "json_config_parser.h" -#include + #include +#include JSONConfigParser JSONConfigParser::createDefault() { - nlohmann::json default_config = { - {"abs", { - {"radius", DEFAULT_ABS_RADIUS}, - {"min_cluster_size", DEFAULT_ABS_MIN_CLUSTER_SIZE}, - {"spider_time_range", DEFAULT_ABS_SPIDER_TIME_RANGE} - }}, - {"tof_imaging", { - {"uniform_bins", { - {"num_bins", DEFAULT_TOF_BINS}, - {"end", DEFAULT_TOF_MAX} - }}, - {"super_resolution", DEFAULT_SUPER_RESOLUTION} - }} - }; - - return JSONConfigParser(default_config); + nlohmann::json default_config = {{"abs", + {{"radius", DEFAULT_ABS_RADIUS}, + {"min_cluster_size", DEFAULT_ABS_MIN_CLUSTER_SIZE}, + {"spider_time_range", DEFAULT_ABS_SPIDER_TIME_RANGE}}}, + {"tof_imaging", + {{"uniform_bins", {{"num_bins", DEFAULT_TOF_BINS}, {"end", DEFAULT_TOF_MAX}}}, + {"super_resolution", DEFAULT_SUPER_RESOLUTION}}}}; + + return JSONConfigParser(default_config); } - /** * @brief Build JSONConfigParser from a given file * @param filepath Path to the JSON configuration file * @return JSONConfigParser */ JSONConfigParser JSONConfigParser::fromFile(const std::string& filepath) { - std::ifstream file(filepath); - if (!file.is_open()) { - throw std::runtime_error("Failed to open configuration file: " + filepath); - } - - nlohmann::json config; - try { - file >> config; - } catch (const nlohmann::json::exception& e) { - throw std::runtime_error("Error parsing JSON file: " + std::string(e.what())); - } - - return JSONConfigParser(config); + std::ifstream file(filepath); + if (!file.is_open()) { + throw std::runtime_error("Failed to open configuration file: " + filepath); + } + + nlohmann::json config; + try { + file >> config; + } catch (const nlohmann::json::exception& e) { + throw std::runtime_error("Error parsing JSON file: " + std::string(e.what())); + } + + return JSONConfigParser(config); } /** * @brief Construct a new JSONConfigParser::JSONConfigParser object * @param config JSON configuration object */ -JSONConfigParser::JSONConfigParser(const nlohmann::json& config) : m_config(config) { - parseTOFBinning(); -} +JSONConfigParser::JSONConfigParser(const nlohmann::json& config) : m_config(config) { parseTOFBinning(); } /** * @brief Get the ABS Radius * @return double */ -double JSONConfigParser::getABSRadius() const { - return m_config.value("/abs/radius"_json_pointer, DEFAULT_ABS_RADIUS); -} +double JSONConfigParser::getABSRadius() const { return m_config.value("/abs/radius"_json_pointer, DEFAULT_ABS_RADIUS); } /** * @brief Get the ABS Min Cluster Size * @return unsigned long int */ unsigned long int JSONConfigParser::getABSMinClusterSize() const { - return m_config.value("/abs/min_cluster_size"_json_pointer, DEFAULT_ABS_MIN_CLUSTER_SIZE); + return m_config.value("/abs/min_cluster_size"_json_pointer, DEFAULT_ABS_MIN_CLUSTER_SIZE); } /** @@ -94,36 +83,34 @@ unsigned long int JSONConfigParser::getABSMinClusterSize() const { * @return unsigned long int */ unsigned long int JSONConfigParser::getABSSpiderTimeRange() const { - return m_config.value("/abs/spider_time_range"_json_pointer, DEFAULT_ABS_SPIDER_TIME_RANGE); + return m_config.value("/abs/spider_time_range"_json_pointer, DEFAULT_ABS_SPIDER_TIME_RANGE); } /** * @brief Get the TOF Bin Edges * @return std::vector */ -std::vector JSONConfigParser::getTOFBinEdges() const { - return m_tof_binning.getBinEdges(); -} +std::vector JSONConfigParser::getTOFBinEdges() const { return m_tof_binning.getBinEdges(); } double JSONConfigParser::getSuperResolution() const { - return m_config.value("/tof_imaging/super_resolution"_json_pointer, DEFAULT_SUPER_RESOLUTION); + return m_config.value("/tof_imaging/super_resolution"_json_pointer, DEFAULT_SUPER_RESOLUTION); } /** * @brief Parse the TOF binning configuration */ void JSONConfigParser::parseTOFBinning() { - if (m_config.contains("/tof_imaging/bin_edges"_json_pointer)) { - m_tof_binning.custom_edges = m_config["/tof_imaging/bin_edges"_json_pointer].get>(); - } else if (m_config.contains("/tof_imaging/uniform_bins"_json_pointer)) { - const auto& uniform = m_config["/tof_imaging/uniform_bins"_json_pointer]; - m_tof_binning.num_bins = uniform.value("num_bins", DEFAULT_TOF_BINS); - m_tof_binning.tof_max = uniform.value("end", DEFAULT_TOF_MAX); - } else { - // Use default values - m_tof_binning.num_bins = DEFAULT_TOF_BINS; - m_tof_binning.tof_max = DEFAULT_TOF_MAX; - } + if (m_config.contains("/tof_imaging/bin_edges"_json_pointer)) { + m_tof_binning.custom_edges = m_config["/tof_imaging/bin_edges"_json_pointer].get>(); + } else if (m_config.contains("/tof_imaging/uniform_bins"_json_pointer)) { + const auto& uniform = m_config["/tof_imaging/uniform_bins"_json_pointer]; + m_tof_binning.num_bins = uniform.value("num_bins", DEFAULT_TOF_BINS); + m_tof_binning.tof_max = uniform.value("end", DEFAULT_TOF_MAX); + } else { + // Use default values + m_tof_binning.num_bins = DEFAULT_TOF_BINS; + m_tof_binning.tof_max = DEFAULT_TOF_MAX; + } } /** @@ -131,19 +118,18 @@ void JSONConfigParser::parseTOFBinning() { * @return std::string */ std::string JSONConfigParser::toString() const { - std::stringstream ss; - ss << "ABS: radius=" << getABSRadius() - << ", min_cluster_size=" << getABSMinClusterSize() - << ", spider_time_range=" << getABSSpiderTimeRange(); + std::stringstream ss; + ss << "ABS: radius=" << getABSRadius() << ", min_cluster_size=" << getABSMinClusterSize() + << ", spider_time_range=" << getABSSpiderTimeRange(); - if (m_tof_binning.isCustom()) { - ss << ", Custom TOF binning with " << m_tof_binning.custom_edges.size() - 1 << " bins"; - } else { - ss << ", TOF bins=" << m_tof_binning.num_bins.value_or(DEFAULT_TOF_BINS) - << ", TOF max=" << (m_tof_binning.tof_max.value_or(DEFAULT_TOF_MAX) * 1000) << " ms"; - } + if (m_tof_binning.isCustom()) { + ss << ", Custom TOF binning with " << m_tof_binning.custom_edges.size() - 1 << " bins"; + } else { + ss << ", TOF bins=" << m_tof_binning.num_bins.value_or(DEFAULT_TOF_BINS) + << ", TOF max=" << (m_tof_binning.tof_max.value_or(DEFAULT_TOF_MAX) * 1000) << " ms"; + } - ss << ", Super Resolution=" << getSuperResolution(); + ss << ", Super Resolution=" << getSuperResolution(); - return ss.str(); + return ss.str(); } diff --git a/sophiread/SophireadCLI/src/sophiread.cpp b/sophiread/SophireadCLI/src/sophiread.cpp index 5b76066..42c0828 100644 --- a/sophiread/SophireadCLI/src/sophiread.cpp +++ b/sophiread/SophireadCLI/src/sophiread.cpp @@ -22,99 +22,102 @@ */ #include #include +#include #include -#include #include +#include #include #include #include -#include #include "abs.h" #include "disk_io.h" -#include "tpx3_fast.h" -#include "user_config.h" #include "json_config_parser.h" #include "sophiread_core.h" +#include "tpx3_fast.h" +#include "user_config.h" struct ProgramOptions { - std::string input_tpx3; - std::string output_hits; - std::string output_events; - std::string config_file; - std::string output_tof_imaging; - std::string tof_filename_base = "tof_image"; - std::string tof_mode = "neutron"; - bool debug_logging = false; - bool verbose = false; + std::string input_tpx3; + std::string output_hits; + std::string output_events; + std::string config_file; + std::string output_tof_imaging; + std::string tof_filename_base = "tof_image"; + std::string tof_mode = "neutron"; + bool debug_logging = false; + bool verbose = false; }; void print_usage(const char* program_name) { - spdlog::info("Usage: {} -i -H -E [-u ] [-T ] [-f ] [-m ] [-d] [-v]", program_name); - spdlog::info("Options:"); - spdlog::info(" -i Input TPX3 file"); - spdlog::info(" -H Output hits HDF5 file"); - spdlog::info(" -E Output events HDF5 file"); - spdlog::info(" -u User configuration JSON file (optional)"); - spdlog::info(" -T Output folder for TIFF TOF images (optional)"); - spdlog::info(" -f Base name for TIFF files (default: tof_image)"); - spdlog::info(" -m TOF mode: 'hit' or 'neutron' (default: neutron)"); - spdlog::info(" -d Enable debug logging"); - spdlog::info(" -v Enable verbose logging"); + spdlog::info( + "Usage: {} -i -H -E [-u ] [-T ] [-f " + "] [-m ] [-d] [-v]", + program_name); + spdlog::info("Options:"); + spdlog::info(" -i Input TPX3 file"); + spdlog::info(" -H Output hits HDF5 file"); + spdlog::info(" -E Output events HDF5 file"); + spdlog::info(" -u User configuration JSON file (optional)"); + spdlog::info(" -T Output folder for TIFF TOF images (optional)"); + spdlog::info(" -f Base name for TIFF files (default: tof_image)"); + spdlog::info(" -m TOF mode: 'hit' or 'neutron' (default: neutron)"); + spdlog::info(" -d Enable debug logging"); + spdlog::info(" -v Enable verbose logging"); } ProgramOptions parse_arguments(int argc, char* argv[]) { - ProgramOptions options; - int opt; - - while ((opt = getopt(argc, argv, "i:H:E:u:T:f:m:dv")) != -1) { - switch (opt) { - case 'i': - options.input_tpx3 = optarg; - break; - case 'H': - options.output_hits = optarg; - break; - case 'E': - options.output_events = optarg; - break; - case 'u': - options.config_file = optarg; - break; - case 'T': - options.output_tof_imaging = optarg; - break; - case 'f': - options.tof_filename_base = optarg; - break; - case 'm': - options.tof_mode = optarg; - break; - case 'd': - options.debug_logging = true; - break; - case 'v': - options.verbose = true; - break; - default: - print_usage(argv[0]); - throw std::runtime_error("Invalid argument"); - } - } - - // Validate required arguments - if (options.input_tpx3.empty()) { + ProgramOptions options; + int opt; + + while ((opt = getopt(argc, argv, "i:H:E:u:T:f:m:dv")) != -1) { + switch (opt) { + case 'i': + options.input_tpx3 = optarg; + break; + case 'H': + options.output_hits = optarg; + break; + case 'E': + options.output_events = optarg; + break; + case 'u': + options.config_file = optarg; + break; + case 'T': + options.output_tof_imaging = optarg; + break; + case 'f': + options.tof_filename_base = optarg; + break; + case 'm': + options.tof_mode = optarg; + break; + case 'd': + options.debug_logging = true; + break; + case 'v': + options.verbose = true; + break; + default: print_usage(argv[0]); - throw std::runtime_error("Missing required arguments"); + throw std::runtime_error("Invalid argument"); } + } - // Validate TOF mode - if (options.tof_mode != "hit" && options.tof_mode != "neutron") { - throw std::runtime_error("Invalid TOF mode. Use 'hit' or 'neutron'."); - } + // Validate required arguments + if (options.input_tpx3.empty()) { + print_usage(argv[0]); + throw std::runtime_error("Missing required arguments"); + } - return options; + // Validate TOF mode + if (options.tof_mode != "hit" && options.tof_mode != "neutron") { + throw std::runtime_error("Invalid TOF mode. Use 'hit' or 'neutron'."); + } + + return options; } /** @@ -124,77 +127,79 @@ ProgramOptions parse_arguments(int argc, char* argv[]) { * @param[in] argv * @return int */ -int main(int argc, char *argv[]) { +int main(int argc, char* argv[]) { try { - ProgramOptions options = parse_arguments(argc, argv); - - // Set logging level based on debug and verbose flags - if (options.debug_logging) { - spdlog::set_level(spdlog::level::debug); - spdlog::debug("Debug logging enabled"); - } else if (options.verbose) { - spdlog::set_level(spdlog::level::info); - } else { - spdlog::set_level(spdlog::level::warn); - } - - spdlog::info("Input file: {}", options.input_tpx3); - spdlog::info("Output hits file: {}", options.output_hits); - spdlog::info("Output events file: {}", options.output_events); - spdlog::info("Configuration file: {}", options.config_file); - spdlog::info("TOF imaging folder: {}", options.output_tof_imaging); - spdlog::info("TOF filename base: {}", options.tof_filename_base); - spdlog::info("TOF mode: {}", options.tof_mode); - - // Load configuration - std::unique_ptr config; - if (!options.config_file.empty()) { - std::string extension = std::filesystem::path(options.config_file).extension().string(); - if (extension == ".json") { - config = std::make_unique(JSONConfigParser::fromFile(options.config_file)); - } else { - spdlog::warn("Deprecated configuration format detected. Please switch to JSON format."); - config = std::make_unique(parseUserDefinedConfigurationFile(options.config_file)); - } - } else { - spdlog::info("No configuration file provided. Using default JSON configuration."); - config = std::make_unique(JSONConfigParser::createDefault()); - } - - spdlog::info("Configuration: {}", config->toString()); - - // Process raw data - auto start = std::chrono::high_resolution_clock::now(); - auto raw_data = sophiread::timedReadDataToCharVec(options.input_tpx3); - auto batches = sophiread::timedFindTPX3H(raw_data); - sophiread::timedLocateTimeStamp(batches, raw_data); - sophiread::timedProcessing(batches, raw_data, *config); - auto end = std::chrono::high_resolution_clock::now(); - auto elapsed = std::chrono::duration_cast(end - start).count(); - spdlog::info("Total processing time: {} s", elapsed / 1e6); - - // Release memory of raw data - std::vector().swap(raw_data); - - // Generate and save TOF images - if (!options.output_tof_imaging.empty()) { - auto tof_images = sophiread::timedCreateTOFImages(batches, config->getSuperResolution(), config->getTOFBinEdges(), options.tof_mode); - sophiread::timedSaveTOFImagingToTIFF(options.output_tof_imaging, tof_images, config->getTOFBinEdges(), options.tof_filename_base); - } - - // Save hits and events - if (!options.output_hits.empty()) { - sophiread::timedSaveHitsToHDF5(options.output_hits, batches); - } - - if (!options.output_events.empty()) { - sophiread::timedSaveEventsToHDF5(options.output_events, batches); - } - - } catch (const std::exception& e) { - spdlog::error("Error: {}", e.what()); - return 1; + ProgramOptions options = parse_arguments(argc, argv); + + // Set logging level based on debug and verbose flags + if (options.debug_logging) { + spdlog::set_level(spdlog::level::debug); + spdlog::debug("Debug logging enabled"); + } else if (options.verbose) { + spdlog::set_level(spdlog::level::info); + } else { + spdlog::set_level(spdlog::level::warn); + } + + spdlog::info("Input file: {}", options.input_tpx3); + spdlog::info("Output hits file: {}", options.output_hits); + spdlog::info("Output events file: {}", options.output_events); + spdlog::info("Configuration file: {}", options.config_file); + spdlog::info("TOF imaging folder: {}", options.output_tof_imaging); + spdlog::info("TOF filename base: {}", options.tof_filename_base); + spdlog::info("TOF mode: {}", options.tof_mode); + + // Load configuration + std::unique_ptr config; + if (!options.config_file.empty()) { + std::string extension = std::filesystem::path(options.config_file).extension().string(); + if (extension == ".json") { + config = std::make_unique(JSONConfigParser::fromFile(options.config_file)); + } else { + spdlog::warn("Deprecated configuration format detected. Please switch to JSON format."); + config = std::make_unique(parseUserDefinedConfigurationFile(options.config_file)); + } + } else { + spdlog::info("No configuration file provided. Using default JSON configuration."); + config = std::make_unique(JSONConfigParser::createDefault()); + } + + spdlog::info("Configuration: {}", config->toString()); + + // Process raw data + auto start = std::chrono::high_resolution_clock::now(); + auto raw_data = sophiread::timedReadDataToCharVec(options.input_tpx3); + auto batches = sophiread::timedFindTPX3H(raw_data); + sophiread::timedLocateTimeStamp(batches, raw_data); + sophiread::timedProcessing(batches, raw_data, *config); + auto end = std::chrono::high_resolution_clock::now(); + auto elapsed = std::chrono::duration_cast(end - start).count(); + spdlog::info("Total processing time: {} s", elapsed / 1e6); + + // Release memory of raw data + std::vector().swap(raw_data); + + // Generate and save TOF images + if (!options.output_tof_imaging.empty()) { + auto tof_images = sophiread::timedCreateTOFImages(batches, config->getSuperResolution(), config->getTOFBinEdges(), + options.tof_mode); + sophiread::timedSaveTOFImagingToTIFF(options.output_tof_imaging, tof_images, config->getTOFBinEdges(), + options.tof_filename_base); } - return 0; + // Save hits and events + if (!options.output_hits.empty()) { + sophiread::timedSaveHitsToHDF5(options.output_hits, batches); + } + + if (!options.output_events.empty()) { + sophiread::timedSaveEventsToHDF5(options.output_events, batches); + } + + } catch (const std::exception& e) { + spdlog::error("Error: {}", e.what()); + return 1; + } + + return 0; } diff --git a/sophiread/SophireadCLI/src/sophiread_core.cpp b/sophiread/SophireadCLI/src/sophiread_core.cpp index 0b11e83..0eb3bf2 100644 --- a/sophiread/SophireadCLI/src/sophiread_core.cpp +++ b/sophiread/SophireadCLI/src/sophiread_core.cpp @@ -20,14 +20,17 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ +#include "sophiread_core.h" + +#include #include +#include + #include -#include #include -#include -#include +#include + #include "disk_io.h" -#include "sophiread_core.h" namespace sophiread { @@ -37,7 +40,7 @@ namespace sophiread { * @param[in] in_tpx3 * @return std::vector */ -std::vector timedReadDataToCharVec(const std::string &in_tpx3) { +std::vector timedReadDataToCharVec(const std::string& in_tpx3) { auto start = std::chrono::high_resolution_clock::now(); auto raw_data = readTPX3RawToCharVec(in_tpx3); auto end = std::chrono::high_resolution_clock::now(); @@ -53,7 +56,7 @@ std::vector timedReadDataToCharVec(const std::string &in_tpx3) { * @param[in] rawdata * @return std::vector */ -std::vector timedFindTPX3H(const std::vector &rawdata) { +std::vector timedFindTPX3H(const std::vector& rawdata) { auto start = std::chrono::high_resolution_clock::now(); auto batches = findTPX3H(rawdata); auto end = std::chrono::high_resolution_clock::now(); @@ -69,12 +72,12 @@ std::vector timedFindTPX3H(const std::vector &rawdata) { * @param[in, out] batches * @param[in] rawdata */ -void timedLocateTimeStamp(std::vector &batches, const std::vector &rawdata) { +void timedLocateTimeStamp(std::vector& batches, const std::vector& rawdata) { auto start = std::chrono::high_resolution_clock::now(); unsigned long tdc_timestamp = 0; unsigned long long gdc_timestamp = 0; unsigned long timer_lsb32 = 0; - for (auto &tpx3 : batches) { + for (auto& tpx3 : batches) { updateTimestamp(tpx3, rawdata, tdc_timestamp, gdc_timestamp, timer_lsb32); } auto end = std::chrono::high_resolution_clock::now(); @@ -89,15 +92,15 @@ void timedLocateTimeStamp(std::vector &batches, const std::vector &r * @param[in] rawdata * @param[in] config */ -void timedProcessing(std::vector &batches, const std::vector &raw_data, const IConfig &config) { +void timedProcessing(std::vector& batches, const std::vector& raw_data, const IConfig& config) { auto start = std::chrono::high_resolution_clock::now(); - tbb::parallel_for(tbb::blocked_range(0, batches.size()), [&](const tbb::blocked_range &r) { + tbb::parallel_for(tbb::blocked_range(0, batches.size()), [&](const tbb::blocked_range& r) { // Define ABS algorithm with user-defined parameters for each thread auto abs_alg_mt = std::make_unique(config.getABSRadius(), config.getABSMinClusterSize(), config.getABSSpiderTimeRange()); for (size_t i = r.begin(); i != r.end(); ++i) { - auto &tpx3 = batches[i]; + auto& tpx3 = batches[i]; extractHits(tpx3, raw_data); abs_alg_mt->reset(); @@ -118,11 +121,11 @@ void timedProcessing(std::vector &batches, const std::vector &raw_da * @param[in] out_hits * @param[in] hits */ -void timedSaveHitsToHDF5(const std::string &out_hits, std::vector &batches) { +void timedSaveHitsToHDF5(const std::string& out_hits, std::vector& batches) { auto start = std::chrono::high_resolution_clock::now(); // move all hits into a single vector std::vector hits; - for (const auto &tpx3 : batches) { + for (const auto& tpx3 : batches) { auto tpx3_hits = tpx3.hits; hits.insert(hits.end(), tpx3_hits.begin(), tpx3_hits.end()); } @@ -135,15 +138,15 @@ void timedSaveHitsToHDF5(const std::string &out_hits, std::vector &batches /** * @brief Timed save events to HDF5. - * + * * @param[in] out_events * @param[in] batches */ -void timedSaveEventsToHDF5(const std::string &out_events, std::vector &batches) { +void timedSaveEventsToHDF5(const std::string& out_events, std::vector& batches) { auto start = std::chrono::high_resolution_clock::now(); // move all events into a single vector std::vector events; - for (const auto &tpx3 : batches) { + for (const auto& tpx3 : batches) { auto tpx3_events = tpx3.neutrons; events.insert(events.end(), tpx3_events.begin(), tpx3_events.end()); } @@ -157,226 +160,224 @@ void timedSaveEventsToHDF5(const std::string &out_events, std::vector &bat /** * @brief Timed create TOF images. * - * This function creates time-of-flight (TOF) images based on the provided batches of TPX3 data. The TOF images are 2D histograms that represent the distribution of neutron events in space for different TOF bins. The function takes into account the super resolution, TOF bin edges, and the mode of operation (hit or neutron) to generate the TOF images. + * This function creates time-of-flight (TOF) images based on the provided batches of TPX3 data. The TOF images are 2D + * histograms that represent the distribution of neutron events in space for different TOF bins. The function takes into + * account the super resolution, TOF bin edges, and the mode of operation (hit or neutron) to generate the TOF images. * * @param[in] batches The vector of TPX3 batches containing the neutron events. * @param[in] super_resolution The super resolution factor used to calculate the dimensions of each 2D histogram. * @param[in] tof_bin_edges The vector of TOF bin edges used to determine the TOF bin for each neutron event. * @param[in] mode The mode of operation, either "hit" or "neutron", which determines the type of events to process. - * @return std::vector>> The vector of TOF images, where each TOF image is a 2D histogram representing the distribution of neutron events in space for a specific TOF bin. + * @return std::vector>> The vector of TOF images, where each TOF image is a 2D + * histogram representing the distribution of neutron events in space for a specific TOF bin. */ -std::vector>> timedCreateTOFImages( - const std::vector& batches, - double super_resolution, - const std::vector& tof_bin_edges, - const std::string& mode) { - - auto start = std::chrono::high_resolution_clock::now(); - - // Initialize the TOF images container - std::vector>> tof_images(tof_bin_edges.size() - 1); - - // Sanity checks - if (tof_bin_edges.size() < 2) { - spdlog::error("Invalid TOF bin edges: at least 2 edges are required"); - return {}; - } - if (batches.empty()) { - spdlog::error("No batches to process"); - return tof_images; - } - - // Calculate the dimensions of each 2D histogram based on super_resolution - // one chip: 0-255 pixel pos - // gap: 5 - // total: 0-255 + 5 + 0-255 -> 517 (TPX3@VENUS only) - int dim_x = static_cast(517 * super_resolution); - int dim_y = static_cast(517 * super_resolution); - - spdlog::debug("Creating TOF images with dimensions: {} x {}", dim_x, dim_y); - spdlog::debug("tof_bin_edges size: {}", tof_bin_edges.size()); - if (!tof_bin_edges.empty()) { - spdlog::debug("First bin edge: {}, Last bin edge: {}", tof_bin_edges.front(), tof_bin_edges.back()); - } - - // Initialize each TOF bin's 2D histogram - for (auto& tof_image : tof_images) { - tof_image.resize(dim_y, std::vector(dim_x, 0)); - } +std::vector>> timedCreateTOFImages(const std::vector& batches, + double super_resolution, + const std::vector& tof_bin_edges, + const std::string& mode) { + auto start = std::chrono::high_resolution_clock::now(); - // Process neutrons from all batches - size_t total_entries = 0; - size_t binned_entries = 0; + // Initialize the TOF images container + std::vector>> tof_images(tof_bin_edges.size() - 1); - for (size_t batch_index = 0; batch_index < batches.size(); ++batch_index) { - const auto& batch = batches[batch_index]; - spdlog::debug("Processing batch {}", batch_index); + // Sanity checks + if (tof_bin_edges.size() < 2) { + spdlog::error("Invalid TOF bin edges: at least 2 edges are required"); + return {}; + } + if (batches.empty()) { + spdlog::error("No batches to process"); + return tof_images; + } - std::vector entries; - if (mode == "hit") { - entries.reserve(batch.hits.size()); - for (const auto& hit : batch.hits) { - entries.push_back(static_cast(&hit)); - } - } else { - entries.reserve(batch.neutrons.size()); - for (const auto& neutron : batch.neutrons) { - entries.push_back(static_cast(&neutron)); - } - } - - if (entries.empty()) { - spdlog::debug("Batch {} is empty", batch_index); - continue; - } + // Calculate the dimensions of each 2D histogram based on super_resolution + // one chip: 0-255 pixel pos + // gap: 5 + // total: 0-255 + 5 + 0-255 -> 517 (TPX3@VENUS only) + int dim_x = static_cast(517 * super_resolution); + int dim_y = static_cast(517 * super_resolution); + + spdlog::debug("Creating TOF images with dimensions: {} x {}", dim_x, dim_y); + spdlog::debug("tof_bin_edges size: {}", tof_bin_edges.size()); + if (!tof_bin_edges.empty()) { + spdlog::debug("First bin edge: {}, Last bin edge: {}", tof_bin_edges.front(), tof_bin_edges.back()); + } - for (const auto& entry : entries) { - total_entries++; - double tof_ns = entry->iGetTOF_ns(); - - // Find the correct TOF bin - // NOTE: tof_bin_edges are in sec, and tof_ns are in nano secs - spdlog::debug("tof_ns: {}, tof_ns/1e9: {}", tof_ns, tof_ns/1e9); - auto it = std::lower_bound(tof_bin_edges.begin(), tof_bin_edges.end(), tof_ns/1e9); - if (it != tof_bin_edges.begin()) { - size_t bin_index = std::distance(tof_bin_edges.begin(), it) - 1; - - // Calculate the x and y indices in the 2D histogram - int x = static_cast(entry->iGetX() * super_resolution); - int y = static_cast(entry->iGetY() * super_resolution); - - // Ensure x and y are within bounds - if (x >= 0 && x < dim_x && y >= 0 && y < dim_y) { - // Increment the count in the appropriate bin and position - tof_images[bin_index][y][x]++; - binned_entries++; - } - } + // Initialize each TOF bin's 2D histogram + for (auto& tof_image : tof_images) { + tof_image.resize(dim_y, std::vector(dim_x, 0)); + } + + // Process neutrons from all batches + size_t total_entries = 0; + size_t binned_entries = 0; + + for (size_t batch_index = 0; batch_index < batches.size(); ++batch_index) { + const auto& batch = batches[batch_index]; + spdlog::debug("Processing batch {}", batch_index); + + std::vector entries; + if (mode == "hit") { + entries.reserve(batch.hits.size()); + for (const auto& hit : batch.hits) { + entries.push_back(static_cast(&hit)); + } + } else { + entries.reserve(batch.neutrons.size()); + for (const auto& neutron : batch.neutrons) { + entries.push_back(static_cast(&neutron)); + } + } + + if (entries.empty()) { + spdlog::debug("Batch {} is empty", batch_index); + continue; + } + + for (const auto& entry : entries) { + total_entries++; + double tof_ns = entry->iGetTOF_ns(); + + // Find the correct TOF bin + // NOTE: tof_bin_edges are in sec, and tof_ns are in nano secs + spdlog::debug("tof_ns: {}, tof_ns/1e9: {}", tof_ns, tof_ns / 1e9); + auto it = std::lower_bound(tof_bin_edges.begin(), tof_bin_edges.end(), tof_ns / 1e9); + if (it != tof_bin_edges.begin()) { + size_t bin_index = std::distance(tof_bin_edges.begin(), it) - 1; + + // Calculate the x and y indices in the 2D histogram + int x = static_cast(entry->iGetX() * super_resolution); + int y = static_cast(entry->iGetY() * super_resolution); + + // Ensure x and y are within bounds + if (x >= 0 && x < dim_x && y >= 0 && y < dim_y) { + // Increment the count in the appropriate bin and position + tof_images[bin_index][y][x]++; + binned_entries++; } + } } + } - auto end = std::chrono::high_resolution_clock::now(); - auto elapsed = std::chrono::duration_cast(end - start).count(); - spdlog::info("TOF image creation time: {} s", elapsed / 1e6); - spdlog::info("Total entries: {}, Binned entries: {}", total_entries, binned_entries); + auto end = std::chrono::high_resolution_clock::now(); + auto elapsed = std::chrono::duration_cast(end - start).count(); + spdlog::info("TOF image creation time: {} s", elapsed / 1e6); + spdlog::info("Total entries: {}, Binned entries: {}", total_entries, binned_entries); - return tof_images; + return tof_images; } /** * @brief Timed save TOF imaging to TIFF. - * + * * @param[in] out_tof_imaging * @param[in] batches * @param[in] tof_bin_edges * @param[in] tof_filename_base */ -void timedSaveTOFImagingToTIFF( - const std::string& out_tof_imaging, - const std::vector>>& tof_images, - const std::vector& tof_bin_edges, - const std::string& tof_filename_base) -{ - auto start = std::chrono::high_resolution_clock::now(); - - // 1. Create output directory if it doesn't exist - if (!std::filesystem::exists(out_tof_imaging)) { - std::filesystem::create_directories(out_tof_imaging); - spdlog::info("Created output directory: {}", out_tof_imaging); - } +void timedSaveTOFImagingToTIFF(const std::string& out_tof_imaging, + const std::vector>>& tof_images, + const std::vector& tof_bin_edges, const std::string& tof_filename_base) { + auto start = std::chrono::high_resolution_clock::now(); - // 2. Initialize vector for spectral data - std::vector spectral_counts(tof_images.size(), 0); - - // 3. Iterate through each TOF bin and save TIFF files - tbb::parallel_for(tbb::blocked_range(0, tof_images.size()), [&](const tbb::blocked_range& range) { - for (size_t bin = range.begin(); bin < range.end(); ++bin) { - // Construct filename - std::string filename = fmt::format("{}/{}_bin_{:04d}.tiff", out_tof_imaging, tof_filename_base, bin + 1); - - // prepare container and fill with current hist2d - uint32_t width = tof_images[bin][0].size(); - uint32_t height = tof_images[bin].size(); - std::vector> accumulated_image = tof_images[bin]; - - // check if file already exist - if (std::filesystem::exists(filename)) { - TIFF* existing_tif = TIFFOpen(filename.c_str(), "r"); - if (existing_tif) { - uint32_t existing_width, existing_height; - TIFFGetField(existing_tif, TIFFTAG_IMAGEWIDTH, &existing_width); - TIFFGetField(existing_tif, TIFFTAG_IMAGELENGTH, &existing_height); - - if (existing_width == width && existing_height == height) { - // Dimensions match, proceed with accumulation - for (uint32_t row = 0; row < height; ++row) { - std::vector scanline(width); - TIFFReadScanline(existing_tif, scanline.data(), row); - for (uint32_t col = 0; col < width; ++col) { - accumulated_image[row][col] += scanline[col]; - } - } - spdlog::debug("Accumulated counts for existing file: {}", filename); - } else { - spdlog::error("Dimension mismatch for file: {}. Expected {}x{}, got {}x{}. Overwriting.", - filename, width, height, existing_width, existing_height); - } - TIFFClose(existing_tif); - } else { - spdlog::error("Failed to open existing TIFF file for reading: {}", filename); - } - } + // 1. Create output directory if it doesn't exist + if (!std::filesystem::exists(out_tof_imaging)) { + std::filesystem::create_directories(out_tof_imaging); + spdlog::info("Created output directory: {}", out_tof_imaging); + } - // Write or update TIFF file - TIFF* tif = TIFFOpen(filename.c_str(), "w"); - if (tif) { - uint32_t width = tof_images[bin][0].size(); - uint32_t height = tof_images[bin].size(); - - TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, width); - TIFFSetField(tif, TIFFTAG_IMAGELENGTH, height); - TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 1); - TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 32); - TIFFSetField(tif, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT); - TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG); - TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK); - - for (uint32_t row = 0; row < height; ++row) { - TIFFWriteScanline(tif, accumulated_image[row].data(), row); + // 2. Initialize vector for spectral data + std::vector spectral_counts(tof_images.size(), 0); + + // 3. Iterate through each TOF bin and save TIFF files + tbb::parallel_for(tbb::blocked_range(0, tof_images.size()), [&](const tbb::blocked_range& range) { + for (size_t bin = range.begin(); bin < range.end(); ++bin) { + // Construct filename + std::string filename = fmt::format("{}/{}_bin_{:04d}.tiff", out_tof_imaging, tof_filename_base, bin + 1); + + // prepare container and fill with current hist2d + uint32_t width = tof_images[bin][0].size(); + uint32_t height = tof_images[bin].size(); + std::vector> accumulated_image = tof_images[bin]; + + // check if file already exist + if (std::filesystem::exists(filename)) { + TIFF* existing_tif = TIFFOpen(filename.c_str(), "r"); + if (existing_tif) { + uint32_t existing_width, existing_height; + TIFFGetField(existing_tif, TIFFTAG_IMAGEWIDTH, &existing_width); + TIFFGetField(existing_tif, TIFFTAG_IMAGELENGTH, &existing_height); + + if (existing_width == width && existing_height == height) { + // Dimensions match, proceed with accumulation + for (uint32_t row = 0; row < height; ++row) { + std::vector scanline(width); + TIFFReadScanline(existing_tif, scanline.data(), row); + for (uint32_t col = 0; col < width; ++col) { + accumulated_image[row][col] += scanline[col]; } - - TIFFClose(tif); - spdlog::debug("Wrote TIFF file: {}", filename); + } + spdlog::debug("Accumulated counts for existing file: {}", filename); } else { - spdlog::error("Failed to open TIFF file for writing: {}", filename); + spdlog::error("Dimension mismatch for file: {}. Expected {}x{}, got {}x{}. Overwriting.", filename, width, + height, existing_width, existing_height); } - - // Accumulate counts for spectral file - spectral_counts[bin] = std::accumulate(accumulated_image.begin(), accumulated_image.end(), 0ULL, - [](unsigned long long sum, const std::vector& row) { - return sum + std::accumulate(row.begin(), row.end(), 0ULL); - }); + TIFFClose(existing_tif); + } else { + spdlog::error("Failed to open existing TIFF file for reading: {}", filename); + } } - }); - - // 4. Write spectral file - std::string spectral_filename = fmt::format("{}/{}_Spectra.txt", out_tof_imaging, tof_filename_base); - // Write spectral data to file - std::ofstream spectral_file(spectral_filename); - if (spectral_file.is_open()) { - spectral_file << "shutter_time,counts\n"; - for (size_t bin = 0; bin < tof_bin_edges.size() - 1; ++bin) { - spectral_file << tof_bin_edges[bin + 1] << "," << spectral_counts[bin] << "\n"; + + // Write or update TIFF file + TIFF* tif = TIFFOpen(filename.c_str(), "w"); + if (tif) { + uint32_t width = tof_images[bin][0].size(); + uint32_t height = tof_images[bin].size(); + + TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, width); + TIFFSetField(tif, TIFFTAG_IMAGELENGTH, height); + TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 1); + TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 32); + TIFFSetField(tif, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT); + TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG); + TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK); + + for (uint32_t row = 0; row < height; ++row) { + TIFFWriteScanline(tif, accumulated_image[row].data(), row); } - spectral_file.close(); - spdlog::info("Wrote spectral file: {}", spectral_filename); - } else { - spdlog::error("Failed to open spectra file for writing: {}", spectral_filename); + + TIFFClose(tif); + spdlog::debug("Wrote TIFF file: {}", filename); + } else { + spdlog::error("Failed to open TIFF file for writing: {}", filename); + } + + // Accumulate counts for spectral file + spectral_counts[bin] = std::accumulate(accumulated_image.begin(), accumulated_image.end(), 0ULL, + [](unsigned long long sum, const std::vector& row) { + return sum + std::accumulate(row.begin(), row.end(), 0ULL); + }); + } + }); + + // 4. Write spectral file + std::string spectral_filename = fmt::format("{}/{}_Spectra.txt", out_tof_imaging, tof_filename_base); + // Write spectral data to file + std::ofstream spectral_file(spectral_filename); + if (spectral_file.is_open()) { + spectral_file << "shutter_time,counts\n"; + for (size_t bin = 0; bin < tof_bin_edges.size() - 1; ++bin) { + spectral_file << tof_bin_edges[bin + 1] << "," << spectral_counts[bin] << "\n"; } + spectral_file.close(); + spdlog::info("Wrote spectral file: {}", spectral_filename); + } else { + spdlog::error("Failed to open spectra file for writing: {}", spectral_filename); + } - auto end = std::chrono::high_resolution_clock::now(); - auto duration = std::chrono::duration_cast(end - start); - spdlog::info("TIFF and spectra file writing completed in {} ms", duration.count()); + auto end = std::chrono::high_resolution_clock::now(); + auto duration = std::chrono::duration_cast(end - start); + spdlog::info("TIFF and spectra file writing completed in {} ms", duration.count()); } -} // namespace sophiread \ No newline at end of file +} // namespace sophiread \ No newline at end of file diff --git a/sophiread/SophireadCLI/src/user_config.cpp b/sophiread/SophireadCLI/src/user_config.cpp index e2fd5e5..8b7d0c0 100644 --- a/sophiread/SophireadCLI/src/user_config.cpp +++ b/sophiread/SophireadCLI/src/user_config.cpp @@ -31,13 +31,23 @@ /** * @brief Construct a new UserConfig object with default values. */ -UserConfig::UserConfig() : m_abs_radius(5.0), m_abs_min_cluster_size(1), m_abs_spider_time_range(75), m_tof_binning(), m_super_resolution(1.0) {} +UserConfig::UserConfig() + : m_abs_radius(5.0), + m_abs_min_cluster_size(1), + m_abs_spider_time_range(75), + m_tof_binning(), + m_super_resolution(1.0) {} /** * @brief Construct a new UserConfig object with user-defined values */ -UserConfig::UserConfig(double abs_radius, unsigned long int abs_min_cluster_size, unsigned long int abs_spider_time_range) - : m_abs_radius(abs_radius), m_abs_min_cluster_size(abs_min_cluster_size), m_abs_spider_time_range(abs_spider_time_range), m_tof_binning(), m_super_resolution(1.0) {} +UserConfig::UserConfig(double abs_radius, unsigned long int abs_min_cluster_size, + unsigned long int abs_spider_time_range) + : m_abs_radius(abs_radius), + m_abs_min_cluster_size(abs_min_cluster_size), + m_abs_spider_time_range(abs_spider_time_range), + m_tof_binning(), + m_super_resolution(1.0) {} /** * @brief Helper function to convert a user configuration to a string for console output. @@ -46,8 +56,7 @@ UserConfig::UserConfig(double abs_radius, unsigned long int abs_min_cluster_size */ std::string UserConfig::toString() const { std::stringstream ss; - ss << "ABS: radius=" << m_abs_radius - << ", min_cluster_size=" << m_abs_min_cluster_size + ss << "ABS: radius=" << m_abs_radius << ", min_cluster_size=" << m_abs_min_cluster_size << ", spider_time_range=" << m_abs_spider_time_range; // Add TOF binning information @@ -118,8 +127,7 @@ UserConfig parseUserDefinedConfigurationFile(const std::string& filepath) { } else if (name == "tof_max") { double value; ss >> value; - } - else { + } else { spdlog::warn("Unknown parameter {} in the user-defined configuration file.", name); } } diff --git a/sophiread/SophireadCLI/src/venus_auto_reducer.cpp b/sophiread/SophireadCLI/src/venus_auto_reducer.cpp index 445999c..133da1e 100644 --- a/sophiread/SophireadCLI/src/venus_auto_reducer.cpp +++ b/sophiread/SophireadCLI/src/venus_auto_reducer.cpp @@ -4,7 +4,7 @@ * @brief Auto reducer demo application for VENUS@SNS * @version 0.1 * @date 2024-08-21 - * + * * @copyright Copyright (c) 2024 * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -19,238 +19,242 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ +#include +#include +#include + +#include +#include +#include #include #include -#include #include -#include -#include -#include -#include #include -#include "sophiread_core.h" +#include + #include "json_config_parser.h" -#include -#include +#include "sophiread_core.h" namespace fs = std::filesystem; struct ProgramOptions { - std::string input_dir; - std::string output_dir; - std::string config_file; - std::string tiff_base = "tof_image"; - std::string tof_mode = "neutron"; - int check_interval = 5; - bool verbose = false; - bool debug = false; + std::string input_dir; + std::string output_dir; + std::string config_file; + std::string tiff_base = "tof_image"; + std::string tof_mode = "neutron"; + int check_interval = 5; + bool verbose = false; + bool debug = false; }; void print_usage(const char* program_name) { - spdlog::info("Usage: {} -i -o [-u ] [-f ] [-m ] [-c ] [-v] [-d]", program_name); - spdlog::info("Options:"); - spdlog::info(" -i Input directory with TPX3 files"); - spdlog::info(" -o Output directory for TIFF files"); - spdlog::info(" -u User configuration JSON file (optional)"); - spdlog::info(" -f Base name for TIFF files (default: tof_image)"); - spdlog::info(" -m TOF mode: 'hit' or 'neutron' (default: neutron)"); - spdlog::info(" -c Check interval in seconds (default: 5)"); - spdlog::info(" -d Debug output"); - spdlog::info(" -v Verbose output"); + spdlog::info( + "Usage: {} -i -o [-u ] [-f ] [-m ] [-c " + "] [-v] [-d]", + program_name); + spdlog::info("Options:"); + spdlog::info(" -i Input directory with TPX3 files"); + spdlog::info(" -o Output directory for TIFF files"); + spdlog::info(" -u User configuration JSON file (optional)"); + spdlog::info(" -f Base name for TIFF files (default: tof_image)"); + spdlog::info(" -m TOF mode: 'hit' or 'neutron' (default: neutron)"); + spdlog::info(" -c Check interval in seconds (default: 5)"); + spdlog::info(" -d Debug output"); + spdlog::info(" -v Verbose output"); } ProgramOptions parse_arguments(int argc, char* argv[]) { - ProgramOptions options; - int opt; - - while ((opt = getopt(argc, argv, "i:o:u:f:m:c:dv")) != -1) { - switch (opt) { - case 'i': - options.input_dir = optarg; - break; - case 'o': - options.output_dir = optarg; - break; - case 'u': - options.config_file = optarg; - break; - case 'f': - options.tiff_base = optarg; - break; - case 'm': - options.tof_mode = optarg; - break; - case 'c': - options.check_interval = std::stoi(optarg); - break; - case 'd': - options.debug = true; - case 'v': - options.verbose = true; - break; - default: - print_usage(argv[0]); - throw std::runtime_error("Invalid argument"); - } - } + ProgramOptions options; + int opt; - // Validate required arguments - if (options.input_dir.empty() || options.output_dir.empty()) { + while ((opt = getopt(argc, argv, "i:o:u:f:m:c:dv")) != -1) { + switch (opt) { + case 'i': + options.input_dir = optarg; + break; + case 'o': + options.output_dir = optarg; + break; + case 'u': + options.config_file = optarg; + break; + case 'f': + options.tiff_base = optarg; + break; + case 'm': + options.tof_mode = optarg; + break; + case 'c': + options.check_interval = std::stoi(optarg); + break; + case 'd': + options.debug = true; + case 'v': + options.verbose = true; + break; + default: print_usage(argv[0]); - throw std::runtime_error("Missing required arguments"); + throw std::runtime_error("Invalid argument"); } + } - // Validate tof_mode - if (options.tof_mode != "hit" && options.tof_mode != "neutron") { - throw std::runtime_error("Invalid TOF mode. Use 'hit' or 'neutron'."); - } + // Validate required arguments + if (options.input_dir.empty() || options.output_dir.empty()) { + print_usage(argv[0]); + throw std::runtime_error("Missing required arguments"); + } - // Validate check_interval - if (options.check_interval <= 0) { - throw std::runtime_error("Check interval must be a positive integer."); - } + // Validate tof_mode + if (options.tof_mode != "hit" && options.tof_mode != "neutron") { + throw std::runtime_error("Invalid TOF mode. Use 'hit' or 'neutron'."); + } + + // Validate check_interval + if (options.check_interval <= 0) { + throw std::runtime_error("Check interval must be a positive integer."); + } - return options; + return options; } /** * @brief Process all existing tpx3 files - * - * @param[in] input_dir - * @param[in] output_dir - * @param[in] tiff_base - * @param[in] tof_mode - * @param[in] config + * + * @param[in] input_dir + * @param[in] output_dir + * @param[in] tiff_base + * @param[in] tof_mode + * @param[in] config * @param[in, out] processed_files */ -void process_existing_files(const std::string& input_dir, const std::string& output_dir, - const std::string& tiff_base, const std::string& tof_mode, - const IConfig& config, std::unordered_set& processed_files) { - spdlog::info("Processing existing files in {}", input_dir); +void process_existing_files(const std::string& input_dir, const std::string& output_dir, const std::string& tiff_base, + const std::string& tof_mode, const IConfig& config, + std::unordered_set& processed_files) { + spdlog::info("Processing existing files in {}", input_dir); - // NOTE: we need to process files sequentially as we are accumulating the counts to the - // same set of tiff files in the output folder - for (const auto& entry : fs::directory_iterator(input_dir)) { - if (entry.is_regular_file() && entry.path().extension() == ".tpx3") { - std::string filename = entry.path().stem().string(); - - if (processed_files.find(filename) != processed_files.end()) { - spdlog::debug("Skipping already processed file: {}", filename); - continue; - } - - spdlog::info("Processing file: {}", entry.path().string()); - - try { - // Read the TPX3 file - auto raw_data = sophiread::timedReadDataToCharVec(entry.path().string()); - - // Find TPX3 headers - auto batches = sophiread::timedFindTPX3H(raw_data); - - // Process the data - sophiread::timedLocateTimeStamp(batches, raw_data); - sophiread::timedProcessing(batches, raw_data, config); - - // Generate output file name - std::string output_file = fs::path(output_dir) / (tiff_base + "_bin_xxxx.tiff"); - - // Create TOF images - auto tof_images = sophiread::timedCreateTOFImages(batches, config.getSuperResolution(), config.getTOFBinEdges(), tof_mode); - - // Save TOF images - sophiread::timedSaveTOFImagingToTIFF(output_dir, tof_images, config.getTOFBinEdges(), tiff_base); - - spdlog::info("Processed and saved: {}", output_file); - - // record processed file - processed_files.insert(entry.path().stem().string()); - } catch (const std::exception& e) { - spdlog::error("Error processing file {}: {}", entry.path().string(), e.what()); - } - } + // NOTE: we need to process files sequentially as we are accumulating the counts to the + // same set of tiff files in the output folder + for (const auto& entry : fs::directory_iterator(input_dir)) { + if (entry.is_regular_file() && entry.path().extension() == ".tpx3") { + std::string filename = entry.path().stem().string(); + + if (processed_files.find(filename) != processed_files.end()) { + spdlog::debug("Skipping already processed file: {}", filename); + continue; + } + + spdlog::info("Processing file: {}", entry.path().string()); + + try { + // Read the TPX3 file + auto raw_data = sophiread::timedReadDataToCharVec(entry.path().string()); + + // Find TPX3 headers + auto batches = sophiread::timedFindTPX3H(raw_data); + + // Process the data + sophiread::timedLocateTimeStamp(batches, raw_data); + sophiread::timedProcessing(batches, raw_data, config); + + // Generate output file name + std::string output_file = fs::path(output_dir) / (tiff_base + "_bin_xxxx.tiff"); + + // Create TOF images + auto tof_images = + sophiread::timedCreateTOFImages(batches, config.getSuperResolution(), config.getTOFBinEdges(), tof_mode); + + // Save TOF images + sophiread::timedSaveTOFImagingToTIFF(output_dir, tof_images, config.getTOFBinEdges(), tiff_base); + + spdlog::info("Processed and saved: {}", output_file); + + // record processed file + processed_files.insert(entry.path().stem().string()); + } catch (const std::exception& e) { + spdlog::error("Error processing file {}: {}", entry.path().string(), e.what()); + } } + } - // Create a string to hold the formatted output - std::ostringstream oss; - oss << "Processed files: ["; + // Create a string to hold the formatted output + std::ostringstream oss; + oss << "Processed files: ["; - // Loop through the set and concatenate elements - for (auto it = processed_files.begin(); it != processed_files.end(); ++it) { - if (it != processed_files.begin()) { - oss << ", "; // Add a comma before each element except the first - } - oss << *it; + // Loop through the set and concatenate elements + for (auto it = processed_files.begin(); it != processed_files.end(); ++it) { + if (it != processed_files.begin()) { + oss << ", "; // Add a comma before each element except the first } - oss << "]"; - spdlog::info(oss.str()); + oss << *it; + } + oss << "]"; + spdlog::info(oss.str()); } -void monitor_directory(const std::string& input_dir, const std::string& output_dir, - const std::string& tiff_base, const std::string& tof_mode, - const IConfig& config, std::unordered_set& processed_files, - int check_interval) { - spdlog::info("Starting directory monitoring: {}", input_dir); - spdlog::info("Check interval: {} seconds", check_interval); - - while (true) { - // Check for *.nxs.h5 file - for (const auto& entry : fs::directory_iterator(input_dir)) { - if (entry.is_regular_file() && entry.path().extension() == ".h5" && - entry.path().stem().extension() == ".nxs") { - spdlog::info("Found *.nxs.h5 file. Stopping monitoring."); - return; - } - } - - // Process any new files - process_existing_files(input_dir, output_dir, tiff_base, tof_mode, config, processed_files); - - // Wait before next check - std::this_thread::sleep_for(std::chrono::seconds(check_interval)); +void monitor_directory(const std::string& input_dir, const std::string& output_dir, const std::string& tiff_base, + const std::string& tof_mode, const IConfig& config, + std::unordered_set& processed_files, int check_interval) { + spdlog::info("Starting directory monitoring: {}", input_dir); + spdlog::info("Check interval: {} seconds", check_interval); + + while (true) { + // Check for *.nxs.h5 file + for (const auto& entry : fs::directory_iterator(input_dir)) { + if (entry.is_regular_file() && entry.path().extension() == ".h5" && entry.path().stem().extension() == ".nxs") { + spdlog::info("Found *.nxs.h5 file. Stopping monitoring."); + return; + } } + + // Process any new files + process_existing_files(input_dir, output_dir, tiff_base, tof_mode, config, processed_files); + + // Wait before next check + std::this_thread::sleep_for(std::chrono::seconds(check_interval)); + } } int main(int argc, char* argv[]) { - try { - ProgramOptions options = parse_arguments(argc, argv); - - // Set logging level based on verbose flag - if (options.debug){ - spdlog::set_level(spdlog::level::debug); - spdlog::debug("Debug logging enabled"); - } else if (options.verbose) { - spdlog::set_level(spdlog::level::info); - } else { - spdlog::set_level(spdlog::level::warn); - } - - spdlog::info("Input directory: {}", options.input_dir); - spdlog::info("Output directory: {}", options.output_dir); - spdlog::info("Config file: {}", options.config_file); - spdlog::info("TIFF base name: {}", options.tiff_base); - spdlog::info("TOF mode: {}", options.tof_mode); - - // Load configuration - std::unique_ptr config; - if (!options.config_file.empty()) { - spdlog::info("Config file: {}", options.config_file); - config = std::make_unique(JSONConfigParser::fromFile(options.config_file)); - } else { - spdlog::info("Using default configuration"); - config = std::make_unique(JSONConfigParser::createDefault()); - } - - spdlog::info("Configuration: {}", config->toString()); - - // Process existing files - std::unordered_set processed_files; - monitor_directory(options.input_dir, options.output_dir, options.tiff_base, - options.tof_mode, *config, processed_files, options.check_interval); - - } catch (const std::exception& e) { - spdlog::error("Error: {}", e.what()); - return 1; + try { + ProgramOptions options = parse_arguments(argc, argv); + + // Set logging level based on verbose flag + if (options.debug) { + spdlog::set_level(spdlog::level::debug); + spdlog::debug("Debug logging enabled"); + } else if (options.verbose) { + spdlog::set_level(spdlog::level::info); + } else { + spdlog::set_level(spdlog::level::warn); } - return 0; + spdlog::info("Input directory: {}", options.input_dir); + spdlog::info("Output directory: {}", options.output_dir); + spdlog::info("Config file: {}", options.config_file); + spdlog::info("TIFF base name: {}", options.tiff_base); + spdlog::info("TOF mode: {}", options.tof_mode); + + // Load configuration + std::unique_ptr config; + if (!options.config_file.empty()) { + spdlog::info("Config file: {}", options.config_file); + config = std::make_unique(JSONConfigParser::fromFile(options.config_file)); + } else { + spdlog::info("Using default configuration"); + config = std::make_unique(JSONConfigParser::createDefault()); + } + + spdlog::info("Configuration: {}", config->toString()); + + // Process existing files + std::unordered_set processed_files; + monitor_directory(options.input_dir, options.output_dir, options.tiff_base, options.tof_mode, *config, + processed_files, options.check_interval); + + } catch (const std::exception& e) { + spdlog::error("Error: {}", e.what()); + return 1; + } + + return 0; } \ No newline at end of file diff --git a/sophiread/SophireadCLI/tests/test_json_config_parser.cpp b/sophiread/SophireadCLI/tests/test_json_config_parser.cpp index 6c70f5e..d8ece0f 100644 --- a/sophiread/SophireadCLI/tests/test_json_config_parser.cpp +++ b/sophiread/SophireadCLI/tests/test_json_config_parser.cpp @@ -3,7 +3,7 @@ * @author Chen Zhang (zhangc@orn.gov) * @brief Unit tests for JSONConfigParser class. * @date 2024-08-16 - * + * * @copyright Copyright (c) 2024 * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -18,17 +18,19 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -#include "json_config_parser.h" #include -#include + #include +#include + +#include "json_config_parser.h" class JSONConfigParserTest : public ::testing::Test { -protected: - void SetUp() override { - // This setup will be used for the uniform binning test - std::ofstream config_file("test_config_uniform.json"); - config_file << R"({ + protected: + void SetUp() override { + // This setup will be used for the uniform binning test + std::ofstream config_file("test_config_uniform.json"); + config_file << R"({ "abs": { "radius": 6.0, "min_cluster_size": 2, @@ -42,11 +44,11 @@ class JSONConfigParserTest : public ::testing::Test { "super_resolution": 2.0 } })"; - config_file.close(); + config_file.close(); - // Setup for custom binning test - std::ofstream config_file_custom("test_config_custom.json"); - config_file_custom << R"({ + // Setup for custom binning test + std::ofstream config_file_custom("test_config_custom.json"); + config_file_custom << R"({ "abs": { "radius": 7.0, "min_cluster_size": 3, @@ -56,84 +58,84 @@ class JSONConfigParserTest : public ::testing::Test { "bin_edges": [0, 0.001, 0.002, 0.005, 0.01, 0.0167] } })"; - config_file_custom.close(); + config_file_custom.close(); - // Setup for default values test - std::ofstream config_file_default("test_config_default.json"); - config_file_default << R"({ + // Setup for default values test + std::ofstream config_file_default("test_config_default.json"); + config_file_default << R"({ "abs": {} })"; - config_file_default.close(); - } - - void TearDown() override { - std::remove("test_config_uniform.json"); - std::remove("test_config_custom.json"); - std::remove("test_config_default.json"); - } + config_file_default.close(); + } + + void TearDown() override { + std::remove("test_config_uniform.json"); + std::remove("test_config_custom.json"); + std::remove("test_config_default.json"); + } }; TEST_F(JSONConfigParserTest, ParsesSuperResolutionCorrectly) { - auto config = JSONConfigParser::fromFile("test_config_uniform.json"); - EXPECT_DOUBLE_EQ(config.getSuperResolution(), 2.0); + auto config = JSONConfigParser::fromFile("test_config_uniform.json"); + EXPECT_DOUBLE_EQ(config.getSuperResolution(), 2.0); } TEST_F(JSONConfigParserTest, ParsesUniformConfigCorrectly) { - auto config = JSONConfigParser::fromFile("test_config_uniform.json"); - - EXPECT_DOUBLE_EQ(config.getABSRadius(), 6.0); - EXPECT_EQ(config.getABSMinClusterSize(), 2); - EXPECT_EQ(config.getABSSpiderTimeRange(), 80); - - auto bin_edges = config.getTOFBinEdges(); - EXPECT_EQ(bin_edges.size(), 1001); // 1000 bins + 1 - EXPECT_DOUBLE_EQ(bin_edges.front(), 0); - EXPECT_DOUBLE_EQ(bin_edges.back(), 0.0167); + auto config = JSONConfigParser::fromFile("test_config_uniform.json"); + + EXPECT_DOUBLE_EQ(config.getABSRadius(), 6.0); + EXPECT_EQ(config.getABSMinClusterSize(), 2); + EXPECT_EQ(config.getABSSpiderTimeRange(), 80); + + auto bin_edges = config.getTOFBinEdges(); + EXPECT_EQ(bin_edges.size(), 1001); // 1000 bins + 1 + EXPECT_DOUBLE_EQ(bin_edges.front(), 0); + EXPECT_DOUBLE_EQ(bin_edges.back(), 0.0167); } TEST_F(JSONConfigParserTest, ParsesCustomConfigCorrectly) { - auto config = JSONConfigParser::fromFile("test_config_custom.json"); - - EXPECT_DOUBLE_EQ(config.getABSRadius(), 7.0); - EXPECT_EQ(config.getABSMinClusterSize(), 3); - EXPECT_EQ(config.getABSSpiderTimeRange(), 85); - - auto bin_edges = config.getTOFBinEdges(); - EXPECT_EQ(bin_edges.size(), 6); - EXPECT_DOUBLE_EQ(bin_edges[0], 0); - EXPECT_DOUBLE_EQ(bin_edges[1], 0.001); - EXPECT_DOUBLE_EQ(bin_edges[2], 0.002); - EXPECT_DOUBLE_EQ(bin_edges[3], 0.005); - EXPECT_DOUBLE_EQ(bin_edges[4], 0.01); - EXPECT_DOUBLE_EQ(bin_edges[5], 0.0167); + auto config = JSONConfigParser::fromFile("test_config_custom.json"); + + EXPECT_DOUBLE_EQ(config.getABSRadius(), 7.0); + EXPECT_EQ(config.getABSMinClusterSize(), 3); + EXPECT_EQ(config.getABSSpiderTimeRange(), 85); + + auto bin_edges = config.getTOFBinEdges(); + EXPECT_EQ(bin_edges.size(), 6); + EXPECT_DOUBLE_EQ(bin_edges[0], 0); + EXPECT_DOUBLE_EQ(bin_edges[1], 0.001); + EXPECT_DOUBLE_EQ(bin_edges[2], 0.002); + EXPECT_DOUBLE_EQ(bin_edges[3], 0.005); + EXPECT_DOUBLE_EQ(bin_edges[4], 0.01); + EXPECT_DOUBLE_EQ(bin_edges[5], 0.0167); } TEST_F(JSONConfigParserTest, UsesDefaultValuesCorrectly) { - auto config = JSONConfigParser::fromFile("test_config_default.json"); - - EXPECT_DOUBLE_EQ(config.getABSRadius(), 5.0); - EXPECT_EQ(config.getABSMinClusterSize(), 1); - EXPECT_EQ(config.getABSSpiderTimeRange(), 75); - - auto bin_edges = config.getTOFBinEdges(); - EXPECT_EQ(bin_edges.size(), 1501); // 1500 bins + 1 - EXPECT_DOUBLE_EQ(bin_edges.front(), 0); - EXPECT_DOUBLE_EQ(bin_edges.back(), 0.0167); - EXPECT_DOUBLE_EQ(config.getSuperResolution(), 1.0); + auto config = JSONConfigParser::fromFile("test_config_default.json"); + + EXPECT_DOUBLE_EQ(config.getABSRadius(), 5.0); + EXPECT_EQ(config.getABSMinClusterSize(), 1); + EXPECT_EQ(config.getABSSpiderTimeRange(), 75); + + auto bin_edges = config.getTOFBinEdges(); + EXPECT_EQ(bin_edges.size(), 1501); // 1500 bins + 1 + EXPECT_DOUBLE_EQ(bin_edges.front(), 0); + EXPECT_DOUBLE_EQ(bin_edges.back(), 0.0167); + EXPECT_DOUBLE_EQ(config.getSuperResolution(), 1.0); } TEST_F(JSONConfigParserTest, ThrowsOnMissingFile) { - EXPECT_THROW(JSONConfigParser::fromFile("non_existent.json"), std::runtime_error); + EXPECT_THROW(JSONConfigParser::fromFile("non_existent.json"), std::runtime_error); } TEST_F(JSONConfigParserTest, ToStringMethodWorksCorrectly) { - auto config = JSONConfigParser::fromFile("test_config_uniform.json"); - std::string result = config.toString(); - - EXPECT_TRUE(result.find("radius=6") != std::string::npos); - EXPECT_TRUE(result.find("min_cluster_size=2") != std::string::npos); - EXPECT_TRUE(result.find("spider_time_range=80") != std::string::npos); - EXPECT_TRUE(result.find("TOF bins=1000") != std::string::npos); - EXPECT_TRUE(result.find("TOF max=16.7 ms") != std::string::npos); - EXPECT_TRUE(result.find("Super Resolution=2") != std::string::npos); + auto config = JSONConfigParser::fromFile("test_config_uniform.json"); + std::string result = config.toString(); + + EXPECT_TRUE(result.find("radius=6") != std::string::npos); + EXPECT_TRUE(result.find("min_cluster_size=2") != std::string::npos); + EXPECT_TRUE(result.find("spider_time_range=80") != std::string::npos); + EXPECT_TRUE(result.find("TOF bins=1000") != std::string::npos); + EXPECT_TRUE(result.find("TOF max=16.7 ms") != std::string::npos); + EXPECT_TRUE(result.find("Super Resolution=2") != std::string::npos); } diff --git a/sophiread/SophireadCLI/tests/test_sophiread_core.cpp b/sophiread/SophireadCLI/tests/test_sophiread_core.cpp index cd634cf..7ebfeeb 100644 --- a/sophiread/SophireadCLI/tests/test_sophiread_core.cpp +++ b/sophiread/SophireadCLI/tests/test_sophiread_core.cpp @@ -3,7 +3,7 @@ * @author: Chen Zhang (zhangc@orn.gov) * @brief: Unit tests for the Sophiread Core module. * @date: 2024-08-21 - * + * * @copyright Copyright (c) 2024 * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -19,133 +19,134 @@ * along with this program. If not, see . */ #include + #include #include -#include "sophiread_core.h" + #include "json_config_parser.h" +#include "sophiread_core.h" class SophireadCoreTest : public ::testing::Test { -protected: - std::vector generateMockTPX3Data(int num_packets = 10) { - std::vector data; - - for (int i = 0; i < num_packets; ++i) { - // Header packet - data.push_back('T'); - data.push_back('P'); - data.push_back('X'); - data.push_back('3'); - data.push_back(0); // chip_layout_type - data.push_back(0); // some random data - data.push_back(8); // data_packet_size (low byte) - data.push_back(0); // data_packet_size (high byte) - - // Data packet (8 bytes) - for (int j = 0; j < 8; ++j) { - data.push_back(0); - } - } - return data; - } + protected: + std::vector generateMockTPX3Data(int num_packets = 10) { + std::vector data; - std::vector generateMockTPX3Batches(int num_batches = 2, int hits_per_batch = 5) { - std::vector batches; - for (int i = 0; i < num_batches; ++i) { - TPX3 batch(i * 100, hits_per_batch, i % 3); // index, num_packets, chip_layout_type - - // Add mock hits - for (int j = 0; j < hits_per_batch; ++j) { - char mock_packet[8] = {0}; // Mock packet data - batch.emplace_back(mock_packet, 1000 + j, 2000 + j); - } - - // Add mock neutrons (derived from hits) - for (const auto& hit : batch.hits) { - batch.neutrons.emplace_back( - hit.getX(), hit.getY(), - hit.getTOF(), hit.getTOT(), - 1 // nHits, assume 1 hit per neutron for simplicity - ); - } - - batches.push_back(std::move(batch)); - } - return batches; - } + for (int i = 0; i < num_packets; ++i) { + // Header packet + data.push_back('T'); + data.push_back('P'); + data.push_back('X'); + data.push_back('3'); + data.push_back(0); // chip_layout_type + data.push_back(0); // some random data + data.push_back(8); // data_packet_size (low byte) + data.push_back(0); // data_packet_size (high byte) - void SetUp() override { - // Create a small test TPX3 file - auto test_data = generateMockTPX3Data(100); - std::ofstream test_file("test.tpx3", std::ios::binary); - test_file.write(test_data.data(), test_data.size()); - test_file.close(); + // Data packet (8 bytes) + for (int j = 0; j < 8; ++j) { + data.push_back(0); + } } + return data; + } + + std::vector generateMockTPX3Batches(int num_batches = 2, int hits_per_batch = 5) { + std::vector batches; + for (int i = 0; i < num_batches; ++i) { + TPX3 batch(i * 100, hits_per_batch, i % 3); // index, num_packets, chip_layout_type + + // Add mock hits + for (int j = 0; j < hits_per_batch; ++j) { + char mock_packet[8] = {0}; // Mock packet data + batch.emplace_back(mock_packet, 1000 + j, 2000 + j); + } - void TearDown() override { - // Remove the test file - std::filesystem::remove("test.tpx3"); + // Add mock neutrons (derived from hits) + for (const auto& hit : batch.hits) { + batch.neutrons.emplace_back(hit.getX(), hit.getY(), hit.getTOF(), hit.getTOT(), + 1 // nHits, assume 1 hit per neutron for simplicity + ); + } + + batches.push_back(std::move(batch)); } + return batches; + } + + void SetUp() override { + // Create a small test TPX3 file + auto test_data = generateMockTPX3Data(100); + std::ofstream test_file("test.tpx3", std::ios::binary); + test_file.write(test_data.data(), test_data.size()); + test_file.close(); + } + + void TearDown() override { + // Remove the test file + std::filesystem::remove("test.tpx3"); + } }; TEST_F(SophireadCoreTest, TimedReadDataToCharVec) { - auto data = sophiread::timedReadDataToCharVec("test.tpx3"); - EXPECT_EQ(data.size(), 1600); // 100 * (8 + 8) bytes + auto data = sophiread::timedReadDataToCharVec("test.tpx3"); + EXPECT_EQ(data.size(), 1600); // 100 * (8 + 8) bytes } TEST_F(SophireadCoreTest, TimedFindTPX3H) { - auto rawdata = generateMockTPX3Data(100); - auto batches = sophiread::timedFindTPX3H(rawdata); - EXPECT_EQ(batches.size(), 100); + auto rawdata = generateMockTPX3Data(100); + auto batches = sophiread::timedFindTPX3H(rawdata); + EXPECT_EQ(batches.size(), 100); } TEST_F(SophireadCoreTest, TimedLocateTimeStamp) { - std::vector raw_data(8000, 'T'); // Simulating TPX3 data - std::vector batches = { TPX3(0, 10, 0) }; // Create a dummy TPX3 batch - sophiread::timedLocateTimeStamp(batches, raw_data); - // Add assertions based on expected behavior + std::vector raw_data(8000, 'T'); // Simulating TPX3 data + std::vector batches = {TPX3(0, 10, 0)}; // Create a dummy TPX3 batch + sophiread::timedLocateTimeStamp(batches, raw_data); + // Add assertions based on expected behavior } TEST_F(SophireadCoreTest, TimedProcessing) { - std::vector raw_data(8000, 'T'); // Simulating TPX3 data - std::vector batches = { TPX3(0, 10, 0) }; // Create a dummy TPX3 batch - JSONConfigParser config = JSONConfigParser::createDefault(); - sophiread::timedProcessing(batches, raw_data, config); - // Add assertions based on expected behavior + std::vector raw_data(8000, 'T'); // Simulating TPX3 data + std::vector batches = {TPX3(0, 10, 0)}; // Create a dummy TPX3 batch + JSONConfigParser config = JSONConfigParser::createDefault(); + sophiread::timedProcessing(batches, raw_data, config); + // Add assertions based on expected behavior } TEST_F(SophireadCoreTest, TimedSaveHitsToHDF5) { - std::vector batches = generateMockTPX3Batches(2, 5); // Create a dummy TPX3 batch - sophiread::timedSaveHitsToHDF5("test_hits.h5", batches); - EXPECT_TRUE(std::filesystem::exists("test_hits.h5")); - std::filesystem::remove("test_hits.h5"); + std::vector batches = generateMockTPX3Batches(2, 5); // Create a dummy TPX3 batch + sophiread::timedSaveHitsToHDF5("test_hits.h5", batches); + EXPECT_TRUE(std::filesystem::exists("test_hits.h5")); + std::filesystem::remove("test_hits.h5"); } TEST_F(SophireadCoreTest, TimedSaveEventsToHDF5) { - std::vector batches = generateMockTPX3Batches(2, 5); // Create a dummy TPX3 batch - sophiread::timedSaveEventsToHDF5("test_events.h5", batches); - EXPECT_TRUE(std::filesystem::exists("test_events.h5")); - std::filesystem::remove("test_events.h5"); + std::vector batches = generateMockTPX3Batches(2, 5); // Create a dummy TPX3 batch + sophiread::timedSaveEventsToHDF5("test_events.h5", batches); + EXPECT_TRUE(std::filesystem::exists("test_events.h5")); + std::filesystem::remove("test_events.h5"); } TEST_F(SophireadCoreTest, TimedCreateTOFImages) { - std::vector batches = generateMockTPX3Batches(2, 5); // Create a dummy TPX3 batch - std::vector tof_bin_edges = {0.0, 0.1, 0.2, 0.3}; - auto images = sophiread::timedCreateTOFImages(batches, 1.0, tof_bin_edges, "neutron"); - EXPECT_EQ(images.size(), 3); // 3 bins + std::vector batches = generateMockTPX3Batches(2, 5); // Create a dummy TPX3 batch + std::vector tof_bin_edges = {0.0, 0.1, 0.2, 0.3}; + auto images = sophiread::timedCreateTOFImages(batches, 1.0, tof_bin_edges, "neutron"); + EXPECT_EQ(images.size(), 3); // 3 bins } TEST_F(SophireadCoreTest, TimedSaveTOFImagingToTIFF) { - std::vector>> tof_images(3, std::vector>(10, std::vector(10, 1))); - std::vector tof_bin_edges = {0.0, 0.1, 0.2, 0.3}; - sophiread::timedSaveTOFImagingToTIFF("test_tof", tof_images, tof_bin_edges, "test"); - EXPECT_TRUE(std::filesystem::exists("test_tof/test_bin_0001.tiff")); - EXPECT_TRUE(std::filesystem::exists("test_tof/test_bin_0002.tiff")); - EXPECT_TRUE(std::filesystem::exists("test_tof/test_bin_0003.tiff")); - EXPECT_TRUE(std::filesystem::exists("test_tof/test_Spectra.txt")); - std::filesystem::remove_all("test_tof"); + std::vector>> tof_images( + 3, std::vector>(10, std::vector(10, 1))); + std::vector tof_bin_edges = {0.0, 0.1, 0.2, 0.3}; + sophiread::timedSaveTOFImagingToTIFF("test_tof", tof_images, tof_bin_edges, "test"); + EXPECT_TRUE(std::filesystem::exists("test_tof/test_bin_0001.tiff")); + EXPECT_TRUE(std::filesystem::exists("test_tof/test_bin_0002.tiff")); + EXPECT_TRUE(std::filesystem::exists("test_tof/test_bin_0003.tiff")); + EXPECT_TRUE(std::filesystem::exists("test_tof/test_Spectra.txt")); + std::filesystem::remove_all("test_tof"); } -int main(int argc, char **argv) { - ::testing::InitGoogleTest(&argc, argv); - return RUN_ALL_TESTS(); +int main(int argc, char** argv) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); } \ No newline at end of file diff --git a/sophiread/SophireadCLI/tests/test_user_config.cpp b/sophiread/SophireadCLI/tests/test_user_config.cpp index 8c75d35..afbe9cf 100644 --- a/sophiread/SophireadCLI/tests/test_user_config.cpp +++ b/sophiread/SophireadCLI/tests/test_user_config.cpp @@ -20,9 +20,11 @@ * along with this program. If not, see . */ #include + #include -#include "user_config.h" + #include "tof_binning.h" +#include "user_config.h" // Test default constructor TEST(UserConfigTest, DefaultConstructor) { @@ -30,11 +32,11 @@ TEST(UserConfigTest, DefaultConstructor) { EXPECT_DOUBLE_EQ(config.getABSRadius(), 5.0); EXPECT_EQ(config.getABSMinClusterSize(), 1); EXPECT_EQ(config.getABSSpiderTimeRange(), 75); - + auto tof_edges = config.getTOFBinEdges(); EXPECT_EQ(tof_edges.size(), 1501); // 1500 bins + 1 EXPECT_DOUBLE_EQ(tof_edges.front(), 0.0); - EXPECT_DOUBLE_EQ(tof_edges.back(), 1.0/60); + EXPECT_DOUBLE_EQ(tof_edges.back(), 1.0 / 60); } // Test parameterized constructor @@ -43,12 +45,12 @@ TEST(UserConfigTest, ParameterizedConstructor) { EXPECT_DOUBLE_EQ(config.getABSRadius(), 10.0); EXPECT_EQ(config.getABSMinClusterSize(), 5); EXPECT_EQ(config.getABSSpiderTimeRange(), 100); - + // TOF binning should still be default auto tof_edges = config.getTOFBinEdges(); EXPECT_EQ(tof_edges.size(), 1501); EXPECT_DOUBLE_EQ(tof_edges.front(), 0.0); - EXPECT_DOUBLE_EQ(tof_edges.back(), 1.0/60); + EXPECT_DOUBLE_EQ(tof_edges.back(), 1.0 / 60); } // Test setters @@ -57,7 +59,7 @@ TEST(UserConfigTest, Setters) { config.setABSRadius(15.0); config.setABSMinClusterSize(10); config.setABSSpiderTimeRange(150); - + EXPECT_DOUBLE_EQ(config.getABSRadius(), 15.0); EXPECT_EQ(config.getABSMinClusterSize(), 10); EXPECT_EQ(config.getABSSpiderTimeRange(), 150); @@ -70,7 +72,7 @@ TEST(UserConfigTest, TOFBinningSetter) { custom_binning.num_bins = 1000; custom_binning.tof_max = 20000.0; config.setTOFBinning(custom_binning); - + auto tof_edges = config.getTOFBinEdges(); EXPECT_EQ(tof_edges.size(), 1001); // 1000 bins + 1 EXPECT_DOUBLE_EQ(tof_edges.front(), 0.0); @@ -82,7 +84,7 @@ TEST(UserConfigTest, CustomTOFBinEdges) { UserConfig config; std::vector custom_edges = {0.0, 100.0, 200.0, 300.0, 400.0}; config.setCustomTOFBinEdges(custom_edges); - + auto tof_edges = config.getTOFBinEdges(); EXPECT_EQ(tof_edges.size(), 5); EXPECT_DOUBLE_EQ(tof_edges[0], 0.0); @@ -124,7 +126,7 @@ TEST(UserConfigTest, ParseValidConfigurationFile) { auto tof_edges = config.getTOFBinEdges(); EXPECT_EQ(tof_edges.size(), 1501); EXPECT_DOUBLE_EQ(tof_edges.front(), 0.0); - EXPECT_DOUBLE_EQ(tof_edges.back(), 1.0/60); + EXPECT_DOUBLE_EQ(tof_edges.back(), 1.0 / 60); // Cleanup std::remove("testConfig.txt"); diff --git a/sophiread/SophireadGUI/src/mainwindow.cpp b/sophiread/SophireadGUI/src/mainwindow.cpp index d6379ad..ada4732 100644 --- a/sophiread/SophireadGUI/src/mainwindow.cpp +++ b/sophiread/SophireadGUI/src/mainwindow.cpp @@ -26,8 +26,7 @@ class ColorMap : public QwtLinearColorMap { } }; -MainWindow::MainWindow(QWidget *parent) - : QMainWindow(parent), ui(new Ui::MainWindow) { +MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { // Set up the UI ui->setupUi(this); @@ -67,7 +66,7 @@ MainWindow::MainWindow(QWidget *parent) unsigned long int min_cluster_size = 1; unsigned long int spider_time_range = 75; - clustering_alg = new ABS(radius,min_cluster_size,spider_time_range); + clustering_alg = new ABS(radius, min_cluster_size, spider_time_range); clustering_alg->set_method("centroid"); // clustering_alg->set_method("fast_gaussian"); @@ -114,9 +113,8 @@ void MainWindow::handlereadfile() { } // Get file name - QString filename = QFileDialog::getOpenFileName( - this, "Timepix File to open", QDir::currentPath(), - "All files (*.*) ;; Timepix raw (*.tpx3)"); + QString filename = QFileDialog::getOpenFileName(this, "Timepix File to open", QDir::currentPath(), + "All files (*.*) ;; Timepix raw (*.tpx3)"); // DEVNOTE: // The original QtConcurrent::run() function is not safe guarding content of @@ -209,9 +207,8 @@ void MainWindow::handlesavedata() { // Save 2D histogram double *swaphisto = NULL; - QString filename = QFileDialog::getSaveFileName( - this, "Save 2D hist as binary", QDir::currentPath(), - "All files (*.*) ;; Pic Binary (*.dat)"); + QString filename = QFileDialog::getSaveFileName(this, "Save 2D hist as binary", QDir::currentPath(), + "All files (*.*) ;; Pic Binary (*.dat)"); if (!filename.isNull()) { FILE *outfile; int ii; @@ -232,8 +229,7 @@ void MainWindow::handlesavedata() { } // Save neutron events to HDF5 archive - filename = QFileDialog::getSaveFileName(this, "Save events to HDF5", - QDir::currentPath(), + filename = QFileDialog::getSaveFileName(this, "Save events to HDF5", QDir::currentPath(), "All files (*.*) ;; HDF5 (*.hdf5)"); if (!filename.isNull()) { saveEventsToHDF5(filename.toStdString(), m_events); diff --git a/sophiread/SophireadLib/benchmarks/benchmark_abs.cpp b/sophiread/SophireadLib/benchmarks/benchmark_abs.cpp index e7a0385..bee91b1 100644 --- a/sophiread/SophireadLib/benchmarks/benchmark_abs.cpp +++ b/sophiread/SophireadLib/benchmarks/benchmark_abs.cpp @@ -4,9 +4,9 @@ * @brief Benchmark the performance of abs clustering method * @version 0.1 * @date 2023-08-25 - * + * * @copyright Copyright (c) 2023 - * + * */ #include #include @@ -49,8 +49,7 @@ std::vector fake_hits() { return hits; } -double run_single_test(std::vector hits, double &fit_time, - double &events_time) { +double run_single_test(std::vector hits, double &fit_time, double &events_time) { // create ABS algorithm ABS abs_alg(5.0, 1, 75); @@ -58,8 +57,7 @@ double run_single_test(std::vector hits, double &fit_time, auto start_fit = chrono::high_resolution_clock::now(); abs_alg.fit(hits); auto end_fit = chrono::high_resolution_clock::now(); - auto duration_fit = - chrono::duration_cast(end_fit - start_fit).count(); + auto duration_fit = chrono::duration_cast(end_fit - start_fit).count(); cout << "abs::fit " << duration_fit << " us" << endl; // convert to neutron events @@ -67,9 +65,7 @@ double run_single_test(std::vector hits, double &fit_time, abs_alg.set_method("centroid"); auto events = abs_alg.get_events(hits); auto end_events = chrono::high_resolution_clock::now(); - auto duration_events = - chrono::duration_cast(end_events - start_events) - .count(); + auto duration_events = chrono::duration_cast(end_events - start_events).count(); cout << "abs::get_events " << duration_events << " us" << endl; fit_time += duration_fit; diff --git a/sophiread/SophireadLib/benchmarks/benchmark_abs_thread.cpp b/sophiread/SophireadLib/benchmarks/benchmark_abs_thread.cpp index 60166d1..71420dd 100644 --- a/sophiread/SophireadLib/benchmarks/benchmark_abs_thread.cpp +++ b/sophiread/SophireadLib/benchmarks/benchmark_abs_thread.cpp @@ -48,8 +48,7 @@ std::vector fake_hits() { int stime = 10 * i + spidertime(gen); // cluster for (int j = 0; j < num_hits_per_cluster; j++) { - hits.emplace_back( - Hit(x, y, tot(gen), toa(gen), ftoa(gen), tof(gen), stime)); + hits.emplace_back(Hit(x, y, tot(gen), toa(gen), ftoa(gen), tof(gen), stime)); } } return hits; @@ -82,9 +81,7 @@ double single_test(const std::vector& hits, int num_thread) { // start threads for (int i = 0; i < num_thread; ++i) { thread_data_list[i].begin = hits.begin() + i * chunk_size; - thread_data_list[i].end = (i == num_thread - 1) - ? hits.end() - : hits.begin() + (i + 1) * chunk_size; + thread_data_list[i].end = (i == num_thread - 1) ? hits.end() : hits.begin() + (i + 1) * chunk_size; threads[i] = std::thread(&thread_data::run, std::ref(thread_data_list[i])); } @@ -96,10 +93,7 @@ double single_test(const std::vector& hits, int num_thread) { // record time auto end = std::chrono::high_resolution_clock::now(); - auto duration = - std::chrono::duration_cast(end - start) - .count() / - 1e6; + auto duration = std::chrono::duration_cast(end - start).count() / 1e6; cout << "[user]total " << duration << " sec" << endl; return duration; diff --git a/sophiread/SophireadLib/include/abs.h b/sophiread/SophireadLib/include/abs.h index 23ad21e..099f5e3 100644 --- a/sophiread/SophireadLib/include/abs.h +++ b/sophiread/SophireadLib/include/abs.h @@ -19,9 +19,8 @@ struct Cluster { */ class ABS : public ClusteringAlgorithm { public: - ABS(double r, unsigned long int min_cluster_size, - unsigned long int spider_time_range) : - m_feature(r), m_min_cluster_size(min_cluster_size), spiderTimeRange_(spider_time_range) {}; + ABS(double r, unsigned long int min_cluster_size, unsigned long int spider_time_range) + : m_feature(r), m_min_cluster_size(min_cluster_size), spiderTimeRange_(spider_time_range) {}; void fit(const std::vector& data); void set_method(std::string method) { m_method = method; } void reset() { clusterLabels_.clear(); } @@ -30,13 +29,13 @@ class ABS : public ClusteringAlgorithm { ~ABS() = default; private: - double m_feature; // feather range - std::string m_method{"centroid"}; // method for centroid - std::vector clusterLabels_; // The cluster labels for each hit + double m_feature; // feather range + std::string m_method{"centroid"}; // method for centroid + std::vector clusterLabels_; // The cluster labels for each hit std::vector> clusterIndices_; // The cluster indices for // each cluster - const int numClusters_ = 4; // The number of clusters use in runtime - unsigned long int m_min_cluster_size = 1; // The maximum cluster size - unsigned long int spiderTimeRange_ = 75; // The spider time range (in ns) - PeakFittingAlgorithm* peakFittingAlgorithm_; // The clustering algorithm + const int numClusters_ = 4; // The number of clusters use in runtime + unsigned long int m_min_cluster_size = 1; // The maximum cluster size + unsigned long int spiderTimeRange_ = 75; // The spider time range (in ns) + PeakFittingAlgorithm* peakFittingAlgorithm_; // The clustering algorithm }; diff --git a/sophiread/SophireadLib/include/centroid.h b/sophiread/SophireadLib/include/centroid.h index 3c80f92..6559469 100644 --- a/sophiread/SophireadLib/include/centroid.h +++ b/sophiread/SophireadLib/include/centroid.h @@ -14,7 +14,7 @@ */ class Centroid : public PeakFittingAlgorithm { public: - Centroid(bool weighted_by_tot = true) : weighted_by_tot(weighted_by_tot){}; + Centroid(bool weighted_by_tot = true) : weighted_by_tot(weighted_by_tot) {}; // Pure virtual function for predicting the peak positions and parameters // predict -> (x, y, tof) diff --git a/sophiread/SophireadLib/include/clustering.h b/sophiread/SophireadLib/include/clustering.h index 39b36e4..daf5924 100644 --- a/sophiread/SophireadLib/include/clustering.h +++ b/sophiread/SophireadLib/include/clustering.h @@ -23,8 +23,7 @@ class ClusteringAlgorithm { virtual void fit(const std::vector& hits) = 0; // generate neutron events with given hits and fitted cluster IDs - virtual std::vector get_events( - const std::vector& hits) = 0; + virtual std::vector get_events(const std::vector& hits) = 0; virtual ~ClusteringAlgorithm() {} }; diff --git a/sophiread/SophireadLib/include/dbscan.h b/sophiread/SophireadLib/include/dbscan.h index 1ce8d70..0fc46be 100644 --- a/sophiread/SophireadLib/include/dbscan.h +++ b/sophiread/SophireadLib/include/dbscan.h @@ -4,12 +4,8 @@ class DBSCAN : public ClusteringAlgorithm { public: - DBSCAN(double eps_time, size_t min_points_time, double eps_xy, - size_t min_points_xy) - : m_eps_time(eps_time), - m_min_points_time(min_points_time), - m_eps_xy(eps_xy), - m_min_points_xy(min_points_xy){}; + DBSCAN(double eps_time, size_t min_points_time, double eps_xy, size_t min_points_xy) + : m_eps_time(eps_time), m_min_points_time(min_points_time), m_eps_xy(eps_xy), m_min_points_xy(min_points_xy) {}; ~DBSCAN() = default; public: @@ -35,20 +31,18 @@ class DBSCAN : public ClusteringAlgorithm { double m_time_max; std::vector m_time_cluster_xy_indexes; // wrt input hits vector }; - void fit1D(std::vector &data, size_t &number_of_clusters, - std::vector &labels, std::vector ¢roids); - void fit2D(std::vector> &data, - size_t &number_of_clusters, std::vector &labels, + void fit1D(std::vector &data, size_t &number_of_clusters, std::vector &labels, + std::vector ¢roids); + void fit2D(std::vector> &data, size_t &number_of_clusters, std::vector &labels, std::vector> ¢roids); - void mergeTimeClusters1D(std::vector &input_infos, - std::vector &merged_infos); + void mergeTimeClusters1D(std::vector &input_infos, std::vector &merged_infos); private: std::string m_method{"centroid"}; // method for centroid - double m_eps_time; // The maximum distance between two time points - size_t m_min_points_time; // The minimum number of points in a time cluster - double m_eps_xy; // The maximum distance between two XY points - size_t m_min_points_xy; // The minimum number of points in an XY cluster + double m_eps_time; // The maximum distance between two time points + size_t m_min_points_time; // The minimum number of points in a time cluster + double m_eps_xy; // The maximum distance between two XY points + size_t m_min_points_xy; // The minimum number of points in an XY cluster std::vector m_events; const size_t m_max_hit_chunk_size = 2e6; std::vector clusterLabels_; // The cluster labels for each hit diff --git a/sophiread/SophireadLib/include/fastgaussian.h b/sophiread/SophireadLib/include/fastgaussian.h index 37aa1c2..460ea11 100644 --- a/sophiread/SophireadLib/include/fastgaussian.h +++ b/sophiread/SophireadLib/include/fastgaussian.h @@ -6,7 +6,7 @@ */ class FastGaussian : public PeakFittingAlgorithm { public: - FastGaussian(){}; + FastGaussian() {}; // Pure virtual function for predicting the peak positions and parameters // predict -> (x, y, tof) diff --git a/sophiread/SophireadLib/include/tpx3.h b/sophiread/SophireadLib/include/tpx3.h index ff3bcb5..128fb46 100644 --- a/sophiread/SophireadLib/include/tpx3.h +++ b/sophiread/SophireadLib/include/tpx3.h @@ -18,7 +18,7 @@ class Hit { public: // default constructor - Hit() : m_x(0), m_y(0), m_tot(0), m_toa(0), m_ftoa(0), m_tof(0), m_spidertime(0){}; + Hit() : m_x(0), m_y(0), m_tot(0), m_toa(0), m_ftoa(0), m_tof(0), m_spidertime(0) {}; // copy constructor Hit(const Hit& hit) : m_x(hit.m_x), @@ -27,17 +27,10 @@ class Hit { m_toa(hit.m_toa), m_ftoa(hit.m_ftoa), m_tof(hit.m_tof), - m_spidertime(hit.m_spidertime){}; - - Hit(int x, int y, int tot, int toa, int ftoa, unsigned int tof, - unsigned long long spidertime) - : m_x(x), - m_y(y), - m_tot(tot), - m_toa(toa), - m_ftoa(ftoa), - m_tof(tof), - m_spidertime(spidertime){}; + m_spidertime(hit.m_spidertime) {}; + + Hit(int x, int y, int tot, int toa, int ftoa, unsigned int tof, unsigned long long spidertime) + : m_x(x), m_y(y), m_tot(tot), m_toa(toa), m_ftoa(ftoa), m_tof(tof), m_spidertime(spidertime) {}; // special constructor that directly parse the raw packet from tpx3 // into a hit @@ -65,9 +58,7 @@ class Hit { double getTOF_ns() const { return m_tof * m_scale_to_ns_40mhz; }; double getTOA_ns() const { return m_toa * m_scale_to_ns_40mhz; }; double getTOT_ns() const { return m_tot * m_scale_to_ns_40mhz; }; - double getSPIDERTIME_ns() const { - return m_spidertime * m_scale_to_ns_40mhz; - }; + double getSPIDERTIME_ns() const { return m_spidertime * m_scale_to_ns_40mhz; }; double getFTOA_ns() const { return m_ftoa * m_scale_to_ns_640mhz; }; std::string toString() const; @@ -79,14 +70,11 @@ class Hit { int m_toa; // time of arrival (40MHz clock, 14 bit) int m_ftoa; // fine time of arrival (640MHz clock, 4 bit) unsigned int m_tof; - unsigned long long - m_spidertime; // time from the spider board (in the unit of 25ns) + unsigned long long m_spidertime; // time from the spider board (in the unit of 25ns) // scale factor that converts time to ns - const double m_scale_to_ns_40mhz = - 25.0; // 40 MHz clock is used for the coarse time of arrival. - const double m_scale_to_ns_640mhz = - 25.0 / 16.0; // 640 MHz clock is used for the fine time of arrival. + const double m_scale_to_ns_40mhz = 25.0; // 40 MHz clock is used for the coarse time of arrival. + const double m_scale_to_ns_640mhz = 25.0 / 16.0; // 640 MHz clock is used for the fine time of arrival. }; /** @@ -95,12 +83,11 @@ class Hit { */ class NeutronEvent { public: - NeutronEvent(const double x, const double y, const double tof, - const double tot, const int nHits) - : m_x(x), m_y(y), m_tof(tof), m_tot(tot), m_nHits(nHits){}; + NeutronEvent(const double x, const double y, const double tof, const double tot, const int nHits) + : m_x(x), m_y(y), m_tof(tof), m_tot(tot), m_nHits(nHits) {}; double getX() const { return m_x; }; double getY() const { return m_y; }; - double getTOT() const { return m_tot;} + double getTOT() const { return m_tot; } double getTOF() const { return m_tof; }; double getTOF_ns() const { return m_tof * m_scale_to_ns_40mhz; }; int getNHits() const { return m_nHits; }; @@ -108,59 +95,49 @@ class NeutronEvent { std::string toString() const; private: - const double m_x, m_y; // pixel coordinates - const double m_tof; // time of flight - const double m_tot; // time-over-threshold - const int m_nHits; // number of hits in the event (cluster size) - const double m_scale_to_ns_40mhz = - 25.0; // 40 MHz clock is used for the coarse time of arrival. + const double m_x, m_y; // pixel coordinates + const double m_tof; // time of flight + const double m_tot; // time-over-threshold + const int m_nHits; // number of hits in the event (cluster size) + const double m_scale_to_ns_40mhz = 25.0; // 40 MHz clock is used for the coarse time of arrival. }; /** - * @brief Class to store user-defined parameters for clustering algorithms + * @brief Class to store user-defined parameters for clustering algorithms * */ class Params { -public: - Params(const double abs_radius, - unsigned long int abs_min_cluster_size, - unsigned long int abs_spider_time_range) : - m_abs_radius(abs_radius), - m_abs_min_cluster_size(abs_min_cluster_size), - m_abs_spider_time_range(abs_spider_time_range){}; - - double getABSRadius() const {return m_abs_radius;}; - unsigned long int getABSMinClusterSize() - const {return m_abs_min_cluster_size;}; - unsigned long int getABSSpidertimeRange() - const {return m_abs_spider_time_range;}; + public: + Params(const double abs_radius, unsigned long int abs_min_cluster_size, unsigned long int abs_spider_time_range) + : m_abs_radius(abs_radius), + m_abs_min_cluster_size(abs_min_cluster_size), + m_abs_spider_time_range(abs_spider_time_range) {}; + + double getABSRadius() const { return m_abs_radius; }; + unsigned long int getABSMinClusterSize() const { return m_abs_min_cluster_size; }; + unsigned long int getABSSpidertimeRange() const { return m_abs_spider_time_range; }; std::string toString() const; -private: + private: // ABS members (see abs.h for details) - double m_abs_radius; + double m_abs_radius; unsigned long int m_abs_min_cluster_size; unsigned long int m_abs_spider_time_range; }; // static file processing std::vector readTimepix3RawData(const std::string& filepath); -Hit packetToHit(const std::vector& packet, const unsigned long long tdc, - const unsigned long long gdc, const int chip_layout_type); -Hit packetToHitAlt(const std::vector& packet, - unsigned long long* rollover_counter, - unsigned long long* previous_time, - const int chip_layout_type); +Hit packetToHit(const std::vector& packet, const unsigned long long tdc, const unsigned long long gdc, + const int chip_layout_type); +Hit packetToHitAlt(const std::vector& packet, unsigned long long* rollover_counter, + unsigned long long* previous_time, const int chip_layout_type); // in memory processing std::vector parseRawBytesToHits(const std::vector& raw_bytes); // -void saveHitsToHDF5(const std::string out_file_name, - const std::vector& hits, - const std::vector& labels); -void saveEventsToHDF5(const std::string out_file_name, - const std::vector& events); +void saveHitsToHDF5(const std::string out_file_name, const std::vector& hits, const std::vector& labels); +void saveEventsToHDF5(const std::string out_file_name, const std::vector& events); -// parse user-defined param file +// parse user-defined param file Params parseUserDefinedParams(const std::string& filepath); // for fast processing raw bytes into hit @@ -171,7 +148,7 @@ struct TPX3H { const int chip_layout_type; TPX3H(std::size_t index, int packet_size, int num_packets, int chip_layout_type) - : index(index), packet_size(packet_size), num_packets(num_packets), chip_layout_type(chip_layout_type){}; + : index(index), packet_size(packet_size), num_packets(num_packets), chip_layout_type(chip_layout_type) {}; }; std::vector fastParseTPX3Raw(const std::vector& raw_bytes); std::vector processBatch(TPX3H batch, const std::vector& raw_bytes); \ No newline at end of file diff --git a/sophiread/SophireadLib/src/abs.cpp b/sophiread/SophireadLib/src/abs.cpp index b3fd815..b9525c3 100644 --- a/sophiread/SophireadLib/src/abs.cpp +++ b/sophiread/SophireadLib/src/abs.cpp @@ -8,7 +8,6 @@ #include "centroid.h" #include "fastgaussian.h" - /** * @brief Generate cluster labels for the hits. * @@ -53,12 +52,9 @@ void ABS::fit(const std::vector& data) { // case_1: cluster is not full, check if hit is within the feather range // of the cluster, if yes, add hit to cluster and update cluster bounds, // if not, id as noise - else if (std::abs(hit.getSPIDERTIME_ns() - cluster.spidertime) <= - spiderTimeRange_) { - if (hit.getX() >= cluster.x_min - m_feature && - hit.getX() <= cluster.x_max + m_feature && - hit.getY() >= cluster.y_min - m_feature && - hit.getY() <= cluster.y_max + m_feature) { + else if (std::abs(hit.getSPIDERTIME_ns() - cluster.spidertime) <= spiderTimeRange_) { + if (hit.getX() >= cluster.x_min - m_feature && hit.getX() <= cluster.x_max + m_feature && + hit.getY() >= cluster.y_min - m_feature && hit.getY() <= cluster.y_max + m_feature) { cluster.size++; cluster.x_min = std::min(cluster.x_min, hit.getX()); cluster.x_max = std::max(cluster.x_max, hit.getX()); @@ -127,8 +123,7 @@ std::vector ABS::get_events(const std::vector& data) { std::vector events; // find the highest label - auto max_label_it = - std::max_element(clusterLabels_.begin(), clusterLabels_.end()); + auto max_label_it = std::max_element(clusterLabels_.begin(), clusterLabels_.end()); int max_label = *max_label_it; // determine fitting algorithm diff --git a/sophiread/SophireadLib/src/centroid.cpp b/sophiread/SophireadLib/src/centroid.cpp index f46dde9..ee624e7 100644 --- a/sophiread/SophireadLib/src/centroid.cpp +++ b/sophiread/SophireadLib/src/centroid.cpp @@ -48,6 +48,6 @@ NeutronEvent Centroid::fit(const std::vector& data) { } tof /= data.size(); - + return NeutronEvent(x, y, tof, tot, data.size()); } diff --git a/sophiread/SophireadLib/src/dbscan.cpp b/sophiread/SophireadLib/src/dbscan.cpp index b710bcb..8b7ad61 100644 --- a/sophiread/SophireadLib/src/dbscan.cpp +++ b/sophiread/SophireadLib/src/dbscan.cpp @@ -3,16 +3,11 @@ #include #include -DBSCAN::TimeClusterInfo::TimeClusterInfo() - : m_time_mean(0.), - m_time_sum(0.), - m_time_min(DBL_MAX), - m_time_max(DBL_MIN) { +DBSCAN::TimeClusterInfo::TimeClusterInfo() : m_time_mean(0.), m_time_sum(0.), m_time_min(DBL_MAX), m_time_max(DBL_MIN) { m_time_cluster_xy_indexes = std::vector(); } -DBSCAN::TimeClusterInfo::TimeClusterInfo(const double time, - const size_t xy_index) +DBSCAN::TimeClusterInfo::TimeClusterInfo(const double time, const size_t xy_index) : m_time_mean(time), m_time_sum(time), m_time_min(time), m_time_max(time) { m_time_cluster_xy_indexes = std::vector{xy_index}; } @@ -39,16 +34,13 @@ void DBSCAN::fit(const std::vector& hits) { if (max_number_of_hits == 0) return; if (m_verbose) { - std::cout << "Number of hits to process: " << max_number_of_hits - << std::endl; - std::cout << "Maximum chunk size (hits): " << m_max_hit_chunk_size - << std::endl; + std::cout << "Number of hits to process: " << max_number_of_hits << std::endl; + std::cout << "Maximum chunk size (hits): " << m_max_hit_chunk_size << std::endl; if (max_number_of_hits <= m_max_hit_chunk_size) std::cout << "Fitting time clusters (1D DBSCAN) on all hits" << std::endl; else - std::cout << "Fitting time clusters (1D DBSCAN) on chunks of hits..." - << std::endl; + std::cout << "Fitting time clusters (1D DBSCAN) on chunks of hits..." << std::endl; } size_t chunk_size{0}; // either max_chunk_size or the number of unprocessed @@ -59,13 +51,11 @@ void DBSCAN::fit(const std::vector& hits) { while (true) { // make a chunk - chunk_size = - std::min(m_max_hit_chunk_size, max_number_of_hits - hit_offset); + chunk_size = std::min(m_max_hit_chunk_size, max_number_of_hits - hit_offset); std::vector chunk; // NOTE: we are using TOA ns here, NOT clock cycle. - std::transform( - hits.begin() + hit_offset, hits.begin() + hit_offset + chunk_size, - std::back_inserter(chunk), [](Hit const& h) { return h.getTOA_ns(); }); + std::transform(hits.begin() + hit_offset, hits.begin() + hit_offset + chunk_size, std::back_inserter(chunk), + [](Hit const& h) { return h.getTOA_ns(); }); // run 1D time clustering on the chunk std::vector labels; @@ -75,19 +65,16 @@ void DBSCAN::fit(const std::vector& hits) { std::vector time_cluster_infos; time_cluster_infos.resize(number_of_clusters); - std::vector - non_cluster_infos; // these unassigned data points may still change - // their status later, during merging of chunks + std::vector non_cluster_infos; // these unassigned data points may still change + // their status later, during merging of chunks // set the time mean of each new info from the centroids vector - for (size_t jj = 0; jj < number_of_clusters; jj++) - time_cluster_infos[jj].m_time_mean = centroids_1D[jj]; + for (size_t jj = 0; jj < number_of_clusters; jj++) time_cluster_infos[jj].m_time_mean = centroids_1D[jj]; for (size_t ii = 0; ii < labels.size(); ii++) { size_t label = labels[ii]; if (label == SIZE_MAX || label > number_of_clusters - 1) { - non_cluster_infos.emplace_back( - TimeClusterInfo(chunk[ii], ii + hit_offset)); + non_cluster_infos.emplace_back(TimeClusterInfo(chunk[ii], ii + hit_offset)); continue; } TimeClusterInfo& info = time_cluster_infos[label]; @@ -98,14 +85,10 @@ void DBSCAN::fit(const std::vector& hits) { } // append non-cluster infos considering them as clusters with a single data // point each - time_cluster_infos.insert(time_cluster_infos.end(), - non_cluster_infos.begin(), - non_cluster_infos.end()); + time_cluster_infos.insert(time_cluster_infos.end(), non_cluster_infos.begin(), non_cluster_infos.end()); // append the new infos to the total infos - all_time_cluster_infos.insert(all_time_cluster_infos.end(), - time_cluster_infos.begin(), - time_cluster_infos.end()); + all_time_cluster_infos.insert(all_time_cluster_infos.end(), time_cluster_infos.begin(), time_cluster_infos.end()); hit_offset += chunk_size; if (max_number_of_hits - hit_offset == 0) break; // processed all hits @@ -122,12 +105,9 @@ void DBSCAN::fit(const std::vector& hits) { assert(m_events.empty()); // must run reset() before fitting if (m_verbose) { - std::cout << "Number of time clusters: " << merged_time_cluster_infos.size() - << std::endl; - std::cout << "Fitting XY clusters (2D DBSCAN) on every time cluster..." - << std::endl; - std::cout << "Eps: " << m_eps_xy << "; min_points: " << m_min_points_xy - << std::endl; + std::cout << "Number of time clusters: " << merged_time_cluster_infos.size() << std::endl; + std::cout << "Fitting XY clusters (2D DBSCAN) on every time cluster..." << std::endl; + std::cout << "Eps: " << m_eps_xy << "; min_points: " << m_min_points_xy << std::endl; } size_t label_offset{0}; @@ -136,8 +116,7 @@ void DBSCAN::fit(const std::vector& hits) { std::vector> xy_points; for (auto& index : info.m_time_cluster_xy_indexes) - xy_points.push_back( - std::pair(hits[index].getX(), hits[index].getY())); + xy_points.push_back(std::pair(hits[index].getX(), hits[index].getY())); std::vector labels; std::vector> centroids_2D; @@ -147,15 +126,12 @@ void DBSCAN::fit(const std::vector& hits) { // set cluster labels to all hits contained in the current time cluster for (size_t ii = 0; ii < labels.size(); ii++) { size_t label = labels[ii]; - int cluster_label = (label == SIZE_MAX || label > number_of_clusters - 1) - ? -1 - : label + label_offset; + int cluster_label = (label == SIZE_MAX || label > number_of_clusters - 1) ? -1 : label + label_offset; clusterLabels_[info.m_time_cluster_xy_indexes[ii]] = cluster_label; } label_offset += number_of_clusters; - std::map - label_counts; // label vs. number of xy points with that label + std::map label_counts; // label vs. number of xy points with that label for (size_t ii = 0; ii < labels.size(); ii++) { size_t label = labels[ii]; if (label == SIZE_MAX || label > number_of_clusters - 1) continue; @@ -167,10 +143,9 @@ void DBSCAN::fit(const std::vector& hits) { // note: currently the tot for each neutron event is set to 0 // as there is no simple solution to incorporate tot info using DBSCAN for (auto const& label_count : label_counts) - m_events.emplace_back( - NeutronEvent(centroids_2D[label_count.first].first * DSCALE /*X*/, - centroids_2D[label_count.first].second * DSCALE /*Y*/, - info.m_time_mean,0, label_count.second)); + m_events.emplace_back(NeutronEvent(centroids_2D[label_count.first].first * DSCALE /*X*/, + centroids_2D[label_count.first].second * DSCALE /*Y*/, info.m_time_mean, 0, + label_count.second)); } } @@ -188,9 +163,7 @@ void DBSCAN::mergeTimeClusters1D(std::vector& input_infos, // before merging, sort the infos by the min time std::sort(input_infos.begin(), input_infos.end(), - [](const TimeClusterInfo& a, const TimeClusterInfo& b) { - return a.m_time_min < b.m_time_min; - }); + [](const TimeClusterInfo& a, const TimeClusterInfo& b) { return a.m_time_min < b.m_time_min; }); merged_infos.clear(); std::vector::const_iterator it = input_infos.begin(); @@ -199,19 +172,16 @@ void DBSCAN::mergeTimeClusters1D(std::vector& input_infos, while (it != input_infos.end()) { next = *(it); if (current.m_time_max > next.m_time_min || - next.m_time_min - current.m_time_max <= - m_eps_time) { // checking if the clusters should be merged + next.m_time_min - current.m_time_max <= m_eps_time) { // checking if the clusters should be merged current.m_time_max = std::max(current.m_time_max, next.m_time_max); current.m_time_min = std::min(current.m_time_min, next.m_time_min); - current.m_time_cluster_xy_indexes.insert( - current.m_time_cluster_xy_indexes.end(), - next.m_time_cluster_xy_indexes.begin(), - next.m_time_cluster_xy_indexes.end()); + current.m_time_cluster_xy_indexes.insert(current.m_time_cluster_xy_indexes.end(), + next.m_time_cluster_xy_indexes.begin(), + next.m_time_cluster_xy_indexes.end()); current.m_time_sum += next.m_time_sum; } else { if (current.m_time_cluster_xy_indexes.size() >= m_min_points_time) { - current.m_time_mean = - current.m_time_sum / current.m_time_cluster_xy_indexes.size(); + current.m_time_mean = current.m_time_sum / current.m_time_cluster_xy_indexes.size(); merged_infos.push_back(current); } current = *(it); @@ -219,8 +189,7 @@ void DBSCAN::mergeTimeClusters1D(std::vector& input_infos, it++; } if (current.m_time_cluster_xy_indexes.size() >= m_min_points_time) { - current.m_time_mean = - current.m_time_sum / current.m_time_cluster_xy_indexes.size(); + current.m_time_mean = current.m_time_sum / current.m_time_cluster_xy_indexes.size(); merged_infos.push_back(current); } } @@ -233,17 +202,14 @@ void DBSCAN::mergeTimeClusters1D(std::vector& input_infos, * @param labels :: (output) cluster labels * @param centroids :: (output) cluster centroids */ -void DBSCAN::fit1D(std::vector& data, size_t& number_of_clusters, - std::vector& labels, +void DBSCAN::fit1D(std::vector& data, size_t& number_of_clusters, std::vector& labels, std::vector& centroids) { // create an arma matrix from the data vector - arma::mat data_mat( - &data[0], 1 /*nrows*/, data.size() /*ncols*/, - false /*arma::mat will re-use the input data vector memory*/); + arma::mat data_mat(&data[0], 1 /*nrows*/, data.size() /*ncols*/, + false /*arma::mat will re-use the input data vector memory*/); // create the dbscan object - mlpack::DBSCAN, mlpack::OrderedPointSelection> dbs( - m_eps_time, m_min_points_time); + mlpack::DBSCAN, mlpack::OrderedPointSelection> dbs(m_eps_time, m_min_points_time); // run mlpack clustering arma::Row labels_row; @@ -259,8 +225,7 @@ void DBSCAN::fit1D(std::vector& data, size_t& number_of_clusters, labels_row.for_each([&labels](size_t& val) { labels.push_back(val); }); // fill in the centroids vector from the arma centroids matrix - centroids_mat.for_each( - [¢roids](arma::mat::elem_type& val) { centroids.push_back(val); }); + centroids_mat.for_each([¢roids](arma::mat::elem_type& val) { centroids.push_back(val); }); } /** @@ -271,17 +236,14 @@ void DBSCAN::fit1D(std::vector& data, size_t& number_of_clusters, * @param labels :: (output) cluster labels * @param centroids :: (output) cluster centroids */ -void DBSCAN::fit2D(std::vector>& data, - size_t& number_of_clusters, std::vector& labels, - std::vector>& centroids) { +void DBSCAN::fit2D(std::vector>& data, size_t& number_of_clusters, + std::vector& labels, std::vector>& centroids) { // create an arma matrix from the data vector - arma::mat data_mat( - &(data[0].first), 2 /*nrows*/, data.size() /*ncols*/, - false /*arma::mat will re-use the input data vector memory*/); + arma::mat data_mat(&(data[0].first), 2 /*nrows*/, data.size() /*ncols*/, + false /*arma::mat will re-use the input data vector memory*/); // create the dbscan object - mlpack::DBSCAN, mlpack::OrderedPointSelection> dbs( - m_eps_xy, m_min_points_xy); + mlpack::DBSCAN, mlpack::OrderedPointSelection> dbs(m_eps_xy, m_min_points_xy); // run mlpack clustering arma::Row labels_row; @@ -292,9 +254,8 @@ void DBSCAN::fit2D(std::vector>& data, labels_row.for_each([&labels](size_t& val) { labels.push_back(val); }); // fill in the centroids vector from the arma centroids matrix - centroids_mat.each_col([¢roids](const arma::vec& b) { - centroids.push_back(std::pair(b[0], b[1])); - }); + centroids_mat.each_col( + [¢roids](const arma::vec& b) { centroids.push_back(std::pair(b[0], b[1])); }); } /** @@ -307,8 +268,7 @@ std::vector DBSCAN::get_events(const std::vector& hits) { if (m_events.size() == 0) { fit(hits); } - if (m_verbose) - std::cout << "Total number of events: " << m_events.size() << std::endl; + if (m_verbose) std::cout << "Total number of events: " << m_events.size() << std::endl; return m_events; } diff --git a/sophiread/SophireadLib/src/fastgaussian.cpp b/sophiread/SophireadLib/src/fastgaussian.cpp index 9b62170..959f2dc 100644 --- a/sophiread/SophireadLib/src/fastgaussian.cpp +++ b/sophiread/SophireadLib/src/fastgaussian.cpp @@ -28,9 +28,7 @@ double getMedian(const std::vector& data) { std::vector sorted_data = data; std::sort(sorted_data.begin(), sorted_data.end()); if (sorted_data.size() % 2 == 0) { - return (sorted_data[sorted_data.size() / 2 - 1] + - sorted_data[sorted_data.size() / 2]) / - 2; + return (sorted_data[sorted_data.size() / 2 - 1] + sorted_data[sorted_data.size() / 2]) / 2; } else { return sorted_data[sorted_data.size() / 2]; } @@ -109,12 +107,10 @@ NeutronEvent FastGaussian::fit(const std::vector& data) { double y_event = x_sol(1) / 2.0; // calculate the tof as the average of the tof of the filtered hits - double tof_event = - std::accumulate(tof_filtered.begin(), tof_filtered.end(), 0.0) / - tof_filtered.size(); + double tof_event = std::accumulate(tof_filtered.begin(), tof_filtered.end(), 0.0) / tof_filtered.size(); // calculate the tot - double tot_event = std::accumulate(tot_filtered.begin(), tot_filtered.end(),0.0); + double tot_event = std::accumulate(tot_filtered.begin(), tot_filtered.end(), 0.0); // even if we are throwing away to bottom half, we still need to return the // pre-filtered number of hits diff --git a/sophiread/SophireadLib/src/tpx3.cpp b/sophiread/SophireadLib/src/tpx3.cpp index 33baa96..add665f 100644 --- a/sophiread/SophireadLib/src/tpx3.cpp +++ b/sophiread/SophireadLib/src/tpx3.cpp @@ -16,23 +16,20 @@ std::string Hit::toString() const { std::stringstream ss; - ss << "Hit: x=" << m_x << ", y=" << m_y << ", tot=" << m_tot - << ", toa=" << m_toa << ", ftoa=" << m_ftoa << ", tof=" << m_tof - << ", spidertime=" << m_spidertime; + ss << "Hit: x=" << m_x << ", y=" << m_y << ", tot=" << m_tot << ", toa=" << m_toa << ", ftoa=" << m_ftoa + << ", tof=" << m_tof << ", spidertime=" << m_spidertime; return ss.str(); } std::string NeutronEvent::toString() const { std::stringstream ss; - ss << "NeutronEvent: x=" << m_x << ", y=" << m_y << ", tof=" << m_tof - << ", nHits=" << m_nHits; + ss << "NeutronEvent: x=" << m_x << ", y=" << m_y << ", tof=" << m_tof << ", nHits=" << m_nHits; return ss.str(); } std::string Params::toString() const { std::stringstream ss; - ss << "ABS: radius=" << m_abs_radius - << ", min_cluster_size=" << m_abs_min_cluster_size + ss << "ABS: radius=" << m_abs_radius << ", min_cluster_size=" << m_abs_min_cluster_size << ", spider_time_range=" << m_abs_spider_time_range; return ss.str(); @@ -121,10 +118,8 @@ Hit::Hit(const char *packet, const unsigned long long tdc, const unsigned long l * this function is used as a temporary solution with assumed timing, until * timing packet is fixed on the hardware side. */ -Hit packetToHitAlt(const std::vector &packet, - unsigned long long *rollover_counter, - unsigned long long *previous_time, - const int chip_layout_type) { +Hit packetToHitAlt(const std::vector &packet, unsigned long long *rollover_counter, + unsigned long long *previous_time, const int chip_layout_type) { unsigned short pixaddr, dcol, spix, pix; unsigned short *spider_time; unsigned short *nTOT; // bytes 2,3, raw time over threshold @@ -154,8 +149,8 @@ Hit packetToHitAlt(const std::vector &packet, *rollover_counter += 1; } - // if the curr hit arrives later than previous hit (in order) - // if it is a lot later, it belongs to the previous rollover + // if the curr hit arrives later than previous hit (in order) + // if it is a lot later, it belongs to the previous rollover } else { if (spidertime - *previous_time > time_range / 2) { if (*rollover_counter > 0) { @@ -172,7 +167,7 @@ Hit packetToHitAlt(const std::vector &packet, // a consistent round off error of 10ns due to using integer for modulus // which is way below the 100ns time resolution needed tof = SPDR_timestamp % 666667; - + // pixel address npixaddr = (unsigned int *)(&packet[4]); // Pixel address (14 bits) pixaddr = (*npixaddr >> 12) & 0xFFFF; @@ -208,15 +203,15 @@ Hit packetToHitAlt(const std::vector &packet, * @param chip_layout_type: chip layout ID number * @return Hit */ -Hit packetToHit(const std::vector &packet, const unsigned long long tdc, - const unsigned long long gdc, const int chip_layout_type) { +Hit packetToHit(const std::vector &packet, const unsigned long long tdc, const unsigned long long gdc, + const int chip_layout_type) { unsigned short pixaddr, dcol, spix, pix; unsigned short *spider_time; unsigned short *nTOT; // bytes 2,3, raw time over threshold unsigned int *nTOA; // bytes 3,4,5,6, raw time of arrival unsigned int *npixaddr; // bytes 4,5,6,7 int x, y, tot, toa, ftoa; - unsigned int spidertime=0, tof=0; + unsigned int spidertime = 0, tof = 0; // timing information spider_time = (unsigned short *)(&packet[0]); // Spider time (16 bits) nTOT = (unsigned short *)(&packet[2]); // ToT (10 bits) @@ -245,8 +240,8 @@ Hit packetToHit(const std::vector &packet, const unsigned long long tdc, // tof calculation // TDC packets not always arrive before corresponding data packets - if (SPDR_timestamp < TDC_timestamp){ - tof = SPDR_timestamp - TDC_timestamp + 1E9/60.0; + if (SPDR_timestamp < TDC_timestamp) { + tof = SPDR_timestamp - TDC_timestamp + 1E9 / 60.0; } else { tof = SPDR_timestamp - TDC_timestamp; } @@ -295,8 +290,7 @@ std::vector readTimepix3RawData(const std::string &filepath) { throw std::runtime_error("Error opening file"); } // Read the data from the file into a buffer - std::vector buffer((std::istreambuf_iterator(file)), - std::istreambuf_iterator()); + std::vector buffer((std::istreambuf_iterator(file)), std::istreambuf_iterator()); file.close(); // Process the buffer to extract the raw data @@ -338,8 +332,7 @@ std::vector readTimepix3RawData(const std::string &filepath) { // and data_packet_num, therefore we are using the code from manufacture // example, tpx3cam.cpp to get the data_packet_size. data_packet_size = ((0xff & char_array[7]) << 8) | (0xff & char_array[6]); - data_packet_num = - data_packet_size >> 3; // every 8 (2^3) bytes is a data packet + data_packet_num = data_packet_size >> 3; // every 8 (2^3) bytes is a data packet // get chip layout type chip_layout_type = (int)char_array[4]; @@ -356,8 +349,7 @@ std::vector readTimepix3RawData(const std::string &filepath) { if (data_packet[7] == 0x6F) { // TDC data packets tdclast = (unsigned long *)(&data_packet[0]); - mytdc = (((*tdclast) >> 12) & - 0xFFFFFFFF); // rick: 0x3fffffff, get 32-bit tdc + mytdc = (((*tdclast) >> 12) & 0xFFFFFFFF); // rick: 0x3fffffff, get 32-bit tdc TDC_LSB32 = GDC_timestamp & 0xFFFFFFFF; TDC_MSB16 = (GDC_timestamp >> 32) & 0xFFFF; if (mytdc < TDC_LSB32) { @@ -384,8 +376,7 @@ std::vector readTimepix3RawData(const std::string &filepath) { } else if ((data_packet[7] & 0xF0) == 0xb0) { // NOTE: as of 2023-02-24, timing data packet cannot be used, using // alternative method to get the timing information - auto hit = packetToHitAlt(data_packet, rollover_counter, - previous_time, chip_layout_type); + auto hit = packetToHitAlt(data_packet, rollover_counter, previous_time, chip_layout_type); // std::cout << hit.toString() << std::endl; // Process the data into hit // auto hit = packetToHit(data_packet, TDC_timestamp, GDC_timestamp, @@ -728,40 +719,40 @@ void saveEventsToHDF5(const std::string out_file_name, const std::vector> name; - if (name == "abs_radius") { +/** + * @brief Parse user-defined parameters from a parameter file + * + * @param filepath: path to the parameter file. + * @return Params + */ + +Params parseUserDefinedParams(const std::string &filepath) { + // default ABS settings + double radius = 5.0; + unsigned long int min_cluster_size = 1; + unsigned long int spider_time_range = 75; + + std::ifstream user_defined_params_file(filepath); + std::string line; + + while (std::getline(user_defined_params_file, line)) { + std::istringstream ss(line); + std::string name; + ss >> name; + if (name == "abs_radius") { ss >> radius; - } else if (name == "abs_min_cluster_size") { + } else if (name == "abs_min_cluster_size") { ss >> min_cluster_size; - } else if (name == "spider_time_range") { + } else if (name == "spider_time_range") { ss >> spider_time_range; - } } + } - Params p(radius, min_cluster_size, spider_time_range); + Params p(radius, min_cluster_size, spider_time_range); - // prints out user-defined parameters - std::cout << "User-defined params file: " << filepath << std::endl; - std::cout << p.toString() << std::endl; + // prints out user-defined parameters + std::cout << "User-defined params file: " << filepath << std::endl; + std::cout << p.toString() << std::endl; - return p; - } + return p; +} diff --git a/sophiread/SophireadLib/tests/generate_fake_tpx3_data.cpp b/sophiread/SophireadLib/tests/generate_fake_tpx3_data.cpp index 105da84..c1f4541 100644 --- a/sophiread/SophireadLib/tests/generate_fake_tpx3_data.cpp +++ b/sophiread/SophireadLib/tests/generate_fake_tpx3_data.cpp @@ -1,248 +1,232 @@ -#include #include +#include #include #include unsigned long long packDataHeader(unsigned long long t, unsigned long long p, unsigned long long x, - unsigned long long three, unsigned long long chip_nr, - unsigned long long mode, unsigned long long num_bytes){ - unsigned long long header; - header = ((num_bytes & 0x000000000000FFFF) << 48) | - ((mode & 0x00000000000000FF) << 40) | - ((chip_nr & 0x00000000000000FF) << 32) | - ((three & 0x00000000000000FF) << 24) | - ((x & 0x00000000000000FF) << 16) | - ((p & 0x00000000000000FF) << 8) | - (t & 0x00000000000000FF); - - // check - // unsigned short t1 = (unsigned short) header & 0xFF; - // unsigned short p1 = (unsigned short) (header >> 8) & 0xFF; - // unsigned short x1 = (unsigned short) (header >> 16) & 0xFF; - // unsigned short three1 = (unsigned short) (header >> 24) & 0xFF; - // unsigned short chip_nr1 = (unsigned short) (header >> 32) & 0xFF; - // unsigned short mode1 = (unsigned short) (header >> 40) & 0xFF; - // unsigned short num_bytes1 = (unsigned short) (header >> 48) & 0xFFFF; - - // std::cout << "T: " << t1 - // << ", P: " << p1 - // << ", X: " << x1 - // << ", three: " << three1 - // << ", chip_nr: " << chip_nr1 - // << ", mode: " << mode1 - // << ", num_bytes: " << num_bytes1; - - return header; + unsigned long long three, unsigned long long chip_nr, unsigned long long mode, + unsigned long long num_bytes) { + unsigned long long header; + header = ((num_bytes & 0x000000000000FFFF) << 48) | ((mode & 0x00000000000000FF) << 40) | + ((chip_nr & 0x00000000000000FF) << 32) | ((three & 0x00000000000000FF) << 24) | + ((x & 0x00000000000000FF) << 16) | ((p & 0x00000000000000FF) << 8) | (t & 0x00000000000000FF); + + // check + // unsigned short t1 = (unsigned short) header & 0xFF; + // unsigned short p1 = (unsigned short) (header >> 8) & 0xFF; + // unsigned short x1 = (unsigned short) (header >> 16) & 0xFF; + // unsigned short three1 = (unsigned short) (header >> 24) & 0xFF; + // unsigned short chip_nr1 = (unsigned short) (header >> 32) & 0xFF; + // unsigned short mode1 = (unsigned short) (header >> 40) & 0xFF; + // unsigned short num_bytes1 = (unsigned short) (header >> 48) & 0xFFFF; + + // std::cout << "T: " << t1 + // << ", P: " << p1 + // << ", X: " << x1 + // << ", three: " << three1 + // << ", chip_nr: " << chip_nr1 + // << ", mode: " << mode1 + // << ", num_bytes: " << num_bytes1; + + return header; } -unsigned long long packPixelHit(unsigned long long spider_time, unsigned long long ftoa, - unsigned long long TOT, unsigned long long TOA, unsigned long long pixaddr){ - unsigned long long temp; - unsigned long long header = 0xb; - - temp = ((header & 0x00000000000000FF) << 60) | - ((pixaddr & 0x000000000000FFFF) << 44) | - ((TOA & 0x0000000000003FFF) << 30) | - ((TOT & 0x00000000000003FF) << 20) | - ((ftoa & 0x00000000000000FF) << 16) | - (spider_time & 0x000000000000FFFF); - - // check - // unsigned short spider_time1 = (unsigned short) temp & 0xFFFF; - // unsigned char ftoa1 = (unsigned char) (temp >> 16) & 0xFF; - // unsigned short TOT1 = (unsigned short) (temp >> 20) & 0x300; - // unsigned short TOA1 = (unsigned short) (temp >> 30) & 0x3FFF; - // unsigned short pixaddr1 = (unsigned short) (temp >> 44) & 0xFFFF; - // unsigned char header1 = (unsigned char) (temp >> 60) & 0xFF; - - // std::cout << "spider_time: " << std::hex << spider_time1 - // << ", ftoa: " << std::hex << +ftoa1 - // << ", TOT: " << std::hex << TOT1 - // << ", TOA: " << std::hex << TOA1 - // << ", pixaddr: " << std::hex << pixaddr1 - // << ", header: " << std::hex << +header1 << std::endl; - - unsigned long long spidertime = (spider_time << 14) | TOA; - std::cout << "spidertime + toa: " << spidertime << " s\n"; - - return temp; +unsigned long long packPixelHit(unsigned long long spider_time, unsigned long long ftoa, unsigned long long TOT, + unsigned long long TOA, unsigned long long pixaddr) { + unsigned long long temp; + unsigned long long header = 0xb; + + temp = ((header & 0x00000000000000FF) << 60) | ((pixaddr & 0x000000000000FFFF) << 44) | + ((TOA & 0x0000000000003FFF) << 30) | ((TOT & 0x00000000000003FF) << 20) | ((ftoa & 0x00000000000000FF) << 16) | + (spider_time & 0x000000000000FFFF); + + // check + // unsigned short spider_time1 = (unsigned short) temp & 0xFFFF; + // unsigned char ftoa1 = (unsigned char) (temp >> 16) & 0xFF; + // unsigned short TOT1 = (unsigned short) (temp >> 20) & 0x300; + // unsigned short TOA1 = (unsigned short) (temp >> 30) & 0x3FFF; + // unsigned short pixaddr1 = (unsigned short) (temp >> 44) & 0xFFFF; + // unsigned char header1 = (unsigned char) (temp >> 60) & 0xFF; + + // std::cout << "spider_time: " << std::hex << spider_time1 + // << ", ftoa: " << std::hex << +ftoa1 + // << ", TOT: " << std::hex << TOT1 + // << ", TOA: " << std::hex << TOA1 + // << ", pixaddr: " << std::hex << pixaddr1 + // << ", header: " << std::hex << +header1 << std::endl; + + unsigned long long spidertime = (spider_time << 14) | TOA; + std::cout << "spidertime + toa: " << spidertime << " s\n"; + + return temp; } -int main(int argc, char** argv){ - - // create a .tpx3 file - std::ofstream write_file("rollover_test_data.tpx3", std::ios::out | std::ios::binary); - if (!write_file){ - std::cout << "Cannot open file!" << std::endl; - return 1; - } - - unsigned long long temp; - temp = packDataHeader('T','P','X','3',0,0,208); - write_file.write((char*) &temp, sizeof(unsigned long long)); - - - // disorder pixel hit datapackets - // std::cout << "Disorder: increment counter" << std::endl; - // increment counter - temp = packPixelHit(0xF000,0xFF,0x3FF,0x3FFF,0x9876); // 25.1662, 1006649343 - write_file.write((char*) &temp, sizeof(unsigned long long)); - - temp = packPixelHit(0xFF00,0xFF,0x3FF,0x3FFF,0x9876); // 26.7391, 1069563903 - write_file.write((char*) &temp, sizeof(unsigned long long)); - - temp = packPixelHit(0xFFFF,0xFF,0x3FF,0x3FFF,0x9876); // 26.8435, 1073741823 - write_file.write((char*) &temp, sizeof(unsigned long long)); - - /* --------------------------------------------------------------*/ - - temp = packPixelHit(0x000F,0xFF,0x3FF,0x3FFF,0x9876); // 0.00655357, 262143 - write_file.write((char*) &temp, sizeof(unsigned long long)); - - temp = packPixelHit(0x00FF,0xFF,0x3FF,0x3FFF,0x9876); // 0.104858, 4194303 - write_file.write((char*) &temp, sizeof(unsigned long long)); +int main(int argc, char** argv) { + // create a .tpx3 file + std::ofstream write_file("rollover_test_data.tpx3", std::ios::out | std::ios::binary); + if (!write_file) { + std::cout << "Cannot open file!" << std::endl; + return 1; + } - temp = packPixelHit(0x0FFF,0xFF,0x3FF,0x3FFF,0x9876); // 1.67772, 67108863 - write_file.write((char*) &temp, sizeof(unsigned long long)); + unsigned long long temp; + temp = packDataHeader('T', 'P', 'X', '3', 0, 0, 208); + write_file.write((char*)&temp, sizeof(unsigned long long)); - temp = packPixelHit(0x2FFF,0xFF,0x3FF,0x3FFF,0x9876); // 5.03316, 201326591 - write_file.write((char*) &temp, sizeof(unsigned long long)); + // disorder pixel hit datapackets + // std::cout << "Disorder: increment counter" << std::endl; + // increment counter + temp = packPixelHit(0xF000, 0xFF, 0x3FF, 0x3FFF, 0x9876); // 25.1662, 1006649343 + write_file.write((char*)&temp, sizeof(unsigned long long)); + temp = packPixelHit(0xFF00, 0xFF, 0x3FF, 0x3FFF, 0x9876); // 26.7391, 1069563903 + write_file.write((char*)&temp, sizeof(unsigned long long)); - // disorder pixel hit datapackets - // std::cout << "Disorder: no changes counter" << std::endl; - // no changes to counter - // temp = packDataHeader('T','P','X','3',0,0,48); - // write_file.write((char*) &temp, sizeof(unsigned long long)); + temp = packPixelHit(0xFFFF, 0xFF, 0x3FF, 0x3FFF, 0x9876); // 26.8435, 1073741823 + write_file.write((char*)&temp, sizeof(unsigned long long)); - temp = packPixelHit(0x000F,0xFF,0x3FF,0x3FFF,0x9876); // 0.00655357, 262143 - write_file.write((char*) &temp, sizeof(unsigned long long)); + /* --------------------------------------------------------------*/ - temp = packPixelHit(0x3FFF,0xFF,0x3FF,0x3FFF,0x9876); // 6.71089, 268435455 - write_file.write((char*) &temp, sizeof(unsigned long long)); + temp = packPixelHit(0x000F, 0xFF, 0x3FF, 0x3FFF, 0x9876); // 0.00655357, 262143 + write_file.write((char*)&temp, sizeof(unsigned long long)); - temp = packPixelHit(0x00FF,0xFF,0x3FF,0x3FFF,0x9876); // 0.104858, 4194303 - write_file.write((char*) &temp, sizeof(unsigned long long)); + temp = packPixelHit(0x00FF, 0xFF, 0x3FF, 0x3FFF, 0x9876); // 0.104858, 4194303 + write_file.write((char*)&temp, sizeof(unsigned long long)); - temp = packPixelHit(0x0FFF,0xFF,0x3FF,0x3FFF,0x9876); // 1.67772, 67108863 - write_file.write((char*) &temp, sizeof(unsigned long long)); + temp = packPixelHit(0x0FFF, 0xFF, 0x3FF, 0x3FFF, 0x9876); // 1.67772, 67108863 + write_file.write((char*)&temp, sizeof(unsigned long long)); - temp = packPixelHit(0x1FFF,0xFF,0x3FF,0x3FFF,0x9876); // 3.35544, 134217727 - write_file.write((char*) &temp, sizeof(unsigned long long)); + temp = packPixelHit(0x2FFF, 0xFF, 0x3FF, 0x3FFF, 0x9876); // 5.03316, 201326591 + write_file.write((char*)&temp, sizeof(unsigned long long)); - temp = packPixelHit(0x2FFF,0xFF,0x3FF,0x3FFF,0x9876); // 5.03316, 201326591 - write_file.write((char*) &temp, sizeof(unsigned long long)); + // disorder pixel hit datapackets + // std::cout << "Disorder: no changes counter" << std::endl; + // no changes to counter + // temp = packDataHeader('T','P','X','3',0,0,48); + // write_file.write((char*) &temp, sizeof(unsigned long long)); + temp = packPixelHit(0x000F, 0xFF, 0x3FF, 0x3FFF, 0x9876); // 0.00655357, 262143 + write_file.write((char*)&temp, sizeof(unsigned long long)); - // in order pixel hit datapackets - // std::cout << "In order: decrement counter" << std::endl; - // decrement counter - // temp = packDataHeader('T','P','X','3',0,0,56); - // write_file.write((char*) &temp, sizeof(unsigned long long)); + temp = packPixelHit(0x3FFF, 0xFF, 0x3FF, 0x3FFF, 0x9876); // 6.71089, 268435455 + write_file.write((char*)&temp, sizeof(unsigned long long)); - temp = packPixelHit(0x000F,0xFF,0x3FF,0x3FFF,0x9876); // 0.00655357, 262143 - write_file.write((char*) &temp, sizeof(unsigned long long)); + temp = packPixelHit(0x00FF, 0xFF, 0x3FF, 0x3FFF, 0x9876); // 0.104858, 4194303 + write_file.write((char*)&temp, sizeof(unsigned long long)); - temp = packPixelHit(0x00FF,0xFF,0x3FF,0x3FFF,0x9876); // 0.104858, 4194303 - write_file.write((char*) &temp, sizeof(unsigned long long)); + temp = packPixelHit(0x0FFF, 0xFF, 0x3FF, 0x3FFF, 0x9876); // 1.67772, 67108863 + write_file.write((char*)&temp, sizeof(unsigned long long)); - temp = packPixelHit(0x0FFF,0xFF,0x3FF,0x3FFF,0x9876); // 1.67772, 67108863 - write_file.write((char*) &temp, sizeof(unsigned long long)); + temp = packPixelHit(0x1FFF, 0xFF, 0x3FF, 0x3FFF, 0x9876); // 3.35544, 134217727 + write_file.write((char*)&temp, sizeof(unsigned long long)); - temp = packPixelHit(0x2FFF,0xFF,0x3FF,0x3FFF,0x9876); // 5.03316, 201326591 - write_file.write((char*) &temp, sizeof(unsigned long long)); + temp = packPixelHit(0x2FFF, 0xFF, 0x3FF, 0x3FFF, 0x9876); // 5.03316, 201326591 + write_file.write((char*)&temp, sizeof(unsigned long long)); - /* --------------------------------------------------------------*/ + // in order pixel hit datapackets + // std::cout << "In order: decrement counter" << std::endl; + // decrement counter + // temp = packDataHeader('T','P','X','3',0,0,56); + // write_file.write((char*) &temp, sizeof(unsigned long long)); - temp = packPixelHit(0xF000,0xFF,0x3FF,0x3FFF,0x9876); // 25.1662, 1006649343 - write_file.write((char*) &temp, sizeof(unsigned long long)); + temp = packPixelHit(0x000F, 0xFF, 0x3FF, 0x3FFF, 0x9876); // 0.00655357, 262143 + write_file.write((char*)&temp, sizeof(unsigned long long)); - temp = packPixelHit(0xFF00,0xFF,0x3FF,0x3FFF,0x9876); // 26.7391, 1069563903 - write_file.write((char*) &temp, sizeof(unsigned long long)); + temp = packPixelHit(0x00FF, 0xFF, 0x3FF, 0x3FFF, 0x9876); // 0.104858, 4194303 + write_file.write((char*)&temp, sizeof(unsigned long long)); - temp = packPixelHit(0xFFFF,0xFF,0x3FF,0x3FFF,0x9876); // 26.8435, 1073741823 - write_file.write((char*) &temp, sizeof(unsigned long long)); + temp = packPixelHit(0x0FFF, 0xFF, 0x3FF, 0x3FFF, 0x9876); // 1.67772, 67108863 + write_file.write((char*)&temp, sizeof(unsigned long long)); - + temp = packPixelHit(0x2FFF, 0xFF, 0x3FF, 0x3FFF, 0x9876); // 5.03316, 201326591 + write_file.write((char*)&temp, sizeof(unsigned long long)); - // in order pixel - // std::cout << "In order: no changes counter" << std::endl; - // no changes to counter - // temp = packDataHeader('T','P','X','3',0,0,48); - // write_file.write((char*) &temp, sizeof(unsigned long long)); + /* --------------------------------------------------------------*/ - temp = packPixelHit(0x000F,0xFF,0x3FF,0x3FFF,0x9876); // 0.00655357, 262143 - write_file.write((char*) &temp, sizeof(unsigned long long)); + temp = packPixelHit(0xF000, 0xFF, 0x3FF, 0x3FFF, 0x9876); // 25.1662, 1006649343 + write_file.write((char*)&temp, sizeof(unsigned long long)); - temp = packPixelHit(0x00FF,0xFF,0x3FF,0x3FFF,0x9876); // 0.104858, 4194303 - write_file.write((char*) &temp, sizeof(unsigned long long)); + temp = packPixelHit(0xFF00, 0xFF, 0x3FF, 0x3FFF, 0x9876); // 26.7391, 1069563903 + write_file.write((char*)&temp, sizeof(unsigned long long)); - temp = packPixelHit(0x0FFF,0xFF,0x3FF,0x3FFF,0x9876); // 1.67772, 67108863 - write_file.write((char*) &temp, sizeof(unsigned long long)); + temp = packPixelHit(0xFFFF, 0xFF, 0x3FF, 0x3FFF, 0x9876); // 26.8435, 1073741823 + write_file.write((char*)&temp, sizeof(unsigned long long)); - temp = packPixelHit(0x1FFF,0xFF,0x3FF,0x3FFF,0x9876); // 3.35544, 134217727 - write_file.write((char*) &temp, sizeof(unsigned long long)); + // in order pixel + // std::cout << "In order: no changes counter" << std::endl; + // no changes to counter + // temp = packDataHeader('T','P','X','3',0,0,48); + // write_file.write((char*) &temp, sizeof(unsigned long long)); - temp = packPixelHit(0x2FFF,0xFF,0x3FF,0x3FFF,0x9876); // 5.03316, 201326591 - write_file.write((char*) &temp, sizeof(unsigned long long)); + temp = packPixelHit(0x000F, 0xFF, 0x3FF, 0x3FFF, 0x9876); // 0.00655357, 262143 + write_file.write((char*)&temp, sizeof(unsigned long long)); - temp = packPixelHit(0x3FFF,0xFF,0x3FF,0x3FFF,0x9876); // 6.71089, 268435455 - write_file.write((char*) &temp, sizeof(unsigned long long)); + temp = packPixelHit(0x00FF, 0xFF, 0x3FF, 0x3FFF, 0x9876); // 0.104858, 4194303 + write_file.write((char*)&temp, sizeof(unsigned long long)); + temp = packPixelHit(0x0FFF, 0xFF, 0x3FF, 0x3FFF, 0x9876); // 1.67772, 67108863 + write_file.write((char*)&temp, sizeof(unsigned long long)); - write_file.close(); + temp = packPixelHit(0x1FFF, 0xFF, 0x3FF, 0x3FFF, 0x9876); // 3.35544, 134217727 + write_file.write((char*)&temp, sizeof(unsigned long long)); - // read in .tpx3 file - std::ifstream read_file("rollover_test_data.tpx3", std::ios::out | std::ios::binary); - if (!read_file){ - std::cout << "Cannot open file!" << std::endl; - return 1; - } + temp = packPixelHit(0x2FFF, 0xFF, 0x3FF, 0x3FFF, 0x9876); // 5.03316, 201326591 + write_file.write((char*)&temp, sizeof(unsigned long long)); - std::cout << " \n Reading file: Checking \n"; + temp = packPixelHit(0x3FFF, 0xFF, 0x3FF, 0x3FFF, 0x9876); // 6.71089, 268435455 + write_file.write((char*)&temp, sizeof(unsigned long long)); - unsigned long long temp1; - read_file.read((char*) &temp1, sizeof(unsigned long long)); - unsigned short t1 = (unsigned short) temp1 & 0xFF; - unsigned short p1 = (unsigned short) (temp1 >> 8) & 0xFF; - unsigned short x1 = (unsigned short) (temp1 >> 16) & 0xFF; - unsigned short three1 = (unsigned short) (temp1 >> 24) & 0xFF; - unsigned short chip_nr1 = (unsigned short) (temp1 >> 32) & 0xFF; - unsigned short mode1 = (unsigned short) (temp1 >> 40) & 0xFF; - unsigned short num_bytes1 = (unsigned short) (temp1 >> 48) & 0xFFFF; + write_file.close(); - // std::cout << "T: " << t1 - // << ", P: " << p1 - // << ", X: " << x1 - // << ", three: " << three1 - // << ", chip_nr: " << chip_nr1 - // << ", mode: " << mode1 - // << ", num_bytes: " << num_bytes1 - // << std::endl; + // read in .tpx3 file + std::ifstream read_file("rollover_test_data.tpx3", std::ios::out | std::ios::binary); + if (!read_file) { + std::cout << "Cannot open file!" << std::endl; + return 1; + } - while (read_file.read((char*) &temp1, sizeof(unsigned long long))){ - // check - unsigned short spider_time1 = (unsigned short) temp1 & 0xFFFF; - unsigned char ftoa1 = (unsigned char) (temp1 >> 16) & 0xFF; - unsigned short TOT1 = (unsigned short) (temp1 >> 20) & 0x300; - unsigned short TOA1 = (unsigned short) (temp1 >> 30) & 0x3FFF; - unsigned short pixaddr1 = (unsigned short) (temp1 >> 44) & 0xFFFF; - unsigned char header1 = (unsigned char) (temp1 >> 60) & 0xFF; + std::cout << " \n Reading file: Checking \n"; - // std::cout << "spider_time: " << std::hex << spider_time1 - // << ", ftoa: " << std::hex << +ftoa1 - // << ", TOT: " << std::hex << TOT1 - // << ", TOA: " << std::hex << TOA1 - // << ", pixaddr: " << std::hex << pixaddr1 - // << ", header: " << std::hex << +header1 << std::endl; + unsigned long long temp1; + read_file.read((char*)&temp1, sizeof(unsigned long long)); + unsigned short t1 = (unsigned short)temp1 & 0xFF; + unsigned short p1 = (unsigned short)(temp1 >> 8) & 0xFF; + unsigned short x1 = (unsigned short)(temp1 >> 16) & 0xFF; + unsigned short three1 = (unsigned short)(temp1 >> 24) & 0xFF; + unsigned short chip_nr1 = (unsigned short)(temp1 >> 32) & 0xFF; + unsigned short mode1 = (unsigned short)(temp1 >> 40) & 0xFF; + unsigned short num_bytes1 = (unsigned short)(temp1 >> 48) & 0xFFFF; - unsigned int spidertime1 = (spider_time1 << 14) | TOA1; - std::cout << "spidertime + toa: " << spidertime1 << " s\n"; - } - + // std::cout << "T: " << t1 + // << ", P: " << p1 + // << ", X: " << x1 + // << ", three: " << three1 + // << ", chip_nr: " << chip_nr1 + // << ", mode: " << mode1 + // << ", num_bytes: " << num_bytes1 + // << std::endl; - read_file.close(); + while (read_file.read((char*)&temp1, sizeof(unsigned long long))) { + // check + unsigned short spider_time1 = (unsigned short)temp1 & 0xFFFF; + unsigned char ftoa1 = (unsigned char)(temp1 >> 16) & 0xFF; + unsigned short TOT1 = (unsigned short)(temp1 >> 20) & 0x300; + unsigned short TOA1 = (unsigned short)(temp1 >> 30) & 0x3FFF; + unsigned short pixaddr1 = (unsigned short)(temp1 >> 44) & 0xFFFF; + unsigned char header1 = (unsigned char)(temp1 >> 60) & 0xFF; - return 0; + // std::cout << "spider_time: " << std::hex << spider_time1 + // << ", ftoa: " << std::hex << +ftoa1 + // << ", TOT: " << std::hex << TOT1 + // << ", TOA: " << std::hex << TOA1 + // << ", pixaddr: " << std::hex << pixaddr1 + // << ", header: " << std::hex << +header1 << std::endl; + + unsigned int spidertime1 = (spider_time1 << 14) | TOA1; + std::cout << "spidertime + toa: " << spidertime1 << " s\n"; + } + + read_file.close(); + return 0; } \ No newline at end of file diff --git a/sophiread/SophireadLib/tests/test_clustering.cpp b/sophiread/SophireadLib/tests/test_clustering.cpp index 96bae7e..5f8a02a 100644 --- a/sophiread/SophireadLib/tests/test_clustering.cpp +++ b/sophiread/SophireadLib/tests/test_clustering.cpp @@ -53,7 +53,7 @@ TEST(Clustering, ABSAlgorithm) { auto data = gen_clusters(); // create the ABS algorithm - ABS abs(5.,1,75); + ABS abs(5., 1, 75); abs.fit(data); abs.set_method("centroid"); auto events = abs.get_events(data); @@ -63,10 +63,8 @@ TEST(Clustering, ABSAlgorithm) { // OPENMP will shuffle the order of the events, we need to extract and sort // them before asserting - std::vector x = {events[0].getX(), events[1].getX(), - events[2].getX()}; - std::vector y = {events[0].getY(), events[1].getY(), - events[2].getY()}; + std::vector x = {events[0].getX(), events[1].getX(), events[2].getX()}; + std::vector y = {events[0].getY(), events[1].getY(), events[2].getY()}; std::sort(x.begin(), x.end()); std::sort(y.begin(), y.end()); @@ -90,18 +88,15 @@ TEST(Clustering, DBSCANAlgorithm) { auto data = gen_clusters(); // create the DSCAN algorithm - DBSCAN dbs(8000. /*eps time*/, 30 /*min_points time*/, 4. /*eps xy*/, - 10 /*min_points xy*/); + DBSCAN dbs(8000. /*eps time*/, 30 /*min_points time*/, 4. /*eps xy*/, 10 /*min_points xy*/); auto events = dbs.get_events(data); // dbs.fit(data); // check that there are 3 events EXPECT_EQ(events.size(), 3); - std::vector x = {events[0].getX(), events[1].getX(), - events[2].getX()}; - std::vector y = {events[0].getY(), events[1].getY(), - events[2].getY()}; + std::vector x = {events[0].getX(), events[1].getX(), events[2].getX()}; + std::vector y = {events[0].getY(), events[1].getY(), events[2].getY()}; std::sort(x.begin(), x.end()); std::sort(y.begin(), y.end()); diff --git a/sophiread/SophireadLib/tests/test_peakfitting.cpp b/sophiread/SophireadLib/tests/test_peakfitting.cpp index acffc8e..2151ffb 100644 --- a/sophiread/SophireadLib/tests/test_peakfitting.cpp +++ b/sophiread/SophireadLib/tests/test_peakfitting.cpp @@ -32,24 +32,18 @@ TEST(PeakFitting, CentroidAlgorithm) { NeutronEvent event = alg.fit(hits); // Check that the event is correct - EXPECT_NEAR(event.getX(), 1863.66 * DSCALE, absolute_error) - << "Centroid x is not correct."; - EXPECT_NEAR(event.getY(), 2718.74 * DSCALE, absolute_error) - << "Centroid y is not correct."; - EXPECT_NEAR(event.getTOF(), 2262.67, absolute_error) - << "Centroid tof is not correct."; + EXPECT_NEAR(event.getX(), 1863.66 * DSCALE, absolute_error) << "Centroid x is not correct."; + EXPECT_NEAR(event.getY(), 2718.74 * DSCALE, absolute_error) << "Centroid y is not correct."; + EXPECT_NEAR(event.getTOF(), 2262.67, absolute_error) << "Centroid tof is not correct."; // CASE_2: not weighted by tot Centroid alg2(false); NeutronEvent event2 = alg2.fit(hits); // Check that the event is correct - EXPECT_NEAR(event2.getX(), 1845.67 * DSCALE, absolute_error) - << "Centroid x is not correct."; - EXPECT_NEAR(event2.getY(), 2674.33 * DSCALE, absolute_error) - << "Centroid y is not correct."; - EXPECT_NEAR(event2.getTOF(), 2262.67, absolute_error) - << "Centroid tof is not correct."; + EXPECT_NEAR(event2.getX(), 1845.67 * DSCALE, absolute_error) << "Centroid x is not correct."; + EXPECT_NEAR(event2.getY(), 2674.33 * DSCALE, absolute_error) << "Centroid y is not correct."; + EXPECT_NEAR(event2.getTOF(), 2262.67, absolute_error) << "Centroid tof is not correct."; } TEST(PeakFitting, FastGaussianAlgorithm) { @@ -63,8 +57,7 @@ TEST(PeakFitting, FastGaussianAlgorithm) { int y = 50 + pos(gen); int mytof = 1000 + tof(gen); int stime = 10 + spidertime(gen); - hits.push_back( - Hit(x, y, 1 + tot(gen), 1 + toa(gen), ftoa(gen), mytof, stime)); + hits.push_back(Hit(x, y, 1 + tot(gen), 1 + toa(gen), ftoa(gen), mytof, stime)); } // Create a fast gaussian algorithm @@ -72,10 +65,7 @@ TEST(PeakFitting, FastGaussianAlgorithm) { NeutronEvent event = alg.fit(hits); // Check that the event is correct - EXPECT_NEAR(event.getX(), 50 * DSCALE, absolute_error) - << "FastGaussian x is not correct."; - EXPECT_NEAR(event.getY(), 50 * DSCALE, absolute_error) - << "FastGaussian y is not correct."; - EXPECT_NEAR(event.getTOF(), 1000, absolute_error) - << "FastGaussian tof is not correct."; + EXPECT_NEAR(event.getX(), 50 * DSCALE, absolute_error) << "FastGaussian x is not correct."; + EXPECT_NEAR(event.getY(), 50 * DSCALE, absolute_error) << "FastGaussian y is not correct."; + EXPECT_NEAR(event.getTOF(), 1000, absolute_error) << "FastGaussian tof is not correct."; } diff --git a/sophiread/SophireadLib/tests/test_tpx3.cpp b/sophiread/SophireadLib/tests/test_tpx3.cpp index 3a630fa..1712a87 100644 --- a/sophiread/SophireadLib/tests/test_tpx3.cpp +++ b/sophiread/SophireadLib/tests/test_tpx3.cpp @@ -1,4 +1,5 @@ #include + #include #include "tpx3.h" @@ -6,8 +7,7 @@ // Test the readTimepix3RawData function TEST(FileHandlingTest, ReadTPX3RawData) { // read the testing raw data - auto hits = - readTimepix3RawData("../data/frames_pinhole_3mm_1s_RESOLUTION_000001.tpx3"); + auto hits = readTimepix3RawData("../data/frames_pinhole_3mm_1s_RESOLUTION_000001.tpx3"); // check the number of hits EXPECT_EQ(hits.size(), 9933804); @@ -31,8 +31,7 @@ TEST(FileHandlingTest, ReadTPX3RawData) { TEST(FileHandlingTest, VerifyTiming) { // read the testing raw data - auto hits = - readTimepix3RawData("../data/greg_220ms_TDC_GDC_enabled_20230214_3.tpx3"); + auto hits = readTimepix3RawData("../data/greg_220ms_TDC_GDC_enabled_20230214_3.tpx3"); // check the number of hits EXPECT_EQ(hits.size(), 365); @@ -54,64 +53,60 @@ TEST(FileHandlingTest, VerifyTiming) { // EXPECT_EQ(hits[365 - 1].getSPIDERTIME(), 8809347419); } -TEST(FileHandlingTest, VerifyRollover){ - // reading the testing raw data - auto hits = - readTimepix3RawData("../data/rollover_test_data.tpx3"); +TEST(FileHandlingTest, VerifyRollover) { + // reading the testing raw data + auto hits = readTimepix3RawData("../data/rollover_test_data.tpx3"); // check the number of hits - EXPECT_EQ(hits.size(),26); - - // check disordered pixels: increment counter - EXPECT_EQ(hits[0].getSPIDERTIME(),1006649343); // 25.1662, 1006649343 - EXPECT_EQ(hits[1].getSPIDERTIME(),1069563903); // 26.7391, 1069563903 - EXPECT_EQ(hits[2].getSPIDERTIME(),1073741823); // 26.8435, 1073741823 - EXPECT_EQ(hits[3].getSPIDERTIME(),262143 + 1073741824); // 0.00655357, 262143 - EXPECT_EQ(hits[4].getSPIDERTIME(),4194303 + 1073741824); // 0.104858, 4194303 - EXPECT_EQ(hits[5].getSPIDERTIME(),67108863 + 1073741824); // 1.67772, 67108863 + EXPECT_EQ(hits.size(), 26); + + // check disordered pixels: increment counter + EXPECT_EQ(hits[0].getSPIDERTIME(), 1006649343); // 25.1662, 1006649343 + EXPECT_EQ(hits[1].getSPIDERTIME(), 1069563903); // 26.7391, 1069563903 + EXPECT_EQ(hits[2].getSPIDERTIME(), 1073741823); // 26.8435, 1073741823 + EXPECT_EQ(hits[3].getSPIDERTIME(), 262143 + 1073741824); // 0.00655357, 262143 + EXPECT_EQ(hits[4].getSPIDERTIME(), 4194303 + 1073741824); // 0.104858, 4194303 + EXPECT_EQ(hits[5].getSPIDERTIME(), 67108863 + 1073741824); // 1.67772, 67108863 EXPECT_EQ(hits[6].getSPIDERTIME(), 201326591 + 1073741824); // 5.03316, 201326591 // check disordered pixels: do nothing to counter - EXPECT_EQ(hits[7].getSPIDERTIME(),262143 + 1073741824); // 0.00655357, 262143 - EXPECT_EQ(hits[8].getSPIDERTIME(),268435455 + 1073741824); // 6.71089, 268435455 - EXPECT_EQ(hits[9].getSPIDERTIME(),4194303 + 1073741824); // 0.104858, 4194303 - EXPECT_EQ(hits[10].getSPIDERTIME(),67108863 + 1073741824); // 1.67772, 67108863 - EXPECT_EQ(hits[11].getSPIDERTIME(),134217727 + 1073741824); // 3.35544, 134217727 - EXPECT_EQ(hits[12].getSPIDERTIME(),201326591 + 1073741824); // 5.03316, 201326591 - - // check order pixels: decrement counter - EXPECT_EQ(hits[13].getSPIDERTIME(),262143 + 1073741824); // 0.00655357, 262143 - EXPECT_EQ(hits[14].getSPIDERTIME(),4194303 + 1073741824); // 0.104858, 4194303 - EXPECT_EQ(hits[15].getSPIDERTIME(),67108863 + 1073741824); // 1.67772, 67108863 - EXPECT_EQ(hits[16].getSPIDERTIME(),201326591 + 1073741824); // 5.03316, 201326591 - EXPECT_EQ(hits[17].getSPIDERTIME(),1006649343); // 25.1662, 1006649343 - EXPECT_EQ(hits[18].getSPIDERTIME(),1069563903); // 26.7391, 1069563903 - EXPECT_EQ(hits[19].getSPIDERTIME(),1073741823); // 26.8435, 1073741823 - - // check ordered pixels: do nothing to counter - EXPECT_EQ(hits[20].getSPIDERTIME(),262143 + 1073741824); // 0.00655357, 262143 - EXPECT_EQ(hits[21].getSPIDERTIME(),4194303 + 1073741824); // 0.104858, 4194303 - EXPECT_EQ(hits[22].getSPIDERTIME(),67108863 + 1073741824); // 1.67772, 67108863 - EXPECT_EQ(hits[23].getSPIDERTIME(),134217727 + 1073741824); // 3.35544, 134217727 - EXPECT_EQ(hits[24].getSPIDERTIME(),201326591 + 1073741824); // 5.03316, 201326591 - EXPECT_EQ(hits[25].getSPIDERTIME(),268435455 + 1073741824); // 6.71089, 268435455 - + EXPECT_EQ(hits[7].getSPIDERTIME(), 262143 + 1073741824); // 0.00655357, 262143 + EXPECT_EQ(hits[8].getSPIDERTIME(), 268435455 + 1073741824); // 6.71089, 268435455 + EXPECT_EQ(hits[9].getSPIDERTIME(), 4194303 + 1073741824); // 0.104858, 4194303 + EXPECT_EQ(hits[10].getSPIDERTIME(), 67108863 + 1073741824); // 1.67772, 67108863 + EXPECT_EQ(hits[11].getSPIDERTIME(), 134217727 + 1073741824); // 3.35544, 134217727 + EXPECT_EQ(hits[12].getSPIDERTIME(), 201326591 + 1073741824); // 5.03316, 201326591 + + // check order pixels: decrement counter + EXPECT_EQ(hits[13].getSPIDERTIME(), 262143 + 1073741824); // 0.00655357, 262143 + EXPECT_EQ(hits[14].getSPIDERTIME(), 4194303 + 1073741824); // 0.104858, 4194303 + EXPECT_EQ(hits[15].getSPIDERTIME(), 67108863 + 1073741824); // 1.67772, 67108863 + EXPECT_EQ(hits[16].getSPIDERTIME(), 201326591 + 1073741824); // 5.03316, 201326591 + EXPECT_EQ(hits[17].getSPIDERTIME(), 1006649343); // 25.1662, 1006649343 + EXPECT_EQ(hits[18].getSPIDERTIME(), 1069563903); // 26.7391, 1069563903 + EXPECT_EQ(hits[19].getSPIDERTIME(), 1073741823); // 26.8435, 1073741823 + + // check ordered pixels: do nothing to counter + EXPECT_EQ(hits[20].getSPIDERTIME(), 262143 + 1073741824); // 0.00655357, 262143 + EXPECT_EQ(hits[21].getSPIDERTIME(), 4194303 + 1073741824); // 0.104858, 4194303 + EXPECT_EQ(hits[22].getSPIDERTIME(), 67108863 + 1073741824); // 1.67772, 67108863 + EXPECT_EQ(hits[23].getSPIDERTIME(), 134217727 + 1073741824); // 3.35544, 134217727 + EXPECT_EQ(hits[24].getSPIDERTIME(), 201326591 + 1073741824); // 5.03316, 201326591 + EXPECT_EQ(hits[25].getSPIDERTIME(), 268435455 + 1073741824); // 6.71089, 268435455 } // Test the parseUserDefinedParams function TEST(FileHandlingTest, ParseUserDefinedParams) { - - // read user-defined param files + // read user-defined param files auto p1 = parseUserDefinedParams("../data/user_defined_params.txt"); auto p2 = parseUserDefinedParams("../data/user_defined_params_1.txt"); - // check user-defined params + // check user-defined params EXPECT_EQ(p1.getABSRadius(), 5.0); - EXPECT_EQ(p1.getABSMinClusterSize(),1); + EXPECT_EQ(p1.getABSMinClusterSize(), 1); EXPECT_EQ(p1.getABSSpidertimeRange(), 75); EXPECT_EQ(p2.getABSRadius(), 20.0); - EXPECT_EQ(p2.getABSMinClusterSize(),30); - EXPECT_EQ(p2.getABSSpidertimeRange(),500000); - + EXPECT_EQ(p2.getABSMinClusterSize(), 30); + EXPECT_EQ(p2.getABSSpidertimeRange(), 500000); } \ No newline at end of file diff --git a/sophiread/SophireadStreamCLI/src/sophiread_stream.cpp b/sophiread/SophireadStreamCLI/src/sophiread_stream.cpp index 794c541..25f0260 100644 --- a/sophiread/SophireadStreamCLI/src/sophiread_stream.cpp +++ b/sophiread/SophireadStreamCLI/src/sophiread_stream.cpp @@ -25,7 +25,7 @@ std::vector neutron_events; */ void process_batch(const std::vector &batch) { ClusteringAlgorithm *alg; - alg = new ABS(5.0,1,75); // select clustering algorithm + alg = new ABS(5.0, 1, 75); // select clustering algorithm alg->set_method("fast_gaussian"); // select peak fitting method alg->fit(batch); std::vector events = alg->get_events(batch); @@ -89,8 +89,7 @@ void reader(std::string filename) { if (char_array[7] == 0x6F) { // TDC data packets tdclast = (unsigned long *)(&char_array[0]); - mytdc = (((*tdclast) >> 12) & - 0xFFFFFFFF); // rick: 0x3fffffff, get 32-bit tdc + mytdc = (((*tdclast) >> 12) & 0xFFFFFFFF); // rick: 0x3fffffff, get 32-bit tdc TDC_LSB32 = GDC_timestamp & 0xFFFFFFFF; TDC_MSB16 = (GDC_timestamp >> 32) & 0xFFFF; if (mytdc < TDC_LSB32) { @@ -117,10 +116,8 @@ void reader(std::string filename) { } } else if ((char_array[7] & 0xF0) == 0xb0) { // Process the data into hit - auto data_packet = std::vector( - char_array, char_array + sizeof(char_array) / sizeof(char)); - auto hit = packetToHit(data_packet, TDC_timestamp, GDC_timestamp, - chip_layout_type); + auto data_packet = std::vector(char_array, char_array + sizeof(char_array) / sizeof(char)); + auto hit = packetToHit(data_packet, TDC_timestamp, GDC_timestamp, chip_layout_type); // std::cout << "Hits: " << hit.getX() << " " << hit.getY() << " " << // hit.getTOF_ns()*1E-6 << " " << hit.getSPIDERTIME_ns()*1E-9 << // std::endl; diff --git a/sophiread/include/version.h b/sophiread/include/version.h index 4d9f4f1..5465154 100644 --- a/sophiread/include/version.h +++ b/sophiread/include/version.h @@ -16,9 +16,7 @@ #define VERSION_PATCH 0 // Version number final -#define VERSION_NUMBER \ - ((unsigned long)(VERSION_MAJOR << 16) | (VERSION_MINOR << 8) | \ - (VERSION_PATCH)) +#define VERSION_NUMBER ((unsigned long)(VERSION_MAJOR << 16) | (VERSION_MINOR << 8) | (VERSION_PATCH)) // Version string #define VERSION_STRING \ diff --git a/sophiread/resources/manufacture/tpx3cam.cpp b/sophiread/resources/manufacture/tpx3cam.cpp index 9c38e7e..b20dc4f 100644 --- a/sophiread/resources/manufacture/tpx3cam.cpp +++ b/sophiread/resources/manufacture/tpx3cam.cpp @@ -2,204 +2,196 @@ Example C++ script to convert a raw data .tpx3 to text file - copyright JL@ Amsterdam Scientific Instruments B.V. - www.amscins.com - 05-12-2019 + copyright JL@ Amsterdam Scientific Instruments B.V. + www.amscins.com + 05-12-2019 ****************************************************************/ #include -#include + +#include #include #include +#include #include -#include using namespace std; +int main(int argc, char* argv[]) { + char* TPX_name; + if (argc == 1) { + cout << "Usage: tpx3 raw data" << endl << endl; + return 0; + } else { + TPX_name = argv[1]; + cout << "imported file: " << TPX_name << endl; + } + + ofstream xy_file("converted.txt"); // Converted and saved txt file + streampos begin, end; + ifstream myfile(TPX_name, ios::binary); + unsigned short xpix, ypix, TOT, TOA, spidrTime; + char chipnr, FTOA; + int frameNr; + int CTOA; + int mode; + unsigned long Timer_LSB32 = 0; + unsigned long Timer_MSB16 = 0; + unsigned long numofTDC = 0; + + if (!myfile) { + cout << "This file is not found!" << endl; + } else { + myfile.seekg(0, myfile.end); + unsigned long long fileLength = myfile.tellg(); + myfile.seekg(0, myfile.beg); + unsigned long long temp64; + cout << "filesize: " << fileLength / (1024 * 1024) << "MB" << endl; + unsigned long long NumofPacket = fileLength / 8; + unsigned long long* datapacket = new unsigned long long[NumofPacket]; + myfile.read((char*)datapacket, fileLength); + myfile.close(); + char* HeaderBuffer = new char[8]; + unsigned long long temp; + + for (unsigned long long i = 0; i < NumofPacket; i++) { + memcpy(HeaderBuffer, &datapacket[i], 8); + if (HeaderBuffer[0] == 'T' && HeaderBuffer[1] == 'P' && HeaderBuffer[2] == 'X') { + int size = ((0xff & HeaderBuffer[7]) << 8) | (0xff & HeaderBuffer[6]); + chipnr = HeaderBuffer[4]; + mode = HeaderBuffer[5]; + for (int j = 0; j < size / 8; j++) { + temp = datapacket[i + j + 1]; + int hdr = (int)(temp >> 56); + int packet = temp >> 60; + double coarsetime; + unsigned long tmpfine; + unsigned long trigtime_fine; + double time_unit, global_timestamp; + int trigger_counter; + unsigned long long int timemaster; + int heartbeatL, heartbeatM; + double TDC_timestamp; + double spidrTimens; + int x, y; + double TOTns; + double TOAns; + long dcol; + long spix; + long pix; + + switch (packet) { + case 0x6: // TDC timestamp packet header + + if ((temp >> 56) == 0x6f) cout << "tdc1 rising edge is working" << endl; + if ((temp >> 56) == 0x6a) cout << "tdc1 falling edge is working" << endl; + if ((temp >> 56) == 0x6e) cout << "tdc2 rising edge is working" << endl; + if ((temp >> 56) == 0x6b) cout << "tdc2 falling edge is working" << endl; + coarsetime = (temp >> 12) & 0xFFFFFFFF; + tmpfine = (temp >> 5) & 0xF; + tmpfine = ((tmpfine - 1) << 9) / 12; + trigtime_fine = (temp & 0x0000000000000E00) | (tmpfine & 0x00000000000001FF); + time_unit = 25. / 4096; + trigger_counter = temp >> 44 & 0xFFF; + TDC_timestamp = coarsetime * 25E-9 + trigtime_fine * time_unit * 1E-9; + // uncomment below to save TDC timestamps into the txt file + xy_file << setprecision(15) << TDC_timestamp << endl; + // cout<< "TDC timestamp: " << setprecision(15) << TDC_timestamp << endl; + numofTDC = numofTDC + 1; + break; + + case 0xb: // Chip data: ToA and ToT timestamp packet, x, y + + spidrTime = (unsigned short)(temp & 0xffff); + dcol = (temp & 0x0FE0000000000000L) >> 52; + spix = (temp & 0x001F800000000000L) >> 45; + pix = (temp & 0x0000700000000000L) >> 44; + x = (int)(dcol + pix / 4); + y = (int)(spix + (pix & 0x3)); + TOA = (unsigned short)((temp >> (16 + 14)) & 0x3fff); + TOT = (unsigned short)((temp >> (16 + 4)) & 0x3ff); + FTOA = (unsigned char)((temp >> 16) & 0xf); + CTOA = (TOA << 4) | (~FTOA & 0xf); + spidrTimens = spidrTime * 25.0 * 16384.0; + TOAns = TOA * 25.0; + TOTns = TOT * 25.0; + global_timestamp = spidrTimens + CTOA * (25.0 / 16); + + /************************************************************ + Condition is different for single Timepix3 chip or quad chips: + Single chip, using "int (Chipnr) +3" + Quad chips, using "int (Chipnr)" + ************************************************************/ + switch (int(chipnr)) // for quad chips; + { + case 0: + x += 260; + y = y; + break; + + case 1: + x = 255 - x + 260; + y = 255 - y + 260; + break; + + case 2: + x = 255 - x; + y = 255 - y + 260; + break; + + case 3: + break; + + default: + break; + } + + // uncomment below to save the chip data into the text file; + xy_file << setprecision(15) << x << " " << y << " " << global_timestamp / 1E9 << " " << TOTns + << endl; // x, y, toa, tot data can be saved into txt data + cout << "Chip-ToA: " << setprecision(15) << global_timestamp / 1E9 << " ToT: " << TOTns << " x: " << x + << " y: " << y << endl; + + break; + + case 0x4: // the global timestamps. + + if (((temp >> 56) & 0xF) == 0x4) { + Timer_LSB32 = (temp >> 16) & 0xFFFFFFFF; + } else if (((temp >> 56) & 0xF) == 0x5) { + Timer_MSB16 = (temp >> 16) & 0xFFFF; + unsigned long long int timemaster; + timemaster = Timer_MSB16; + timemaster = (timemaster << 32) & 0xFFFF00000000; + timemaster = timemaster | Timer_LSB32; + int diff = (spidrTime >> 14) - ((Timer_LSB32 >> 28) & 0x3); + + if ((spidrTime >> 14) == ((Timer_LSB32 >> 28) & 0x3)) { + } else { + Timer_MSB16 = Timer_MSB16 - diff; + } + // uncomment below to save the global timestamps into the text file; + xy_file << " Global time: " << setprecision(15) << timemaster * 25e-9 + << endl; // global timestamps can be saved into text file + } -int main(int argc, char *argv[]) -{ - char* TPX_name; - if (argc == 1) { - cout << "Usage: tpx3 raw data" << endl << endl; - return 0; - } - else { - TPX_name = argv[1]; - cout << "imported file: " << TPX_name << endl; - } - - ofstream xy_file("converted.txt"); //Converted and saved txt file - streampos begin, end; - ifstream myfile(TPX_name, ios::binary); - unsigned short xpix, ypix, TOT, TOA, spidrTime; - char chipnr, FTOA; - int frameNr; - int CTOA; - int mode; - unsigned long Timer_LSB32 = 0; - unsigned long Timer_MSB16 = 0; - unsigned long numofTDC=0; - - if (!myfile) { - cout << "This file is not found!" << endl; - } - else { - myfile.seekg(0, myfile.end); - unsigned long long fileLength = myfile.tellg(); - myfile.seekg(0, myfile.beg); - unsigned long long temp64; - cout << "filesize: " << fileLength/(1024*1024) <<"MB" << endl; - unsigned long long NumofPacket = fileLength / 8; - unsigned long long* datapacket = new unsigned long long [NumofPacket]; - myfile.read((char*) datapacket, fileLength); - myfile.close(); - char* HeaderBuffer = new char[8]; - unsigned long long temp; - - for (unsigned long long i = 0; i < NumofPacket; i++) { - memcpy(HeaderBuffer, &datapacket[i], 8); - if (HeaderBuffer[0] == 'T' && HeaderBuffer[1] == 'P' && HeaderBuffer[2] == 'X') { - int size = ((0xff & HeaderBuffer[7]) << 8) | (0xff & HeaderBuffer[6]); - chipnr = HeaderBuffer[4]; - mode = HeaderBuffer[5]; - for (int j = 0; j < size / 8; j++) { - temp = datapacket[i + j + 1]; - int hdr = (int)(temp >> 56); - int packet = temp >> 60; - double coarsetime; - unsigned long tmpfine; - unsigned long trigtime_fine; - double time_unit, global_timestamp; - int trigger_counter; - unsigned long long int timemaster; - int heartbeatL, heartbeatM; - double TDC_timestamp; - double spidrTimens; - int x, y; - double TOTns; - double TOAns; - long dcol; - long spix; - long pix; - - switch (packet) - { - case 0x6: //TDC timestamp packet header - - if ((temp >> 56) == 0x6f) cout << "tdc1 rising edge is working" << endl; - if ((temp >> 56) == 0x6a) cout << "tdc1 falling edge is working" << endl; - if ((temp >> 56) == 0x6e) cout << "tdc2 rising edge is working" << endl; - if ((temp >> 56) == 0x6b) cout << "tdc2 falling edge is working" << endl; - coarsetime = (temp >> 12) & 0xFFFFFFFF; - tmpfine = (temp >> 5) & 0xF; - tmpfine = ((tmpfine - 1) << 9) / 12; - trigtime_fine = (temp & 0x0000000000000E00) | (tmpfine & 0x00000000000001FF); - time_unit = 25. / 4096; - trigger_counter = temp >> 44 & 0xFFF; - TDC_timestamp = coarsetime * 25E-9 + trigtime_fine * time_unit*1E-9; - //uncomment below to save TDC timestamps into the txt file - xy_file << setprecision(15) << TDC_timestamp << endl; - // cout<< "TDC timestamp: " << setprecision(15) << TDC_timestamp << endl; - numofTDC=numofTDC+1; - break; - - case 0xb: //Chip data: ToA and ToT timestamp packet, x, y - - spidrTime = (unsigned short)(temp & 0xffff); - dcol = (temp & 0x0FE0000000000000L) >> 52; - spix = (temp & 0x001F800000000000L) >> 45; - pix = (temp & 0x0000700000000000L) >> 44; - x = (int)(dcol + pix / 4); - y = (int)(spix + (pix & 0x3)); - TOA = (unsigned short)((temp >> (16 + 14)) & 0x3fff); - TOT = (unsigned short)((temp >> (16 + 4)) & 0x3ff); - FTOA = (unsigned char)((temp >> 16) & 0xf); - CTOA = (TOA << 4) | (~FTOA & 0xf); - spidrTimens = spidrTime * 25.0 * 16384.0; - TOAns = TOA * 25.0; - TOTns = TOT * 25.0; - global_timestamp = spidrTimens + CTOA * (25.0 / 16); - - /************************************************************ - Condition is different for single Timepix3 chip or quad chips: - Single chip, using "int (Chipnr) +3" - Quad chips, using "int (Chipnr)" - ************************************************************/ - switch (int (chipnr)) // for quad chips; - { - - case 0: - x += 260; - y = y; - break; - - case 1: - x = 255 - x + 260; - y = 255 - y + 260; - break; - - case 2: - x = 255 - x; - y = 255 - y + 260; - break; - - case 3: - break; - - default: - break; - - } - - //uncomment below to save the chip data into the text file; - xy_file << setprecision(15) << x << " " << y << " " << global_timestamp / 1E9 << " " << TOTns << endl; //x, y, toa, tot data can be saved into txt data - cout<< "Chip-ToA: " << setprecision(15) << global_timestamp / 1E9 << " ToT: " << TOTns << " x: " << x << " y: " << y << endl; - - break; - - case 0x4: //the global timestamps. - - if (((temp >> 56) & 0xF) == 0x4) { - Timer_LSB32 = (temp >> 16) & 0xFFFFFFFF; - } - else if (((temp >> 56) & 0xF) == 0x5) - { - Timer_MSB16 = (temp >> 16) & 0xFFFF; - unsigned long long int timemaster; - timemaster = Timer_MSB16; - timemaster = (timemaster << 32) & 0xFFFF00000000; - timemaster = timemaster | Timer_LSB32; - int diff = (spidrTime >> 14) - ((Timer_LSB32 >> 28) & 0x3); - - if ((spidrTime >> 14) == ((Timer_LSB32 >> 28) & 0x3)) - { - } - else { - Timer_MSB16 = Timer_MSB16 - diff; - } - //uncomment below to save the global timestamps into the text file; - xy_file << " Global time: " << setprecision(15) << timemaster * 25e-9 << endl; //global timestamps can be saved into text file - } - - break; - - default: - break; - } + break; - } - i += (size / 8); - printf("i : %lld\r", i); - } + default: + break; + } } - - delete [] HeaderBuffer; - delete [] datapacket; + i += (size / 8); + printf("i : %lld\r", i); + } } - cout<<"the number of TDCs: "< Date: Thu, 22 Aug 2024 15:13:41 -0400 Subject: [PATCH 31/38] Revert "clang-format" This reverts commit dcabc22b8b7b8b5800f44b74cc781ff7f4420d9e. --- sophiread/CMakeLists.txt | 9 - .../benchmarks/benchmark_mmap.cpp | 330 +++++++------- sophiread/FastSophiread/include/abs.h | 2 +- sophiread/FastSophiread/include/centroid.h | 4 +- sophiread/FastSophiread/include/disk_io.h | 6 +- .../FastSophiread/include/fastgaussian.h | 4 +- sophiread/FastSophiread/include/hit.h | 6 +- .../FastSophiread/include/iposition_tof.h | 12 +- sophiread/FastSophiread/include/neutron.h | 2 +- sophiread/FastSophiread/include/tpx3_fast.h | 8 +- sophiread/FastSophiread/src/disk_io.cpp | 38 +- sophiread/FastSophiread/src/hit.cpp | 7 +- sophiread/FastSophiread/src/tpx3_fast.cpp | 25 +- sophiread/FastSophiread/tests/test_tpx3.cpp | 74 ++-- sophiread/SophireadCLI/include/iconfig.h | 16 +- .../SophireadCLI/include/json_config_parser.h | 55 ++- .../SophireadCLI/include/sophiread_core.h | 22 +- sophiread/SophireadCLI/include/tof_binning.h | 44 +- sophiread/SophireadCLI/include/user_config.h | 9 +- .../SophireadCLI/src/json_config_parser.cpp | 118 ++--- sophiread/SophireadCLI/src/sophiread.cpp | 285 ++++++------ sophiread/SophireadCLI/src/sophiread_core.cpp | 411 +++++++++--------- sophiread/SophireadCLI/src/user_config.cpp | 22 +- .../SophireadCLI/src/venus_auto_reducer.cpp | 396 +++++++++-------- .../tests/test_json_config_parser.cpp | 144 +++--- .../tests/test_sophiread_core.cpp | 189 ++++---- .../SophireadCLI/tests/test_user_config.cpp | 20 +- sophiread/SophireadGUI/src/mainwindow.cpp | 18 +- .../SophireadLib/benchmarks/benchmark_abs.cpp | 14 +- .../benchmarks/benchmark_abs_thread.cpp | 12 +- sophiread/SophireadLib/include/abs.h | 19 +- sophiread/SophireadLib/include/centroid.h | 2 +- sophiread/SophireadLib/include/clustering.h | 3 +- sophiread/SophireadLib/include/dbscan.h | 26 +- sophiread/SophireadLib/include/fastgaussian.h | 2 +- sophiread/SophireadLib/include/tpx3.h | 97 +++-- sophiread/SophireadLib/src/abs.cpp | 13 +- sophiread/SophireadLib/src/centroid.cpp | 2 +- sophiread/SophireadLib/src/dbscan.cpp | 126 ++++-- sophiread/SophireadLib/src/fastgaussian.cpp | 10 +- sophiread/SophireadLib/src/tpx3.cpp | 105 +++-- .../tests/generate_fake_tpx3_data.cpp | 380 ++++++++-------- .../SophireadLib/tests/test_clustering.cpp | 17 +- .../SophireadLib/tests/test_peakfitting.cpp | 30 +- sophiread/SophireadLib/tests/test_tpx3.cpp | 91 ++-- .../src/sophiread_stream.cpp | 11 +- sophiread/include/version.h | 4 +- sophiread/resources/manufacture/tpx3cam.cpp | 364 ++++++++-------- 48 files changed, 1875 insertions(+), 1729 deletions(-) diff --git a/sophiread/CMakeLists.txt b/sophiread/CMakeLists.txt index 584436a..db0e729 100644 --- a/sophiread/CMakeLists.txt +++ b/sophiread/CMakeLists.txt @@ -103,12 +103,3 @@ if(DOXYGEN_FOUND) index.html ) endif(DOXYGEN_FOUND) - -# --------------- Clang format --------------# -file(GLOB_RECURSE ALL_CXX_SOURCE_FILES *.cpp *.hpp *.h *.c) -add_custom_target( - format - COMMAND clang-format -i ${ALL_CXX_SOURCE_FILES} - WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} - COMMENT "Running clang-format" -) diff --git a/sophiread/FastSophiread/benchmarks/benchmark_mmap.cpp b/sophiread/FastSophiread/benchmarks/benchmark_mmap.cpp index e935b39..34f613e 100644 --- a/sophiread/FastSophiread/benchmarks/benchmark_mmap.cpp +++ b/sophiread/FastSophiread/benchmarks/benchmark_mmap.cpp @@ -21,22 +21,22 @@ */ #include -#include // for std::numeric_limits<> #include #include +#include // for std::numeric_limits<> -#include "abs.h" #include "disk_io.h" #include "hit.h" #include "spdlog/spdlog.h" +#include "abs.h" #include "tbb/tbb.h" #include "tpx3_fast.h" // from SophireadLib/include/tpx3.cpp #include - #include "neutron.h" -void saveEventsToHDF5(const std::string out_file_name, const std::vector& events); +void saveEventsToHDF5(const std::string out_file_name, + const std::vector& events); /** * @brief Save events to HDF5 file. * @@ -46,11 +46,11 @@ void saveEventsToHDF5(const std::string out_file_name, const std::vector& events) { +void saveEventsToHDF5(const std::string out_file_name, const std::vector &events) { // sanity check if (events.size() == 0) return; @@ -65,41 +65,44 @@ void saveEventsToHDF5(const std::string out_file_name, const std::vector x(events.size()); - std::transform(events.begin(), events.end(), x.begin(), [](const Neutron& event) { return event.getX(); }); + std::transform(events.begin(), events.end(), x.begin(), [](const Neutron &event) { return event.getX(); }); H5::DataSet x_dataset = group.createDataSet("x", float_type, dataspace); x_dataset.write(x.data(), float_type); // -- write y std::vector y(events.size()); - std::transform(events.begin(), events.end(), y.begin(), [](const Neutron& event) { return event.getY(); }); + std::transform(events.begin(), events.end(), y.begin(), [](const Neutron &event) { return event.getY(); }); H5::DataSet y_dataset = group.createDataSet("y", float_type, dataspace); y_dataset.write(y.data(), float_type); // -- write TOF_ns std::vector tof_ns(events.size()); - std::transform(events.begin(), events.end(), tof_ns.begin(), [](const Neutron& event) { return event.getTOF_ns(); }); + std::transform(events.begin(), events.end(), tof_ns.begin(), + [](const Neutron &event) { return event.getTOF_ns(); }); H5::DataSet tof_ns_dataset = group.createDataSet("tof", float_type, dataspace); tof_ns_dataset.write(tof_ns.data(), float_type); // -- write Nhits std::vector nhits(events.size()); - std::transform(events.begin(), events.end(), nhits.begin(), [](const Neutron& event) { return event.getNHits(); }); + std::transform(events.begin(), events.end(), nhits.begin(), + [](const Neutron &event) { return event.getNHits(); }); H5::DataSet nhits_dataset = group.createDataSet("nHits", int_type, dataspace); nhits_dataset.write(nhits.data(), int_type); // -- write TOT std::vector tot(events.size()); - std::transform(events.begin(), events.end(), tot.begin(), [](const Neutron& event) { return event.getTOT(); }); + std::transform(events.begin(), events.end(), tot.begin(), [](const Neutron &event) { return event.getTOT(); }); H5::DataSet tot_dataset = group.createDataSet("tot", float_type, dataspace); tot_dataset.write(tot.data(), float_type); // -- close file out_file.close(); } -void saveEventsToCSV(const std::string out_file_name, const std::vector& events); +void saveEventsToCSV( const std::string out_file_name, + const std::vector& events); /** * @brief Save events to CSV file. * * @param out_file_name: output file name. * @param events: neutron events to be saved. */ -void saveEventsToCSV(const std::string out_file_name, const std::vector& events) { +void saveEventsToCSV(const std::string out_file_name, const std::vector &events) { // sanity check if (events.size() == 0) return; @@ -116,23 +119,28 @@ void saveEventsToCSV(const std::string out_file_name, const std::vector file << "X,Y,TOF (ns),Nhits, TOT\n"; // write events - for (auto& event : events) { - file << event.getX() << "," << event.getY() << "," << event.getTOF_ns() << "," << event.getNHits() << "," - << event.getTOT() << "\n"; + for (auto & event : events) { + file << + event.getX() << "," << + event.getY() << "," << + event.getTOF_ns() << "," << + event.getNHits() << "," << + event.getTOT() << "\n"; } // -- close file file.close(); } -void saveEventsToBIN(const std::string out_file_name, const std::vector& events); +void saveEventsToBIN( const std::string out_file_name, + const std::vector& events); /** * @brief Save events to BIN file. * * @param out_file_name: output file name. * @param events: neutron events to be saved. */ -void saveEventsToBIN(const std::string out_file_name, const std::vector& events) { +void saveEventsToBIN(const std::string out_file_name, const std::vector &events) { // sanity check if (events.size() == 0) return; @@ -147,9 +155,10 @@ void saveEventsToBIN(const std::string out_file_name, const std::vector } // write events - for (auto& event : events) { - std::uint16_t x = (std::uint16_t)(event.getX() * std::numeric_limits::max() / 512.0); - std::uint16_t y = (std::uint16_t)(event.getY() * std::numeric_limits::max() / 512.0); + for (auto & event : events) + { + std::uint16_t x = (std::uint16_t)(event.getX() * std::numeric_limits::max()/512.0); + std::uint16_t y = (std::uint16_t)(event.getY() * std::numeric_limits::max()/512.0); std::uint64_t tof = (std::uint64_t)event.getTOF_ns(); std::uint16_t nhits = (std::uint16_t)event.getNHits(); std::uint16_t tot = (std::uint16_t)event.getTOT(); @@ -162,15 +171,16 @@ void saveEventsToBIN(const std::string out_file_name, const std::vector } // https://stackoverflow.com/questions/874134/find-out-if-string-ends-with-another-string-in-c -bool endsWith(std::string const& fullString, std::string const& ending) { - if (fullString.length() >= ending.length()) { - return (0 == fullString.compare(fullString.length() - ending.length(), ending.length(), ending)); - } else { - return false; - } +bool endsWith (std::string const &fullString, std::string const &ending) { + if (fullString.length() >= ending.length()) { + return (0 == fullString.compare (fullString.length() - ending.length(), ending.length(), ending)); + } else { + return false; + } } -int main(int argc, char* argv[]) { +int main(int argc, char* argv[]) +{ if (argc < 2) { spdlog::critical("Usage: {} [ [options]]", argv[0]); return 1; @@ -188,22 +198,23 @@ int main(int argc, char* argv[]) { bool use_tgdc = false; bool debug = false; float pulse_rate = 0.0; - if (argc > 3) { - use_tbb = strstr(argv[3], "tbb") != NULL; - use_mmap = strstr(argv[3], "mmap") != NULL; - use_tgdc = strstr(argv[3], "tgdc") != NULL; - debug = strstr(argv[3], "debug") != NULL; - - // select pulse rate - pulse_rate = strstr(argv[3], "1hz") ? 1.0 : pulse_rate; - pulse_rate = strstr(argv[3], "10hz") ? 10.0 : pulse_rate; - pulse_rate = strstr(argv[3], "15hz") ? 15.0 : pulse_rate; - pulse_rate = strstr(argv[3], "30hz") ? 30.0 : pulse_rate; - pulse_rate = strstr(argv[3], "45hz") ? 45.0 : pulse_rate; - pulse_rate = strstr(argv[3], "60hz") ? 60.0 : pulse_rate; + if( argc>3 ) + { + use_tbb = strstr(argv[3],"tbb")!=NULL; + use_mmap = strstr(argv[3],"mmap")!=NULL; + use_tgdc = strstr(argv[3],"tgdc")!=NULL; + debug = strstr(argv[3],"debug")!=NULL; + + // select pulse rate + pulse_rate = strstr(argv[3],"1hz") ? 1.0 : pulse_rate; + pulse_rate = strstr(argv[3],"10hz") ? 10.0 : pulse_rate; + pulse_rate = strstr(argv[3],"15hz") ? 15.0 : pulse_rate; + pulse_rate = strstr(argv[3],"30hz") ? 30.0 : pulse_rate; + pulse_rate = strstr(argv[3],"45hz") ? 45.0 : pulse_rate; + pulse_rate = strstr(argv[3],"60hz") ? 60.0 : pulse_rate; } - if (debug) spdlog::set_level(spdlog::level::debug); + if( debug ) spdlog::set_level(spdlog::level::debug); // report statistics size_t n_hits = 0; @@ -212,7 +223,15 @@ int main(int argc, char* argv[]) { // manage timers (for statistics) // but see also NOTES section of https://en.cppreference.com/w/cpp/chrono/high_resolution_clock - enum { TOTAL = 0, RAW_DATA, BATCHES, EVENTS, GATHER, OUTPUT, NUM_TIMERS }; + enum { + TOTAL = 0, + RAW_DATA, + BATCHES, + EVENTS, + GATHER, + OUTPUT, + NUM_TIMERS + }; struct { std::chrono::time_point begin; std::chrono::time_point end; @@ -228,8 +247,7 @@ int main(int argc, char* argv[]) { timer[TOTAL].begin = timer[RAW_DATA].begin = std::chrono::high_resolution_clock::now(); auto raw_data = use_mmap ? mmapTPX3RawToMapInfo(in_tpx3) : readTPX3RawToMapInfo(in_tpx3); timer[RAW_DATA].end = std::chrono::high_resolution_clock::now(); - timer[RAW_DATA].accumulated = static_cast( - std::chrono::duration_cast(timer[RAW_DATA].end - timer[RAW_DATA].begin).count()); + timer[RAW_DATA].accumulated = static_cast(std::chrono::duration_cast(timer[RAW_DATA].end - timer[RAW_DATA].begin).count()); size_t raw_data_consumed = 0; unsigned long tdc_timestamp = 0; @@ -242,15 +260,17 @@ int main(int argc, char* argv[]) { spdlog::debug("@{:p}, {}", raw_data.map, raw_data.max); - if (raw_data.map == NULL) { + if ( raw_data.map == NULL ) + { spdlog::error("Insufficient memory: {}", in_tpx3); exit(EXIT_FAILURE); } - // NB: processing large memory-mapped files requires a restriction - // on the amount of raw data that is treated by the algorithm +// NB: processing large memory-mapped files requires a restriction +// on the amount of raw data that is treated by the algorithm - if (use_tbb) { + if( use_tbb ) + { spdlog::debug("Processing tbb..."); method_events = "TBB Parallel"; } else { @@ -261,121 +281,120 @@ int main(int argc, char* argv[]) { std::cerr << std::endl; std::cerr.precision(2); - while (raw_data_consumed < raw_data.max) { - // manage partial batches - size_t consumed = 0; - char* raw_data_ptr = raw_data.map + raw_data_consumed; - size_t raw_data_size = raw_data.max - raw_data_consumed; - - // spdlog::debug("raw_data: {}/{} ({:.2}%)", raw_data_consumed, raw_data.max, - // static_cast(raw_data_consumed)*100.0/static_cast(raw_data.max)); - std::cerr << "\rraw_data: " << raw_data_consumed << "/" << raw_data.max << " (" - << static_cast(raw_data_consumed) * 100.0 / static_cast(raw_data.max) << "%)"; - - timer[BATCHES].begin = std::chrono::high_resolution_clock::now(); - auto batches = findTPX3H(raw_data_ptr, raw_data_size, consumed); - if (use_tgdc) { - // extract tdc and gdc timestamps from the batches read so far - for (auto& tpx3 : batches) { - updateTimestamp(tpx3, raw_data_ptr, consumed, tdc_timestamp, gdc_timestamp, timer_lsb32); - } +while (raw_data_consumed < raw_data.max) { + + // manage partial batches + size_t consumed = 0; + char *raw_data_ptr = raw_data.map + raw_data_consumed; + size_t raw_data_size = raw_data.max - raw_data_consumed; + + //spdlog::debug("raw_data: {}/{} ({:.2}%)", raw_data_consumed, raw_data.max, static_cast(raw_data_consumed)*100.0/static_cast(raw_data.max)); + std::cerr << "\rraw_data: " + << raw_data_consumed << "/" << raw_data.max + << " (" << static_cast(raw_data_consumed)*100.0/static_cast(raw_data.max) << "%)"; + + timer[BATCHES].begin = std::chrono::high_resolution_clock::now(); + auto batches = findTPX3H(raw_data_ptr, raw_data_size, consumed); + if (use_tgdc) + { + // extract tdc and gdc timestamps from the batches read so far + for (auto& tpx3 : batches) { + updateTimestamp(tpx3, raw_data_ptr, consumed, tdc_timestamp, gdc_timestamp, timer_lsb32); } - timer[BATCHES].end = timer[EVENTS].begin = std::chrono::high_resolution_clock::now(); - - if (use_tbb) { - // https://github.com/jjallaire/TBB/blob/master/inst/examples/parallel-vector-sum.cpp - struct ComputeEvents { - // source vector(s) - char* input; - std::size_t range; - std::vector& batch; - - // output vector-of-vector-of-events - tbb::concurrent_vector> output; - - // standard and splitting constructor - ComputeEvents(char* input, std::size_t range, std::vector& batch) - : input(input), range(range), batch(batch), output() {} - ComputeEvents(ComputeEvents& body, tbb::split) - : input(body.input), range(body.range), batch(body.batch), output() {} - - // generate just the range of hits - void operator()(const tbb::blocked_range& r) { - auto abs_alg_mt = std::make_unique(5.0, 1, 75); - for (size_t i = r.begin(); i != r.end(); ++i) { - TPX3& _tpx3 = batch[i]; - extractHits(_tpx3, input, range); - abs_alg_mt->reset(); - abs_alg_mt->fit(_tpx3.hits); - output.push_back(abs_alg_mt->get_events(_tpx3.hits)); - } + } + timer[BATCHES].end = timer[EVENTS].begin = std::chrono::high_resolution_clock::now(); + + if( use_tbb ) + { + // https://github.com/jjallaire/TBB/blob/master/inst/examples/parallel-vector-sum.cpp + struct ComputeEvents { + // source vector(s) + char *input; + std::size_t range; + std::vector& batch; + + // output vector-of-vector-of-events + tbb::concurrent_vector> output; + + // standard and splitting constructor + ComputeEvents(char *input, std::size_t range, std::vector& batch) : input(input), range(range), batch(batch), output() {} + ComputeEvents(ComputeEvents& body, tbb::split) : input(body.input), range(body.range), batch(body.batch), output() {} + + // generate just the range of hits + void operator()(const tbb::blocked_range& r) { + auto abs_alg_mt = std::make_unique(5.0, 1, 75); + for (size_t i = r.begin(); i != r.end(); ++i) { + TPX3& _tpx3 = batch[i]; + extractHits(_tpx3, input, range); + abs_alg_mt->reset(); + abs_alg_mt->fit(_tpx3.hits); + output.push_back(abs_alg_mt->get_events(_tpx3.hits)); } + } - // join vectors - void join(ComputeEvents& rhs) { - for (auto& a : rhs.output) { - output.push_back(a); - } + // join vectors + void join(ComputeEvents& rhs) { + for (auto& a : rhs.output) { + output.push_back(a); } - }; - - ComputeEvents compute(raw_data_ptr, consumed, batches); - tbb::parallel_reduce(tbb::blocked_range(0, batches.size()), compute); - // transfer vectors - for (auto& a : compute.output) { - events.push_back(a); - } - } else { - auto abs_alg = std::make_unique(5.0, 1, 75); - for (auto& _tpx3 : batches) { - extractHits(_tpx3, raw_data_ptr, consumed); - abs_alg->reset(); - abs_alg->fit(_tpx3.hits); - events.push_back(abs_alg->get_events(_tpx3.hits)); } + }; + + ComputeEvents compute(raw_data_ptr, consumed, batches); + tbb::parallel_reduce(tbb::blocked_range(0, batches.size()), compute); + // transfer vectors + for (auto& a : compute.output) { + events.push_back(a); + } + } else { + auto abs_alg = std::make_unique(5.0, 1, 75); + for (auto& _tpx3 : batches) { + extractHits(_tpx3, raw_data_ptr, consumed); + abs_alg->reset(); + abs_alg->fit(_tpx3.hits); + events.push_back(abs_alg->get_events(_tpx3.hits)); } - timer[EVENTS].end = std::chrono::high_resolution_clock::now(); + } + timer[EVENTS].end = std::chrono::high_resolution_clock::now(); - // report statistics + // report statistics + for (const auto& tpx3 : batches) { + n_hits += tpx3.hits.size(); + } + // sanity check: hit.getTOF() should be smaller than 666,667 clock, which is + // equivalent to 16.67 ms + if ( pulse_rate > 0.0 ) { for (const auto& tpx3 : batches) { - n_hits += tpx3.hits.size(); - } - // sanity check: hit.getTOF() should be smaller than 666,667 clock, which is - // equivalent to 16.67 ms - if (pulse_rate > 0.0) { - for (const auto& tpx3 : batches) { - for (const auto& hit : tpx3.hits) { - auto tof_ms = hit.getTOF_ns() * 1e-6; - if (tof_ms > (1.0 / pulse_rate) + 1e-6) { - spdlog::debug("TOF: {} ms", tof_ms); - n_bad_hits++; - } + for (const auto& hit : tpx3.hits) { + auto tof_ms = hit.getTOF_ns() * 1e-6; + if (tof_ms > (1.0/pulse_rate)+1e-6) { + spdlog::debug("TOF: {} ms", tof_ms); + n_bad_hits++; } } } - for (const auto& e : events) { - n_events += e.size(); - } - - // manage iterations on partial raw_data - raw_data_consumed += consumed; - timer[BATCHES].accumulated += static_cast( - std::chrono::duration_cast(timer[BATCHES].end - timer[BATCHES].begin).count()); - timer[EVENTS].accumulated += static_cast( - std::chrono::duration_cast(timer[EVENTS].end - timer[EVENTS].begin).count()); + } + for (const auto& e : events) { + n_events += e.size(); } + // manage iterations on partial raw_data + raw_data_consumed += consumed; + timer[BATCHES].accumulated += static_cast(std::chrono::duration_cast(timer[BATCHES].end - timer[BATCHES].begin).count()); + timer[EVENTS].accumulated += static_cast(std::chrono::duration_cast(timer[EVENTS].end - timer[EVENTS].begin).count()); +} + std::cerr << std::endl; timer[TOTAL].end = std::chrono::high_resolution_clock::now(); - timer[TOTAL].accumulated += static_cast( - std::chrono::duration_cast(timer[TOTAL].end - timer[TOTAL].begin).count()); + timer[TOTAL].accumulated += static_cast(std::chrono::duration_cast(timer[TOTAL].end - timer[TOTAL].begin).count()); spdlog::info("Number of hits: {}", n_hits); spdlog::info("Number of events: {}", n_events); - if (n_hits > 0) { - spdlog::info("bad/total hit ratio: {:.2f}%", (n_bad_hits * 100.0) / n_hits); + if (n_hits > 0) + { + spdlog::info("bad/total hit ratio: {:.2f}%", (n_bad_hits*100.0)/n_hits); auto speed_raw_data = n_hits / (timer[RAW_DATA].accumulated / 1e6); auto speed_batches = n_hits / (timer[BATCHES].accumulated / 1e6); @@ -389,7 +408,8 @@ int main(int argc, char* argv[]) { // write output // save events to file - if (!no_output && n_events > 0) { + if( !no_output && n_events > 0 ) + { spdlog::debug("Writing output: {}", out_dat); // NB: events is a vector-of-vectors-of-events that we transfrom into a vector-of-events @@ -408,22 +428,14 @@ int main(int argc, char* argv[]) { std::string bin_ext = ".bin"; std::string h5_ext = ".h5"; - if (endsWith(out_dat, csv_ext)) - saveEventsToCSV(out_dat, v); - else if (endsWith(out_dat, bin_ext)) - saveEventsToBIN(out_dat, v); - else if (endsWith(out_dat, h5_ext)) - saveEventsToHDF5(out_dat, v); - else { - spdlog::debug("Unhanded extention (.bin, .csv or .h5 are known)"); - no_output = true; - } + if (endsWith(out_dat, csv_ext)) saveEventsToCSV(out_dat, v); + else if (endsWith(out_dat, bin_ext)) saveEventsToBIN(out_dat, v); + else if (endsWith(out_dat, h5_ext)) saveEventsToHDF5(out_dat, v); + else { spdlog::debug("Unhanded extention (.bin, .csv or .h5 are known)"); no_output = true; } timer[OUTPUT].end = std::chrono::high_resolution_clock::now(); - timer[GATHER].accumulated += static_cast( - std::chrono::duration_cast(timer[GATHER].end - timer[GATHER].begin).count()); - timer[OUTPUT].accumulated += static_cast( - std::chrono::duration_cast(timer[OUTPUT].end - timer[OUTPUT].begin).count()); + timer[GATHER].accumulated += static_cast(std::chrono::duration_cast(timer[GATHER].end - timer[GATHER].begin).count()); + timer[OUTPUT].accumulated += static_cast(std::chrono::duration_cast(timer[OUTPUT].end - timer[OUTPUT].begin).count()); auto speed_gather = n_events / (timer[GATHER].accumulated / 1e6); auto speed_output = n_events / (timer[OUTPUT].accumulated / 1e6); spdlog::info("{:s} speed: {:& data); void set_method(std::string method) { m_method = method; } void reset() { clusterLabels_.clear(); } diff --git a/sophiread/FastSophiread/include/centroid.h b/sophiread/FastSophiread/include/centroid.h index 603ce76..331c287 100644 --- a/sophiread/FastSophiread/include/centroid.h +++ b/sophiread/FastSophiread/include/centroid.h @@ -36,9 +36,9 @@ */ class Centroid : public PeakFittingAlgorithm { public: - Centroid(bool weighted_by_tot = true) : m_weighted_by_tot(weighted_by_tot) {}; + Centroid(bool weighted_by_tot = true) : m_weighted_by_tot(weighted_by_tot){}; Centroid(bool weighted_by_tot, double super_resolution_factor) - : m_weighted_by_tot(weighted_by_tot), m_super_resolution_factor(super_resolution_factor) {}; + : m_weighted_by_tot(weighted_by_tot), m_super_resolution_factor(super_resolution_factor){}; void set_weighted_by_tot(bool weighted_by_tot) { m_weighted_by_tot = weighted_by_tot; } diff --git a/sophiread/FastSophiread/include/disk_io.h b/sophiread/FastSophiread/include/disk_io.h index e1b1ca0..8c3f889 100644 --- a/sophiread/FastSophiread/include/disk_io.h +++ b/sophiread/FastSophiread/include/disk_io.h @@ -34,9 +34,9 @@ std::vector readTPX3RawToCharVec(const std::string& tpx3file); typedef struct mapinfo { - int fd; - char* map; - size_t max; + int fd; + char *map; + size_t max; } mapinfo_t; mapinfo_t readTPX3RawToMapInfo(const std::string& tpx3file); diff --git a/sophiread/FastSophiread/include/fastgaussian.h b/sophiread/FastSophiread/include/fastgaussian.h index bcb26c1..fd477d9 100644 --- a/sophiread/FastSophiread/include/fastgaussian.h +++ b/sophiread/FastSophiread/include/fastgaussian.h @@ -29,8 +29,8 @@ */ class FastGaussian : public PeakFittingAlgorithm { public: - FastGaussian() {}; - FastGaussian(double super_resolution_factor) : m_super_resolution_factor(super_resolution_factor) {}; + FastGaussian(){}; + FastGaussian(double super_resolution_factor) : m_super_resolution_factor(super_resolution_factor){}; void set_super_resolution_factor(double super_resolution_factor) { m_super_resolution_factor = super_resolution_factor; diff --git a/sophiread/FastSophiread/include/hit.h b/sophiread/FastSophiread/include/hit.h index a6ac129..af8fa9e 100644 --- a/sophiread/FastSophiread/include/hit.h +++ b/sophiread/FastSophiread/include/hit.h @@ -29,7 +29,7 @@ class Hit : public IPositionTOF { public: // default constructor - Hit() : m_x(0), m_y(0), m_tot(0), m_toa(0), m_ftoa(0), m_tof(0), m_spidertime(0) {}; + Hit() : m_x(0), m_y(0), m_tot(0), m_toa(0), m_ftoa(0), m_tof(0), m_spidertime(0){}; // copy constructor Hit(const Hit& hit) : m_x(hit.m_x), @@ -38,10 +38,10 @@ class Hit : public IPositionTOF { m_toa(hit.m_toa), m_ftoa(hit.m_ftoa), m_tof(hit.m_tof), - m_spidertime(hit.m_spidertime) {}; + m_spidertime(hit.m_spidertime){}; Hit(int x, int y, int tot, int toa, int ftoa, unsigned int tof, unsigned long long spidertime) - : m_x(x), m_y(y), m_tot(tot), m_toa(toa), m_ftoa(ftoa), m_tof(tof), m_spidertime(spidertime) {}; + : m_x(x), m_y(y), m_tot(tot), m_toa(toa), m_ftoa(ftoa), m_tof(tof), m_spidertime(spidertime){}; // special constructor that directly parse the raw packet from tpx3 // into a hit diff --git a/sophiread/FastSophiread/include/iposition_tof.h b/sophiread/FastSophiread/include/iposition_tof.h index 55fda3a..6f09783 100644 --- a/sophiread/FastSophiread/include/iposition_tof.h +++ b/sophiread/FastSophiread/include/iposition_tof.h @@ -4,7 +4,7 @@ * @brief Interface for neutron and hit * @version 0.1 * @date 2024-08-20 - * + * * @copyright Copyright (c) 2024 * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -22,9 +22,9 @@ #pragma once class IPositionTOF { - public: - virtual ~IPositionTOF() = default; - virtual double iGetX() const = 0; - virtual double iGetY() const = 0; - virtual double iGetTOF_ns() const = 0; +public: + virtual ~IPositionTOF() = default; + virtual double iGetX() const = 0; + virtual double iGetY() const = 0; + virtual double iGetTOF_ns() const = 0; }; diff --git a/sophiread/FastSophiread/include/neutron.h b/sophiread/FastSophiread/include/neutron.h index ab77dfe..f39e43f 100644 --- a/sophiread/FastSophiread/include/neutron.h +++ b/sophiread/FastSophiread/include/neutron.h @@ -29,7 +29,7 @@ class Neutron : public IPositionTOF { public: Neutron(const double x, const double y, const double tof, const double tot, const int nHits) - : m_x(x), m_y(y), m_tof(tof), m_tot(tot), m_nHits(nHits) {}; + : m_x(x), m_y(y), m_tof(tof), m_tot(tot), m_nHits(nHits){}; double getX() const { return m_x; }; double getY() const { return m_y; }; diff --git a/sophiread/FastSophiread/include/tpx3_fast.h b/sophiread/FastSophiread/include/tpx3_fast.h index 37093b1..47734b7 100644 --- a/sophiread/FastSophiread/include/tpx3_fast.h +++ b/sophiread/FastSophiread/include/tpx3_fast.h @@ -33,10 +33,10 @@ * @note Each TPX3 dataset batch comes from a single sub-chip */ struct TPX3 { - std::size_t index; // index of the dataset batch in the raw charater array - const int num_packets; // number of packets in the dataset batch (time packet and data packet) - const int chip_layout_type; // data source (sub-chip ID) - std::vector hits; // hits extracted from the dataset batch + std::size_t index; // index of the dataset batch in the raw charater array + const int num_packets; // number of packets in the dataset batch (time packet and data packet) + const int chip_layout_type; // data source (sub-chip ID) + std::vector hits; // hits extracted from the dataset batch std::vector neutrons; // neutrons from clustering hits unsigned long tdc_timestamp; // starting tdc timestamp of the dataset batch diff --git a/sophiread/FastSophiread/src/disk_io.cpp b/sophiread/FastSophiread/src/disk_io.cpp index 0c4aa4e..bb9a475 100644 --- a/sophiread/FastSophiread/src/disk_io.cpp +++ b/sophiread/FastSophiread/src/disk_io.cpp @@ -31,7 +31,7 @@ * @param[in] tpx3file * @return std::vector */ -std::vector readTPX3RawToCharVec(const std::string &tpx3file) { +std::vector readTPX3RawToCharVec(const std::string& tpx3file) { // Open the file std::ifstream file(tpx3file, std::ios::binary | std::ios::ate); @@ -64,8 +64,9 @@ std::vector readTPX3RawToCharVec(const std::string &tpx3file) { * @param tpx3file * @return mapinfo_t that defines { char *, and size_t } */ -mapinfo_t readTPX3RawToMapInfo(const std::string &tpx3file) { - mapinfo_t info = {-1, NULL, 0}; +mapinfo_t readTPX3RawToMapInfo(const std::string& tpx3file) +{ + mapinfo_t info = { -1, NULL, 0 }; // Open the file std::ifstream file(tpx3file, std::ios::binary | std::ios::ate); @@ -86,10 +87,8 @@ mapinfo_t readTPX3RawToMapInfo(const std::string &tpx3file) { info.map = reinterpret_cast(malloc(info.max)); // Read the data from file and store it in the vector - if (info.map == NULL) - info.max = 0; - else - file.read(info.map, info.max); + if (info.map == NULL) info.max = 0; + else file.read(info.map, info.max); // Close the file file.close(); @@ -98,10 +97,10 @@ mapinfo_t readTPX3RawToMapInfo(const std::string &tpx3file) { } // for mmap support -#include #include -#include #include +#include +#include #include /** @@ -110,8 +109,9 @@ mapinfo_t readTPX3RawToMapInfo(const std::string &tpx3file) { * @param tpx3file * @return mapinfo_t that defines { char *, and size_t } */ -mapinfo_t mmapTPX3RawToMapInfo(const std::string &tpx3file) { - mapinfo_t info = {-1, NULL, 0}; +mapinfo_t mmapTPX3RawToMapInfo(const std::string& tpx3file) +{ + mapinfo_t info = { -1, NULL, 0 }; std::ifstream file(tpx3file, std::ios::binary); if (!file.is_open()) { @@ -119,21 +119,17 @@ mapinfo_t mmapTPX3RawToMapInfo(const std::string &tpx3file) { exit(EXIT_FAILURE); } - info.fd = open(tpx3file.c_str(), O_RDWR, 0666); + info.fd = open(tpx3file.c_str(),O_RDWR,0666); - if (info.fd == -1) { - perror(tpx3file.c_str()); - exit(EXIT_FAILURE); - } - info.max = lseek(info.fd, 0, SEEK_END); // determine the sizeof the file - info.map = reinterpret_cast(mmap(0, info.max, PROT_READ | PROT_WRITE, MAP_SHARED, info.fd, 0)); + if( info.fd == -1 ) { perror(tpx3file.c_str()); exit(EXIT_FAILURE); } + info.max = lseek(info.fd,0,SEEK_END); // determine the sizeof the file + info.map = reinterpret_cast(mmap(0, info.max, PROT_READ|PROT_WRITE, MAP_SHARED, info.fd, 0)); // https://lemire.me/blog/2012/06/26/which-is-fastest-read-fread-ifstream-or-mmap/ // says to add MAP_POPULATE to make it mmap() faster... // TODO: Test this return info; - // https://stackoverflow.com/questions/26569217/do-i-have-to-munmap-a-mmap-file ( consensus is that you do not need to - // do it ) + // https://stackoverflow.com/questions/26569217/do-i-have-to-munmap-a-mmap-file ( consensus is that you do not need to do it ) } /** @@ -142,7 +138,7 @@ mapinfo_t mmapTPX3RawToMapInfo(const std::string &tpx3file) { * @param[in] originalFileName * @return std::string */ -std::string generateFileNameWithMicroTimestamp(const std::string &originalFileName) { +std::string generateFileNameWithMicroTimestamp(const std::string& originalFileName) { auto now = std::chrono::high_resolution_clock::now(); auto seconds = std::chrono::time_point_cast(now); auto micros = std::chrono::duration_cast(now - seconds); diff --git a/sophiread/FastSophiread/src/hit.cpp b/sophiread/FastSophiread/src/hit.cpp index f12a139..aad67a3 100644 --- a/sophiread/FastSophiread/src/hit.cpp +++ b/sophiread/FastSophiread/src/hit.cpp @@ -21,7 +21,6 @@ * along with this program. If not, see . */ #include "hit.h" - #include /** @@ -63,16 +62,16 @@ Hit::Hit(const char *packet, const unsigned long long TDC_timestamp, const unsig m_spidertime = (SPDR_MSB18 << 30) & 0xFFFFC0000000; m_spidertime = m_spidertime | spidertime; - // additional check to make sure rollover of spidertime is correct + // additional check to make sure rollover of spidertime is correct // 4e7 is roughly 1 second in the units of 25 ns // 1073741824 is 2^30 (in units of 25 ns) - if ((m_spidertime - GDC_timestamp) >= 4e7) { + if ((m_spidertime-GDC_timestamp)>=4e7){ m_spidertime -= 1073741824; } // tof calculation m_tof = m_spidertime - TDC_timestamp; - while (m_tof * 25E-6 > 16.67) { + while (m_tof*25E-6 > 16.67){ m_tof -= 666667; } diff --git a/sophiread/FastSophiread/src/tpx3_fast.cpp b/sophiread/FastSophiread/src/tpx3_fast.cpp index 3487bc7..cfa8188 100644 --- a/sophiread/FastSophiread/src/tpx3_fast.cpp +++ b/sophiread/FastSophiread/src/tpx3_fast.cpp @@ -25,19 +25,20 @@ #include #include -#define MAX_BATCH_LEN 100000 // enough to process suann_socket_background_serval32.tpx3 without rollover +#define MAX_BATCH_LEN 100000 // enough to process suann_socket_background_serval32.tpx3 without rollover #ifdef MAX_BATCH_LEN -#include #include +#include // allow MAX_BATCH_LEN to come from the environment long unsigned int _get_max_batch_len(void) { - if (const char *env_p = std::getenv("MAX_BATCH_LEN")) { - auto max_batch_len = std::strtoul(env_p, NULL, 0); - // no conversion produce 0 - if (max_batch_len != 0) return max_batch_len; - } - return MAX_BATCH_LEN; + if (const char* env_p = std::getenv("MAX_BATCH_LEN")) { + auto max_batch_len = std::strtoul(env_p, NULL, 0); + // no conversion produce 0 + if (max_batch_len != 0 ) + return max_batch_len; + } + return MAX_BATCH_LEN; } #endif @@ -142,7 +143,9 @@ std::vector findTPX3H(const std::vector &raw_bytes) { * @param[in] size * @return std::vector */ -std::vector findTPX3H(char *raw_bytes, std::size_t size) { return findTPX3H(raw_bytes, raw_bytes + size); } +std::vector findTPX3H(char *raw_bytes, std::size_t size) { + return findTPX3H(raw_bytes, raw_bytes + size); +} /** * @brief Locate all TPX3H (chip dataset) in the raw data. @@ -151,7 +154,7 @@ std::vector findTPX3H(char *raw_bytes, std::size_t size) { return findTPX3 * @param[out] consumed * @return std::vector */ -std::vector findTPX3H(const std::vector &raw_bytes, std::size_t &consumed) { +std::vector findTPX3H(const std::vector &raw_bytes, std::size_t& consumed) { return findTPX3H(raw_bytes.cbegin(), raw_bytes.cend(), consumed); } @@ -163,7 +166,7 @@ std::vector findTPX3H(const std::vector &raw_bytes, std::size_t &con * @param[out] consumed * @return std::vector */ -std::vector findTPX3H(char *raw_bytes, std::size_t size, std::size_t &consumed) { +std::vector findTPX3H(char *raw_bytes, std::size_t size, std::size_t& consumed) { return findTPX3H(raw_bytes, raw_bytes + size, consumed); } diff --git a/sophiread/FastSophiread/tests/test_tpx3.cpp b/sophiread/FastSophiread/tests/test_tpx3.cpp index 2a1f166..86900dc 100644 --- a/sophiread/FastSophiread/tests/test_tpx3.cpp +++ b/sophiread/FastSophiread/tests/test_tpx3.cpp @@ -223,12 +223,10 @@ TEST(TPX3FuncTest, TestExtractHits_mmap) { TEST(TPX3FuncTest, TestExtractHits_large) { // memory map the testing raw data - auto mapdata = mmapTPX3RawToMapInfo( - "../data/HV2700_1500_500_THLrel_274_sophy_chopper_60Hz_4.1mm_aperture_siemen_star_120s_000000.tpx3"); - const int n_hits_reference = - 5303344; // HV2700_1500_500_THLrel_274_sophy_chopper_60Hz_4.1mm_aperture_siemen_star_120s_000000.tpx3 - // auto mapdata = mmapTPX3RawToMapInfo("../data/suann_socket_background_serval32.tpx3"); - // const int n_hits_reference = 98533; // suann_socket_background_serval32.tpx3 + auto mapdata = mmapTPX3RawToMapInfo("../data/HV2700_1500_500_THLrel_274_sophy_chopper_60Hz_4.1mm_aperture_siemen_star_120s_000000.tpx3"); + const int n_hits_reference = 5303344; // HV2700_1500_500_THLrel_274_sophy_chopper_60Hz_4.1mm_aperture_siemen_star_120s_000000.tpx3 + //auto mapdata = mmapTPX3RawToMapInfo("../data/suann_socket_background_serval32.tpx3"); + //const int n_hits_reference = 98533; // suann_socket_background_serval32.tpx3 size_t raw_data_consumed = 0; size_t n_hits = 0; // NB: these track timestamps; if they are reset improperly, the system keeps searching and gets the wrong answers! @@ -236,48 +234,50 @@ TEST(TPX3FuncTest, TestExtractHits_large) { unsigned long long int gdc_timestamp = 0; unsigned long timer_lsb32 = 0; - while (raw_data_consumed < mapdata.max) { - // manage partial batches - size_t consumed = 0; - char* raw_data_ptr = mapdata.map + raw_data_consumed; - size_t raw_data_size = mapdata.max - raw_data_consumed; + while (raw_data_consumed < mapdata.max) { - // locate all headers - auto batches = findTPX3H(raw_data_ptr, raw_data_size, consumed); + // manage partial batches + size_t consumed = 0; + char *raw_data_ptr = mapdata.map + raw_data_consumed; + size_t raw_data_size = mapdata.max - raw_data_consumed; - // locate gdc and tdc - for (auto& tpx3 : batches) { - updateTimestamp(tpx3, raw_data_ptr, consumed, tdc_timestamp, gdc_timestamp, timer_lsb32); - } + // locate all headers + auto batches = findTPX3H(raw_data_ptr, raw_data_size, consumed); - // extract hits - for (auto& tpx3 : batches) { - extractHits(tpx3, raw_data_ptr, consumed); - } + // locate gdc and tdc + for (auto& tpx3 : batches) { + updateTimestamp(tpx3, raw_data_ptr, consumed, tdc_timestamp, gdc_timestamp, timer_lsb32); + } - // count all hits - for (const auto& tpx3 : batches) { - auto hits = tpx3.hits; - n_hits += hits.size(); - } + // extract hits + for (auto& tpx3 : batches) { + extractHits(tpx3, raw_data_ptr, consumed); + } - // make sure no tof is above 16.67 ms - for (const auto& tpx3 : batches) { - auto hits = tpx3.hits; - for (const auto& hit : hits) { - auto tof_ms = hit.getTOF_ns() * 1e-6; - EXPECT_LT(tof_ms, 16670); - } - } + // count all hits + for (const auto& tpx3 : batches) { + auto hits = tpx3.hits; + n_hits += hits.size(); + } - // manage iterations on partial raw_data - raw_data_consumed += consumed; + // make sure no tof is above 16.67 ms + for (const auto& tpx3 : batches) { + auto hits = tpx3.hits; + for (const auto& hit : hits) { + auto tof_ms = hit.getTOF_ns() * 1e-6; + EXPECT_LT(tof_ms, 16670); + } } + // manage iterations on partial raw_data + raw_data_consumed += consumed; + } + EXPECT_EQ(n_hits, n_hits_reference); + } -int main(int argc, char** argv) { +int main(int argc, char **argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/sophiread/SophireadCLI/include/iconfig.h b/sophiread/SophireadCLI/include/iconfig.h index 092eeb5..a9b4120 100644 --- a/sophiread/SophireadCLI/include/iconfig.h +++ b/sophiread/SophireadCLI/include/iconfig.h @@ -25,14 +25,14 @@ #include class IConfig { - public: - virtual ~IConfig() = default; +public: + virtual ~IConfig() = default; - virtual double getABSRadius() const = 0; - virtual unsigned long int getABSMinClusterSize() const = 0; - virtual unsigned long int getABSSpiderTimeRange() const = 0; - virtual std::vector getTOFBinEdges() const = 0; - virtual double getSuperResolution() const = 0; + virtual double getABSRadius() const = 0; + virtual unsigned long int getABSMinClusterSize() const = 0; + virtual unsigned long int getABSSpiderTimeRange() const = 0; + virtual std::vector getTOFBinEdges() const = 0; + virtual double getSuperResolution() const = 0; - virtual std::string toString() const = 0; + virtual std::string toString() const = 0; }; \ No newline at end of file diff --git a/sophiread/SophireadCLI/include/json_config_parser.h b/sophiread/SophireadCLI/include/json_config_parser.h index 7023bab..f1e1dd2 100644 --- a/sophiread/SophireadCLI/include/json_config_parser.h +++ b/sophiread/SophireadCLI/include/json_config_parser.h @@ -21,38 +21,37 @@ */ #pragma once -#include #include #include - +#include #include "iconfig.h" #include "tof_binning.h" class JSONConfigParser : public IConfig { - public: - static JSONConfigParser createDefault(); - static JSONConfigParser fromFile(const std::string& filepath); - - double getABSRadius() const override; - unsigned long int getABSMinClusterSize() const override; - unsigned long int getABSSpiderTimeRange() const override; - std::vector getTOFBinEdges() const override; - double getSuperResolution() const override; - - std::string toString() const override; - - private: - JSONConfigParser(const nlohmann::json& config); - nlohmann::json m_config; - TOFBinning m_tof_binning; - - void parseTOFBinning(); - - // Default values - static constexpr double DEFAULT_ABS_RADIUS = 5.0; - static constexpr unsigned long int DEFAULT_ABS_MIN_CLUSTER_SIZE = 1; - static constexpr unsigned long int DEFAULT_ABS_SPIDER_TIME_RANGE = 75; - static constexpr int DEFAULT_TOF_BINS = 1500; - static constexpr double DEFAULT_TOF_MAX = 16.7e-3; // 16.7 milliseconds - static constexpr double DEFAULT_SUPER_RESOLUTION = 1.0; +public: + static JSONConfigParser createDefault(); + static JSONConfigParser fromFile(const std::string& filepath); + + double getABSRadius() const override; + unsigned long int getABSMinClusterSize() const override; + unsigned long int getABSSpiderTimeRange() const override; + std::vector getTOFBinEdges() const override; + double getSuperResolution() const override; + + std::string toString() const override; + +private: + JSONConfigParser(const nlohmann::json& config); + nlohmann::json m_config; + TOFBinning m_tof_binning; + + void parseTOFBinning(); + + // Default values + static constexpr double DEFAULT_ABS_RADIUS = 5.0; + static constexpr unsigned long int DEFAULT_ABS_MIN_CLUSTER_SIZE = 1; + static constexpr unsigned long int DEFAULT_ABS_SPIDER_TIME_RANGE = 75; + static constexpr int DEFAULT_TOF_BINS = 1500; + static constexpr double DEFAULT_TOF_MAX = 16.7e-3; // 16.7 milliseconds + static constexpr double DEFAULT_SUPER_RESOLUTION = 1.0; }; diff --git a/sophiread/SophireadCLI/include/sophiread_core.h b/sophiread/SophireadCLI/include/sophiread_core.h index c73bd2c..b90c991 100644 --- a/sophiread/SophireadCLI/include/sophiread_core.h +++ b/sophiread/SophireadCLI/include/sophiread_core.h @@ -23,10 +23,9 @@ #include #include - #include "abs.h" -#include "iconfig.h" #include "tpx3_fast.h" +#include "iconfig.h" namespace sophiread { @@ -36,11 +35,14 @@ void timedLocateTimeStamp(std::vector& batches, const std::vector& r void timedProcessing(std::vector& batches, const std::vector& raw_data, const IConfig& config); void timedSaveHitsToHDF5(const std::string& out_hits, std::vector& batches); void timedSaveEventsToHDF5(const std::string& out_events, std::vector& batches); -std::vector>> timedCreateTOFImages(const std::vector& batches, - double super_resolution, - const std::vector& tof_bin_edges, - const std::string& mode); -void timedSaveTOFImagingToTIFF(const std::string& out_tof_imaging, - const std::vector>>& tof_images, - const std::vector& tof_bin_edges, const std::string& tof_filename_base); -} // namespace sophiread +std::vector>> timedCreateTOFImages( + const std::vector& batches, + double super_resolution, + const std::vector& tof_bin_edges, + const std::string& mode); +void timedSaveTOFImagingToTIFF( + const std::string& out_tof_imaging, + const std::vector>>& tof_images, + const std::vector& tof_bin_edges, + const std::string& tof_filename_base); +} // namespace sophiread diff --git a/sophiread/SophireadCLI/include/tof_binning.h b/sophiread/SophireadCLI/include/tof_binning.h index f96c21f..b79167e 100644 --- a/sophiread/SophireadCLI/include/tof_binning.h +++ b/sophiread/SophireadCLI/include/tof_binning.h @@ -4,7 +4,7 @@ * @brief TOF binning * @version 0.1 * @date 2024-08-16 - * + * * @copyright Copyright (c) 2024 * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -21,32 +21,36 @@ */ #pragma once -#include #include +#include struct TOFBinning { - std::optional num_bins; - std::optional tof_max; - std::vector custom_edges; + std::optional num_bins; + std::optional tof_max; + std::vector custom_edges; - // Default constructor - TOFBinning() : num_bins(1500), tof_max(1.0 / 60) {} + // Default constructor + TOFBinning() : num_bins(1500), tof_max(1.0/60) {} - bool isUniform() const { return num_bins.has_value() && tof_max.has_value() && custom_edges.empty(); } - - bool isCustom() const { return !custom_edges.empty(); } + bool isUniform() const { + return num_bins.has_value() && tof_max.has_value() && custom_edges.empty(); + } - std::vector getBinEdges() const { - if (isCustom()) { - return custom_edges; + bool isCustom() const { + return !custom_edges.empty(); } - int bins = num_bins.value_or(1500); - double max = tof_max.value_or(1.0 / 60); - std::vector edges(bins + 1); - for (int i = 0; i <= bins; ++i) { - edges[i] = max * i / bins; + std::vector getBinEdges() const { + if (isCustom()) { + return custom_edges; + } + + int bins = num_bins.value_or(1500); + double max = tof_max.value_or(1.0/60); + std::vector edges(bins + 1); + for (int i = 0; i <= bins; ++i) { + edges[i] = max * i / bins; + } + return edges; } - return edges; - } }; \ No newline at end of file diff --git a/sophiread/SophireadCLI/include/user_config.h b/sophiread/SophireadCLI/include/user_config.h index 3ab877c..5e8238e 100644 --- a/sophiread/SophireadCLI/include/user_config.h +++ b/sophiread/SophireadCLI/include/user_config.h @@ -23,7 +23,6 @@ #pragma once #include - #include "iconfig.h" #include "tof_binning.h" @@ -39,17 +38,15 @@ class UserConfig : public IConfig { void setABSMinClusterSize(unsigned long int abs_min_cluster_size) { m_abs_min_cluster_size = abs_min_cluster_size; } unsigned long int getABSSpiderTimeRange() const override { return m_abs_spider_time_range; } - void setABSSpiderTimeRange(unsigned long int abs_spider_time_range) { - m_abs_spider_time_range = abs_spider_time_range; - } + void setABSSpiderTimeRange(unsigned long int abs_spider_time_range) { m_abs_spider_time_range = abs_spider_time_range; } std::vector getTOFBinEdges() const override { return m_tof_binning.getBinEdges(); } void setTOFBinning(const TOFBinning& tof_binning) { m_tof_binning = tof_binning; } void setCustomTOFBinEdges(const std::vector& edges) { m_tof_binning.custom_edges = edges; } // no super resolution for old config format - double getSuperResolution() const override { return m_super_resolution; } - void setSuperResolution(double super_resolution) { m_super_resolution = super_resolution; } + double getSuperResolution() const override {return m_super_resolution; } + void setSuperResolution(double super_resolution) {m_super_resolution = super_resolution; } std::string toString() const override; diff --git a/sophiread/SophireadCLI/src/json_config_parser.cpp b/sophiread/SophireadCLI/src/json_config_parser.cpp index 1591f92..e2bef14 100644 --- a/sophiread/SophireadCLI/src/json_config_parser.cpp +++ b/sophiread/SophireadCLI/src/json_config_parser.cpp @@ -20,62 +20,73 @@ * along with this program. If not, see . */ #include "json_config_parser.h" - +#include #include -#include JSONConfigParser JSONConfigParser::createDefault() { - nlohmann::json default_config = {{"abs", - {{"radius", DEFAULT_ABS_RADIUS}, - {"min_cluster_size", DEFAULT_ABS_MIN_CLUSTER_SIZE}, - {"spider_time_range", DEFAULT_ABS_SPIDER_TIME_RANGE}}}, - {"tof_imaging", - {{"uniform_bins", {{"num_bins", DEFAULT_TOF_BINS}, {"end", DEFAULT_TOF_MAX}}}, - {"super_resolution", DEFAULT_SUPER_RESOLUTION}}}}; - - return JSONConfigParser(default_config); + nlohmann::json default_config = { + {"abs", { + {"radius", DEFAULT_ABS_RADIUS}, + {"min_cluster_size", DEFAULT_ABS_MIN_CLUSTER_SIZE}, + {"spider_time_range", DEFAULT_ABS_SPIDER_TIME_RANGE} + }}, + {"tof_imaging", { + {"uniform_bins", { + {"num_bins", DEFAULT_TOF_BINS}, + {"end", DEFAULT_TOF_MAX} + }}, + {"super_resolution", DEFAULT_SUPER_RESOLUTION} + }} + }; + + return JSONConfigParser(default_config); } + /** * @brief Build JSONConfigParser from a given file * @param filepath Path to the JSON configuration file * @return JSONConfigParser */ JSONConfigParser JSONConfigParser::fromFile(const std::string& filepath) { - std::ifstream file(filepath); - if (!file.is_open()) { - throw std::runtime_error("Failed to open configuration file: " + filepath); - } - - nlohmann::json config; - try { - file >> config; - } catch (const nlohmann::json::exception& e) { - throw std::runtime_error("Error parsing JSON file: " + std::string(e.what())); - } - - return JSONConfigParser(config); + std::ifstream file(filepath); + if (!file.is_open()) { + throw std::runtime_error("Failed to open configuration file: " + filepath); + } + + nlohmann::json config; + try { + file >> config; + } catch (const nlohmann::json::exception& e) { + throw std::runtime_error("Error parsing JSON file: " + std::string(e.what())); + } + + return JSONConfigParser(config); } /** * @brief Construct a new JSONConfigParser::JSONConfigParser object * @param config JSON configuration object */ -JSONConfigParser::JSONConfigParser(const nlohmann::json& config) : m_config(config) { parseTOFBinning(); } +JSONConfigParser::JSONConfigParser(const nlohmann::json& config) : m_config(config) { + parseTOFBinning(); +} /** * @brief Get the ABS Radius * @return double */ -double JSONConfigParser::getABSRadius() const { return m_config.value("/abs/radius"_json_pointer, DEFAULT_ABS_RADIUS); } +double JSONConfigParser::getABSRadius() const { + return m_config.value("/abs/radius"_json_pointer, DEFAULT_ABS_RADIUS); +} /** * @brief Get the ABS Min Cluster Size * @return unsigned long int */ unsigned long int JSONConfigParser::getABSMinClusterSize() const { - return m_config.value("/abs/min_cluster_size"_json_pointer, DEFAULT_ABS_MIN_CLUSTER_SIZE); + return m_config.value("/abs/min_cluster_size"_json_pointer, DEFAULT_ABS_MIN_CLUSTER_SIZE); } /** @@ -83,34 +94,36 @@ unsigned long int JSONConfigParser::getABSMinClusterSize() const { * @return unsigned long int */ unsigned long int JSONConfigParser::getABSSpiderTimeRange() const { - return m_config.value("/abs/spider_time_range"_json_pointer, DEFAULT_ABS_SPIDER_TIME_RANGE); + return m_config.value("/abs/spider_time_range"_json_pointer, DEFAULT_ABS_SPIDER_TIME_RANGE); } /** * @brief Get the TOF Bin Edges * @return std::vector */ -std::vector JSONConfigParser::getTOFBinEdges() const { return m_tof_binning.getBinEdges(); } +std::vector JSONConfigParser::getTOFBinEdges() const { + return m_tof_binning.getBinEdges(); +} double JSONConfigParser::getSuperResolution() const { - return m_config.value("/tof_imaging/super_resolution"_json_pointer, DEFAULT_SUPER_RESOLUTION); + return m_config.value("/tof_imaging/super_resolution"_json_pointer, DEFAULT_SUPER_RESOLUTION); } /** * @brief Parse the TOF binning configuration */ void JSONConfigParser::parseTOFBinning() { - if (m_config.contains("/tof_imaging/bin_edges"_json_pointer)) { - m_tof_binning.custom_edges = m_config["/tof_imaging/bin_edges"_json_pointer].get>(); - } else if (m_config.contains("/tof_imaging/uniform_bins"_json_pointer)) { - const auto& uniform = m_config["/tof_imaging/uniform_bins"_json_pointer]; - m_tof_binning.num_bins = uniform.value("num_bins", DEFAULT_TOF_BINS); - m_tof_binning.tof_max = uniform.value("end", DEFAULT_TOF_MAX); - } else { - // Use default values - m_tof_binning.num_bins = DEFAULT_TOF_BINS; - m_tof_binning.tof_max = DEFAULT_TOF_MAX; - } + if (m_config.contains("/tof_imaging/bin_edges"_json_pointer)) { + m_tof_binning.custom_edges = m_config["/tof_imaging/bin_edges"_json_pointer].get>(); + } else if (m_config.contains("/tof_imaging/uniform_bins"_json_pointer)) { + const auto& uniform = m_config["/tof_imaging/uniform_bins"_json_pointer]; + m_tof_binning.num_bins = uniform.value("num_bins", DEFAULT_TOF_BINS); + m_tof_binning.tof_max = uniform.value("end", DEFAULT_TOF_MAX); + } else { + // Use default values + m_tof_binning.num_bins = DEFAULT_TOF_BINS; + m_tof_binning.tof_max = DEFAULT_TOF_MAX; + } } /** @@ -118,18 +131,19 @@ void JSONConfigParser::parseTOFBinning() { * @return std::string */ std::string JSONConfigParser::toString() const { - std::stringstream ss; - ss << "ABS: radius=" << getABSRadius() << ", min_cluster_size=" << getABSMinClusterSize() - << ", spider_time_range=" << getABSSpiderTimeRange(); + std::stringstream ss; + ss << "ABS: radius=" << getABSRadius() + << ", min_cluster_size=" << getABSMinClusterSize() + << ", spider_time_range=" << getABSSpiderTimeRange(); - if (m_tof_binning.isCustom()) { - ss << ", Custom TOF binning with " << m_tof_binning.custom_edges.size() - 1 << " bins"; - } else { - ss << ", TOF bins=" << m_tof_binning.num_bins.value_or(DEFAULT_TOF_BINS) - << ", TOF max=" << (m_tof_binning.tof_max.value_or(DEFAULT_TOF_MAX) * 1000) << " ms"; - } + if (m_tof_binning.isCustom()) { + ss << ", Custom TOF binning with " << m_tof_binning.custom_edges.size() - 1 << " bins"; + } else { + ss << ", TOF bins=" << m_tof_binning.num_bins.value_or(DEFAULT_TOF_BINS) + << ", TOF max=" << (m_tof_binning.tof_max.value_or(DEFAULT_TOF_MAX) * 1000) << " ms"; + } - ss << ", Super Resolution=" << getSuperResolution(); + ss << ", Super Resolution=" << getSuperResolution(); - return ss.str(); + return ss.str(); } diff --git a/sophiread/SophireadCLI/src/sophiread.cpp b/sophiread/SophireadCLI/src/sophiread.cpp index 42c0828..5b76066 100644 --- a/sophiread/SophireadCLI/src/sophiread.cpp +++ b/sophiread/SophireadCLI/src/sophiread.cpp @@ -22,102 +22,99 @@ */ #include #include -#include #include +#include #include -#include #include #include #include +#include #include "abs.h" #include "disk_io.h" -#include "json_config_parser.h" -#include "sophiread_core.h" #include "tpx3_fast.h" #include "user_config.h" +#include "json_config_parser.h" +#include "sophiread_core.h" struct ProgramOptions { - std::string input_tpx3; - std::string output_hits; - std::string output_events; - std::string config_file; - std::string output_tof_imaging; - std::string tof_filename_base = "tof_image"; - std::string tof_mode = "neutron"; - bool debug_logging = false; - bool verbose = false; + std::string input_tpx3; + std::string output_hits; + std::string output_events; + std::string config_file; + std::string output_tof_imaging; + std::string tof_filename_base = "tof_image"; + std::string tof_mode = "neutron"; + bool debug_logging = false; + bool verbose = false; }; void print_usage(const char* program_name) { - spdlog::info( - "Usage: {} -i -H -E [-u ] [-T ] [-f " - "] [-m ] [-d] [-v]", - program_name); - spdlog::info("Options:"); - spdlog::info(" -i Input TPX3 file"); - spdlog::info(" -H Output hits HDF5 file"); - spdlog::info(" -E Output events HDF5 file"); - spdlog::info(" -u User configuration JSON file (optional)"); - spdlog::info(" -T Output folder for TIFF TOF images (optional)"); - spdlog::info(" -f Base name for TIFF files (default: tof_image)"); - spdlog::info(" -m TOF mode: 'hit' or 'neutron' (default: neutron)"); - spdlog::info(" -d Enable debug logging"); - spdlog::info(" -v Enable verbose logging"); + spdlog::info("Usage: {} -i -H -E [-u ] [-T ] [-f ] [-m ] [-d] [-v]", program_name); + spdlog::info("Options:"); + spdlog::info(" -i Input TPX3 file"); + spdlog::info(" -H Output hits HDF5 file"); + spdlog::info(" -E Output events HDF5 file"); + spdlog::info(" -u User configuration JSON file (optional)"); + spdlog::info(" -T Output folder for TIFF TOF images (optional)"); + spdlog::info(" -f Base name for TIFF files (default: tof_image)"); + spdlog::info(" -m TOF mode: 'hit' or 'neutron' (default: neutron)"); + spdlog::info(" -d Enable debug logging"); + spdlog::info(" -v Enable verbose logging"); } ProgramOptions parse_arguments(int argc, char* argv[]) { - ProgramOptions options; - int opt; - - while ((opt = getopt(argc, argv, "i:H:E:u:T:f:m:dv")) != -1) { - switch (opt) { - case 'i': - options.input_tpx3 = optarg; - break; - case 'H': - options.output_hits = optarg; - break; - case 'E': - options.output_events = optarg; - break; - case 'u': - options.config_file = optarg; - break; - case 'T': - options.output_tof_imaging = optarg; - break; - case 'f': - options.tof_filename_base = optarg; - break; - case 'm': - options.tof_mode = optarg; - break; - case 'd': - options.debug_logging = true; - break; - case 'v': - options.verbose = true; - break; - default: - print_usage(argv[0]); - throw std::runtime_error("Invalid argument"); + ProgramOptions options; + int opt; + + while ((opt = getopt(argc, argv, "i:H:E:u:T:f:m:dv")) != -1) { + switch (opt) { + case 'i': + options.input_tpx3 = optarg; + break; + case 'H': + options.output_hits = optarg; + break; + case 'E': + options.output_events = optarg; + break; + case 'u': + options.config_file = optarg; + break; + case 'T': + options.output_tof_imaging = optarg; + break; + case 'f': + options.tof_filename_base = optarg; + break; + case 'm': + options.tof_mode = optarg; + break; + case 'd': + options.debug_logging = true; + break; + case 'v': + options.verbose = true; + break; + default: + print_usage(argv[0]); + throw std::runtime_error("Invalid argument"); + } } - } - // Validate required arguments - if (options.input_tpx3.empty()) { - print_usage(argv[0]); - throw std::runtime_error("Missing required arguments"); - } + // Validate required arguments + if (options.input_tpx3.empty()) { + print_usage(argv[0]); + throw std::runtime_error("Missing required arguments"); + } - // Validate TOF mode - if (options.tof_mode != "hit" && options.tof_mode != "neutron") { - throw std::runtime_error("Invalid TOF mode. Use 'hit' or 'neutron'."); - } + // Validate TOF mode + if (options.tof_mode != "hit" && options.tof_mode != "neutron") { + throw std::runtime_error("Invalid TOF mode. Use 'hit' or 'neutron'."); + } - return options; + return options; } /** @@ -127,79 +124,77 @@ ProgramOptions parse_arguments(int argc, char* argv[]) { * @param[in] argv * @return int */ -int main(int argc, char* argv[]) { +int main(int argc, char *argv[]) { try { - ProgramOptions options = parse_arguments(argc, argv); - - // Set logging level based on debug and verbose flags - if (options.debug_logging) { - spdlog::set_level(spdlog::level::debug); - spdlog::debug("Debug logging enabled"); - } else if (options.verbose) { - spdlog::set_level(spdlog::level::info); - } else { - spdlog::set_level(spdlog::level::warn); - } - - spdlog::info("Input file: {}", options.input_tpx3); - spdlog::info("Output hits file: {}", options.output_hits); - spdlog::info("Output events file: {}", options.output_events); - spdlog::info("Configuration file: {}", options.config_file); - spdlog::info("TOF imaging folder: {}", options.output_tof_imaging); - spdlog::info("TOF filename base: {}", options.tof_filename_base); - spdlog::info("TOF mode: {}", options.tof_mode); - - // Load configuration - std::unique_ptr config; - if (!options.config_file.empty()) { - std::string extension = std::filesystem::path(options.config_file).extension().string(); - if (extension == ".json") { - config = std::make_unique(JSONConfigParser::fromFile(options.config_file)); - } else { - spdlog::warn("Deprecated configuration format detected. Please switch to JSON format."); - config = std::make_unique(parseUserDefinedConfigurationFile(options.config_file)); - } - } else { - spdlog::info("No configuration file provided. Using default JSON configuration."); - config = std::make_unique(JSONConfigParser::createDefault()); - } - - spdlog::info("Configuration: {}", config->toString()); - - // Process raw data - auto start = std::chrono::high_resolution_clock::now(); - auto raw_data = sophiread::timedReadDataToCharVec(options.input_tpx3); - auto batches = sophiread::timedFindTPX3H(raw_data); - sophiread::timedLocateTimeStamp(batches, raw_data); - sophiread::timedProcessing(batches, raw_data, *config); - auto end = std::chrono::high_resolution_clock::now(); - auto elapsed = std::chrono::duration_cast(end - start).count(); - spdlog::info("Total processing time: {} s", elapsed / 1e6); - - // Release memory of raw data - std::vector().swap(raw_data); - - // Generate and save TOF images - if (!options.output_tof_imaging.empty()) { - auto tof_images = sophiread::timedCreateTOFImages(batches, config->getSuperResolution(), config->getTOFBinEdges(), - options.tof_mode); - sophiread::timedSaveTOFImagingToTIFF(options.output_tof_imaging, tof_images, config->getTOFBinEdges(), - options.tof_filename_base); + ProgramOptions options = parse_arguments(argc, argv); + + // Set logging level based on debug and verbose flags + if (options.debug_logging) { + spdlog::set_level(spdlog::level::debug); + spdlog::debug("Debug logging enabled"); + } else if (options.verbose) { + spdlog::set_level(spdlog::level::info); + } else { + spdlog::set_level(spdlog::level::warn); + } + + spdlog::info("Input file: {}", options.input_tpx3); + spdlog::info("Output hits file: {}", options.output_hits); + spdlog::info("Output events file: {}", options.output_events); + spdlog::info("Configuration file: {}", options.config_file); + spdlog::info("TOF imaging folder: {}", options.output_tof_imaging); + spdlog::info("TOF filename base: {}", options.tof_filename_base); + spdlog::info("TOF mode: {}", options.tof_mode); + + // Load configuration + std::unique_ptr config; + if (!options.config_file.empty()) { + std::string extension = std::filesystem::path(options.config_file).extension().string(); + if (extension == ".json") { + config = std::make_unique(JSONConfigParser::fromFile(options.config_file)); + } else { + spdlog::warn("Deprecated configuration format detected. Please switch to JSON format."); + config = std::make_unique(parseUserDefinedConfigurationFile(options.config_file)); + } + } else { + spdlog::info("No configuration file provided. Using default JSON configuration."); + config = std::make_unique(JSONConfigParser::createDefault()); + } + + spdlog::info("Configuration: {}", config->toString()); + + // Process raw data + auto start = std::chrono::high_resolution_clock::now(); + auto raw_data = sophiread::timedReadDataToCharVec(options.input_tpx3); + auto batches = sophiread::timedFindTPX3H(raw_data); + sophiread::timedLocateTimeStamp(batches, raw_data); + sophiread::timedProcessing(batches, raw_data, *config); + auto end = std::chrono::high_resolution_clock::now(); + auto elapsed = std::chrono::duration_cast(end - start).count(); + spdlog::info("Total processing time: {} s", elapsed / 1e6); + + // Release memory of raw data + std::vector().swap(raw_data); + + // Generate and save TOF images + if (!options.output_tof_imaging.empty()) { + auto tof_images = sophiread::timedCreateTOFImages(batches, config->getSuperResolution(), config->getTOFBinEdges(), options.tof_mode); + sophiread::timedSaveTOFImagingToTIFF(options.output_tof_imaging, tof_images, config->getTOFBinEdges(), options.tof_filename_base); + } + + // Save hits and events + if (!options.output_hits.empty()) { + sophiread::timedSaveHitsToHDF5(options.output_hits, batches); + } + + if (!options.output_events.empty()) { + sophiread::timedSaveEventsToHDF5(options.output_events, batches); + } + + } catch (const std::exception& e) { + spdlog::error("Error: {}", e.what()); + return 1; } - // Save hits and events - if (!options.output_hits.empty()) { - sophiread::timedSaveHitsToHDF5(options.output_hits, batches); - } - - if (!options.output_events.empty()) { - sophiread::timedSaveEventsToHDF5(options.output_events, batches); - } - - } catch (const std::exception& e) { - spdlog::error("Error: {}", e.what()); - return 1; - } - - return 0; + return 0; } diff --git a/sophiread/SophireadCLI/src/sophiread_core.cpp b/sophiread/SophireadCLI/src/sophiread_core.cpp index 0eb3bf2..0b11e83 100644 --- a/sophiread/SophireadCLI/src/sophiread_core.cpp +++ b/sophiread/SophireadCLI/src/sophiread_core.cpp @@ -20,17 +20,14 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -#include "sophiread_core.h" - -#include #include -#include - #include -#include #include - +#include +#include +#include #include "disk_io.h" +#include "sophiread_core.h" namespace sophiread { @@ -40,7 +37,7 @@ namespace sophiread { * @param[in] in_tpx3 * @return std::vector */ -std::vector timedReadDataToCharVec(const std::string& in_tpx3) { +std::vector timedReadDataToCharVec(const std::string &in_tpx3) { auto start = std::chrono::high_resolution_clock::now(); auto raw_data = readTPX3RawToCharVec(in_tpx3); auto end = std::chrono::high_resolution_clock::now(); @@ -56,7 +53,7 @@ std::vector timedReadDataToCharVec(const std::string& in_tpx3) { * @param[in] rawdata * @return std::vector */ -std::vector timedFindTPX3H(const std::vector& rawdata) { +std::vector timedFindTPX3H(const std::vector &rawdata) { auto start = std::chrono::high_resolution_clock::now(); auto batches = findTPX3H(rawdata); auto end = std::chrono::high_resolution_clock::now(); @@ -72,12 +69,12 @@ std::vector timedFindTPX3H(const std::vector& rawdata) { * @param[in, out] batches * @param[in] rawdata */ -void timedLocateTimeStamp(std::vector& batches, const std::vector& rawdata) { +void timedLocateTimeStamp(std::vector &batches, const std::vector &rawdata) { auto start = std::chrono::high_resolution_clock::now(); unsigned long tdc_timestamp = 0; unsigned long long gdc_timestamp = 0; unsigned long timer_lsb32 = 0; - for (auto& tpx3 : batches) { + for (auto &tpx3 : batches) { updateTimestamp(tpx3, rawdata, tdc_timestamp, gdc_timestamp, timer_lsb32); } auto end = std::chrono::high_resolution_clock::now(); @@ -92,15 +89,15 @@ void timedLocateTimeStamp(std::vector& batches, const std::vector& r * @param[in] rawdata * @param[in] config */ -void timedProcessing(std::vector& batches, const std::vector& raw_data, const IConfig& config) { +void timedProcessing(std::vector &batches, const std::vector &raw_data, const IConfig &config) { auto start = std::chrono::high_resolution_clock::now(); - tbb::parallel_for(tbb::blocked_range(0, batches.size()), [&](const tbb::blocked_range& r) { + tbb::parallel_for(tbb::blocked_range(0, batches.size()), [&](const tbb::blocked_range &r) { // Define ABS algorithm with user-defined parameters for each thread auto abs_alg_mt = std::make_unique(config.getABSRadius(), config.getABSMinClusterSize(), config.getABSSpiderTimeRange()); for (size_t i = r.begin(); i != r.end(); ++i) { - auto& tpx3 = batches[i]; + auto &tpx3 = batches[i]; extractHits(tpx3, raw_data); abs_alg_mt->reset(); @@ -121,11 +118,11 @@ void timedProcessing(std::vector& batches, const std::vector& raw_da * @param[in] out_hits * @param[in] hits */ -void timedSaveHitsToHDF5(const std::string& out_hits, std::vector& batches) { +void timedSaveHitsToHDF5(const std::string &out_hits, std::vector &batches) { auto start = std::chrono::high_resolution_clock::now(); // move all hits into a single vector std::vector hits; - for (const auto& tpx3 : batches) { + for (const auto &tpx3 : batches) { auto tpx3_hits = tpx3.hits; hits.insert(hits.end(), tpx3_hits.begin(), tpx3_hits.end()); } @@ -138,15 +135,15 @@ void timedSaveHitsToHDF5(const std::string& out_hits, std::vector& batches /** * @brief Timed save events to HDF5. - * + * * @param[in] out_events * @param[in] batches */ -void timedSaveEventsToHDF5(const std::string& out_events, std::vector& batches) { +void timedSaveEventsToHDF5(const std::string &out_events, std::vector &batches) { auto start = std::chrono::high_resolution_clock::now(); // move all events into a single vector std::vector events; - for (const auto& tpx3 : batches) { + for (const auto &tpx3 : batches) { auto tpx3_events = tpx3.neutrons; events.insert(events.end(), tpx3_events.begin(), tpx3_events.end()); } @@ -160,224 +157,226 @@ void timedSaveEventsToHDF5(const std::string& out_events, std::vector& bat /** * @brief Timed create TOF images. * - * This function creates time-of-flight (TOF) images based on the provided batches of TPX3 data. The TOF images are 2D - * histograms that represent the distribution of neutron events in space for different TOF bins. The function takes into - * account the super resolution, TOF bin edges, and the mode of operation (hit or neutron) to generate the TOF images. + * This function creates time-of-flight (TOF) images based on the provided batches of TPX3 data. The TOF images are 2D histograms that represent the distribution of neutron events in space for different TOF bins. The function takes into account the super resolution, TOF bin edges, and the mode of operation (hit or neutron) to generate the TOF images. * * @param[in] batches The vector of TPX3 batches containing the neutron events. * @param[in] super_resolution The super resolution factor used to calculate the dimensions of each 2D histogram. * @param[in] tof_bin_edges The vector of TOF bin edges used to determine the TOF bin for each neutron event. * @param[in] mode The mode of operation, either "hit" or "neutron", which determines the type of events to process. - * @return std::vector>> The vector of TOF images, where each TOF image is a 2D - * histogram representing the distribution of neutron events in space for a specific TOF bin. + * @return std::vector>> The vector of TOF images, where each TOF image is a 2D histogram representing the distribution of neutron events in space for a specific TOF bin. */ -std::vector>> timedCreateTOFImages(const std::vector& batches, - double super_resolution, - const std::vector& tof_bin_edges, - const std::string& mode) { - auto start = std::chrono::high_resolution_clock::now(); - - // Initialize the TOF images container - std::vector>> tof_images(tof_bin_edges.size() - 1); - - // Sanity checks - if (tof_bin_edges.size() < 2) { - spdlog::error("Invalid TOF bin edges: at least 2 edges are required"); - return {}; - } - if (batches.empty()) { - spdlog::error("No batches to process"); - return tof_images; - } - - // Calculate the dimensions of each 2D histogram based on super_resolution - // one chip: 0-255 pixel pos - // gap: 5 - // total: 0-255 + 5 + 0-255 -> 517 (TPX3@VENUS only) - int dim_x = static_cast(517 * super_resolution); - int dim_y = static_cast(517 * super_resolution); - - spdlog::debug("Creating TOF images with dimensions: {} x {}", dim_x, dim_y); - spdlog::debug("tof_bin_edges size: {}", tof_bin_edges.size()); - if (!tof_bin_edges.empty()) { - spdlog::debug("First bin edge: {}, Last bin edge: {}", tof_bin_edges.front(), tof_bin_edges.back()); - } - - // Initialize each TOF bin's 2D histogram - for (auto& tof_image : tof_images) { - tof_image.resize(dim_y, std::vector(dim_x, 0)); - } - - // Process neutrons from all batches - size_t total_entries = 0; - size_t binned_entries = 0; +std::vector>> timedCreateTOFImages( + const std::vector& batches, + double super_resolution, + const std::vector& tof_bin_edges, + const std::string& mode) { + + auto start = std::chrono::high_resolution_clock::now(); + + // Initialize the TOF images container + std::vector>> tof_images(tof_bin_edges.size() - 1); + + // Sanity checks + if (tof_bin_edges.size() < 2) { + spdlog::error("Invalid TOF bin edges: at least 2 edges are required"); + return {}; + } + if (batches.empty()) { + spdlog::error("No batches to process"); + return tof_images; + } + + // Calculate the dimensions of each 2D histogram based on super_resolution + // one chip: 0-255 pixel pos + // gap: 5 + // total: 0-255 + 5 + 0-255 -> 517 (TPX3@VENUS only) + int dim_x = static_cast(517 * super_resolution); + int dim_y = static_cast(517 * super_resolution); + + spdlog::debug("Creating TOF images with dimensions: {} x {}", dim_x, dim_y); + spdlog::debug("tof_bin_edges size: {}", tof_bin_edges.size()); + if (!tof_bin_edges.empty()) { + spdlog::debug("First bin edge: {}, Last bin edge: {}", tof_bin_edges.front(), tof_bin_edges.back()); + } + + // Initialize each TOF bin's 2D histogram + for (auto& tof_image : tof_images) { + tof_image.resize(dim_y, std::vector(dim_x, 0)); + } - for (size_t batch_index = 0; batch_index < batches.size(); ++batch_index) { - const auto& batch = batches[batch_index]; - spdlog::debug("Processing batch {}", batch_index); + // Process neutrons from all batches + size_t total_entries = 0; + size_t binned_entries = 0; - std::vector entries; - if (mode == "hit") { - entries.reserve(batch.hits.size()); - for (const auto& hit : batch.hits) { - entries.push_back(static_cast(&hit)); - } - } else { - entries.reserve(batch.neutrons.size()); - for (const auto& neutron : batch.neutrons) { - entries.push_back(static_cast(&neutron)); - } - } + for (size_t batch_index = 0; batch_index < batches.size(); ++batch_index) { + const auto& batch = batches[batch_index]; + spdlog::debug("Processing batch {}", batch_index); - if (entries.empty()) { - spdlog::debug("Batch {} is empty", batch_index); - continue; - } + std::vector entries; + if (mode == "hit") { + entries.reserve(batch.hits.size()); + for (const auto& hit : batch.hits) { + entries.push_back(static_cast(&hit)); + } + } else { + entries.reserve(batch.neutrons.size()); + for (const auto& neutron : batch.neutrons) { + entries.push_back(static_cast(&neutron)); + } + } + + if (entries.empty()) { + spdlog::debug("Batch {} is empty", batch_index); + continue; + } - for (const auto& entry : entries) { - total_entries++; - double tof_ns = entry->iGetTOF_ns(); - - // Find the correct TOF bin - // NOTE: tof_bin_edges are in sec, and tof_ns are in nano secs - spdlog::debug("tof_ns: {}, tof_ns/1e9: {}", tof_ns, tof_ns / 1e9); - auto it = std::lower_bound(tof_bin_edges.begin(), tof_bin_edges.end(), tof_ns / 1e9); - if (it != tof_bin_edges.begin()) { - size_t bin_index = std::distance(tof_bin_edges.begin(), it) - 1; - - // Calculate the x and y indices in the 2D histogram - int x = static_cast(entry->iGetX() * super_resolution); - int y = static_cast(entry->iGetY() * super_resolution); - - // Ensure x and y are within bounds - if (x >= 0 && x < dim_x && y >= 0 && y < dim_y) { - // Increment the count in the appropriate bin and position - tof_images[bin_index][y][x]++; - binned_entries++; + for (const auto& entry : entries) { + total_entries++; + double tof_ns = entry->iGetTOF_ns(); + + // Find the correct TOF bin + // NOTE: tof_bin_edges are in sec, and tof_ns are in nano secs + spdlog::debug("tof_ns: {}, tof_ns/1e9: {}", tof_ns, tof_ns/1e9); + auto it = std::lower_bound(tof_bin_edges.begin(), tof_bin_edges.end(), tof_ns/1e9); + if (it != tof_bin_edges.begin()) { + size_t bin_index = std::distance(tof_bin_edges.begin(), it) - 1; + + // Calculate the x and y indices in the 2D histogram + int x = static_cast(entry->iGetX() * super_resolution); + int y = static_cast(entry->iGetY() * super_resolution); + + // Ensure x and y are within bounds + if (x >= 0 && x < dim_x && y >= 0 && y < dim_y) { + // Increment the count in the appropriate bin and position + tof_images[bin_index][y][x]++; + binned_entries++; + } + } } - } } - } - auto end = std::chrono::high_resolution_clock::now(); - auto elapsed = std::chrono::duration_cast(end - start).count(); - spdlog::info("TOF image creation time: {} s", elapsed / 1e6); - spdlog::info("Total entries: {}, Binned entries: {}", total_entries, binned_entries); + auto end = std::chrono::high_resolution_clock::now(); + auto elapsed = std::chrono::duration_cast(end - start).count(); + spdlog::info("TOF image creation time: {} s", elapsed / 1e6); + spdlog::info("Total entries: {}, Binned entries: {}", total_entries, binned_entries); - return tof_images; + return tof_images; } /** * @brief Timed save TOF imaging to TIFF. - * + * * @param[in] out_tof_imaging * @param[in] batches * @param[in] tof_bin_edges * @param[in] tof_filename_base */ -void timedSaveTOFImagingToTIFF(const std::string& out_tof_imaging, - const std::vector>>& tof_images, - const std::vector& tof_bin_edges, const std::string& tof_filename_base) { - auto start = std::chrono::high_resolution_clock::now(); +void timedSaveTOFImagingToTIFF( + const std::string& out_tof_imaging, + const std::vector>>& tof_images, + const std::vector& tof_bin_edges, + const std::string& tof_filename_base) +{ + auto start = std::chrono::high_resolution_clock::now(); + + // 1. Create output directory if it doesn't exist + if (!std::filesystem::exists(out_tof_imaging)) { + std::filesystem::create_directories(out_tof_imaging); + spdlog::info("Created output directory: {}", out_tof_imaging); + } - // 1. Create output directory if it doesn't exist - if (!std::filesystem::exists(out_tof_imaging)) { - std::filesystem::create_directories(out_tof_imaging); - spdlog::info("Created output directory: {}", out_tof_imaging); - } + // 2. Initialize vector for spectral data + std::vector spectral_counts(tof_images.size(), 0); + + // 3. Iterate through each TOF bin and save TIFF files + tbb::parallel_for(tbb::blocked_range(0, tof_images.size()), [&](const tbb::blocked_range& range) { + for (size_t bin = range.begin(); bin < range.end(); ++bin) { + // Construct filename + std::string filename = fmt::format("{}/{}_bin_{:04d}.tiff", out_tof_imaging, tof_filename_base, bin + 1); + + // prepare container and fill with current hist2d + uint32_t width = tof_images[bin][0].size(); + uint32_t height = tof_images[bin].size(); + std::vector> accumulated_image = tof_images[bin]; + + // check if file already exist + if (std::filesystem::exists(filename)) { + TIFF* existing_tif = TIFFOpen(filename.c_str(), "r"); + if (existing_tif) { + uint32_t existing_width, existing_height; + TIFFGetField(existing_tif, TIFFTAG_IMAGEWIDTH, &existing_width); + TIFFGetField(existing_tif, TIFFTAG_IMAGELENGTH, &existing_height); + + if (existing_width == width && existing_height == height) { + // Dimensions match, proceed with accumulation + for (uint32_t row = 0; row < height; ++row) { + std::vector scanline(width); + TIFFReadScanline(existing_tif, scanline.data(), row); + for (uint32_t col = 0; col < width; ++col) { + accumulated_image[row][col] += scanline[col]; + } + } + spdlog::debug("Accumulated counts for existing file: {}", filename); + } else { + spdlog::error("Dimension mismatch for file: {}. Expected {}x{}, got {}x{}. Overwriting.", + filename, width, height, existing_width, existing_height); + } + TIFFClose(existing_tif); + } else { + spdlog::error("Failed to open existing TIFF file for reading: {}", filename); + } + } - // 2. Initialize vector for spectral data - std::vector spectral_counts(tof_images.size(), 0); - - // 3. Iterate through each TOF bin and save TIFF files - tbb::parallel_for(tbb::blocked_range(0, tof_images.size()), [&](const tbb::blocked_range& range) { - for (size_t bin = range.begin(); bin < range.end(); ++bin) { - // Construct filename - std::string filename = fmt::format("{}/{}_bin_{:04d}.tiff", out_tof_imaging, tof_filename_base, bin + 1); - - // prepare container and fill with current hist2d - uint32_t width = tof_images[bin][0].size(); - uint32_t height = tof_images[bin].size(); - std::vector> accumulated_image = tof_images[bin]; - - // check if file already exist - if (std::filesystem::exists(filename)) { - TIFF* existing_tif = TIFFOpen(filename.c_str(), "r"); - if (existing_tif) { - uint32_t existing_width, existing_height; - TIFFGetField(existing_tif, TIFFTAG_IMAGEWIDTH, &existing_width); - TIFFGetField(existing_tif, TIFFTAG_IMAGELENGTH, &existing_height); - - if (existing_width == width && existing_height == height) { - // Dimensions match, proceed with accumulation - for (uint32_t row = 0; row < height; ++row) { - std::vector scanline(width); - TIFFReadScanline(existing_tif, scanline.data(), row); - for (uint32_t col = 0; col < width; ++col) { - accumulated_image[row][col] += scanline[col]; + // Write or update TIFF file + TIFF* tif = TIFFOpen(filename.c_str(), "w"); + if (tif) { + uint32_t width = tof_images[bin][0].size(); + uint32_t height = tof_images[bin].size(); + + TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, width); + TIFFSetField(tif, TIFFTAG_IMAGELENGTH, height); + TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 1); + TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 32); + TIFFSetField(tif, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT); + TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG); + TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK); + + for (uint32_t row = 0; row < height; ++row) { + TIFFWriteScanline(tif, accumulated_image[row].data(), row); } - } - spdlog::debug("Accumulated counts for existing file: {}", filename); + + TIFFClose(tif); + spdlog::debug("Wrote TIFF file: {}", filename); } else { - spdlog::error("Dimension mismatch for file: {}. Expected {}x{}, got {}x{}. Overwriting.", filename, width, - height, existing_width, existing_height); + spdlog::error("Failed to open TIFF file for writing: {}", filename); } - TIFFClose(existing_tif); - } else { - spdlog::error("Failed to open existing TIFF file for reading: {}", filename); - } - } - - // Write or update TIFF file - TIFF* tif = TIFFOpen(filename.c_str(), "w"); - if (tif) { - uint32_t width = tof_images[bin][0].size(); - uint32_t height = tof_images[bin].size(); - - TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, width); - TIFFSetField(tif, TIFFTAG_IMAGELENGTH, height); - TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 1); - TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 32); - TIFFSetField(tif, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT); - TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG); - TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK); - - for (uint32_t row = 0; row < height; ++row) { - TIFFWriteScanline(tif, accumulated_image[row].data(), row); - } - TIFFClose(tif); - spdlog::debug("Wrote TIFF file: {}", filename); - } else { - spdlog::error("Failed to open TIFF file for writing: {}", filename); + // Accumulate counts for spectral file + spectral_counts[bin] = std::accumulate(accumulated_image.begin(), accumulated_image.end(), 0ULL, + [](unsigned long long sum, const std::vector& row) { + return sum + std::accumulate(row.begin(), row.end(), 0ULL); + }); } - - // Accumulate counts for spectral file - spectral_counts[bin] = std::accumulate(accumulated_image.begin(), accumulated_image.end(), 0ULL, - [](unsigned long long sum, const std::vector& row) { - return sum + std::accumulate(row.begin(), row.end(), 0ULL); - }); - } - }); - - // 4. Write spectral file - std::string spectral_filename = fmt::format("{}/{}_Spectra.txt", out_tof_imaging, tof_filename_base); - // Write spectral data to file - std::ofstream spectral_file(spectral_filename); - if (spectral_file.is_open()) { - spectral_file << "shutter_time,counts\n"; - for (size_t bin = 0; bin < tof_bin_edges.size() - 1; ++bin) { - spectral_file << tof_bin_edges[bin + 1] << "," << spectral_counts[bin] << "\n"; + }); + + // 4. Write spectral file + std::string spectral_filename = fmt::format("{}/{}_Spectra.txt", out_tof_imaging, tof_filename_base); + // Write spectral data to file + std::ofstream spectral_file(spectral_filename); + if (spectral_file.is_open()) { + spectral_file << "shutter_time,counts\n"; + for (size_t bin = 0; bin < tof_bin_edges.size() - 1; ++bin) { + spectral_file << tof_bin_edges[bin + 1] << "," << spectral_counts[bin] << "\n"; + } + spectral_file.close(); + spdlog::info("Wrote spectral file: {}", spectral_filename); + } else { + spdlog::error("Failed to open spectra file for writing: {}", spectral_filename); } - spectral_file.close(); - spdlog::info("Wrote spectral file: {}", spectral_filename); - } else { - spdlog::error("Failed to open spectra file for writing: {}", spectral_filename); - } - auto end = std::chrono::high_resolution_clock::now(); - auto duration = std::chrono::duration_cast(end - start); - spdlog::info("TIFF and spectra file writing completed in {} ms", duration.count()); + auto end = std::chrono::high_resolution_clock::now(); + auto duration = std::chrono::duration_cast(end - start); + spdlog::info("TIFF and spectra file writing completed in {} ms", duration.count()); } -} // namespace sophiread \ No newline at end of file +} // namespace sophiread \ No newline at end of file diff --git a/sophiread/SophireadCLI/src/user_config.cpp b/sophiread/SophireadCLI/src/user_config.cpp index 8b7d0c0..e2fd5e5 100644 --- a/sophiread/SophireadCLI/src/user_config.cpp +++ b/sophiread/SophireadCLI/src/user_config.cpp @@ -31,23 +31,13 @@ /** * @brief Construct a new UserConfig object with default values. */ -UserConfig::UserConfig() - : m_abs_radius(5.0), - m_abs_min_cluster_size(1), - m_abs_spider_time_range(75), - m_tof_binning(), - m_super_resolution(1.0) {} +UserConfig::UserConfig() : m_abs_radius(5.0), m_abs_min_cluster_size(1), m_abs_spider_time_range(75), m_tof_binning(), m_super_resolution(1.0) {} /** * @brief Construct a new UserConfig object with user-defined values */ -UserConfig::UserConfig(double abs_radius, unsigned long int abs_min_cluster_size, - unsigned long int abs_spider_time_range) - : m_abs_radius(abs_radius), - m_abs_min_cluster_size(abs_min_cluster_size), - m_abs_spider_time_range(abs_spider_time_range), - m_tof_binning(), - m_super_resolution(1.0) {} +UserConfig::UserConfig(double abs_radius, unsigned long int abs_min_cluster_size, unsigned long int abs_spider_time_range) + : m_abs_radius(abs_radius), m_abs_min_cluster_size(abs_min_cluster_size), m_abs_spider_time_range(abs_spider_time_range), m_tof_binning(), m_super_resolution(1.0) {} /** * @brief Helper function to convert a user configuration to a string for console output. @@ -56,7 +46,8 @@ UserConfig::UserConfig(double abs_radius, unsigned long int abs_min_cluster_size */ std::string UserConfig::toString() const { std::stringstream ss; - ss << "ABS: radius=" << m_abs_radius << ", min_cluster_size=" << m_abs_min_cluster_size + ss << "ABS: radius=" << m_abs_radius + << ", min_cluster_size=" << m_abs_min_cluster_size << ", spider_time_range=" << m_abs_spider_time_range; // Add TOF binning information @@ -127,7 +118,8 @@ UserConfig parseUserDefinedConfigurationFile(const std::string& filepath) { } else if (name == "tof_max") { double value; ss >> value; - } else { + } + else { spdlog::warn("Unknown parameter {} in the user-defined configuration file.", name); } } diff --git a/sophiread/SophireadCLI/src/venus_auto_reducer.cpp b/sophiread/SophireadCLI/src/venus_auto_reducer.cpp index 133da1e..445999c 100644 --- a/sophiread/SophireadCLI/src/venus_auto_reducer.cpp +++ b/sophiread/SophireadCLI/src/venus_auto_reducer.cpp @@ -4,7 +4,7 @@ * @brief Auto reducer demo application for VENUS@SNS * @version 0.1 * @date 2024-08-21 - * + * * @copyright Copyright (c) 2024 * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -19,242 +19,238 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -#include -#include -#include - -#include -#include -#include #include #include +#include #include -#include +#include #include - -#include "json_config_parser.h" +#include +#include +#include #include "sophiread_core.h" +#include "json_config_parser.h" +#include +#include namespace fs = std::filesystem; struct ProgramOptions { - std::string input_dir; - std::string output_dir; - std::string config_file; - std::string tiff_base = "tof_image"; - std::string tof_mode = "neutron"; - int check_interval = 5; - bool verbose = false; - bool debug = false; + std::string input_dir; + std::string output_dir; + std::string config_file; + std::string tiff_base = "tof_image"; + std::string tof_mode = "neutron"; + int check_interval = 5; + bool verbose = false; + bool debug = false; }; void print_usage(const char* program_name) { - spdlog::info( - "Usage: {} -i -o [-u ] [-f ] [-m ] [-c " - "] [-v] [-d]", - program_name); - spdlog::info("Options:"); - spdlog::info(" -i Input directory with TPX3 files"); - spdlog::info(" -o Output directory for TIFF files"); - spdlog::info(" -u User configuration JSON file (optional)"); - spdlog::info(" -f Base name for TIFF files (default: tof_image)"); - spdlog::info(" -m TOF mode: 'hit' or 'neutron' (default: neutron)"); - spdlog::info(" -c Check interval in seconds (default: 5)"); - spdlog::info(" -d Debug output"); - spdlog::info(" -v Verbose output"); + spdlog::info("Usage: {} -i -o [-u ] [-f ] [-m ] [-c ] [-v] [-d]", program_name); + spdlog::info("Options:"); + spdlog::info(" -i Input directory with TPX3 files"); + spdlog::info(" -o Output directory for TIFF files"); + spdlog::info(" -u User configuration JSON file (optional)"); + spdlog::info(" -f Base name for TIFF files (default: tof_image)"); + spdlog::info(" -m TOF mode: 'hit' or 'neutron' (default: neutron)"); + spdlog::info(" -c Check interval in seconds (default: 5)"); + spdlog::info(" -d Debug output"); + spdlog::info(" -v Verbose output"); } ProgramOptions parse_arguments(int argc, char* argv[]) { - ProgramOptions options; - int opt; + ProgramOptions options; + int opt; + + while ((opt = getopt(argc, argv, "i:o:u:f:m:c:dv")) != -1) { + switch (opt) { + case 'i': + options.input_dir = optarg; + break; + case 'o': + options.output_dir = optarg; + break; + case 'u': + options.config_file = optarg; + break; + case 'f': + options.tiff_base = optarg; + break; + case 'm': + options.tof_mode = optarg; + break; + case 'c': + options.check_interval = std::stoi(optarg); + break; + case 'd': + options.debug = true; + case 'v': + options.verbose = true; + break; + default: + print_usage(argv[0]); + throw std::runtime_error("Invalid argument"); + } + } - while ((opt = getopt(argc, argv, "i:o:u:f:m:c:dv")) != -1) { - switch (opt) { - case 'i': - options.input_dir = optarg; - break; - case 'o': - options.output_dir = optarg; - break; - case 'u': - options.config_file = optarg; - break; - case 'f': - options.tiff_base = optarg; - break; - case 'm': - options.tof_mode = optarg; - break; - case 'c': - options.check_interval = std::stoi(optarg); - break; - case 'd': - options.debug = true; - case 'v': - options.verbose = true; - break; - default: + // Validate required arguments + if (options.input_dir.empty() || options.output_dir.empty()) { print_usage(argv[0]); - throw std::runtime_error("Invalid argument"); + throw std::runtime_error("Missing required arguments"); } - } - - // Validate required arguments - if (options.input_dir.empty() || options.output_dir.empty()) { - print_usage(argv[0]); - throw std::runtime_error("Missing required arguments"); - } - // Validate tof_mode - if (options.tof_mode != "hit" && options.tof_mode != "neutron") { - throw std::runtime_error("Invalid TOF mode. Use 'hit' or 'neutron'."); - } + // Validate tof_mode + if (options.tof_mode != "hit" && options.tof_mode != "neutron") { + throw std::runtime_error("Invalid TOF mode. Use 'hit' or 'neutron'."); + } - // Validate check_interval - if (options.check_interval <= 0) { - throw std::runtime_error("Check interval must be a positive integer."); - } + // Validate check_interval + if (options.check_interval <= 0) { + throw std::runtime_error("Check interval must be a positive integer."); + } - return options; + return options; } /** * @brief Process all existing tpx3 files - * - * @param[in] input_dir - * @param[in] output_dir - * @param[in] tiff_base - * @param[in] tof_mode - * @param[in] config + * + * @param[in] input_dir + * @param[in] output_dir + * @param[in] tiff_base + * @param[in] tof_mode + * @param[in] config * @param[in, out] processed_files */ -void process_existing_files(const std::string& input_dir, const std::string& output_dir, const std::string& tiff_base, - const std::string& tof_mode, const IConfig& config, - std::unordered_set& processed_files) { - spdlog::info("Processing existing files in {}", input_dir); - - // NOTE: we need to process files sequentially as we are accumulating the counts to the - // same set of tiff files in the output folder - for (const auto& entry : fs::directory_iterator(input_dir)) { - if (entry.is_regular_file() && entry.path().extension() == ".tpx3") { - std::string filename = entry.path().stem().string(); - - if (processed_files.find(filename) != processed_files.end()) { - spdlog::debug("Skipping already processed file: {}", filename); - continue; - } - - spdlog::info("Processing file: {}", entry.path().string()); - - try { - // Read the TPX3 file - auto raw_data = sophiread::timedReadDataToCharVec(entry.path().string()); - - // Find TPX3 headers - auto batches = sophiread::timedFindTPX3H(raw_data); +void process_existing_files(const std::string& input_dir, const std::string& output_dir, + const std::string& tiff_base, const std::string& tof_mode, + const IConfig& config, std::unordered_set& processed_files) { + spdlog::info("Processing existing files in {}", input_dir); - // Process the data - sophiread::timedLocateTimeStamp(batches, raw_data); - sophiread::timedProcessing(batches, raw_data, config); - - // Generate output file name - std::string output_file = fs::path(output_dir) / (tiff_base + "_bin_xxxx.tiff"); - - // Create TOF images - auto tof_images = - sophiread::timedCreateTOFImages(batches, config.getSuperResolution(), config.getTOFBinEdges(), tof_mode); - - // Save TOF images - sophiread::timedSaveTOFImagingToTIFF(output_dir, tof_images, config.getTOFBinEdges(), tiff_base); - - spdlog::info("Processed and saved: {}", output_file); - - // record processed file - processed_files.insert(entry.path().stem().string()); - } catch (const std::exception& e) { - spdlog::error("Error processing file {}: {}", entry.path().string(), e.what()); - } + // NOTE: we need to process files sequentially as we are accumulating the counts to the + // same set of tiff files in the output folder + for (const auto& entry : fs::directory_iterator(input_dir)) { + if (entry.is_regular_file() && entry.path().extension() == ".tpx3") { + std::string filename = entry.path().stem().string(); + + if (processed_files.find(filename) != processed_files.end()) { + spdlog::debug("Skipping already processed file: {}", filename); + continue; + } + + spdlog::info("Processing file: {}", entry.path().string()); + + try { + // Read the TPX3 file + auto raw_data = sophiread::timedReadDataToCharVec(entry.path().string()); + + // Find TPX3 headers + auto batches = sophiread::timedFindTPX3H(raw_data); + + // Process the data + sophiread::timedLocateTimeStamp(batches, raw_data); + sophiread::timedProcessing(batches, raw_data, config); + + // Generate output file name + std::string output_file = fs::path(output_dir) / (tiff_base + "_bin_xxxx.tiff"); + + // Create TOF images + auto tof_images = sophiread::timedCreateTOFImages(batches, config.getSuperResolution(), config.getTOFBinEdges(), tof_mode); + + // Save TOF images + sophiread::timedSaveTOFImagingToTIFF(output_dir, tof_images, config.getTOFBinEdges(), tiff_base); + + spdlog::info("Processed and saved: {}", output_file); + + // record processed file + processed_files.insert(entry.path().stem().string()); + } catch (const std::exception& e) { + spdlog::error("Error processing file {}: {}", entry.path().string(), e.what()); + } + } } - } - // Create a string to hold the formatted output - std::ostringstream oss; - oss << "Processed files: ["; + // Create a string to hold the formatted output + std::ostringstream oss; + oss << "Processed files: ["; - // Loop through the set and concatenate elements - for (auto it = processed_files.begin(); it != processed_files.end(); ++it) { - if (it != processed_files.begin()) { - oss << ", "; // Add a comma before each element except the first + // Loop through the set and concatenate elements + for (auto it = processed_files.begin(); it != processed_files.end(); ++it) { + if (it != processed_files.begin()) { + oss << ", "; // Add a comma before each element except the first + } + oss << *it; } - oss << *it; - } - oss << "]"; - spdlog::info(oss.str()); + oss << "]"; + spdlog::info(oss.str()); } -void monitor_directory(const std::string& input_dir, const std::string& output_dir, const std::string& tiff_base, - const std::string& tof_mode, const IConfig& config, - std::unordered_set& processed_files, int check_interval) { - spdlog::info("Starting directory monitoring: {}", input_dir); - spdlog::info("Check interval: {} seconds", check_interval); - - while (true) { - // Check for *.nxs.h5 file - for (const auto& entry : fs::directory_iterator(input_dir)) { - if (entry.is_regular_file() && entry.path().extension() == ".h5" && entry.path().stem().extension() == ".nxs") { - spdlog::info("Found *.nxs.h5 file. Stopping monitoring."); - return; - } +void monitor_directory(const std::string& input_dir, const std::string& output_dir, + const std::string& tiff_base, const std::string& tof_mode, + const IConfig& config, std::unordered_set& processed_files, + int check_interval) { + spdlog::info("Starting directory monitoring: {}", input_dir); + spdlog::info("Check interval: {} seconds", check_interval); + + while (true) { + // Check for *.nxs.h5 file + for (const auto& entry : fs::directory_iterator(input_dir)) { + if (entry.is_regular_file() && entry.path().extension() == ".h5" && + entry.path().stem().extension() == ".nxs") { + spdlog::info("Found *.nxs.h5 file. Stopping monitoring."); + return; + } + } + + // Process any new files + process_existing_files(input_dir, output_dir, tiff_base, tof_mode, config, processed_files); + + // Wait before next check + std::this_thread::sleep_for(std::chrono::seconds(check_interval)); } - - // Process any new files - process_existing_files(input_dir, output_dir, tiff_base, tof_mode, config, processed_files); - - // Wait before next check - std::this_thread::sleep_for(std::chrono::seconds(check_interval)); - } } int main(int argc, char* argv[]) { - try { - ProgramOptions options = parse_arguments(argc, argv); - - // Set logging level based on verbose flag - if (options.debug) { - spdlog::set_level(spdlog::level::debug); - spdlog::debug("Debug logging enabled"); - } else if (options.verbose) { - spdlog::set_level(spdlog::level::info); - } else { - spdlog::set_level(spdlog::level::warn); + try { + ProgramOptions options = parse_arguments(argc, argv); + + // Set logging level based on verbose flag + if (options.debug){ + spdlog::set_level(spdlog::level::debug); + spdlog::debug("Debug logging enabled"); + } else if (options.verbose) { + spdlog::set_level(spdlog::level::info); + } else { + spdlog::set_level(spdlog::level::warn); + } + + spdlog::info("Input directory: {}", options.input_dir); + spdlog::info("Output directory: {}", options.output_dir); + spdlog::info("Config file: {}", options.config_file); + spdlog::info("TIFF base name: {}", options.tiff_base); + spdlog::info("TOF mode: {}", options.tof_mode); + + // Load configuration + std::unique_ptr config; + if (!options.config_file.empty()) { + spdlog::info("Config file: {}", options.config_file); + config = std::make_unique(JSONConfigParser::fromFile(options.config_file)); + } else { + spdlog::info("Using default configuration"); + config = std::make_unique(JSONConfigParser::createDefault()); + } + + spdlog::info("Configuration: {}", config->toString()); + + // Process existing files + std::unordered_set processed_files; + monitor_directory(options.input_dir, options.output_dir, options.tiff_base, + options.tof_mode, *config, processed_files, options.check_interval); + + } catch (const std::exception& e) { + spdlog::error("Error: {}", e.what()); + return 1; } - spdlog::info("Input directory: {}", options.input_dir); - spdlog::info("Output directory: {}", options.output_dir); - spdlog::info("Config file: {}", options.config_file); - spdlog::info("TIFF base name: {}", options.tiff_base); - spdlog::info("TOF mode: {}", options.tof_mode); - - // Load configuration - std::unique_ptr config; - if (!options.config_file.empty()) { - spdlog::info("Config file: {}", options.config_file); - config = std::make_unique(JSONConfigParser::fromFile(options.config_file)); - } else { - spdlog::info("Using default configuration"); - config = std::make_unique(JSONConfigParser::createDefault()); - } - - spdlog::info("Configuration: {}", config->toString()); - - // Process existing files - std::unordered_set processed_files; - monitor_directory(options.input_dir, options.output_dir, options.tiff_base, options.tof_mode, *config, - processed_files, options.check_interval); - - } catch (const std::exception& e) { - spdlog::error("Error: {}", e.what()); - return 1; - } - - return 0; + return 0; } \ No newline at end of file diff --git a/sophiread/SophireadCLI/tests/test_json_config_parser.cpp b/sophiread/SophireadCLI/tests/test_json_config_parser.cpp index d8ece0f..6c70f5e 100644 --- a/sophiread/SophireadCLI/tests/test_json_config_parser.cpp +++ b/sophiread/SophireadCLI/tests/test_json_config_parser.cpp @@ -3,7 +3,7 @@ * @author Chen Zhang (zhangc@orn.gov) * @brief Unit tests for JSONConfigParser class. * @date 2024-08-16 - * + * * @copyright Copyright (c) 2024 * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -18,19 +18,17 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ +#include "json_config_parser.h" #include - -#include #include - -#include "json_config_parser.h" +#include class JSONConfigParserTest : public ::testing::Test { - protected: - void SetUp() override { - // This setup will be used for the uniform binning test - std::ofstream config_file("test_config_uniform.json"); - config_file << R"({ +protected: + void SetUp() override { + // This setup will be used for the uniform binning test + std::ofstream config_file("test_config_uniform.json"); + config_file << R"({ "abs": { "radius": 6.0, "min_cluster_size": 2, @@ -44,11 +42,11 @@ class JSONConfigParserTest : public ::testing::Test { "super_resolution": 2.0 } })"; - config_file.close(); + config_file.close(); - // Setup for custom binning test - std::ofstream config_file_custom("test_config_custom.json"); - config_file_custom << R"({ + // Setup for custom binning test + std::ofstream config_file_custom("test_config_custom.json"); + config_file_custom << R"({ "abs": { "radius": 7.0, "min_cluster_size": 3, @@ -58,84 +56,84 @@ class JSONConfigParserTest : public ::testing::Test { "bin_edges": [0, 0.001, 0.002, 0.005, 0.01, 0.0167] } })"; - config_file_custom.close(); + config_file_custom.close(); - // Setup for default values test - std::ofstream config_file_default("test_config_default.json"); - config_file_default << R"({ + // Setup for default values test + std::ofstream config_file_default("test_config_default.json"); + config_file_default << R"({ "abs": {} })"; - config_file_default.close(); - } - - void TearDown() override { - std::remove("test_config_uniform.json"); - std::remove("test_config_custom.json"); - std::remove("test_config_default.json"); - } + config_file_default.close(); + } + + void TearDown() override { + std::remove("test_config_uniform.json"); + std::remove("test_config_custom.json"); + std::remove("test_config_default.json"); + } }; TEST_F(JSONConfigParserTest, ParsesSuperResolutionCorrectly) { - auto config = JSONConfigParser::fromFile("test_config_uniform.json"); - EXPECT_DOUBLE_EQ(config.getSuperResolution(), 2.0); + auto config = JSONConfigParser::fromFile("test_config_uniform.json"); + EXPECT_DOUBLE_EQ(config.getSuperResolution(), 2.0); } TEST_F(JSONConfigParserTest, ParsesUniformConfigCorrectly) { - auto config = JSONConfigParser::fromFile("test_config_uniform.json"); - - EXPECT_DOUBLE_EQ(config.getABSRadius(), 6.0); - EXPECT_EQ(config.getABSMinClusterSize(), 2); - EXPECT_EQ(config.getABSSpiderTimeRange(), 80); - - auto bin_edges = config.getTOFBinEdges(); - EXPECT_EQ(bin_edges.size(), 1001); // 1000 bins + 1 - EXPECT_DOUBLE_EQ(bin_edges.front(), 0); - EXPECT_DOUBLE_EQ(bin_edges.back(), 0.0167); + auto config = JSONConfigParser::fromFile("test_config_uniform.json"); + + EXPECT_DOUBLE_EQ(config.getABSRadius(), 6.0); + EXPECT_EQ(config.getABSMinClusterSize(), 2); + EXPECT_EQ(config.getABSSpiderTimeRange(), 80); + + auto bin_edges = config.getTOFBinEdges(); + EXPECT_EQ(bin_edges.size(), 1001); // 1000 bins + 1 + EXPECT_DOUBLE_EQ(bin_edges.front(), 0); + EXPECT_DOUBLE_EQ(bin_edges.back(), 0.0167); } TEST_F(JSONConfigParserTest, ParsesCustomConfigCorrectly) { - auto config = JSONConfigParser::fromFile("test_config_custom.json"); - - EXPECT_DOUBLE_EQ(config.getABSRadius(), 7.0); - EXPECT_EQ(config.getABSMinClusterSize(), 3); - EXPECT_EQ(config.getABSSpiderTimeRange(), 85); - - auto bin_edges = config.getTOFBinEdges(); - EXPECT_EQ(bin_edges.size(), 6); - EXPECT_DOUBLE_EQ(bin_edges[0], 0); - EXPECT_DOUBLE_EQ(bin_edges[1], 0.001); - EXPECT_DOUBLE_EQ(bin_edges[2], 0.002); - EXPECT_DOUBLE_EQ(bin_edges[3], 0.005); - EXPECT_DOUBLE_EQ(bin_edges[4], 0.01); - EXPECT_DOUBLE_EQ(bin_edges[5], 0.0167); + auto config = JSONConfigParser::fromFile("test_config_custom.json"); + + EXPECT_DOUBLE_EQ(config.getABSRadius(), 7.0); + EXPECT_EQ(config.getABSMinClusterSize(), 3); + EXPECT_EQ(config.getABSSpiderTimeRange(), 85); + + auto bin_edges = config.getTOFBinEdges(); + EXPECT_EQ(bin_edges.size(), 6); + EXPECT_DOUBLE_EQ(bin_edges[0], 0); + EXPECT_DOUBLE_EQ(bin_edges[1], 0.001); + EXPECT_DOUBLE_EQ(bin_edges[2], 0.002); + EXPECT_DOUBLE_EQ(bin_edges[3], 0.005); + EXPECT_DOUBLE_EQ(bin_edges[4], 0.01); + EXPECT_DOUBLE_EQ(bin_edges[5], 0.0167); } TEST_F(JSONConfigParserTest, UsesDefaultValuesCorrectly) { - auto config = JSONConfigParser::fromFile("test_config_default.json"); - - EXPECT_DOUBLE_EQ(config.getABSRadius(), 5.0); - EXPECT_EQ(config.getABSMinClusterSize(), 1); - EXPECT_EQ(config.getABSSpiderTimeRange(), 75); - - auto bin_edges = config.getTOFBinEdges(); - EXPECT_EQ(bin_edges.size(), 1501); // 1500 bins + 1 - EXPECT_DOUBLE_EQ(bin_edges.front(), 0); - EXPECT_DOUBLE_EQ(bin_edges.back(), 0.0167); - EXPECT_DOUBLE_EQ(config.getSuperResolution(), 1.0); + auto config = JSONConfigParser::fromFile("test_config_default.json"); + + EXPECT_DOUBLE_EQ(config.getABSRadius(), 5.0); + EXPECT_EQ(config.getABSMinClusterSize(), 1); + EXPECT_EQ(config.getABSSpiderTimeRange(), 75); + + auto bin_edges = config.getTOFBinEdges(); + EXPECT_EQ(bin_edges.size(), 1501); // 1500 bins + 1 + EXPECT_DOUBLE_EQ(bin_edges.front(), 0); + EXPECT_DOUBLE_EQ(bin_edges.back(), 0.0167); + EXPECT_DOUBLE_EQ(config.getSuperResolution(), 1.0); } TEST_F(JSONConfigParserTest, ThrowsOnMissingFile) { - EXPECT_THROW(JSONConfigParser::fromFile("non_existent.json"), std::runtime_error); + EXPECT_THROW(JSONConfigParser::fromFile("non_existent.json"), std::runtime_error); } TEST_F(JSONConfigParserTest, ToStringMethodWorksCorrectly) { - auto config = JSONConfigParser::fromFile("test_config_uniform.json"); - std::string result = config.toString(); - - EXPECT_TRUE(result.find("radius=6") != std::string::npos); - EXPECT_TRUE(result.find("min_cluster_size=2") != std::string::npos); - EXPECT_TRUE(result.find("spider_time_range=80") != std::string::npos); - EXPECT_TRUE(result.find("TOF bins=1000") != std::string::npos); - EXPECT_TRUE(result.find("TOF max=16.7 ms") != std::string::npos); - EXPECT_TRUE(result.find("Super Resolution=2") != std::string::npos); + auto config = JSONConfigParser::fromFile("test_config_uniform.json"); + std::string result = config.toString(); + + EXPECT_TRUE(result.find("radius=6") != std::string::npos); + EXPECT_TRUE(result.find("min_cluster_size=2") != std::string::npos); + EXPECT_TRUE(result.find("spider_time_range=80") != std::string::npos); + EXPECT_TRUE(result.find("TOF bins=1000") != std::string::npos); + EXPECT_TRUE(result.find("TOF max=16.7 ms") != std::string::npos); + EXPECT_TRUE(result.find("Super Resolution=2") != std::string::npos); } diff --git a/sophiread/SophireadCLI/tests/test_sophiread_core.cpp b/sophiread/SophireadCLI/tests/test_sophiread_core.cpp index 7ebfeeb..cd634cf 100644 --- a/sophiread/SophireadCLI/tests/test_sophiread_core.cpp +++ b/sophiread/SophireadCLI/tests/test_sophiread_core.cpp @@ -3,7 +3,7 @@ * @author: Chen Zhang (zhangc@orn.gov) * @brief: Unit tests for the Sophiread Core module. * @date: 2024-08-21 - * + * * @copyright Copyright (c) 2024 * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -19,134 +19,133 @@ * along with this program. If not, see . */ #include - #include #include - -#include "json_config_parser.h" #include "sophiread_core.h" +#include "json_config_parser.h" class SophireadCoreTest : public ::testing::Test { - protected: - std::vector generateMockTPX3Data(int num_packets = 10) { - std::vector data; - - for (int i = 0; i < num_packets; ++i) { - // Header packet - data.push_back('T'); - data.push_back('P'); - data.push_back('X'); - data.push_back('3'); - data.push_back(0); // chip_layout_type - data.push_back(0); // some random data - data.push_back(8); // data_packet_size (low byte) - data.push_back(0); // data_packet_size (high byte) - - // Data packet (8 bytes) - for (int j = 0; j < 8; ++j) { - data.push_back(0); - } +protected: + std::vector generateMockTPX3Data(int num_packets = 10) { + std::vector data; + + for (int i = 0; i < num_packets; ++i) { + // Header packet + data.push_back('T'); + data.push_back('P'); + data.push_back('X'); + data.push_back('3'); + data.push_back(0); // chip_layout_type + data.push_back(0); // some random data + data.push_back(8); // data_packet_size (low byte) + data.push_back(0); // data_packet_size (high byte) + + // Data packet (8 bytes) + for (int j = 0; j < 8; ++j) { + data.push_back(0); + } + } + return data; } - return data; - } - - std::vector generateMockTPX3Batches(int num_batches = 2, int hits_per_batch = 5) { - std::vector batches; - for (int i = 0; i < num_batches; ++i) { - TPX3 batch(i * 100, hits_per_batch, i % 3); // index, num_packets, chip_layout_type - - // Add mock hits - for (int j = 0; j < hits_per_batch; ++j) { - char mock_packet[8] = {0}; // Mock packet data - batch.emplace_back(mock_packet, 1000 + j, 2000 + j); - } - // Add mock neutrons (derived from hits) - for (const auto& hit : batch.hits) { - batch.neutrons.emplace_back(hit.getX(), hit.getY(), hit.getTOF(), hit.getTOT(), - 1 // nHits, assume 1 hit per neutron for simplicity - ); - } - - batches.push_back(std::move(batch)); + std::vector generateMockTPX3Batches(int num_batches = 2, int hits_per_batch = 5) { + std::vector batches; + for (int i = 0; i < num_batches; ++i) { + TPX3 batch(i * 100, hits_per_batch, i % 3); // index, num_packets, chip_layout_type + + // Add mock hits + for (int j = 0; j < hits_per_batch; ++j) { + char mock_packet[8] = {0}; // Mock packet data + batch.emplace_back(mock_packet, 1000 + j, 2000 + j); + } + + // Add mock neutrons (derived from hits) + for (const auto& hit : batch.hits) { + batch.neutrons.emplace_back( + hit.getX(), hit.getY(), + hit.getTOF(), hit.getTOT(), + 1 // nHits, assume 1 hit per neutron for simplicity + ); + } + + batches.push_back(std::move(batch)); + } + return batches; } - return batches; - } - void SetUp() override { - // Create a small test TPX3 file - auto test_data = generateMockTPX3Data(100); - std::ofstream test_file("test.tpx3", std::ios::binary); - test_file.write(test_data.data(), test_data.size()); - test_file.close(); - } + void SetUp() override { + // Create a small test TPX3 file + auto test_data = generateMockTPX3Data(100); + std::ofstream test_file("test.tpx3", std::ios::binary); + test_file.write(test_data.data(), test_data.size()); + test_file.close(); + } - void TearDown() override { - // Remove the test file - std::filesystem::remove("test.tpx3"); - } + void TearDown() override { + // Remove the test file + std::filesystem::remove("test.tpx3"); + } }; TEST_F(SophireadCoreTest, TimedReadDataToCharVec) { - auto data = sophiread::timedReadDataToCharVec("test.tpx3"); - EXPECT_EQ(data.size(), 1600); // 100 * (8 + 8) bytes + auto data = sophiread::timedReadDataToCharVec("test.tpx3"); + EXPECT_EQ(data.size(), 1600); // 100 * (8 + 8) bytes } TEST_F(SophireadCoreTest, TimedFindTPX3H) { - auto rawdata = generateMockTPX3Data(100); - auto batches = sophiread::timedFindTPX3H(rawdata); - EXPECT_EQ(batches.size(), 100); + auto rawdata = generateMockTPX3Data(100); + auto batches = sophiread::timedFindTPX3H(rawdata); + EXPECT_EQ(batches.size(), 100); } TEST_F(SophireadCoreTest, TimedLocateTimeStamp) { - std::vector raw_data(8000, 'T'); // Simulating TPX3 data - std::vector batches = {TPX3(0, 10, 0)}; // Create a dummy TPX3 batch - sophiread::timedLocateTimeStamp(batches, raw_data); - // Add assertions based on expected behavior + std::vector raw_data(8000, 'T'); // Simulating TPX3 data + std::vector batches = { TPX3(0, 10, 0) }; // Create a dummy TPX3 batch + sophiread::timedLocateTimeStamp(batches, raw_data); + // Add assertions based on expected behavior } TEST_F(SophireadCoreTest, TimedProcessing) { - std::vector raw_data(8000, 'T'); // Simulating TPX3 data - std::vector batches = {TPX3(0, 10, 0)}; // Create a dummy TPX3 batch - JSONConfigParser config = JSONConfigParser::createDefault(); - sophiread::timedProcessing(batches, raw_data, config); - // Add assertions based on expected behavior + std::vector raw_data(8000, 'T'); // Simulating TPX3 data + std::vector batches = { TPX3(0, 10, 0) }; // Create a dummy TPX3 batch + JSONConfigParser config = JSONConfigParser::createDefault(); + sophiread::timedProcessing(batches, raw_data, config); + // Add assertions based on expected behavior } TEST_F(SophireadCoreTest, TimedSaveHitsToHDF5) { - std::vector batches = generateMockTPX3Batches(2, 5); // Create a dummy TPX3 batch - sophiread::timedSaveHitsToHDF5("test_hits.h5", batches); - EXPECT_TRUE(std::filesystem::exists("test_hits.h5")); - std::filesystem::remove("test_hits.h5"); + std::vector batches = generateMockTPX3Batches(2, 5); // Create a dummy TPX3 batch + sophiread::timedSaveHitsToHDF5("test_hits.h5", batches); + EXPECT_TRUE(std::filesystem::exists("test_hits.h5")); + std::filesystem::remove("test_hits.h5"); } TEST_F(SophireadCoreTest, TimedSaveEventsToHDF5) { - std::vector batches = generateMockTPX3Batches(2, 5); // Create a dummy TPX3 batch - sophiread::timedSaveEventsToHDF5("test_events.h5", batches); - EXPECT_TRUE(std::filesystem::exists("test_events.h5")); - std::filesystem::remove("test_events.h5"); + std::vector batches = generateMockTPX3Batches(2, 5); // Create a dummy TPX3 batch + sophiread::timedSaveEventsToHDF5("test_events.h5", batches); + EXPECT_TRUE(std::filesystem::exists("test_events.h5")); + std::filesystem::remove("test_events.h5"); } TEST_F(SophireadCoreTest, TimedCreateTOFImages) { - std::vector batches = generateMockTPX3Batches(2, 5); // Create a dummy TPX3 batch - std::vector tof_bin_edges = {0.0, 0.1, 0.2, 0.3}; - auto images = sophiread::timedCreateTOFImages(batches, 1.0, tof_bin_edges, "neutron"); - EXPECT_EQ(images.size(), 3); // 3 bins + std::vector batches = generateMockTPX3Batches(2, 5); // Create a dummy TPX3 batch + std::vector tof_bin_edges = {0.0, 0.1, 0.2, 0.3}; + auto images = sophiread::timedCreateTOFImages(batches, 1.0, tof_bin_edges, "neutron"); + EXPECT_EQ(images.size(), 3); // 3 bins } TEST_F(SophireadCoreTest, TimedSaveTOFImagingToTIFF) { - std::vector>> tof_images( - 3, std::vector>(10, std::vector(10, 1))); - std::vector tof_bin_edges = {0.0, 0.1, 0.2, 0.3}; - sophiread::timedSaveTOFImagingToTIFF("test_tof", tof_images, tof_bin_edges, "test"); - EXPECT_TRUE(std::filesystem::exists("test_tof/test_bin_0001.tiff")); - EXPECT_TRUE(std::filesystem::exists("test_tof/test_bin_0002.tiff")); - EXPECT_TRUE(std::filesystem::exists("test_tof/test_bin_0003.tiff")); - EXPECT_TRUE(std::filesystem::exists("test_tof/test_Spectra.txt")); - std::filesystem::remove_all("test_tof"); + std::vector>> tof_images(3, std::vector>(10, std::vector(10, 1))); + std::vector tof_bin_edges = {0.0, 0.1, 0.2, 0.3}; + sophiread::timedSaveTOFImagingToTIFF("test_tof", tof_images, tof_bin_edges, "test"); + EXPECT_TRUE(std::filesystem::exists("test_tof/test_bin_0001.tiff")); + EXPECT_TRUE(std::filesystem::exists("test_tof/test_bin_0002.tiff")); + EXPECT_TRUE(std::filesystem::exists("test_tof/test_bin_0003.tiff")); + EXPECT_TRUE(std::filesystem::exists("test_tof/test_Spectra.txt")); + std::filesystem::remove_all("test_tof"); } -int main(int argc, char** argv) { - ::testing::InitGoogleTest(&argc, argv); - return RUN_ALL_TESTS(); +int main(int argc, char **argv) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); } \ No newline at end of file diff --git a/sophiread/SophireadCLI/tests/test_user_config.cpp b/sophiread/SophireadCLI/tests/test_user_config.cpp index afbe9cf..8c75d35 100644 --- a/sophiread/SophireadCLI/tests/test_user_config.cpp +++ b/sophiread/SophireadCLI/tests/test_user_config.cpp @@ -20,11 +20,9 @@ * along with this program. If not, see . */ #include - #include - -#include "tof_binning.h" #include "user_config.h" +#include "tof_binning.h" // Test default constructor TEST(UserConfigTest, DefaultConstructor) { @@ -32,11 +30,11 @@ TEST(UserConfigTest, DefaultConstructor) { EXPECT_DOUBLE_EQ(config.getABSRadius(), 5.0); EXPECT_EQ(config.getABSMinClusterSize(), 1); EXPECT_EQ(config.getABSSpiderTimeRange(), 75); - + auto tof_edges = config.getTOFBinEdges(); EXPECT_EQ(tof_edges.size(), 1501); // 1500 bins + 1 EXPECT_DOUBLE_EQ(tof_edges.front(), 0.0); - EXPECT_DOUBLE_EQ(tof_edges.back(), 1.0 / 60); + EXPECT_DOUBLE_EQ(tof_edges.back(), 1.0/60); } // Test parameterized constructor @@ -45,12 +43,12 @@ TEST(UserConfigTest, ParameterizedConstructor) { EXPECT_DOUBLE_EQ(config.getABSRadius(), 10.0); EXPECT_EQ(config.getABSMinClusterSize(), 5); EXPECT_EQ(config.getABSSpiderTimeRange(), 100); - + // TOF binning should still be default auto tof_edges = config.getTOFBinEdges(); EXPECT_EQ(tof_edges.size(), 1501); EXPECT_DOUBLE_EQ(tof_edges.front(), 0.0); - EXPECT_DOUBLE_EQ(tof_edges.back(), 1.0 / 60); + EXPECT_DOUBLE_EQ(tof_edges.back(), 1.0/60); } // Test setters @@ -59,7 +57,7 @@ TEST(UserConfigTest, Setters) { config.setABSRadius(15.0); config.setABSMinClusterSize(10); config.setABSSpiderTimeRange(150); - + EXPECT_DOUBLE_EQ(config.getABSRadius(), 15.0); EXPECT_EQ(config.getABSMinClusterSize(), 10); EXPECT_EQ(config.getABSSpiderTimeRange(), 150); @@ -72,7 +70,7 @@ TEST(UserConfigTest, TOFBinningSetter) { custom_binning.num_bins = 1000; custom_binning.tof_max = 20000.0; config.setTOFBinning(custom_binning); - + auto tof_edges = config.getTOFBinEdges(); EXPECT_EQ(tof_edges.size(), 1001); // 1000 bins + 1 EXPECT_DOUBLE_EQ(tof_edges.front(), 0.0); @@ -84,7 +82,7 @@ TEST(UserConfigTest, CustomTOFBinEdges) { UserConfig config; std::vector custom_edges = {0.0, 100.0, 200.0, 300.0, 400.0}; config.setCustomTOFBinEdges(custom_edges); - + auto tof_edges = config.getTOFBinEdges(); EXPECT_EQ(tof_edges.size(), 5); EXPECT_DOUBLE_EQ(tof_edges[0], 0.0); @@ -126,7 +124,7 @@ TEST(UserConfigTest, ParseValidConfigurationFile) { auto tof_edges = config.getTOFBinEdges(); EXPECT_EQ(tof_edges.size(), 1501); EXPECT_DOUBLE_EQ(tof_edges.front(), 0.0); - EXPECT_DOUBLE_EQ(tof_edges.back(), 1.0 / 60); + EXPECT_DOUBLE_EQ(tof_edges.back(), 1.0/60); // Cleanup std::remove("testConfig.txt"); diff --git a/sophiread/SophireadGUI/src/mainwindow.cpp b/sophiread/SophireadGUI/src/mainwindow.cpp index ada4732..d6379ad 100644 --- a/sophiread/SophireadGUI/src/mainwindow.cpp +++ b/sophiread/SophireadGUI/src/mainwindow.cpp @@ -26,7 +26,8 @@ class ColorMap : public QwtLinearColorMap { } }; -MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { +MainWindow::MainWindow(QWidget *parent) + : QMainWindow(parent), ui(new Ui::MainWindow) { // Set up the UI ui->setupUi(this); @@ -66,7 +67,7 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWi unsigned long int min_cluster_size = 1; unsigned long int spider_time_range = 75; - clustering_alg = new ABS(radius, min_cluster_size, spider_time_range); + clustering_alg = new ABS(radius,min_cluster_size,spider_time_range); clustering_alg->set_method("centroid"); // clustering_alg->set_method("fast_gaussian"); @@ -113,8 +114,9 @@ void MainWindow::handlereadfile() { } // Get file name - QString filename = QFileDialog::getOpenFileName(this, "Timepix File to open", QDir::currentPath(), - "All files (*.*) ;; Timepix raw (*.tpx3)"); + QString filename = QFileDialog::getOpenFileName( + this, "Timepix File to open", QDir::currentPath(), + "All files (*.*) ;; Timepix raw (*.tpx3)"); // DEVNOTE: // The original QtConcurrent::run() function is not safe guarding content of @@ -207,8 +209,9 @@ void MainWindow::handlesavedata() { // Save 2D histogram double *swaphisto = NULL; - QString filename = QFileDialog::getSaveFileName(this, "Save 2D hist as binary", QDir::currentPath(), - "All files (*.*) ;; Pic Binary (*.dat)"); + QString filename = QFileDialog::getSaveFileName( + this, "Save 2D hist as binary", QDir::currentPath(), + "All files (*.*) ;; Pic Binary (*.dat)"); if (!filename.isNull()) { FILE *outfile; int ii; @@ -229,7 +232,8 @@ void MainWindow::handlesavedata() { } // Save neutron events to HDF5 archive - filename = QFileDialog::getSaveFileName(this, "Save events to HDF5", QDir::currentPath(), + filename = QFileDialog::getSaveFileName(this, "Save events to HDF5", + QDir::currentPath(), "All files (*.*) ;; HDF5 (*.hdf5)"); if (!filename.isNull()) { saveEventsToHDF5(filename.toStdString(), m_events); diff --git a/sophiread/SophireadLib/benchmarks/benchmark_abs.cpp b/sophiread/SophireadLib/benchmarks/benchmark_abs.cpp index bee91b1..e7a0385 100644 --- a/sophiread/SophireadLib/benchmarks/benchmark_abs.cpp +++ b/sophiread/SophireadLib/benchmarks/benchmark_abs.cpp @@ -4,9 +4,9 @@ * @brief Benchmark the performance of abs clustering method * @version 0.1 * @date 2023-08-25 - * + * * @copyright Copyright (c) 2023 - * + * */ #include #include @@ -49,7 +49,8 @@ std::vector fake_hits() { return hits; } -double run_single_test(std::vector hits, double &fit_time, double &events_time) { +double run_single_test(std::vector hits, double &fit_time, + double &events_time) { // create ABS algorithm ABS abs_alg(5.0, 1, 75); @@ -57,7 +58,8 @@ double run_single_test(std::vector hits, double &fit_time, double &events_t auto start_fit = chrono::high_resolution_clock::now(); abs_alg.fit(hits); auto end_fit = chrono::high_resolution_clock::now(); - auto duration_fit = chrono::duration_cast(end_fit - start_fit).count(); + auto duration_fit = + chrono::duration_cast(end_fit - start_fit).count(); cout << "abs::fit " << duration_fit << " us" << endl; // convert to neutron events @@ -65,7 +67,9 @@ double run_single_test(std::vector hits, double &fit_time, double &events_t abs_alg.set_method("centroid"); auto events = abs_alg.get_events(hits); auto end_events = chrono::high_resolution_clock::now(); - auto duration_events = chrono::duration_cast(end_events - start_events).count(); + auto duration_events = + chrono::duration_cast(end_events - start_events) + .count(); cout << "abs::get_events " << duration_events << " us" << endl; fit_time += duration_fit; diff --git a/sophiread/SophireadLib/benchmarks/benchmark_abs_thread.cpp b/sophiread/SophireadLib/benchmarks/benchmark_abs_thread.cpp index 71420dd..60166d1 100644 --- a/sophiread/SophireadLib/benchmarks/benchmark_abs_thread.cpp +++ b/sophiread/SophireadLib/benchmarks/benchmark_abs_thread.cpp @@ -48,7 +48,8 @@ std::vector fake_hits() { int stime = 10 * i + spidertime(gen); // cluster for (int j = 0; j < num_hits_per_cluster; j++) { - hits.emplace_back(Hit(x, y, tot(gen), toa(gen), ftoa(gen), tof(gen), stime)); + hits.emplace_back( + Hit(x, y, tot(gen), toa(gen), ftoa(gen), tof(gen), stime)); } } return hits; @@ -81,7 +82,9 @@ double single_test(const std::vector& hits, int num_thread) { // start threads for (int i = 0; i < num_thread; ++i) { thread_data_list[i].begin = hits.begin() + i * chunk_size; - thread_data_list[i].end = (i == num_thread - 1) ? hits.end() : hits.begin() + (i + 1) * chunk_size; + thread_data_list[i].end = (i == num_thread - 1) + ? hits.end() + : hits.begin() + (i + 1) * chunk_size; threads[i] = std::thread(&thread_data::run, std::ref(thread_data_list[i])); } @@ -93,7 +96,10 @@ double single_test(const std::vector& hits, int num_thread) { // record time auto end = std::chrono::high_resolution_clock::now(); - auto duration = std::chrono::duration_cast(end - start).count() / 1e6; + auto duration = + std::chrono::duration_cast(end - start) + .count() / + 1e6; cout << "[user]total " << duration << " sec" << endl; return duration; diff --git a/sophiread/SophireadLib/include/abs.h b/sophiread/SophireadLib/include/abs.h index 099f5e3..23ad21e 100644 --- a/sophiread/SophireadLib/include/abs.h +++ b/sophiread/SophireadLib/include/abs.h @@ -19,8 +19,9 @@ struct Cluster { */ class ABS : public ClusteringAlgorithm { public: - ABS(double r, unsigned long int min_cluster_size, unsigned long int spider_time_range) - : m_feature(r), m_min_cluster_size(min_cluster_size), spiderTimeRange_(spider_time_range) {}; + ABS(double r, unsigned long int min_cluster_size, + unsigned long int spider_time_range) : + m_feature(r), m_min_cluster_size(min_cluster_size), spiderTimeRange_(spider_time_range) {}; void fit(const std::vector& data); void set_method(std::string method) { m_method = method; } void reset() { clusterLabels_.clear(); } @@ -29,13 +30,13 @@ class ABS : public ClusteringAlgorithm { ~ABS() = default; private: - double m_feature; // feather range - std::string m_method{"centroid"}; // method for centroid - std::vector clusterLabels_; // The cluster labels for each hit + double m_feature; // feather range + std::string m_method{"centroid"}; // method for centroid + std::vector clusterLabels_; // The cluster labels for each hit std::vector> clusterIndices_; // The cluster indices for // each cluster - const int numClusters_ = 4; // The number of clusters use in runtime - unsigned long int m_min_cluster_size = 1; // The maximum cluster size - unsigned long int spiderTimeRange_ = 75; // The spider time range (in ns) - PeakFittingAlgorithm* peakFittingAlgorithm_; // The clustering algorithm + const int numClusters_ = 4; // The number of clusters use in runtime + unsigned long int m_min_cluster_size = 1; // The maximum cluster size + unsigned long int spiderTimeRange_ = 75; // The spider time range (in ns) + PeakFittingAlgorithm* peakFittingAlgorithm_; // The clustering algorithm }; diff --git a/sophiread/SophireadLib/include/centroid.h b/sophiread/SophireadLib/include/centroid.h index 6559469..3c80f92 100644 --- a/sophiread/SophireadLib/include/centroid.h +++ b/sophiread/SophireadLib/include/centroid.h @@ -14,7 +14,7 @@ */ class Centroid : public PeakFittingAlgorithm { public: - Centroid(bool weighted_by_tot = true) : weighted_by_tot(weighted_by_tot) {}; + Centroid(bool weighted_by_tot = true) : weighted_by_tot(weighted_by_tot){}; // Pure virtual function for predicting the peak positions and parameters // predict -> (x, y, tof) diff --git a/sophiread/SophireadLib/include/clustering.h b/sophiread/SophireadLib/include/clustering.h index daf5924..39b36e4 100644 --- a/sophiread/SophireadLib/include/clustering.h +++ b/sophiread/SophireadLib/include/clustering.h @@ -23,7 +23,8 @@ class ClusteringAlgorithm { virtual void fit(const std::vector& hits) = 0; // generate neutron events with given hits and fitted cluster IDs - virtual std::vector get_events(const std::vector& hits) = 0; + virtual std::vector get_events( + const std::vector& hits) = 0; virtual ~ClusteringAlgorithm() {} }; diff --git a/sophiread/SophireadLib/include/dbscan.h b/sophiread/SophireadLib/include/dbscan.h index 0fc46be..1ce8d70 100644 --- a/sophiread/SophireadLib/include/dbscan.h +++ b/sophiread/SophireadLib/include/dbscan.h @@ -4,8 +4,12 @@ class DBSCAN : public ClusteringAlgorithm { public: - DBSCAN(double eps_time, size_t min_points_time, double eps_xy, size_t min_points_xy) - : m_eps_time(eps_time), m_min_points_time(min_points_time), m_eps_xy(eps_xy), m_min_points_xy(min_points_xy) {}; + DBSCAN(double eps_time, size_t min_points_time, double eps_xy, + size_t min_points_xy) + : m_eps_time(eps_time), + m_min_points_time(min_points_time), + m_eps_xy(eps_xy), + m_min_points_xy(min_points_xy){}; ~DBSCAN() = default; public: @@ -31,18 +35,20 @@ class DBSCAN : public ClusteringAlgorithm { double m_time_max; std::vector m_time_cluster_xy_indexes; // wrt input hits vector }; - void fit1D(std::vector &data, size_t &number_of_clusters, std::vector &labels, - std::vector ¢roids); - void fit2D(std::vector> &data, size_t &number_of_clusters, std::vector &labels, + void fit1D(std::vector &data, size_t &number_of_clusters, + std::vector &labels, std::vector ¢roids); + void fit2D(std::vector> &data, + size_t &number_of_clusters, std::vector &labels, std::vector> ¢roids); - void mergeTimeClusters1D(std::vector &input_infos, std::vector &merged_infos); + void mergeTimeClusters1D(std::vector &input_infos, + std::vector &merged_infos); private: std::string m_method{"centroid"}; // method for centroid - double m_eps_time; // The maximum distance between two time points - size_t m_min_points_time; // The minimum number of points in a time cluster - double m_eps_xy; // The maximum distance between two XY points - size_t m_min_points_xy; // The minimum number of points in an XY cluster + double m_eps_time; // The maximum distance between two time points + size_t m_min_points_time; // The minimum number of points in a time cluster + double m_eps_xy; // The maximum distance between two XY points + size_t m_min_points_xy; // The minimum number of points in an XY cluster std::vector m_events; const size_t m_max_hit_chunk_size = 2e6; std::vector clusterLabels_; // The cluster labels for each hit diff --git a/sophiread/SophireadLib/include/fastgaussian.h b/sophiread/SophireadLib/include/fastgaussian.h index 460ea11..37aa1c2 100644 --- a/sophiread/SophireadLib/include/fastgaussian.h +++ b/sophiread/SophireadLib/include/fastgaussian.h @@ -6,7 +6,7 @@ */ class FastGaussian : public PeakFittingAlgorithm { public: - FastGaussian() {}; + FastGaussian(){}; // Pure virtual function for predicting the peak positions and parameters // predict -> (x, y, tof) diff --git a/sophiread/SophireadLib/include/tpx3.h b/sophiread/SophireadLib/include/tpx3.h index 128fb46..ff3bcb5 100644 --- a/sophiread/SophireadLib/include/tpx3.h +++ b/sophiread/SophireadLib/include/tpx3.h @@ -18,7 +18,7 @@ class Hit { public: // default constructor - Hit() : m_x(0), m_y(0), m_tot(0), m_toa(0), m_ftoa(0), m_tof(0), m_spidertime(0) {}; + Hit() : m_x(0), m_y(0), m_tot(0), m_toa(0), m_ftoa(0), m_tof(0), m_spidertime(0){}; // copy constructor Hit(const Hit& hit) : m_x(hit.m_x), @@ -27,10 +27,17 @@ class Hit { m_toa(hit.m_toa), m_ftoa(hit.m_ftoa), m_tof(hit.m_tof), - m_spidertime(hit.m_spidertime) {}; - - Hit(int x, int y, int tot, int toa, int ftoa, unsigned int tof, unsigned long long spidertime) - : m_x(x), m_y(y), m_tot(tot), m_toa(toa), m_ftoa(ftoa), m_tof(tof), m_spidertime(spidertime) {}; + m_spidertime(hit.m_spidertime){}; + + Hit(int x, int y, int tot, int toa, int ftoa, unsigned int tof, + unsigned long long spidertime) + : m_x(x), + m_y(y), + m_tot(tot), + m_toa(toa), + m_ftoa(ftoa), + m_tof(tof), + m_spidertime(spidertime){}; // special constructor that directly parse the raw packet from tpx3 // into a hit @@ -58,7 +65,9 @@ class Hit { double getTOF_ns() const { return m_tof * m_scale_to_ns_40mhz; }; double getTOA_ns() const { return m_toa * m_scale_to_ns_40mhz; }; double getTOT_ns() const { return m_tot * m_scale_to_ns_40mhz; }; - double getSPIDERTIME_ns() const { return m_spidertime * m_scale_to_ns_40mhz; }; + double getSPIDERTIME_ns() const { + return m_spidertime * m_scale_to_ns_40mhz; + }; double getFTOA_ns() const { return m_ftoa * m_scale_to_ns_640mhz; }; std::string toString() const; @@ -70,11 +79,14 @@ class Hit { int m_toa; // time of arrival (40MHz clock, 14 bit) int m_ftoa; // fine time of arrival (640MHz clock, 4 bit) unsigned int m_tof; - unsigned long long m_spidertime; // time from the spider board (in the unit of 25ns) + unsigned long long + m_spidertime; // time from the spider board (in the unit of 25ns) // scale factor that converts time to ns - const double m_scale_to_ns_40mhz = 25.0; // 40 MHz clock is used for the coarse time of arrival. - const double m_scale_to_ns_640mhz = 25.0 / 16.0; // 640 MHz clock is used for the fine time of arrival. + const double m_scale_to_ns_40mhz = + 25.0; // 40 MHz clock is used for the coarse time of arrival. + const double m_scale_to_ns_640mhz = + 25.0 / 16.0; // 640 MHz clock is used for the fine time of arrival. }; /** @@ -83,11 +95,12 @@ class Hit { */ class NeutronEvent { public: - NeutronEvent(const double x, const double y, const double tof, const double tot, const int nHits) - : m_x(x), m_y(y), m_tof(tof), m_tot(tot), m_nHits(nHits) {}; + NeutronEvent(const double x, const double y, const double tof, + const double tot, const int nHits) + : m_x(x), m_y(y), m_tof(tof), m_tot(tot), m_nHits(nHits){}; double getX() const { return m_x; }; double getY() const { return m_y; }; - double getTOT() const { return m_tot; } + double getTOT() const { return m_tot;} double getTOF() const { return m_tof; }; double getTOF_ns() const { return m_tof * m_scale_to_ns_40mhz; }; int getNHits() const { return m_nHits; }; @@ -95,49 +108,59 @@ class NeutronEvent { std::string toString() const; private: - const double m_x, m_y; // pixel coordinates - const double m_tof; // time of flight - const double m_tot; // time-over-threshold - const int m_nHits; // number of hits in the event (cluster size) - const double m_scale_to_ns_40mhz = 25.0; // 40 MHz clock is used for the coarse time of arrival. + const double m_x, m_y; // pixel coordinates + const double m_tof; // time of flight + const double m_tot; // time-over-threshold + const int m_nHits; // number of hits in the event (cluster size) + const double m_scale_to_ns_40mhz = + 25.0; // 40 MHz clock is used for the coarse time of arrival. }; /** - * @brief Class to store user-defined parameters for clustering algorithms + * @brief Class to store user-defined parameters for clustering algorithms * */ class Params { - public: - Params(const double abs_radius, unsigned long int abs_min_cluster_size, unsigned long int abs_spider_time_range) - : m_abs_radius(abs_radius), - m_abs_min_cluster_size(abs_min_cluster_size), - m_abs_spider_time_range(abs_spider_time_range) {}; - - double getABSRadius() const { return m_abs_radius; }; - unsigned long int getABSMinClusterSize() const { return m_abs_min_cluster_size; }; - unsigned long int getABSSpidertimeRange() const { return m_abs_spider_time_range; }; +public: + Params(const double abs_radius, + unsigned long int abs_min_cluster_size, + unsigned long int abs_spider_time_range) : + m_abs_radius(abs_radius), + m_abs_min_cluster_size(abs_min_cluster_size), + m_abs_spider_time_range(abs_spider_time_range){}; + + double getABSRadius() const {return m_abs_radius;}; + unsigned long int getABSMinClusterSize() + const {return m_abs_min_cluster_size;}; + unsigned long int getABSSpidertimeRange() + const {return m_abs_spider_time_range;}; std::string toString() const; - private: +private: // ABS members (see abs.h for details) - double m_abs_radius; + double m_abs_radius; unsigned long int m_abs_min_cluster_size; unsigned long int m_abs_spider_time_range; }; // static file processing std::vector readTimepix3RawData(const std::string& filepath); -Hit packetToHit(const std::vector& packet, const unsigned long long tdc, const unsigned long long gdc, - const int chip_layout_type); -Hit packetToHitAlt(const std::vector& packet, unsigned long long* rollover_counter, - unsigned long long* previous_time, const int chip_layout_type); +Hit packetToHit(const std::vector& packet, const unsigned long long tdc, + const unsigned long long gdc, const int chip_layout_type); +Hit packetToHitAlt(const std::vector& packet, + unsigned long long* rollover_counter, + unsigned long long* previous_time, + const int chip_layout_type); // in memory processing std::vector parseRawBytesToHits(const std::vector& raw_bytes); // -void saveHitsToHDF5(const std::string out_file_name, const std::vector& hits, const std::vector& labels); -void saveEventsToHDF5(const std::string out_file_name, const std::vector& events); +void saveHitsToHDF5(const std::string out_file_name, + const std::vector& hits, + const std::vector& labels); +void saveEventsToHDF5(const std::string out_file_name, + const std::vector& events); -// parse user-defined param file +// parse user-defined param file Params parseUserDefinedParams(const std::string& filepath); // for fast processing raw bytes into hit @@ -148,7 +171,7 @@ struct TPX3H { const int chip_layout_type; TPX3H(std::size_t index, int packet_size, int num_packets, int chip_layout_type) - : index(index), packet_size(packet_size), num_packets(num_packets), chip_layout_type(chip_layout_type) {}; + : index(index), packet_size(packet_size), num_packets(num_packets), chip_layout_type(chip_layout_type){}; }; std::vector fastParseTPX3Raw(const std::vector& raw_bytes); std::vector processBatch(TPX3H batch, const std::vector& raw_bytes); \ No newline at end of file diff --git a/sophiread/SophireadLib/src/abs.cpp b/sophiread/SophireadLib/src/abs.cpp index b9525c3..b3fd815 100644 --- a/sophiread/SophireadLib/src/abs.cpp +++ b/sophiread/SophireadLib/src/abs.cpp @@ -8,6 +8,7 @@ #include "centroid.h" #include "fastgaussian.h" + /** * @brief Generate cluster labels for the hits. * @@ -52,9 +53,12 @@ void ABS::fit(const std::vector& data) { // case_1: cluster is not full, check if hit is within the feather range // of the cluster, if yes, add hit to cluster and update cluster bounds, // if not, id as noise - else if (std::abs(hit.getSPIDERTIME_ns() - cluster.spidertime) <= spiderTimeRange_) { - if (hit.getX() >= cluster.x_min - m_feature && hit.getX() <= cluster.x_max + m_feature && - hit.getY() >= cluster.y_min - m_feature && hit.getY() <= cluster.y_max + m_feature) { + else if (std::abs(hit.getSPIDERTIME_ns() - cluster.spidertime) <= + spiderTimeRange_) { + if (hit.getX() >= cluster.x_min - m_feature && + hit.getX() <= cluster.x_max + m_feature && + hit.getY() >= cluster.y_min - m_feature && + hit.getY() <= cluster.y_max + m_feature) { cluster.size++; cluster.x_min = std::min(cluster.x_min, hit.getX()); cluster.x_max = std::max(cluster.x_max, hit.getX()); @@ -123,7 +127,8 @@ std::vector ABS::get_events(const std::vector& data) { std::vector events; // find the highest label - auto max_label_it = std::max_element(clusterLabels_.begin(), clusterLabels_.end()); + auto max_label_it = + std::max_element(clusterLabels_.begin(), clusterLabels_.end()); int max_label = *max_label_it; // determine fitting algorithm diff --git a/sophiread/SophireadLib/src/centroid.cpp b/sophiread/SophireadLib/src/centroid.cpp index ee624e7..f46dde9 100644 --- a/sophiread/SophireadLib/src/centroid.cpp +++ b/sophiread/SophireadLib/src/centroid.cpp @@ -48,6 +48,6 @@ NeutronEvent Centroid::fit(const std::vector& data) { } tof /= data.size(); - + return NeutronEvent(x, y, tof, tot, data.size()); } diff --git a/sophiread/SophireadLib/src/dbscan.cpp b/sophiread/SophireadLib/src/dbscan.cpp index 8b7ad61..b710bcb 100644 --- a/sophiread/SophireadLib/src/dbscan.cpp +++ b/sophiread/SophireadLib/src/dbscan.cpp @@ -3,11 +3,16 @@ #include #include -DBSCAN::TimeClusterInfo::TimeClusterInfo() : m_time_mean(0.), m_time_sum(0.), m_time_min(DBL_MAX), m_time_max(DBL_MIN) { +DBSCAN::TimeClusterInfo::TimeClusterInfo() + : m_time_mean(0.), + m_time_sum(0.), + m_time_min(DBL_MAX), + m_time_max(DBL_MIN) { m_time_cluster_xy_indexes = std::vector(); } -DBSCAN::TimeClusterInfo::TimeClusterInfo(const double time, const size_t xy_index) +DBSCAN::TimeClusterInfo::TimeClusterInfo(const double time, + const size_t xy_index) : m_time_mean(time), m_time_sum(time), m_time_min(time), m_time_max(time) { m_time_cluster_xy_indexes = std::vector{xy_index}; } @@ -34,13 +39,16 @@ void DBSCAN::fit(const std::vector& hits) { if (max_number_of_hits == 0) return; if (m_verbose) { - std::cout << "Number of hits to process: " << max_number_of_hits << std::endl; - std::cout << "Maximum chunk size (hits): " << m_max_hit_chunk_size << std::endl; + std::cout << "Number of hits to process: " << max_number_of_hits + << std::endl; + std::cout << "Maximum chunk size (hits): " << m_max_hit_chunk_size + << std::endl; if (max_number_of_hits <= m_max_hit_chunk_size) std::cout << "Fitting time clusters (1D DBSCAN) on all hits" << std::endl; else - std::cout << "Fitting time clusters (1D DBSCAN) on chunks of hits..." << std::endl; + std::cout << "Fitting time clusters (1D DBSCAN) on chunks of hits..." + << std::endl; } size_t chunk_size{0}; // either max_chunk_size or the number of unprocessed @@ -51,11 +59,13 @@ void DBSCAN::fit(const std::vector& hits) { while (true) { // make a chunk - chunk_size = std::min(m_max_hit_chunk_size, max_number_of_hits - hit_offset); + chunk_size = + std::min(m_max_hit_chunk_size, max_number_of_hits - hit_offset); std::vector chunk; // NOTE: we are using TOA ns here, NOT clock cycle. - std::transform(hits.begin() + hit_offset, hits.begin() + hit_offset + chunk_size, std::back_inserter(chunk), - [](Hit const& h) { return h.getTOA_ns(); }); + std::transform( + hits.begin() + hit_offset, hits.begin() + hit_offset + chunk_size, + std::back_inserter(chunk), [](Hit const& h) { return h.getTOA_ns(); }); // run 1D time clustering on the chunk std::vector labels; @@ -65,16 +75,19 @@ void DBSCAN::fit(const std::vector& hits) { std::vector time_cluster_infos; time_cluster_infos.resize(number_of_clusters); - std::vector non_cluster_infos; // these unassigned data points may still change - // their status later, during merging of chunks + std::vector + non_cluster_infos; // these unassigned data points may still change + // their status later, during merging of chunks // set the time mean of each new info from the centroids vector - for (size_t jj = 0; jj < number_of_clusters; jj++) time_cluster_infos[jj].m_time_mean = centroids_1D[jj]; + for (size_t jj = 0; jj < number_of_clusters; jj++) + time_cluster_infos[jj].m_time_mean = centroids_1D[jj]; for (size_t ii = 0; ii < labels.size(); ii++) { size_t label = labels[ii]; if (label == SIZE_MAX || label > number_of_clusters - 1) { - non_cluster_infos.emplace_back(TimeClusterInfo(chunk[ii], ii + hit_offset)); + non_cluster_infos.emplace_back( + TimeClusterInfo(chunk[ii], ii + hit_offset)); continue; } TimeClusterInfo& info = time_cluster_infos[label]; @@ -85,10 +98,14 @@ void DBSCAN::fit(const std::vector& hits) { } // append non-cluster infos considering them as clusters with a single data // point each - time_cluster_infos.insert(time_cluster_infos.end(), non_cluster_infos.begin(), non_cluster_infos.end()); + time_cluster_infos.insert(time_cluster_infos.end(), + non_cluster_infos.begin(), + non_cluster_infos.end()); // append the new infos to the total infos - all_time_cluster_infos.insert(all_time_cluster_infos.end(), time_cluster_infos.begin(), time_cluster_infos.end()); + all_time_cluster_infos.insert(all_time_cluster_infos.end(), + time_cluster_infos.begin(), + time_cluster_infos.end()); hit_offset += chunk_size; if (max_number_of_hits - hit_offset == 0) break; // processed all hits @@ -105,9 +122,12 @@ void DBSCAN::fit(const std::vector& hits) { assert(m_events.empty()); // must run reset() before fitting if (m_verbose) { - std::cout << "Number of time clusters: " << merged_time_cluster_infos.size() << std::endl; - std::cout << "Fitting XY clusters (2D DBSCAN) on every time cluster..." << std::endl; - std::cout << "Eps: " << m_eps_xy << "; min_points: " << m_min_points_xy << std::endl; + std::cout << "Number of time clusters: " << merged_time_cluster_infos.size() + << std::endl; + std::cout << "Fitting XY clusters (2D DBSCAN) on every time cluster..." + << std::endl; + std::cout << "Eps: " << m_eps_xy << "; min_points: " << m_min_points_xy + << std::endl; } size_t label_offset{0}; @@ -116,7 +136,8 @@ void DBSCAN::fit(const std::vector& hits) { std::vector> xy_points; for (auto& index : info.m_time_cluster_xy_indexes) - xy_points.push_back(std::pair(hits[index].getX(), hits[index].getY())); + xy_points.push_back( + std::pair(hits[index].getX(), hits[index].getY())); std::vector labels; std::vector> centroids_2D; @@ -126,12 +147,15 @@ void DBSCAN::fit(const std::vector& hits) { // set cluster labels to all hits contained in the current time cluster for (size_t ii = 0; ii < labels.size(); ii++) { size_t label = labels[ii]; - int cluster_label = (label == SIZE_MAX || label > number_of_clusters - 1) ? -1 : label + label_offset; + int cluster_label = (label == SIZE_MAX || label > number_of_clusters - 1) + ? -1 + : label + label_offset; clusterLabels_[info.m_time_cluster_xy_indexes[ii]] = cluster_label; } label_offset += number_of_clusters; - std::map label_counts; // label vs. number of xy points with that label + std::map + label_counts; // label vs. number of xy points with that label for (size_t ii = 0; ii < labels.size(); ii++) { size_t label = labels[ii]; if (label == SIZE_MAX || label > number_of_clusters - 1) continue; @@ -143,9 +167,10 @@ void DBSCAN::fit(const std::vector& hits) { // note: currently the tot for each neutron event is set to 0 // as there is no simple solution to incorporate tot info using DBSCAN for (auto const& label_count : label_counts) - m_events.emplace_back(NeutronEvent(centroids_2D[label_count.first].first * DSCALE /*X*/, - centroids_2D[label_count.first].second * DSCALE /*Y*/, info.m_time_mean, 0, - label_count.second)); + m_events.emplace_back( + NeutronEvent(centroids_2D[label_count.first].first * DSCALE /*X*/, + centroids_2D[label_count.first].second * DSCALE /*Y*/, + info.m_time_mean,0, label_count.second)); } } @@ -163,7 +188,9 @@ void DBSCAN::mergeTimeClusters1D(std::vector& input_infos, // before merging, sort the infos by the min time std::sort(input_infos.begin(), input_infos.end(), - [](const TimeClusterInfo& a, const TimeClusterInfo& b) { return a.m_time_min < b.m_time_min; }); + [](const TimeClusterInfo& a, const TimeClusterInfo& b) { + return a.m_time_min < b.m_time_min; + }); merged_infos.clear(); std::vector::const_iterator it = input_infos.begin(); @@ -172,16 +199,19 @@ void DBSCAN::mergeTimeClusters1D(std::vector& input_infos, while (it != input_infos.end()) { next = *(it); if (current.m_time_max > next.m_time_min || - next.m_time_min - current.m_time_max <= m_eps_time) { // checking if the clusters should be merged + next.m_time_min - current.m_time_max <= + m_eps_time) { // checking if the clusters should be merged current.m_time_max = std::max(current.m_time_max, next.m_time_max); current.m_time_min = std::min(current.m_time_min, next.m_time_min); - current.m_time_cluster_xy_indexes.insert(current.m_time_cluster_xy_indexes.end(), - next.m_time_cluster_xy_indexes.begin(), - next.m_time_cluster_xy_indexes.end()); + current.m_time_cluster_xy_indexes.insert( + current.m_time_cluster_xy_indexes.end(), + next.m_time_cluster_xy_indexes.begin(), + next.m_time_cluster_xy_indexes.end()); current.m_time_sum += next.m_time_sum; } else { if (current.m_time_cluster_xy_indexes.size() >= m_min_points_time) { - current.m_time_mean = current.m_time_sum / current.m_time_cluster_xy_indexes.size(); + current.m_time_mean = + current.m_time_sum / current.m_time_cluster_xy_indexes.size(); merged_infos.push_back(current); } current = *(it); @@ -189,7 +219,8 @@ void DBSCAN::mergeTimeClusters1D(std::vector& input_infos, it++; } if (current.m_time_cluster_xy_indexes.size() >= m_min_points_time) { - current.m_time_mean = current.m_time_sum / current.m_time_cluster_xy_indexes.size(); + current.m_time_mean = + current.m_time_sum / current.m_time_cluster_xy_indexes.size(); merged_infos.push_back(current); } } @@ -202,14 +233,17 @@ void DBSCAN::mergeTimeClusters1D(std::vector& input_infos, * @param labels :: (output) cluster labels * @param centroids :: (output) cluster centroids */ -void DBSCAN::fit1D(std::vector& data, size_t& number_of_clusters, std::vector& labels, +void DBSCAN::fit1D(std::vector& data, size_t& number_of_clusters, + std::vector& labels, std::vector& centroids) { // create an arma matrix from the data vector - arma::mat data_mat(&data[0], 1 /*nrows*/, data.size() /*ncols*/, - false /*arma::mat will re-use the input data vector memory*/); + arma::mat data_mat( + &data[0], 1 /*nrows*/, data.size() /*ncols*/, + false /*arma::mat will re-use the input data vector memory*/); // create the dbscan object - mlpack::DBSCAN, mlpack::OrderedPointSelection> dbs(m_eps_time, m_min_points_time); + mlpack::DBSCAN, mlpack::OrderedPointSelection> dbs( + m_eps_time, m_min_points_time); // run mlpack clustering arma::Row labels_row; @@ -225,7 +259,8 @@ void DBSCAN::fit1D(std::vector& data, size_t& number_of_clusters, std::v labels_row.for_each([&labels](size_t& val) { labels.push_back(val); }); // fill in the centroids vector from the arma centroids matrix - centroids_mat.for_each([¢roids](arma::mat::elem_type& val) { centroids.push_back(val); }); + centroids_mat.for_each( + [¢roids](arma::mat::elem_type& val) { centroids.push_back(val); }); } /** @@ -236,14 +271,17 @@ void DBSCAN::fit1D(std::vector& data, size_t& number_of_clusters, std::v * @param labels :: (output) cluster labels * @param centroids :: (output) cluster centroids */ -void DBSCAN::fit2D(std::vector>& data, size_t& number_of_clusters, - std::vector& labels, std::vector>& centroids) { +void DBSCAN::fit2D(std::vector>& data, + size_t& number_of_clusters, std::vector& labels, + std::vector>& centroids) { // create an arma matrix from the data vector - arma::mat data_mat(&(data[0].first), 2 /*nrows*/, data.size() /*ncols*/, - false /*arma::mat will re-use the input data vector memory*/); + arma::mat data_mat( + &(data[0].first), 2 /*nrows*/, data.size() /*ncols*/, + false /*arma::mat will re-use the input data vector memory*/); // create the dbscan object - mlpack::DBSCAN, mlpack::OrderedPointSelection> dbs(m_eps_xy, m_min_points_xy); + mlpack::DBSCAN, mlpack::OrderedPointSelection> dbs( + m_eps_xy, m_min_points_xy); // run mlpack clustering arma::Row labels_row; @@ -254,8 +292,9 @@ void DBSCAN::fit2D(std::vector>& data, size_t& number_ labels_row.for_each([&labels](size_t& val) { labels.push_back(val); }); // fill in the centroids vector from the arma centroids matrix - centroids_mat.each_col( - [¢roids](const arma::vec& b) { centroids.push_back(std::pair(b[0], b[1])); }); + centroids_mat.each_col([¢roids](const arma::vec& b) { + centroids.push_back(std::pair(b[0], b[1])); + }); } /** @@ -268,7 +307,8 @@ std::vector DBSCAN::get_events(const std::vector& hits) { if (m_events.size() == 0) { fit(hits); } - if (m_verbose) std::cout << "Total number of events: " << m_events.size() << std::endl; + if (m_verbose) + std::cout << "Total number of events: " << m_events.size() << std::endl; return m_events; } diff --git a/sophiread/SophireadLib/src/fastgaussian.cpp b/sophiread/SophireadLib/src/fastgaussian.cpp index 959f2dc..9b62170 100644 --- a/sophiread/SophireadLib/src/fastgaussian.cpp +++ b/sophiread/SophireadLib/src/fastgaussian.cpp @@ -28,7 +28,9 @@ double getMedian(const std::vector& data) { std::vector sorted_data = data; std::sort(sorted_data.begin(), sorted_data.end()); if (sorted_data.size() % 2 == 0) { - return (sorted_data[sorted_data.size() / 2 - 1] + sorted_data[sorted_data.size() / 2]) / 2; + return (sorted_data[sorted_data.size() / 2 - 1] + + sorted_data[sorted_data.size() / 2]) / + 2; } else { return sorted_data[sorted_data.size() / 2]; } @@ -107,10 +109,12 @@ NeutronEvent FastGaussian::fit(const std::vector& data) { double y_event = x_sol(1) / 2.0; // calculate the tof as the average of the tof of the filtered hits - double tof_event = std::accumulate(tof_filtered.begin(), tof_filtered.end(), 0.0) / tof_filtered.size(); + double tof_event = + std::accumulate(tof_filtered.begin(), tof_filtered.end(), 0.0) / + tof_filtered.size(); // calculate the tot - double tot_event = std::accumulate(tot_filtered.begin(), tot_filtered.end(), 0.0); + double tot_event = std::accumulate(tot_filtered.begin(), tot_filtered.end(),0.0); // even if we are throwing away to bottom half, we still need to return the // pre-filtered number of hits diff --git a/sophiread/SophireadLib/src/tpx3.cpp b/sophiread/SophireadLib/src/tpx3.cpp index add665f..33baa96 100644 --- a/sophiread/SophireadLib/src/tpx3.cpp +++ b/sophiread/SophireadLib/src/tpx3.cpp @@ -16,20 +16,23 @@ std::string Hit::toString() const { std::stringstream ss; - ss << "Hit: x=" << m_x << ", y=" << m_y << ", tot=" << m_tot << ", toa=" << m_toa << ", ftoa=" << m_ftoa - << ", tof=" << m_tof << ", spidertime=" << m_spidertime; + ss << "Hit: x=" << m_x << ", y=" << m_y << ", tot=" << m_tot + << ", toa=" << m_toa << ", ftoa=" << m_ftoa << ", tof=" << m_tof + << ", spidertime=" << m_spidertime; return ss.str(); } std::string NeutronEvent::toString() const { std::stringstream ss; - ss << "NeutronEvent: x=" << m_x << ", y=" << m_y << ", tof=" << m_tof << ", nHits=" << m_nHits; + ss << "NeutronEvent: x=" << m_x << ", y=" << m_y << ", tof=" << m_tof + << ", nHits=" << m_nHits; return ss.str(); } std::string Params::toString() const { std::stringstream ss; - ss << "ABS: radius=" << m_abs_radius << ", min_cluster_size=" << m_abs_min_cluster_size + ss << "ABS: radius=" << m_abs_radius + << ", min_cluster_size=" << m_abs_min_cluster_size << ", spider_time_range=" << m_abs_spider_time_range; return ss.str(); @@ -118,8 +121,10 @@ Hit::Hit(const char *packet, const unsigned long long tdc, const unsigned long l * this function is used as a temporary solution with assumed timing, until * timing packet is fixed on the hardware side. */ -Hit packetToHitAlt(const std::vector &packet, unsigned long long *rollover_counter, - unsigned long long *previous_time, const int chip_layout_type) { +Hit packetToHitAlt(const std::vector &packet, + unsigned long long *rollover_counter, + unsigned long long *previous_time, + const int chip_layout_type) { unsigned short pixaddr, dcol, spix, pix; unsigned short *spider_time; unsigned short *nTOT; // bytes 2,3, raw time over threshold @@ -149,8 +154,8 @@ Hit packetToHitAlt(const std::vector &packet, unsigned long long *rollover *rollover_counter += 1; } - // if the curr hit arrives later than previous hit (in order) - // if it is a lot later, it belongs to the previous rollover + // if the curr hit arrives later than previous hit (in order) + // if it is a lot later, it belongs to the previous rollover } else { if (spidertime - *previous_time > time_range / 2) { if (*rollover_counter > 0) { @@ -167,7 +172,7 @@ Hit packetToHitAlt(const std::vector &packet, unsigned long long *rollover // a consistent round off error of 10ns due to using integer for modulus // which is way below the 100ns time resolution needed tof = SPDR_timestamp % 666667; - + // pixel address npixaddr = (unsigned int *)(&packet[4]); // Pixel address (14 bits) pixaddr = (*npixaddr >> 12) & 0xFFFF; @@ -203,15 +208,15 @@ Hit packetToHitAlt(const std::vector &packet, unsigned long long *rollover * @param chip_layout_type: chip layout ID number * @return Hit */ -Hit packetToHit(const std::vector &packet, const unsigned long long tdc, const unsigned long long gdc, - const int chip_layout_type) { +Hit packetToHit(const std::vector &packet, const unsigned long long tdc, + const unsigned long long gdc, const int chip_layout_type) { unsigned short pixaddr, dcol, spix, pix; unsigned short *spider_time; unsigned short *nTOT; // bytes 2,3, raw time over threshold unsigned int *nTOA; // bytes 3,4,5,6, raw time of arrival unsigned int *npixaddr; // bytes 4,5,6,7 int x, y, tot, toa, ftoa; - unsigned int spidertime = 0, tof = 0; + unsigned int spidertime=0, tof=0; // timing information spider_time = (unsigned short *)(&packet[0]); // Spider time (16 bits) nTOT = (unsigned short *)(&packet[2]); // ToT (10 bits) @@ -240,8 +245,8 @@ Hit packetToHit(const std::vector &packet, const unsigned long long tdc, c // tof calculation // TDC packets not always arrive before corresponding data packets - if (SPDR_timestamp < TDC_timestamp) { - tof = SPDR_timestamp - TDC_timestamp + 1E9 / 60.0; + if (SPDR_timestamp < TDC_timestamp){ + tof = SPDR_timestamp - TDC_timestamp + 1E9/60.0; } else { tof = SPDR_timestamp - TDC_timestamp; } @@ -290,7 +295,8 @@ std::vector readTimepix3RawData(const std::string &filepath) { throw std::runtime_error("Error opening file"); } // Read the data from the file into a buffer - std::vector buffer((std::istreambuf_iterator(file)), std::istreambuf_iterator()); + std::vector buffer((std::istreambuf_iterator(file)), + std::istreambuf_iterator()); file.close(); // Process the buffer to extract the raw data @@ -332,7 +338,8 @@ std::vector readTimepix3RawData(const std::string &filepath) { // and data_packet_num, therefore we are using the code from manufacture // example, tpx3cam.cpp to get the data_packet_size. data_packet_size = ((0xff & char_array[7]) << 8) | (0xff & char_array[6]); - data_packet_num = data_packet_size >> 3; // every 8 (2^3) bytes is a data packet + data_packet_num = + data_packet_size >> 3; // every 8 (2^3) bytes is a data packet // get chip layout type chip_layout_type = (int)char_array[4]; @@ -349,7 +356,8 @@ std::vector readTimepix3RawData(const std::string &filepath) { if (data_packet[7] == 0x6F) { // TDC data packets tdclast = (unsigned long *)(&data_packet[0]); - mytdc = (((*tdclast) >> 12) & 0xFFFFFFFF); // rick: 0x3fffffff, get 32-bit tdc + mytdc = (((*tdclast) >> 12) & + 0xFFFFFFFF); // rick: 0x3fffffff, get 32-bit tdc TDC_LSB32 = GDC_timestamp & 0xFFFFFFFF; TDC_MSB16 = (GDC_timestamp >> 32) & 0xFFFF; if (mytdc < TDC_LSB32) { @@ -376,7 +384,8 @@ std::vector readTimepix3RawData(const std::string &filepath) { } else if ((data_packet[7] & 0xF0) == 0xb0) { // NOTE: as of 2023-02-24, timing data packet cannot be used, using // alternative method to get the timing information - auto hit = packetToHitAlt(data_packet, rollover_counter, previous_time, chip_layout_type); + auto hit = packetToHitAlt(data_packet, rollover_counter, + previous_time, chip_layout_type); // std::cout << hit.toString() << std::endl; // Process the data into hit // auto hit = packetToHit(data_packet, TDC_timestamp, GDC_timestamp, @@ -719,40 +728,40 @@ void saveEventsToHDF5(const std::string out_file_name, const std::vector> name; - if (name == "abs_radius") { + /** + * @brief Parse user-defined parameters from a parameter file + * + * @param filepath: path to the parameter file. + * @return Params + */ + + Params parseUserDefinedParams(const std::string &filepath) { + // default ABS settings + double radius = 5.0; + unsigned long int min_cluster_size = 1; + unsigned long int spider_time_range = 75; + + std::ifstream user_defined_params_file(filepath); + std::string line; + + while (std::getline(user_defined_params_file, line)) { + std::istringstream ss(line); + std::string name; + ss >> name; + if (name == "abs_radius") { ss >> radius; - } else if (name == "abs_min_cluster_size") { + } else if (name == "abs_min_cluster_size") { ss >> min_cluster_size; - } else if (name == "spider_time_range") { + } else if (name == "spider_time_range") { ss >> spider_time_range; + } } - } - Params p(radius, min_cluster_size, spider_time_range); + Params p(radius, min_cluster_size, spider_time_range); - // prints out user-defined parameters - std::cout << "User-defined params file: " << filepath << std::endl; - std::cout << p.toString() << std::endl; + // prints out user-defined parameters + std::cout << "User-defined params file: " << filepath << std::endl; + std::cout << p.toString() << std::endl; - return p; -} + return p; + } diff --git a/sophiread/SophireadLib/tests/generate_fake_tpx3_data.cpp b/sophiread/SophireadLib/tests/generate_fake_tpx3_data.cpp index c1f4541..105da84 100644 --- a/sophiread/SophireadLib/tests/generate_fake_tpx3_data.cpp +++ b/sophiread/SophireadLib/tests/generate_fake_tpx3_data.cpp @@ -1,232 +1,248 @@ -#include #include +#include #include #include unsigned long long packDataHeader(unsigned long long t, unsigned long long p, unsigned long long x, - unsigned long long three, unsigned long long chip_nr, unsigned long long mode, - unsigned long long num_bytes) { - unsigned long long header; - header = ((num_bytes & 0x000000000000FFFF) << 48) | ((mode & 0x00000000000000FF) << 40) | - ((chip_nr & 0x00000000000000FF) << 32) | ((three & 0x00000000000000FF) << 24) | - ((x & 0x00000000000000FF) << 16) | ((p & 0x00000000000000FF) << 8) | (t & 0x00000000000000FF); - - // check - // unsigned short t1 = (unsigned short) header & 0xFF; - // unsigned short p1 = (unsigned short) (header >> 8) & 0xFF; - // unsigned short x1 = (unsigned short) (header >> 16) & 0xFF; - // unsigned short three1 = (unsigned short) (header >> 24) & 0xFF; - // unsigned short chip_nr1 = (unsigned short) (header >> 32) & 0xFF; - // unsigned short mode1 = (unsigned short) (header >> 40) & 0xFF; - // unsigned short num_bytes1 = (unsigned short) (header >> 48) & 0xFFFF; - - // std::cout << "T: " << t1 - // << ", P: " << p1 - // << ", X: " << x1 - // << ", three: " << three1 - // << ", chip_nr: " << chip_nr1 - // << ", mode: " << mode1 - // << ", num_bytes: " << num_bytes1; - - return header; + unsigned long long three, unsigned long long chip_nr, + unsigned long long mode, unsigned long long num_bytes){ + unsigned long long header; + header = ((num_bytes & 0x000000000000FFFF) << 48) | + ((mode & 0x00000000000000FF) << 40) | + ((chip_nr & 0x00000000000000FF) << 32) | + ((three & 0x00000000000000FF) << 24) | + ((x & 0x00000000000000FF) << 16) | + ((p & 0x00000000000000FF) << 8) | + (t & 0x00000000000000FF); + + // check + // unsigned short t1 = (unsigned short) header & 0xFF; + // unsigned short p1 = (unsigned short) (header >> 8) & 0xFF; + // unsigned short x1 = (unsigned short) (header >> 16) & 0xFF; + // unsigned short three1 = (unsigned short) (header >> 24) & 0xFF; + // unsigned short chip_nr1 = (unsigned short) (header >> 32) & 0xFF; + // unsigned short mode1 = (unsigned short) (header >> 40) & 0xFF; + // unsigned short num_bytes1 = (unsigned short) (header >> 48) & 0xFFFF; + + // std::cout << "T: " << t1 + // << ", P: " << p1 + // << ", X: " << x1 + // << ", three: " << three1 + // << ", chip_nr: " << chip_nr1 + // << ", mode: " << mode1 + // << ", num_bytes: " << num_bytes1; + + return header; } -unsigned long long packPixelHit(unsigned long long spider_time, unsigned long long ftoa, unsigned long long TOT, - unsigned long long TOA, unsigned long long pixaddr) { - unsigned long long temp; - unsigned long long header = 0xb; - - temp = ((header & 0x00000000000000FF) << 60) | ((pixaddr & 0x000000000000FFFF) << 44) | - ((TOA & 0x0000000000003FFF) << 30) | ((TOT & 0x00000000000003FF) << 20) | ((ftoa & 0x00000000000000FF) << 16) | - (spider_time & 0x000000000000FFFF); - - // check - // unsigned short spider_time1 = (unsigned short) temp & 0xFFFF; - // unsigned char ftoa1 = (unsigned char) (temp >> 16) & 0xFF; - // unsigned short TOT1 = (unsigned short) (temp >> 20) & 0x300; - // unsigned short TOA1 = (unsigned short) (temp >> 30) & 0x3FFF; - // unsigned short pixaddr1 = (unsigned short) (temp >> 44) & 0xFFFF; - // unsigned char header1 = (unsigned char) (temp >> 60) & 0xFF; - - // std::cout << "spider_time: " << std::hex << spider_time1 - // << ", ftoa: " << std::hex << +ftoa1 - // << ", TOT: " << std::hex << TOT1 - // << ", TOA: " << std::hex << TOA1 - // << ", pixaddr: " << std::hex << pixaddr1 - // << ", header: " << std::hex << +header1 << std::endl; - - unsigned long long spidertime = (spider_time << 14) | TOA; - std::cout << "spidertime + toa: " << spidertime << " s\n"; - - return temp; +unsigned long long packPixelHit(unsigned long long spider_time, unsigned long long ftoa, + unsigned long long TOT, unsigned long long TOA, unsigned long long pixaddr){ + unsigned long long temp; + unsigned long long header = 0xb; + + temp = ((header & 0x00000000000000FF) << 60) | + ((pixaddr & 0x000000000000FFFF) << 44) | + ((TOA & 0x0000000000003FFF) << 30) | + ((TOT & 0x00000000000003FF) << 20) | + ((ftoa & 0x00000000000000FF) << 16) | + (spider_time & 0x000000000000FFFF); + + // check + // unsigned short spider_time1 = (unsigned short) temp & 0xFFFF; + // unsigned char ftoa1 = (unsigned char) (temp >> 16) & 0xFF; + // unsigned short TOT1 = (unsigned short) (temp >> 20) & 0x300; + // unsigned short TOA1 = (unsigned short) (temp >> 30) & 0x3FFF; + // unsigned short pixaddr1 = (unsigned short) (temp >> 44) & 0xFFFF; + // unsigned char header1 = (unsigned char) (temp >> 60) & 0xFF; + + // std::cout << "spider_time: " << std::hex << spider_time1 + // << ", ftoa: " << std::hex << +ftoa1 + // << ", TOT: " << std::hex << TOT1 + // << ", TOA: " << std::hex << TOA1 + // << ", pixaddr: " << std::hex << pixaddr1 + // << ", header: " << std::hex << +header1 << std::endl; + + unsigned long long spidertime = (spider_time << 14) | TOA; + std::cout << "spidertime + toa: " << spidertime << " s\n"; + + return temp; } -int main(int argc, char** argv) { - // create a .tpx3 file - std::ofstream write_file("rollover_test_data.tpx3", std::ios::out | std::ios::binary); - if (!write_file) { - std::cout << "Cannot open file!" << std::endl; - return 1; - } +int main(int argc, char** argv){ + + // create a .tpx3 file + std::ofstream write_file("rollover_test_data.tpx3", std::ios::out | std::ios::binary); + if (!write_file){ + std::cout << "Cannot open file!" << std::endl; + return 1; + } + + unsigned long long temp; + temp = packDataHeader('T','P','X','3',0,0,208); + write_file.write((char*) &temp, sizeof(unsigned long long)); + + + // disorder pixel hit datapackets + // std::cout << "Disorder: increment counter" << std::endl; + // increment counter + temp = packPixelHit(0xF000,0xFF,0x3FF,0x3FFF,0x9876); // 25.1662, 1006649343 + write_file.write((char*) &temp, sizeof(unsigned long long)); + + temp = packPixelHit(0xFF00,0xFF,0x3FF,0x3FFF,0x9876); // 26.7391, 1069563903 + write_file.write((char*) &temp, sizeof(unsigned long long)); + + temp = packPixelHit(0xFFFF,0xFF,0x3FF,0x3FFF,0x9876); // 26.8435, 1073741823 + write_file.write((char*) &temp, sizeof(unsigned long long)); + + /* --------------------------------------------------------------*/ + + temp = packPixelHit(0x000F,0xFF,0x3FF,0x3FFF,0x9876); // 0.00655357, 262143 + write_file.write((char*) &temp, sizeof(unsigned long long)); + + temp = packPixelHit(0x00FF,0xFF,0x3FF,0x3FFF,0x9876); // 0.104858, 4194303 + write_file.write((char*) &temp, sizeof(unsigned long long)); - unsigned long long temp; - temp = packDataHeader('T', 'P', 'X', '3', 0, 0, 208); - write_file.write((char*)&temp, sizeof(unsigned long long)); + temp = packPixelHit(0x0FFF,0xFF,0x3FF,0x3FFF,0x9876); // 1.67772, 67108863 + write_file.write((char*) &temp, sizeof(unsigned long long)); - // disorder pixel hit datapackets - // std::cout << "Disorder: increment counter" << std::endl; - // increment counter - temp = packPixelHit(0xF000, 0xFF, 0x3FF, 0x3FFF, 0x9876); // 25.1662, 1006649343 - write_file.write((char*)&temp, sizeof(unsigned long long)); + temp = packPixelHit(0x2FFF,0xFF,0x3FF,0x3FFF,0x9876); // 5.03316, 201326591 + write_file.write((char*) &temp, sizeof(unsigned long long)); - temp = packPixelHit(0xFF00, 0xFF, 0x3FF, 0x3FFF, 0x9876); // 26.7391, 1069563903 - write_file.write((char*)&temp, sizeof(unsigned long long)); - temp = packPixelHit(0xFFFF, 0xFF, 0x3FF, 0x3FFF, 0x9876); // 26.8435, 1073741823 - write_file.write((char*)&temp, sizeof(unsigned long long)); + // disorder pixel hit datapackets + // std::cout << "Disorder: no changes counter" << std::endl; + // no changes to counter + // temp = packDataHeader('T','P','X','3',0,0,48); + // write_file.write((char*) &temp, sizeof(unsigned long long)); - /* --------------------------------------------------------------*/ + temp = packPixelHit(0x000F,0xFF,0x3FF,0x3FFF,0x9876); // 0.00655357, 262143 + write_file.write((char*) &temp, sizeof(unsigned long long)); - temp = packPixelHit(0x000F, 0xFF, 0x3FF, 0x3FFF, 0x9876); // 0.00655357, 262143 - write_file.write((char*)&temp, sizeof(unsigned long long)); + temp = packPixelHit(0x3FFF,0xFF,0x3FF,0x3FFF,0x9876); // 6.71089, 268435455 + write_file.write((char*) &temp, sizeof(unsigned long long)); - temp = packPixelHit(0x00FF, 0xFF, 0x3FF, 0x3FFF, 0x9876); // 0.104858, 4194303 - write_file.write((char*)&temp, sizeof(unsigned long long)); + temp = packPixelHit(0x00FF,0xFF,0x3FF,0x3FFF,0x9876); // 0.104858, 4194303 + write_file.write((char*) &temp, sizeof(unsigned long long)); - temp = packPixelHit(0x0FFF, 0xFF, 0x3FF, 0x3FFF, 0x9876); // 1.67772, 67108863 - write_file.write((char*)&temp, sizeof(unsigned long long)); + temp = packPixelHit(0x0FFF,0xFF,0x3FF,0x3FFF,0x9876); // 1.67772, 67108863 + write_file.write((char*) &temp, sizeof(unsigned long long)); - temp = packPixelHit(0x2FFF, 0xFF, 0x3FF, 0x3FFF, 0x9876); // 5.03316, 201326591 - write_file.write((char*)&temp, sizeof(unsigned long long)); + temp = packPixelHit(0x1FFF,0xFF,0x3FF,0x3FFF,0x9876); // 3.35544, 134217727 + write_file.write((char*) &temp, sizeof(unsigned long long)); - // disorder pixel hit datapackets - // std::cout << "Disorder: no changes counter" << std::endl; - // no changes to counter - // temp = packDataHeader('T','P','X','3',0,0,48); - // write_file.write((char*) &temp, sizeof(unsigned long long)); + temp = packPixelHit(0x2FFF,0xFF,0x3FF,0x3FFF,0x9876); // 5.03316, 201326591 + write_file.write((char*) &temp, sizeof(unsigned long long)); - temp = packPixelHit(0x000F, 0xFF, 0x3FF, 0x3FFF, 0x9876); // 0.00655357, 262143 - write_file.write((char*)&temp, sizeof(unsigned long long)); - temp = packPixelHit(0x3FFF, 0xFF, 0x3FF, 0x3FFF, 0x9876); // 6.71089, 268435455 - write_file.write((char*)&temp, sizeof(unsigned long long)); + // in order pixel hit datapackets + // std::cout << "In order: decrement counter" << std::endl; + // decrement counter + // temp = packDataHeader('T','P','X','3',0,0,56); + // write_file.write((char*) &temp, sizeof(unsigned long long)); - temp = packPixelHit(0x00FF, 0xFF, 0x3FF, 0x3FFF, 0x9876); // 0.104858, 4194303 - write_file.write((char*)&temp, sizeof(unsigned long long)); + temp = packPixelHit(0x000F,0xFF,0x3FF,0x3FFF,0x9876); // 0.00655357, 262143 + write_file.write((char*) &temp, sizeof(unsigned long long)); - temp = packPixelHit(0x0FFF, 0xFF, 0x3FF, 0x3FFF, 0x9876); // 1.67772, 67108863 - write_file.write((char*)&temp, sizeof(unsigned long long)); + temp = packPixelHit(0x00FF,0xFF,0x3FF,0x3FFF,0x9876); // 0.104858, 4194303 + write_file.write((char*) &temp, sizeof(unsigned long long)); - temp = packPixelHit(0x1FFF, 0xFF, 0x3FF, 0x3FFF, 0x9876); // 3.35544, 134217727 - write_file.write((char*)&temp, sizeof(unsigned long long)); + temp = packPixelHit(0x0FFF,0xFF,0x3FF,0x3FFF,0x9876); // 1.67772, 67108863 + write_file.write((char*) &temp, sizeof(unsigned long long)); - temp = packPixelHit(0x2FFF, 0xFF, 0x3FF, 0x3FFF, 0x9876); // 5.03316, 201326591 - write_file.write((char*)&temp, sizeof(unsigned long long)); + temp = packPixelHit(0x2FFF,0xFF,0x3FF,0x3FFF,0x9876); // 5.03316, 201326591 + write_file.write((char*) &temp, sizeof(unsigned long long)); - // in order pixel hit datapackets - // std::cout << "In order: decrement counter" << std::endl; - // decrement counter - // temp = packDataHeader('T','P','X','3',0,0,56); - // write_file.write((char*) &temp, sizeof(unsigned long long)); + /* --------------------------------------------------------------*/ - temp = packPixelHit(0x000F, 0xFF, 0x3FF, 0x3FFF, 0x9876); // 0.00655357, 262143 - write_file.write((char*)&temp, sizeof(unsigned long long)); + temp = packPixelHit(0xF000,0xFF,0x3FF,0x3FFF,0x9876); // 25.1662, 1006649343 + write_file.write((char*) &temp, sizeof(unsigned long long)); - temp = packPixelHit(0x00FF, 0xFF, 0x3FF, 0x3FFF, 0x9876); // 0.104858, 4194303 - write_file.write((char*)&temp, sizeof(unsigned long long)); + temp = packPixelHit(0xFF00,0xFF,0x3FF,0x3FFF,0x9876); // 26.7391, 1069563903 + write_file.write((char*) &temp, sizeof(unsigned long long)); - temp = packPixelHit(0x0FFF, 0xFF, 0x3FF, 0x3FFF, 0x9876); // 1.67772, 67108863 - write_file.write((char*)&temp, sizeof(unsigned long long)); + temp = packPixelHit(0xFFFF,0xFF,0x3FF,0x3FFF,0x9876); // 26.8435, 1073741823 + write_file.write((char*) &temp, sizeof(unsigned long long)); - temp = packPixelHit(0x2FFF, 0xFF, 0x3FF, 0x3FFF, 0x9876); // 5.03316, 201326591 - write_file.write((char*)&temp, sizeof(unsigned long long)); + - /* --------------------------------------------------------------*/ + // in order pixel + // std::cout << "In order: no changes counter" << std::endl; + // no changes to counter + // temp = packDataHeader('T','P','X','3',0,0,48); + // write_file.write((char*) &temp, sizeof(unsigned long long)); - temp = packPixelHit(0xF000, 0xFF, 0x3FF, 0x3FFF, 0x9876); // 25.1662, 1006649343 - write_file.write((char*)&temp, sizeof(unsigned long long)); + temp = packPixelHit(0x000F,0xFF,0x3FF,0x3FFF,0x9876); // 0.00655357, 262143 + write_file.write((char*) &temp, sizeof(unsigned long long)); - temp = packPixelHit(0xFF00, 0xFF, 0x3FF, 0x3FFF, 0x9876); // 26.7391, 1069563903 - write_file.write((char*)&temp, sizeof(unsigned long long)); + temp = packPixelHit(0x00FF,0xFF,0x3FF,0x3FFF,0x9876); // 0.104858, 4194303 + write_file.write((char*) &temp, sizeof(unsigned long long)); - temp = packPixelHit(0xFFFF, 0xFF, 0x3FF, 0x3FFF, 0x9876); // 26.8435, 1073741823 - write_file.write((char*)&temp, sizeof(unsigned long long)); + temp = packPixelHit(0x0FFF,0xFF,0x3FF,0x3FFF,0x9876); // 1.67772, 67108863 + write_file.write((char*) &temp, sizeof(unsigned long long)); - // in order pixel - // std::cout << "In order: no changes counter" << std::endl; - // no changes to counter - // temp = packDataHeader('T','P','X','3',0,0,48); - // write_file.write((char*) &temp, sizeof(unsigned long long)); + temp = packPixelHit(0x1FFF,0xFF,0x3FF,0x3FFF,0x9876); // 3.35544, 134217727 + write_file.write((char*) &temp, sizeof(unsigned long long)); - temp = packPixelHit(0x000F, 0xFF, 0x3FF, 0x3FFF, 0x9876); // 0.00655357, 262143 - write_file.write((char*)&temp, sizeof(unsigned long long)); + temp = packPixelHit(0x2FFF,0xFF,0x3FF,0x3FFF,0x9876); // 5.03316, 201326591 + write_file.write((char*) &temp, sizeof(unsigned long long)); - temp = packPixelHit(0x00FF, 0xFF, 0x3FF, 0x3FFF, 0x9876); // 0.104858, 4194303 - write_file.write((char*)&temp, sizeof(unsigned long long)); + temp = packPixelHit(0x3FFF,0xFF,0x3FF,0x3FFF,0x9876); // 6.71089, 268435455 + write_file.write((char*) &temp, sizeof(unsigned long long)); - temp = packPixelHit(0x0FFF, 0xFF, 0x3FF, 0x3FFF, 0x9876); // 1.67772, 67108863 - write_file.write((char*)&temp, sizeof(unsigned long long)); - temp = packPixelHit(0x1FFF, 0xFF, 0x3FF, 0x3FFF, 0x9876); // 3.35544, 134217727 - write_file.write((char*)&temp, sizeof(unsigned long long)); + write_file.close(); - temp = packPixelHit(0x2FFF, 0xFF, 0x3FF, 0x3FFF, 0x9876); // 5.03316, 201326591 - write_file.write((char*)&temp, sizeof(unsigned long long)); + // read in .tpx3 file + std::ifstream read_file("rollover_test_data.tpx3", std::ios::out | std::ios::binary); + if (!read_file){ + std::cout << "Cannot open file!" << std::endl; + return 1; + } - temp = packPixelHit(0x3FFF, 0xFF, 0x3FF, 0x3FFF, 0x9876); // 6.71089, 268435455 - write_file.write((char*)&temp, sizeof(unsigned long long)); + std::cout << " \n Reading file: Checking \n"; - write_file.close(); + unsigned long long temp1; + read_file.read((char*) &temp1, sizeof(unsigned long long)); + unsigned short t1 = (unsigned short) temp1 & 0xFF; + unsigned short p1 = (unsigned short) (temp1 >> 8) & 0xFF; + unsigned short x1 = (unsigned short) (temp1 >> 16) & 0xFF; + unsigned short three1 = (unsigned short) (temp1 >> 24) & 0xFF; + unsigned short chip_nr1 = (unsigned short) (temp1 >> 32) & 0xFF; + unsigned short mode1 = (unsigned short) (temp1 >> 40) & 0xFF; + unsigned short num_bytes1 = (unsigned short) (temp1 >> 48) & 0xFFFF; - // read in .tpx3 file - std::ifstream read_file("rollover_test_data.tpx3", std::ios::out | std::ios::binary); - if (!read_file) { - std::cout << "Cannot open file!" << std::endl; - return 1; - } + // std::cout << "T: " << t1 + // << ", P: " << p1 + // << ", X: " << x1 + // << ", three: " << three1 + // << ", chip_nr: " << chip_nr1 + // << ", mode: " << mode1 + // << ", num_bytes: " << num_bytes1 + // << std::endl; - std::cout << " \n Reading file: Checking \n"; + while (read_file.read((char*) &temp1, sizeof(unsigned long long))){ + // check + unsigned short spider_time1 = (unsigned short) temp1 & 0xFFFF; + unsigned char ftoa1 = (unsigned char) (temp1 >> 16) & 0xFF; + unsigned short TOT1 = (unsigned short) (temp1 >> 20) & 0x300; + unsigned short TOA1 = (unsigned short) (temp1 >> 30) & 0x3FFF; + unsigned short pixaddr1 = (unsigned short) (temp1 >> 44) & 0xFFFF; + unsigned char header1 = (unsigned char) (temp1 >> 60) & 0xFF; - unsigned long long temp1; - read_file.read((char*)&temp1, sizeof(unsigned long long)); - unsigned short t1 = (unsigned short)temp1 & 0xFF; - unsigned short p1 = (unsigned short)(temp1 >> 8) & 0xFF; - unsigned short x1 = (unsigned short)(temp1 >> 16) & 0xFF; - unsigned short three1 = (unsigned short)(temp1 >> 24) & 0xFF; - unsigned short chip_nr1 = (unsigned short)(temp1 >> 32) & 0xFF; - unsigned short mode1 = (unsigned short)(temp1 >> 40) & 0xFF; - unsigned short num_bytes1 = (unsigned short)(temp1 >> 48) & 0xFFFF; + // std::cout << "spider_time: " << std::hex << spider_time1 + // << ", ftoa: " << std::hex << +ftoa1 + // << ", TOT: " << std::hex << TOT1 + // << ", TOA: " << std::hex << TOA1 + // << ", pixaddr: " << std::hex << pixaddr1 + // << ", header: " << std::hex << +header1 << std::endl; - // std::cout << "T: " << t1 - // << ", P: " << p1 - // << ", X: " << x1 - // << ", three: " << three1 - // << ", chip_nr: " << chip_nr1 - // << ", mode: " << mode1 - // << ", num_bytes: " << num_bytes1 - // << std::endl; + unsigned int spidertime1 = (spider_time1 << 14) | TOA1; + std::cout << "spidertime + toa: " << spidertime1 << " s\n"; + } + - while (read_file.read((char*)&temp1, sizeof(unsigned long long))) { - // check - unsigned short spider_time1 = (unsigned short)temp1 & 0xFFFF; - unsigned char ftoa1 = (unsigned char)(temp1 >> 16) & 0xFF; - unsigned short TOT1 = (unsigned short)(temp1 >> 20) & 0x300; - unsigned short TOA1 = (unsigned short)(temp1 >> 30) & 0x3FFF; - unsigned short pixaddr1 = (unsigned short)(temp1 >> 44) & 0xFFFF; - unsigned char header1 = (unsigned char)(temp1 >> 60) & 0xFF; + read_file.close(); - // std::cout << "spider_time: " << std::hex << spider_time1 - // << ", ftoa: " << std::hex << +ftoa1 - // << ", TOT: " << std::hex << TOT1 - // << ", TOA: " << std::hex << TOA1 - // << ", pixaddr: " << std::hex << pixaddr1 - // << ", header: " << std::hex << +header1 << std::endl; - - unsigned int spidertime1 = (spider_time1 << 14) | TOA1; - std::cout << "spidertime + toa: " << spidertime1 << " s\n"; - } - - read_file.close(); + return 0; - return 0; } \ No newline at end of file diff --git a/sophiread/SophireadLib/tests/test_clustering.cpp b/sophiread/SophireadLib/tests/test_clustering.cpp index 5f8a02a..96bae7e 100644 --- a/sophiread/SophireadLib/tests/test_clustering.cpp +++ b/sophiread/SophireadLib/tests/test_clustering.cpp @@ -53,7 +53,7 @@ TEST(Clustering, ABSAlgorithm) { auto data = gen_clusters(); // create the ABS algorithm - ABS abs(5., 1, 75); + ABS abs(5.,1,75); abs.fit(data); abs.set_method("centroid"); auto events = abs.get_events(data); @@ -63,8 +63,10 @@ TEST(Clustering, ABSAlgorithm) { // OPENMP will shuffle the order of the events, we need to extract and sort // them before asserting - std::vector x = {events[0].getX(), events[1].getX(), events[2].getX()}; - std::vector y = {events[0].getY(), events[1].getY(), events[2].getY()}; + std::vector x = {events[0].getX(), events[1].getX(), + events[2].getX()}; + std::vector y = {events[0].getY(), events[1].getY(), + events[2].getY()}; std::sort(x.begin(), x.end()); std::sort(y.begin(), y.end()); @@ -88,15 +90,18 @@ TEST(Clustering, DBSCANAlgorithm) { auto data = gen_clusters(); // create the DSCAN algorithm - DBSCAN dbs(8000. /*eps time*/, 30 /*min_points time*/, 4. /*eps xy*/, 10 /*min_points xy*/); + DBSCAN dbs(8000. /*eps time*/, 30 /*min_points time*/, 4. /*eps xy*/, + 10 /*min_points xy*/); auto events = dbs.get_events(data); // dbs.fit(data); // check that there are 3 events EXPECT_EQ(events.size(), 3); - std::vector x = {events[0].getX(), events[1].getX(), events[2].getX()}; - std::vector y = {events[0].getY(), events[1].getY(), events[2].getY()}; + std::vector x = {events[0].getX(), events[1].getX(), + events[2].getX()}; + std::vector y = {events[0].getY(), events[1].getY(), + events[2].getY()}; std::sort(x.begin(), x.end()); std::sort(y.begin(), y.end()); diff --git a/sophiread/SophireadLib/tests/test_peakfitting.cpp b/sophiread/SophireadLib/tests/test_peakfitting.cpp index 2151ffb..acffc8e 100644 --- a/sophiread/SophireadLib/tests/test_peakfitting.cpp +++ b/sophiread/SophireadLib/tests/test_peakfitting.cpp @@ -32,18 +32,24 @@ TEST(PeakFitting, CentroidAlgorithm) { NeutronEvent event = alg.fit(hits); // Check that the event is correct - EXPECT_NEAR(event.getX(), 1863.66 * DSCALE, absolute_error) << "Centroid x is not correct."; - EXPECT_NEAR(event.getY(), 2718.74 * DSCALE, absolute_error) << "Centroid y is not correct."; - EXPECT_NEAR(event.getTOF(), 2262.67, absolute_error) << "Centroid tof is not correct."; + EXPECT_NEAR(event.getX(), 1863.66 * DSCALE, absolute_error) + << "Centroid x is not correct."; + EXPECT_NEAR(event.getY(), 2718.74 * DSCALE, absolute_error) + << "Centroid y is not correct."; + EXPECT_NEAR(event.getTOF(), 2262.67, absolute_error) + << "Centroid tof is not correct."; // CASE_2: not weighted by tot Centroid alg2(false); NeutronEvent event2 = alg2.fit(hits); // Check that the event is correct - EXPECT_NEAR(event2.getX(), 1845.67 * DSCALE, absolute_error) << "Centroid x is not correct."; - EXPECT_NEAR(event2.getY(), 2674.33 * DSCALE, absolute_error) << "Centroid y is not correct."; - EXPECT_NEAR(event2.getTOF(), 2262.67, absolute_error) << "Centroid tof is not correct."; + EXPECT_NEAR(event2.getX(), 1845.67 * DSCALE, absolute_error) + << "Centroid x is not correct."; + EXPECT_NEAR(event2.getY(), 2674.33 * DSCALE, absolute_error) + << "Centroid y is not correct."; + EXPECT_NEAR(event2.getTOF(), 2262.67, absolute_error) + << "Centroid tof is not correct."; } TEST(PeakFitting, FastGaussianAlgorithm) { @@ -57,7 +63,8 @@ TEST(PeakFitting, FastGaussianAlgorithm) { int y = 50 + pos(gen); int mytof = 1000 + tof(gen); int stime = 10 + spidertime(gen); - hits.push_back(Hit(x, y, 1 + tot(gen), 1 + toa(gen), ftoa(gen), mytof, stime)); + hits.push_back( + Hit(x, y, 1 + tot(gen), 1 + toa(gen), ftoa(gen), mytof, stime)); } // Create a fast gaussian algorithm @@ -65,7 +72,10 @@ TEST(PeakFitting, FastGaussianAlgorithm) { NeutronEvent event = alg.fit(hits); // Check that the event is correct - EXPECT_NEAR(event.getX(), 50 * DSCALE, absolute_error) << "FastGaussian x is not correct."; - EXPECT_NEAR(event.getY(), 50 * DSCALE, absolute_error) << "FastGaussian y is not correct."; - EXPECT_NEAR(event.getTOF(), 1000, absolute_error) << "FastGaussian tof is not correct."; + EXPECT_NEAR(event.getX(), 50 * DSCALE, absolute_error) + << "FastGaussian x is not correct."; + EXPECT_NEAR(event.getY(), 50 * DSCALE, absolute_error) + << "FastGaussian y is not correct."; + EXPECT_NEAR(event.getTOF(), 1000, absolute_error) + << "FastGaussian tof is not correct."; } diff --git a/sophiread/SophireadLib/tests/test_tpx3.cpp b/sophiread/SophireadLib/tests/test_tpx3.cpp index 1712a87..3a630fa 100644 --- a/sophiread/SophireadLib/tests/test_tpx3.cpp +++ b/sophiread/SophireadLib/tests/test_tpx3.cpp @@ -1,5 +1,4 @@ #include - #include #include "tpx3.h" @@ -7,7 +6,8 @@ // Test the readTimepix3RawData function TEST(FileHandlingTest, ReadTPX3RawData) { // read the testing raw data - auto hits = readTimepix3RawData("../data/frames_pinhole_3mm_1s_RESOLUTION_000001.tpx3"); + auto hits = + readTimepix3RawData("../data/frames_pinhole_3mm_1s_RESOLUTION_000001.tpx3"); // check the number of hits EXPECT_EQ(hits.size(), 9933804); @@ -31,7 +31,8 @@ TEST(FileHandlingTest, ReadTPX3RawData) { TEST(FileHandlingTest, VerifyTiming) { // read the testing raw data - auto hits = readTimepix3RawData("../data/greg_220ms_TDC_GDC_enabled_20230214_3.tpx3"); + auto hits = + readTimepix3RawData("../data/greg_220ms_TDC_GDC_enabled_20230214_3.tpx3"); // check the number of hits EXPECT_EQ(hits.size(), 365); @@ -53,60 +54,64 @@ TEST(FileHandlingTest, VerifyTiming) { // EXPECT_EQ(hits[365 - 1].getSPIDERTIME(), 8809347419); } -TEST(FileHandlingTest, VerifyRollover) { - // reading the testing raw data - auto hits = readTimepix3RawData("../data/rollover_test_data.tpx3"); +TEST(FileHandlingTest, VerifyRollover){ + // reading the testing raw data + auto hits = + readTimepix3RawData("../data/rollover_test_data.tpx3"); // check the number of hits - EXPECT_EQ(hits.size(), 26); - - // check disordered pixels: increment counter - EXPECT_EQ(hits[0].getSPIDERTIME(), 1006649343); // 25.1662, 1006649343 - EXPECT_EQ(hits[1].getSPIDERTIME(), 1069563903); // 26.7391, 1069563903 - EXPECT_EQ(hits[2].getSPIDERTIME(), 1073741823); // 26.8435, 1073741823 - EXPECT_EQ(hits[3].getSPIDERTIME(), 262143 + 1073741824); // 0.00655357, 262143 - EXPECT_EQ(hits[4].getSPIDERTIME(), 4194303 + 1073741824); // 0.104858, 4194303 - EXPECT_EQ(hits[5].getSPIDERTIME(), 67108863 + 1073741824); // 1.67772, 67108863 + EXPECT_EQ(hits.size(),26); + + // check disordered pixels: increment counter + EXPECT_EQ(hits[0].getSPIDERTIME(),1006649343); // 25.1662, 1006649343 + EXPECT_EQ(hits[1].getSPIDERTIME(),1069563903); // 26.7391, 1069563903 + EXPECT_EQ(hits[2].getSPIDERTIME(),1073741823); // 26.8435, 1073741823 + EXPECT_EQ(hits[3].getSPIDERTIME(),262143 + 1073741824); // 0.00655357, 262143 + EXPECT_EQ(hits[4].getSPIDERTIME(),4194303 + 1073741824); // 0.104858, 4194303 + EXPECT_EQ(hits[5].getSPIDERTIME(),67108863 + 1073741824); // 1.67772, 67108863 EXPECT_EQ(hits[6].getSPIDERTIME(), 201326591 + 1073741824); // 5.03316, 201326591 // check disordered pixels: do nothing to counter - EXPECT_EQ(hits[7].getSPIDERTIME(), 262143 + 1073741824); // 0.00655357, 262143 - EXPECT_EQ(hits[8].getSPIDERTIME(), 268435455 + 1073741824); // 6.71089, 268435455 - EXPECT_EQ(hits[9].getSPIDERTIME(), 4194303 + 1073741824); // 0.104858, 4194303 - EXPECT_EQ(hits[10].getSPIDERTIME(), 67108863 + 1073741824); // 1.67772, 67108863 - EXPECT_EQ(hits[11].getSPIDERTIME(), 134217727 + 1073741824); // 3.35544, 134217727 - EXPECT_EQ(hits[12].getSPIDERTIME(), 201326591 + 1073741824); // 5.03316, 201326591 - - // check order pixels: decrement counter - EXPECT_EQ(hits[13].getSPIDERTIME(), 262143 + 1073741824); // 0.00655357, 262143 - EXPECT_EQ(hits[14].getSPIDERTIME(), 4194303 + 1073741824); // 0.104858, 4194303 - EXPECT_EQ(hits[15].getSPIDERTIME(), 67108863 + 1073741824); // 1.67772, 67108863 - EXPECT_EQ(hits[16].getSPIDERTIME(), 201326591 + 1073741824); // 5.03316, 201326591 - EXPECT_EQ(hits[17].getSPIDERTIME(), 1006649343); // 25.1662, 1006649343 - EXPECT_EQ(hits[18].getSPIDERTIME(), 1069563903); // 26.7391, 1069563903 - EXPECT_EQ(hits[19].getSPIDERTIME(), 1073741823); // 26.8435, 1073741823 - - // check ordered pixels: do nothing to counter - EXPECT_EQ(hits[20].getSPIDERTIME(), 262143 + 1073741824); // 0.00655357, 262143 - EXPECT_EQ(hits[21].getSPIDERTIME(), 4194303 + 1073741824); // 0.104858, 4194303 - EXPECT_EQ(hits[22].getSPIDERTIME(), 67108863 + 1073741824); // 1.67772, 67108863 - EXPECT_EQ(hits[23].getSPIDERTIME(), 134217727 + 1073741824); // 3.35544, 134217727 - EXPECT_EQ(hits[24].getSPIDERTIME(), 201326591 + 1073741824); // 5.03316, 201326591 - EXPECT_EQ(hits[25].getSPIDERTIME(), 268435455 + 1073741824); // 6.71089, 268435455 + EXPECT_EQ(hits[7].getSPIDERTIME(),262143 + 1073741824); // 0.00655357, 262143 + EXPECT_EQ(hits[8].getSPIDERTIME(),268435455 + 1073741824); // 6.71089, 268435455 + EXPECT_EQ(hits[9].getSPIDERTIME(),4194303 + 1073741824); // 0.104858, 4194303 + EXPECT_EQ(hits[10].getSPIDERTIME(),67108863 + 1073741824); // 1.67772, 67108863 + EXPECT_EQ(hits[11].getSPIDERTIME(),134217727 + 1073741824); // 3.35544, 134217727 + EXPECT_EQ(hits[12].getSPIDERTIME(),201326591 + 1073741824); // 5.03316, 201326591 + + // check order pixels: decrement counter + EXPECT_EQ(hits[13].getSPIDERTIME(),262143 + 1073741824); // 0.00655357, 262143 + EXPECT_EQ(hits[14].getSPIDERTIME(),4194303 + 1073741824); // 0.104858, 4194303 + EXPECT_EQ(hits[15].getSPIDERTIME(),67108863 + 1073741824); // 1.67772, 67108863 + EXPECT_EQ(hits[16].getSPIDERTIME(),201326591 + 1073741824); // 5.03316, 201326591 + EXPECT_EQ(hits[17].getSPIDERTIME(),1006649343); // 25.1662, 1006649343 + EXPECT_EQ(hits[18].getSPIDERTIME(),1069563903); // 26.7391, 1069563903 + EXPECT_EQ(hits[19].getSPIDERTIME(),1073741823); // 26.8435, 1073741823 + + // check ordered pixels: do nothing to counter + EXPECT_EQ(hits[20].getSPIDERTIME(),262143 + 1073741824); // 0.00655357, 262143 + EXPECT_EQ(hits[21].getSPIDERTIME(),4194303 + 1073741824); // 0.104858, 4194303 + EXPECT_EQ(hits[22].getSPIDERTIME(),67108863 + 1073741824); // 1.67772, 67108863 + EXPECT_EQ(hits[23].getSPIDERTIME(),134217727 + 1073741824); // 3.35544, 134217727 + EXPECT_EQ(hits[24].getSPIDERTIME(),201326591 + 1073741824); // 5.03316, 201326591 + EXPECT_EQ(hits[25].getSPIDERTIME(),268435455 + 1073741824); // 6.71089, 268435455 + } // Test the parseUserDefinedParams function TEST(FileHandlingTest, ParseUserDefinedParams) { - // read user-defined param files + + // read user-defined param files auto p1 = parseUserDefinedParams("../data/user_defined_params.txt"); auto p2 = parseUserDefinedParams("../data/user_defined_params_1.txt"); - // check user-defined params + // check user-defined params EXPECT_EQ(p1.getABSRadius(), 5.0); - EXPECT_EQ(p1.getABSMinClusterSize(), 1); + EXPECT_EQ(p1.getABSMinClusterSize(),1); EXPECT_EQ(p1.getABSSpidertimeRange(), 75); EXPECT_EQ(p2.getABSRadius(), 20.0); - EXPECT_EQ(p2.getABSMinClusterSize(), 30); - EXPECT_EQ(p2.getABSSpidertimeRange(), 500000); + EXPECT_EQ(p2.getABSMinClusterSize(),30); + EXPECT_EQ(p2.getABSSpidertimeRange(),500000); + } \ No newline at end of file diff --git a/sophiread/SophireadStreamCLI/src/sophiread_stream.cpp b/sophiread/SophireadStreamCLI/src/sophiread_stream.cpp index 25f0260..794c541 100644 --- a/sophiread/SophireadStreamCLI/src/sophiread_stream.cpp +++ b/sophiread/SophireadStreamCLI/src/sophiread_stream.cpp @@ -25,7 +25,7 @@ std::vector neutron_events; */ void process_batch(const std::vector &batch) { ClusteringAlgorithm *alg; - alg = new ABS(5.0, 1, 75); // select clustering algorithm + alg = new ABS(5.0,1,75); // select clustering algorithm alg->set_method("fast_gaussian"); // select peak fitting method alg->fit(batch); std::vector events = alg->get_events(batch); @@ -89,7 +89,8 @@ void reader(std::string filename) { if (char_array[7] == 0x6F) { // TDC data packets tdclast = (unsigned long *)(&char_array[0]); - mytdc = (((*tdclast) >> 12) & 0xFFFFFFFF); // rick: 0x3fffffff, get 32-bit tdc + mytdc = (((*tdclast) >> 12) & + 0xFFFFFFFF); // rick: 0x3fffffff, get 32-bit tdc TDC_LSB32 = GDC_timestamp & 0xFFFFFFFF; TDC_MSB16 = (GDC_timestamp >> 32) & 0xFFFF; if (mytdc < TDC_LSB32) { @@ -116,8 +117,10 @@ void reader(std::string filename) { } } else if ((char_array[7] & 0xF0) == 0xb0) { // Process the data into hit - auto data_packet = std::vector(char_array, char_array + sizeof(char_array) / sizeof(char)); - auto hit = packetToHit(data_packet, TDC_timestamp, GDC_timestamp, chip_layout_type); + auto data_packet = std::vector( + char_array, char_array + sizeof(char_array) / sizeof(char)); + auto hit = packetToHit(data_packet, TDC_timestamp, GDC_timestamp, + chip_layout_type); // std::cout << "Hits: " << hit.getX() << " " << hit.getY() << " " << // hit.getTOF_ns()*1E-6 << " " << hit.getSPIDERTIME_ns()*1E-9 << // std::endl; diff --git a/sophiread/include/version.h b/sophiread/include/version.h index 5465154..4d9f4f1 100644 --- a/sophiread/include/version.h +++ b/sophiread/include/version.h @@ -16,7 +16,9 @@ #define VERSION_PATCH 0 // Version number final -#define VERSION_NUMBER ((unsigned long)(VERSION_MAJOR << 16) | (VERSION_MINOR << 8) | (VERSION_PATCH)) +#define VERSION_NUMBER \ + ((unsigned long)(VERSION_MAJOR << 16) | (VERSION_MINOR << 8) | \ + (VERSION_PATCH)) // Version string #define VERSION_STRING \ diff --git a/sophiread/resources/manufacture/tpx3cam.cpp b/sophiread/resources/manufacture/tpx3cam.cpp index b20dc4f..9c38e7e 100644 --- a/sophiread/resources/manufacture/tpx3cam.cpp +++ b/sophiread/resources/manufacture/tpx3cam.cpp @@ -2,196 +2,204 @@ Example C++ script to convert a raw data .tpx3 to text file - copyright JL@ Amsterdam Scientific Instruments B.V. - www.amscins.com - 05-12-2019 + copyright JL@ Amsterdam Scientific Instruments B.V. + www.amscins.com + 05-12-2019 ****************************************************************/ #include - -#include +#include #include #include -#include #include +#include using namespace std; -int main(int argc, char* argv[]) { - char* TPX_name; - if (argc == 1) { - cout << "Usage: tpx3 raw data" << endl << endl; - return 0; - } else { - TPX_name = argv[1]; - cout << "imported file: " << TPX_name << endl; - } - - ofstream xy_file("converted.txt"); // Converted and saved txt file - streampos begin, end; - ifstream myfile(TPX_name, ios::binary); - unsigned short xpix, ypix, TOT, TOA, spidrTime; - char chipnr, FTOA; - int frameNr; - int CTOA; - int mode; - unsigned long Timer_LSB32 = 0; - unsigned long Timer_MSB16 = 0; - unsigned long numofTDC = 0; - - if (!myfile) { - cout << "This file is not found!" << endl; - } else { - myfile.seekg(0, myfile.end); - unsigned long long fileLength = myfile.tellg(); - myfile.seekg(0, myfile.beg); - unsigned long long temp64; - cout << "filesize: " << fileLength / (1024 * 1024) << "MB" << endl; - unsigned long long NumofPacket = fileLength / 8; - unsigned long long* datapacket = new unsigned long long[NumofPacket]; - myfile.read((char*)datapacket, fileLength); - myfile.close(); - char* HeaderBuffer = new char[8]; - unsigned long long temp; - - for (unsigned long long i = 0; i < NumofPacket; i++) { - memcpy(HeaderBuffer, &datapacket[i], 8); - if (HeaderBuffer[0] == 'T' && HeaderBuffer[1] == 'P' && HeaderBuffer[2] == 'X') { - int size = ((0xff & HeaderBuffer[7]) << 8) | (0xff & HeaderBuffer[6]); - chipnr = HeaderBuffer[4]; - mode = HeaderBuffer[5]; - for (int j = 0; j < size / 8; j++) { - temp = datapacket[i + j + 1]; - int hdr = (int)(temp >> 56); - int packet = temp >> 60; - double coarsetime; - unsigned long tmpfine; - unsigned long trigtime_fine; - double time_unit, global_timestamp; - int trigger_counter; - unsigned long long int timemaster; - int heartbeatL, heartbeatM; - double TDC_timestamp; - double spidrTimens; - int x, y; - double TOTns; - double TOAns; - long dcol; - long spix; - long pix; - - switch (packet) { - case 0x6: // TDC timestamp packet header - - if ((temp >> 56) == 0x6f) cout << "tdc1 rising edge is working" << endl; - if ((temp >> 56) == 0x6a) cout << "tdc1 falling edge is working" << endl; - if ((temp >> 56) == 0x6e) cout << "tdc2 rising edge is working" << endl; - if ((temp >> 56) == 0x6b) cout << "tdc2 falling edge is working" << endl; - coarsetime = (temp >> 12) & 0xFFFFFFFF; - tmpfine = (temp >> 5) & 0xF; - tmpfine = ((tmpfine - 1) << 9) / 12; - trigtime_fine = (temp & 0x0000000000000E00) | (tmpfine & 0x00000000000001FF); - time_unit = 25. / 4096; - trigger_counter = temp >> 44 & 0xFFF; - TDC_timestamp = coarsetime * 25E-9 + trigtime_fine * time_unit * 1E-9; - // uncomment below to save TDC timestamps into the txt file - xy_file << setprecision(15) << TDC_timestamp << endl; - // cout<< "TDC timestamp: " << setprecision(15) << TDC_timestamp << endl; - numofTDC = numofTDC + 1; - break; - - case 0xb: // Chip data: ToA and ToT timestamp packet, x, y - - spidrTime = (unsigned short)(temp & 0xffff); - dcol = (temp & 0x0FE0000000000000L) >> 52; - spix = (temp & 0x001F800000000000L) >> 45; - pix = (temp & 0x0000700000000000L) >> 44; - x = (int)(dcol + pix / 4); - y = (int)(spix + (pix & 0x3)); - TOA = (unsigned short)((temp >> (16 + 14)) & 0x3fff); - TOT = (unsigned short)((temp >> (16 + 4)) & 0x3ff); - FTOA = (unsigned char)((temp >> 16) & 0xf); - CTOA = (TOA << 4) | (~FTOA & 0xf); - spidrTimens = spidrTime * 25.0 * 16384.0; - TOAns = TOA * 25.0; - TOTns = TOT * 25.0; - global_timestamp = spidrTimens + CTOA * (25.0 / 16); - - /************************************************************ - Condition is different for single Timepix3 chip or quad chips: - Single chip, using "int (Chipnr) +3" - Quad chips, using "int (Chipnr)" - ************************************************************/ - switch (int(chipnr)) // for quad chips; - { - case 0: - x += 260; - y = y; - break; - - case 1: - x = 255 - x + 260; - y = 255 - y + 260; - break; - - case 2: - x = 255 - x; - y = 255 - y + 260; - break; - - case 3: - break; - - default: - break; - } - - // uncomment below to save the chip data into the text file; - xy_file << setprecision(15) << x << " " << y << " " << global_timestamp / 1E9 << " " << TOTns - << endl; // x, y, toa, tot data can be saved into txt data - cout << "Chip-ToA: " << setprecision(15) << global_timestamp / 1E9 << " ToT: " << TOTns << " x: " << x - << " y: " << y << endl; - - break; - - case 0x4: // the global timestamps. - - if (((temp >> 56) & 0xF) == 0x4) { - Timer_LSB32 = (temp >> 16) & 0xFFFFFFFF; - } else if (((temp >> 56) & 0xF) == 0x5) { - Timer_MSB16 = (temp >> 16) & 0xFFFF; - unsigned long long int timemaster; - timemaster = Timer_MSB16; - timemaster = (timemaster << 32) & 0xFFFF00000000; - timemaster = timemaster | Timer_LSB32; - int diff = (spidrTime >> 14) - ((Timer_LSB32 >> 28) & 0x3); - - if ((spidrTime >> 14) == ((Timer_LSB32 >> 28) & 0x3)) { - } else { - Timer_MSB16 = Timer_MSB16 - diff; - } - // uncomment below to save the global timestamps into the text file; - xy_file << " Global time: " << setprecision(15) << timemaster * 25e-9 - << endl; // global timestamps can be saved into text file - } - break; +int main(int argc, char *argv[]) +{ + char* TPX_name; + if (argc == 1) { + cout << "Usage: tpx3 raw data" << endl << endl; + return 0; + } + else { + TPX_name = argv[1]; + cout << "imported file: " << TPX_name << endl; + } - default: - break; - } - } - i += (size / 8); - printf("i : %lld\r", i); - } + ofstream xy_file("converted.txt"); //Converted and saved txt file + streampos begin, end; + ifstream myfile(TPX_name, ios::binary); + unsigned short xpix, ypix, TOT, TOA, spidrTime; + char chipnr, FTOA; + int frameNr; + int CTOA; + int mode; + unsigned long Timer_LSB32 = 0; + unsigned long Timer_MSB16 = 0; + unsigned long numofTDC=0; + + if (!myfile) { + cout << "This file is not found!" << endl; } + else { + myfile.seekg(0, myfile.end); + unsigned long long fileLength = myfile.tellg(); + myfile.seekg(0, myfile.beg); + unsigned long long temp64; + cout << "filesize: " << fileLength/(1024*1024) <<"MB" << endl; + unsigned long long NumofPacket = fileLength / 8; + unsigned long long* datapacket = new unsigned long long [NumofPacket]; + myfile.read((char*) datapacket, fileLength); + myfile.close(); + char* HeaderBuffer = new char[8]; + unsigned long long temp; + + for (unsigned long long i = 0; i < NumofPacket; i++) { + memcpy(HeaderBuffer, &datapacket[i], 8); + if (HeaderBuffer[0] == 'T' && HeaderBuffer[1] == 'P' && HeaderBuffer[2] == 'X') { + int size = ((0xff & HeaderBuffer[7]) << 8) | (0xff & HeaderBuffer[6]); + chipnr = HeaderBuffer[4]; + mode = HeaderBuffer[5]; + for (int j = 0; j < size / 8; j++) { + temp = datapacket[i + j + 1]; + int hdr = (int)(temp >> 56); + int packet = temp >> 60; + double coarsetime; + unsigned long tmpfine; + unsigned long trigtime_fine; + double time_unit, global_timestamp; + int trigger_counter; + unsigned long long int timemaster; + int heartbeatL, heartbeatM; + double TDC_timestamp; + double spidrTimens; + int x, y; + double TOTns; + double TOAns; + long dcol; + long spix; + long pix; + + switch (packet) + { + case 0x6: //TDC timestamp packet header + + if ((temp >> 56) == 0x6f) cout << "tdc1 rising edge is working" << endl; + if ((temp >> 56) == 0x6a) cout << "tdc1 falling edge is working" << endl; + if ((temp >> 56) == 0x6e) cout << "tdc2 rising edge is working" << endl; + if ((temp >> 56) == 0x6b) cout << "tdc2 falling edge is working" << endl; + coarsetime = (temp >> 12) & 0xFFFFFFFF; + tmpfine = (temp >> 5) & 0xF; + tmpfine = ((tmpfine - 1) << 9) / 12; + trigtime_fine = (temp & 0x0000000000000E00) | (tmpfine & 0x00000000000001FF); + time_unit = 25. / 4096; + trigger_counter = temp >> 44 & 0xFFF; + TDC_timestamp = coarsetime * 25E-9 + trigtime_fine * time_unit*1E-9; + //uncomment below to save TDC timestamps into the txt file + xy_file << setprecision(15) << TDC_timestamp << endl; + // cout<< "TDC timestamp: " << setprecision(15) << TDC_timestamp << endl; + numofTDC=numofTDC+1; + break; + + case 0xb: //Chip data: ToA and ToT timestamp packet, x, y + + spidrTime = (unsigned short)(temp & 0xffff); + dcol = (temp & 0x0FE0000000000000L) >> 52; + spix = (temp & 0x001F800000000000L) >> 45; + pix = (temp & 0x0000700000000000L) >> 44; + x = (int)(dcol + pix / 4); + y = (int)(spix + (pix & 0x3)); + TOA = (unsigned short)((temp >> (16 + 14)) & 0x3fff); + TOT = (unsigned short)((temp >> (16 + 4)) & 0x3ff); + FTOA = (unsigned char)((temp >> 16) & 0xf); + CTOA = (TOA << 4) | (~FTOA & 0xf); + spidrTimens = spidrTime * 25.0 * 16384.0; + TOAns = TOA * 25.0; + TOTns = TOT * 25.0; + global_timestamp = spidrTimens + CTOA * (25.0 / 16); + + /************************************************************ + Condition is different for single Timepix3 chip or quad chips: + Single chip, using "int (Chipnr) +3" + Quad chips, using "int (Chipnr)" + ************************************************************/ + switch (int (chipnr)) // for quad chips; + { + + case 0: + x += 260; + y = y; + break; + + case 1: + x = 255 - x + 260; + y = 255 - y + 260; + break; + + case 2: + x = 255 - x; + y = 255 - y + 260; + break; + + case 3: + break; + + default: + break; + + } + + //uncomment below to save the chip data into the text file; + xy_file << setprecision(15) << x << " " << y << " " << global_timestamp / 1E9 << " " << TOTns << endl; //x, y, toa, tot data can be saved into txt data + cout<< "Chip-ToA: " << setprecision(15) << global_timestamp / 1E9 << " ToT: " << TOTns << " x: " << x << " y: " << y << endl; + + break; + + case 0x4: //the global timestamps. + + if (((temp >> 56) & 0xF) == 0x4) { + Timer_LSB32 = (temp >> 16) & 0xFFFFFFFF; + } + else if (((temp >> 56) & 0xF) == 0x5) + { + Timer_MSB16 = (temp >> 16) & 0xFFFF; + unsigned long long int timemaster; + timemaster = Timer_MSB16; + timemaster = (timemaster << 32) & 0xFFFF00000000; + timemaster = timemaster | Timer_LSB32; + int diff = (spidrTime >> 14) - ((Timer_LSB32 >> 28) & 0x3); + + if ((spidrTime >> 14) == ((Timer_LSB32 >> 28) & 0x3)) + { + } + else { + Timer_MSB16 = Timer_MSB16 - diff; + } + //uncomment below to save the global timestamps into the text file; + xy_file << " Global time: " << setprecision(15) << timemaster * 25e-9 << endl; //global timestamps can be saved into text file + } + + break; + + default: + break; + } - delete[] HeaderBuffer; - delete[] datapacket; - } - cout << "the number of TDCs: " << numofTDC << endl; - xy_file.close(); - cout << "finished! " << endl; + } + i += (size / 8); + printf("i : %lld\r", i); + } + } - return 0; + delete [] HeaderBuffer; + delete [] datapacket; + } + cout<<"the number of TDCs: "< Date: Thu, 22 Aug 2024 15:22:14 -0400 Subject: [PATCH 32/38] comment out duplicate compiler settings --- sophiread/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sophiread/CMakeLists.txt b/sophiread/CMakeLists.txt index db0e729..2419d30 100644 --- a/sophiread/CMakeLists.txt +++ b/sophiread/CMakeLists.txt @@ -53,8 +53,8 @@ add_custom_target(copy_resources ALL DEPENDS ${CMAKE_BINARY_DIR}/data) # Add compiler flags if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") add_compile_options( - -O3 - -std=c++20 + # -O3 + # -std=c++20 -march=native -ffast-math -pthread From cdf28f40933aa65d8bfeba369b4e6a036a16906f Mon Sep 17 00:00:00 2001 From: Chen Zhang Date: Thu, 22 Aug 2024 15:53:55 -0400 Subject: [PATCH 33/38] address review comments --- sophiread/CMakeLists.txt | 2 -- sophiread/FastSophiread/benchmarks/benchmark_mmap.cpp | 8 ++++---- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/sophiread/CMakeLists.txt b/sophiread/CMakeLists.txt index 2419d30..f101296 100644 --- a/sophiread/CMakeLists.txt +++ b/sophiread/CMakeLists.txt @@ -53,8 +53,6 @@ add_custom_target(copy_resources ALL DEPENDS ${CMAKE_BINARY_DIR}/data) # Add compiler flags if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") add_compile_options( - # -O3 - # -std=c++20 -march=native -ffast-math -pthread diff --git a/sophiread/FastSophiread/benchmarks/benchmark_mmap.cpp b/sophiread/FastSophiread/benchmarks/benchmark_mmap.cpp index 34f613e..e8169a6 100644 --- a/sophiread/FastSophiread/benchmarks/benchmark_mmap.cpp +++ b/sophiread/FastSophiread/benchmarks/benchmark_mmap.cpp @@ -75,19 +75,19 @@ void saveEventsToHDF5(const std::string out_file_name, const std::vector tof_ns(events.size()); - std::transform(events.begin(), events.end(), tof_ns.begin(), + std::transform(events.cbegin(), events.cend(), tof_ns.begin(), [](const Neutron &event) { return event.getTOF_ns(); }); H5::DataSet tof_ns_dataset = group.createDataSet("tof", float_type, dataspace); tof_ns_dataset.write(tof_ns.data(), float_type); // -- write Nhits std::vector nhits(events.size()); - std::transform(events.begin(), events.end(), nhits.begin(), + std::transform(events.cbegin(), events.cend(), nhits.begin(), [](const Neutron &event) { return event.getNHits(); }); H5::DataSet nhits_dataset = group.createDataSet("nHits", int_type, dataspace); nhits_dataset.write(nhits.data(), int_type); // -- write TOT std::vector tot(events.size()); - std::transform(events.begin(), events.end(), tot.begin(), [](const Neutron &event) { return event.getTOT(); }); + std::transform(events.cbegin(), events.cend(), tot.begin(), [](const Neutron &event) { return event.getTOT(); }); H5::DataSet tot_dataset = group.createDataSet("tot", float_type, dataspace); tot_dataset.write(tot.data(), float_type); // -- close file @@ -260,7 +260,7 @@ int main(int argc, char* argv[]) spdlog::debug("@{:p}, {}", raw_data.map, raw_data.max); - if ( raw_data.map == NULL ) + if ( raw_data.map == nullptr ) { spdlog::error("Insufficient memory: {}", in_tpx3); exit(EXIT_FAILURE); From f07ff8dea0202eef692517257e238973889efcc5 Mon Sep 17 00:00:00 2001 From: Chen Zhang Date: Thu, 22 Aug 2024 16:11:53 -0400 Subject: [PATCH 34/38] fix potential fall through --- sophiread/SophireadCLI/src/venus_auto_reducer.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/sophiread/SophireadCLI/src/venus_auto_reducer.cpp b/sophiread/SophireadCLI/src/venus_auto_reducer.cpp index 445999c..bee3155 100644 --- a/sophiread/SophireadCLI/src/venus_auto_reducer.cpp +++ b/sophiread/SophireadCLI/src/venus_auto_reducer.cpp @@ -84,6 +84,7 @@ ProgramOptions parse_arguments(int argc, char* argv[]) { break; case 'd': options.debug = true; + break; case 'v': options.verbose = true; break; From cf1be8b8fb1dd21c7b471a2c38d234b108c6e6e1 Mon Sep 17 00:00:00 2001 From: Chen Zhang Date: Mon, 26 Aug 2024 08:16:28 -0400 Subject: [PATCH 35/38] address review feedback --- sophiread/.clang-format | 2 - sophiread/SophireadCLI/include/tiff_types.h | 18 +++++++ sophiread/SophireadCLI/src/sophiread_core.cpp | 52 ++++++++++--------- 3 files changed, 45 insertions(+), 27 deletions(-) create mode 100644 sophiread/SophireadCLI/include/tiff_types.h diff --git a/sophiread/.clang-format b/sophiread/.clang-format index 173a76c..97b0c10 100644 --- a/sophiread/.clang-format +++ b/sophiread/.clang-format @@ -246,5 +246,3 @@ WhitespaceSensitiveMacros: - BOOST_PP_STRINGIZE - NS_SWIFT_NAME - CF_SWIFT_NAME -... - diff --git a/sophiread/SophireadCLI/include/tiff_types.h b/sophiread/SophireadCLI/include/tiff_types.h new file mode 100644 index 0000000..f85a4ab --- /dev/null +++ b/sophiread/SophireadCLI/include/tiff_types.h @@ -0,0 +1,18 @@ +/** + * @file tiff_types.h + * @author Chen Zhang (zhangc@ornl.gov) + * @brief Define TIFF bit depth + * @version 0.1 + * @date 2024-08-23 + * + * @copyright Copyright (c) 2024 + * SPDX - License - Identifier: GPL - 3.0 + + */ +#pragma once + +#include + +// Define bit-depth types for TIFF images +using TIFF8Bit = uint8_t; // 8-bit +using TIFF16Bit = uint16_t; // 16-bit +using TIFF32Bit = uint32_t; // 32-bit \ No newline at end of file diff --git a/sophiread/SophireadCLI/src/sophiread_core.cpp b/sophiread/SophireadCLI/src/sophiread_core.cpp index 0b11e83..03ad2bd 100644 --- a/sophiread/SophireadCLI/src/sophiread_core.cpp +++ b/sophiread/SophireadCLI/src/sophiread_core.cpp @@ -28,6 +28,7 @@ #include #include "disk_io.h" #include "sophiread_core.h" +#include "tiff_types.h" namespace sophiread { @@ -135,7 +136,7 @@ void timedSaveHitsToHDF5(const std::string &out_hits, std::vector &batches /** * @brief Timed save events to HDF5. - * + * * @param[in] out_events * @param[in] batches */ @@ -166,11 +167,11 @@ void timedSaveEventsToHDF5(const std::string &out_events, std::vector &bat * @return std::vector>> The vector of TOF images, where each TOF image is a 2D histogram representing the distribution of neutron events in space for a specific TOF bin. */ std::vector>> timedCreateTOFImages( - const std::vector& batches, - double super_resolution, + const std::vector& batches, + double super_resolution, const std::vector& tof_bin_edges, const std::string& mode) { - + auto start = std::chrono::high_resolution_clock::now(); // Initialize the TOF images container @@ -185,7 +186,7 @@ std::vector>> timedCreateTOFImages( spdlog::error("No batches to process"); return tof_images; } - + // Calculate the dimensions of each 2D histogram based on super_resolution // one chip: 0-255 pixel pos // gap: 5 @@ -198,7 +199,7 @@ std::vector>> timedCreateTOFImages( if (!tof_bin_edges.empty()) { spdlog::debug("First bin edge: {}, Last bin edge: {}", tof_bin_edges.front(), tof_bin_edges.back()); } - + // Initialize each TOF bin's 2D histogram for (auto& tof_image : tof_images) { tof_image.resize(dim_y, std::vector(dim_x, 0)); @@ -224,7 +225,7 @@ std::vector>> timedCreateTOFImages( entries.push_back(static_cast(&neutron)); } } - + if (entries.empty()) { spdlog::debug("Batch {} is empty", batch_index); continue; @@ -232,19 +233,20 @@ std::vector>> timedCreateTOFImages( for (const auto& entry : entries) { total_entries++; - double tof_ns = entry->iGetTOF_ns(); + const double tof_ns = entry->iGetTOF_ns(); + const double tof_s = tof_ns/1e9; // Find the correct TOF bin // NOTE: tof_bin_edges are in sec, and tof_ns are in nano secs - spdlog::debug("tof_ns: {}, tof_ns/1e9: {}", tof_ns, tof_ns/1e9); - auto it = std::lower_bound(tof_bin_edges.begin(), tof_bin_edges.end(), tof_ns/1e9); - if (it != tof_bin_edges.begin()) { - size_t bin_index = std::distance(tof_bin_edges.begin(), it) - 1; - + spdlog::debug("tof_ns: {}, tof_ns/1e9: {}", tof_ns, tof_s); + + if (const auto it = std::lower_bound(tof_bin_edges.cbegin(), tof_bin_edges.cend(), tof_s); it != tof_bin_edges.cbegin()) { + const size_t bin_index = std::distance(tof_bin_edges.cbegin(), it) - 1; + // Calculate the x and y indices in the 2D histogram - int x = static_cast(entry->iGetX() * super_resolution); - int y = static_cast(entry->iGetY() * super_resolution); - + const int x = std::round(entry->iGetX() * super_resolution); + const int y = std::round(entry->iGetY() * super_resolution); + // Ensure x and y are within bounds if (x >= 0 && x < dim_x && y >= 0 && y < dim_y) { // Increment the count in the appropriate bin and position @@ -255,8 +257,8 @@ std::vector>> timedCreateTOFImages( } } - auto end = std::chrono::high_resolution_clock::now(); - auto elapsed = std::chrono::duration_cast(end - start).count(); + const auto end = std::chrono::high_resolution_clock::now(); + const auto elapsed = std::chrono::duration_cast(end - start).count(); spdlog::info("TOF image creation time: {} s", elapsed / 1e6); spdlog::info("Total entries: {}, Binned entries: {}", total_entries, binned_entries); @@ -265,7 +267,7 @@ std::vector>> timedCreateTOFImages( /** * @brief Timed save TOF imaging to TIFF. - * + * * @param[in] out_tof_imaging * @param[in] batches * @param[in] tof_bin_edges @@ -273,7 +275,7 @@ std::vector>> timedCreateTOFImages( */ void timedSaveTOFImagingToTIFF( const std::string& out_tof_imaging, - const std::vector>>& tof_images, + const std::vector>>& tof_images, const std::vector& tof_bin_edges, const std::string& tof_filename_base) { @@ -286,7 +288,7 @@ void timedSaveTOFImagingToTIFF( } // 2. Initialize vector for spectral data - std::vector spectral_counts(tof_images.size(), 0); + std::vector spectral_counts(tof_images.size(), 0); // 3. Iterate through each TOF bin and save TIFF files tbb::parallel_for(tbb::blocked_range(0, tof_images.size()), [&](const tbb::blocked_range& range) { @@ -295,9 +297,9 @@ void timedSaveTOFImagingToTIFF( std::string filename = fmt::format("{}/{}_bin_{:04d}.tiff", out_tof_imaging, tof_filename_base, bin + 1); // prepare container and fill with current hist2d - uint32_t width = tof_images[bin][0].size(); - uint32_t height = tof_images[bin].size(); - std::vector> accumulated_image = tof_images[bin]; + const uint32_t width = tof_images[bin][0].size(); + const uint32_t height = tof_images[bin].size(); + std::vector> accumulated_image = tof_images[bin]; // check if file already exist if (std::filesystem::exists(filename)) { @@ -310,7 +312,7 @@ void timedSaveTOFImagingToTIFF( if (existing_width == width && existing_height == height) { // Dimensions match, proceed with accumulation for (uint32_t row = 0; row < height; ++row) { - std::vector scanline(width); + std::vector scanline(width); TIFFReadScanline(existing_tif, scanline.data(), row); for (uint32_t col = 0; col < width; ++col) { accumulated_image[row][col] += scanline[col]; From f2c986e78a044eba1f24fbe64af5d1da42717599 Mon Sep 17 00:00:00 2001 From: Chen Zhang Date: Mon, 26 Aug 2024 11:40:15 -0400 Subject: [PATCH 36/38] address review cmt on cp vector --- sophiread/SophireadCLI/src/sophiread_core.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sophiread/SophireadCLI/src/sophiread_core.cpp b/sophiread/SophireadCLI/src/sophiread_core.cpp index 03ad2bd..5170019 100644 --- a/sophiread/SophireadCLI/src/sophiread_core.cpp +++ b/sophiread/SophireadCLI/src/sophiread_core.cpp @@ -124,8 +124,8 @@ void timedSaveHitsToHDF5(const std::string &out_hits, std::vector &batches // move all hits into a single vector std::vector hits; for (const auto &tpx3 : batches) { - auto tpx3_hits = tpx3.hits; - hits.insert(hits.end(), tpx3_hits.begin(), tpx3_hits.end()); + const auto &tpx3_hits = tpx3.hits; + hits.insert(hits.end(), tpx3_hits.cbegin(), tpx3_hits.cend()); } // save hits to HDF5 file saveHitsToHDF5(out_hits, hits); @@ -145,8 +145,8 @@ void timedSaveEventsToHDF5(const std::string &out_events, std::vector &bat // move all events into a single vector std::vector events; for (const auto &tpx3 : batches) { - auto tpx3_events = tpx3.neutrons; - events.insert(events.end(), tpx3_events.begin(), tpx3_events.end()); + const auto &tpx3_events = tpx3.neutrons; + events.insert(events.end(), tpx3_events.cbegin(), tpx3_events.cend()); } // save events to HDF5 file saveNeutronToHDF5(out_events, events); From bbba359322e56035a67bb04f2b68e26a3626e12a Mon Sep 17 00:00:00 2001 From: Chen Zhang Date: Mon, 26 Aug 2024 11:47:27 -0400 Subject: [PATCH 37/38] address review comments --- sophiread/SophireadCLI/src/sophiread.cpp | 2 +- sophiread/SophireadCLI/src/sophiread_core.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/sophiread/SophireadCLI/src/sophiread.cpp b/sophiread/SophireadCLI/src/sophiread.cpp index 5b76066..b2b4f0a 100644 --- a/sophiread/SophireadCLI/src/sophiread.cpp +++ b/sophiread/SophireadCLI/src/sophiread.cpp @@ -99,7 +99,7 @@ ProgramOptions parse_arguments(int argc, char* argv[]) { break; default: print_usage(argv[0]); - throw std::runtime_error("Invalid argument"); + throw std::runtime_error(std::string("Invalid argument: ") + static_cast(optopt)); } } diff --git a/sophiread/SophireadCLI/src/sophiread_core.cpp b/sophiread/SophireadCLI/src/sophiread_core.cpp index 5170019..dac4ca4 100644 --- a/sophiread/SophireadCLI/src/sophiread_core.cpp +++ b/sophiread/SophireadCLI/src/sophiread_core.cpp @@ -332,8 +332,8 @@ void timedSaveTOFImagingToTIFF( // Write or update TIFF file TIFF* tif = TIFFOpen(filename.c_str(), "w"); if (tif) { - uint32_t width = tof_images[bin][0].size(); - uint32_t height = tof_images[bin].size(); + const uint32_t width = tof_images[bin][0].size(); + const uint32_t height = tof_images[bin].size(); TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, width); TIFFSetField(tif, TIFFTAG_IMAGELENGTH, height); From d1e8bbcc6da34fc5057bd55518d25817b686db93 Mon Sep 17 00:00:00 2001 From: Chen Zhang Date: Mon, 26 Aug 2024 13:21:26 -0400 Subject: [PATCH 38/38] switch to const as suggested --- sophiread/SophireadCLI/src/sophiread_core.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sophiread/SophireadCLI/src/sophiread_core.cpp b/sophiread/SophireadCLI/src/sophiread_core.cpp index dac4ca4..41cb203 100644 --- a/sophiread/SophireadCLI/src/sophiread_core.cpp +++ b/sophiread/SophireadCLI/src/sophiread_core.cpp @@ -354,9 +354,9 @@ void timedSaveTOFImagingToTIFF( } // Accumulate counts for spectral file - spectral_counts[bin] = std::accumulate(accumulated_image.begin(), accumulated_image.end(), 0ULL, - [](unsigned long long sum, const std::vector& row) { - return sum + std::accumulate(row.begin(), row.end(), 0ULL); + spectral_counts[bin] = std::accumulate(accumulated_image.cbegin(), accumulated_image.cend(), static_cast(0), + [](const auto sum, const auto & row) { + return sum + std::accumulate(row.cbegin(), row.cend(), 0ULL); }); } });