diff --git a/RELEASE.md b/RELEASE.md index 88686f53..34e82535 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -1,5 +1,9 @@ # Release notes +### New Features: + +- Added ``read_columns`` to JungfrauDataFile to only read selected columns + ## 2026.9.30 ### New Features: diff --git a/include/aare/JungfrauDataFile.hpp b/include/aare/JungfrauDataFile.hpp index fe74d5e3..a84b5da1 100644 --- a/include/aare/JungfrauDataFile.hpp +++ b/include/aare/JungfrauDataFile.hpp @@ -62,6 +62,14 @@ class JungfrauDataFile : public FileInterface { // Extra functions needed for FileInterface Frame read_frame() override; Frame read_frame(size_t frame_number) override; + /** + * @brief Read the columns at the current frame + * @param column_indices vector of column indices to read + * @pre column_indices must be sorted in ascending order + * @return Frame containing the columns at the current frame + */ + Frame read_columns(const std::vector column_indices); + std::vector read_n(size_t n_frames = 0) override; void read_into(std::byte *image_buf) override; void read_into(std::byte *image_buf, size_t n_frames) override; diff --git a/python/src/jungfrau_data_file.hpp b/python/src/jungfrau_data_file.hpp index 44ecd90c..ae8bc54b 100644 --- a/python/src/jungfrau_data_file.hpp +++ b/python/src/jungfrau_data_file.hpp @@ -78,6 +78,24 @@ void define_jungfrau_data_file_io_bindings(py::module &m) { R"( Read a single frame from the file. )") + .def( + "read_columns", + [](JungfrauDataFile &self, + const std::vector column_indices) { + return self.read_columns(column_indices); + }, + R"( + Read the columns at the current frame. + Parameters + ---------- + column_indices : list of int + The indices of the columns to read. + Returns + ------- + Frame : Frame + The columns read from the file. + ) + )") .def("read_n", &read_n_dat_frames, R"( Read maximum n_frames frames from the file. diff --git a/src/JungfrauDataFile.cpp b/src/JungfrauDataFile.cpp index 34820b8b..d876037c 100644 --- a/src/JungfrauDataFile.cpp +++ b/src/JungfrauDataFile.cpp @@ -35,6 +35,43 @@ Frame JungfrauDataFile::read_frame(size_t frame_number) { return f; } +Frame JungfrauDataFile::read_columns(const std::vector column_indices) { + + Frame f(rows(), column_indices.size(), Dtype::UINT16); + + auto image_buf = reinterpret_cast(f.data()); + + m_fp.seek(header_size, SEEK_CUR); // skip header + + auto current_position = m_fp.tell(); + + for (size_t row = 0; row < rows(); ++row) { + const auto row_start = + current_position + row * cols() * sizeof(pixel_type); + for (const auto column_index : column_indices) { + m_fp.seek(row_start + column_index * sizeof(pixel_type), SEEK_SET); + if (auto rc = fread(image_buf, 1, sizeof(pixel_type), m_fp.get()); + rc != sizeof(pixel_type)) { + throw std::runtime_error(LOCATION + + "Could not read image from file" + + m_fp.error_msg()); + } + image_buf += sizeof(pixel_type); + } + } + + // prepare for next read + // if we are at the end of the file, open the next file + ++m_current_frame_index; + if (m_current_frame_index >= m_last_frame_in_file[m_current_file_index] && + (m_current_frame_index < m_total_frames)) { + ++m_current_file_index; + open_file(m_current_file_index); + } + + return f; +} + std::vector JungfrauDataFile::read_n(size_t n_frames) { std::vector frames; for (size_t i = 0; i < n_frames; ++i) { diff --git a/src/JungfrauDataFile.test.cpp b/src/JungfrauDataFile.test.cpp index ca409b1f..bcc3da46 100644 --- a/src/JungfrauDataFile.test.cpp +++ b/src/JungfrauDataFile.test.cpp @@ -1,12 +1,13 @@ // SPDX-License-Identifier: MPL-2.0 #include "aare/JungfrauDataFile.hpp" +#include "jungfrau_file_helpers.hpp" #include "test_config.hpp" #include using aare::JungfrauDataFile; using aare::JungfrauDataHeader; -TEST_CASE("Open a Jungfrau data file", "[.with-data]") { +TEST_CASE("Open a Jungfrau data file", "[.with-data][jungfrau_file]") { // we know we have 4 files with 7, 7, 7, and 3 frames // firs frame number if 1 and the bunch id is frame_number**2 // so we can check the header @@ -38,7 +39,7 @@ TEST_CASE("Open a Jungfrau data file", "[.with-data]") { } } -TEST_CASE("Seek in a JungfrauDataFile", "[.with-data]") { +TEST_CASE("Seek in a JungfrauDataFile", "[.with-data][jungfrau_file]") { auto fpath = test_data_path() / "dat" / "AldoJF65k_000000.dat"; REQUIRE(std::filesystem::exists(fpath)); @@ -72,7 +73,7 @@ TEST_CASE("Seek in a JungfrauDataFile", "[.with-data]") { } TEST_CASE("Open a Jungfrau data file with non zero file index", - "[.with-data]") { + "[.with-data][jungfrau_file]") { auto fpath = test_data_path() / "dat" / "AldoJF65k_000003.dat"; REQUIRE(std::filesystem::exists(fpath)); @@ -96,7 +97,8 @@ TEST_CASE("Open a Jungfrau data file with non zero file index", REQUIRE(f.current_file().stem() == "AldoJF65k_000003"); } -TEST_CASE("Read into throws if size doesn't match", "[.with-data]") { +TEST_CASE("Read into throws if size doesn't match", + "[.with-data][jungfrau_file]") { auto fpath = test_data_path() / "dat" / "AldoJF65k_000000.dat"; REQUIRE(std::filesystem::exists(fpath)); @@ -110,4 +112,36 @@ TEST_CASE("Read into throws if size doesn't match", "[.with-data]") { REQUIRE_THROWS(f.read_into(&image)); REQUIRE(f.tell() == 0); +} + +TEST_CASE("Read columns from a Jungfrau data file", "[jungfrau_file]") { + + TemporaryJungfrauFiles temp_files(1, []([[maybe_unused]] const size_t) { + aare::NDArray data( + {aare::Jungfrau::nRows, aare::Jungfrau::nCols}); + + for (ssize_t row = 0; row < data.shape(0); ++row) { + for (ssize_t col = 0; col < data.shape(1); ++col) { + data(row, col) = static_cast(col); + } + } + return data; + }); + + const std::filesystem::path path = temp_files.file_path(); + + JungfrauDataFile f(path); + + auto columns = f.read_columns({0, 10, 30}); + + REQUIRE(columns.rows() == aare::Jungfrau::nRows); + REQUIRE(columns.cols() == 3); + + auto columns_view = columns.view(); + + for (ssize_t row = 0; row < columns.rows(); ++row) { + CHECK(columns_view(row, 0) == 0); + CHECK(columns_view(row, 1) == 10); + CHECK(columns_view(row, 2) == 30); + } } \ No newline at end of file