diff --git a/CMakeLists.txt b/CMakeLists.txt index daeb790..dddb44b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,12 +1,17 @@ cmake_minimum_required(VERSION 3.15) project(aare - VERSION 1.0.0 DESCRIPTION "Data processing library for PSI detectors" HOMEPAGE_URL "https://github.com/slsdetectorgroup/aare" LANGUAGES C CXX ) +# Read VERSION file into project version +set(VERSION_FILE "${CMAKE_CURRENT_SOURCE_DIR}/VERSION") +file(READ "${VERSION_FILE}" VERSION_CONTENT) +string(STRIP "${VERSION_CONTENT}" PROJECT_VERSION_STRING) +set(PROJECT_VERSION ${PROJECT_VERSION_STRING}) + set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) @@ -404,6 +409,9 @@ target_include_directories(aare_core PUBLIC "$" ) +set(THREADS_PREFER_PTHREAD_FLAG ON) +find_package(Threads REQUIRED) + target_link_libraries( aare_core PUBLIC @@ -412,6 +420,7 @@ target_link_libraries( ${STD_FS_LIB} # from helpers.cmake PRIVATE aare_compiler_flags + Threads::Threads $ ) diff --git a/VERSION b/VERSION new file mode 100644 index 0000000..bd52db8 --- /dev/null +++ b/VERSION @@ -0,0 +1 @@ +0.0.0 \ No newline at end of file diff --git a/conda-recipe/meta.yaml b/conda-recipe/meta.yaml index bfa6323..8fea745 100644 --- a/conda-recipe/meta.yaml +++ b/conda-recipe/meta.yaml @@ -1,6 +1,10 @@ +source: + path: ../ + +{% set version = load_file_regex(load_file = 'VERSION', regex_pattern = '(\d+(?:\.\d+)*(?:[\+\w\.]+))').group(1) %} package: name: aare - version: 2025.4.22 #TODO! how to not duplicate this? + version: {{version}} source: path: .. diff --git a/include/aare/Fit.hpp b/include/aare/Fit.hpp index 6fd10aa..eb9ac22 100644 --- a/include/aare/Fit.hpp +++ b/include/aare/Fit.hpp @@ -15,6 +15,12 @@ NDArray gaus(NDView x, NDView par); double pol1(const double x, const double *par); NDArray pol1(NDView x, NDView par); +double scurve(const double x, const double *par); +NDArray scurve(NDView x, NDView par); + +double scurve2(const double x, const double *par); +NDArray scurve2(NDView x, NDView par); + } // namespace func @@ -25,6 +31,9 @@ std::array gaus_init_par(const NDView x, const NDView pol1_init_par(const NDView x, const NDView y); +std::array scurve_init_par(const NDView x, const NDView y); +std::array scurve2_init_par(const NDView x, const NDView y); + static constexpr int DEFAULT_NUM_THREADS = 4; /** @@ -38,7 +47,7 @@ NDArray fit_gaus(NDView x, NDView y); /** * @brief Fit a 1D Gaussian to each pixel. Data layout [row, col, values] * @param x x values - * @param y y vales, layout [row, col, values] + * @param y y values, layout [row, col, values] * @param n_threads number of threads to use */ @@ -51,7 +60,7 @@ NDArray fit_gaus(NDView x, NDView y, /** * @brief Fit a 1D Gaussian with error estimates * @param x x values - * @param y y vales, layout [row, col, values] + * @param y y values, layout [row, col, values] * @param y_err error in y, layout [row, col, values] * @param par_out output parameters * @param par_err_out output error parameters @@ -64,7 +73,7 @@ void fit_gaus(NDView x, NDView y, NDView y_err, * @brief Fit a 1D Gaussian to each pixel with error estimates. Data layout * [row, col, values] * @param x x values - * @param y y vales, layout [row, col, values] + * @param y y values, layout [row, col, values] * @param y_err error in y, layout [row, col, values] * @param par_out output parameters, layout [row, col, values] * @param par_err_out output parameter errors, layout [row, col, values] @@ -88,5 +97,19 @@ void fit_pol1(NDView x, NDView y, NDView y_err, NDView par_out, NDView par_err_out,NDView chi2_out, int n_threads = DEFAULT_NUM_THREADS); +NDArray fit_scurve(NDView x, NDView y); +NDArray fit_scurve(NDView x, NDView y, int n_threads); +void fit_scurve(NDView x, NDView y, NDView y_err, + NDView par_out, NDView par_err_out, double& chi2); +void fit_scurve(NDView x, NDView y, NDView y_err, + NDView par_out, NDView par_err_out, NDView chi2_out, + int n_threads); +NDArray fit_scurve2(NDView x, NDView y); +NDArray fit_scurve2(NDView x, NDView y, int n_threads); +void fit_scurve2(NDView x, NDView y, NDView y_err, + NDView par_out, NDView par_err_out, double& chi2); +void fit_scurve2(NDView x, NDView y, NDView y_err, + NDView par_out, NDView par_err_out, NDView chi2_out, + int n_threads); } // namespace aare \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index 7415062..db3cb3c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,10 +1,16 @@ +[tool.scikit-build.metadata.version] +provider = "scikit_build_core.metadata.regex" +input = "VERSION" +regex = '^(?P\d+(?:\.\d+)*(?:[\.\+\w]+)?)$' +result = "{version}" + [build-system] requires = ["scikit-build-core>=0.10", "pybind11", "numpy"] build-backend = "scikit_build_core.build" [project] name = "aare" -version = "2025.4.22" +dynamic = ["version"] requires-python = ">=3.11" dependencies = [ "numpy", diff --git a/python/aare/__init__.py b/python/aare/__init__.py index e1e5757..d2bbe0a 100644 --- a/python/aare/__init__.py +++ b/python/aare/__init__.py @@ -15,7 +15,7 @@ from .ClusterFinder import ClusterFinder, ClusterCollector, ClusterFinderMT, Clu from .ClusterVector import ClusterVector -from ._aare import fit_gaus, fit_pol1 +from ._aare import fit_gaus, fit_pol1, fit_scurve, fit_scurve2 from ._aare import Interpolator from ._aare import calculate_eta2 diff --git a/python/aare/func.py b/python/aare/func.py index ca60cf2..e8a7b46 100644 --- a/python/aare/func.py +++ b/python/aare/func.py @@ -1 +1 @@ -from ._aare import gaus, pol1 \ No newline at end of file +from ._aare import gaus, pol1, scurve, scurve2 \ No newline at end of file diff --git a/python/src/fit.hpp b/python/src/fit.hpp index 8e6cfef..97dafb5 100644 --- a/python/src/fit.hpp +++ b/python/src/fit.hpp @@ -55,6 +55,47 @@ void define_fit_bindings(py::module &m) { )", py::arg("x"), py::arg("par")); + m.def( + "scurve", + [](py::array_t x, + py::array_t par) { + auto x_view = make_view_1d(x); + auto par_view = make_view_1d(par); + auto y = new NDArray{aare::func::scurve(x_view, par_view)}; + return return_image_data(y); + }, + R"( + Evaluate a 1D scurve function for all points in x using parameters par. + + Parameters + ---------- + x : array_like + The points at which to evaluate the scurve function. + par : array_like + The parameters of the scurve function. The first element is the background slope, the second element is the background intercept, the third element is the mean, the fourth element is the standard deviation, the fifth element is inflexion point count number, and the sixth element is C. + )", + py::arg("x"), py::arg("par")); + + m.def( + "scurve2", + [](py::array_t x, + py::array_t par) { + auto x_view = make_view_1d(x); + auto par_view = make_view_1d(par); + auto y = new NDArray{aare::func::scurve2(x_view, par_view)}; + return return_image_data(y); + }, + R"( + Evaluate a 1D scurve2 function for all points in x using parameters par. + + Parameters + ---------- + x : array_like + The points at which to evaluate the scurve function. + par : array_like + The parameters of the scurve2 function. The first element is the background slope, the second element is the background intercept, the third element is the mean, the fourth element is the standard deviation, the fifth element is inflexion point count number, and the sixth element is C. + )", + py::arg("x"), py::arg("par")); m.def( "fit_gaus", @@ -235,6 +276,180 @@ n_threads : int, optional R"( Fit a 1D polynomial to data with error estimates. +Parameters +---------- +x : array_like + The x values. +y : array_like + The y values. +y_err : array_like + The error in the y values. +n_threads : int, optional + The number of threads to use. Default is 4. +)", + py::arg("x"), py::arg("y"), py::arg("y_err"), py::arg("n_threads") = 4); + +//========= + m.def( + "fit_scurve", + [](py::array_t x, + py::array_t y, + int n_threads) { + if (y.ndim() == 3) { + auto par = new NDArray{}; + + auto x_view = make_view_1d(x); + auto y_view = make_view_3d(y); + *par = aare::fit_scurve(x_view, y_view, n_threads); + return return_image_data(par); + } else if (y.ndim() == 1) { + auto par = new NDArray{}; + auto x_view = make_view_1d(x); + auto y_view = make_view_1d(y); + *par = aare::fit_scurve(x_view, y_view); + return return_image_data(par); + } else { + throw std::runtime_error("Data must be 1D or 3D"); + } + }, + py::arg("x"), py::arg("y"), py::arg("n_threads") = 4); + + m.def( + "fit_scurve", + [](py::array_t x, + py::array_t y, + py::array_t y_err, + int n_threads) { + if (y.ndim() == 3) { + auto par = new NDArray({y.shape(0), y.shape(1), 6}); + + auto par_err = + new NDArray({y.shape(0), y.shape(1), 6}); + + auto y_view = make_view_3d(y); + auto y_view_err = make_view_3d(y_err); + auto x_view = make_view_1d(x); + + auto chi2 = new NDArray({y.shape(0), y.shape(1)}); + + aare::fit_scurve(x_view, y_view, y_view_err, par->view(), + par_err->view(), chi2->view(), n_threads); + return py::dict("par"_a = return_image_data(par), + "par_err"_a = return_image_data(par_err), + "chi2"_a = return_image_data(chi2), + "Ndf"_a = y.shape(2) - 2); + + + } else if (y.ndim() == 1) { + auto par = new NDArray({2}); + auto par_err = new NDArray({2}); + + auto y_view = make_view_1d(y); + auto y_view_err = make_view_1d(y_err); + auto x_view = make_view_1d(x); + + double chi2 = 0; + + aare::fit_scurve(x_view, y_view, y_view_err, par->view(), + par_err->view(), chi2); + return py::dict("par"_a = return_image_data(par), + "par_err"_a = return_image_data(par_err), + "chi2"_a = chi2, "Ndf"_a = y.size() - 2); + + } else { + throw std::runtime_error("Data must be 1D or 3D"); + } + }, + R"( +Fit a 1D polynomial to data with error estimates. + +Parameters +---------- +x : array_like + The x values. +y : array_like + The y values. +y_err : array_like + The error in the y values. +n_threads : int, optional + The number of threads to use. Default is 4. +)", + py::arg("x"), py::arg("y"), py::arg("y_err"), py::arg("n_threads") = 4); + + + m.def( + "fit_scurve2", + [](py::array_t x, + py::array_t y, + int n_threads) { + if (y.ndim() == 3) { + auto par = new NDArray{}; + + auto x_view = make_view_1d(x); + auto y_view = make_view_3d(y); + *par = aare::fit_scurve2(x_view, y_view, n_threads); + return return_image_data(par); + } else if (y.ndim() == 1) { + auto par = new NDArray{}; + auto x_view = make_view_1d(x); + auto y_view = make_view_1d(y); + *par = aare::fit_scurve2(x_view, y_view); + return return_image_data(par); + } else { + throw std::runtime_error("Data must be 1D or 3D"); + } + }, + py::arg("x"), py::arg("y"), py::arg("n_threads") = 4); + + m.def( + "fit_scurve2", + [](py::array_t x, + py::array_t y, + py::array_t y_err, + int n_threads) { + if (y.ndim() == 3) { + auto par = new NDArray({y.shape(0), y.shape(1), 6}); + + auto par_err = + new NDArray({y.shape(0), y.shape(1), 6}); + + auto y_view = make_view_3d(y); + auto y_view_err = make_view_3d(y_err); + auto x_view = make_view_1d(x); + + auto chi2 = new NDArray({y.shape(0), y.shape(1)}); + + aare::fit_scurve2(x_view, y_view, y_view_err, par->view(), + par_err->view(), chi2->view(), n_threads); + return py::dict("par"_a = return_image_data(par), + "par_err"_a = return_image_data(par_err), + "chi2"_a = return_image_data(chi2), + "Ndf"_a = y.shape(2) - 2); + + + } else if (y.ndim() == 1) { + auto par = new NDArray({6}); + auto par_err = new NDArray({6}); + + auto y_view = make_view_1d(y); + auto y_view_err = make_view_1d(y_err); + auto x_view = make_view_1d(x); + + double chi2 = 0; + + aare::fit_scurve2(x_view, y_view, y_view_err, par->view(), + par_err->view(), chi2); + return py::dict("par"_a = return_image_data(par), + "par_err"_a = return_image_data(par_err), + "chi2"_a = chi2, "Ndf"_a = y.size() - 2); + + } else { + throw std::runtime_error("Data must be 1D or 3D"); + } + }, + R"( +Fit a 1D polynomial to data with error estimates. + Parameters ---------- x : array_like diff --git a/src/Fit.cpp b/src/Fit.cpp index 9126109..d104675 100644 --- a/src/Fit.cpp +++ b/src/Fit.cpp @@ -34,6 +34,30 @@ NDArray pol1(NDView x, NDView par) { return y; } +double scurve(const double x, const double * par) { + return (par[0] + par[1] * x) + 0.5 * (1 + erf((x - par[2]) / (sqrt(2) * par[3]))) * (par[4] + par[5] * (x - par[2])); +} + +NDArray scurve(NDView x, NDView par) { + NDArray y({x.shape()}, 0); + for (ssize_t i = 0; i < x.size(); i++) { + y(i) = scurve(x(i), par.data()); + } + return y; +} + +double scurve2(const double x, const double * par) { + return (par[0] + par[1] * x) + 0.5 * (1 - erf((x - par[2]) / (sqrt(2) * par[3]))) * (par[4] + par[5] * (x - par[2])); +} + +NDArray scurve2(NDView x, NDView par) { + NDArray y({x.shape()}, 0); + for (ssize_t i = 0; i < x.size(); i++) { + y(i) = scurve2(x(i), par.data()); + } + return y; +} + } // namespace func NDArray fit_gaus(NDView x, NDView y) { @@ -273,4 +297,229 @@ NDArray fit_pol1(NDView x, NDView y, return result; } +// ~~ S-CURVES ~~ + +// SCURVE -- +std::array scurve_init_par(const NDView x, const NDView y){ + // Estimate the initial parameters for the fit + std::array start_par{0, 0, 0, 0, 0, 0}; + + auto ymax = std::max_element(y.begin(), y.end()); + auto ymin = std::min_element(y.begin(), y.end()); + start_par[4] = *ymin + (*ymax - *ymin) / 2; + + // Find the first x where the corresponding y value is above the threshold (start_par[4]) + for (ssize_t i = 0; i < y.size(); ++i) { + if (y[i] >= start_par[4]) { + start_par[2] = x[i]; + break; // Exit the loop after finding the first valid x + } + } + + start_par[3] = 2 * sqrt(start_par[2]); + start_par[0] = 100; + start_par[1] = 0.25; + start_par[5] = 1; + return start_par; +} + +// - No error +NDArray fit_scurve(NDView x, NDView y) { + NDArray result = scurve_init_par(x, y); + lm_status_struct status; + + lmcurve(result.size(), result.data(), x.size(), x.data(), y.data(), + aare::func::scurve, &lm_control_double, &status); + + return result; +} + +NDArray fit_scurve(NDView x, NDView y, int n_threads) { + NDArray result({y.shape(0), y.shape(1), 6}, 0); + + auto process = [&x, &y, &result](ssize_t first_row, ssize_t last_row) { + for (ssize_t row = first_row; row < last_row; row++) { + for (ssize_t col = 0; col < y.shape(1); col++) { + NDView values(&y(row, col, 0), {y.shape(2)}); + auto res = fit_scurve(x, values); + result(row, col, 0) = res(0); + result(row, col, 1) = res(1); + result(row, col, 2) = res(2); + result(row, col, 3) = res(3); + result(row, col, 4) = res(4); + result(row, col, 5) = res(5); + } + } + }; + + auto tasks = split_task(0, y.shape(0), n_threads); + RunInParallel(process, tasks); + return result; +} + +// - Error +void fit_scurve(NDView x, NDView y, NDView y_err, + NDView par_out, NDView par_err_out, double& chi2) { + + // Check that we have the correct sizes + if (y.size() != x.size() || y.size() != y_err.size() || + par_out.size() != 6 || par_err_out.size() != 6) { + throw std::runtime_error("Data, x, data_err must have the same size " + "and par_out, par_err_out must have size 6"); + } + + lm_status_struct status; + par_out = scurve_init_par(x, y); + std::array cov = {0}; // size 6x6 + // std::array cov{0, 0, 0, 0}; + + lmcurve2(par_out.size(), par_out.data(), par_err_out.data(), cov.data(), + x.size(), x.data(), y.data(), y_err.data(), aare::func::scurve, + &lm_control_double, &status); + + // Calculate chi2 + chi2 = 0; + for (ssize_t i = 0; i < y.size(); i++) { + chi2 += std::pow((y(i) - func::pol1(x(i), par_out.data())) / y_err(i), 2); + } +} + +void fit_scurve(NDView x, NDView y, NDView y_err, + NDView par_out, NDView par_err_out, NDView chi2_out, + int n_threads) { + + auto process = [&](ssize_t first_row, ssize_t last_row) { + for (ssize_t row = first_row; row < last_row; row++) { + for (ssize_t col = 0; col < y.shape(1); col++) { + NDView y_view(&y(row, col, 0), {y.shape(2)}); + NDView y_err_view(&y_err(row, col, 0), + {y_err.shape(2)}); + NDView par_out_view(&par_out(row, col, 0), + {par_out.shape(2)}); + NDView par_err_out_view(&par_err_out(row, col, 0), + {par_err_out.shape(2)}); + + fit_scurve(x, y_view, y_err_view, par_out_view, par_err_out_view, chi2_out(row, col)); + + } + } + }; + + auto tasks = split_task(0, y.shape(0), n_threads); + RunInParallel(process, tasks); + +} + +// SCURVE2 --- + +std::array scurve2_init_par(const NDView x, const NDView y){ + // Estimate the initial parameters for the fit + std::array start_par{0, 0, 0, 0, 0, 0}; + + auto ymax = std::max_element(y.begin(), y.end()); + auto ymin = std::min_element(y.begin(), y.end()); + start_par[4] = *ymin + (*ymax - *ymin) / 2; + + // Find the first x where the corresponding y value is above the threshold (start_par[4]) + for (ssize_t i = 0; i < y.size(); ++i) { + if (y[i] <= start_par[4]) { + start_par[2] = x[i]; + break; // Exit the loop after finding the first valid x + } + } + + start_par[3] = 2 * sqrt(start_par[2]); + start_par[0] = 100; + start_par[1] = 0.25; + start_par[5] = -1; + return start_par; +} + +// - No error +NDArray fit_scurve2(NDView x, NDView y) { + NDArray result = scurve2_init_par(x, y); + lm_status_struct status; + + lmcurve(result.size(), result.data(), x.size(), x.data(), y.data(), + aare::func::scurve2, &lm_control_double, &status); + + return result; +} + +NDArray fit_scurve2(NDView x, NDView y, int n_threads) { + NDArray result({y.shape(0), y.shape(1), 6}, 0); + + auto process = [&x, &y, &result](ssize_t first_row, ssize_t last_row) { + for (ssize_t row = first_row; row < last_row; row++) { + for (ssize_t col = 0; col < y.shape(1); col++) { + NDView values(&y(row, col, 0), {y.shape(2)}); + auto res = fit_scurve2(x, values); + result(row, col, 0) = res(0); + result(row, col, 1) = res(1); + result(row, col, 2) = res(2); + result(row, col, 3) = res(3); + result(row, col, 4) = res(4); + result(row, col, 5) = res(5); + } + } + }; + + auto tasks = split_task(0, y.shape(0), n_threads); + RunInParallel(process, tasks); + return result; +} + +// - Error +void fit_scurve2(NDView x, NDView y, NDView y_err, + NDView par_out, NDView par_err_out, double& chi2) { + + // Check that we have the correct sizes + if (y.size() != x.size() || y.size() != y_err.size() || + par_out.size() != 6 || par_err_out.size() != 6) { + throw std::runtime_error("Data, x, data_err must have the same size " + "and par_out, par_err_out must have size 6"); + } + + lm_status_struct status; + par_out = scurve2_init_par(x, y); + std::array cov = {0}; // size 6x6 + // std::array cov{0, 0, 0, 0}; + + lmcurve2(par_out.size(), par_out.data(), par_err_out.data(), cov.data(), + x.size(), x.data(), y.data(), y_err.data(), aare::func::scurve2, + &lm_control_double, &status); + + // Calculate chi2 + chi2 = 0; + for (ssize_t i = 0; i < y.size(); i++) { + chi2 += std::pow((y(i) - func::pol1(x(i), par_out.data())) / y_err(i), 2); + } +} + +void fit_scurve2(NDView x, NDView y, NDView y_err, + NDView par_out, NDView par_err_out, NDView chi2_out, + int n_threads) { + + auto process = [&](ssize_t first_row, ssize_t last_row) { + for (ssize_t row = first_row; row < last_row; row++) { + for (ssize_t col = 0; col < y.shape(1); col++) { + NDView y_view(&y(row, col, 0), {y.shape(2)}); + NDView y_err_view(&y_err(row, col, 0), + {y_err.shape(2)}); + NDView par_out_view(&par_out(row, col, 0), + {par_out.shape(2)}); + NDView par_err_out_view(&par_err_out(row, col, 0), + {par_err_out.shape(2)}); + + fit_scurve2(x, y_view, y_err_view, par_out_view, par_err_out_view, chi2_out(row, col)); + + } + } + }; + + auto tasks = split_task(0, y.shape(0), n_threads); + RunInParallel(process, tasks); + +} + } // namespace aare \ No newline at end of file diff --git a/src/JungfrauDataFile.cpp b/src/JungfrauDataFile.cpp index 8f1f904..59a1a0a 100644 --- a/src/JungfrauDataFile.cpp +++ b/src/JungfrauDataFile.cpp @@ -89,7 +89,7 @@ void JungfrauDataFile::seek(size_t frame_index) { : frame_index; auto byte_offset = frame_offset * (m_bytes_per_frame + header_size); m_fp.seek(byte_offset); -}; +} size_t JungfrauDataFile::tell() { return m_current_frame_index; } size_t JungfrauDataFile::total_frames() const { return m_total_frames; } @@ -235,4 +235,4 @@ std::filesystem::path JungfrauDataFile::fpath(size_t file_index) const { return m_path / fname; } -} // namespace aare \ No newline at end of file +} // namespace aare diff --git a/src/NumpyFile.cpp b/src/NumpyFile.cpp index 109439a..e375ce3 100644 --- a/src/NumpyFile.cpp +++ b/src/NumpyFile.cpp @@ -72,8 +72,8 @@ void NumpyFile::get_frame_into(size_t frame_number, std::byte *image_buf) { } } -size_t NumpyFile::pixels_per_frame() { return m_pixels_per_frame; }; -size_t NumpyFile::bytes_per_frame() { return m_bytes_per_frame; }; +size_t NumpyFile::pixels_per_frame() { return m_pixels_per_frame; } +size_t NumpyFile::bytes_per_frame() { return m_bytes_per_frame; } std::vector NumpyFile::read_n(size_t n_frames) { // TODO: implement this in a more efficient way @@ -197,4 +197,4 @@ void NumpyFile::load_metadata() { m_header = {dtype, fortran_order, shape}; } -} // namespace aare \ No newline at end of file +} // namespace aare diff --git a/src/RawFile.cpp b/src/RawFile.cpp index acd5fee..0a4804c 100644 --- a/src/RawFile.cpp +++ b/src/RawFile.cpp @@ -26,7 +26,7 @@ RawFile::RawFile(const std::filesystem::path &fname, const std::string &mode) } } -Frame RawFile::read_frame() { return get_frame(m_current_frame++); }; +Frame RawFile::read_frame() { return get_frame(m_current_frame++); } Frame RawFile::read_frame(size_t frame_number) { seek(frame_number); @@ -44,13 +44,13 @@ void RawFile::read_into(std::byte *image_buf, size_t n_frames) { void RawFile::read_into(std::byte *image_buf) { return get_frame_into(m_current_frame++, image_buf); -}; +} void RawFile::read_into(std::byte *image_buf, DetectorHeader *header) { return get_frame_into(m_current_frame++, image_buf, header); -}; +} void RawFile::read_into(std::byte *image_buf, size_t n_frames, DetectorHeader *header) { // return get_frame_into(m_current_frame++, image_buf, header); @@ -62,7 +62,7 @@ void RawFile::read_into(std::byte *image_buf, size_t n_frames, DetectorHeader *h header+=n_modules(); } -}; +} size_t RawFile::n_modules() const { return m_master.n_modules(); } @@ -86,9 +86,9 @@ void RawFile::seek(size_t frame_index) { frame_index, total_frames())); } m_current_frame = frame_index; -}; +} -size_t RawFile::tell() { return m_current_frame; }; +size_t RawFile::tell() { return m_current_frame; } size_t RawFile::total_frames() const { return m_master.frames_in_file(); } size_t RawFile::rows() const { return m_geometry.pixels_y; } @@ -301,4 +301,4 @@ size_t RawFile::frame_number(size_t frame_index) { } -} // namespace aare \ No newline at end of file +} // namespace aare diff --git a/src/RawMasterFile.cpp b/src/RawMasterFile.cpp index 08b5d2a..8a2db87 100644 --- a/src/RawMasterFile.cpp +++ b/src/RawMasterFile.cpp @@ -87,7 +87,7 @@ int ScanParameters::start() const { return m_start; } int ScanParameters::stop() const { return m_stop; } void ScanParameters::increment_stop(){ m_stop += 1; -}; +} int ScanParameters::step() const { return m_step; } const std::string &ScanParameters::dac() const { return m_dac; } bool ScanParameters::enabled() const { return m_enabled; } @@ -421,4 +421,4 @@ void RawMasterFile::parse_raw(const std::filesystem::path &fpath) { if(m_frames_in_file==0) m_frames_in_file = m_total_frames_expected; } -} // namespace aare \ No newline at end of file +} // namespace aare diff --git a/update_version.py b/update_version.py new file mode 100644 index 0000000..476895a --- /dev/null +++ b/update_version.py @@ -0,0 +1,57 @@ +# SPDX-License-Identifier: LGPL-3.0-or-other +# Copyright (C) 2021 Contributors to the Aare Package +""" +Script to update VERSION file with semantic versioning if provided as an argument, or with 0.0.0 if no argument is provided. +""" + +import sys +import os +import re + +from packaging.version import Version, InvalidVersion + + +SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__)) + +def is_integer(value): + try: + int(value) + except ValueError: + return False + else: + return True + + +def get_version(): + + # Check at least one argument is passed + if len(sys.argv) < 2: + return "0.0.0" + + version = sys.argv[1] + + try: + v = Version(version) # normalize check if version follows PEP 440 specification + + version_normalized = version.replace("-", ".") + + version_normalized = re.sub(r'0*(\d+)', lambda m : str(int(m.group(0))), version_normalized) #remove leading zeros + + return version_normalized + + except InvalidVersion as e: + print(f"Invalid version {version}. Version format must follow semantic versioning format of python PEP 440 version identification specification.") + sys.exit(1) + + +def write_version_to_file(version): + version_file_path = os.path.join(SCRIPT_DIR, "VERSION") + with open(version_file_path, "w") as version_file: + version_file.write(version) + print(f"Version {version} written to VERSION file.") + +# Main script +if __name__ == "__main__": + + version = get_version() + write_version_to_file(version) \ No newline at end of file