solved merge conflict

This commit is contained in:
mazzol_a committed 2026-10-06 16:07:23 +02:00
commit 9a0fa9cdc4
52 files changed
+401 -175

No files matched your search

+1 -1
View File
@@ -17,7 +17,7 @@ jobs:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest,macos-latest]
os: [ubuntu-latest, macos-latest, windows-latest]
steps:
- uses: actions/checkout@v4
+37
View File
@@ -0,0 +1,37 @@
name: Build and test on Windows
on:
workflow_dispatch:
push:
pull_request:
jobs:
build:
runs-on: windows-latest
steps:
- uses: actions/checkout@v6
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Install Python dependencies
run: python -m pip install ninja numpy matplotlib pytest pytest-check boost-histogram
# Puts cl.exe on the path so that CMake can use the Ninja generator
- uses: ilammy/msvc-dev-cmd@v1
- name: Configure
run: cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release -DAARE_TESTS=ON -DAARE_PYTHON_BINDINGS=ON -DAARE_WARNINGS_AS_ERRORS=ON
- name: Build
run: cmake --build build
- name: C++ unit tests
run: ctest --test-dir build --output-on-failure -j4
- name: Python unit tests
env:
PYTHONPATH: ${{ github.workspace }}\build
run: python -m pytest python/tests
+39 -21
View File
@@ -208,6 +208,11 @@ if(AARE_FETCH_FMT)
CACHE BOOL "")
FetchContent_MakeAvailable(fmt)
set_property(TARGET fmt PROPERTY POSITION_INDEPENDENT_CODE ON)
if(MSVC)
# fmt 12.1.0 calls std::isalpha only to check that ADL finds it and drops
# the result, which MSVC's standard library marks [[nodiscard]]
target_compile_options(fmt PRIVATE /wd4834)
endif()
else()
find_package(fmt 8 REQUIRED)
endif()
@@ -254,18 +259,30 @@ target_compile_features(aare_compiler_flags INTERFACE cxx_std_17)
# MSVC specific #
# ##############################################################################
if(MSVC)
add_compile_definitions(AARE_MSVC)
if(CMAKE_BUILD_TYPE STREQUAL "Release")
message(STATUS "Release build")
target_compile_options(aare_compiler_flags INTERFACE /O2)
else()
message(STATUS "Debug build")
target_compile_options(aare_compiler_flags
INTERFACE /Od /Zi /MDd /D_ITERATOR_DEBUG_LEVEL=2)
target_link_options(aare_compiler_flags INTERFACE /DEBUG:FULL)
endif()
target_compile_options(aare_compiler_flags INTERFACE /w # disable warnings
# CMake sets the per-configuration optimisation, debug-info and
# runtime-library flags itself, so only warnings and conformance here.
target_compile_options(
aare_compiler_flags
INTERFACE /W4 # -Wall -Wextra
/permissive- # standard conformance, like CMAKE_CXX_EXTENSIONS OFF
/utf-8 # sources are UTF-8
/we4715 # not all control paths return a value, like
# -Werror=return-type
/wd4244 # narrowing conversions: -Wconversion is not enabled
/wd4267 # on gcc/clang either
/wd4068 # unknown pragma: the GCC diagnostic pragmas
/wd4324 # structure padded due to alignas: intended
)
target_compile_definitions(aare_compiler_flags
INTERFACE _CRT_SECURE_NO_WARNINGS)
if(AARE_WARNINGS_AS_ERRORS)
target_compile_options(aare_compiler_flags INTERFACE /WX)
endif()
if(AARE_ASAN)
message(WARNING "AARE_ASAN is not supported with MSVC. Ignoring.")
endif()
else()
# ############################################################################
@@ -318,22 +335,22 @@ else()
endif()
endif()
if(AARE_ASAN)
message(STATUS "AddressSanitizer enabled")
target_compile_options(
aare_compiler_flags INTERFACE -fsanitize=address,undefined,pointer-compare
-fno-omit-frame-pointer)
target_link_libraries(
aare_compiler_flags INTERFACE -fsanitize=address,undefined,pointer-compare
-fno-omit-frame-pointer)
endif()
endif() # GCC/Clang specific
if(AARE_PYTHON_BINDINGS)
add_subdirectory(python)
endif()
if(AARE_ASAN)
message(STATUS "AddressSanitizer enabled")
target_compile_options(
aare_compiler_flags INTERFACE -fsanitize=address,undefined,pointer-compare
-fno-omit-frame-pointer)
target_link_libraries(
aare_compiler_flags INTERFACE -fsanitize=address,undefined,pointer-compare
-fno-omit-frame-pointer)
endif()
if(AARE_TESTS)
enable_testing()
add_subdirectory(tests)
@@ -485,6 +502,7 @@ if(AARE_TESTS)
${CMAKE_CURRENT_SOURCE_DIR}/src/hist/PixelHistogram.test.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/hist/PedestalTrackingPixelHistogram.test.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/JungfrauDataFile.test.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/logger.test.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/MultiThreadedFileReader.test.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/NumpyFile.test.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/NumpyHelpers.test.cpp
+9 -1
View File
@@ -1,7 +1,15 @@
# Release notes
### New Features:
## Next:
### New Features:
- Added the Python ``xy`` type with ``row`` and ``col`` fields, enabling access
to ``RawFile.geometry``, ``RawMasterFile.detector_layout``, and
``RawMasterFile.udp_interfaces_per_module``. It unpacks as ``row, col``.
- Added Windows wheels (``win_amd64``, Python 3.12 to 3.14) to the wheel
build, so releases publish them to PyPI alongside the Linux and macOS
wheels.
- Added ``read_columns`` to JungfrauDataFile to only read selected columns
## 2026.9.30
+2
View File
@@ -10,6 +10,8 @@
#include <string>
#include <vector>
using aare::ssize_t;
struct TestCase {
std::string name;
double true_A;
+1
View File
@@ -3,6 +3,7 @@
#include <benchmark/benchmark.h>
using aare::NDArray;
using aare::ssize_t;
constexpr ssize_t size = 1024;
class TwoArrays : public benchmark::Fixture {
+3 -3
View File
@@ -83,7 +83,7 @@ struct Cluster {
data[cluster_center_index - ClusterSizeX] +
data[cluster_center_index - 1 - ClusterSizeX];
// subcluster top right from center
if (ClusterSizeX > 2) {
if constexpr (ClusterSizeX > 2) {
sum_2x2_subcluster[1] =
data[cluster_center_index] +
data[cluster_center_index + 1] +
@@ -91,7 +91,7 @@ struct Cluster {
data[cluster_center_index - ClusterSizeX + 1];
}
// subcluster bottom left from center
if (ClusterSizeY > 2) {
if constexpr (ClusterSizeY > 2) {
sum_2x2_subcluster[2] =
data[cluster_center_index] +
data[cluster_center_index - 1] +
@@ -99,7 +99,7 @@ struct Cluster {
data[cluster_center_index + ClusterSizeX - 1];
}
// subcluster bottom right from center
if (ClusterSizeX > 2 && ClusterSizeY > 2) {
if constexpr (ClusterSizeX > 2 && ClusterSizeY > 2) {
sum_2x2_subcluster[3] =
data[cluster_center_index] +
data[cluster_center_index + 1] +
+20 -1
View File
@@ -1,10 +1,29 @@
// SPDX-License-Identifier: MPL-2.0
#pragma once
#include "aare/defs.hpp"
#include <cstdint>
#include <cstdio>
#include <filesystem>
namespace aare {
// MSVC's long is 32 bits, so fseek/ftell stop working past 2 GB there
inline int fseek64(FILE *fp, int64_t offset, int whence) {
#ifdef _WIN32
return _fseeki64(fp, offset, whence);
#else
return fseek(fp, static_cast<long>(offset), whence);
#endif
}
inline int64_t ftell64(FILE *fp) {
#ifdef _WIN32
return _ftelli64(fp);
#else
return ftell(fp);
#endif
}
/**
* \brief RAII wrapper for FILE pointer
*/
@@ -22,7 +41,7 @@ class FilePtr {
FILE *get();
ssize_t tell();
void seek(ssize_t offset, int whence = SEEK_SET) {
if (fseek(fp_, offset, whence) != 0)
if (fseek64(fp_, offset, whence) != 0)
throw std::runtime_error("Error seeking in file");
}
std::string error_msg();
+2 -1
View File
@@ -2,6 +2,7 @@
#pragma once
#include "aare/Dtype.hpp"
#include "aare/FileInterface.hpp"
#include "aare/FilePtr.hpp"
#include "aare/NumpyHelpers.hpp"
#include "aare/defs.hpp"
@@ -77,7 +78,7 @@ class NumpyFile : public FileInterface {
*/
template <typename T, size_t NDim> NDArray<T, NDim> load() {
NDArray<T, NDim> arr(make_shape<NDim>(m_header.shape));
if (fseek(fp, static_cast<long>(header_size), SEEK_SET)) {
if (fseek64(fp, header_size, SEEK_SET)) {
throw std::runtime_error(LOCATION +
"Error seeking to the start of the data");
}
+17
View File
@@ -5,6 +5,7 @@
#include <array>
#include <cassert>
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <stdexcept>
@@ -13,6 +14,10 @@
#include <variant>
#include <vector>
#if !defined(_MSC_VER)
#include <sys/types.h> // ::ssize_t
#endif
/**
* @brief LOCATION macro to get the current location in the code
*/
@@ -54,6 +59,18 @@
namespace aare {
/**
* Signed size and index type used throughout the library (shapes, strides,
* frame indices, ROI coordinates). On POSIX this is the platform's ssize_t,
* so nothing changes for existing code. MSVC has no ssize_t, so there it is
* std::ptrdiff_t, which is the same type as Py_ssize_t and NumPy's npy_intp.
*/
#if defined(_MSC_VER)
using ssize_t = std::ptrdiff_t;
#else
using ssize_t = ::ssize_t;
#endif
inline constexpr size_t bits_per_byte = 8;
void assert_failed(const std::string &msg);
+49 -21
View File
@@ -2,9 +2,12 @@
#pragma once
/*Utility to log to console*/
#include <chrono>
#include <cstdio>
#include <ctime>
#include <iostream>
#include <sstream>
#include <sys/time.h>
#include <string>
namespace aare {
@@ -55,12 +58,53 @@ enum TLogLevel {
#define __AT__ \
std::string(__FILE__) + std::string("::") + std::string(__func__) + \
std::string("(): ")
#define __SHORT_FORM_OF_FILE__ \
(strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__)
#define __SHORT_FORM_OF_FILE__ aare::short_file_name(__FILE__)
#define __SHORT_AT__ \
std::string(__SHORT_FORM_OF_FILE__) + std::string("::") + \
std::string(__func__) + std::string("(): ")
/**
* @brief Strip the directory part of a path, accepting both '/' and '\\' as
* separators so that __FILE__ is shortened the same way on every platform.
*/
inline const char *short_file_name(const char *path) noexcept {
const char *base = path;
for (const char *p = path; *p != '\0'; ++p) {
if (*p == '/' || *p == '\\')
base = p + 1;
}
return base;
}
/**
* @brief Local wall-clock time as HH:MM:SS.mmm (strftime "%X" plus
* milliseconds), built from std::chrono so it works on every platform.
*/
inline std::string log_timestamp() {
using namespace std::chrono;
const auto now = system_clock::now();
const time_t t = system_clock::to_time_t(now);
const auto ms = duration_cast<milliseconds>(now.time_since_epoch()) % 1000;
tm local{};
#if defined(_MSC_VER)
localtime_s(&local, &t);
#else
localtime_r(&t, &local);
#endif
constexpr size_t buffer_len = 12;
char buffer[buffer_len];
if (strftime(buffer, buffer_len, "%X", &local) == 0)
buffer[0] = '\0';
constexpr size_t result_len = 100;
char result[result_len];
snprintf(result, result_len, "%s.%03d", buffer,
static_cast<int>(ms.count()));
return result;
}
class Logger {
std::ostringstream os;
TLogLevel m_level = AARE_LOG_LEVEL;
@@ -111,28 +155,12 @@ class Logger {
return os;
}
static std::string Timestamp() {
constexpr size_t buffer_len = 12;
char buffer[buffer_len];
time_t t;
::time(&t);
tm r;
strftime(buffer, buffer_len, "%X", localtime_r(&t, &r));
buffer[buffer_len - 1] = '\0';
struct timeval tv;
gettimeofday(&tv, nullptr);
constexpr size_t result_len = 100;
char result[result_len];
snprintf(result, result_len, "%s.%03ld", buffer,
static_cast<long>(tv.tv_usec) / 1000);
result[result_len - 1] = '\0';
return result;
}
static std::string Timestamp() { return log_timestamp(); }
};
// TODO! Do we need to keep the runtime option?
#define LOG(level) \
if (level > AARE_LOG_LEVEL) \
if constexpr (level > AARE_LOG_LEVEL) \
; \
else if (level > aare::Logger::ReportingLevel()) \
; \
+1 -1
View File
@@ -19,7 +19,7 @@ T string_to(const std::string &t, const std::string &unit) {
double tval{0};
try {
tval = std::stod(t);
} catch (const std::invalid_argument &e) {
} catch (const std::invalid_argument &) {
throw std::runtime_error("Could not convert string to time");
}
+3
View File
@@ -28,6 +28,9 @@ build = "cp{312,313,314}-manylinux_x86_64"
build = "cp{312,313,314}-macosx_*"
archs = ["arm64"]
[tool.cibuildwheel.windows]
build = "cp{312,313,314}-win_amd64"
[tool.scikit-build]
build.verbose = true
+1
View File
@@ -25,6 +25,7 @@ from ._aare import Pedestal_d, Pedestal_f, ClusterFinder_Cluster3x3i, VarCluster
from ._aare import DetectorType, FrameDiscardPolicy, ReadoutMode
from ._aare import hitmap
from ._aare import ROI
from ._aare import xy
from ._aare import corner
from ._aare import UDPPortPosition
-5
View File
@@ -13,9 +13,6 @@ namespace py = pybind11;
using namespace aare;
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter"
template <typename Type, uint8_t ClusterSizeX, uint8_t ClusterSizeY,
typename CoordType>
void define_Cluster(py::module &m, const std::string &typestr) {
@@ -140,5 +137,3 @@ void reduce_to_2x2(py::module &m) {
row-major order.
)doc");
}
#pragma GCC diagnostic pop
-5
View File
@@ -20,9 +20,6 @@ namespace py = pybind11;
using namespace aare;
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter"
template <typename T, uint8_t ClusterSizeX, uint8_t ClusterSizeY,
typename CoordType = uint16_t>
void define_ClusterCollector(py::module &m, const std::string &typestr) {
@@ -42,5 +39,3 @@ void define_ClusterCollector(py::module &m, const std::string &typestr) {
},
py::return_value_policy::take_ownership);
}
#pragma GCC diagnostic pop
+5 -12
View File
@@ -13,11 +13,6 @@
#include <string>
#include <utility>
// Disable warnings for unused parameters, as we ignore some
// in the __exit__ method
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter"
namespace py = pybind11;
using namespace ::aare;
@@ -171,14 +166,12 @@ void define_ClusterFile(py::module &m, const std::string &typestr) {
.def("__enter__", [](ClusterFile<ClusterType> &self) { return &self; })
.def("__exit__",
[](ClusterFile<ClusterType> &self,
const std::optional<pybind11::type> &exc_type,
const std::optional<pybind11::object> &exc_value,
const std::optional<pybind11::object> &traceback) {
self.close();
})
[[maybe_unused]] const std::optional<pybind11::type> &exc_type,
[[maybe_unused]] const std::optional<pybind11::object>
&exc_value,
[[maybe_unused]] const std::optional<pybind11::object>
&traceback) { self.close(); })
.def("__iter__", [](ClusterFile<ClusterType> &self) { return &self; })
.def("__next__",
[](File &self) { return ChunkIterator(self.chunks()).next(); });
}
#pragma GCC diagnostic pop
-5
View File
@@ -17,9 +17,6 @@
namespace py = pybind11;
using namespace aare;
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter"
template <typename T, uint8_t ClusterSizeX, uint8_t ClusterSizeY,
typename CoordType = uint16_t>
void define_ClusterFileSink(py::module &m, const std::string &typestr) {
@@ -34,5 +31,3 @@ void define_ClusterFileSink(py::module &m, const std::string &typestr) {
const std::filesystem::path &>())
.def("stop", &ClusterFileSink<ClusterType>::stop);
}
#pragma GCC diagnostic pop
-5
View File
@@ -20,9 +20,6 @@ namespace py = pybind11;
using namespace aare;
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter"
template <typename T, uint8_t ClusterSizeX, uint8_t ClusterSizeY,
typename CoordType = uint16_t>
void define_ClusterFinder(py::module &m, const std::string &typestr) {
@@ -92,5 +89,3 @@ void define_ClusterFinder(py::module &m, const std::string &typestr) {
},
py::arg("frame").noconvert(), py::arg("frame_number") = 0);
}
#pragma GCC diagnostic pop
+2 -6
View File
@@ -20,9 +20,6 @@ namespace py = pybind11;
using namespace aare;
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter"
template <typename T, uint8_t ClusterSizeX, uint8_t ClusterSizeY,
typename CoordType = uint16_t>
void define_ClusterFinderMT(py::module &m, const std::string &typestr) {
@@ -55,7 +52,8 @@ void define_ClusterFinderMT(py::module &m, const std::string &typestr) {
py::arg("frame").noconvert(), py::arg("frame_number") = 0)
.def_property_readonly(
"cluster_size",
[](ClusterFinderMT<ClusterType, uint16_t, pd_type> &self) {
[]([[maybe_unused]] ClusterFinderMT<ClusterType, uint16_t, pd_type>
&self) {
return py::make_tuple(ClusterSizeX, ClusterSizeY);
})
.def("clear_pedestal",
@@ -93,5 +91,3 @@ void define_ClusterFinderMT(py::module &m, const std::string &typestr) {
py::arg("nSigma"),
R"(sets the number of sigma for all cluster finders.)");
}
#pragma GCC diagnostic pop
+7 -12
View File
@@ -18,9 +18,6 @@ namespace py = pybind11;
using namespace aare;
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter"
template <typename Type, uint8_t ClusterSizeX, uint8_t ClusterSizeY,
typename CoordType = uint16_t>
void define_ClusterVector(py::module &m, const std::string &typestr) {
@@ -123,8 +120,8 @@ void define_ClusterVector(py::module &m, const std::string &typestr) {
"padding.")
.def_property_readonly(
"fmt",
[typestr](ClusterVector<ClusterType> &self) {
return fmt_format<ClusterType>;
[typestr]([[maybe_unused]] ClusterVector<ClusterType> &self) {
return fmt_format<ClusterType>();
},
"PEP 3118 format string for one stored cluster.")
@@ -145,11 +142,11 @@ void define_ClusterVector(py::module &m, const std::string &typestr) {
.def_buffer(
[typestr](ClusterVector<ClusterType> &self) -> py::buffer_info {
return py::buffer_info(
self.data(), /* Pointer to buffer */
self.item_size(), /* Size of one scalar */
fmt_format<ClusterType>, /* Format descriptor */
1, /* Number of dimensions */
{self.size()}, /* Buffer dimensions */
self.data(), /* Pointer to buffer */
self.item_size(), /* Size of one scalar */
fmt_format<ClusterType>(), /* Format descriptor */
1, /* Number of dimensions */
{self.size()}, /* Buffer dimensions */
{self.item_size()} /* Strides (in bytes) for each index */
);
});
@@ -228,5 +225,3 @@ void define_3x3_reduction(py::module &m) {
)doc",
py::arg("clustervector"));
}
#pragma GCC diagnostic pop
+16
View File
@@ -1,4 +1,5 @@
// SPDX-License-Identifier: MPL-2.0
#include <pybind11/operators.h>
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
@@ -77,6 +78,21 @@ void define_defs_bindings(py::module &m) {
self.ymin == other.ymin && self.ymax == other.ymax;
});
py::class_<xy>(m, "xy")
.def(py::init<uint32_t, uint32_t>(), py::arg("row"), py::arg("col"))
.def_readwrite("row", &xy::row)
.def_readwrite("col", &xy::col)
.def("__repr__",
[](const xy &self) {
return fmt::format("xy(row={}, col={})", self.row, self.col);
})
.def("__iter__",
[](const xy &self) {
return py::iter(py::make_tuple(self.row, self.col));
})
.def(py::self == py::self)
.def(py::self != py::self);
py::enum_<DetectorType>(m, "DetectorType")
.value("Jungfrau", DetectorType::Jungfrau)
.value("Eiger", DetectorType::Eiger)
@@ -16,6 +16,7 @@
#include <pybind11/stl/filesystem.h>
namespace py = pybind11;
using aare::ssize_t;
inline py::dtype multi_threaded_reader_numpy_dtype(const aare::Dtype &dtype) {
using aare::Dtype;
+8 -1
View File
@@ -318,7 +318,14 @@ void define_raw_file_io_bindings(py::module &m) {
Cols for the given ROI.
)")
.def_property_readonly("bitdepth", &RawFile::bitdepth)
.def_property_readonly("geometry", &RawFile::geometry)
.def_property_readonly("geometry", &RawFile::geometry, R"(
Number of modules along each axis of the detector.
Returns
-------
xy
Module rows and columns.
)")
.def_property_readonly("detector_type", &RawFile::detector_type)
.def_property_readonly("master", &RawFile::master)
.def_property_readonly("n_modules", &RawFile::n_modules)
+1
View File
@@ -9,6 +9,7 @@
#include <pybind11/stl_bind.h>
namespace py = pybind11;
using aare::ssize_t;
template <typename DataType>
py::array_t<DataType>
+7 -11
View File
@@ -21,11 +21,6 @@
namespace py = pybind11;
using namespace ::aare;
// Disable warnings for unused parameters, as we ignore some
// in the __exit__ method
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter"
void define_file_io_bindings(py::module &m) {
PYBIND11_NUMPY_DTYPE(DetectorHeader, frameNumber, expLength, packetNumber,
@@ -122,9 +117,12 @@ void define_file_io_bindings(py::module &m) {
})
.def("__enter__", [](File &self) { return &self; })
.def("__exit__",
[](File &self, const std::optional<pybind11::type> &exc_type,
const std::optional<pybind11::object> &exc_value,
const std::optional<pybind11::object> &traceback) {
[]([[maybe_unused]] File &self,
[[maybe_unused]] const std::optional<pybind11::type> &exc_type,
[[maybe_unused]] const std::optional<pybind11::object>
&exc_value,
[[maybe_unused]] const std::optional<pybind11::object>
&traceback) {
// self.close();
})
.def("__iter__", [](File &self) { return &self; })
@@ -146,7 +144,7 @@ void define_file_io_bindings(py::module &m) {
self.read_into(
reinterpret_cast<std::byte *>(image.mutable_data()));
return image;
} catch (std::runtime_error &e) {
} catch (std::runtime_error &) {
throw py::stop_iteration();
}
});
@@ -160,6 +158,4 @@ void define_file_io_bindings(py::module &m) {
.def_property_readonly("start", &ScanParameters::start)
.def_property_readonly("stop", &ScanParameters::stop)
.def_property_readonly("step", &ScanParameters::step);
#pragma GCC diagnostic pop
}
+1
View File
@@ -11,6 +11,7 @@
namespace py = pybind11;
using namespace pybind11::literals;
using aare::ssize_t;
template <typename Model>
py::object
+7 -12
View File
@@ -15,11 +15,6 @@
namespace py = pybind11;
using namespace ::aare;
// Disable warnings for unused parameters, as we ignore some
// in the __exit__ method
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter"
auto read_dat_frame(JungfrauDataFile &self) {
py::array_t<JungfrauDataHeader> header(1);
py::array_t<uint16_t> image({self.rows(), self.cols()});
@@ -119,20 +114,20 @@ void define_jungfrau_data_file_io_bindings(py::module &m) {
)")
.def("__enter__", [](JungfrauDataFile &self) { return &self; })
.def("__exit__",
[](JungfrauDataFile &self,
const std::optional<pybind11::type> &exc_type,
const std::optional<pybind11::object> &exc_value,
const std::optional<pybind11::object> &traceback) {
[]([[maybe_unused]] JungfrauDataFile &self,
[[maybe_unused]] const std::optional<pybind11::type> &exc_type,
[[maybe_unused]] const std::optional<pybind11::object>
&exc_value,
[[maybe_unused]] const std::optional<pybind11::object>
&traceback) {
// self.close();
})
.def("__iter__", [](JungfrauDataFile &self) { return &self; })
.def("__next__", [](JungfrauDataFile &self) {
try {
return read_dat_frame(self);
} catch (std::runtime_error &e) {
} catch (std::runtime_error &) {
throw py::stop_iteration();
}
});
}
#pragma GCC diagnostic pop
+7 -2
View File
@@ -112,8 +112,13 @@ struct fmt_format_trait<Cluster<T, ClusterSizeX, ClusterSizeY, CoordType>> {
}
};
template <typename ClusterType>
auto fmt_format = fmt_format_trait<ClusterType>::value();
// Function-local static rather than a variable template: MSVC does not run
// the dynamic initializer of a variable template instantiated from inside
// another template, which left the format string empty.
template <typename ClusterType> const std::string &fmt_format() {
static const std::string value = fmt_format_trait<ClusterType>::value();
return value;
}
/**
* Helper function to allocate image data given item size and shape
+16 -2
View File
@@ -67,9 +67,23 @@ void define_raw_master_file_bindings(py::module &m) {
.def_property_readonly("total_frames_expected",
&RawMasterFile::total_frames_expected)
.def_property_readonly("detector_layout",
&RawMasterFile::detector_layout)
&RawMasterFile::detector_layout, R"(
Number of modules along each axis of the detector
Returns
----------
xy
Module rows and columns
)")
.def_property_readonly("udp_interfaces_per_module",
&RawMasterFile::udp_interfaces_per_module)
&RawMasterFile::udp_interfaces_per_module, R"(
Number of UDP interfaces along each axis of a module
Returns
----------
xy
UDP interface rows and columns
)")
.def_property_readonly("analog_samples", &RawMasterFile::analog_samples,
R"(
Number of analog samples
+7 -11
View File
@@ -61,11 +61,6 @@ auto read_n_frames_from_RawSubFile(RawSubFile &self, size_t n_frames) {
return py::make_tuple(header, image);
}
// Disable warnings for unused parameters, as we ignore some
// in the __exit__ method
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter"
void define_raw_sub_file_io_bindings(py::module &m) {
py::class_<RawSubFile>(m, "RawSubFile")
.def(py::init<const std::filesystem::path &, DetectorType, size_t,
@@ -89,17 +84,18 @@ void define_raw_sub_file_io_bindings(py::module &m) {
})
.def("__enter__", [](RawSubFile &self) { return &self; })
.def("__exit__",
[](RawSubFile &self, const std::optional<pybind11::type> &exc_type,
const std::optional<pybind11::object> &exc_value,
const std::optional<pybind11::object> &traceback) {})
[]([[maybe_unused]] RawSubFile &self,
[[maybe_unused]] const std::optional<pybind11::type> &exc_type,
[[maybe_unused]] const std::optional<pybind11::object>
&exc_value,
[[maybe_unused]] const std::optional<pybind11::object>
&traceback) {})
.def("__iter__", [](RawSubFile &self) { return &self; })
.def("__next__", [](RawSubFile &self) {
try {
return read_frame_from_RawSubFile(self);
} catch (std::runtime_error &e) {
} catch (std::runtime_error &) {
throw py::stop_iteration();
}
});
}
#pragma GCC diagnostic pop
+15 -1
View File
@@ -1,7 +1,7 @@
# SPDX-License-Identifier: MPL-2.0
import pytest
import json
from aare import File, RawFile, RawSubFile, DetectorType, ROI, UDPPortPosition
from aare import File, RawFile, RawSubFile, DetectorType, ROI, UDPPortPosition, xy
import numpy as np
@@ -237,6 +237,20 @@ def test_raw_frame_count_across_rois(small_raw_file, short_module):
reader.read_roi(roi_index)
def test_raw_file_geometry(small_raw_file):
metadata = json.loads(small_raw_file.read_text())
metadata["Geometry"] = {"x": 1, "y": 2}
small_raw_file.write_text(json.dumps(metadata))
for index in range(2):
data = (small_raw_file.parent / f"run_d0_f{index}_0.raw").read_bytes()
(small_raw_file.parent / f"run_d1_f{index}_0.raw").write_bytes(data)
reader = RawFile(small_raw_file)
assert reader.geometry == xy(row=2, col=1)
assert reader.master.detector_layout == xy(row=2, col=1)
assert reader.master.udp_interfaces_per_module == xy(row=1, col=1)
@pytest.mark.parametrize("disabled_port", [0, 1])
def test_raw_frame_count_with_disabled_udp_port(small_raw_file, disabled_port):
metadata = json.loads(small_raw_file.read_text())
+26
View File
@@ -0,0 +1,26 @@
# SPDX-License-Identifier: MPL-2.0
from aare import xy
def test_xy_fields():
layout = xy(row=2, col=3)
assert layout.row == 2
assert layout.col == 3
layout.row = 4
assert layout.row == 4
def test_xy_unpacks_as_row_col():
row, col = xy(2, 3)
assert (row, col) == (2, 3)
def test_xy_comparison():
assert xy(2, 3) == xy(2, 3)
assert xy(2, 3) != xy(3, 2)
assert xy(2, 3) != (2, 3)
def test_xy_repr():
assert repr(xy(2, 3)) == "xy(row=2, col=3)"
+5 -2
View File
@@ -12,6 +12,7 @@
using aare::Cluster;
using aare::ClusterVector;
using aare::ssize_t;
using C1 = Cluster<int32_t, 2, 2>;
TEST_CASE("A newly created ClusterVector is empty") {
@@ -180,7 +181,8 @@ TEST_CASE("Summing 3x1 clusters of int64") {
Cluster<int32_t, 3, 1> c3 = {11, 12, {13, 14, 15}};
cv.push_back(c3);
REQUIRE(cv.capacity() == 4);
// growth factor is implementation defined (2x libstdc++, 1.5x MSVC)
REQUIRE(cv.capacity() >= 3);
REQUIRE(cv.size() == 3);
/*
@@ -236,7 +238,8 @@ TEST_CASE("Push back more than initial capacity") {
Cluster<int32_t, 2, 2> c3 = {11, 12, {13, 14, 15, 16}};
cv.push_back(c3);
REQUIRE(cv.size() == 3);
REQUIRE(cv.capacity() == 4);
// growth factor is implementation defined (2x libstdc++, 1.5x MSVC)
REQUIRE(cv.capacity() >= 3);
Cluster<int32_t, 2, 2> *ptr =
reinterpret_cast<Cluster<int32_t, 2, 2> *>(cv.data());
+1 -1
View File
@@ -159,7 +159,7 @@ Dtype Dtype::from_bitdepth(uint8_t bitdepth) {
std::string Dtype::to_string() const {
char ec{};
if (endian::native == endian::little)
if constexpr (endian::native == endian::little)
ec = '<';
else
ec = '>';
+3 -3
View File
@@ -9,10 +9,10 @@ namespace aare {
FilePtr::FilePtr(const std::filesystem::path &fname,
const std::string &mode = "rb") {
fp_ = fopen(fname.c_str(), mode.c_str());
fp_ = fopen(fname.string().c_str(), mode.c_str());
if (!fp_)
throw std::runtime_error(
fmt::format("Could not open: {}", fname.c_str()));
fmt::format("Could not open: {}", fname.string()));
}
FilePtr::FilePtr(FilePtr &&other) { std::swap(fp_, other.fp_); }
@@ -27,7 +27,7 @@ FilePtr::operator bool() const noexcept { return fp_ != nullptr; }
FILE *FilePtr::get() { return fp_; }
ssize_t FilePtr::tell() {
auto pos = ftell(fp_);
auto pos = ftell64(fp_);
if (pos == -1)
throw std::runtime_error(
fmt::format("Error getting file position: {}", error_msg()));
+2 -2
View File
@@ -174,7 +174,7 @@ void JungfrauDataFile::find_frame_size(const std::filesystem::path &fname) {
void JungfrauDataFile::parse_fname(const std::filesystem::path &fname) {
m_path = fname.parent_path();
m_base_name = fname.stem();
m_base_name = fname.stem().string();
// find file index, then remove if from the base name
if (auto pos = m_base_name.find_last_of('_'); pos != std::string::npos) {
@@ -259,7 +259,7 @@ JungfrauDataHeader JungfrauDataFile::read_header() {
throw std::runtime_error(LOCATION + "Could not read header from file" +
m_fp.error_msg());
}
m_fp.seek(-header_size, SEEK_CUR);
m_fp.seek(-static_cast<ssize_t>(header_size), SEEK_CUR);
return header;
}
+1
View File
@@ -22,6 +22,7 @@ using aare::File;
using aare::FileConfig;
using aare::Frame;
using aare::NumpyFile;
using aare::ssize_t;
using aare::experimental::MultiThreadedFileReader;
namespace {
+1
View File
@@ -9,6 +9,7 @@
using aare::NDArray;
using aare::NDView;
using aare::Shape;
using aare::ssize_t;
TEST_CASE("Initial size is zero if no size is specified") {
NDArray<double> a;
+1
View File
@@ -11,6 +11,7 @@
using aare::NDView;
using aare::num_elements;
using aare::Shape;
using aare::ssize_t;
static_assert(std::is_convertible_v<NDView<int, 2>, NDView<const int, 2>>);
static_assert(!std::is_convertible_v<NDView<const int, 2>, NDView<int, 2>>);
+7 -8
View File
@@ -28,8 +28,7 @@ NumpyFile::NumpyFile(const std::filesystem::path &fname,
throw std::runtime_error(
fmt::format("Could not open: {} for reading", fname.string()));
}
initial_header_len = aare::NumpyHelpers::write_header(
std::filesystem::path(fname.c_str()), m_header);
initial_header_len = aare::NumpyHelpers::write_header(fname, m_header);
}
m_pixels_per_frame =
std::accumulate(m_header.shape.begin() + 1, m_header.shape.end(), 1,
@@ -46,7 +45,7 @@ void NumpyFile::write_impl(void *data, uint64_t size) {
if (!(m_mode == "w" || m_mode == "a")) {
throw std::invalid_argument("File not open for writing");
}
if (fseek(fp, 0, SEEK_END))
if (fseek64(fp, 0, SEEK_END))
throw std::runtime_error("Could not seek to end of file");
size_t const rc = fwrite(data, size, 1, fp);
if (rc != 1) {
@@ -68,8 +67,8 @@ void NumpyFile::get_frame_into(size_t frame_number, std::byte *image_buf) {
if (frame_number > m_header.shape[0]) {
throw std::invalid_argument("Frame number out of range");
}
if (fseek(fp, header_size + frame_number * m_bytes_per_frame,
SEEK_SET)) // NOLINT
if (fseek64(fp, header_size + frame_number * m_bytes_per_frame,
SEEK_SET)) // NOLINT
throw std::runtime_error("Could not seek to frame");
size_t const rc = fread(image_buf, m_bytes_per_frame, 1, fp);
@@ -101,15 +100,15 @@ void NumpyFile::read_into(std::byte *image_buf, size_t n_frames) {
NumpyFile::~NumpyFile() noexcept {
if (m_mode == "w" || m_mode == "a") {
// determine number of frames
if (fseek(fp, 0, SEEK_END)) {
if (fseek64(fp, 0, SEEK_END)) {
std::cout << "Could not seek to end of file" << std::endl;
}
size_t const file_size = ftell(fp);
size_t const file_size = ftell64(fp);
size_t const data_size = file_size - initial_header_len;
size_t const n_frames = data_size / m_bytes_per_frame;
// update number of frames in header (first element of shape)
m_header.shape[0] = n_frames;
if (fseek(fp, 0, SEEK_SET)) {
if (fseek64(fp, 0, SEEK_SET)) {
std::cout << "Could not seek to beginning of file" << std::endl;
}
// create string stream to contain header
+2 -2
View File
@@ -20,7 +20,7 @@ bool complete_ROI(const ROI &roi, const DetectorGeometry &geometry) {
bool complete_ROI(const std::vector<ROI> &rois,
const DetectorGeometry &geometry) {
if (rois.empty() or rois.size() > 1) {
if (rois.empty() || rois.size() > 1) {
return false;
} else {
return complete_ROI(rois[0], geometry);
@@ -34,7 +34,7 @@ bool complete_ROI(const ROIGeometry &roi, const DetectorGeometry &geometry) {
bool complete_ROI(const std::vector<ROIGeometry> &rois,
const DetectorGeometry &geometry) {
if (rois.empty() or rois.size() > 1) {
if (rois.empty() || rois.size() > 1) {
return false;
} else {
return complete_ROI(rois[0], geometry);
+1 -1
View File
@@ -302,7 +302,7 @@ void RawFile::open_subfiles(const size_t roi_index) {
DetectorHeader RawFile::read_header(const std::filesystem::path &fname) {
DetectorHeader h{};
FILE *fp = fopen(fname.string().c_str(), "r");
FILE *fp = fopen(fname.string().c_str(), "rb");
if (!fp)
throw std::runtime_error(fmt::format(
"Could not open file '{}' for frame index 0", fname.string()));
+11 -11
View File
@@ -11,8 +11,8 @@ namespace aare {
RawFileNameComponents::RawFileNameComponents(
const std::filesystem::path &fname) {
m_base_path = fname.parent_path();
m_base_name = fname.stem();
m_ext = fname.extension();
m_base_name = fname.stem().string();
m_ext = fname.extension().string();
if (m_ext != ".json" && m_ext != ".raw") {
throw std::runtime_error(LOCATION +
@@ -23,7 +23,7 @@ RawFileNameComponents::RawFileNameComponents(
try {
auto pos = m_base_name.rfind('_');
m_file_index = std::stoi(m_base_name.substr(pos + 1));
} catch (const std::invalid_argument &e) {
} catch (const std::invalid_argument &) {
throw std::runtime_error(LOCATION + "Could not parse file index");
}
@@ -370,7 +370,7 @@ void RawMasterFile::parse_json(std::istream &is) {
try {
m_quad = j.at("Quad");
} catch (const json::out_of_range &e) {
} catch (const json::out_of_range &) {
// keep the optional empty
}
// try{
@@ -384,7 +384,7 @@ void RawMasterFile::parse_json(std::istream &is) {
if (digital_flag) {
m_digital_samples = j.at("Digital Samples");
}
} catch (const json::out_of_range &e) {
} catch (const json::out_of_range &) {
// keep the optional empty
}
try {
@@ -393,7 +393,7 @@ void RawMasterFile::parse_json(std::istream &is) {
if (m_transceiver_flag) {
m_transceiver_samples = j.at("Transceiver Samples");
}
} catch (const json::out_of_range &e) {
} catch (const json::out_of_range &) {
// keep the optional empty
}
try {
@@ -414,7 +414,7 @@ void RawMasterFile::parse_json(std::istream &is) {
m_scan_parameters
.increment_stop(); // adjust for endpoint being included
}
} catch (const json::out_of_range &e) {
} catch (const json::out_of_range &) {
// not a scan
}
try {
@@ -424,7 +424,7 @@ void RawMasterFile::parse_json(std::istream &is) {
m_udp_port_types.value().push_back(
string_to<UDPPortPosition>(elem));
}
} catch (const json::out_of_range &e) {
} catch (const json::out_of_range &) {
// leave the optional empty
}
try {
@@ -433,13 +433,13 @@ void RawMasterFile::parse_json(std::istream &is) {
for (auto &elem : json_list_obj) {
m_disabled_udp_ports.push_back(static_cast<size_t>(elem));
}
} catch (const json::out_of_range &e) {
} catch (const json::out_of_range &) {
m_disabled_udp_ports
.clear(); // empty list if not present in master file
}
try {
m_udp_interfaces_per_module = {j.at("Number of UDP Interfaces"), 1};
} catch (const json::out_of_range &e) {
} catch (const json::out_of_range &) {
if (m_type == DetectorType::Eiger && m_quad == 1)
m_udp_interfaces_per_module = {2, 1};
else if (m_type == DetectorType::Eiger) {
@@ -485,7 +485,7 @@ void RawMasterFile::parse_json(std::istream &is) {
}
}
} catch (const json::out_of_range &e) {
} catch (const json::out_of_range &) {
// fill ROI with full detector size if not present in master file
m_rois.push_back(
{0, m_detector_layout.col * static_cast<ssize_t>(m_pixels_x), 0,
+1 -1
View File
@@ -163,7 +163,7 @@ void RawSubFile::parse_fname(const std::filesystem::path &fname) {
// f1 is the file index - thi is the one we need
// 0 is the measurement index, will not change
m_path = fname.parent_path();
m_base_name = fname.filename();
m_base_name = fname.filename().string();
// Regex to extract numbers after 'd' and 'f'
std::regex pattern(R"(^(.*_d)(\d+)(_f)(\d+)(_\d+\.raw)$)");
+2
View File
@@ -3,6 +3,8 @@
#include <aare/algorithm.hpp>
#include <catch2/catch_test_macros.hpp>
using aare::ssize_t;
TEST_CASE("Find the closed index in a 1D array", "[algorithm]") {
aare::NDArray<double, 1> arr({5});
for (ssize_t i = 0; i < arr.size(); i++) {
+3 -2
View File
@@ -10,6 +10,7 @@ using Catch::Matchers::WithinAbs;
#include <vector>
using aare::BitOffset;
using aare::ssize_t;
TEST_CASE("test_adc_sar_05_decode64to16") {
uint64_t input = 0;
@@ -24,7 +25,7 @@ TEST_CASE("test_adc_sar_05_decode64to16") {
// test all bits by iteratting through the bitlist
std::vector<int> bitlist = {29, 19, 28, 18, 31, 21, 27, 20, 24, 23, 25, 22};
for (size_t i = 0; i < bitlist.size(); i++) {
input = 1UL << bitlist[i];
input = 1ULL << bitlist[i];
output = aare::adc_sar_05_decode64to16(input);
CHECK(output == (1 << i));
}
@@ -94,7 +95,7 @@ TEST_CASE("Each byte buffer decoder maps to its own bit order") {
};
for (const auto &c : cases) {
uint64_t word = (1UL << c.bit0) | (1UL << c.bit1);
uint64_t word = (1ULL << c.bit0) | (1ULL << c.bit1);
std::vector<uint8_t> buffer(8);
std::memcpy(buffer.data(), &word, sizeof(word));
aare::NDView<const uint8_t, 2> bytes(buffer.data(), {1, 8});
+4 -1
View File
@@ -269,7 +269,10 @@ void fit_3d(const FitModel<Model> &model, NDView<double, 1> x,
NDView<double, 3> par_out, NDView<double, 3> err_out,
NDView<double, 2> chi2_out, int n_threads,
std::optional<const NDView<size_t, 3>> fit_range) {
constexpr std::size_t npar = Model::npar;
// static so the lambda below can use it as an array size without
// capturing it, which MSVC otherwise rejects
static constexpr std::size_t npar = Model::npar;
detail::check_fit_3d_shapes<Model>(x, y, y_err, par_out, err_out, chi2_out);
const bool has_errors = (y_err.size() > 0);
+2 -1
View File
@@ -17,6 +17,7 @@
using aare::Minimizer;
using aare::NDArray;
using aare::NDView;
using aare::ssize_t;
using Catch::Approx;
namespace {
@@ -1099,7 +1100,7 @@ TEST_CASE("Samples with a zero error are ignored even when NaN", "[fit]") {
INFO("minimizer " << name(minimizer));
const double nan = std::numeric_limits<double>::quiet_NaN();
constexpr ssize_t n = 10;
static constexpr ssize_t n = 10; // static: used inside GENERATE's lambda
NDArray<double, 1> x({n});
NDArray<double, 1> y({n});
NDArray<double, 1> y_err({n}, 1.0);
@@ -20,6 +20,7 @@ using aare::FileConfig;
using aare::Frame;
using aare::NumpyFile;
using aare::PedestalTrackingPixelHistogram;
using aare::ssize_t;
namespace {
+1
View File
@@ -18,6 +18,7 @@
using aare::NDArray;
using aare::NDView;
using aare::PixelHistogram;
using aare::ssize_t;
namespace {
// The synchronous fill() has been removed; fill_async() is the only entry
+41
View File
@@ -0,0 +1,41 @@
// SPDX-License-Identifier: MPL-2.0
#include "aare/logger.hpp"
#include <catch2/catch_test_macros.hpp>
#include <catch2/matchers/catch_matchers_string.hpp>
#include <iostream>
#include <sstream>
#include <string>
using Catch::Matchers::EndsWith;
using Catch::Matchers::Matches;
using Catch::Matchers::StartsWith;
TEST_CASE("Log timestamp is HH:MM:SS.mmm", "[logger]") {
const std::string ts = aare::Logger::Timestamp();
REQUIRE_THAT(ts, Matches(R"(\d{2}:\d{2}:\d{2}\.\d{3})"));
}
TEST_CASE("short_file_name strips both kinds of directory separators",
"[logger]") {
REQUIRE(std::string(aare::short_file_name("/a/b/File.cpp")) == "File.cpp");
REQUIRE(std::string(aare::short_file_name("C:\\a\\b\\File.cpp")) ==
"File.cpp");
REQUIRE(std::string(aare::short_file_name("File.cpp")) == "File.cpp");
}
TEST_CASE("LOG writes one coloured line with timestamp and level", "[logger]") {
std::ostringstream captured;
auto *previous = std::clog.rdbuf(captured.rdbuf());
LOG(aare::logWARNING) << "hello " << 42;
std::clog.rdbuf(previous);
const std::string line = captured.str();
// <colour>- HH:MM:SS.mmm WARNING: hello 42<reset>\n
REQUIRE_THAT(line, StartsWith(std::string(YELLOW BOLD) + "- "));
REQUIRE_THAT(line, Matches(std::string("\x1b\\[33m\x1b\\[1m- ") +
R"(\d{2}:\d{2}:\d{2}\.\d{3} WARNING: hello 42)" +
"\x1b\\[0m\n"));
REQUIRE_THAT(line, EndsWith(std::string(RESET) + "\n"));
}