diff --git a/sophiread/SophireadCLI/include/sophiread_core.h b/sophiread/SophireadCLI/include/sophiread_core.h index 5d8fd1c7..0efd87a0 100644 --- a/sophiread/SophireadCLI/include/sophiread_core.h +++ b/sophiread/SophireadCLI/include/sophiread_core.h @@ -46,4 +46,9 @@ void updateTOFImages( std::vector>>& tof_images, const TPX3& batch, double super_resolution, const std::vector& tof_bin_edges, const std::string& mode); +std::vector calculateSpectralCounts( + const std::vector>>& tof_images); +void writeSpectralFile(const std::string& filename, + const std::vector& spectral_counts, + const std::vector& tof_bin_edges); } // namespace sophiread diff --git a/sophiread/SophireadCLI/src/sophiread.cpp b/sophiread/SophireadCLI/src/sophiread.cpp index c4e44726..708dad69 100644 --- a/sophiread/SophireadCLI/src/sophiread.cpp +++ b/sophiread/SophireadCLI/src/sophiread.cpp @@ -37,6 +37,7 @@ struct ProgramOptions { std::string output_tof_imaging; std::string tof_filename_base = "tof_image"; std::string tof_mode = "neutron"; + std::string spectra_filen = "Spectra"; size_t chunk_size = 5ULL * 1024 * 1024 * 1024; // Default 5GB bool debug_logging = false; bool verbose = false; @@ -68,6 +69,7 @@ void print_usage(const char* program_name) { spdlog::info( " -m TOF mode: 'hit' or 'neutron' (default: " "neutron)"); + spdlog::info(" -s Output filename for spectra"); spdlog::info(" -c Chunk size in MB (default: 5120)"); spdlog::info(" -d Enable debug logging"); spdlog::info(" -v Enable verbose logging"); @@ -83,7 +85,7 @@ ProgramOptions parse_arguments(int argc, char* argv[]) { ProgramOptions options; int opt; - while ((opt = getopt(argc, argv, "i:H:E:u:T:f:m:c:dv")) != -1) { + while ((opt = getopt(argc, argv, "i:H:E:u:T:f:m:s:c:dv")) != -1) { switch (opt) { case 'i': options.input_tpx3 = optarg; @@ -106,6 +108,9 @@ ProgramOptions parse_arguments(int argc, char* argv[]) { case 'm': options.tof_mode = optarg; break; + case 's': + options.spectra_filen = optarg; + break; case 'c': options.chunk_size = static_cast(std::stoull(optarg)) * 1024 * 1024; // Convert MB to bytes @@ -210,7 +215,9 @@ int main(int argc, char* argv[]) { // Initialize TOF images if needed std::vector>> tof_images; - if (!options.output_tof_imaging.empty()) { + const bool needs_tof_images = + !options.output_tof_imaging.empty() || !options.spectra_filen.empty(); + if (needs_tof_images) { tof_images = sophiread::initializeTOFImages(config->getSuperResolution(), config->getTOFBinEdges()); } @@ -258,7 +265,7 @@ int main(int argc, char* argv[]) { } // Update TOF images - if (!options.output_tof_imaging.empty()) { + if (needs_tof_images) { sophiread::updateTOFImages( tof_images, batch, config->getSuperResolution(), config->getTOFBinEdges(), options.tof_mode); @@ -293,13 +300,6 @@ int main(int argc, char* argv[]) { eventsFile.close(); } - // Save TOF images if needed - if (!options.output_tof_imaging.empty()) { - sophiread::timedSaveTOFImagingToTIFF(options.output_tof_imaging, - tof_images, config->getTOFBinEdges(), - options.tof_filename_base); - } - auto end = std::chrono::high_resolution_clock::now(); auto elapsed = std::chrono::duration_cast(end - start) @@ -309,6 +309,25 @@ int main(int argc, char* argv[]) { spdlog::info("Total hits: {}", totalHits); spdlog::info("Total neutrons: {}", totalNeutrons); + // Save TOF images if needed + if (!options.output_tof_imaging.empty()) { + spdlog::info("Saving TOF imaging to TIFF: {}", + options.output_tof_imaging); + sophiread::timedSaveTOFImagingToTIFF(options.output_tof_imaging, + tof_images, config->getTOFBinEdges(), + options.tof_filename_base); + } + + // Save spectra if needed + if (!options.spectra_filen.empty()) { + spdlog::info("Saving spectra to file: {}", options.spectra_filen); + std::string spectral_filename = options.spectra_filen + ".txt"; + std::vector spectral_counts = + sophiread::calculateSpectralCounts(tof_images); + sophiread::writeSpectralFile(spectral_filename, spectral_counts, + config->getTOFBinEdges()); + } + } catch (const std::exception& e) { spdlog::error("Error: {}", e.what()); return 1; diff --git a/sophiread/SophireadCLI/src/sophiread_core.cpp b/sophiread/SophireadCLI/src/sophiread_core.cpp index d716f06e..270497fb 100644 --- a/sophiread/SophireadCLI/src/sophiread_core.cpp +++ b/sophiread/SophireadCLI/src/sophiread_core.cpp @@ -365,10 +365,7 @@ void timedSaveTOFImagingToTIFF( 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 + // 2. Iterate through each TOF bin and save TIFF files tbb::parallel_for( tbb::blocked_range(0, tof_images.size()), [&](const tbb::blocked_range &range) { @@ -439,21 +436,42 @@ void timedSaveTOFImagingToTIFF( } else { spdlog::error("Failed to open TIFF file for writing: {}", filename); } - - // Accumulate counts for spectral file - 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); - }); } }); + // 3. Calculate spectral counts + std::vector spectral_counts = calculateSpectralCounts(tof_images); // 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); + writeSpectralFile(spectral_filename, spectral_counts, tof_bin_edges); + + 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()); +} + +std::vector calculateSpectralCounts( + const std::vector>> &tof_images) { + std::vector spectral_counts(tof_images.size(), 0); + + for (size_t bin = 0; bin < tof_images.size(); ++bin) { + spectral_counts[bin] = std::accumulate( + tof_images[bin].cbegin(), tof_images[bin].cend(), + static_cast(0), [](const auto sum, const auto &row) { + return sum + std::accumulate(row.cbegin(), row.cend(), 0ULL); + }); + } + + return spectral_counts; +} + +void writeSpectralFile(const std::string &filename, + const std::vector &spectral_counts, + const std::vector &tof_bin_edges) { + std::ofstream spectral_file(filename); if (spectral_file.is_open()) { spectral_file << "shutter_time,counts\n"; for (size_t bin = 0; bin < tof_bin_edges.size() - 1; ++bin) { @@ -461,17 +479,10 @@ void timedSaveTOFImagingToTIFF( << "\n"; } spectral_file.close(); - spdlog::info("Wrote spectral file: {}", spectral_filename); + spdlog::info("Wrote spectral file: {}", filename); } else { - spdlog::error("Failed to open spectra file for writing: {}", - spectral_filename); + spdlog::error("Failed to open spectra file for writing: {}", 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()); } } // namespace sophiread \ No newline at end of file diff --git a/sophiread/SophireadCLI/tests/test_sophiread_core.cpp b/sophiread/SophireadCLI/tests/test_sophiread_core.cpp index 53ad6226..763dd170 100644 --- a/sophiread/SophireadCLI/tests/test_sophiread_core.cpp +++ b/sophiread/SophireadCLI/tests/test_sophiread_core.cpp @@ -150,6 +150,23 @@ TEST_F(SophireadCoreTest, TimedSaveTOFImagingToTIFF) { 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")); + + // Check spectra file contents + std::ifstream spectra_file("test_tof/test_Spectra.txt"); + std::string line; + std::vector lines; + while (std::getline(spectra_file, line)) { + lines.push_back(line); + } + // close the file + spectra_file.close(); + + EXPECT_EQ(lines.size(), 4); // Header + 3 data lines + EXPECT_EQ(lines[0], "shutter_time,counts"); + EXPECT_EQ(lines[1], "0.1,100"); // 10 * 10 * 1 = 100 counts for each bin + EXPECT_EQ(lines[2], "0.2,100"); + EXPECT_EQ(lines[3], "0.3,100"); + std::filesystem::remove_all("test_tof"); } @@ -180,6 +197,46 @@ TEST_F(SophireadCoreTest, UpdateTOFImages) { EXPECT_TRUE(updated); } +TEST_F(SophireadCoreTest, CalculateSpectralCounts) { + std::vector>> tof_images( + 3, std::vector>( + 5, std::vector( + 5, 2))); // 3 bins, 5x5 images, all values 2 + + auto spectral_counts = sophiread::calculateSpectralCounts(tof_images); + + EXPECT_EQ(spectral_counts.size(), 3); // 3 bins + for (const auto& count : spectral_counts) { + EXPECT_EQ(count, 50); // 5 * 5 * 2 = 50 for each bin + } +} + +TEST_F(SophireadCoreTest, WriteSpectralFile) { + std::vector spectral_counts = {10, 20, 30}; + std::vector tof_bin_edges = {0.0, 0.1, 0.2, 0.3}; + std::string filename = "test_spectra.txt"; + + sophiread::writeSpectralFile(filename, spectral_counts, tof_bin_edges); + + EXPECT_TRUE(std::filesystem::exists(filename)); + + // Read and check file contents + std::ifstream file(filename); + std::string line; + std::vector lines; + while (std::getline(file, line)) { + lines.push_back(line); + } + + EXPECT_EQ(lines.size(), 4); // Header + 3 data lines + EXPECT_EQ(lines[0], "shutter_time,counts"); + EXPECT_EQ(lines[1], "0.1,10"); + EXPECT_EQ(lines[2], "0.2,20"); + EXPECT_EQ(lines[3], "0.3,30"); + + std::filesystem::remove(filename); +} + int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS();