mirror of
https://github.com/slsdetectorgroup/aare.git
synced 2025-06-14 08:17:13 +02:00
Compare commits
175 Commits
2024.12.16
...
dev/hdf5
Author | SHA1 | Date | |
---|---|---|---|
119ca96a52 | |||
053536d135 | |||
286b2888ca | |||
52aa1d4d9b | |||
8556ab6564 | |||
51a87e2a1e | |||
e6dd1f3ec2 | |||
65672d06f3 | |||
bceefe6d64 | |||
cc57cc7c27 | |||
d89530ed22 | |||
7917e6f81a | |||
a26073fb41 | |||
3cc44f780f | |||
f3f3e2af6a | |||
031d9503d8 | |||
2a069f3b6e | |||
f9751902a2 | |||
cba2e46e2f | |||
b4a9b4caec | |||
be7f510775 | |||
56fa6f6bfb | |||
ca4d392b2f | |||
3b65e92cb7 | |||
755a8fb2b7 | |||
dc7f6d44f2 | |||
480e28c927 | |||
d7242671b2 | |||
a6a02249bc | |||
efd2338f54 | |||
b97f1e24f9 | |||
1bc2fd770a | |||
a3f831dc9e | |||
76b8872fe6 | |||
55236ce6cc | |||
e7d3e667b0 | |||
d9cbf0f481 | |||
5681e18403 | |||
69964e08d5 | |||
94ac58b09e | |||
9ecf4f4b44 | |||
f2a024644b | |||
9e1b8731b0 | |||
a6eebbe9bd | |||
81588fba3b | |||
276283ff14 | |||
cf158e2dcd | |||
12ae1424fb | |||
6db201f397 | |||
d5226909fe | |||
eb6862ff99 | |||
f06e722dce | |||
2e0424254c | |||
7b5e32a824 | |||
86d343f5f5 | |||
fd0196f2fd | |||
129e7e9f9d | |||
58c934d9cf | |||
4088b0889d | |||
d5f8daf194 | |||
c6e8e5f6a1 | |||
b501c31e38 | |||
326941e2b4 | |||
84aafa75f6 | |||
177459c98a | |||
c49a2fdf8e | |||
14211047ff | |||
acd9d5d487 | |||
d4050ec557 | |||
fca9d5d2fa | |||
1174f7f434 | |||
2bb7d360bf | |||
a90e532b21 | |||
8d8182c632 | |||
5f34ab6df1 | |||
5c8a5099fd | |||
7c93632605 | |||
54def26334 | |||
a59e9656be | |||
3f753ec900 | |||
15e52565a9 | |||
e71569b15e | |||
92f5421481 | |||
113f34cc98 | |||
53a90e197e | |||
6e4db45b57 | |||
76f050f69f | |||
a13affa4d3 | |||
8b0eee1e66 | |||
894065fe9c | |||
f16273a566 | |||
20d1d02fda | |||
10e4e10431 | |||
017960d963 | |||
a12e43b176 | |||
9de84a7f87 | |||
885309d97c | |||
e24ed68416 | |||
248d25486f | |||
7db1ae4d94 | |||
a24bbd9cf9 | |||
d7ef9bb1d8 | |||
de9fc16e89 | |||
85a6b5b95e | |||
50eeba4005 | |||
98d2d6098e | |||
61af1105a1 | |||
240960d3e7 | |||
04728929cb | |||
3083d51699 | |||
4240942cec | |||
745d09fbe9 | |||
e1533282f1 | |||
8cad7a50a6 | |||
9d8e803474 | |||
a42c0d645b | |||
508adf5016 | |||
e038bd1646 | |||
7e5f91c6ec | |||
ed9ef7c600 | |||
57bb6c71ae | |||
f8f98b6ec3 | |||
0876b6891a | |||
6ad76f63c1 | |||
6e7e81b36b | |||
5d8ad27b21 | |||
b529b6d33b | |||
602b04e49f | |||
11cd2ec654 | |||
e59a361b51 | |||
1ad362ccfc | |||
332bdeb02b | |||
3a987319d4 | |||
5614cb4673 | |||
8ae6bb76f8 | |||
1d2c38c1d4 | |||
b7a47576a1 | |||
fc1c9f35d6 | |||
5d2f25a6e9 | |||
6a83988485 | |||
8abfc68138 | |||
8ff6f9f506 | |||
dadf5f4869 | |||
dcb9a98faa | |||
7309cff47c | |||
c0c5e07ad8 | |||
2faa317bdf | |||
f7031d7f87 | |||
d86cb533c8 | |||
4c750cc3be | |||
e96fe31f11 | |||
cd5a738696 | |||
1ba43b69d3 | |||
fff536782b | |||
5a3ca2ae2d | |||
078e5d81ec | |||
6cde968c60 | |||
f6d736facd | |||
e1cc774d6c | |||
d0f435a7ab | |||
7ce02006f2 | |||
7550a2cb97 | |||
caf7b4ecdb | |||
72d10b7735 | |||
cc95561eda | |||
dc9e10016d | |||
21ce7a3efa | |||
acdce8454b | |||
d07da42745 | |||
0b252709bd | |||
e5df929a9a | |||
b7337fc6c5 | |||
09de69c090 | |||
b23e697e26 | |||
4233509615 |
42
.clang-tidy
Normal file
42
.clang-tidy
Normal file
@ -0,0 +1,42 @@
|
||||
|
||||
---
|
||||
Checks: '*,
|
||||
-altera-*,
|
||||
-android-cloexec-fopen,
|
||||
-cppcoreguidelines-pro-bounds-array-to-pointer-decay,
|
||||
-cppcoreguidelines-pro-bounds-pointer-arithmetic,
|
||||
-fuchsia*,
|
||||
-readability-else-after-return,
|
||||
-readability-avoid-const-params-in-decls,
|
||||
-readability-identifier-length,
|
||||
-cppcoreguidelines-pro-bounds-constant-array-index,
|
||||
-cppcoreguidelines-pro-type-reinterpret-cast,
|
||||
-llvm-header-guard,
|
||||
-modernize-use-nodiscard,
|
||||
-misc-non-private-member-variables-in-classes,
|
||||
-readability-static-accessed-through-instance,
|
||||
-readability-braces-around-statements,
|
||||
-readability-isolate-declaration,
|
||||
-readability-implicit-bool-conversion,
|
||||
-readability-identifier-length,
|
||||
-readability-identifier-naming,
|
||||
-hicpp-signed-bitwise,
|
||||
-hicpp-no-array-decay,
|
||||
-hicpp-braces-around-statements,
|
||||
-google-runtime-references,
|
||||
-google-readability-todo,
|
||||
-google-readability-braces-around-statements,
|
||||
-modernize-use-trailing-return-type,
|
||||
-llvmlibc-*'
|
||||
|
||||
HeaderFilterRegex: \.hpp
|
||||
FormatStyle: none
|
||||
CheckOptions:
|
||||
- { key: readability-identifier-naming.NamespaceCase, value: lower_case }
|
||||
# - { key: readability-identifier-naming.FunctionCase, value: lower_case }
|
||||
- { key: readability-identifier-naming.ClassCase, value: CamelCase }
|
||||
# - { key: readability-identifier-naming.MethodCase, value: CamelCase }
|
||||
# - { key: readability-identifier-naming.StructCase, value: CamelCase }
|
||||
# - { key: readability-identifier-naming.VariableCase, value: lower_case }
|
||||
- { key: readability-identifier-naming.GlobalConstantCase, value: UPPER_CASE }
|
||||
...
|
58
.gitea/workflows/cmake_build.yml
Normal file
58
.gitea/workflows/cmake_build.yml
Normal file
@ -0,0 +1,58 @@
|
||||
name: Build the package using cmake then documentation
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
pages: write
|
||||
id-token: write
|
||||
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
platform: [ubuntu-latest, ]
|
||||
python-version: ["3.12", ]
|
||||
|
||||
runs-on: ${{ matrix.platform }}
|
||||
|
||||
|
||||
defaults:
|
||||
run:
|
||||
shell: "bash -l {0}"
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Setup dev env
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get -y install cmake gcc g++
|
||||
|
||||
- name: Get conda
|
||||
uses: conda-incubator/setup-miniconda@v3
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
environment-file: etc/dev-env.yml
|
||||
miniforge-version: latest
|
||||
channels: conda-forge
|
||||
conda-remove-defaults: "true"
|
||||
|
||||
- name: Build library
|
||||
run: |
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. -DAARE_SYSTEM_LIBRARIES=ON -DAARE_DOCS=ON
|
||||
make -j 2
|
||||
make docs
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
36
.gitea/workflows/rh8-native.yml
Normal file
36
.gitea/workflows/rh8-native.yml
Normal file
@ -0,0 +1,36 @@
|
||||
name: Build on RHEL8
|
||||
|
||||
on:
|
||||
push:
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: "ubuntu-latest"
|
||||
container:
|
||||
image: gitea.psi.ch/images/rhel8-developer-gitea-actions
|
||||
steps:
|
||||
# workaround until actions/checkout@v4 is available for RH8
|
||||
# - uses: actions/checkout@v4
|
||||
- name: Clone repository
|
||||
run: |
|
||||
echo Cloning ${{ github.ref_name }}
|
||||
git clone https://${{secrets.GITHUB_TOKEN}}@gitea.psi.ch/${{ github.repository }}.git --branch=${{ github.ref_name }} .
|
||||
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
dnf install -y cmake python3.12 python3.12-devel python3.12-pip
|
||||
|
||||
- name: Build library
|
||||
run: |
|
||||
mkdir build && cd build
|
||||
cmake .. -DAARE_PYTHON_BINDINGS=ON -DAARE_TESTS=ON -DPython_FIND_VIRTUALENV=FIRST
|
||||
make -j 2
|
||||
|
||||
- name: C++ unit tests
|
||||
working-directory: ${{gitea.workspace}}/build
|
||||
run: ctest
|
31
.gitea/workflows/rh9-native.yml
Normal file
31
.gitea/workflows/rh9-native.yml
Normal file
@ -0,0 +1,31 @@
|
||||
name: Build on RHEL9
|
||||
|
||||
on:
|
||||
push:
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: "ubuntu-latest"
|
||||
container:
|
||||
image: gitea.psi.ch/images/rhel9-developer-gitea-actions
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
dnf install -y cmake python3.12 python3.12-devel python3.12-pip
|
||||
|
||||
- name: Build library
|
||||
run: |
|
||||
mkdir build && cd build
|
||||
cmake .. -DAARE_PYTHON_BINDINGS=ON -DAARE_TESTS=ON
|
||||
make -j 2
|
||||
|
||||
- name: C++ unit tests
|
||||
working-directory: ${{gitea.workspace}}/build
|
||||
run: ctest
|
@ -1,10 +1,9 @@
|
||||
name: Build pkgs and deploy if on main
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- developer
|
||||
release:
|
||||
types:
|
||||
- published
|
||||
|
||||
jobs:
|
||||
build:
|
||||
@ -25,16 +24,15 @@ jobs:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Get conda
|
||||
uses: conda-incubator/setup-miniconda@v3.0.4
|
||||
uses: conda-incubator/setup-miniconda@v3
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
environment-file: etc/dev-env.yml
|
||||
miniforge-version: latest
|
||||
channels: conda-forge
|
||||
|
||||
- name: Prepare
|
||||
run: conda install conda-build=24.9 conda-verify pytest anaconda-client
|
||||
conda-remove-defaults: "true"
|
||||
|
||||
- name: Enable upload
|
||||
if: github.ref == 'refs/heads/main'
|
||||
run: conda config --set anaconda_upload yes
|
||||
|
||||
- name: Build
|
41
.github/workflows/build_conda.yml
vendored
Normal file
41
.github/workflows/build_conda.yml
vendored
Normal file
@ -0,0 +1,41 @@
|
||||
name: Build pkgs and deploy if on main
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- developer
|
||||
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
platform: [ubuntu-latest, ] # macos-12, windows-2019]
|
||||
python-version: ["3.12",]
|
||||
|
||||
runs-on: ${{ matrix.platform }}
|
||||
|
||||
# The setup-miniconda action needs this to activate miniconda
|
||||
defaults:
|
||||
run:
|
||||
shell: "bash -l {0}"
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Get conda
|
||||
uses: conda-incubator/setup-miniconda@v3
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
environment-file: etc/dev-env.yml
|
||||
miniforge-version: latest
|
||||
channels: conda-forge
|
||||
conda-remove-defaults: "true"
|
||||
|
||||
|
||||
- name: Disable upload
|
||||
run: conda config --set anaconda_upload no
|
||||
|
||||
- name: Build
|
||||
run: conda build conda-recipe
|
||||
|
19
.github/workflows/build_docs.yml
vendored
19
.github/workflows/build_docs.yml
vendored
@ -2,10 +2,12 @@ name: Build the package using cmake then documentation
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
pull_request:
|
||||
release:
|
||||
types:
|
||||
- published
|
||||
|
||||
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
pages: write
|
||||
@ -16,12 +18,11 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
platform: [ubuntu-latest, ] # macos-12, windows-2019]
|
||||
platform: [ubuntu-latest, ]
|
||||
python-version: ["3.12",]
|
||||
|
||||
runs-on: ${{ matrix.platform }}
|
||||
|
||||
# The setup-miniconda action needs this to activate miniconda
|
||||
defaults:
|
||||
run:
|
||||
shell: "bash -l {0}"
|
||||
@ -30,13 +31,13 @@ jobs:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Get conda
|
||||
uses: conda-incubator/setup-miniconda@v3.0.4
|
||||
uses: conda-incubator/setup-miniconda@v3
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
environment-file: etc/dev-env.yml
|
||||
miniforge-version: latest
|
||||
channels: conda-forge
|
||||
|
||||
- name: Prepare
|
||||
run: conda install doxygen sphinx=7.1.2 breathe pybind11 sphinx_rtd_theme furo nlohmann_json zeromq fmt numpy
|
||||
conda-remove-defaults: "true"
|
||||
|
||||
- name: Build library
|
||||
run: |
|
||||
@ -57,7 +58,7 @@ jobs:
|
||||
url: ${{ steps.deployment.outputs.page_url }}
|
||||
runs-on: ubuntu-latest
|
||||
needs: build
|
||||
if: github.ref == 'refs/heads/main'
|
||||
if: (github.event_name == 'release' && github.event.action == 'published') || (github.event_name == 'workflow_dispatch' )
|
||||
steps:
|
||||
- name: Deploy to GitHub Pages
|
||||
id: deployment
|
||||
|
64
.github/workflows/build_wheel.yml
vendored
Normal file
64
.github/workflows/build_wheel.yml
vendored
Normal file
@ -0,0 +1,64 @@
|
||||
name: Build wheel
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
release:
|
||||
types:
|
||||
- published
|
||||
|
||||
|
||||
jobs:
|
||||
build_wheels:
|
||||
name: Build wheels on ${{ matrix.os }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest,]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Build wheels
|
||||
run: pipx run cibuildwheel==2.23.0
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: cibw-wheels-${{ matrix.os }}-${{ strategy.job-index }}
|
||||
path: ./wheelhouse/*.whl
|
||||
|
||||
build_sdist:
|
||||
name: Build source distribution
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Build sdist
|
||||
run: pipx run build --sdist
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: cibw-sdist
|
||||
path: dist/*.tar.gz
|
||||
|
||||
upload_pypi:
|
||||
needs: [build_wheels, build_sdist]
|
||||
runs-on: ubuntu-latest
|
||||
environment: pypi
|
||||
permissions:
|
||||
id-token: write
|
||||
if: github.event_name == 'release' && github.event.action == 'published'
|
||||
# or, alternatively, upload to PyPI on every tag starting with 'v' (remove on: release above to use this)
|
||||
# if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v')
|
||||
steps:
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
# unpacks all CIBW artifacts into dist/
|
||||
pattern: cibw-*
|
||||
path: dist
|
||||
merge-multiple: true
|
||||
|
||||
- uses: pypa/gh-action-pypi-publish@release/v1
|
3
.gitignore
vendored
3
.gitignore
vendored
@ -17,7 +17,8 @@ Testing/
|
||||
ctbDict.cpp
|
||||
ctbDict.h
|
||||
|
||||
|
||||
wheelhouse/
|
||||
dist/
|
||||
|
||||
*.pyc
|
||||
*/__pycache__/*
|
||||
|
163
CMakeLists.txt
163
CMakeLists.txt
@ -1,16 +1,29 @@
|
||||
cmake_minimum_required(VERSION 3.14)
|
||||
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)
|
||||
|
||||
execute_process(
|
||||
COMMAND git log -1 --format=%h
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}
|
||||
OUTPUT_VARIABLE GIT_HASH
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
message(STATUS "Building from git hash: ${GIT_HASH}")
|
||||
|
||||
if (${CMAKE_VERSION} VERSION_GREATER "3.24")
|
||||
cmake_policy(SET CMP0135 NEW) #Fetch content download timestamp
|
||||
endif()
|
||||
@ -31,7 +44,7 @@ set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH})
|
||||
|
||||
|
||||
# General options
|
||||
option(AARE_PYTHON_BINDINGS "Build python bindings" ON)
|
||||
option(AARE_PYTHON_BINDINGS "Build python bindings" OFF)
|
||||
option(AARE_TESTS "Build tests" OFF)
|
||||
option(AARE_BENCHMARKS "Build benchmarks" OFF)
|
||||
option(AARE_EXAMPLES "Build examples" OFF)
|
||||
@ -40,6 +53,7 @@ option(AARE_DOCS "Build documentation" OFF)
|
||||
option(AARE_VERBOSE "Verbose output" OFF)
|
||||
option(AARE_CUSTOM_ASSERT "Use custom assert" OFF)
|
||||
option(AARE_INSTALL_PYTHONEXT "Install the python extension in the install tree under CMAKE_INSTALL_PREFIX/aare/" OFF)
|
||||
option(AARE_HDF5 "Hdf5 File Format" OFF)
|
||||
option(AARE_ASAN "Enable AddressSanitizer" OFF)
|
||||
|
||||
# Configure which of the dependencies to use FetchContent for
|
||||
@ -48,6 +62,7 @@ option(AARE_FETCH_PYBIND11 "Use FetchContent to download pybind11" ON)
|
||||
option(AARE_FETCH_CATCH "Use FetchContent to download catch2" ON)
|
||||
option(AARE_FETCH_JSON "Use FetchContent to download nlohmann::json" ON)
|
||||
option(AARE_FETCH_ZMQ "Use FetchContent to download libzmq" ON)
|
||||
option(AARE_FETCH_LMFIT "Use FetchContent to download lmfit" ON)
|
||||
|
||||
|
||||
#Convenience option to use system libraries only (no FetchContent)
|
||||
@ -59,10 +74,15 @@ if(AARE_SYSTEM_LIBRARIES)
|
||||
set(AARE_FETCH_CATCH OFF CACHE BOOL "Disabled FetchContent for catch2" FORCE)
|
||||
set(AARE_FETCH_JSON OFF CACHE BOOL "Disabled FetchContent for nlohmann::json" FORCE)
|
||||
set(AARE_FETCH_ZMQ OFF CACHE BOOL "Disabled FetchContent for libzmq" FORCE)
|
||||
# Still fetch lmfit when setting AARE_SYSTEM_LIBRARIES since this is not available
|
||||
# on conda-forge
|
||||
endif()
|
||||
|
||||
if(AARE_VERBOSE)
|
||||
add_compile_definitions(AARE_VERBOSE)
|
||||
add_compile_definitions(AARE_LOG_LEVEL=aare::logDEBUG5)
|
||||
else()
|
||||
add_compile_definitions(AARE_LOG_LEVEL=aare::logINFOBLUE)
|
||||
endif()
|
||||
|
||||
if(AARE_CUSTOM_ASSERT)
|
||||
@ -74,18 +94,70 @@ if(AARE_BENCHMARKS)
|
||||
endif()
|
||||
|
||||
|
||||
|
||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||
|
||||
if(AARE_FETCH_LMFIT)
|
||||
#TODO! Should we fetch lmfit from the web or inlcude a tar.gz in the repo?
|
||||
set(LMFIT_PATCH_COMMAND git apply ${CMAKE_CURRENT_SOURCE_DIR}/patches/lmfit.patch)
|
||||
|
||||
# For cmake < 3.28 we can't supply EXCLUDE_FROM_ALL to FetchContent_Declare
|
||||
# so we need this workaround
|
||||
if (${CMAKE_VERSION} VERSION_LESS "3.28")
|
||||
FetchContent_Declare(
|
||||
lmfit
|
||||
GIT_REPOSITORY https://jugit.fz-juelich.de/mlz/lmfit.git
|
||||
GIT_TAG main
|
||||
PATCH_COMMAND ${LMFIT_PATCH_COMMAND}
|
||||
UPDATE_DISCONNECTED 1
|
||||
)
|
||||
else()
|
||||
FetchContent_Declare(
|
||||
lmfit
|
||||
GIT_REPOSITORY https://jugit.fz-juelich.de/mlz/lmfit.git
|
||||
GIT_TAG main
|
||||
PATCH_COMMAND ${LMFIT_PATCH_COMMAND}
|
||||
UPDATE_DISCONNECTED 1
|
||||
EXCLUDE_FROM_ALL 1
|
||||
)
|
||||
endif()
|
||||
|
||||
|
||||
#Disable what we don't need from lmfit
|
||||
set(BUILD_TESTING OFF CACHE BOOL "")
|
||||
set(LMFIT_CPPTEST OFF CACHE BOOL "")
|
||||
set(LIB_MAN OFF CACHE BOOL "")
|
||||
set(LMFIT_CPPTEST OFF CACHE BOOL "")
|
||||
set(BUILD_SHARED_LIBS OFF CACHE BOOL "")
|
||||
|
||||
if (${CMAKE_VERSION} VERSION_LESS "3.28")
|
||||
if(NOT lmfit_POPULATED)
|
||||
FetchContent_Populate(lmfit)
|
||||
add_subdirectory(${lmfit_SOURCE_DIR} ${lmfit_BINARY_DIR} EXCLUDE_FROM_ALL)
|
||||
endif()
|
||||
else()
|
||||
FetchContent_MakeAvailable(lmfit)
|
||||
endif()
|
||||
|
||||
set_property(TARGET lmfit PROPERTY POSITION_INDEPENDENT_CODE ON)
|
||||
else()
|
||||
find_package(lmfit REQUIRED)
|
||||
endif()
|
||||
|
||||
|
||||
if(AARE_FETCH_ZMQ)
|
||||
# Fetchcontent_Declare is deprecated need to find a way to update this
|
||||
# for now setting the policy to old is enough
|
||||
if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.30")
|
||||
cmake_policy(SET CMP0169 OLD)
|
||||
endif()
|
||||
set(ZMQ_PATCH_COMMAND git apply ${CMAKE_CURRENT_SOURCE_DIR}/patches/libzmq_cmake_version.patch)
|
||||
FetchContent_Declare(
|
||||
libzmq
|
||||
GIT_REPOSITORY https://github.com/zeromq/libzmq.git
|
||||
GIT_TAG v4.3.4
|
||||
PATCH_COMMAND ${ZMQ_PATCH_COMMAND}
|
||||
UPDATE_DISCONNECTED 1
|
||||
)
|
||||
# Disable unwanted options from libzmq
|
||||
set(BUILD_TESTS OFF CACHE BOOL "Switch off libzmq test build")
|
||||
@ -127,8 +199,8 @@ if (AARE_FETCH_FMT)
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||
INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
|
||||
)
|
||||
INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
|
||||
)
|
||||
else()
|
||||
find_package(fmt 6 REQUIRED)
|
||||
endif()
|
||||
@ -146,7 +218,6 @@ if (AARE_FETCH_JSON)
|
||||
install(
|
||||
TARGETS nlohmann_json
|
||||
EXPORT "${TARGETS_EXPORT_NAME}"
|
||||
|
||||
)
|
||||
message(STATUS "target: ${NLOHMANN_JSON_TARGET_NAME}")
|
||||
else()
|
||||
@ -279,15 +350,28 @@ endif()
|
||||
|
||||
set(PUBLICHEADERS
|
||||
include/aare/ArrayExpr.hpp
|
||||
include/aare/CalculateEta.hpp
|
||||
include/aare/Cluster.hpp
|
||||
include/aare/ClusterFinder.hpp
|
||||
include/aare/ClusterFile.hpp
|
||||
include/aare/CtbRawFile.hpp
|
||||
include/aare/ClusterVector.hpp
|
||||
include/aare/decode.hpp
|
||||
include/aare/type_traits.hpp
|
||||
include/aare/scan_parameters.hpp
|
||||
include/aare/to_string.hpp
|
||||
include/aare/string_utils.hpp
|
||||
include/aare/defs.hpp
|
||||
include/aare/Dtype.hpp
|
||||
include/aare/File.hpp
|
||||
include/aare/Fit.hpp
|
||||
include/aare/FileInterface.hpp
|
||||
include/aare/FilePtr.hpp
|
||||
include/aare/Frame.hpp
|
||||
include/aare/GainMap.hpp
|
||||
include/aare/geo_helpers.hpp
|
||||
include/aare/JungfrauDataFile.hpp
|
||||
include/aare/logger.hpp
|
||||
include/aare/NDArray.hpp
|
||||
include/aare/NDView.hpp
|
||||
include/aare/NumpyFile.hpp
|
||||
@ -298,33 +382,58 @@ set(PUBLICHEADERS
|
||||
include/aare/RawMasterFile.hpp
|
||||
include/aare/RawSubFile.hpp
|
||||
include/aare/VarClusterFinder.hpp
|
||||
|
||||
include/aare/utils/task.hpp
|
||||
)
|
||||
|
||||
|
||||
set(SourceFiles
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/CtbRawFile.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/ClusterFile.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/defs.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/to_string.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/string_utils.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Dtype.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/decode.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Frame.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/File.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/FilePtr.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Fit.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/geo_helpers.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/JungfrauDataFile.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/NumpyFile.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/NumpyHelpers.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Interpolator.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/PixelMap.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/RawFile.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/RawSubFile.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/RawMasterFile.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/utils/task.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/utils/ifstream_helpers.cpp
|
||||
)
|
||||
|
||||
# HDF5
|
||||
if (AARE_HDF5)
|
||||
find_package(HDF5 1.10 COMPONENTS CXX REQUIRED)
|
||||
add_definitions(
|
||||
${HDF5_DEFINITIONS}
|
||||
)
|
||||
list (APPEND PUBLICHEADERS
|
||||
include/aare/Hdf5File.hpp
|
||||
include/aare/Hdf5MasterFile.hpp
|
||||
)
|
||||
list (APPEND SourceFiles
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Hdf5File.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Hdf5MasterFile.cpp
|
||||
)
|
||||
endif (AARE_HDF5)
|
||||
|
||||
add_library(aare_core STATIC ${SourceFiles})
|
||||
target_include_directories(aare_core PUBLIC
|
||||
"$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>"
|
||||
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>"
|
||||
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>"
|
||||
)
|
||||
|
||||
|
||||
set(THREADS_PREFER_PTHREAD_FLAG ON)
|
||||
find_package(Threads REQUIRED)
|
||||
|
||||
target_link_libraries(
|
||||
aare_core
|
||||
@ -334,8 +443,21 @@ target_link_libraries(
|
||||
${STD_FS_LIB} # from helpers.cmake
|
||||
PRIVATE
|
||||
aare_compiler_flags
|
||||
Threads::Threads
|
||||
$<BUILD_INTERFACE:lmfit>
|
||||
|
||||
)
|
||||
|
||||
if (AARE_HDF5 AND HDF5_FOUND)
|
||||
add_definitions(-DHDF5_FOUND)
|
||||
target_link_libraries(aare_core PUBLIC
|
||||
${HDF5_LIBRARIES}
|
||||
)
|
||||
target_include_directories(aare_core PUBLIC
|
||||
${HDF5_INCLUDE_DIRS}
|
||||
)
|
||||
endif()
|
||||
|
||||
set_target_properties(aare_core PROPERTIES
|
||||
ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}
|
||||
PUBLIC_HEADER "${PUBLICHEADERS}"
|
||||
@ -347,25 +469,44 @@ endif()
|
||||
|
||||
if(AARE_TESTS)
|
||||
set(TestSources
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/algorithm.test.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/defs.test.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/to_string.test.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/scan_parameters.test.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/decode.test.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Dtype.test.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Frame.test.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/geo_helpers.test.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/RawMasterFile.test.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/NDArray.test.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/NDView.test.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/ClusterFinder.test.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/ClusterVector.test.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Cluster.test.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/CalculateEta.test.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/ClusterFile.test.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/ClusterFinderMT.test.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Pedestal.test.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/JungfrauDataFile.test.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/NumpyFile.test.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/NumpyHelpers.test.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/RawFile.test.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/RawSubFile.test.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/utils/task.test.cpp
|
||||
|
||||
)
|
||||
if(HDF5_FOUND)
|
||||
list (APPEND TestSources
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Hdf5MasterFile.test.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Hdf5File.test.cpp
|
||||
)
|
||||
endif()
|
||||
target_sources(tests PRIVATE ${TestSources} )
|
||||
endif()
|
||||
|
||||
|
||||
|
||||
|
||||
###------------------------------------------------------------------------------------------
|
||||
###------------------------------------------------------------------------------------------
|
||||
|
||||
@ -436,4 +577,4 @@ if(AARE_MASTER_PROJECT)
|
||||
set(CMAKE_INSTALL_DIR "share/cmake/${PROJECT_NAME}")
|
||||
set(PROJECT_LIBRARIES aare-core aare-compiler-flags )
|
||||
include(cmake/package_config.cmake)
|
||||
endif()
|
||||
endif()
|
||||
|
22
RELEASE.md
Normal file
22
RELEASE.md
Normal file
@ -0,0 +1,22 @@
|
||||
# Release notes
|
||||
|
||||
|
||||
### head
|
||||
|
||||
Features:
|
||||
|
||||
- Cluster finder now works with 5x5, 7x7 and 9x9 clusters
|
||||
|
||||
|
||||
### 2025.05.22
|
||||
|
||||
Features:
|
||||
|
||||
- Added scurve fitting
|
||||
|
||||
Bugfixes:
|
||||
|
||||
- Fixed crash when opening raw files with large number of data files
|
||||
|
||||
|
||||
|
@ -1,11 +1,27 @@
|
||||
find_package(benchmark REQUIRED)
|
||||
|
||||
add_executable(ndarray_benchmark ndarray_benchmark.cpp)
|
||||
include(FetchContent)
|
||||
|
||||
target_link_libraries(ndarray_benchmark benchmark::benchmark aare_core aare_compiler_flags)
|
||||
# target_link_libraries(tests PRIVATE aare_core aare_compiler_flags)
|
||||
|
||||
set_target_properties(ndarray_benchmark PROPERTIES
|
||||
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}
|
||||
# OUTPUT_NAME run_tests
|
||||
FetchContent_Declare(
|
||||
benchmark
|
||||
GIT_REPOSITORY https://github.com/google/benchmark.git
|
||||
GIT_TAG v1.8.3 # Change to the latest version if needed
|
||||
)
|
||||
|
||||
# Ensure Google Benchmark is built correctly
|
||||
set(BENCHMARK_ENABLE_TESTING OFF CACHE BOOL "" FORCE)
|
||||
|
||||
FetchContent_MakeAvailable(benchmark)
|
||||
|
||||
add_executable(benchmarks)
|
||||
|
||||
target_sources(benchmarks PRIVATE ndarray_benchmark.cpp calculateeta_benchmark.cpp)
|
||||
|
||||
# Link Google Benchmark and other necessary libraries
|
||||
target_link_libraries(benchmarks PRIVATE benchmark::benchmark aare_core aare_compiler_flags)
|
||||
|
||||
# Set output properties
|
||||
set_target_properties(benchmarks PROPERTIES
|
||||
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}
|
||||
OUTPUT_NAME run_benchmarks
|
||||
)
|
70
benchmarks/calculateeta_benchmark.cpp
Normal file
70
benchmarks/calculateeta_benchmark.cpp
Normal file
@ -0,0 +1,70 @@
|
||||
#include "aare/CalculateEta.hpp"
|
||||
#include "aare/ClusterFile.hpp"
|
||||
#include <benchmark/benchmark.h>
|
||||
|
||||
using namespace aare;
|
||||
|
||||
class ClusterFixture : public benchmark::Fixture {
|
||||
public:
|
||||
Cluster<int, 2, 2> cluster_2x2{};
|
||||
Cluster<int, 3, 3> cluster_3x3{};
|
||||
|
||||
private:
|
||||
using benchmark::Fixture::SetUp;
|
||||
|
||||
void SetUp([[maybe_unused]] const benchmark::State &state) override {
|
||||
int temp_data[4] = {1, 2, 3, 1};
|
||||
std::copy(std::begin(temp_data), std::end(temp_data),
|
||||
std::begin(cluster_2x2.data));
|
||||
|
||||
cluster_2x2.x = 0;
|
||||
cluster_2x2.y = 0;
|
||||
|
||||
int temp_data2[9] = {1, 2, 3, 1, 3, 4, 5, 1, 20};
|
||||
std::copy(std::begin(temp_data2), std::end(temp_data2),
|
||||
std::begin(cluster_3x3.data));
|
||||
|
||||
cluster_3x3.x = 0;
|
||||
cluster_3x3.y = 0;
|
||||
}
|
||||
|
||||
// void TearDown(::benchmark::State& state) {
|
||||
// }
|
||||
};
|
||||
|
||||
BENCHMARK_F(ClusterFixture, Calculate2x2Eta)(benchmark::State &st) {
|
||||
for (auto _ : st) {
|
||||
// This code gets timed
|
||||
Eta2 eta = calculate_eta2(cluster_2x2);
|
||||
benchmark::DoNotOptimize(eta);
|
||||
}
|
||||
}
|
||||
|
||||
// almost takes double the time
|
||||
BENCHMARK_F(ClusterFixture, CalculateGeneralEtaFor2x2Cluster)
|
||||
(benchmark::State &st) {
|
||||
for (auto _ : st) {
|
||||
// This code gets timed
|
||||
Eta2 eta = calculate_eta2<int, 2, 2>(cluster_2x2);
|
||||
benchmark::DoNotOptimize(eta);
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_F(ClusterFixture, Calculate3x3Eta)(benchmark::State &st) {
|
||||
for (auto _ : st) {
|
||||
// This code gets timed
|
||||
Eta2 eta = calculate_eta2(cluster_3x3);
|
||||
benchmark::DoNotOptimize(eta);
|
||||
}
|
||||
}
|
||||
|
||||
// almost takes double the time
|
||||
BENCHMARK_F(ClusterFixture, CalculateGeneralEtaFor3x3Cluster)
|
||||
(benchmark::State &st) {
|
||||
for (auto _ : st) {
|
||||
// This code gets timed
|
||||
Eta2 eta = calculate_eta2<int, 3, 3>(cluster_3x3);
|
||||
benchmark::DoNotOptimize(eta);
|
||||
}
|
||||
}
|
||||
// BENCHMARK_MAIN();
|
@ -1,136 +1,132 @@
|
||||
#include <benchmark/benchmark.h>
|
||||
#include "aare/NDArray.hpp"
|
||||
|
||||
#include <benchmark/benchmark.h>
|
||||
|
||||
using aare::NDArray;
|
||||
|
||||
constexpr ssize_t size = 1024;
|
||||
class TwoArrays : public benchmark::Fixture {
|
||||
public:
|
||||
NDArray<int,2> a{{size,size},0};
|
||||
NDArray<int,2> b{{size,size},0};
|
||||
void SetUp(::benchmark::State& state) {
|
||||
for(uint32_t i = 0; i < size; i++){
|
||||
for(uint32_t j = 0; j < size; j++){
|
||||
a(i, j)= i*j+1;
|
||||
b(i, j)= i*j+1;
|
||||
}
|
||||
public:
|
||||
NDArray<int, 2> a{{size, size}, 0};
|
||||
NDArray<int, 2> b{{size, size}, 0};
|
||||
void SetUp(::benchmark::State &state) {
|
||||
for (uint32_t i = 0; i < size; i++) {
|
||||
for (uint32_t j = 0; j < size; j++) {
|
||||
a(i, j) = i * j + 1;
|
||||
b(i, j) = i * j + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// void TearDown(::benchmark::State& state) {
|
||||
// }
|
||||
// void TearDown(::benchmark::State& state) {
|
||||
// }
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
BENCHMARK_F(TwoArrays, AddWithOperator)(benchmark::State& st) {
|
||||
for (auto _ : st) {
|
||||
// This code gets timed
|
||||
NDArray<int,2> res = a+b;
|
||||
benchmark::DoNotOptimize(res);
|
||||
}
|
||||
}
|
||||
BENCHMARK_F(TwoArrays, AddWithIndex)(benchmark::State& st) {
|
||||
for (auto _ : st) {
|
||||
// This code gets timed
|
||||
NDArray<int,2> res(a.shape());
|
||||
for (uint32_t i = 0; i < a.size(); i++) {
|
||||
res(i) = a(i) + b(i);
|
||||
BENCHMARK_F(TwoArrays, AddWithOperator)(benchmark::State &st) {
|
||||
for (auto _ : st) {
|
||||
// This code gets timed
|
||||
NDArray<int, 2> res = a + b;
|
||||
benchmark::DoNotOptimize(res);
|
||||
}
|
||||
}
|
||||
BENCHMARK_F(TwoArrays, AddWithIndex)(benchmark::State &st) {
|
||||
for (auto _ : st) {
|
||||
// This code gets timed
|
||||
NDArray<int, 2> res(a.shape());
|
||||
for (uint32_t i = 0; i < a.size(); i++) {
|
||||
res(i) = a(i) + b(i);
|
||||
}
|
||||
benchmark::DoNotOptimize(res);
|
||||
}
|
||||
benchmark::DoNotOptimize(res);
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_F(TwoArrays, SubtractWithOperator)(benchmark::State& st) {
|
||||
for (auto _ : st) {
|
||||
// This code gets timed
|
||||
NDArray<int,2> res = a-b;
|
||||
benchmark::DoNotOptimize(res);
|
||||
}
|
||||
}
|
||||
BENCHMARK_F(TwoArrays, SubtractWithIndex)(benchmark::State& st) {
|
||||
for (auto _ : st) {
|
||||
// This code gets timed
|
||||
NDArray<int,2> res(a.shape());
|
||||
for (uint32_t i = 0; i < a.size(); i++) {
|
||||
res(i) = a(i) - b(i);
|
||||
BENCHMARK_F(TwoArrays, SubtractWithOperator)(benchmark::State &st) {
|
||||
for (auto _ : st) {
|
||||
// This code gets timed
|
||||
NDArray<int, 2> res = a - b;
|
||||
benchmark::DoNotOptimize(res);
|
||||
}
|
||||
}
|
||||
BENCHMARK_F(TwoArrays, SubtractWithIndex)(benchmark::State &st) {
|
||||
for (auto _ : st) {
|
||||
// This code gets timed
|
||||
NDArray<int, 2> res(a.shape());
|
||||
for (uint32_t i = 0; i < a.size(); i++) {
|
||||
res(i) = a(i) - b(i);
|
||||
}
|
||||
benchmark::DoNotOptimize(res);
|
||||
}
|
||||
benchmark::DoNotOptimize(res);
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_F(TwoArrays, MultiplyWithOperator)(benchmark::State& st) {
|
||||
for (auto _ : st) {
|
||||
// This code gets timed
|
||||
NDArray<int,2> res = a*b;
|
||||
benchmark::DoNotOptimize(res);
|
||||
}
|
||||
}
|
||||
BENCHMARK_F(TwoArrays, MultiplyWithIndex)(benchmark::State& st) {
|
||||
for (auto _ : st) {
|
||||
// This code gets timed
|
||||
NDArray<int,2> res(a.shape());
|
||||
for (uint32_t i = 0; i < a.size(); i++) {
|
||||
res(i) = a(i) * b(i);
|
||||
BENCHMARK_F(TwoArrays, MultiplyWithOperator)(benchmark::State &st) {
|
||||
for (auto _ : st) {
|
||||
// This code gets timed
|
||||
NDArray<int, 2> res = a * b;
|
||||
benchmark::DoNotOptimize(res);
|
||||
}
|
||||
}
|
||||
BENCHMARK_F(TwoArrays, MultiplyWithIndex)(benchmark::State &st) {
|
||||
for (auto _ : st) {
|
||||
// This code gets timed
|
||||
NDArray<int, 2> res(a.shape());
|
||||
for (uint32_t i = 0; i < a.size(); i++) {
|
||||
res(i) = a(i) * b(i);
|
||||
}
|
||||
benchmark::DoNotOptimize(res);
|
||||
}
|
||||
benchmark::DoNotOptimize(res);
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_F(TwoArrays, DivideWithOperator)(benchmark::State& st) {
|
||||
for (auto _ : st) {
|
||||
// This code gets timed
|
||||
NDArray<int,2> res = a/b;
|
||||
benchmark::DoNotOptimize(res);
|
||||
}
|
||||
}
|
||||
BENCHMARK_F(TwoArrays, DivideWithIndex)(benchmark::State& st) {
|
||||
for (auto _ : st) {
|
||||
// This code gets timed
|
||||
NDArray<int,2> res(a.shape());
|
||||
for (uint32_t i = 0; i < a.size(); i++) {
|
||||
res(i) = a(i) / b(i);
|
||||
BENCHMARK_F(TwoArrays, DivideWithOperator)(benchmark::State &st) {
|
||||
for (auto _ : st) {
|
||||
// This code gets timed
|
||||
NDArray<int, 2> res = a / b;
|
||||
benchmark::DoNotOptimize(res);
|
||||
}
|
||||
}
|
||||
BENCHMARK_F(TwoArrays, DivideWithIndex)(benchmark::State &st) {
|
||||
for (auto _ : st) {
|
||||
// This code gets timed
|
||||
NDArray<int, 2> res(a.shape());
|
||||
for (uint32_t i = 0; i < a.size(); i++) {
|
||||
res(i) = a(i) / b(i);
|
||||
}
|
||||
benchmark::DoNotOptimize(res);
|
||||
}
|
||||
benchmark::DoNotOptimize(res);
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_F(TwoArrays, FourAddWithOperator)(benchmark::State& st) {
|
||||
for (auto _ : st) {
|
||||
// This code gets timed
|
||||
NDArray<int,2> res = a+b+a+b;
|
||||
benchmark::DoNotOptimize(res);
|
||||
}
|
||||
}
|
||||
BENCHMARK_F(TwoArrays, FourAddWithIndex)(benchmark::State& st) {
|
||||
for (auto _ : st) {
|
||||
// This code gets timed
|
||||
NDArray<int,2> res(a.shape());
|
||||
for (uint32_t i = 0; i < a.size(); i++) {
|
||||
res(i) = a(i) + b(i) + a(i) + b(i);
|
||||
BENCHMARK_F(TwoArrays, FourAddWithOperator)(benchmark::State &st) {
|
||||
for (auto _ : st) {
|
||||
// This code gets timed
|
||||
NDArray<int, 2> res = a + b + a + b;
|
||||
benchmark::DoNotOptimize(res);
|
||||
}
|
||||
}
|
||||
BENCHMARK_F(TwoArrays, FourAddWithIndex)(benchmark::State &st) {
|
||||
for (auto _ : st) {
|
||||
// This code gets timed
|
||||
NDArray<int, 2> res(a.shape());
|
||||
for (uint32_t i = 0; i < a.size(); i++) {
|
||||
res(i) = a(i) + b(i) + a(i) + b(i);
|
||||
}
|
||||
benchmark::DoNotOptimize(res);
|
||||
}
|
||||
benchmark::DoNotOptimize(res);
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_F(TwoArrays, MultiplyAddDivideWithOperator)(benchmark::State& st) {
|
||||
for (auto _ : st) {
|
||||
// This code gets timed
|
||||
NDArray<int,2> res = a*a+b/a;
|
||||
benchmark::DoNotOptimize(res);
|
||||
}
|
||||
}
|
||||
BENCHMARK_F(TwoArrays, MultiplyAddDivideWithIndex)(benchmark::State& st) {
|
||||
for (auto _ : st) {
|
||||
// This code gets timed
|
||||
NDArray<int,2> res(a.shape());
|
||||
for (uint32_t i = 0; i < a.size(); i++) {
|
||||
res(i) = a(i) * a(i) + b(i) / a(i);
|
||||
BENCHMARK_F(TwoArrays, MultiplyAddDivideWithOperator)(benchmark::State &st) {
|
||||
for (auto _ : st) {
|
||||
// This code gets timed
|
||||
NDArray<int, 2> res = a * a + b / a;
|
||||
benchmark::DoNotOptimize(res);
|
||||
}
|
||||
}
|
||||
BENCHMARK_F(TwoArrays, MultiplyAddDivideWithIndex)(benchmark::State &st) {
|
||||
for (auto _ : st) {
|
||||
// This code gets timed
|
||||
NDArray<int, 2> res(a.shape());
|
||||
for (uint32_t i = 0; i < a.size(); i++) {
|
||||
res(i) = a(i) * a(i) + b(i) / a(i);
|
||||
}
|
||||
benchmark::DoNotOptimize(res);
|
||||
}
|
||||
benchmark::DoNotOptimize(res);
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_MAIN();
|
@ -1,28 +1,5 @@
|
||||
python:
|
||||
- 3.11
|
||||
- 3.11
|
||||
- 3.11
|
||||
- 3.12
|
||||
- 3.12
|
||||
- 3.12
|
||||
- 3.13
|
||||
|
||||
|
||||
|
||||
numpy:
|
||||
- 1.26
|
||||
- 2.0
|
||||
- 2.1
|
||||
- 1.26
|
||||
- 2.0
|
||||
- 2.1
|
||||
- 2.1
|
||||
|
||||
|
||||
zip_keys:
|
||||
- python
|
||||
- numpy
|
||||
|
||||
pin_run_as_build:
|
||||
numpy: x.x
|
||||
python: x.x
|
@ -1,7 +1,10 @@
|
||||
source:
|
||||
path: ../
|
||||
|
||||
{% set version = load_file_regex(load_file = 'VERSION', regex_pattern = '(\d+(?:\.\d+)*(?:[\+\w\.]+))').group(1) %}
|
||||
package:
|
||||
name: aare
|
||||
version: 2024.12.16.dev0 #TODO! how to not duplicate this?
|
||||
|
||||
version: {{version}}
|
||||
|
||||
source:
|
||||
path: ..
|
||||
@ -9,44 +12,39 @@ source:
|
||||
build:
|
||||
number: 0
|
||||
script:
|
||||
- unset CMAKE_GENERATOR && {{ PYTHON }} -m pip install . -vv # [not win]
|
||||
- {{ PYTHON }} -m pip install . -vv # [win]
|
||||
- unset CMAKE_GENERATOR && {{ PYTHON }} -m pip install . -vv
|
||||
|
||||
requirements:
|
||||
build:
|
||||
- python {{python}}
|
||||
- numpy {{ numpy }}
|
||||
- {{ compiler('cxx') }}
|
||||
|
||||
|
||||
host:
|
||||
- cmake
|
||||
- ninja
|
||||
- python {{python}}
|
||||
- numpy {{ numpy }}
|
||||
|
||||
host:
|
||||
- python
|
||||
- pip
|
||||
- numpy=2.1
|
||||
- scikit-build-core
|
||||
- pybind11 >=2.13.0
|
||||
- fmt
|
||||
- zeromq
|
||||
- nlohmann_json
|
||||
- catch2
|
||||
- matplotlib # needed in host to solve the environment for run
|
||||
|
||||
run:
|
||||
- python {{python}}
|
||||
- numpy {{ numpy }}
|
||||
- python
|
||||
- {{ pin_compatible('numpy') }}
|
||||
- matplotlib
|
||||
|
||||
|
||||
|
||||
test:
|
||||
imports:
|
||||
- aare
|
||||
# requires:
|
||||
# - pytest
|
||||
# source_files:
|
||||
# - tests
|
||||
# commands:
|
||||
# - pytest tests
|
||||
requires:
|
||||
- pytest
|
||||
- boost-histogram
|
||||
source_files:
|
||||
- python/tests
|
||||
commands:
|
||||
- python -m pytest python/tests
|
||||
|
||||
about:
|
||||
summary: An example project built with pybind11 and scikit-build.
|
||||
# license_file: LICENSE
|
||||
summary: Data analysis library for hybrid pixel detectors from PSI
|
||||
|
@ -12,28 +12,6 @@ set(SPHINX_BUILD ${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
|
||||
file(GLOB SPHINX_SOURCE_FILES CONFIGURE_DEPENDS "src/*.rst")
|
||||
# set(SPHINX_SOURCE_FILES
|
||||
# src/index.rst
|
||||
# src/Installation.rst
|
||||
# src/Requirements.rst
|
||||
# src/NDArray.rst
|
||||
# src/NDView.rst
|
||||
# src/File.rst
|
||||
# src/Frame.rst
|
||||
# src/Dtype.rst
|
||||
# src/ClusterFinder.rst
|
||||
# src/ClusterFile.rst
|
||||
# src/Pedestal.rst
|
||||
# src/RawFile.rst
|
||||
# src/RawSubFile.rst
|
||||
# src/RawMasterFile.rst
|
||||
# src/VarClusterFinder.rst
|
||||
# src/pyVarClusterFinder.rst
|
||||
# src/pyFile.rst
|
||||
# src/pyCtbRawFile.rst
|
||||
# src/pyRawFile.rst
|
||||
# src/pyRawMasterFile.rst
|
||||
# )
|
||||
|
||||
|
||||
foreach(filename ${SPHINX_SOURCE_FILES})
|
||||
|
7
docs/src/ClusterFinderMT.rst
Normal file
7
docs/src/ClusterFinderMT.rst
Normal file
@ -0,0 +1,7 @@
|
||||
ClusterFinderMT
|
||||
==================
|
||||
|
||||
|
||||
.. doxygenclass:: aare::ClusterFinderMT
|
||||
:members:
|
||||
:undoc-members:
|
8
docs/src/Hdf5File.rst
Normal file
8
docs/src/Hdf5File.rst
Normal file
@ -0,0 +1,8 @@
|
||||
Hdf5File
|
||||
===============
|
||||
|
||||
|
||||
.. doxygenclass:: aare::Hdf5File
|
||||
:members:
|
||||
:undoc-members:
|
||||
:private-members:
|
14
docs/src/Hdf5MasterFile.rst
Normal file
14
docs/src/Hdf5MasterFile.rst
Normal file
@ -0,0 +1,14 @@
|
||||
Hdf5MasterFile
|
||||
===============
|
||||
|
||||
|
||||
.. doxygenclass:: aare::Hdf5MasterFile
|
||||
:members:
|
||||
:undoc-members:
|
||||
:private-members:
|
||||
|
||||
|
||||
.. doxygenclass:: aare::Hdf5FileNameComponents
|
||||
:members:
|
||||
:undoc-members:
|
||||
:private-members:
|
25
docs/src/JungfrauDataFile.rst
Normal file
25
docs/src/JungfrauDataFile.rst
Normal file
@ -0,0 +1,25 @@
|
||||
JungfrauDataFile
|
||||
==================
|
||||
|
||||
JungfrauDataFile is a class to read the .dat files that are produced by Aldo's receiver.
|
||||
It is mostly used for calibration.
|
||||
|
||||
The structure of the file is:
|
||||
|
||||
* JungfrauDataHeader
|
||||
* Binary data (256x256, 256x1024 or 512x1024)
|
||||
* JungfrauDataHeader
|
||||
* ...
|
||||
|
||||
There is no metadata indicating number of frames or the size of the image, but this
|
||||
will be infered by this reader.
|
||||
|
||||
.. doxygenstruct:: aare::JungfrauDataHeader
|
||||
:members:
|
||||
:undoc-members:
|
||||
:private-members:
|
||||
|
||||
.. doxygenclass:: aare::JungfrauDataFile
|
||||
:members:
|
||||
:undoc-members:
|
||||
:private-members:
|
47
docs/src/Philosophy.rst
Normal file
47
docs/src/Philosophy.rst
Normal file
@ -0,0 +1,47 @@
|
||||
****************
|
||||
Philosophy
|
||||
****************
|
||||
|
||||
|
||||
Fast code with a simple interface
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Aare should be fast and efficient, but also easy to use. We strive to keep a simple interface that feels intuitive.
|
||||
Internally we use C++ for performance and the ability to integrate the library in other programs, but we see most
|
||||
users using the Python interface.
|
||||
|
||||
Live at head
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
As a user of the library you should be able to, and is expected to, use the latest version. Bug fixes will rarely be backported
|
||||
to older releases. By upgrading frequently you will benefit from the latest features and minimize the effort to maintain your scripts/code
|
||||
by doing several small upgrades instead of one big upgrade.
|
||||
|
||||
API
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
We aim to keep the API stable and only break it for good reasons. But specially now in the early stages of development
|
||||
the API will change. On those occasions it will be clearly stated in the release notes. However, the norm should be a
|
||||
backward compatible API.
|
||||
|
||||
Documentation
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Being a library it is important to have a well documented API. We use Doxygen to generate the C++ documentation
|
||||
and Sphinx for the Python part. Breathe is used to integrate the two into one Sphinx html site. The documentation is built
|
||||
automatically on release by the CI and published to GitHub pages. In addition to the generated API documentation,
|
||||
certain classes might need more descriptions of the usage. This is then placed in the .rst files in the docs/src directory.
|
||||
|
||||
.. attention::
|
||||
|
||||
The code should be well documented, but using descriptive names is more important. In the same spirit
|
||||
if a function is called `getNumberOfFrames()` you don't need to write a comment saying that it gets the
|
||||
number of frames.
|
||||
|
||||
|
||||
Dependencies
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Deployment in the scientific community is often tricky. Either due to old OS versions or the lack of package managers.
|
||||
We strive to keep the dependencies to a minimum and will vendor some libraries to simplify deployment even though it comes
|
||||
at a cost of build time.
|
@ -2,18 +2,21 @@ Requirements
|
||||
==============================================
|
||||
|
||||
- C++17 compiler (gcc 8/clang 7)
|
||||
- CMake 3.14+
|
||||
- CMake 3.15+
|
||||
|
||||
**Internally used libraries**
|
||||
|
||||
.. note ::
|
||||
|
||||
These can also be picked up from the system/conda environment by specifying:
|
||||
To save compile time some of the dependencies can also be picked up from the system/conda environment by specifying:
|
||||
-DAARE_SYSTEM_LIBRARIES=ON during the cmake configuration.
|
||||
|
||||
- pybind11
|
||||
To simplify deployment we build and statically link a few libraries.
|
||||
|
||||
- fmt
|
||||
- lmfit - https://jugit.fz-juelich.de/mlz/lmfit
|
||||
- nlohmann_json
|
||||
- pybind11
|
||||
- ZeroMQ
|
||||
|
||||
**Extra dependencies for building documentation**
|
||||
|
47
docs/src/Tests.rst
Normal file
47
docs/src/Tests.rst
Normal file
@ -0,0 +1,47 @@
|
||||
****************
|
||||
Tests
|
||||
****************
|
||||
|
||||
We test the code both from the C++ and Python API. By default only tests that does not require image data is run.
|
||||
|
||||
C++
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. -DAARE_TESTS=ON
|
||||
make -j 4
|
||||
|
||||
export AARE_TEST_DATA=/path/to/test/data
|
||||
./run_test [.files] #or using ctest, [.files] is the option to include tests needing data
|
||||
|
||||
|
||||
|
||||
Python
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
#From the root dir of the library
|
||||
python -m pytest python/tests --files # passing --files will run the tests needing data
|
||||
|
||||
|
||||
|
||||
Getting the test data
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. attention ::
|
||||
|
||||
The tests needing the test data are not run by default. To make the data available, you need to set the environment variable
|
||||
AARE_TEST_DATA to the path of the test data directory. Then pass either [.files] for the C++ tests or --files for Python
|
||||
|
||||
The image files needed for the test are large and are not included in the repository. They are stored
|
||||
using GIT LFS in a separate repository. To get the test data, you need to clone the repository.
|
||||
To do this, you need to have GIT LFS installed. You can find instructions on how to install it here: https://git-lfs.github.com/
|
||||
Once you have GIT LFS installed, you can clone the repository like any normal repo using:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
git clone https://gitea.psi.ch/detectors/aare-test-data.git
|
86
docs/src/Workflow.rst
Normal file
86
docs/src/Workflow.rst
Normal file
@ -0,0 +1,86 @@
|
||||
****************
|
||||
Workflow
|
||||
****************
|
||||
|
||||
This page describes how we develop aare.
|
||||
|
||||
GitHub centric
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
We use GitHub for all development. Issues and pull requests provide a platform for collaboration as well
|
||||
as a record of the development process. Even if we discuss things in person, we record the outcome in an issue.
|
||||
If a particular implementation is chosen over another, the reason should be recorded in the pull request.
|
||||
|
||||
|
||||
Branches
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
We aim for an as lightweight branching strategy as possible. Short-lived feature branches are merged back into main.
|
||||
The main branch is expected to always be in a releasable state. A release is simply a tag on main which provides a
|
||||
reference and triggers the CI to build the release artifacts (conda, pypi etc.). For large features consider merging
|
||||
smaller chunks into main as they are completed, rather than waiting for the entire feature to be finished. Worst case
|
||||
make sure your feature branch merges with main regularly to avoid large merge conflicts later on.
|
||||
|
||||
.. note::
|
||||
|
||||
The main branch is expected to always work. Feel free to pull from main instead of sticking to a
|
||||
release
|
||||
|
||||
|
||||
Releases
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Release early, release often. As soon as "enough" new features have been implemented, a release is created.
|
||||
A release should not be a big thing, rather a routine part of development that does not require any special person or
|
||||
unfamiliar steps.
|
||||
|
||||
|
||||
|
||||
Checklists for deployment
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
**Feature:**
|
||||
|
||||
#. Create a new issue for the feature (label feature)
|
||||
#. Create a new branch from main.
|
||||
#. Implement the feature including test and documentation
|
||||
#. Add the feature to RELEASE.md under head
|
||||
#. Create a pull request linked to the issue
|
||||
#. Code is reviewed by at least one other person
|
||||
#. Once approved, the branch is merged into main
|
||||
|
||||
|
||||
**BugFix:**
|
||||
|
||||
Essentially the same as for a feature, if possible start with
|
||||
a failing test that demonstrates the bug.
|
||||
|
||||
#. Create a new issue for the bug (label bug)
|
||||
#. Create a new branch from main.
|
||||
#. **Write a test that fails for the bug**
|
||||
#. Implement the fix
|
||||
#. **Run the test to ensure it passes**
|
||||
#. Add the bugfix to RELEASE.md under head
|
||||
#. Create a pull request linked to the issue.
|
||||
#. Code is reviewed by at least one other person
|
||||
#. Once approved, the branch is merged into main
|
||||
|
||||
**Release:**
|
||||
|
||||
#. Once "enough" new features have been implemented, a release is created
|
||||
#. Update RELEASE.md with the tag of the release and verify that it is complete
|
||||
#. Create the release in GitHub describing the new features and bug fixes
|
||||
#. CI makes magic
|
||||
|
||||
|
||||
**Update documentation only:**
|
||||
|
||||
.. attention::
|
||||
|
||||
It's possible to update the documentation without changing the code, but take
|
||||
care since the docs will reflect the code in main and not the latest release.
|
||||
|
||||
#. Create a PR to main with the documentation changes
|
||||
#. Create a pull request linked to the issue.
|
||||
#. Code is reviewed by at least one other person
|
||||
#. Once merged you can manually trigger the CI workflow for documentation
|
5
docs/src/algorithm.rst
Normal file
5
docs/src/algorithm.rst
Normal file
@ -0,0 +1,5 @@
|
||||
algorithm
|
||||
=============
|
||||
|
||||
.. doxygenfile:: algorithm.hpp
|
||||
|
@ -20,9 +20,6 @@ AARE
|
||||
Requirements
|
||||
Consume
|
||||
|
||||
|
||||
|
||||
|
||||
.. toctree::
|
||||
:caption: Python API
|
||||
:maxdepth: 1
|
||||
@ -30,29 +27,46 @@ AARE
|
||||
pyFile
|
||||
pyCtbRawFile
|
||||
pyClusterFile
|
||||
pyClusterVector
|
||||
pyJungfrauDataFile
|
||||
pyRawFile
|
||||
pyRawMasterFile
|
||||
pyHdf5File
|
||||
pyHdf5MasterFile
|
||||
pyVarClusterFinder
|
||||
|
||||
pyFit
|
||||
|
||||
|
||||
.. toctree::
|
||||
:caption: C++ API
|
||||
:maxdepth: 1
|
||||
|
||||
algorithm
|
||||
NDArray
|
||||
NDView
|
||||
Frame
|
||||
File
|
||||
Dtype
|
||||
ClusterFinder
|
||||
ClusterFinderMT
|
||||
ClusterFile
|
||||
ClusterVector
|
||||
JungfrauDataFile
|
||||
Pedestal
|
||||
RawFile
|
||||
RawSubFile
|
||||
RawMasterFile
|
||||
Hdf5File
|
||||
Hdf5MasterFile
|
||||
VarClusterFinder
|
||||
|
||||
|
||||
|
||||
|
||||
.. toctree::
|
||||
:caption: Developer
|
||||
:maxdepth: 3
|
||||
|
||||
Philosophy
|
||||
Workflow
|
||||
Tests
|
33
docs/src/pyClusterVector.rst
Normal file
33
docs/src/pyClusterVector.rst
Normal file
@ -0,0 +1,33 @@
|
||||
ClusterVector
|
||||
================
|
||||
|
||||
The ClusterVector, holds clusters from the ClusterFinder. Since it is templated
|
||||
in C++ we use a suffix indicating the data type in python. The suffix is
|
||||
``_i`` for integer, ``_f`` for float, and ``_d`` for double.
|
||||
|
||||
At the moment the functionality from python is limited and it is not supported
|
||||
to push_back clusters to the vector. The intended use case is to pass it to
|
||||
C++ functions that support the ClusterVector or to view it as a numpy array.
|
||||
|
||||
**View ClusterVector as numpy array**
|
||||
|
||||
.. code:: python
|
||||
|
||||
from aare import ClusterFile
|
||||
with ClusterFile("path/to/file") as f:
|
||||
cluster_vector = f.read_frame()
|
||||
|
||||
# Create a copy of the cluster data in a numpy array
|
||||
clusters = np.array(cluster_vector)
|
||||
|
||||
# Avoid copying the data by passing copy=False
|
||||
clusters = np.array(cluster_vector, copy = False)
|
||||
|
||||
|
||||
.. py:currentmodule:: aare
|
||||
|
||||
.. autoclass:: ClusterVector_i
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
:inherited-members:
|
19
docs/src/pyFit.rst
Normal file
19
docs/src/pyFit.rst
Normal file
@ -0,0 +1,19 @@
|
||||
|
||||
Fit
|
||||
========
|
||||
|
||||
.. py:currentmodule:: aare
|
||||
|
||||
|
||||
**Functions**
|
||||
|
||||
.. autofunction:: gaus
|
||||
|
||||
.. autofunction:: pol1
|
||||
|
||||
|
||||
**Fitting**
|
||||
|
||||
.. autofunction:: fit_gaus
|
||||
|
||||
.. autofunction:: fit_pol1
|
10
docs/src/pyHdf5File.rst
Normal file
10
docs/src/pyHdf5File.rst
Normal file
@ -0,0 +1,10 @@
|
||||
Hdf5File
|
||||
===================
|
||||
|
||||
.. py:currentmodule:: aare
|
||||
|
||||
.. autoclass:: Hdf5File
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
:inherited-members:
|
10
docs/src/pyHdf5MasterFile.rst
Normal file
10
docs/src/pyHdf5MasterFile.rst
Normal file
@ -0,0 +1,10 @@
|
||||
Hdf5MasterFile
|
||||
===================
|
||||
|
||||
.. py:currentmodule:: aare
|
||||
|
||||
.. autoclass:: Hdf5MasterFile
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
:inherited-members:
|
10
docs/src/pyJungfrauDataFile.rst
Normal file
10
docs/src/pyJungfrauDataFile.rst
Normal file
@ -0,0 +1,10 @@
|
||||
JungfrauDataFile
|
||||
===================
|
||||
|
||||
.. py:currentmodule:: aare
|
||||
|
||||
.. autoclass:: JungfrauDataFile
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
:inherited-members:
|
13
etc/dev-env.yml
Normal file
13
etc/dev-env.yml
Normal file
@ -0,0 +1,13 @@
|
||||
name: dev-environment
|
||||
channels:
|
||||
- conda-forge
|
||||
dependencies:
|
||||
- anaconda-client
|
||||
- conda-build
|
||||
- doxygen
|
||||
- sphinx=7.1.2
|
||||
- breathe
|
||||
- sphinx_rtd_theme
|
||||
- furo
|
||||
- zeromq
|
||||
|
@ -1,22 +1,25 @@
|
||||
#pragma once
|
||||
#include <cstdint> //int64_t
|
||||
#include <cstddef> //size_t
|
||||
#include <array>
|
||||
|
||||
#include "aare/defs.hpp"
|
||||
#include <array>
|
||||
#include <cassert>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
namespace aare {
|
||||
|
||||
template <typename E, int64_t Ndim> class ArrayExpr {
|
||||
template <typename E, ssize_t Ndim> class ArrayExpr {
|
||||
public:
|
||||
static constexpr bool is_leaf = false;
|
||||
|
||||
auto operator[](size_t i) const { return static_cast<E const &>(*this)[i]; }
|
||||
auto operator()(size_t i) const { return static_cast<E const &>(*this)[i]; }
|
||||
auto size() const { return static_cast<E const &>(*this).size(); }
|
||||
std::array<int64_t, Ndim> shape() const { return static_cast<E const &>(*this).shape(); }
|
||||
std::array<ssize_t, Ndim> shape() const {
|
||||
return static_cast<E const &>(*this).shape();
|
||||
}
|
||||
};
|
||||
|
||||
template <typename A, typename B, int64_t Ndim>
|
||||
template <typename A, typename B, ssize_t Ndim>
|
||||
class ArrayAdd : public ArrayExpr<ArrayAdd<A, B, Ndim>, Ndim> {
|
||||
const A &arr1_;
|
||||
const B &arr2_;
|
||||
@ -27,10 +30,10 @@ class ArrayAdd : public ArrayExpr<ArrayAdd<A, B, Ndim>, Ndim> {
|
||||
}
|
||||
auto operator[](int i) const { return arr1_[i] + arr2_[i]; }
|
||||
size_t size() const { return arr1_.size(); }
|
||||
std::array<int64_t, Ndim> shape() const { return arr1_.shape(); }
|
||||
std::array<ssize_t, Ndim> shape() const { return arr1_.shape(); }
|
||||
};
|
||||
|
||||
template <typename A, typename B, int64_t Ndim>
|
||||
template <typename A, typename B, ssize_t Ndim>
|
||||
class ArraySub : public ArrayExpr<ArraySub<A, B, Ndim>, Ndim> {
|
||||
const A &arr1_;
|
||||
const B &arr2_;
|
||||
@ -41,11 +44,11 @@ class ArraySub : public ArrayExpr<ArraySub<A, B, Ndim>, Ndim> {
|
||||
}
|
||||
auto operator[](int i) const { return arr1_[i] - arr2_[i]; }
|
||||
size_t size() const { return arr1_.size(); }
|
||||
std::array<int64_t, Ndim> shape() const { return arr1_.shape(); }
|
||||
std::array<ssize_t, Ndim> shape() const { return arr1_.shape(); }
|
||||
};
|
||||
|
||||
template <typename A, typename B, int64_t Ndim>
|
||||
class ArrayMul : public ArrayExpr<ArrayMul<A, B, Ndim>,Ndim> {
|
||||
template <typename A, typename B, ssize_t Ndim>
|
||||
class ArrayMul : public ArrayExpr<ArrayMul<A, B, Ndim>, Ndim> {
|
||||
const A &arr1_;
|
||||
const B &arr2_;
|
||||
|
||||
@ -55,10 +58,10 @@ class ArrayMul : public ArrayExpr<ArrayMul<A, B, Ndim>,Ndim> {
|
||||
}
|
||||
auto operator[](int i) const { return arr1_[i] * arr2_[i]; }
|
||||
size_t size() const { return arr1_.size(); }
|
||||
std::array<int64_t, Ndim> shape() const { return arr1_.shape(); }
|
||||
std::array<ssize_t, Ndim> shape() const { return arr1_.shape(); }
|
||||
};
|
||||
|
||||
template <typename A, typename B, int64_t Ndim>
|
||||
template <typename A, typename B, ssize_t Ndim>
|
||||
class ArrayDiv : public ArrayExpr<ArrayDiv<A, B, Ndim>, Ndim> {
|
||||
const A &arr1_;
|
||||
const B &arr2_;
|
||||
@ -69,31 +72,27 @@ class ArrayDiv : public ArrayExpr<ArrayDiv<A, B, Ndim>, Ndim> {
|
||||
}
|
||||
auto operator[](int i) const { return arr1_[i] / arr2_[i]; }
|
||||
size_t size() const { return arr1_.size(); }
|
||||
std::array<int64_t, Ndim> shape() const { return arr1_.shape(); }
|
||||
std::array<ssize_t, Ndim> shape() const { return arr1_.shape(); }
|
||||
};
|
||||
|
||||
|
||||
|
||||
template <typename A, typename B, int64_t Ndim>
|
||||
template <typename A, typename B, ssize_t Ndim>
|
||||
auto operator+(const ArrayExpr<A, Ndim> &arr1, const ArrayExpr<B, Ndim> &arr2) {
|
||||
return ArrayAdd<ArrayExpr<A, Ndim>, ArrayExpr<B, Ndim>, Ndim>(arr1, arr2);
|
||||
}
|
||||
|
||||
template <typename A, typename B, int64_t Ndim>
|
||||
auto operator-(const ArrayExpr<A,Ndim> &arr1, const ArrayExpr<B, Ndim> &arr2) {
|
||||
template <typename A, typename B, ssize_t Ndim>
|
||||
auto operator-(const ArrayExpr<A, Ndim> &arr1, const ArrayExpr<B, Ndim> &arr2) {
|
||||
return ArraySub<ArrayExpr<A, Ndim>, ArrayExpr<B, Ndim>, Ndim>(arr1, arr2);
|
||||
}
|
||||
|
||||
template <typename A, typename B, int64_t Ndim>
|
||||
template <typename A, typename B, ssize_t Ndim>
|
||||
auto operator*(const ArrayExpr<A, Ndim> &arr1, const ArrayExpr<B, Ndim> &arr2) {
|
||||
return ArrayMul<ArrayExpr<A, Ndim>, ArrayExpr<B, Ndim>, Ndim>(arr1, arr2);
|
||||
}
|
||||
|
||||
template <typename A, typename B, int64_t Ndim>
|
||||
template <typename A, typename B, ssize_t Ndim>
|
||||
auto operator/(const ArrayExpr<A, Ndim> &arr1, const ArrayExpr<B, Ndim> &arr2) {
|
||||
return ArrayDiv<ArrayExpr<A, Ndim>, ArrayExpr<B, Ndim>, Ndim>(arr1, arr2);
|
||||
}
|
||||
|
||||
|
||||
|
||||
} // namespace aare
|
170
include/aare/CalculateEta.hpp
Normal file
170
include/aare/CalculateEta.hpp
Normal file
@ -0,0 +1,170 @@
|
||||
#pragma once
|
||||
|
||||
#include "aare/Cluster.hpp"
|
||||
#include "aare/ClusterVector.hpp"
|
||||
#include "aare/NDArray.hpp"
|
||||
|
||||
namespace aare {
|
||||
|
||||
enum class corner : int {
|
||||
cBottomLeft = 0,
|
||||
cBottomRight = 1,
|
||||
cTopLeft = 2,
|
||||
cTopRight = 3
|
||||
};
|
||||
|
||||
enum class pixel : int {
|
||||
pBottomLeft = 0,
|
||||
pBottom = 1,
|
||||
pBottomRight = 2,
|
||||
pLeft = 3,
|
||||
pCenter = 4,
|
||||
pRight = 5,
|
||||
pTopLeft = 6,
|
||||
pTop = 7,
|
||||
pTopRight = 8
|
||||
};
|
||||
|
||||
template <typename T> struct Eta2 {
|
||||
double x;
|
||||
double y;
|
||||
int c;
|
||||
T sum;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Calculate the eta2 values for all clusters in a Clustervector
|
||||
*/
|
||||
template <typename ClusterType,
|
||||
typename = std::enable_if_t<is_cluster_v<ClusterType>>>
|
||||
NDArray<double, 2> calculate_eta2(const ClusterVector<ClusterType> &clusters) {
|
||||
NDArray<double, 2> eta2({static_cast<int64_t>(clusters.size()), 2});
|
||||
|
||||
for (size_t i = 0; i < clusters.size(); i++) {
|
||||
auto e = calculate_eta2(clusters[i]);
|
||||
eta2(i, 0) = e.x;
|
||||
eta2(i, 1) = e.y;
|
||||
}
|
||||
|
||||
return eta2;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Calculate the eta2 values for a generic sized cluster and return them
|
||||
* in a Eta2 struct containing etay, etax and the index of the respective 2x2
|
||||
* subcluster.
|
||||
*/
|
||||
template <typename T, uint8_t ClusterSizeX, uint8_t ClusterSizeY,
|
||||
typename CoordType>
|
||||
Eta2<T>
|
||||
calculate_eta2(const Cluster<T, ClusterSizeX, ClusterSizeY, CoordType> &cl) {
|
||||
Eta2<T> eta{};
|
||||
|
||||
auto max_sum = cl.max_sum_2x2();
|
||||
eta.sum = max_sum.first;
|
||||
auto c = max_sum.second;
|
||||
|
||||
size_t cluster_center_index =
|
||||
(ClusterSizeX / 2) + (ClusterSizeY / 2) * ClusterSizeX;
|
||||
|
||||
size_t index_bottom_left_max_2x2_subcluster =
|
||||
(int(c / (ClusterSizeX - 1))) * ClusterSizeX + c % (ClusterSizeX - 1);
|
||||
|
||||
// check that cluster center is in max subcluster
|
||||
if (cluster_center_index != index_bottom_left_max_2x2_subcluster &&
|
||||
cluster_center_index != index_bottom_left_max_2x2_subcluster + 1 &&
|
||||
cluster_center_index !=
|
||||
index_bottom_left_max_2x2_subcluster + ClusterSizeX &&
|
||||
cluster_center_index !=
|
||||
index_bottom_left_max_2x2_subcluster + ClusterSizeX + 1)
|
||||
throw std::runtime_error("Photon center is not in max 2x2_subcluster");
|
||||
|
||||
if ((cluster_center_index - index_bottom_left_max_2x2_subcluster) %
|
||||
ClusterSizeX ==
|
||||
0) {
|
||||
if ((cl.data[cluster_center_index + 1] +
|
||||
cl.data[cluster_center_index]) != 0)
|
||||
|
||||
eta.x = static_cast<double>(cl.data[cluster_center_index + 1]) /
|
||||
static_cast<double>((cl.data[cluster_center_index + 1] +
|
||||
cl.data[cluster_center_index]));
|
||||
} else {
|
||||
if ((cl.data[cluster_center_index] +
|
||||
cl.data[cluster_center_index - 1]) != 0)
|
||||
|
||||
eta.x = static_cast<double>(cl.data[cluster_center_index]) /
|
||||
static_cast<double>((cl.data[cluster_center_index - 1] +
|
||||
cl.data[cluster_center_index]));
|
||||
}
|
||||
if ((cluster_center_index - index_bottom_left_max_2x2_subcluster) /
|
||||
ClusterSizeX <
|
||||
1) {
|
||||
assert(cluster_center_index + ClusterSizeX <
|
||||
ClusterSizeX * ClusterSizeY); // suppress warning
|
||||
if ((cl.data[cluster_center_index] +
|
||||
cl.data[cluster_center_index + ClusterSizeX]) != 0)
|
||||
eta.y = static_cast<double>(
|
||||
cl.data[cluster_center_index + ClusterSizeX]) /
|
||||
static_cast<double>(
|
||||
(cl.data[cluster_center_index] +
|
||||
cl.data[cluster_center_index + ClusterSizeX]));
|
||||
} else {
|
||||
if ((cl.data[cluster_center_index] +
|
||||
cl.data[cluster_center_index - ClusterSizeX]) != 0)
|
||||
eta.y = static_cast<double>(cl.data[cluster_center_index]) /
|
||||
static_cast<double>(
|
||||
(cl.data[cluster_center_index] +
|
||||
cl.data[cluster_center_index - ClusterSizeX]));
|
||||
}
|
||||
|
||||
eta.c = c; // TODO only supported for 2x2 and 3x3 clusters -> at least no
|
||||
// underyling enum class
|
||||
return eta;
|
||||
}
|
||||
|
||||
// TODO! Look up eta2 calculation - photon center should be top right corner
|
||||
template <typename T>
|
||||
Eta2<T> calculate_eta2(const Cluster<T, 2, 2, int16_t> &cl) {
|
||||
Eta2<T> eta{};
|
||||
|
||||
if ((cl.data[0] + cl.data[1]) != 0)
|
||||
eta.x = static_cast<double>(cl.data[1]) / (cl.data[0] + cl.data[1]);
|
||||
if ((cl.data[0] + cl.data[2]) != 0)
|
||||
eta.y = static_cast<double>(cl.data[2]) / (cl.data[0] + cl.data[2]);
|
||||
eta.sum = cl.sum();
|
||||
eta.c = static_cast<int>(corner::cBottomLeft); // TODO! This is not correct,
|
||||
// but need to put something
|
||||
return eta;
|
||||
}
|
||||
|
||||
// calculates Eta3 for 3x3 cluster based on code from analyze_cluster
|
||||
// TODO only supported for 3x3 Clusters
|
||||
template <typename T> Eta2<T> calculate_eta3(const Cluster<T, 3, 3> &cl) {
|
||||
|
||||
Eta2<T> eta{};
|
||||
|
||||
T sum = 0;
|
||||
|
||||
std::for_each(std::begin(cl.data), std::end(cl.data),
|
||||
[&sum](T x) { sum += x; });
|
||||
|
||||
eta.sum = sum;
|
||||
|
||||
eta.c = corner::cBottomLeft;
|
||||
|
||||
if ((cl.data[3] + cl.data[4] + cl.data[5]) != 0)
|
||||
|
||||
eta.x = static_cast<double>(-cl.data[3] + cl.data[3 + 2]) /
|
||||
|
||||
(cl.data[3] + cl.data[4] + cl.data[5]);
|
||||
|
||||
if ((cl.data[1] + cl.data[4] + cl.data[7]) != 0)
|
||||
|
||||
eta.y = static_cast<double>(-cl.data[1] + cl.data[2 * 3 + 1]) /
|
||||
|
||||
(cl.data[1] + cl.data[4] + cl.data[7]);
|
||||
|
||||
return eta;
|
||||
}
|
||||
|
||||
} // namespace aare
|
99
include/aare/CircularFifo.hpp
Normal file
99
include/aare/CircularFifo.hpp
Normal file
@ -0,0 +1,99 @@
|
||||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
#include <fmt/color.h>
|
||||
#include <fmt/format.h>
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
|
||||
#include "aare/ProducerConsumerQueue.hpp"
|
||||
|
||||
namespace aare {
|
||||
|
||||
template <class ItemType> class CircularFifo {
|
||||
uint32_t fifo_size;
|
||||
aare::ProducerConsumerQueue<ItemType> free_slots;
|
||||
aare::ProducerConsumerQueue<ItemType> filled_slots;
|
||||
|
||||
public:
|
||||
CircularFifo() : CircularFifo(100){};
|
||||
CircularFifo(uint32_t size)
|
||||
: fifo_size(size), free_slots(size + 1), filled_slots(size + 1) {
|
||||
|
||||
// TODO! how do we deal with alignment for writing? alignas???
|
||||
// Do we give the user a chance to provide memory locations?
|
||||
// Templated allocator?
|
||||
for (size_t i = 0; i < fifo_size; ++i) {
|
||||
free_slots.write(ItemType{});
|
||||
}
|
||||
}
|
||||
|
||||
bool next() {
|
||||
// TODO! avoid default constructing ItemType
|
||||
ItemType it;
|
||||
if (!filled_slots.read(it))
|
||||
return false;
|
||||
if (!free_slots.write(std::move(it)))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
~CircularFifo() {}
|
||||
|
||||
using value_type = ItemType;
|
||||
|
||||
auto numFilledSlots() const noexcept { return filled_slots.sizeGuess(); }
|
||||
auto numFreeSlots() const noexcept { return free_slots.sizeGuess(); }
|
||||
auto isFull() const noexcept { return filled_slots.isFull(); }
|
||||
|
||||
ItemType pop_free() {
|
||||
ItemType v;
|
||||
while (!free_slots.read(v))
|
||||
;
|
||||
return std::move(v);
|
||||
// return v;
|
||||
}
|
||||
|
||||
bool try_pop_free(ItemType &v) { return free_slots.read(v); }
|
||||
|
||||
ItemType pop_value(std::chrono::nanoseconds wait,
|
||||
std::atomic<bool> &stopped) {
|
||||
ItemType v;
|
||||
while (!filled_slots.read(v) && !stopped) {
|
||||
std::this_thread::sleep_for(wait);
|
||||
}
|
||||
return std::move(v);
|
||||
}
|
||||
|
||||
ItemType pop_value() {
|
||||
ItemType v;
|
||||
while (!filled_slots.read(v))
|
||||
;
|
||||
return std::move(v);
|
||||
}
|
||||
|
||||
ItemType *frontPtr() { return filled_slots.frontPtr(); }
|
||||
|
||||
// TODO! Add function to move item from filled to free to be used
|
||||
// with the frontPtr function
|
||||
|
||||
template <class... Args> void push_value(Args &&...recordArgs) {
|
||||
while (!filled_slots.write(std::forward<Args>(recordArgs)...))
|
||||
;
|
||||
}
|
||||
|
||||
template <class... Args> bool try_push_value(Args &&...recordArgs) {
|
||||
return filled_slots.write(std::forward<Args>(recordArgs)...);
|
||||
}
|
||||
|
||||
template <class... Args> void push_free(Args &&...recordArgs) {
|
||||
while (!free_slots.write(std::forward<Args>(recordArgs)...))
|
||||
;
|
||||
}
|
||||
|
||||
template <class... Args> bool try_push_free(Args &&...recordArgs) {
|
||||
return free_slots.write(std::forward<Args>(recordArgs)...);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace aare
|
86
include/aare/Cluster.hpp
Normal file
86
include/aare/Cluster.hpp
Normal file
@ -0,0 +1,86 @@
|
||||
|
||||
/************************************************
|
||||
* @file Cluster.hpp
|
||||
* @short definition of cluster, where CoordType (x,y) give
|
||||
* the cluster center coordinates and data the actual cluster data
|
||||
* cluster size is given as template parameters
|
||||
***********************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <numeric>
|
||||
#include <type_traits>
|
||||
|
||||
namespace aare {
|
||||
|
||||
// requires clause c++20 maybe update
|
||||
template <typename T, uint8_t ClusterSizeX, uint8_t ClusterSizeY,
|
||||
typename CoordType = int16_t>
|
||||
struct Cluster {
|
||||
|
||||
static_assert(std::is_arithmetic_v<T>, "T needs to be an arithmetic type");
|
||||
static_assert(std::is_integral_v<CoordType>,
|
||||
"CoordType needs to be an integral type");
|
||||
static_assert(ClusterSizeX > 0 && ClusterSizeY > 0,
|
||||
"Cluster sizes must be bigger than zero");
|
||||
|
||||
CoordType x;
|
||||
CoordType y;
|
||||
std::array<T, ClusterSizeX * ClusterSizeY> data;
|
||||
|
||||
static constexpr uint8_t cluster_size_x = ClusterSizeX;
|
||||
static constexpr uint8_t cluster_size_y = ClusterSizeY;
|
||||
using value_type = T;
|
||||
using coord_type = CoordType;
|
||||
|
||||
T sum() const { return std::accumulate(data.begin(), data.end(), T{}); }
|
||||
|
||||
std::pair<T, int> max_sum_2x2() const {
|
||||
|
||||
if constexpr (cluster_size_x == 3 && cluster_size_y == 3) {
|
||||
std::array<T, 4> sum_2x2_subclusters;
|
||||
sum_2x2_subclusters[0] = data[0] + data[1] + data[3] + data[4];
|
||||
sum_2x2_subclusters[1] = data[1] + data[2] + data[4] + data[5];
|
||||
sum_2x2_subclusters[2] = data[3] + data[4] + data[6] + data[7];
|
||||
sum_2x2_subclusters[3] = data[4] + data[5] + data[7] + data[8];
|
||||
int index = std::max_element(sum_2x2_subclusters.begin(),
|
||||
sum_2x2_subclusters.end()) -
|
||||
sum_2x2_subclusters.begin();
|
||||
return std::make_pair(sum_2x2_subclusters[index], index);
|
||||
} else if constexpr (cluster_size_x == 2 && cluster_size_y == 2) {
|
||||
return std::make_pair(data[0] + data[1] + data[2] + data[3], 0);
|
||||
} else {
|
||||
constexpr size_t num_2x2_subclusters =
|
||||
(ClusterSizeX - 1) * (ClusterSizeY - 1);
|
||||
|
||||
std::array<T, num_2x2_subclusters> sum_2x2_subcluster;
|
||||
for (size_t i = 0; i < ClusterSizeY - 1; ++i) {
|
||||
for (size_t j = 0; j < ClusterSizeX - 1; ++j)
|
||||
sum_2x2_subcluster[i * (ClusterSizeX - 1) + j] =
|
||||
data[i * ClusterSizeX + j] +
|
||||
data[i * ClusterSizeX + j + 1] +
|
||||
data[(i + 1) * ClusterSizeX + j] +
|
||||
data[(i + 1) * ClusterSizeX + j + 1];
|
||||
}
|
||||
|
||||
int index = std::max_element(sum_2x2_subcluster.begin(),
|
||||
sum_2x2_subcluster.end()) -
|
||||
sum_2x2_subcluster.begin();
|
||||
return std::make_pair(sum_2x2_subcluster[index], index);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Type Traits for is_cluster_type
|
||||
template <typename T>
|
||||
struct is_cluster : std::false_type {}; // Default case: Not a Cluster
|
||||
|
||||
template <typename T, uint8_t X, uint8_t Y, typename CoordType>
|
||||
struct is_cluster<Cluster<T, X, Y, CoordType>> : std::true_type {}; // Cluster
|
||||
|
||||
template <typename T> constexpr bool is_cluster_v = is_cluster<T>::value;
|
||||
|
||||
} // namespace aare
|
58
include/aare/ClusterCollector.hpp
Normal file
58
include/aare/ClusterCollector.hpp
Normal file
@ -0,0 +1,58 @@
|
||||
#pragma once
|
||||
#include <atomic>
|
||||
#include <thread>
|
||||
|
||||
#include "aare/ClusterFinderMT.hpp"
|
||||
#include "aare/ClusterVector.hpp"
|
||||
#include "aare/ProducerConsumerQueue.hpp"
|
||||
|
||||
namespace aare {
|
||||
|
||||
template <typename ClusterType,
|
||||
typename = std::enable_if_t<is_cluster_v<ClusterType>>>
|
||||
class ClusterCollector {
|
||||
ProducerConsumerQueue<ClusterVector<ClusterType>> *m_source;
|
||||
std::atomic<bool> m_stop_requested{false};
|
||||
std::atomic<bool> m_stopped{true};
|
||||
std::chrono::milliseconds m_default_wait{1};
|
||||
std::thread m_thread;
|
||||
std::vector<ClusterVector<ClusterType>> m_clusters;
|
||||
|
||||
void process() {
|
||||
m_stopped = false;
|
||||
fmt::print("ClusterCollector started\n");
|
||||
while (!m_stop_requested || !m_source->isEmpty()) {
|
||||
if (ClusterVector<ClusterType> *clusters = m_source->frontPtr();
|
||||
clusters != nullptr) {
|
||||
m_clusters.push_back(std::move(*clusters));
|
||||
m_source->popFront();
|
||||
} else {
|
||||
std::this_thread::sleep_for(m_default_wait);
|
||||
}
|
||||
}
|
||||
fmt::print("ClusterCollector stopped\n");
|
||||
m_stopped = true;
|
||||
}
|
||||
|
||||
public:
|
||||
ClusterCollector(ClusterFinderMT<ClusterType, uint16_t, double> *source) {
|
||||
m_source = source->sink();
|
||||
m_thread =
|
||||
std::thread(&ClusterCollector::process,
|
||||
this); // only one process does that so why isnt it
|
||||
// automatically written to m_cluster in collect
|
||||
// - instead of writing first to m_sink?
|
||||
}
|
||||
void stop() {
|
||||
m_stop_requested = true;
|
||||
m_thread.join();
|
||||
}
|
||||
std::vector<ClusterVector<ClusterType>> steal_clusters() {
|
||||
if (!m_stopped) {
|
||||
throw std::runtime_error("ClusterCollector is still running");
|
||||
}
|
||||
return std::move(m_clusters);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace aare
|
@ -1,45 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#include "aare/Cluster.hpp"
|
||||
#include "aare/ClusterVector.hpp"
|
||||
#include "aare/GainMap.hpp"
|
||||
#include "aare/NDArray.hpp"
|
||||
#include "aare/defs.hpp"
|
||||
#include "aare/logger.hpp"
|
||||
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <optional>
|
||||
|
||||
namespace aare {
|
||||
|
||||
struct Cluster3x3 {
|
||||
int16_t x;
|
||||
int16_t y;
|
||||
int32_t data[9];
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
cBottomLeft = 0,
|
||||
cBottomRight = 1,
|
||||
cTopLeft = 2,
|
||||
cTopRight = 3
|
||||
} corner;
|
||||
|
||||
typedef enum {
|
||||
pBottomLeft = 0,
|
||||
pBottom = 1,
|
||||
pBottomRight = 2,
|
||||
pLeft = 3,
|
||||
pCenter = 4,
|
||||
pRight = 5,
|
||||
pTopLeft = 6,
|
||||
pTop = 7,
|
||||
pTopRight = 8
|
||||
} pixel;
|
||||
|
||||
struct ClusterAnalysis {
|
||||
uint32_t c;
|
||||
int32_t tot;
|
||||
double etax;
|
||||
double etay;
|
||||
};
|
||||
|
||||
/*
|
||||
Binary cluster file. Expects data to be layed out as:
|
||||
int32_t frame_number
|
||||
@ -49,33 +22,434 @@ int32_t frame_number
|
||||
uint32_t number_of_clusters
|
||||
....
|
||||
*/
|
||||
|
||||
// TODO: change to support any type of clusters, e.g. header line with
|
||||
// clsuter_size_x, cluster_size_y,
|
||||
/**
|
||||
* @brief Class to read and write cluster files
|
||||
* Expects data to be laid out as:
|
||||
*
|
||||
*
|
||||
* int32_t frame_number
|
||||
* uint32_t number_of_clusters
|
||||
* int16_t x, int16_t y, int32_t data[9] * number_of_clusters
|
||||
* int32_t frame_number
|
||||
* uint32_t number_of_clusters
|
||||
* etc.
|
||||
*/
|
||||
template <typename ClusterType,
|
||||
typename Enable = std::enable_if_t<is_cluster_v<ClusterType>>>
|
||||
class ClusterFile {
|
||||
FILE *fp{};
|
||||
uint32_t m_num_left{};
|
||||
size_t m_chunk_size{};
|
||||
const std::string m_mode;
|
||||
const std::string m_filename{};
|
||||
uint32_t m_num_left{}; /*Number of photons left in frame*/
|
||||
size_t m_chunk_size{}; /*Number of clusters to read at a time*/
|
||||
std::string m_mode; /*Mode to open the file in*/
|
||||
std::optional<ROI> m_roi; /*Region of interest, will be applied if set*/
|
||||
std::optional<NDArray<int32_t, 2>>
|
||||
m_noise_map; /*Noise map to cut photons, will be applied if set*/
|
||||
std::optional<InvertedGainMap> m_gain_map; /*Gain map to apply to the
|
||||
clusters, will be applied if set*/
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Construct a new Cluster File object
|
||||
* @param fname path to the file
|
||||
* @param chunk_size number of clusters to read at a time when iterating
|
||||
* over the file
|
||||
* @param mode mode to open the file in. "r" for reading, "w" for writing,
|
||||
* "a" for appending
|
||||
* @throws std::runtime_error if the file could not be opened
|
||||
*/
|
||||
ClusterFile(const std::filesystem::path &fname, size_t chunk_size = 1000,
|
||||
const std::string &mode = "r");
|
||||
~ClusterFile();
|
||||
std::vector<Cluster3x3> read_clusters(size_t n_clusters);
|
||||
std::vector<Cluster3x3> read_frame(int32_t &out_fnum);
|
||||
void write_frame(int32_t frame_number,
|
||||
const ClusterVector<int32_t> &clusters);
|
||||
std::vector<Cluster3x3>
|
||||
read_cluster_with_cut(size_t n_clusters, double *noise_map, int nx, int ny);
|
||||
const std::string &mode = "r")
|
||||
|
||||
: m_filename(fname.string()), m_chunk_size(chunk_size), m_mode(mode) {
|
||||
|
||||
if (mode == "r") {
|
||||
fp = fopen(m_filename.c_str(), "rb");
|
||||
if (!fp) {
|
||||
throw std::runtime_error("Could not open file for reading: " +
|
||||
m_filename);
|
||||
}
|
||||
} else if (mode == "w") {
|
||||
fp = fopen(m_filename.c_str(), "wb");
|
||||
if (!fp) {
|
||||
throw std::runtime_error("Could not open file for writing: " +
|
||||
m_filename);
|
||||
}
|
||||
} else if (mode == "a") {
|
||||
fp = fopen(m_filename.c_str(), "ab");
|
||||
if (!fp) {
|
||||
throw std::runtime_error("Could not open file for appending: " +
|
||||
m_filename);
|
||||
}
|
||||
} else {
|
||||
throw std::runtime_error("Unsupported mode: " + mode);
|
||||
}
|
||||
}
|
||||
|
||||
~ClusterFile() { close(); }
|
||||
|
||||
/**
|
||||
* @brief Read n_clusters clusters from the file discarding
|
||||
* frame numbers. If EOF is reached the returned vector will
|
||||
* have less than n_clusters clusters
|
||||
*/
|
||||
ClusterVector<ClusterType> read_clusters(size_t n_clusters) {
|
||||
if (m_mode != "r") {
|
||||
throw std::runtime_error("File not opened for reading");
|
||||
}
|
||||
if (m_noise_map || m_roi) {
|
||||
return read_clusters_with_cut(n_clusters);
|
||||
} else {
|
||||
return read_clusters_without_cut(n_clusters);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Read a single frame from the file and return the
|
||||
* clusters. The cluster vector will have the frame number
|
||||
* set.
|
||||
* @throws std::runtime_error if the file is not opened for
|
||||
* reading or the file pointer not at the beginning of a
|
||||
* frame
|
||||
*/
|
||||
ClusterVector<ClusterType> read_frame() {
|
||||
if (m_mode != "r") {
|
||||
throw std::runtime_error(LOCATION + "File not opened for reading");
|
||||
}
|
||||
if (m_noise_map || m_roi) {
|
||||
return read_frame_with_cut();
|
||||
} else {
|
||||
return read_frame_without_cut();
|
||||
}
|
||||
}
|
||||
|
||||
void write_frame(const ClusterVector<ClusterType> &clusters) {
|
||||
if (m_mode != "w" && m_mode != "a") {
|
||||
throw std::runtime_error("File not opened for writing");
|
||||
}
|
||||
|
||||
int32_t frame_number = clusters.frame_number();
|
||||
fwrite(&frame_number, sizeof(frame_number), 1, fp);
|
||||
uint32_t n_clusters = clusters.size();
|
||||
fwrite(&n_clusters, sizeof(n_clusters), 1, fp);
|
||||
fwrite(clusters.data(), clusters.item_size(), clusters.size(), fp);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return the chunk size
|
||||
*/
|
||||
size_t chunk_size() const { return m_chunk_size; }
|
||||
void close();
|
||||
|
||||
/**
|
||||
* @brief Set the region of interest to use when reading
|
||||
* clusters. If set only clusters within the ROI will be
|
||||
* read.
|
||||
*/
|
||||
void set_roi(ROI roi) { m_roi = roi; }
|
||||
|
||||
/**
|
||||
* @brief Set the noise map to use when reading clusters. If
|
||||
* set clusters below the noise level will be discarded.
|
||||
* Selection criteria one of: Central pixel above noise,
|
||||
* highest 2x2 sum above 2 * noise, total sum above 3 *
|
||||
* noise.
|
||||
*/
|
||||
void set_noise_map(const NDView<int32_t, 2> noise_map) {
|
||||
m_noise_map = NDArray<int32_t, 2>(noise_map);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the gain map to use when reading clusters. If set the gain map
|
||||
* will be applied to the clusters that pass ROI and noise_map selection.
|
||||
* The gain map is expected to be in ADU/energy.
|
||||
*/
|
||||
void set_gain_map(const NDView<double, 2> gain_map) {
|
||||
m_gain_map = InvertedGainMap(gain_map);
|
||||
}
|
||||
|
||||
void set_gain_map(const InvertedGainMap &gain_map) {
|
||||
m_gain_map = gain_map;
|
||||
}
|
||||
|
||||
void set_gain_map(const InvertedGainMap &&gain_map) {
|
||||
m_gain_map = gain_map;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Close the file. If not closed the file will be
|
||||
* closed in the destructor
|
||||
*/
|
||||
void close() {
|
||||
if (fp) {
|
||||
fclose(fp);
|
||||
fp = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
/** @brief Open the file in specific mode
|
||||
*
|
||||
*/
|
||||
void open(const std::string &mode) {
|
||||
if (fp) {
|
||||
close();
|
||||
}
|
||||
|
||||
if (mode == "r") {
|
||||
fp = fopen(m_filename.c_str(), "rb");
|
||||
if (!fp) {
|
||||
throw std::runtime_error("Could not open file for reading: " +
|
||||
m_filename);
|
||||
}
|
||||
m_mode = "r";
|
||||
} else if (mode == "w") {
|
||||
fp = fopen(m_filename.c_str(), "wb");
|
||||
if (!fp) {
|
||||
throw std::runtime_error("Could not open file for writing: " +
|
||||
m_filename);
|
||||
}
|
||||
m_mode = "w";
|
||||
} else if (mode == "a") {
|
||||
fp = fopen(m_filename.c_str(), "ab");
|
||||
if (!fp) {
|
||||
throw std::runtime_error("Could not open file for appending: " +
|
||||
m_filename);
|
||||
}
|
||||
m_mode = "a";
|
||||
} else {
|
||||
throw std::runtime_error("Unsupported mode: " + mode);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
ClusterVector<ClusterType> read_clusters_with_cut(size_t n_clusters);
|
||||
ClusterVector<ClusterType> read_clusters_without_cut(size_t n_clusters);
|
||||
ClusterVector<ClusterType> read_frame_with_cut();
|
||||
ClusterVector<ClusterType> read_frame_without_cut();
|
||||
bool is_selected(ClusterType &cl);
|
||||
ClusterType read_one_cluster();
|
||||
};
|
||||
|
||||
int analyze_data(int32_t *data, int32_t *t2, int32_t *t3, char *quad,
|
||||
double *eta2x, double *eta2y, double *eta3x, double *eta3y);
|
||||
int analyze_cluster(Cluster3x3& cl, int32_t *t2, int32_t *t3, char *quad,
|
||||
double *eta2x, double *eta2y, double *eta3x, double *eta3y);
|
||||
template <typename ClusterType, typename Enable>
|
||||
ClusterVector<ClusterType>
|
||||
ClusterFile<ClusterType, Enable>::read_clusters_without_cut(size_t n_clusters) {
|
||||
if (m_mode != "r") {
|
||||
throw std::runtime_error("File not opened for reading");
|
||||
}
|
||||
|
||||
NDArray<double, 2> calculate_eta2( ClusterVector<int>& clusters);
|
||||
std::array<double,2> calculate_eta2( Cluster3x3& cl);
|
||||
ClusterVector<ClusterType> clusters(n_clusters);
|
||||
clusters.resize(n_clusters);
|
||||
|
||||
int32_t iframe = 0; // frame number needs to be 4 bytes!
|
||||
size_t nph_read = 0;
|
||||
uint32_t nn = m_num_left;
|
||||
uint32_t nph = m_num_left; // number of clusters in frame needs to be 4
|
||||
|
||||
auto buf = clusters.data();
|
||||
// if there are photons left from previous frame read them first
|
||||
if (nph) {
|
||||
if (nph > n_clusters) {
|
||||
// if we have more photons left in the frame then photons to
|
||||
// read we read directly the requested number
|
||||
nn = n_clusters;
|
||||
} else {
|
||||
nn = nph;
|
||||
}
|
||||
nph_read += fread((buf + nph_read), clusters.item_size(), nn, fp);
|
||||
m_num_left = nph - nn; // write back the number of photons left
|
||||
}
|
||||
|
||||
if (nph_read < n_clusters) {
|
||||
// keep on reading frames and photons until reaching n_clusters
|
||||
while (fread(&iframe, sizeof(iframe), 1, fp)) {
|
||||
clusters.set_frame_number(iframe);
|
||||
// read number of clusters in frame
|
||||
if (fread(&nph, sizeof(nph), 1, fp)) {
|
||||
if (nph > (n_clusters - nph_read))
|
||||
nn = n_clusters - nph_read;
|
||||
else
|
||||
nn = nph;
|
||||
|
||||
nph_read +=
|
||||
fread((buf + nph_read), clusters.item_size(), nn, fp);
|
||||
m_num_left = nph - nn;
|
||||
}
|
||||
if (nph_read >= n_clusters)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Resize the vector to the number o f clusters.
|
||||
// No new allocation, only change bounds.
|
||||
clusters.resize(nph_read);
|
||||
if (m_gain_map)
|
||||
m_gain_map->apply_gain_map(clusters);
|
||||
return clusters;
|
||||
}
|
||||
|
||||
template <typename ClusterType, typename Enable>
|
||||
ClusterVector<ClusterType>
|
||||
ClusterFile<ClusterType, Enable>::read_clusters_with_cut(size_t n_clusters) {
|
||||
ClusterVector<ClusterType> clusters;
|
||||
clusters.reserve(n_clusters);
|
||||
|
||||
// if there are photons left from previous frame read them first
|
||||
if (m_num_left) {
|
||||
while (m_num_left && clusters.size() < n_clusters) {
|
||||
ClusterType c = read_one_cluster();
|
||||
if (is_selected(c)) {
|
||||
clusters.push_back(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// we did not have enough clusters left in the previous frame
|
||||
// keep on reading frames until reaching n_clusters
|
||||
if (clusters.size() < n_clusters) {
|
||||
// sanity check
|
||||
if (m_num_left) {
|
||||
throw std::runtime_error(
|
||||
LOCATION + "Entered second loop with clusters left\n");
|
||||
}
|
||||
|
||||
int32_t frame_number = 0; // frame number needs to be 4 bytes!
|
||||
while (fread(&frame_number, sizeof(frame_number), 1, fp)) {
|
||||
if (fread(&m_num_left, sizeof(m_num_left), 1, fp)) {
|
||||
clusters.set_frame_number(
|
||||
frame_number); // cluster vector will hold the last
|
||||
// frame number
|
||||
while (m_num_left && clusters.size() < n_clusters) {
|
||||
ClusterType c = read_one_cluster();
|
||||
if (is_selected(c)) {
|
||||
clusters.push_back(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// we have enough clusters, break out of the outer while loop
|
||||
if (clusters.size() >= n_clusters)
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (m_gain_map)
|
||||
m_gain_map->apply_gain_map(clusters);
|
||||
|
||||
return clusters;
|
||||
}
|
||||
|
||||
template <typename ClusterType, typename Enable>
|
||||
ClusterType ClusterFile<ClusterType, Enable>::read_one_cluster() {
|
||||
ClusterType c;
|
||||
auto rc = fread(&c, sizeof(c), 1, fp);
|
||||
if (rc != 1) {
|
||||
throw std::runtime_error(LOCATION + "Could not read cluster");
|
||||
}
|
||||
--m_num_left;
|
||||
return c;
|
||||
}
|
||||
|
||||
template <typename ClusterType, typename Enable>
|
||||
ClusterVector<ClusterType>
|
||||
ClusterFile<ClusterType, Enable>::read_frame_without_cut() {
|
||||
if (m_mode != "r") {
|
||||
throw std::runtime_error("File not opened for reading");
|
||||
}
|
||||
if (m_num_left) {
|
||||
throw std::runtime_error(
|
||||
"There are still photons left in the last frame");
|
||||
}
|
||||
int32_t frame_number;
|
||||
if (fread(&frame_number, sizeof(frame_number), 1, fp) != 1) {
|
||||
throw std::runtime_error(LOCATION + "Could not read frame number");
|
||||
}
|
||||
|
||||
int32_t n_clusters; // Saved as 32bit integer in the cluster file
|
||||
if (fread(&n_clusters, sizeof(n_clusters), 1, fp) != 1) {
|
||||
throw std::runtime_error(LOCATION +
|
||||
"Could not read number of clusters");
|
||||
}
|
||||
|
||||
LOG(logDEBUG1) << "Reading " << n_clusters << " clusters from frame "
|
||||
<< frame_number;
|
||||
|
||||
ClusterVector<ClusterType> clusters(n_clusters);
|
||||
clusters.set_frame_number(frame_number);
|
||||
clusters.resize(n_clusters);
|
||||
|
||||
LOG(logDEBUG1) << "clusters.item_size(): " << clusters.item_size();
|
||||
|
||||
if (fread(clusters.data(), clusters.item_size(), n_clusters, fp) !=
|
||||
static_cast<size_t>(n_clusters)) {
|
||||
throw std::runtime_error(LOCATION + "Could not read clusters");
|
||||
}
|
||||
|
||||
if (m_gain_map)
|
||||
m_gain_map->apply_gain_map(clusters);
|
||||
return clusters;
|
||||
}
|
||||
|
||||
template <typename ClusterType, typename Enable>
|
||||
ClusterVector<ClusterType>
|
||||
ClusterFile<ClusterType, Enable>::read_frame_with_cut() {
|
||||
if (m_mode != "r") {
|
||||
throw std::runtime_error("File not opened for reading");
|
||||
}
|
||||
if (m_num_left) {
|
||||
throw std::runtime_error(
|
||||
"There are still photons left in the last frame");
|
||||
}
|
||||
int32_t frame_number;
|
||||
if (fread(&frame_number, sizeof(frame_number), 1, fp) != 1) {
|
||||
throw std::runtime_error("Could not read frame number");
|
||||
}
|
||||
|
||||
if (fread(&m_num_left, sizeof(m_num_left), 1, fp) != 1) {
|
||||
throw std::runtime_error("Could not read number of clusters");
|
||||
}
|
||||
|
||||
ClusterVector<ClusterType> clusters;
|
||||
clusters.reserve(m_num_left);
|
||||
clusters.set_frame_number(frame_number);
|
||||
while (m_num_left) {
|
||||
ClusterType c = read_one_cluster();
|
||||
if (is_selected(c)) {
|
||||
clusters.push_back(c);
|
||||
}
|
||||
}
|
||||
if (m_gain_map)
|
||||
m_gain_map->apply_gain_map(clusters);
|
||||
return clusters;
|
||||
}
|
||||
|
||||
template <typename ClusterType, typename Enable>
|
||||
bool ClusterFile<ClusterType, Enable>::is_selected(ClusterType &cl) {
|
||||
// Should fail fast
|
||||
if (m_roi) {
|
||||
if (!(m_roi->contains(cl.x, cl.y))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
size_t cluster_center_index =
|
||||
(ClusterType::cluster_size_x / 2) +
|
||||
(ClusterType::cluster_size_y / 2) * ClusterType::cluster_size_x;
|
||||
|
||||
if (m_noise_map) {
|
||||
auto sum_1x1 = cl.data[cluster_center_index]; // central pixel
|
||||
auto sum_2x2 = cl.max_sum_2x2().first; // highest sum of 2x2 subclusters
|
||||
auto total_sum = cl.sum(); // sum of all pixels
|
||||
|
||||
auto noise =
|
||||
(*m_noise_map)(cl.y, cl.x); // TODO! check if this is correct
|
||||
if (sum_1x1 <= noise || sum_2x2 <= 2 * noise ||
|
||||
total_sum <= 3 * noise) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// we passed all checks
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace aare
|
||||
|
65
include/aare/ClusterFileSink.hpp
Normal file
65
include/aare/ClusterFileSink.hpp
Normal file
@ -0,0 +1,65 @@
|
||||
#pragma once
|
||||
#include <atomic>
|
||||
#include <filesystem>
|
||||
#include <thread>
|
||||
|
||||
#include "aare/ClusterFinderMT.hpp"
|
||||
#include "aare/ClusterVector.hpp"
|
||||
#include "aare/ProducerConsumerQueue.hpp"
|
||||
|
||||
namespace aare {
|
||||
|
||||
template <typename ClusterType,
|
||||
typename = std::enable_if_t<is_cluster_v<ClusterType>>>
|
||||
class ClusterFileSink {
|
||||
ProducerConsumerQueue<ClusterVector<ClusterType>> *m_source;
|
||||
std::atomic<bool> m_stop_requested{false};
|
||||
std::atomic<bool> m_stopped{true};
|
||||
std::chrono::milliseconds m_default_wait{1};
|
||||
std::thread m_thread;
|
||||
std::ofstream m_file;
|
||||
|
||||
void process() {
|
||||
m_stopped = false;
|
||||
LOG(logDEBUG) << "ClusterFileSink started";
|
||||
while (!m_stop_requested || !m_source->isEmpty()) {
|
||||
if (ClusterVector<ClusterType> *clusters = m_source->frontPtr();
|
||||
clusters != nullptr) {
|
||||
// Write clusters to file
|
||||
int32_t frame_number =
|
||||
clusters->frame_number(); // TODO! Should we store frame
|
||||
// number already as int?
|
||||
uint32_t num_clusters = clusters->size();
|
||||
m_file.write(reinterpret_cast<const char *>(&frame_number),
|
||||
sizeof(frame_number));
|
||||
m_file.write(reinterpret_cast<const char *>(&num_clusters),
|
||||
sizeof(num_clusters));
|
||||
m_file.write(reinterpret_cast<const char *>(clusters->data()),
|
||||
clusters->size() * clusters->item_size());
|
||||
m_source->popFront();
|
||||
} else {
|
||||
std::this_thread::sleep_for(m_default_wait);
|
||||
}
|
||||
}
|
||||
LOG(logDEBUG) << "ClusterFileSink stopped";
|
||||
m_stopped = true;
|
||||
}
|
||||
|
||||
public:
|
||||
ClusterFileSink(ClusterFinderMT<ClusterType, uint16_t, double> *source,
|
||||
const std::filesystem::path &fname) {
|
||||
LOG(logDEBUG) << "ClusterFileSink: "
|
||||
<< "source: " << source->sink()
|
||||
<< ", file: " << fname.string();
|
||||
m_source = source->sink();
|
||||
m_thread = std::thread(&ClusterFileSink::process, this);
|
||||
m_file.open(fname, std::ios::binary);
|
||||
}
|
||||
void stop() {
|
||||
m_stop_requested = true;
|
||||
m_thread.join();
|
||||
m_file.close();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace aare
|
@ -1,148 +0,0 @@
|
||||
#pragma once
|
||||
#include "aare/core/defs.hpp"
|
||||
#include <filesystem>
|
||||
#include <string>
|
||||
#include <fmt/format.h>
|
||||
|
||||
namespace aare {
|
||||
struct ClusterHeader {
|
||||
int32_t frame_number;
|
||||
int32_t n_clusters;
|
||||
std::string to_string() const {
|
||||
return "frame_number: " + std::to_string(frame_number) + ", n_clusters: " + std::to_string(n_clusters);
|
||||
}
|
||||
};
|
||||
|
||||
struct ClusterV2_ {
|
||||
int16_t x;
|
||||
int16_t y;
|
||||
std::array<int32_t, 9> data;
|
||||
std::string to_string(bool detailed = false) const {
|
||||
if (detailed) {
|
||||
std::string data_str = "[";
|
||||
for (auto &d : data) {
|
||||
data_str += std::to_string(d) + ", ";
|
||||
}
|
||||
data_str += "]";
|
||||
return "x: " + std::to_string(x) + ", y: " + std::to_string(y) + ", data: " + data_str;
|
||||
}
|
||||
return "x: " + std::to_string(x) + ", y: " + std::to_string(y);
|
||||
}
|
||||
};
|
||||
|
||||
struct ClusterV2 {
|
||||
ClusterV2_ cluster;
|
||||
int32_t frame_number;
|
||||
std::string to_string() const {
|
||||
return "frame_number: " + std::to_string(frame_number) + ", " + cluster.to_string();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief
|
||||
* important not: fp always points to the clusters header and does not point to individual clusters
|
||||
*
|
||||
*/
|
||||
class ClusterFileV2 {
|
||||
std::filesystem::path m_fpath;
|
||||
std::string m_mode;
|
||||
FILE *fp{nullptr};
|
||||
|
||||
void check_open(){
|
||||
if (!fp)
|
||||
throw std::runtime_error(fmt::format("File: {} not open", m_fpath.string()));
|
||||
}
|
||||
|
||||
public:
|
||||
ClusterFileV2(std::filesystem::path const &fpath, std::string const &mode): m_fpath(fpath), m_mode(mode) {
|
||||
if (m_mode != "r" && m_mode != "w")
|
||||
throw std::invalid_argument("mode must be 'r' or 'w'");
|
||||
if (m_mode == "r" && !std::filesystem::exists(m_fpath))
|
||||
throw std::invalid_argument("File does not exist");
|
||||
if (mode == "r") {
|
||||
fp = fopen(fpath.string().c_str(), "rb");
|
||||
} else if (mode == "w") {
|
||||
if (std::filesystem::exists(fpath)) {
|
||||
fp = fopen(fpath.string().c_str(), "r+b");
|
||||
} else {
|
||||
fp = fopen(fpath.string().c_str(), "wb");
|
||||
}
|
||||
}
|
||||
if (fp == nullptr) {
|
||||
throw std::runtime_error("Failed to open file");
|
||||
}
|
||||
}
|
||||
~ClusterFileV2() { close(); }
|
||||
std::vector<ClusterV2> read() {
|
||||
check_open();
|
||||
|
||||
ClusterHeader header;
|
||||
fread(&header, sizeof(ClusterHeader), 1, fp);
|
||||
std::vector<ClusterV2_> clusters_(header.n_clusters);
|
||||
fread(clusters_.data(), sizeof(ClusterV2_), header.n_clusters, fp);
|
||||
std::vector<ClusterV2> clusters;
|
||||
for (auto &c : clusters_) {
|
||||
ClusterV2 cluster;
|
||||
cluster.cluster = std::move(c);
|
||||
cluster.frame_number = header.frame_number;
|
||||
clusters.push_back(cluster);
|
||||
}
|
||||
|
||||
return clusters;
|
||||
}
|
||||
std::vector<std::vector<ClusterV2>> read(int n_frames) {
|
||||
std::vector<std::vector<ClusterV2>> clusters;
|
||||
for (int i = 0; i < n_frames; i++) {
|
||||
clusters.push_back(read());
|
||||
}
|
||||
return clusters;
|
||||
}
|
||||
|
||||
size_t write(std::vector<ClusterV2> const &clusters) {
|
||||
check_open();
|
||||
if (m_mode != "w")
|
||||
throw std::runtime_error("File not opened in write mode");
|
||||
if (clusters.empty())
|
||||
return 0;
|
||||
|
||||
ClusterHeader header;
|
||||
header.frame_number = clusters[0].frame_number;
|
||||
header.n_clusters = clusters.size();
|
||||
fwrite(&header, sizeof(ClusterHeader), 1, fp);
|
||||
for (auto &c : clusters) {
|
||||
fwrite(&c.cluster, sizeof(ClusterV2_), 1, fp);
|
||||
}
|
||||
return clusters.size();
|
||||
}
|
||||
|
||||
size_t write(std::vector<std::vector<ClusterV2>> const &clusters) {
|
||||
check_open();
|
||||
if (m_mode != "w")
|
||||
throw std::runtime_error("File not opened in write mode");
|
||||
|
||||
size_t n_clusters = 0;
|
||||
for (auto &c : clusters) {
|
||||
n_clusters += write(c);
|
||||
}
|
||||
return n_clusters;
|
||||
}
|
||||
|
||||
int seek_to_begin() { return fseek(fp, 0, SEEK_SET); }
|
||||
int seek_to_end() { return fseek(fp, 0, SEEK_END); }
|
||||
|
||||
int32_t frame_number() {
|
||||
auto pos = ftell(fp);
|
||||
ClusterHeader header;
|
||||
fread(&header, sizeof(ClusterHeader), 1, fp);
|
||||
fseek(fp, pos, SEEK_SET);
|
||||
return header.frame_number;
|
||||
}
|
||||
|
||||
void close() {
|
||||
if (fp) {
|
||||
fclose(fp);
|
||||
fp = nullptr;
|
||||
}
|
||||
}
|
||||
};
|
||||
} // namespace aare
|
@ -10,31 +10,19 @@
|
||||
|
||||
namespace aare {
|
||||
|
||||
/** enum to define the event types */
|
||||
enum class eventType {
|
||||
PEDESTAL, /** pedestal */
|
||||
NEIGHBOUR, /** neighbour i.e. below threshold, but in the cluster of a
|
||||
photon */
|
||||
PHOTON, /** photon i.e. above threshold */
|
||||
PHOTON_MAX, /** maximum of a cluster satisfying the photon conditions */
|
||||
NEGATIVE_PEDESTAL, /** negative value, will not be accounted for as pedestal
|
||||
in order to avoid drift of the pedestal towards
|
||||
negative values */
|
||||
UNDEFINED_EVENT = -1 /** undefined */
|
||||
};
|
||||
|
||||
template <typename FRAME_TYPE = uint16_t, typename PEDESTAL_TYPE = double,
|
||||
typename CT = int32_t>
|
||||
template <typename ClusterType = Cluster<int32_t, 3, 3>,
|
||||
typename FRAME_TYPE = uint16_t, typename PEDESTAL_TYPE = double>
|
||||
class ClusterFinder {
|
||||
Shape<2> m_image_size;
|
||||
const int m_cluster_sizeX;
|
||||
const int m_cluster_sizeY;
|
||||
// const PEDESTAL_TYPE m_threshold;
|
||||
const PEDESTAL_TYPE m_nSigma;
|
||||
const PEDESTAL_TYPE c2;
|
||||
const PEDESTAL_TYPE c3;
|
||||
Pedestal<PEDESTAL_TYPE> m_pedestal;
|
||||
ClusterVector<CT> m_clusters;
|
||||
ClusterVector<ClusterType> m_clusters;
|
||||
|
||||
static const uint8_t ClusterSizeX = ClusterType::cluster_size_x;
|
||||
static const uint8_t ClusterSizeY = ClusterType::cluster_size_y;
|
||||
using CT = typename ClusterType::value_type;
|
||||
|
||||
public:
|
||||
/**
|
||||
@ -45,15 +33,16 @@ class ClusterFinder {
|
||||
* @param capacity initial capacity of the cluster vector
|
||||
*
|
||||
*/
|
||||
ClusterFinder(Shape<2> image_size, Shape<2> cluster_size,
|
||||
PEDESTAL_TYPE nSigma = 5.0, size_t capacity = 1000000)
|
||||
: m_image_size(image_size), m_cluster_sizeX(cluster_size[0]),
|
||||
m_cluster_sizeY(cluster_size[1]),
|
||||
m_nSigma(nSigma),
|
||||
c2(sqrt((m_cluster_sizeY + 1) / 2 * (m_cluster_sizeX + 1) / 2)),
|
||||
c3(sqrt(m_cluster_sizeX * m_cluster_sizeY)),
|
||||
m_pedestal(image_size[0], image_size[1]),
|
||||
m_clusters(m_cluster_sizeX, m_cluster_sizeY, capacity) {};
|
||||
ClusterFinder(Shape<2> image_size, PEDESTAL_TYPE nSigma = 5.0,
|
||||
size_t capacity = 1000000)
|
||||
: m_image_size(image_size), m_nSigma(nSigma),
|
||||
c2(sqrt((ClusterSizeY + 1) / 2 * (ClusterSizeX + 1) / 2)),
|
||||
c3(sqrt(ClusterSizeX * ClusterSizeY)),
|
||||
m_pedestal(image_size[0], image_size[1]), m_clusters(capacity) {
|
||||
LOG(logDEBUG) << "ClusterFinder: "
|
||||
<< "image_size: " << image_size[0] << "x" << image_size[1]
|
||||
<< ", nSigma: " << nSigma << ", capacity: " << capacity;
|
||||
}
|
||||
|
||||
void push_pedestal_frame(NDView<FRAME_TYPE, 2> frame) {
|
||||
m_pedestal.push(frame);
|
||||
@ -61,6 +50,7 @@ class ClusterFinder {
|
||||
|
||||
NDArray<PEDESTAL_TYPE, 2> pedestal() { return m_pedestal.mean(); }
|
||||
NDArray<PEDESTAL_TYPE, 2> noise() { return m_pedestal.std(); }
|
||||
void clear_pedestal() { m_pedestal.clear(); }
|
||||
|
||||
/**
|
||||
* @brief Move the clusters from the ClusterVector in the ClusterFinder to a
|
||||
@ -69,23 +59,28 @@ class ClusterFinder {
|
||||
* same capacity as the old one
|
||||
*
|
||||
*/
|
||||
ClusterVector<CT> steal_clusters(bool realloc_same_capacity = false) {
|
||||
ClusterVector<CT> tmp = std::move(m_clusters);
|
||||
ClusterVector<ClusterType>
|
||||
steal_clusters(bool realloc_same_capacity = false) {
|
||||
ClusterVector<ClusterType> tmp = std::move(m_clusters);
|
||||
if (realloc_same_capacity)
|
||||
m_clusters = ClusterVector<CT>(m_cluster_sizeX, m_cluster_sizeY,
|
||||
tmp.capacity());
|
||||
m_clusters = ClusterVector<ClusterType>(tmp.capacity());
|
||||
else
|
||||
m_clusters = ClusterVector<CT>(m_cluster_sizeX, m_cluster_sizeY);
|
||||
m_clusters = ClusterVector<ClusterType>{};
|
||||
return tmp;
|
||||
}
|
||||
void find_clusters(NDView<FRAME_TYPE, 2> frame) {
|
||||
void find_clusters(NDView<FRAME_TYPE, 2> frame, uint64_t frame_number = 0) {
|
||||
// // TODO! deal with even size clusters
|
||||
// // currently 3,3 -> +/- 1
|
||||
// // 4,4 -> +/- 2
|
||||
int dy = m_cluster_sizeY / 2;
|
||||
int dx = m_cluster_sizeX / 2;
|
||||
int dy = ClusterSizeY / 2;
|
||||
int dx = ClusterSizeX / 2;
|
||||
int has_center_pixel_x =
|
||||
ClusterSizeX %
|
||||
2; // for even sized clusters there is no proper cluster center and
|
||||
// even amount of pixels around the center
|
||||
int has_center_pixel_y = ClusterSizeY % 2;
|
||||
|
||||
std::vector<CT> cluster_data(m_cluster_sizeX * m_cluster_sizeY);
|
||||
m_clusters.set_frame_number(frame_number);
|
||||
for (int iy = 0; iy < frame.shape(0); iy++) {
|
||||
for (int ix = 0; ix < frame.shape(1); ix++) {
|
||||
|
||||
@ -100,8 +95,8 @@ class ClusterFinder {
|
||||
continue; // NEGATIVE_PEDESTAL go to next pixel
|
||||
// TODO! No pedestal update???
|
||||
|
||||
for (int ir = -dy; ir < dy + 1; ir++) {
|
||||
for (int ic = -dx; ic < dx + 1; ic++) {
|
||||
for (int ir = -dy; ir < dy + has_center_pixel_y; ir++) {
|
||||
for (int ic = -dx; ic < dx + has_center_pixel_x; ic++) {
|
||||
if (ix + ic >= 0 && ix + ic < frame.shape(1) &&
|
||||
iy + ir >= 0 && iy + ir < frame.shape(0)) {
|
||||
PEDESTAL_TYPE val =
|
||||
@ -121,28 +116,34 @@ class ClusterFinder {
|
||||
} else if (total > c3 * m_nSigma * rms) {
|
||||
// pass
|
||||
} else {
|
||||
// m_pedestal.push(iy, ix, frame(iy, ix));
|
||||
m_pedestal.push_fast(iy, ix, frame(iy, ix));
|
||||
continue; // It was a pedestal value nothing to store
|
||||
// m_pedestal.push(iy, ix, frame(iy, ix)); // Safe option
|
||||
m_pedestal.push_fast(
|
||||
iy, ix,
|
||||
frame(iy,
|
||||
ix)); // Assume we have reached n_samples in the
|
||||
// pedestal, slight performance improvement
|
||||
continue; // It was a pedestal value nothing to store
|
||||
}
|
||||
|
||||
// Store cluster
|
||||
if (value == max) {
|
||||
// Zero out the cluster data
|
||||
std::fill(cluster_data.begin(), cluster_data.end(), 0);
|
||||
ClusterType cluster{};
|
||||
cluster.x = ix;
|
||||
cluster.y = iy;
|
||||
|
||||
// Fill the cluster data since we have a photon to store
|
||||
// It's worth redoing the look since most of the time we
|
||||
// don't have a photon
|
||||
int i = 0;
|
||||
for (int ir = -dy; ir < dy + 1; ir++) {
|
||||
for (int ic = -dx; ic < dx + 1; ic++) {
|
||||
for (int ir = -dy; ir < dy + has_center_pixel_y; ir++) {
|
||||
for (int ic = -dx; ic < dx + has_center_pixel_y; ic++) {
|
||||
if (ix + ic >= 0 && ix + ic < frame.shape(1) &&
|
||||
iy + ir >= 0 && iy + ir < frame.shape(0)) {
|
||||
CT tmp =
|
||||
static_cast<CT>(frame(iy + ir, ix + ic)) -
|
||||
m_pedestal.mean(iy + ir, ix + ic);
|
||||
cluster_data[i] =
|
||||
static_cast<CT>(
|
||||
m_pedestal.mean(iy + ir, ix + ic));
|
||||
cluster.data[i] =
|
||||
tmp; // Watch for out of bounds access
|
||||
i++;
|
||||
}
|
||||
@ -150,121 +151,11 @@ class ClusterFinder {
|
||||
}
|
||||
|
||||
// Add the cluster to the output ClusterVector
|
||||
m_clusters.push_back(
|
||||
ix, iy,
|
||||
reinterpret_cast<std::byte *>(cluster_data.data()));
|
||||
m_clusters.push_back(cluster);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// // template <typename FRAME_TYPE, typename PEDESTAL_TYPE>
|
||||
// std::vector<DynamicCluster>
|
||||
// find_clusters_with_threshold(NDView<FRAME_TYPE, 2> frame,
|
||||
// Pedestal<PEDESTAL_TYPE> &pedestal) {
|
||||
// assert(m_threshold > 0);
|
||||
// std::vector<DynamicCluster> clusters;
|
||||
// std::vector<std::vector<eventType>> eventMask;
|
||||
// for (int i = 0; i < frame.shape(0); i++) {
|
||||
// eventMask.push_back(std::vector<eventType>(frame.shape(1)));
|
||||
// }
|
||||
// double tthr, tthr1, tthr2;
|
||||
|
||||
// NDArray<FRAME_TYPE, 2> rest({frame.shape(0), frame.shape(1)});
|
||||
// NDArray<int, 2> nph({frame.shape(0), frame.shape(1)});
|
||||
// // convert to n photons
|
||||
// // nph = (frame-pedestal.mean()+0.5*m_threshold)/m_threshold; // can
|
||||
// be
|
||||
// // optimized with expression templates?
|
||||
// for (int iy = 0; iy < frame.shape(0); iy++) {
|
||||
// for (int ix = 0; ix < frame.shape(1); ix++) {
|
||||
// auto val = frame(iy, ix) - pedestal.mean(iy, ix);
|
||||
// nph(iy, ix) = (val + 0.5 * m_threshold) / m_threshold;
|
||||
// nph(iy, ix) = nph(iy, ix) < 0 ? 0 : nph(iy, ix);
|
||||
// rest(iy, ix) = val - nph(iy, ix) * m_threshold;
|
||||
// }
|
||||
// }
|
||||
// // iterate over frame pixels
|
||||
// for (int iy = 0; iy < frame.shape(0); iy++) {
|
||||
// for (int ix = 0; ix < frame.shape(1); ix++) {
|
||||
// eventMask[iy][ix] = eventType::PEDESTAL;
|
||||
// // initialize max and total
|
||||
// FRAME_TYPE max = std::numeric_limits<FRAME_TYPE>::min();
|
||||
// long double total = 0;
|
||||
// if (rest(iy, ix) <= 0.25 * m_threshold) {
|
||||
// pedestal.push(iy, ix, frame(iy, ix));
|
||||
// continue;
|
||||
// }
|
||||
// eventMask[iy][ix] = eventType::NEIGHBOUR;
|
||||
// // iterate over cluster pixels around the current pixel
|
||||
// (ix,iy) for (short ir = -(m_cluster_sizeY / 2);
|
||||
// ir < (m_cluster_sizeY / 2) + 1; ir++) {
|
||||
// for (short ic = -(m_cluster_sizeX / 2);
|
||||
// ic < (m_cluster_sizeX / 2) + 1; ic++) {
|
||||
// if (ix + ic >= 0 && ix + ic < frame.shape(1) &&
|
||||
// iy + ir >= 0 && iy + ir < frame.shape(0)) {
|
||||
// auto val = frame(iy + ir, ix + ic) -
|
||||
// pedestal.mean(iy + ir, ix + ic);
|
||||
// total += val;
|
||||
// if (val > max) {
|
||||
// max = val;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// auto rms = pedestal.std(iy, ix);
|
||||
// if (m_nSigma == 0) {
|
||||
// tthr = m_threshold;
|
||||
// tthr1 = m_threshold;
|
||||
// tthr2 = m_threshold;
|
||||
// } else {
|
||||
// tthr = m_nSigma * rms;
|
||||
// tthr1 = m_nSigma * rms * c3;
|
||||
// tthr2 = m_nSigma * rms * c2;
|
||||
|
||||
// if (m_threshold > 2 * tthr)
|
||||
// tthr = m_threshold - tthr;
|
||||
// if (m_threshold > 2 * tthr1)
|
||||
// tthr1 = tthr - tthr1;
|
||||
// if (m_threshold > 2 * tthr2)
|
||||
// tthr2 = tthr - tthr2;
|
||||
// }
|
||||
// if (total > tthr1 || max > tthr) {
|
||||
// eventMask[iy][ix] = eventType::PHOTON;
|
||||
// nph(iy, ix) += 1;
|
||||
// rest(iy, ix) -= m_threshold;
|
||||
// } else {
|
||||
// pedestal.push(iy, ix, frame(iy, ix));
|
||||
// continue;
|
||||
// }
|
||||
// if (eventMask[iy][ix] == eventType::PHOTON &&
|
||||
// frame(iy, ix) - pedestal.mean(iy, ix) >= max) {
|
||||
// eventMask[iy][ix] = eventType::PHOTON_MAX;
|
||||
// DynamicCluster cluster(m_cluster_sizeX, m_cluster_sizeY,
|
||||
// Dtype(typeid(FRAME_TYPE)));
|
||||
// cluster.x = ix;
|
||||
// cluster.y = iy;
|
||||
// short i = 0;
|
||||
// for (short ir = -(m_cluster_sizeY / 2);
|
||||
// ir < (m_cluster_sizeY / 2) + 1; ir++) {
|
||||
// for (short ic = -(m_cluster_sizeX / 2);
|
||||
// ic < (m_cluster_sizeX / 2) + 1; ic++) {
|
||||
// if (ix + ic >= 0 && ix + ic < frame.shape(1) &&
|
||||
// iy + ir >= 0 && iy + ir < frame.shape(0)) {
|
||||
// auto tmp = frame(iy + ir, ix + ic) -
|
||||
// pedestal.mean(iy + ir, ix + ic);
|
||||
// cluster.set<FRAME_TYPE>(i, tmp);
|
||||
// i++;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// clusters.push_back(cluster);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// return clusters;
|
||||
// }
|
||||
};
|
||||
|
||||
} // namespace aare
|
284
include/aare/ClusterFinderMT.hpp
Normal file
284
include/aare/ClusterFinderMT.hpp
Normal file
@ -0,0 +1,284 @@
|
||||
#pragma once
|
||||
#include <atomic>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
#include "aare/ClusterFinder.hpp"
|
||||
#include "aare/NDArray.hpp"
|
||||
#include "aare/ProducerConsumerQueue.hpp"
|
||||
#include "aare/logger.hpp"
|
||||
|
||||
namespace aare {
|
||||
|
||||
enum class FrameType {
|
||||
DATA,
|
||||
PEDESTAL,
|
||||
};
|
||||
|
||||
struct FrameWrapper {
|
||||
FrameType type;
|
||||
uint64_t frame_number;
|
||||
NDArray<uint16_t, 2> data;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief ClusterFinderMT is a multi-threaded version of ClusterFinder. It uses
|
||||
* a producer-consumer queue to distribute the frames to the threads. The
|
||||
* clusters are collected in a single output queue.
|
||||
* @tparam FRAME_TYPE type of the frame data
|
||||
* @tparam PEDESTAL_TYPE type of the pedestal data
|
||||
* @tparam CT type of the cluster data
|
||||
*/
|
||||
template <typename ClusterType = Cluster<int32_t, 3, 3>,
|
||||
typename FRAME_TYPE = uint16_t, typename PEDESTAL_TYPE = double>
|
||||
class ClusterFinderMT {
|
||||
|
||||
protected:
|
||||
using CT = typename ClusterType::value_type;
|
||||
size_t m_current_thread{0};
|
||||
size_t m_n_threads{0};
|
||||
using Finder = ClusterFinder<ClusterType, FRAME_TYPE, PEDESTAL_TYPE>;
|
||||
using InputQueue = ProducerConsumerQueue<FrameWrapper>;
|
||||
using OutputQueue = ProducerConsumerQueue<ClusterVector<ClusterType>>;
|
||||
std::vector<std::unique_ptr<InputQueue>> m_input_queues;
|
||||
std::vector<std::unique_ptr<OutputQueue>> m_output_queues;
|
||||
|
||||
OutputQueue m_sink{1000}; // All clusters go into this queue
|
||||
|
||||
std::vector<std::unique_ptr<Finder>> m_cluster_finders;
|
||||
std::vector<std::thread> m_threads;
|
||||
std::thread m_collect_thread;
|
||||
std::chrono::milliseconds m_default_wait{1};
|
||||
|
||||
private:
|
||||
std::atomic<bool> m_stop_requested{false};
|
||||
std::atomic<bool> m_processing_threads_stopped{true};
|
||||
|
||||
/**
|
||||
* @brief Function called by the processing threads. It reads the frames
|
||||
* from the input queue and processes them.
|
||||
*/
|
||||
void process(int thread_id) {
|
||||
auto cf = m_cluster_finders[thread_id].get();
|
||||
auto q = m_input_queues[thread_id].get();
|
||||
bool realloc_same_capacity = true;
|
||||
|
||||
while (!m_stop_requested || !q->isEmpty()) {
|
||||
if (FrameWrapper *frame = q->frontPtr(); frame != nullptr) {
|
||||
|
||||
switch (frame->type) {
|
||||
case FrameType::DATA:
|
||||
cf->find_clusters(frame->data.view(), frame->frame_number);
|
||||
m_output_queues[thread_id]->write(
|
||||
cf->steal_clusters(realloc_same_capacity));
|
||||
break;
|
||||
|
||||
case FrameType::PEDESTAL:
|
||||
m_cluster_finders[thread_id]->push_pedestal_frame(
|
||||
frame->data.view());
|
||||
break;
|
||||
}
|
||||
|
||||
// frame is processed now discard it
|
||||
m_input_queues[thread_id]->popFront();
|
||||
} else {
|
||||
std::this_thread::sleep_for(m_default_wait);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Collect all the clusters from the output queues and write them to
|
||||
* the sink
|
||||
*/
|
||||
void collect() {
|
||||
bool empty = true;
|
||||
while (!m_stop_requested || !empty || !m_processing_threads_stopped) {
|
||||
empty = true;
|
||||
for (auto &queue : m_output_queues) {
|
||||
if (!queue->isEmpty()) {
|
||||
|
||||
while (!m_sink.write(std::move(*queue->frontPtr()))) {
|
||||
std::this_thread::sleep_for(m_default_wait);
|
||||
}
|
||||
queue->popFront();
|
||||
empty = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Construct a new ClusterFinderMT object
|
||||
* @param image_size size of the image
|
||||
* @param cluster_size size of the cluster
|
||||
* @param nSigma number of sigma above the pedestal to consider a photon
|
||||
* @param capacity initial capacity of the cluster vector. Should match
|
||||
* expected number of clusters in a frame per frame.
|
||||
* @param n_threads number of threads to use
|
||||
*/
|
||||
ClusterFinderMT(Shape<2> image_size, PEDESTAL_TYPE nSigma = 5.0,
|
||||
size_t capacity = 2000, size_t n_threads = 3)
|
||||
: m_n_threads(n_threads) {
|
||||
|
||||
LOG(logDEBUG1) << "ClusterFinderMT: "
|
||||
<< "image_size: " << image_size[0] << "x"
|
||||
<< image_size[1] << ", nSigma: " << nSigma
|
||||
<< ", capacity: " << capacity
|
||||
<< ", n_threads: " << n_threads;
|
||||
|
||||
for (size_t i = 0; i < n_threads; i++) {
|
||||
m_cluster_finders.push_back(
|
||||
std::make_unique<
|
||||
ClusterFinder<ClusterType, FRAME_TYPE, PEDESTAL_TYPE>>(
|
||||
image_size, nSigma, capacity));
|
||||
}
|
||||
for (size_t i = 0; i < n_threads; i++) {
|
||||
m_input_queues.emplace_back(std::make_unique<InputQueue>(200));
|
||||
m_output_queues.emplace_back(std::make_unique<OutputQueue>(200));
|
||||
}
|
||||
// TODO! Should we start automatically?
|
||||
start();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return the sink queue where all the clusters are collected
|
||||
* @warning You need to empty this queue otherwise the cluster finder will
|
||||
* wait forever
|
||||
*/
|
||||
ProducerConsumerQueue<ClusterVector<ClusterType>> *sink() {
|
||||
return &m_sink;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Start all processing threads
|
||||
*/
|
||||
void start() {
|
||||
m_processing_threads_stopped = false;
|
||||
m_stop_requested = false;
|
||||
|
||||
for (size_t i = 0; i < m_n_threads; i++) {
|
||||
m_threads.push_back(
|
||||
std::thread(&ClusterFinderMT::process, this, i));
|
||||
}
|
||||
|
||||
m_collect_thread = std::thread(&ClusterFinderMT::collect, this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Stop all processing threads
|
||||
*/
|
||||
void stop() {
|
||||
m_stop_requested = true;
|
||||
|
||||
for (auto &thread : m_threads) {
|
||||
thread.join();
|
||||
}
|
||||
m_threads.clear();
|
||||
|
||||
m_processing_threads_stopped = true;
|
||||
m_collect_thread.join();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Wait for all the queues to be empty. Mostly used for timing tests.
|
||||
*/
|
||||
void sync() {
|
||||
for (auto &q : m_input_queues) {
|
||||
while (!q->isEmpty()) {
|
||||
std::this_thread::sleep_for(m_default_wait);
|
||||
}
|
||||
}
|
||||
for (auto &q : m_output_queues) {
|
||||
while (!q->isEmpty()) {
|
||||
std::this_thread::sleep_for(m_default_wait);
|
||||
}
|
||||
}
|
||||
while (!m_sink.isEmpty()) {
|
||||
std::this_thread::sleep_for(m_default_wait);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Push a pedestal frame to all the cluster finders. The frames is
|
||||
* expected to be dark. No photon finding is done. Just pedestal update.
|
||||
*/
|
||||
void push_pedestal_frame(NDView<FRAME_TYPE, 2> frame) {
|
||||
FrameWrapper fw{FrameType::PEDESTAL, 0,
|
||||
NDArray(frame)}; // TODO! copies the data!
|
||||
|
||||
for (auto &queue : m_input_queues) {
|
||||
while (!queue->write(fw)) {
|
||||
std::this_thread::sleep_for(m_default_wait);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Push the frame to the queue of the next available thread. Function
|
||||
* returns once the frame is in a queue.
|
||||
* @note Spin locks with a default wait if the queue is full.
|
||||
*/
|
||||
void find_clusters(NDView<FRAME_TYPE, 2> frame, uint64_t frame_number = 0) {
|
||||
FrameWrapper fw{FrameType::DATA, frame_number,
|
||||
NDArray(frame)}; // TODO! copies the data!
|
||||
while (!m_input_queues[m_current_thread % m_n_threads]->write(fw)) {
|
||||
std::this_thread::sleep_for(m_default_wait);
|
||||
}
|
||||
m_current_thread++;
|
||||
}
|
||||
|
||||
void clear_pedestal() {
|
||||
if (!m_processing_threads_stopped) {
|
||||
throw std::runtime_error("ClusterFinderMT is still running");
|
||||
}
|
||||
for (auto &cf : m_cluster_finders) {
|
||||
cf->clear_pedestal();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return the pedestal currently used by the cluster finder
|
||||
* @param thread_index index of the thread
|
||||
*/
|
||||
auto pedestal(size_t thread_index = 0) {
|
||||
if (m_cluster_finders.empty()) {
|
||||
throw std::runtime_error("No cluster finders available");
|
||||
}
|
||||
if (!m_processing_threads_stopped) {
|
||||
throw std::runtime_error("ClusterFinderMT is still running");
|
||||
}
|
||||
if (thread_index >= m_cluster_finders.size()) {
|
||||
throw std::runtime_error("Thread index out of range");
|
||||
}
|
||||
return m_cluster_finders[thread_index]->pedestal();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return the noise currently used by the cluster finder
|
||||
* @param thread_index index of the thread
|
||||
*/
|
||||
auto noise(size_t thread_index = 0) {
|
||||
if (m_cluster_finders.empty()) {
|
||||
throw std::runtime_error("No cluster finders available");
|
||||
}
|
||||
if (!m_processing_threads_stopped) {
|
||||
throw std::runtime_error("ClusterFinderMT is still running");
|
||||
}
|
||||
if (thread_index >= m_cluster_finders.size()) {
|
||||
throw std::runtime_error("Thread index out of range");
|
||||
}
|
||||
return m_cluster_finders[thread_index]->noise();
|
||||
}
|
||||
|
||||
// void push(FrameWrapper&& frame) {
|
||||
// //TODO! need to loop until we are successful
|
||||
// auto rc = m_input_queue.write(std::move(frame));
|
||||
// fmt::print("pushed frame {}\n", rc);
|
||||
// }
|
||||
};
|
||||
|
||||
} // namespace aare
|
@ -1,4 +1,7 @@
|
||||
#pragma once
|
||||
#include "aare/Cluster.hpp" //TODO maybe store in seperate file !!!
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <numeric>
|
||||
@ -6,174 +9,161 @@
|
||||
|
||||
#include <fmt/core.h>
|
||||
|
||||
#include "aare/Cluster.hpp"
|
||||
#include "aare/NDView.hpp"
|
||||
|
||||
namespace aare {
|
||||
|
||||
template <typename ClusterType,
|
||||
typename = std::enable_if_t<is_cluster_v<ClusterType>>>
|
||||
class ClusterVector; // Forward declaration
|
||||
|
||||
/**
|
||||
* @brief ClusterVector is a container for clusters of various sizes. It uses a
|
||||
* contiguous memory buffer to store the clusters.
|
||||
* @brief ClusterVector is a container for clusters of various sizes. It
|
||||
* uses a contiguous memory buffer to store the clusters. It is templated on
|
||||
* the data type and the coordinate type of the clusters.
|
||||
* @note push_back can invalidate pointers to elements in the container
|
||||
* @warning ClusterVector is currently move only to catch unintended copies,
|
||||
* but this might change since there are probably use cases where copying is
|
||||
* needed.
|
||||
* @tparam T data type of the pixels in the cluster
|
||||
* @tparam CoordType data type of the x and y coordinates of the cluster (normally int16_t)
|
||||
* @tparam CoordType data type of the x and y coordinates of the cluster
|
||||
* (normally int16_t)
|
||||
*/
|
||||
template <typename T, typename CoordType=int16_t> class ClusterVector {
|
||||
using value_type = T;
|
||||
size_t m_cluster_size_x;
|
||||
size_t m_cluster_size_y;
|
||||
std::byte *m_data{};
|
||||
size_t m_size{0};
|
||||
size_t m_capacity;
|
||||
/*
|
||||
Format string used in the python bindings to create a numpy
|
||||
array from the buffer
|
||||
= - native byte order
|
||||
h - short
|
||||
d - double
|
||||
i - int
|
||||
*/
|
||||
constexpr static char m_fmt_base[] = "=h:x:\nh:y:\n({},{}){}:data:" ;
|
||||
template <typename T, uint8_t ClusterSizeX, uint8_t ClusterSizeY,
|
||||
typename CoordType>
|
||||
class ClusterVector<Cluster<T, ClusterSizeX, ClusterSizeY, CoordType>> {
|
||||
|
||||
std::vector<Cluster<T, ClusterSizeX, ClusterSizeY, CoordType>> m_data{};
|
||||
int32_t m_frame_number{0}; // TODO! Check frame number size and type
|
||||
|
||||
public:
|
||||
using value_type = T;
|
||||
using ClusterType = Cluster<T, ClusterSizeX, ClusterSizeY, CoordType>;
|
||||
|
||||
/**
|
||||
* @brief Construct a new ClusterVector object
|
||||
* @param cluster_size_x size of the cluster in x direction
|
||||
* @param cluster_size_y size of the cluster in y direction
|
||||
* @param capacity initial capacity of the buffer in number of clusters
|
||||
* @param frame_number frame number of the clusters. Default is 0, which is
|
||||
* also used to indicate that the clusters come from many frames
|
||||
*/
|
||||
ClusterVector(size_t cluster_size_x, size_t cluster_size_y,
|
||||
size_t capacity = 1024)
|
||||
: m_cluster_size_x(cluster_size_x), m_cluster_size_y(cluster_size_y),
|
||||
m_capacity(capacity) {
|
||||
allocate_buffer(capacity);
|
||||
}
|
||||
~ClusterVector() {
|
||||
delete[] m_data;
|
||||
ClusterVector(size_t capacity = 1024, uint64_t frame_number = 0)
|
||||
: m_frame_number(frame_number) {
|
||||
m_data.reserve(capacity);
|
||||
}
|
||||
|
||||
|
||||
//Move constructor
|
||||
// Move constructor
|
||||
ClusterVector(ClusterVector &&other) noexcept
|
||||
: m_cluster_size_x(other.m_cluster_size_x),
|
||||
m_cluster_size_y(other.m_cluster_size_y), m_data(other.m_data),
|
||||
m_size(other.m_size), m_capacity(other.m_capacity) {
|
||||
other.m_data = nullptr;
|
||||
other.m_size = 0;
|
||||
other.m_capacity = 0;
|
||||
: m_data(other.m_data), m_frame_number(other.m_frame_number) {
|
||||
other.m_data.clear();
|
||||
}
|
||||
|
||||
//Move assignment operator
|
||||
ClusterVector& operator=(ClusterVector &&other) noexcept {
|
||||
// Move assignment operator
|
||||
ClusterVector &operator=(ClusterVector &&other) noexcept {
|
||||
if (this != &other) {
|
||||
delete[] m_data;
|
||||
m_cluster_size_x = other.m_cluster_size_x;
|
||||
m_cluster_size_y = other.m_cluster_size_y;
|
||||
m_data = other.m_data;
|
||||
m_size = other.m_size;
|
||||
m_capacity = other.m_capacity;
|
||||
other.m_data = nullptr;
|
||||
other.m_size = 0;
|
||||
other.m_capacity = 0;
|
||||
m_frame_number = other.m_frame_number;
|
||||
other.m_data.clear();
|
||||
other.m_frame_number = 0;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Reserve space for at least capacity clusters
|
||||
* @param capacity number of clusters to reserve space for
|
||||
* @note If capacity is less than the current capacity, the function does nothing.
|
||||
*/
|
||||
void reserve(size_t capacity) {
|
||||
if (capacity > m_capacity) {
|
||||
allocate_buffer(capacity);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Add a cluster to the vector
|
||||
* @param x x-coordinate of the cluster
|
||||
* @param y y-coordinate of the cluster
|
||||
* @param data pointer to the data of the cluster
|
||||
* @warning The data pointer must point to a buffer of size cluster_size_x * cluster_size_y * sizeof(T)
|
||||
*/
|
||||
void push_back(CoordType x, CoordType y, const std::byte *data) {
|
||||
if (m_size == m_capacity) {
|
||||
allocate_buffer(m_capacity * 2);
|
||||
}
|
||||
std::byte *ptr = element_ptr(m_size);
|
||||
*reinterpret_cast<CoordType *>(ptr) = x;
|
||||
ptr += sizeof(CoordType);
|
||||
*reinterpret_cast<CoordType *>(ptr) = y;
|
||||
ptr += sizeof(CoordType);
|
||||
|
||||
std::copy(data, data + m_cluster_size_x * m_cluster_size_y * sizeof(T),
|
||||
ptr);
|
||||
m_size++;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Sum the pixels in each cluster
|
||||
* @return std::vector<T> vector of sums for each cluster
|
||||
*/
|
||||
std::vector<T> sum() {
|
||||
std::vector<T> sums(m_size);
|
||||
const size_t stride = element_offset();
|
||||
const size_t n_pixels = m_cluster_size_x * m_cluster_size_y;
|
||||
std::byte *ptr = m_data + 2 * sizeof(CoordType); // skip x and y
|
||||
std::vector<T> sums(m_data.size());
|
||||
|
||||
std::transform(
|
||||
m_data.begin(), m_data.end(), sums.begin(),
|
||||
[](const ClusterType &cluster) { return cluster.sum(); });
|
||||
|
||||
for (size_t i = 0; i < m_size; i++) {
|
||||
sums[i] =
|
||||
std::accumulate(reinterpret_cast<T *>(ptr),
|
||||
reinterpret_cast<T *>(ptr) + n_pixels, T{});
|
||||
ptr += stride;
|
||||
}
|
||||
return sums;
|
||||
}
|
||||
|
||||
size_t size() const { return m_size; }
|
||||
size_t capacity() const { return m_capacity; }
|
||||
|
||||
/**
|
||||
* @brief Return the offset in bytes for a single cluster
|
||||
* @brief Sum the pixels in the 2x2 subcluster with the biggest pixel sum in
|
||||
* each cluster
|
||||
* @return std::vector<T> vector of sums for each cluster
|
||||
*/
|
||||
size_t element_offset() const {
|
||||
return 2*sizeof(CoordType) +
|
||||
m_cluster_size_x * m_cluster_size_y * sizeof(T);
|
||||
}
|
||||
/**
|
||||
* @brief Return the offset in bytes for the i-th cluster
|
||||
*/
|
||||
size_t element_offset(size_t i) const { return element_offset() * i; }
|
||||
std::vector<T> sum_2x2() {
|
||||
std::vector<T> sums_2x2(m_data.size());
|
||||
|
||||
/**
|
||||
* @brief Return a pointer to the i-th cluster
|
||||
*/
|
||||
std::byte *element_ptr(size_t i) { return m_data + element_offset(i); }
|
||||
const std::byte * element_ptr(size_t i) const { return m_data + element_offset(i); }
|
||||
std::transform(m_data.begin(), m_data.end(), sums_2x2.begin(),
|
||||
[](const ClusterType &cluster) {
|
||||
return cluster.max_sum_2x2().first;
|
||||
});
|
||||
|
||||
size_t cluster_size_x() const { return m_cluster_size_x; }
|
||||
size_t cluster_size_y() const { return m_cluster_size_y; }
|
||||
|
||||
std::byte *data() { return m_data; }
|
||||
std::byte const *data() const { return m_data; }
|
||||
|
||||
template<typename V>
|
||||
V& at(size_t i) {
|
||||
return *reinterpret_cast<V*>(element_ptr(i));
|
||||
return sums_2x2;
|
||||
}
|
||||
|
||||
const std::string_view fmt_base() const {
|
||||
//TODO! how do we match on coord_t?
|
||||
return m_fmt_base;
|
||||
/**
|
||||
* @brief Reserve space for at least capacity clusters
|
||||
* @param capacity number of clusters to reserve space for
|
||||
* @note If capacity is less than the current capacity, the function does
|
||||
* nothing.
|
||||
*/
|
||||
void reserve(size_t capacity) { m_data.reserve(capacity); }
|
||||
|
||||
void resize(size_t size) { m_data.resize(size); }
|
||||
|
||||
void push_back(const ClusterType &cluster) { m_data.push_back(cluster); }
|
||||
|
||||
ClusterVector &operator+=(const ClusterVector &other) {
|
||||
m_data.insert(m_data.end(), other.begin(), other.end());
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
void allocate_buffer(size_t new_capacity) {
|
||||
size_t num_bytes = element_offset() * new_capacity;
|
||||
std::byte *new_data = new std::byte[num_bytes]{};
|
||||
std::copy(m_data, m_data + element_offset() * m_size, new_data);
|
||||
delete[] m_data;
|
||||
m_data = new_data;
|
||||
m_capacity = new_capacity;
|
||||
/**
|
||||
* @brief Return the number of clusters in the vector
|
||||
*/
|
||||
size_t size() const { return m_data.size(); }
|
||||
|
||||
uint8_t cluster_size_x() const { return ClusterSizeX; }
|
||||
|
||||
uint8_t cluster_size_y() const { return ClusterSizeY; }
|
||||
|
||||
/**
|
||||
* @brief Return the capacity of the buffer in number of clusters. This is
|
||||
* the number of clusters that can be stored in the current buffer without
|
||||
* reallocation.
|
||||
*/
|
||||
size_t capacity() const { return m_data.capacity(); }
|
||||
|
||||
auto begin() const { return m_data.begin(); }
|
||||
|
||||
auto end() const { return m_data.end(); }
|
||||
|
||||
/**
|
||||
* @brief Return the size in bytes of a single cluster
|
||||
*/
|
||||
size_t item_size() const {
|
||||
return sizeof(ClusterType); // 2 * sizeof(CoordType) + ClusterSizeX *
|
||||
// ClusterSizeY * sizeof(T);
|
||||
}
|
||||
|
||||
ClusterType *data() { return m_data.data(); }
|
||||
ClusterType const *data() const { return m_data.data(); }
|
||||
|
||||
/**
|
||||
* @brief Return a reference to the i-th cluster casted to type V
|
||||
* @tparam V type of the cluster
|
||||
*/
|
||||
ClusterType &operator[](size_t i) { return m_data[i]; }
|
||||
|
||||
const ClusterType &operator[](size_t i) const { return m_data[i]; }
|
||||
|
||||
/**
|
||||
* @brief Return the frame number of the clusters. 0 is used to indicate
|
||||
* that the clusters come from many frames
|
||||
*/
|
||||
int32_t frame_number() const { return m_frame_number; }
|
||||
|
||||
void set_frame_number(int32_t frame_number) {
|
||||
m_frame_number = frame_number;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1,27 +1,27 @@
|
||||
#pragma once
|
||||
|
||||
#include "aare/FileInterface.hpp"
|
||||
#include "aare/RawMasterFile.hpp"
|
||||
#include "aare/Frame.hpp"
|
||||
#include "aare/RawMasterFile.hpp"
|
||||
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
|
||||
namespace aare{
|
||||
namespace aare {
|
||||
|
||||
|
||||
class CtbRawFile{
|
||||
class CtbRawFile {
|
||||
RawMasterFile m_master;
|
||||
std::ifstream m_file;
|
||||
size_t m_current_frame{0};
|
||||
size_t m_current_subfile{0};
|
||||
size_t m_num_subfiles{0};
|
||||
public:
|
||||
|
||||
public:
|
||||
CtbRawFile(const std::filesystem::path &fname);
|
||||
|
||||
void read_into(std::byte *image_buf, DetectorHeader* header = nullptr);
|
||||
void seek(size_t frame_index); //!< seek to the given frame index
|
||||
size_t tell() const; //!< get the frame index of the file pointer
|
||||
void read_into(std::byte *image_buf, DetectorHeader *header = nullptr);
|
||||
void seek(size_t frame_index); //!< seek to the given frame index
|
||||
size_t tell() const; //!< get the frame index of the file pointer
|
||||
|
||||
// in the specific class we can expose more functionality
|
||||
|
||||
@ -29,13 +29,13 @@ public:
|
||||
size_t frames_in_file() const;
|
||||
|
||||
RawMasterFile master() const;
|
||||
private:
|
||||
|
||||
private:
|
||||
void find_subfiles();
|
||||
size_t sub_file_index(size_t frame_index) const {
|
||||
return frame_index / m_master.max_frames_per_file();
|
||||
}
|
||||
void open_data_file(size_t subfile_index);
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace aare
|
@ -6,31 +6,37 @@
|
||||
|
||||
namespace aare {
|
||||
|
||||
// The format descriptor is a single character that specifies the type of the data
|
||||
// The format descriptor is a single character that specifies the type of the
|
||||
// data
|
||||
// - python documentation: https://docs.python.org/3/c-api/arg.html#numbers
|
||||
// - py::format_descriptor<T>::format() (in pybind11) does not return the same format as
|
||||
// - py::format_descriptor<T>::format() (in pybind11) does not return the same
|
||||
// format as
|
||||
// written in python.org documentation.
|
||||
// - numpy also doesn't use the same format. and also numpy associates the format
|
||||
// with variable bitdepth types. (e.g. long is int64 on linux64 and int32 on win64)
|
||||
// https://numpy.org/doc/stable/reference/arrays.scalars.html
|
||||
// - numpy also doesn't use the same format. and also numpy associates the
|
||||
// format
|
||||
// with variable bitdepth types. (e.g. long is int64 on linux64 and int32 on
|
||||
// win64) https://numpy.org/doc/stable/reference/arrays.scalars.html
|
||||
//
|
||||
// github issue discussing this:
|
||||
// https://github.com/pybind/pybind11/issues/1908#issuecomment-658358767
|
||||
//
|
||||
// [IN LINUX] the difference is for int64 (long) and uint64 (unsigned long). The format
|
||||
// descriptor is 'q' and 'Q' respectively and in the documentation it is 'l' and 'k'.
|
||||
// [IN LINUX] the difference is for int64 (long) and uint64 (unsigned long). The
|
||||
// format descriptor is 'q' and 'Q' respectively and in the documentation it is
|
||||
// 'l' and 'k'.
|
||||
|
||||
// in practice numpy doesn't seem to care when reading buffer info: the library
|
||||
// interprets 'q' or 'l' as int64 and 'Q' or 'L' as uint64.
|
||||
// for this reason we decided to use the same format descriptor as pybind to avoid
|
||||
// any further discrepancies.
|
||||
// for this reason we decided to use the same format descriptor as pybind to
|
||||
// avoid any further discrepancies.
|
||||
|
||||
// in the following order:
|
||||
// int8, uint8, int16, uint16, int32, uint32, int64, uint64, float, double
|
||||
const char DTYPE_FORMAT_DSC[] = {'b', 'B', 'h', 'H', 'i', 'I', 'q', 'Q', 'f', 'd'};
|
||||
const char DTYPE_FORMAT_DSC[] = {'b', 'B', 'h', 'H', 'i',
|
||||
'I', 'q', 'Q', 'f', 'd'};
|
||||
|
||||
// on linux64 & apple
|
||||
const char NUMPY_FORMAT_DSC[] = {'b', 'B', 'h', 'H', 'i', 'I', 'l', 'L', 'f', 'd'};
|
||||
const char NUMPY_FORMAT_DSC[] = {'b', 'B', 'h', 'H', 'i',
|
||||
'I', 'l', 'L', 'f', 'd'};
|
||||
/**
|
||||
* @brief enum class to define the endianess of the system
|
||||
*/
|
||||
@ -52,12 +58,29 @@ enum class endian {
|
||||
*/
|
||||
class Dtype {
|
||||
public:
|
||||
enum TypeIndex { INT8, UINT8, INT16, UINT16, INT32, UINT32, INT64, UINT64, FLOAT, DOUBLE, ERROR, NONE };
|
||||
enum TypeIndex {
|
||||
INT8,
|
||||
UINT8,
|
||||
INT16,
|
||||
UINT16,
|
||||
INT32,
|
||||
UINT32,
|
||||
INT64,
|
||||
UINT64,
|
||||
FLOAT,
|
||||
DOUBLE,
|
||||
ERROR,
|
||||
NONE
|
||||
};
|
||||
|
||||
uint8_t bitdepth() const;
|
||||
size_t bytes() const;
|
||||
std::string format_descr() const { return std::string(1, DTYPE_FORMAT_DSC[static_cast<int>(m_type)]); }
|
||||
std::string numpy_descr() const { return std::string(1, NUMPY_FORMAT_DSC[static_cast<int>(m_type)]); }
|
||||
std::string format_descr() const {
|
||||
return std::string(1, DTYPE_FORMAT_DSC[static_cast<int>(m_type)]);
|
||||
}
|
||||
std::string numpy_descr() const {
|
||||
return std::string(1, NUMPY_FORMAT_DSC[static_cast<int>(m_type)]);
|
||||
}
|
||||
|
||||
explicit Dtype(const std::type_info &t);
|
||||
explicit Dtype(std::string_view sv);
|
||||
|
@ -5,12 +5,12 @@
|
||||
namespace aare {
|
||||
|
||||
/**
|
||||
* @brief RAII File class for reading, and in the future potentially writing
|
||||
* image files in various formats. Minimal generic interface. For specail fuctions
|
||||
* plase use the RawFile or NumpyFile classes directly.
|
||||
* Wraps FileInterface to abstract the underlying file format
|
||||
* @note **frame_number** refers the the frame number sent by the detector while **frame_index**
|
||||
* is the position of the frame in the file
|
||||
* @brief RAII File class for reading, and in the future potentially writing
|
||||
* image files in various formats. Minimal generic interface. For specail
|
||||
* fuctions plase use the RawFile, NumpyFile or Hdf5File classes directly. Wraps
|
||||
* FileInterface to abstract the underlying file format
|
||||
* @note **frame_number** refers the the frame number sent by the detector while
|
||||
* **frame_index** is the position of the frame in the file
|
||||
*/
|
||||
class File {
|
||||
std::unique_ptr<FileInterface> file_impl;
|
||||
@ -25,40 +25,46 @@ class File {
|
||||
* @throws std::invalid_argument if the file mode is not supported
|
||||
*
|
||||
*/
|
||||
File(const std::filesystem::path &fname, const std::string &mode="r", const FileConfig &cfg = {});
|
||||
|
||||
/**Since the object is responsible for managing the file we disable copy construction */
|
||||
File(File const &other) = delete;
|
||||
File(const std::filesystem::path &fname, const std::string &mode = "r",
|
||||
const FileConfig &cfg = {});
|
||||
|
||||
/**Since the object is responsible for managing the file we disable copy
|
||||
* construction */
|
||||
File(File const &other) = delete;
|
||||
|
||||
/**The same goes for copy assignment */
|
||||
File& operator=(File const &other) = delete;
|
||||
File &operator=(File const &other) = delete;
|
||||
|
||||
File(File &&other) noexcept;
|
||||
File& operator=(File &&other) noexcept;
|
||||
File &operator=(File &&other) noexcept;
|
||||
~File() = default;
|
||||
|
||||
Frame read_frame(); //!< read one frame from the file at the current position
|
||||
Frame read_frame(size_t frame_index); //!< read one frame at the position given by frame number
|
||||
std::vector<Frame> read_n(size_t n_frames); //!< read n_frames from the file at the current position
|
||||
|
||||
// void close(); //!< close the file
|
||||
|
||||
Frame
|
||||
read_frame(); //!< read one frame from the file at the current position
|
||||
Frame read_frame(size_t frame_index); //!< read one frame at the position
|
||||
//!< given by frame number
|
||||
std::vector<Frame> read_n(size_t n_frames); //!< read n_frames from the file
|
||||
//!< at the current position
|
||||
|
||||
void read_into(std::byte *image_buf);
|
||||
void read_into(std::byte *image_buf, size_t n_frames);
|
||||
|
||||
size_t frame_number(); //!< get the frame number at the current position
|
||||
size_t frame_number(size_t frame_index); //!< get the frame number at the given frame index
|
||||
size_t bytes_per_frame() const;
|
||||
size_t pixels_per_frame() const;
|
||||
size_t bytes_per_pixel() const;
|
||||
|
||||
size_t frame_number(); //!< get the frame number at the current position
|
||||
size_t frame_number(
|
||||
size_t frame_index); //!< get the frame number at the given frame index
|
||||
size_t bytes_per_frame() const;
|
||||
size_t pixels_per_frame() const;
|
||||
size_t bytes_per_pixel() const;
|
||||
size_t bitdepth() const;
|
||||
void seek(size_t frame_index); //!< seek to the given frame index
|
||||
size_t tell() const; //!< get the frame index of the file pointer
|
||||
void seek(size_t frame_index); //!< seek to the given frame index
|
||||
size_t tell() const; //!< get the frame index of the file pointer
|
||||
size_t total_frames() const;
|
||||
size_t rows() const;
|
||||
size_t cols() const;
|
||||
|
||||
DetectorType detector_type() const;
|
||||
|
||||
|
||||
};
|
||||
|
||||
} // namespace aare
|
@ -1,7 +1,7 @@
|
||||
#pragma once
|
||||
#include "aare/Dtype.hpp"
|
||||
#include "aare/Frame.hpp"
|
||||
#include "aare/defs.hpp"
|
||||
#include "aare/to_string.hpp"
|
||||
|
||||
#include <filesystem>
|
||||
#include <vector>
|
||||
@ -20,8 +20,10 @@ struct FileConfig {
|
||||
uint64_t rows{};
|
||||
uint64_t cols{};
|
||||
bool operator==(const FileConfig &other) const {
|
||||
return dtype == other.dtype && rows == other.rows && cols == other.cols && geometry == other.geometry &&
|
||||
detector_type == other.detector_type && max_frames_per_file == other.max_frames_per_file;
|
||||
return dtype == other.dtype && rows == other.rows &&
|
||||
cols == other.cols && geometry == other.geometry &&
|
||||
detector_type == other.detector_type &&
|
||||
max_frames_per_file == other.max_frames_per_file;
|
||||
}
|
||||
bool operator!=(const FileConfig &other) const { return !(*this == other); }
|
||||
|
||||
@ -32,8 +34,11 @@ struct FileConfig {
|
||||
int max_frames_per_file{};
|
||||
size_t total_frames{};
|
||||
std::string to_string() const {
|
||||
return "{ dtype: " + dtype.to_string() + ", rows: " + std::to_string(rows) + ", cols: " + std::to_string(cols) +
|
||||
", geometry: " + geometry.to_string() + ", detector_type: " + ToString(detector_type) +
|
||||
return "{ dtype: " + dtype.to_string() +
|
||||
", rows: " + std::to_string(rows) +
|
||||
", cols: " + std::to_string(cols) +
|
||||
", geometry: " + geometry.to_string() +
|
||||
", detector_type: " + ToString(detector_type) +
|
||||
", max_frames_per_file: " + std::to_string(max_frames_per_file) +
|
||||
", total_frames: " + std::to_string(total_frames) + " }";
|
||||
}
|
||||
@ -41,8 +46,9 @@ struct FileConfig {
|
||||
|
||||
/**
|
||||
* @brief FileInterface class to define the interface for file operations
|
||||
* @note parent class for NumpyFile and RawFile
|
||||
* @note all functions are pure virtual and must be implemented by the derived classes
|
||||
* @note parent class for NumpyFile, RawFile and Hdf5File
|
||||
* @note all functions are pure virtual and must be implemented by the derived
|
||||
* classes
|
||||
*/
|
||||
class FileInterface {
|
||||
public:
|
||||
@ -64,17 +70,20 @@ class FileInterface {
|
||||
* @param n_frames number of frames to read
|
||||
* @return vector of frames
|
||||
*/
|
||||
virtual std::vector<Frame> read_n(size_t n_frames) = 0; // Is this the right interface?
|
||||
virtual std::vector<Frame>
|
||||
read_n(size_t n_frames) = 0; // Is this the right interface?
|
||||
|
||||
/**
|
||||
* @brief read one frame from the file at the current position and store it in the provided buffer
|
||||
* @brief read one frame from the file at the current position and store it
|
||||
* in the provided buffer
|
||||
* @param image_buf buffer to store the frame
|
||||
* @return void
|
||||
*/
|
||||
virtual void read_into(std::byte *image_buf) = 0;
|
||||
|
||||
/**
|
||||
* @brief read n_frames from the file at the current position and store them in the provided buffer
|
||||
* @brief read n_frames from the file at the current position and store them
|
||||
* in the provided buffer
|
||||
* @param image_buf buffer to store the frames
|
||||
* @param n_frames number of frames to read
|
||||
* @return void
|
||||
@ -134,7 +143,6 @@ class FileInterface {
|
||||
*/
|
||||
virtual size_t bitdepth() const = 0;
|
||||
|
||||
|
||||
virtual DetectorType detector_type() const = 0;
|
||||
|
||||
// function to query the data type of the file
|
||||
|
30
include/aare/FilePtr.hpp
Normal file
30
include/aare/FilePtr.hpp
Normal file
@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
#include <cstdio>
|
||||
#include <filesystem>
|
||||
|
||||
namespace aare {
|
||||
|
||||
/**
|
||||
* \brief RAII wrapper for FILE pointer
|
||||
*/
|
||||
class FilePtr {
|
||||
FILE *fp_{nullptr};
|
||||
|
||||
public:
|
||||
FilePtr() = default;
|
||||
FilePtr(const std::filesystem::path &fname, const std::string &mode);
|
||||
FilePtr(const FilePtr &) = delete; // we don't want a copy
|
||||
FilePtr &operator=(const FilePtr &) = delete; // since we handle a resource
|
||||
FilePtr(FilePtr &&other);
|
||||
FilePtr &operator=(FilePtr &&other);
|
||||
FILE *get();
|
||||
ssize_t tell();
|
||||
void seek(ssize_t offset, int whence = SEEK_SET) {
|
||||
if (fseek(fp_, offset, whence) != 0)
|
||||
throw std::runtime_error("Error seeking in file");
|
||||
}
|
||||
std::string error_msg();
|
||||
~FilePtr();
|
||||
};
|
||||
|
||||
} // namespace aare
|
120
include/aare/Fit.hpp
Normal file
120
include/aare/Fit.hpp
Normal file
@ -0,0 +1,120 @@
|
||||
#pragma once
|
||||
|
||||
#include <cmath>
|
||||
#include <fmt/core.h>
|
||||
#include <vector>
|
||||
|
||||
#include "aare/NDArray.hpp"
|
||||
|
||||
namespace aare {
|
||||
|
||||
namespace func {
|
||||
double gaus(const double x, const double *par);
|
||||
NDArray<double, 1> gaus(NDView<double, 1> x, NDView<double, 1> par);
|
||||
|
||||
double pol1(const double x, const double *par);
|
||||
NDArray<double, 1> pol1(NDView<double, 1> x, NDView<double, 1> par);
|
||||
|
||||
double scurve(const double x, const double *par);
|
||||
NDArray<double, 1> scurve(NDView<double, 1> x, NDView<double, 1> par);
|
||||
|
||||
double scurve2(const double x, const double *par);
|
||||
NDArray<double, 1> scurve2(NDView<double, 1> x, NDView<double, 1> par);
|
||||
|
||||
} // namespace func
|
||||
|
||||
/**
|
||||
* @brief Estimate the initial parameters for a Gaussian fit
|
||||
*/
|
||||
std::array<double, 3> gaus_init_par(const NDView<double, 1> x,
|
||||
const NDView<double, 1> y);
|
||||
|
||||
std::array<double, 2> pol1_init_par(const NDView<double, 1> x,
|
||||
const NDView<double, 1> y);
|
||||
|
||||
std::array<double, 6> scurve_init_par(const NDView<double, 1> x,
|
||||
const NDView<double, 1> y);
|
||||
std::array<double, 6> scurve2_init_par(const NDView<double, 1> x,
|
||||
const NDView<double, 1> y);
|
||||
|
||||
static constexpr int DEFAULT_NUM_THREADS = 4;
|
||||
|
||||
/**
|
||||
* @brief Fit a 1D Gaussian to data.
|
||||
* @param data data to fit
|
||||
* @param x x values
|
||||
*/
|
||||
NDArray<double, 1> fit_gaus(NDView<double, 1> x, NDView<double, 1> y);
|
||||
|
||||
/**
|
||||
* @brief Fit a 1D Gaussian to each pixel. Data layout [row, col, values]
|
||||
* @param x x values
|
||||
* @param y y values, layout [row, col, values]
|
||||
* @param n_threads number of threads to use
|
||||
*/
|
||||
|
||||
NDArray<double, 3> fit_gaus(NDView<double, 1> x, NDView<double, 3> y,
|
||||
int n_threads = DEFAULT_NUM_THREADS);
|
||||
|
||||
/**
|
||||
* @brief Fit a 1D Gaussian with error estimates
|
||||
* @param x x 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
|
||||
*/
|
||||
void fit_gaus(NDView<double, 1> x, NDView<double, 1> y, NDView<double, 1> y_err,
|
||||
NDView<double, 1> par_out, NDView<double, 1> par_err_out,
|
||||
double &chi2);
|
||||
|
||||
/**
|
||||
* @brief Fit a 1D Gaussian to each pixel with error estimates. Data layout
|
||||
* [row, col, values]
|
||||
* @param x x 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]
|
||||
* @param n_threads number of threads to use
|
||||
*/
|
||||
void fit_gaus(NDView<double, 1> x, NDView<double, 3> y, NDView<double, 3> y_err,
|
||||
NDView<double, 3> par_out, NDView<double, 3> par_err_out,
|
||||
NDView<double, 2> chi2_out, int n_threads = DEFAULT_NUM_THREADS);
|
||||
|
||||
NDArray<double, 1> fit_pol1(NDView<double, 1> x, NDView<double, 1> y);
|
||||
|
||||
NDArray<double, 3> fit_pol1(NDView<double, 1> x, NDView<double, 3> y,
|
||||
int n_threads = DEFAULT_NUM_THREADS);
|
||||
|
||||
void fit_pol1(NDView<double, 1> x, NDView<double, 1> y, NDView<double, 1> y_err,
|
||||
NDView<double, 1> par_out, NDView<double, 1> par_err_out,
|
||||
double &chi2);
|
||||
|
||||
// TODO! not sure we need to offer the different version in C++
|
||||
void fit_pol1(NDView<double, 1> x, NDView<double, 3> y, NDView<double, 3> y_err,
|
||||
NDView<double, 3> par_out, NDView<double, 3> par_err_out,
|
||||
NDView<double, 2> chi2_out, int n_threads = DEFAULT_NUM_THREADS);
|
||||
|
||||
NDArray<double, 1> fit_scurve(NDView<double, 1> x, NDView<double, 1> y);
|
||||
NDArray<double, 3> fit_scurve(NDView<double, 1> x, NDView<double, 3> y,
|
||||
int n_threads);
|
||||
void fit_scurve(NDView<double, 1> x, NDView<double, 1> y,
|
||||
NDView<double, 1> y_err, NDView<double, 1> par_out,
|
||||
NDView<double, 1> par_err_out, double &chi2);
|
||||
void fit_scurve(NDView<double, 1> x, NDView<double, 3> y,
|
||||
NDView<double, 3> y_err, NDView<double, 3> par_out,
|
||||
NDView<double, 3> par_err_out, NDView<double, 2> chi2_out,
|
||||
int n_threads);
|
||||
|
||||
NDArray<double, 1> fit_scurve2(NDView<double, 1> x, NDView<double, 1> y);
|
||||
NDArray<double, 3> fit_scurve2(NDView<double, 1> x, NDView<double, 3> y,
|
||||
int n_threads);
|
||||
void fit_scurve2(NDView<double, 1> x, NDView<double, 1> y,
|
||||
NDView<double, 1> y_err, NDView<double, 1> par_out,
|
||||
NDView<double, 1> par_err_out, double &chi2);
|
||||
void fit_scurve2(NDView<double, 1> x, NDView<double, 3> y,
|
||||
NDView<double, 3> y_err, NDView<double, 3> par_out,
|
||||
NDView<double, 3> par_err_out, NDView<double, 2> chi2_out,
|
||||
int n_threads);
|
||||
} // namespace aare
|
@ -19,7 +19,7 @@ class Frame {
|
||||
uint32_t m_cols;
|
||||
Dtype m_dtype;
|
||||
std::byte *m_data;
|
||||
//TODO! Add frame number?
|
||||
// TODO! Add frame number?
|
||||
|
||||
public:
|
||||
/**
|
||||
@ -39,7 +39,7 @@ class Frame {
|
||||
* @param dtype data type of the pixels
|
||||
*/
|
||||
Frame(const std::byte *bytes, uint32_t rows, uint32_t cols, Dtype dtype);
|
||||
~Frame(){ delete[] m_data; };
|
||||
~Frame() { delete[] m_data; };
|
||||
|
||||
/** @warning Copy is disabled to ensure performance when passing
|
||||
* frames around. Can discuss enabling it.
|
||||
@ -52,7 +52,6 @@ class Frame {
|
||||
Frame &operator=(Frame &&other) noexcept;
|
||||
Frame(Frame &&other) noexcept;
|
||||
|
||||
|
||||
Frame clone() const; //<- Explicit copy
|
||||
|
||||
uint32_t rows() const;
|
||||
@ -93,7 +92,7 @@ class Frame {
|
||||
if (row >= m_rows || col >= m_cols) {
|
||||
throw std::out_of_range("Invalid row or column index");
|
||||
}
|
||||
//TODO! add tests then reimplement using pixel_ptr
|
||||
// TODO! add tests then reimplement using pixel_ptr
|
||||
T data;
|
||||
std::memcpy(&data, m_data + (row * m_cols + col) * m_dtype.bytes(),
|
||||
m_dtype.bytes());
|
||||
@ -102,18 +101,18 @@ class Frame {
|
||||
/**
|
||||
* @brief Return an NDView of the frame. This is the preferred way to access
|
||||
* data in the frame.
|
||||
*
|
||||
*
|
||||
* @tparam T type of the pixels
|
||||
* @return NDView<T, 2>
|
||||
* @return NDView<T, 2>
|
||||
*/
|
||||
template <typename T> NDView<T, 2> view() {
|
||||
std::array<int64_t, 2> shape = {static_cast<int64_t>(m_rows),
|
||||
static_cast<int64_t>(m_cols)};
|
||||
std::array<ssize_t, 2> shape = {static_cast<ssize_t>(m_rows),
|
||||
static_cast<ssize_t>(m_cols)};
|
||||
T *data = reinterpret_cast<T *>(m_data);
|
||||
return NDView<T, 2>(data, shape);
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Copy the frame data into a new NDArray. This is a deep copy.
|
||||
*/
|
||||
template <typename T> NDArray<T> image() {
|
||||
|
68
include/aare/GainMap.hpp
Normal file
68
include/aare/GainMap.hpp
Normal file
@ -0,0 +1,68 @@
|
||||
/************************************************
|
||||
* @file GainMap.hpp
|
||||
* @short function to apply gain map of image size to a vector of clusters -
|
||||
*note stored gainmap is inverted for efficient aaplication to images
|
||||
***********************************************/
|
||||
|
||||
#pragma once
|
||||
#include "aare/Cluster.hpp"
|
||||
#include "aare/ClusterVector.hpp"
|
||||
#include "aare/NDArray.hpp"
|
||||
#include "aare/NDView.hpp"
|
||||
#include <memory>
|
||||
|
||||
namespace aare {
|
||||
|
||||
class InvertedGainMap {
|
||||
|
||||
public:
|
||||
explicit InvertedGainMap(const NDArray<double, 2> &gain_map)
|
||||
: m_gain_map(gain_map) {
|
||||
for (auto &item : m_gain_map) {
|
||||
item = 1.0 / item;
|
||||
}
|
||||
};
|
||||
|
||||
explicit InvertedGainMap(const NDView<double, 2> gain_map) {
|
||||
m_gain_map = NDArray<double, 2>(gain_map);
|
||||
for (auto &item : m_gain_map) {
|
||||
item = 1.0 / item;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename ClusterType,
|
||||
typename = std::enable_if_t<is_cluster_v<ClusterType>>>
|
||||
void apply_gain_map(ClusterVector<ClusterType> &clustervec) {
|
||||
// in principle we need to know the size of the image for this lookup
|
||||
size_t ClusterSizeX = clustervec.cluster_size_x();
|
||||
size_t ClusterSizeY = clustervec.cluster_size_y();
|
||||
|
||||
using T = typename ClusterVector<ClusterType>::value_type;
|
||||
|
||||
int64_t index_cluster_center_x = ClusterSizeX / 2;
|
||||
int64_t index_cluster_center_y = ClusterSizeY / 2;
|
||||
for (size_t i = 0; i < clustervec.size(); i++) {
|
||||
auto &cl = clustervec[i];
|
||||
|
||||
if (cl.x > 0 && cl.y > 0 && cl.x < m_gain_map.shape(1) - 1 &&
|
||||
cl.y < m_gain_map.shape(0) - 1) {
|
||||
for (size_t j = 0; j < ClusterSizeX * ClusterSizeY; j++) {
|
||||
size_t x = cl.x + j % ClusterSizeX - index_cluster_center_x;
|
||||
size_t y = cl.y + j / ClusterSizeX - index_cluster_center_y;
|
||||
cl.data[j] = static_cast<T>(
|
||||
static_cast<double>(cl.data[j]) *
|
||||
m_gain_map(
|
||||
y, x)); // cast after conversion to keep precision
|
||||
}
|
||||
} else {
|
||||
// clear edge clusters
|
||||
cl.data.fill(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
NDArray<double, 2> m_gain_map{};
|
||||
};
|
||||
|
||||
} // end of namespace aare
|
211
include/aare/Hdf5File.hpp
Normal file
211
include/aare/Hdf5File.hpp
Normal file
@ -0,0 +1,211 @@
|
||||
#pragma once
|
||||
#include "aare/FileInterface.hpp"
|
||||
#include "aare/Frame.hpp"
|
||||
#include "aare/Hdf5MasterFile.hpp"
|
||||
#include "aare/NDArray.hpp" //for pixel map
|
||||
|
||||
#include <optional>
|
||||
|
||||
namespace aare {
|
||||
|
||||
class H5Handles {
|
||||
std::string file_name;
|
||||
std::string dataset_name;
|
||||
H5::H5File file;
|
||||
H5::DataSet dataset;
|
||||
H5::DataSpace dataspace;
|
||||
H5::DataType datatype;
|
||||
std::unique_ptr<H5::DataSpace> memspace;
|
||||
std::vector<hsize_t> dims;
|
||||
std::vector<hsize_t> count;
|
||||
std::vector<hsize_t> offset;
|
||||
|
||||
public:
|
||||
H5Handles(const std::string &fname, const std::string &dname)
|
||||
: file_name(fname), dataset_name(dname), file(fname, H5F_ACC_RDONLY),
|
||||
dataset(file.openDataSet(dname)), dataspace(dataset.getSpace()),
|
||||
datatype(dataset.getDataType()) {
|
||||
intialize_dimensions();
|
||||
initialize_memspace();
|
||||
}
|
||||
|
||||
std::vector<hsize_t> get_dims() const { return dims; }
|
||||
|
||||
void seek(size_t frame_index) {
|
||||
if (frame_index >= dims[0]) {
|
||||
throw std::runtime_error(LOCATION + "Invalid frame number");
|
||||
}
|
||||
offset[0] = static_cast<hsize_t>(frame_index);
|
||||
}
|
||||
|
||||
void get_data_into(size_t frame_index, std::byte *frame_buffer,
|
||||
size_t n_frames = 1) {
|
||||
seek(frame_index);
|
||||
count[0] = static_cast<hsize_t>(n_frames);
|
||||
// std::cout << "offset:" << ToString(offset) << " count:" <<
|
||||
// ToString(count) << std::endl;
|
||||
dataspace.selectHyperslab(H5S_SELECT_SET, count.data(), offset.data());
|
||||
dataset.read(frame_buffer, datatype, *memspace, dataspace);
|
||||
}
|
||||
|
||||
void get_header_into(size_t frame_index, int part_index,
|
||||
std::byte *header_buffer) {
|
||||
seek(frame_index);
|
||||
offset[1] = static_cast<hsize_t>(part_index);
|
||||
// std::cout << "offset:" << ToString(offset) << " count:" <<
|
||||
// ToString(count) << std::endl;
|
||||
dataspace.selectHyperslab(H5S_SELECT_SET, count.data(), offset.data());
|
||||
dataset.read(header_buffer, datatype, *memspace, dataspace);
|
||||
}
|
||||
|
||||
private:
|
||||
void intialize_dimensions() {
|
||||
int rank = dataspace.getSimpleExtentNdims();
|
||||
dims.resize(rank);
|
||||
dataspace.getSimpleExtentDims(dims.data(), nullptr);
|
||||
}
|
||||
|
||||
void initialize_memspace() {
|
||||
int rank = dataspace.getSimpleExtentNdims();
|
||||
count.clear();
|
||||
offset.clear();
|
||||
|
||||
// header datasets or header virtual datasets
|
||||
if (rank == 1 || rank == 2) {
|
||||
count = std::vector<hsize_t>(rank, 1); // slice 1 value
|
||||
offset = std::vector<hsize_t>(rank, 0);
|
||||
memspace = std::make_unique<H5::DataSpace>(H5S_SCALAR);
|
||||
} else if (rank >= 3) {
|
||||
// data dataset (frame x height x width)
|
||||
count = {1, dims[1], dims[2]};
|
||||
offset = {0, 0, 0};
|
||||
hsize_t dims_image[2] = {dims[1], dims[2]};
|
||||
memspace = std::make_unique<H5::DataSpace>(2, dims_image);
|
||||
} else {
|
||||
throw std::runtime_error(
|
||||
LOCATION + "Invalid rank for dataset: " + std::to_string(rank));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Fn>
|
||||
void read_hdf5_header_fields(DetectorHeader *header, Fn &&fn_read_field) {
|
||||
fn_read_field(0, reinterpret_cast<std::byte *>(&(header->frameNumber)));
|
||||
fn_read_field(1, reinterpret_cast<std::byte *>(&(header->expLength)));
|
||||
fn_read_field(2, reinterpret_cast<std::byte *>(&(header->packetNumber)));
|
||||
fn_read_field(3, reinterpret_cast<std::byte *>(&(header->bunchId)));
|
||||
fn_read_field(4, reinterpret_cast<std::byte *>(&(header->timestamp)));
|
||||
fn_read_field(5, reinterpret_cast<std::byte *>(&(header->modId)));
|
||||
fn_read_field(6, reinterpret_cast<std::byte *>(&(header->row)));
|
||||
fn_read_field(7, reinterpret_cast<std::byte *>(&(header->column)));
|
||||
fn_read_field(8, reinterpret_cast<std::byte *>(&(header->reserved)));
|
||||
fn_read_field(9, reinterpret_cast<std::byte *>(&(header->debug)));
|
||||
fn_read_field(10, reinterpret_cast<std::byte *>(&(header->roundRNumber)));
|
||||
fn_read_field(11, reinterpret_cast<std::byte *>(&(header->detType)));
|
||||
fn_read_field(12, reinterpret_cast<std::byte *>(&(header->version)));
|
||||
fn_read_field(13, reinterpret_cast<std::byte *>(&(header->packetMask)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Class to read .h5 files. The class will parse the master file
|
||||
* to find the correct geometry for the frames.
|
||||
* @note A more generic interface is available in the aare::File class.
|
||||
* Consider using that unless you need hdf5 file specific functionality.
|
||||
*/
|
||||
class Hdf5File : public FileInterface {
|
||||
Hdf5MasterFile m_master;
|
||||
|
||||
size_t m_current_frame{};
|
||||
size_t m_total_frames{};
|
||||
size_t m_rows{};
|
||||
size_t m_cols{};
|
||||
|
||||
static const std::string metadata_group_name;
|
||||
static const std::vector<std::string> header_dataset_names;
|
||||
std::unique_ptr<H5Handles> m_data_dataset{nullptr};
|
||||
std::vector<std::unique_ptr<H5Handles>> m_header_datasets{};
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Hdf5File constructor
|
||||
* @param fname path to the master file (.json)
|
||||
* @param mode file mode (only "r" is supported at the moment)
|
||||
|
||||
*/
|
||||
Hdf5File(const std::filesystem::path &fname, const std::string &mode = "r");
|
||||
virtual ~Hdf5File() override;
|
||||
|
||||
Frame read_frame() override;
|
||||
Frame read_frame(size_t frame_number) override;
|
||||
std::vector<Frame> read_n(size_t n_frames) override;
|
||||
void read_into(std::byte *image_buf) override;
|
||||
void read_into(std::byte *image_buf, size_t n_frames) override;
|
||||
|
||||
// TODO! do we need to adapt the API?
|
||||
void read_into(std::byte *image_buf, DetectorHeader *header);
|
||||
void read_into(std::byte *image_buf, size_t n_frames,
|
||||
DetectorHeader *header);
|
||||
|
||||
size_t frame_number(size_t frame_index) override;
|
||||
size_t bytes_per_frame() override;
|
||||
size_t pixels_per_frame() override;
|
||||
size_t bytes_per_pixel() const;
|
||||
void seek(size_t frame_index) override;
|
||||
size_t tell() override;
|
||||
size_t total_frames() const override;
|
||||
size_t rows() const override;
|
||||
size_t cols() const override;
|
||||
size_t bitdepth() const override;
|
||||
xy geometry();
|
||||
size_t n_modules() const;
|
||||
Hdf5MasterFile master() const;
|
||||
|
||||
DetectorType detector_type() const override;
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief get the frame at the given frame index
|
||||
* @param frame_number frame number to read
|
||||
* @return Frame
|
||||
*/
|
||||
Frame get_frame(size_t frame_index);
|
||||
|
||||
/**
|
||||
* @brief read the frame at the given frame index into the image buffer
|
||||
* @param frame_number frame number to read
|
||||
* @param n_frames number of frames to read (default is 1)
|
||||
* @param image_buf buffer to store the frame
|
||||
*/
|
||||
void get_frame_into(size_t frame_index, std::byte *frame_buffer,
|
||||
size_t n_frames = 1, DetectorHeader *header = nullptr);
|
||||
|
||||
/**
|
||||
* @brief read the frame at the given frame index into the image buffer
|
||||
* @param frame_index frame number to read
|
||||
* @param n_frames number of frames to read (default is 1)
|
||||
* @param frame_buffer buffer to store the frame
|
||||
*/
|
||||
void get_data_into(size_t frame_index, std::byte *frame_buffer,
|
||||
size_t n_frames = 1);
|
||||
|
||||
/**
|
||||
* @brief read the header at the given frame index into the header buffer
|
||||
* @param frame_index frame number to read
|
||||
* @param part_index part index to read (for virtual datasets)
|
||||
* @param header buffer to store the header
|
||||
*/
|
||||
void get_header_into(size_t frame_index, int part_index,
|
||||
DetectorHeader *header);
|
||||
|
||||
/**
|
||||
* @brief read the header of the file
|
||||
* @param fname path to the data subfile
|
||||
* @return DetectorHeader
|
||||
*/
|
||||
static DetectorHeader read_header(const std::filesystem::path &fname);
|
||||
|
||||
void open_data_file();
|
||||
void open_header_files();
|
||||
};
|
||||
|
||||
} // namespace aare
|
135
include/aare/Hdf5MasterFile.hpp
Normal file
135
include/aare/Hdf5MasterFile.hpp
Normal file
@ -0,0 +1,135 @@
|
||||
#pragma once
|
||||
#include "aare/defs.hpp"
|
||||
#include "aare/scan_parameters.hpp"
|
||||
|
||||
#include "H5Cpp.h"
|
||||
#include <filesystem>
|
||||
#include <fmt/format.h>
|
||||
#include <fstream>
|
||||
#include <optional>
|
||||
|
||||
namespace aare {
|
||||
|
||||
using ns = std::chrono::nanoseconds;
|
||||
|
||||
/**
|
||||
* @brief Class for parsing a master file either in our .json format or the old
|
||||
* .Hdf5 format
|
||||
*/
|
||||
class Hdf5MasterFile {
|
||||
std::filesystem::path m_file_name{};
|
||||
std::string m_version;
|
||||
DetectorType m_type;
|
||||
TimingMode m_timing_mode;
|
||||
xy m_geometry{};
|
||||
int m_image_size_in_bytes{};
|
||||
int m_pixels_y{};
|
||||
int m_pixels_x{};
|
||||
int m_max_frames_per_file{};
|
||||
FrameDiscardPolicy m_frame_discard_policy{};
|
||||
int m_frame_padding{};
|
||||
std::optional<ScanParameters> m_scan_parameters{};
|
||||
size_t m_total_frames_expected{};
|
||||
std::optional<ns> m_exptime{};
|
||||
std::optional<ns> m_period{};
|
||||
std::optional<BurstMode> m_burst_mode{};
|
||||
std::optional<int> m_number_of_udp_interfaces{};
|
||||
int m_bitdepth{};
|
||||
std::optional<bool> m_ten_giga{};
|
||||
std::optional<int> m_threshold_energy{};
|
||||
std::optional<std::vector<int>> m_threshold_energy_all{};
|
||||
std::optional<ns> m_subexptime{};
|
||||
std::optional<ns> m_subperiod{};
|
||||
std::optional<bool> m_quad{};
|
||||
std::optional<int> m_number_of_rows{};
|
||||
std::optional<std::vector<size_t>> m_rate_corrections{};
|
||||
std::optional<uint32_t> m_adc_mask{};
|
||||
bool m_analog_flag{};
|
||||
std::optional<int> m_analog_samples{};
|
||||
bool m_digital_flag{};
|
||||
std::optional<int> m_digital_samples{};
|
||||
std::optional<int> m_dbit_offset{};
|
||||
std::optional<size_t> m_dbit_list{};
|
||||
std::optional<int> m_transceiver_mask{};
|
||||
bool m_transceiver_flag{};
|
||||
std::optional<int> m_transceiver_samples{};
|
||||
// g1 roi - will not be implemented?
|
||||
std::optional<ROI> m_roi{};
|
||||
std::optional<int> m_counter_mask{};
|
||||
std::optional<std::vector<ns>> m_exptime_array{};
|
||||
std::optional<std::vector<ns>> m_gate_delay_array{};
|
||||
std::optional<int> m_gates{};
|
||||
std::optional<std::map<std::string, std::string>>
|
||||
m_additional_json_header{};
|
||||
size_t m_frames_in_file{};
|
||||
|
||||
// TODO! should these be bool?
|
||||
|
||||
public:
|
||||
Hdf5MasterFile(const std::filesystem::path &fpath);
|
||||
|
||||
std::filesystem::path file_name() const;
|
||||
|
||||
const std::string &version() const; //!< For example "7.2"
|
||||
const DetectorType &detector_type() const;
|
||||
const TimingMode &timing_mode() const;
|
||||
xy geometry() const;
|
||||
int image_size_in_bytes() const;
|
||||
int pixels_y() const;
|
||||
int pixels_x() const;
|
||||
int max_frames_per_file() const;
|
||||
const FrameDiscardPolicy &frame_discard_policy() const;
|
||||
int frame_padding() const;
|
||||
std::optional<ScanParameters> scan_parameters() const;
|
||||
size_t total_frames_expected() const;
|
||||
std::optional<ns> exptime() const;
|
||||
std::optional<ns> period() const;
|
||||
std::optional<BurstMode> burst_mode() const;
|
||||
std::optional<int> number_of_udp_interfaces() const;
|
||||
int bitdepth() const;
|
||||
std::optional<bool> ten_giga() const;
|
||||
std::optional<int> threshold_energy() const;
|
||||
std::optional<std::vector<int>> threshold_energy_all() const;
|
||||
std::optional<ns> subexptime() const;
|
||||
std::optional<ns> subperiod() const;
|
||||
std::optional<bool> quad() const;
|
||||
std::optional<int> number_of_rows() const;
|
||||
std::optional<std::vector<size_t>> rate_corrections() const;
|
||||
std::optional<uint32_t> adc_mask() const;
|
||||
bool analog_flag() const;
|
||||
std::optional<int> analog_samples() const;
|
||||
bool digital_flag() const;
|
||||
std::optional<int> digital_samples() const;
|
||||
std::optional<int> dbit_offset() const;
|
||||
std::optional<size_t> dbit_list() const;
|
||||
std::optional<int> transceiver_mask() const;
|
||||
bool transceiver_flag() const;
|
||||
std::optional<int> transceiver_samples() const;
|
||||
// g1 roi - will not be implemented?
|
||||
std::optional<ROI> roi() const;
|
||||
std::optional<int> counter_mask() const;
|
||||
std::optional<std::vector<ns>> exptime_array() const;
|
||||
std::optional<std::vector<ns>> gate_delay_array() const;
|
||||
std::optional<int> gates() const;
|
||||
std::optional<std::map<std::string, std::string>>
|
||||
additional_json_header() const;
|
||||
size_t frames_in_file() const;
|
||||
size_t n_modules() const;
|
||||
|
||||
private:
|
||||
static const std::string metadata_group_name;
|
||||
void parse_acquisition_metadata(const std::filesystem::path &fpath);
|
||||
|
||||
template <typename T>
|
||||
T h5_read_scalar_dataset(const H5::DataSet &dataset,
|
||||
const H5::DataType &data_type);
|
||||
|
||||
template <typename T>
|
||||
T h5_get_scalar_dataset(const H5::H5File &file,
|
||||
const std::string &dataset_name);
|
||||
};
|
||||
|
||||
template <>
|
||||
std::string Hdf5MasterFile::h5_read_scalar_dataset<std::string>(
|
||||
const H5::DataSet &dataset, const H5::DataType &data_type);
|
||||
} // namespace aare
|
130
include/aare/Interpolator.hpp
Normal file
130
include/aare/Interpolator.hpp
Normal file
@ -0,0 +1,130 @@
|
||||
#pragma once
|
||||
|
||||
#include "aare/CalculateEta.hpp"
|
||||
#include "aare/Cluster.hpp"
|
||||
#include "aare/ClusterFile.hpp" //Cluster_3x3
|
||||
#include "aare/ClusterVector.hpp"
|
||||
#include "aare/NDArray.hpp"
|
||||
#include "aare/NDView.hpp"
|
||||
#include "aare/algorithm.hpp"
|
||||
|
||||
namespace aare {
|
||||
|
||||
struct Photon {
|
||||
double x;
|
||||
double y;
|
||||
double energy;
|
||||
};
|
||||
|
||||
class Interpolator {
|
||||
NDArray<double, 3> m_ietax;
|
||||
NDArray<double, 3> m_ietay;
|
||||
|
||||
NDArray<double, 1> m_etabinsx;
|
||||
NDArray<double, 1> m_etabinsy;
|
||||
NDArray<double, 1> m_energy_bins;
|
||||
|
||||
public:
|
||||
Interpolator(NDView<double, 3> etacube, NDView<double, 1> xbins,
|
||||
NDView<double, 1> ybins, NDView<double, 1> ebins);
|
||||
NDArray<double, 3> get_ietax() { return m_ietax; }
|
||||
NDArray<double, 3> get_ietay() { return m_ietay; }
|
||||
|
||||
template <typename ClusterType,
|
||||
typename Eanble = std::enable_if_t<is_cluster_v<ClusterType>>>
|
||||
std::vector<Photon> interpolate(const ClusterVector<ClusterType> &clusters);
|
||||
};
|
||||
|
||||
// TODO: generalize to support any clustertype!!! otherwise add std::enable_if_t
|
||||
// to only take Cluster2x2 and Cluster3x3
|
||||
template <typename ClusterType, typename Enable>
|
||||
std::vector<Photon>
|
||||
Interpolator::interpolate(const ClusterVector<ClusterType> &clusters) {
|
||||
std::vector<Photon> photons;
|
||||
photons.reserve(clusters.size());
|
||||
|
||||
if (clusters.cluster_size_x() == 3 || clusters.cluster_size_y() == 3) {
|
||||
for (const ClusterType &cluster : clusters) {
|
||||
|
||||
auto eta = calculate_eta2(cluster);
|
||||
|
||||
Photon photon;
|
||||
photon.x = cluster.x;
|
||||
photon.y = cluster.y;
|
||||
photon.energy = static_cast<decltype(photon.energy)>(eta.sum);
|
||||
|
||||
// auto ie = nearest_index(m_energy_bins, photon.energy)-1;
|
||||
// auto ix = nearest_index(m_etabinsx, eta.x)-1;
|
||||
// auto iy = nearest_index(m_etabinsy, eta.y)-1;
|
||||
// Finding the index of the last element that is smaller
|
||||
// should work fine as long as we have many bins
|
||||
auto ie = last_smaller(m_energy_bins, photon.energy);
|
||||
auto ix = last_smaller(m_etabinsx, eta.x);
|
||||
auto iy = last_smaller(m_etabinsy, eta.y);
|
||||
|
||||
// fmt::print("ex: {}, ix: {}, iy: {}\n", ie, ix, iy);
|
||||
|
||||
double dX, dY;
|
||||
// cBottomLeft = 0,
|
||||
// cBottomRight = 1,
|
||||
// cTopLeft = 2,
|
||||
// cTopRight = 3
|
||||
switch (static_cast<corner>(eta.c)) {
|
||||
case corner::cTopLeft:
|
||||
dX = -1.;
|
||||
dY = 0;
|
||||
break;
|
||||
case corner::cTopRight:;
|
||||
dX = 0;
|
||||
dY = 0;
|
||||
break;
|
||||
case corner::cBottomLeft:
|
||||
dX = -1.;
|
||||
dY = -1.;
|
||||
break;
|
||||
case corner::cBottomRight:
|
||||
dX = 0.;
|
||||
dY = -1.;
|
||||
break;
|
||||
}
|
||||
photon.x += m_ietax(ix, iy, ie) * 2 + dX;
|
||||
photon.y += m_ietay(ix, iy, ie) * 2 + dY;
|
||||
photons.push_back(photon);
|
||||
}
|
||||
} else if (clusters.cluster_size_x() == 2 ||
|
||||
clusters.cluster_size_y() == 2) {
|
||||
for (const ClusterType &cluster : clusters) {
|
||||
auto eta = calculate_eta2(cluster);
|
||||
|
||||
Photon photon;
|
||||
photon.x = cluster.x;
|
||||
photon.y = cluster.y;
|
||||
photon.energy = static_cast<decltype(photon.energy)>(eta.sum);
|
||||
|
||||
// Now do some actual interpolation.
|
||||
// Find which energy bin the cluster is in
|
||||
// auto ie = nearest_index(m_energy_bins, photon.energy)-1;
|
||||
// auto ix = nearest_index(m_etabinsx, eta.x)-1;
|
||||
// auto iy = nearest_index(m_etabinsy, eta.y)-1;
|
||||
// Finding the index of the last element that is smaller
|
||||
// should work fine as long as we have many bins
|
||||
auto ie = last_smaller(m_energy_bins, photon.energy);
|
||||
auto ix = last_smaller(m_etabinsx, eta.x);
|
||||
auto iy = last_smaller(m_etabinsy, eta.y);
|
||||
|
||||
photon.x += m_ietax(ix, iy, ie) *
|
||||
2; // eta goes between 0 and 1 but we could move the hit
|
||||
// anywhere in the 2x2
|
||||
photon.y += m_ietay(ix, iy, ie) * 2;
|
||||
photons.push_back(photon);
|
||||
}
|
||||
|
||||
} else {
|
||||
throw std::runtime_error(
|
||||
"Only 3x3 and 2x2 clusters are supported for interpolation");
|
||||
}
|
||||
|
||||
return photons;
|
||||
}
|
||||
|
||||
} // namespace aare
|
115
include/aare/JungfrauDataFile.hpp
Normal file
115
include/aare/JungfrauDataFile.hpp
Normal file
@ -0,0 +1,115 @@
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
#include <filesystem>
|
||||
#include <vector>
|
||||
|
||||
#include "aare/FileInterface.hpp"
|
||||
#include "aare/FilePtr.hpp"
|
||||
#include "aare/NDArray.hpp"
|
||||
#include "aare/defs.hpp"
|
||||
namespace aare {
|
||||
|
||||
struct JungfrauDataHeader {
|
||||
uint64_t framenum;
|
||||
uint64_t bunchid;
|
||||
};
|
||||
|
||||
class JungfrauDataFile : public FileInterface {
|
||||
|
||||
size_t m_rows{}; //!< number of rows in the image, from find_frame_size();
|
||||
size_t
|
||||
m_cols{}; //!< number of columns in the image, from find_frame_size();
|
||||
size_t m_bytes_per_frame{}; //!< number of bytes per frame excluding header
|
||||
size_t m_total_frames{}; //!< total number of frames in the series of files
|
||||
size_t m_offset{}; //!< file index of the first file, allow starting at non
|
||||
//!< zero file
|
||||
size_t m_current_file_index{}; //!< The index of the open file
|
||||
size_t m_current_frame_index{}; //!< The index of the current frame (with
|
||||
//!< reference to all files)
|
||||
|
||||
std::vector<size_t>
|
||||
m_last_frame_in_file{}; //!< Used for seeking to the correct file
|
||||
std::filesystem::path m_path; //!< path to the files
|
||||
std::string m_base_name; //!< base name used for formatting file names
|
||||
|
||||
FilePtr m_fp; //!< RAII wrapper for a FILE*
|
||||
|
||||
using pixel_type = uint16_t;
|
||||
static constexpr size_t header_size = sizeof(JungfrauDataHeader);
|
||||
static constexpr size_t n_digits_in_file_index =
|
||||
6; //!< to format file names
|
||||
|
||||
public:
|
||||
JungfrauDataFile(const std::filesystem::path &fname);
|
||||
|
||||
std::string base_name()
|
||||
const; //!< get the base name of the file (without path and extension)
|
||||
size_t bytes_per_frame() override;
|
||||
size_t pixels_per_frame() override;
|
||||
size_t bytes_per_pixel() const;
|
||||
size_t bitdepth() const override;
|
||||
void seek(size_t frame_index)
|
||||
override; //!< seek to the given frame index (note not byte offset)
|
||||
size_t tell() override; //!< get the frame index of the file pointer
|
||||
size_t total_frames() const override;
|
||||
size_t rows() const override;
|
||||
size_t cols() const override;
|
||||
std::array<ssize_t, 2> shape() const;
|
||||
size_t n_files() const; //!< get the number of files in the series.
|
||||
|
||||
// Extra functions needed for FileInterface
|
||||
Frame read_frame() override;
|
||||
Frame read_frame(size_t frame_number) override;
|
||||
std::vector<Frame> 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;
|
||||
size_t frame_number(size_t frame_index) override;
|
||||
DetectorType detector_type() const override;
|
||||
|
||||
/**
|
||||
* @brief Read a single frame from the file into the given buffer.
|
||||
* @param image_buf buffer to read the frame into. (Note the caller is
|
||||
* responsible for allocating the buffer)
|
||||
* @param header pointer to a JungfrauDataHeader or nullptr to skip header)
|
||||
*/
|
||||
void read_into(std::byte *image_buf, JungfrauDataHeader *header = nullptr);
|
||||
|
||||
/**
|
||||
* @brief Read a multiple frames from the file into the given buffer.
|
||||
* @param image_buf buffer to read the frame into. (Note the caller is
|
||||
* responsible for allocating the buffer)
|
||||
* @param n_frames number of frames to read
|
||||
* @param header pointer to a JungfrauDataHeader or nullptr to skip header)
|
||||
*/
|
||||
void read_into(std::byte *image_buf, size_t n_frames,
|
||||
JungfrauDataHeader *header = nullptr);
|
||||
|
||||
/**
|
||||
* @brief Read a single frame from the file into the given NDArray
|
||||
* @param image NDArray to read the frame into.
|
||||
*/
|
||||
void read_into(NDArray<uint16_t> *image,
|
||||
JungfrauDataHeader *header = nullptr);
|
||||
|
||||
JungfrauDataHeader read_header();
|
||||
std::filesystem::path current_file() const {
|
||||
return fpath(m_current_file_index + m_offset);
|
||||
}
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Find the size of the frame in the file. (256x256, 256x1024,
|
||||
* 512x1024)
|
||||
* @param fname path to the file
|
||||
* @throws std::runtime_error if the file is empty or the size cannot be
|
||||
* determined
|
||||
*/
|
||||
void find_frame_size(const std::filesystem::path &fname);
|
||||
|
||||
void parse_fname(const std::filesystem::path &fname);
|
||||
void scan_files();
|
||||
void open_file(size_t file_index);
|
||||
std::filesystem::path fpath(size_t frame_index) const;
|
||||
};
|
||||
|
||||
} // namespace aare
|
@ -21,11 +21,10 @@ TODO! Add expression templates for operators
|
||||
|
||||
namespace aare {
|
||||
|
||||
|
||||
template <typename T, int64_t Ndim = 2>
|
||||
template <typename T, ssize_t Ndim = 2>
|
||||
class NDArray : public ArrayExpr<NDArray<T, Ndim>, Ndim> {
|
||||
std::array<int64_t, Ndim> shape_;
|
||||
std::array<int64_t, Ndim> strides_;
|
||||
std::array<ssize_t, Ndim> shape_;
|
||||
std::array<ssize_t, Ndim> strides_;
|
||||
size_t size_{};
|
||||
T *data_;
|
||||
|
||||
@ -34,7 +33,7 @@ class NDArray : public ArrayExpr<NDArray<T, Ndim>, Ndim> {
|
||||
* @brief Default constructor. Will construct an empty NDArray.
|
||||
*
|
||||
*/
|
||||
NDArray() : shape_(), strides_(c_strides<Ndim>(shape_)), data_(nullptr) {};
|
||||
NDArray() : shape_(), strides_(c_strides<Ndim>(shape_)), data_(nullptr){};
|
||||
|
||||
/**
|
||||
* @brief Construct a new NDArray object with a given shape.
|
||||
@ -42,20 +41,19 @@ class NDArray : public ArrayExpr<NDArray<T, Ndim>, Ndim> {
|
||||
*
|
||||
* @param shape shape of the new NDArray
|
||||
*/
|
||||
explicit NDArray(std::array<int64_t, Ndim> shape)
|
||||
explicit NDArray(std::array<ssize_t, Ndim> shape)
|
||||
: shape_(shape), strides_(c_strides<Ndim>(shape_)),
|
||||
size_(std::accumulate(shape_.begin(), shape_.end(), 1,
|
||||
std::multiplies<>())),
|
||||
data_(new T[size_]) {}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Construct a new NDArray object with a shape and value.
|
||||
*
|
||||
* @param shape shape of the new array
|
||||
* @param value value to initialize the array with
|
||||
*/
|
||||
NDArray(std::array<int64_t, Ndim> shape, T value) : NDArray(shape) {
|
||||
NDArray(std::array<ssize_t, Ndim> shape, T value) : NDArray(shape) {
|
||||
this->operator=(value);
|
||||
}
|
||||
|
||||
@ -69,12 +67,16 @@ class NDArray : public ArrayExpr<NDArray<T, Ndim>, Ndim> {
|
||||
std::copy(v.begin(), v.end(), begin());
|
||||
}
|
||||
|
||||
template <size_t Size>
|
||||
NDArray(const std::array<T, Size> &arr) : NDArray<T, 1>({Size}) {
|
||||
std::copy(arr.begin(), arr.end(), begin());
|
||||
}
|
||||
|
||||
// Move constructor
|
||||
NDArray(NDArray &&other) noexcept
|
||||
: shape_(other.shape_), strides_(c_strides<Ndim>(shape_)),
|
||||
size_(other.size_), data_(other.data_) {
|
||||
other.reset(); // TODO! is this necessary?
|
||||
|
||||
}
|
||||
|
||||
// Copy constructor
|
||||
@ -97,6 +99,9 @@ class NDArray : public ArrayExpr<NDArray<T, Ndim>, Ndim> {
|
||||
auto begin() { return data_; }
|
||||
auto end() { return data_ + size_; }
|
||||
|
||||
auto begin() const { return data_; }
|
||||
auto end() const { return data_ + size_; }
|
||||
|
||||
using value_type = T;
|
||||
|
||||
NDArray &operator=(NDArray &&other) noexcept; // Move assign
|
||||
@ -105,6 +110,20 @@ class NDArray : public ArrayExpr<NDArray<T, Ndim>, Ndim> {
|
||||
NDArray &operator-=(const NDArray &other);
|
||||
NDArray &operator*=(const NDArray &other);
|
||||
|
||||
// Write directly to the data array, or create a new one
|
||||
template <size_t Size>
|
||||
NDArray<T, 1> &operator=(const std::array<T, Size> &other) {
|
||||
if (Size != size_) {
|
||||
delete[] data_;
|
||||
size_ = Size;
|
||||
data_ = new T[size_];
|
||||
}
|
||||
for (size_t i = 0; i < Size; ++i) {
|
||||
data_[i] = other[i];
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
// NDArray& operator/=(const NDArray& other);
|
||||
|
||||
template <typename V> NDArray &operator/=(const NDArray<V, Ndim> &other) {
|
||||
@ -159,22 +178,22 @@ class NDArray : public ArrayExpr<NDArray<T, Ndim>, Ndim> {
|
||||
}
|
||||
|
||||
// TODO! is int the right type for index?
|
||||
T &operator()(int64_t i) { return data_[i]; }
|
||||
const T &operator()(int64_t i) const { return data_[i]; }
|
||||
T &operator()(ssize_t i) { return data_[i]; }
|
||||
const T &operator()(ssize_t i) const { return data_[i]; }
|
||||
|
||||
T &operator[](int64_t i) { return data_[i]; }
|
||||
const T &operator[](int64_t i) const { return data_[i]; }
|
||||
T &operator[](ssize_t i) { return data_[i]; }
|
||||
const T &operator[](ssize_t i) const { return data_[i]; }
|
||||
|
||||
T *data() { return data_; }
|
||||
std::byte *buffer() { return reinterpret_cast<std::byte *>(data_); }
|
||||
size_t size() const { return size_; }
|
||||
ssize_t size() const { return static_cast<ssize_t>(size_); }
|
||||
size_t total_bytes() const { return size_ * sizeof(T); }
|
||||
std::array<int64_t, Ndim> shape() const noexcept { return shape_; }
|
||||
int64_t shape(int64_t i) const noexcept { return shape_[i]; }
|
||||
std::array<int64_t, Ndim> strides() const noexcept { return strides_; }
|
||||
std::array<ssize_t, Ndim> shape() const noexcept { return shape_; }
|
||||
ssize_t shape(ssize_t i) const noexcept { return shape_[i]; }
|
||||
std::array<ssize_t, Ndim> strides() const noexcept { return strides_; }
|
||||
size_t bitdepth() const noexcept { return sizeof(T) * 8; }
|
||||
|
||||
std::array<int64_t, Ndim> byte_strides() const noexcept {
|
||||
std::array<ssize_t, Ndim> byte_strides() const noexcept {
|
||||
auto byte_strides = strides_;
|
||||
for (auto &val : byte_strides)
|
||||
val *= sizeof(T);
|
||||
@ -201,7 +220,7 @@ class NDArray : public ArrayExpr<NDArray<T, Ndim>, Ndim> {
|
||||
};
|
||||
|
||||
// Move assign
|
||||
template <typename T, int64_t Ndim>
|
||||
template <typename T, ssize_t Ndim>
|
||||
NDArray<T, Ndim> &
|
||||
NDArray<T, Ndim>::operator=(NDArray<T, Ndim> &&other) noexcept {
|
||||
if (this != &other) {
|
||||
@ -215,7 +234,7 @@ NDArray<T, Ndim>::operator=(NDArray<T, Ndim> &&other) noexcept {
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename T, int64_t Ndim>
|
||||
template <typename T, ssize_t Ndim>
|
||||
NDArray<T, Ndim> &NDArray<T, Ndim>::operator+=(const NDArray<T, Ndim> &other) {
|
||||
// check shape
|
||||
if (shape_ == other.shape_) {
|
||||
@ -227,7 +246,7 @@ NDArray<T, Ndim> &NDArray<T, Ndim>::operator+=(const NDArray<T, Ndim> &other) {
|
||||
throw(std::runtime_error("Shape of ImageDatas must match"));
|
||||
}
|
||||
|
||||
template <typename T, int64_t Ndim>
|
||||
template <typename T, ssize_t Ndim>
|
||||
NDArray<T, Ndim> &NDArray<T, Ndim>::operator-=(const NDArray<T, Ndim> &other) {
|
||||
// check shape
|
||||
if (shape_ == other.shape_) {
|
||||
@ -239,7 +258,7 @@ NDArray<T, Ndim> &NDArray<T, Ndim>::operator-=(const NDArray<T, Ndim> &other) {
|
||||
throw(std::runtime_error("Shape of ImageDatas must match"));
|
||||
}
|
||||
|
||||
template <typename T, int64_t Ndim>
|
||||
template <typename T, ssize_t Ndim>
|
||||
NDArray<T, Ndim> &NDArray<T, Ndim>::operator*=(const NDArray<T, Ndim> &other) {
|
||||
// check shape
|
||||
if (shape_ == other.shape_) {
|
||||
@ -251,14 +270,14 @@ NDArray<T, Ndim> &NDArray<T, Ndim>::operator*=(const NDArray<T, Ndim> &other) {
|
||||
throw(std::runtime_error("Shape of ImageDatas must match"));
|
||||
}
|
||||
|
||||
template <typename T, int64_t Ndim>
|
||||
template <typename T, ssize_t Ndim>
|
||||
NDArray<T, Ndim> &NDArray<T, Ndim>::operator&=(const T &mask) {
|
||||
for (auto it = begin(); it != end(); ++it)
|
||||
*it &= mask;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename T, int64_t Ndim>
|
||||
template <typename T, ssize_t Ndim>
|
||||
NDArray<bool, Ndim> NDArray<T, Ndim>::operator>(const NDArray &other) {
|
||||
if (shape_ == other.shape_) {
|
||||
NDArray<bool, Ndim> result{shape_};
|
||||
@ -270,7 +289,7 @@ NDArray<bool, Ndim> NDArray<T, Ndim>::operator>(const NDArray &other) {
|
||||
throw(std::runtime_error("Shape of ImageDatas must match"));
|
||||
}
|
||||
|
||||
template <typename T, int64_t Ndim>
|
||||
template <typename T, ssize_t Ndim>
|
||||
NDArray<T, Ndim> &NDArray<T, Ndim>::operator=(const NDArray<T, Ndim> &other) {
|
||||
if (this != &other) {
|
||||
delete[] data_;
|
||||
@ -283,7 +302,7 @@ NDArray<T, Ndim> &NDArray<T, Ndim>::operator=(const NDArray<T, Ndim> &other) {
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename T, int64_t Ndim>
|
||||
template <typename T, ssize_t Ndim>
|
||||
bool NDArray<T, Ndim>::operator==(const NDArray<T, Ndim> &other) const {
|
||||
if (shape_ != other.shape_)
|
||||
return false;
|
||||
@ -295,80 +314,80 @@ bool NDArray<T, Ndim>::operator==(const NDArray<T, Ndim> &other) const {
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T, int64_t Ndim>
|
||||
template <typename T, ssize_t Ndim>
|
||||
bool NDArray<T, Ndim>::operator!=(const NDArray<T, Ndim> &other) const {
|
||||
return !((*this) == other);
|
||||
}
|
||||
template <typename T, int64_t Ndim>
|
||||
template <typename T, ssize_t Ndim>
|
||||
NDArray<T, Ndim> &NDArray<T, Ndim>::operator++() {
|
||||
for (uint32_t i = 0; i < size_; ++i)
|
||||
data_[i] += 1;
|
||||
return *this;
|
||||
}
|
||||
template <typename T, int64_t Ndim>
|
||||
template <typename T, ssize_t Ndim>
|
||||
NDArray<T, Ndim> &NDArray<T, Ndim>::operator=(const T &value) {
|
||||
std::fill_n(data_, size_, value);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename T, int64_t Ndim>
|
||||
template <typename T, ssize_t Ndim>
|
||||
NDArray<T, Ndim> &NDArray<T, Ndim>::operator+=(const T &value) {
|
||||
for (uint32_t i = 0; i < size_; ++i)
|
||||
data_[i] += value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename T, int64_t Ndim>
|
||||
template <typename T, ssize_t Ndim>
|
||||
NDArray<T, Ndim> NDArray<T, Ndim>::operator+(const T &value) {
|
||||
NDArray result = *this;
|
||||
result += value;
|
||||
return result;
|
||||
}
|
||||
template <typename T, int64_t Ndim>
|
||||
template <typename T, ssize_t Ndim>
|
||||
NDArray<T, Ndim> &NDArray<T, Ndim>::operator-=(const T &value) {
|
||||
for (uint32_t i = 0; i < size_; ++i)
|
||||
data_[i] -= value;
|
||||
return *this;
|
||||
}
|
||||
template <typename T, int64_t Ndim>
|
||||
template <typename T, ssize_t Ndim>
|
||||
NDArray<T, Ndim> NDArray<T, Ndim>::operator-(const T &value) {
|
||||
NDArray result = *this;
|
||||
result -= value;
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T, int64_t Ndim>
|
||||
template <typename T, ssize_t Ndim>
|
||||
NDArray<T, Ndim> &NDArray<T, Ndim>::operator/=(const T &value) {
|
||||
for (uint32_t i = 0; i < size_; ++i)
|
||||
data_[i] /= value;
|
||||
return *this;
|
||||
}
|
||||
template <typename T, int64_t Ndim>
|
||||
template <typename T, ssize_t Ndim>
|
||||
NDArray<T, Ndim> NDArray<T, Ndim>::operator/(const T &value) {
|
||||
NDArray result = *this;
|
||||
result /= value;
|
||||
return result;
|
||||
}
|
||||
template <typename T, int64_t Ndim>
|
||||
template <typename T, ssize_t Ndim>
|
||||
NDArray<T, Ndim> &NDArray<T, Ndim>::operator*=(const T &value) {
|
||||
for (uint32_t i = 0; i < size_; ++i)
|
||||
data_[i] *= value;
|
||||
return *this;
|
||||
}
|
||||
template <typename T, int64_t Ndim>
|
||||
template <typename T, ssize_t Ndim>
|
||||
NDArray<T, Ndim> NDArray<T, Ndim>::operator*(const T &value) {
|
||||
NDArray result = *this;
|
||||
result *= value;
|
||||
return result;
|
||||
}
|
||||
template <typename T, int64_t Ndim> void NDArray<T, Ndim>::Print() {
|
||||
if (shape_[0] < 20 && shape_[1] < 20)
|
||||
Print_all();
|
||||
else
|
||||
Print_some();
|
||||
}
|
||||
// template <typename T, ssize_t Ndim> void NDArray<T, Ndim>::Print() {
|
||||
// if (shape_[0] < 20 && shape_[1] < 20)
|
||||
// Print_all();
|
||||
// else
|
||||
// Print_some();
|
||||
// }
|
||||
|
||||
template <typename T, int64_t Ndim>
|
||||
template <typename T, ssize_t Ndim>
|
||||
std::ostream &operator<<(std::ostream &os, const NDArray<T, Ndim> &arr) {
|
||||
for (auto row = 0; row < arr.shape(0); ++row) {
|
||||
for (auto col = 0; col < arr.shape(1); ++col) {
|
||||
@ -380,7 +399,7 @@ std::ostream &operator<<(std::ostream &os, const NDArray<T, Ndim> &arr) {
|
||||
return os;
|
||||
}
|
||||
|
||||
template <typename T, int64_t Ndim> void NDArray<T, Ndim>::Print_all() {
|
||||
template <typename T, ssize_t Ndim> void NDArray<T, Ndim>::Print_all() {
|
||||
for (auto row = 0; row < shape_[0]; ++row) {
|
||||
for (auto col = 0; col < shape_[1]; ++col) {
|
||||
std::cout << std::setw(3);
|
||||
@ -389,7 +408,7 @@ template <typename T, int64_t Ndim> void NDArray<T, Ndim>::Print_all() {
|
||||
std::cout << "\n";
|
||||
}
|
||||
}
|
||||
template <typename T, int64_t Ndim> void NDArray<T, Ndim>::Print_some() {
|
||||
template <typename T, ssize_t Ndim> void NDArray<T, Ndim>::Print_some() {
|
||||
for (auto row = 0; row < 5; ++row) {
|
||||
for (auto col = 0; col < 5; ++col) {
|
||||
std::cout << std::setw(7);
|
||||
@ -399,7 +418,7 @@ template <typename T, int64_t Ndim> void NDArray<T, Ndim>::Print_some() {
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, int64_t Ndim>
|
||||
template <typename T, ssize_t Ndim>
|
||||
void save(NDArray<T, Ndim> &img, std::string &pathname) {
|
||||
std::ofstream f;
|
||||
f.open(pathname, std::ios::binary);
|
||||
@ -407,9 +426,9 @@ void save(NDArray<T, Ndim> &img, std::string &pathname) {
|
||||
f.close();
|
||||
}
|
||||
|
||||
template <typename T, int64_t Ndim>
|
||||
template <typename T, ssize_t Ndim>
|
||||
NDArray<T, Ndim> load(const std::string &pathname,
|
||||
std::array<int64_t, Ndim> shape) {
|
||||
std::array<ssize_t, Ndim> shape) {
|
||||
NDArray<T, Ndim> img{shape};
|
||||
std::ifstream f;
|
||||
f.open(pathname, std::ios::binary);
|
||||
|
@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "aare/ArrayExpr.hpp"
|
||||
#include "aare/defs.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
@ -14,10 +14,11 @@
|
||||
#include <vector>
|
||||
namespace aare {
|
||||
|
||||
template <int64_t Ndim> using Shape = std::array<int64_t, Ndim>;
|
||||
template <ssize_t Ndim> using Shape = std::array<ssize_t, Ndim>;
|
||||
|
||||
// TODO! fix mismatch between signed and unsigned
|
||||
template <int64_t Ndim> Shape<Ndim> make_shape(const std::vector<size_t> &shape) {
|
||||
template <ssize_t Ndim>
|
||||
Shape<Ndim> make_shape(const std::vector<size_t> &shape) {
|
||||
if (shape.size() != Ndim)
|
||||
throw std::runtime_error("Shape size mismatch");
|
||||
Shape<Ndim> arr;
|
||||
@ -25,62 +26,74 @@ template <int64_t Ndim> Shape<Ndim> make_shape(const std::vector<size_t> &shape)
|
||||
return arr;
|
||||
}
|
||||
|
||||
template <int64_t Dim = 0, typename Strides> int64_t element_offset(const Strides & /*unused*/) { return 0; }
|
||||
template <ssize_t Dim = 0, typename Strides>
|
||||
ssize_t element_offset(const Strides & /*unused*/) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
template <int64_t Dim = 0, typename Strides, typename... Ix>
|
||||
int64_t element_offset(const Strides &strides, int64_t i, Ix... index) {
|
||||
template <ssize_t Dim = 0, typename Strides, typename... Ix>
|
||||
ssize_t element_offset(const Strides &strides, ssize_t i, Ix... index) {
|
||||
return i * strides[Dim] + element_offset<Dim + 1>(strides, index...);
|
||||
}
|
||||
|
||||
template <int64_t Ndim> std::array<int64_t, Ndim> c_strides(const std::array<int64_t, Ndim> &shape) {
|
||||
std::array<int64_t, Ndim> strides{};
|
||||
template <ssize_t Ndim>
|
||||
std::array<ssize_t, Ndim> c_strides(const std::array<ssize_t, Ndim> &shape) {
|
||||
std::array<ssize_t, Ndim> strides{};
|
||||
std::fill(strides.begin(), strides.end(), 1);
|
||||
for (int64_t i = Ndim - 1; i > 0; --i) {
|
||||
for (ssize_t i = Ndim - 1; i > 0; --i) {
|
||||
strides[i - 1] = strides[i] * shape[i];
|
||||
}
|
||||
return strides;
|
||||
}
|
||||
|
||||
template <int64_t Ndim> std::array<int64_t, Ndim> make_array(const std::vector<int64_t> &vec) {
|
||||
template <ssize_t Ndim>
|
||||
std::array<ssize_t, Ndim> make_array(const std::vector<ssize_t> &vec) {
|
||||
assert(vec.size() == Ndim);
|
||||
std::array<int64_t, Ndim> arr{};
|
||||
std::array<ssize_t, Ndim> arr{};
|
||||
std::copy_n(vec.begin(), Ndim, arr.begin());
|
||||
return arr;
|
||||
}
|
||||
|
||||
template <typename T, int64_t Ndim = 2> class NDView : public ArrayExpr<NDView<T, Ndim>, Ndim> {
|
||||
template <typename T, ssize_t Ndim = 2>
|
||||
class NDView : public ArrayExpr<NDView<T, Ndim>, Ndim> {
|
||||
public:
|
||||
NDView() = default;
|
||||
~NDView() = default;
|
||||
NDView(const NDView &) = default;
|
||||
NDView(NDView &&) = default;
|
||||
|
||||
NDView(T *buffer, std::array<int64_t, Ndim> shape)
|
||||
NDView(T *buffer, std::array<ssize_t, Ndim> shape)
|
||||
: buffer_(buffer), strides_(c_strides<Ndim>(shape)), shape_(shape),
|
||||
size_(std::accumulate(std::begin(shape), std::end(shape), 1, std::multiplies<>())) {}
|
||||
size_(std::accumulate(std::begin(shape), std::end(shape), 1,
|
||||
std::multiplies<>())) {}
|
||||
|
||||
// NDView(T *buffer, const std::vector<int64_t> &shape)
|
||||
// : buffer_(buffer), strides_(c_strides<Ndim>(make_array<Ndim>(shape))), shape_(make_array<Ndim>(shape)),
|
||||
// size_(std::accumulate(std::begin(shape), std::end(shape), 1, std::multiplies<>())) {}
|
||||
// NDView(T *buffer, const std::vector<ssize_t> &shape)
|
||||
// : buffer_(buffer),
|
||||
// strides_(c_strides<Ndim>(make_array<Ndim>(shape))),
|
||||
// shape_(make_array<Ndim>(shape)),
|
||||
// size_(std::accumulate(std::begin(shape), std::end(shape), 1,
|
||||
// std::multiplies<>())) {}
|
||||
|
||||
template <typename... Ix> std::enable_if_t<sizeof...(Ix) == Ndim, T &> operator()(Ix... index) {
|
||||
template <typename... Ix>
|
||||
std::enable_if_t<sizeof...(Ix) == Ndim, T &> operator()(Ix... index) {
|
||||
return buffer_[element_offset(strides_, index...)];
|
||||
}
|
||||
|
||||
template <typename... Ix> std::enable_if_t<sizeof...(Ix) == Ndim, T &> operator()(Ix... index) const {
|
||||
template <typename... Ix>
|
||||
std::enable_if_t<sizeof...(Ix) == Ndim, T &> operator()(Ix... index) const {
|
||||
return buffer_[element_offset(strides_, index...)];
|
||||
}
|
||||
|
||||
size_t size() const { return size_; }
|
||||
ssize_t size() const { return static_cast<ssize_t>(size_); }
|
||||
size_t total_bytes() const { return size_ * sizeof(T); }
|
||||
std::array<int64_t, Ndim> strides() const noexcept { return strides_; }
|
||||
std::array<ssize_t, Ndim> strides() const noexcept { return strides_; }
|
||||
|
||||
T *begin() { return buffer_; }
|
||||
T *end() { return buffer_ + size_; }
|
||||
T const *begin() const { return buffer_; }
|
||||
T const *end() const { return buffer_ + size_; }
|
||||
T &operator()(int64_t i) const { return buffer_[i]; }
|
||||
T &operator[](int64_t i) const { return buffer_[i]; }
|
||||
T &operator()(ssize_t i) const { return buffer_[i]; }
|
||||
T &operator[](ssize_t i) const { return buffer_[i]; }
|
||||
|
||||
bool operator==(const NDView &other) const {
|
||||
if (size_ != other.size_)
|
||||
@ -94,10 +107,24 @@ template <typename T, int64_t Ndim = 2> class NDView : public ArrayExpr<NDView<T
|
||||
|
||||
NDView &operator+=(const T val) { return elemenwise(val, std::plus<T>()); }
|
||||
NDView &operator-=(const T val) { return elemenwise(val, std::minus<T>()); }
|
||||
NDView &operator*=(const T val) { return elemenwise(val, std::multiplies<T>()); }
|
||||
NDView &operator/=(const T val) { return elemenwise(val, std::divides<T>()); }
|
||||
NDView &operator*=(const T val) {
|
||||
return elemenwise(val, std::multiplies<T>());
|
||||
}
|
||||
NDView &operator/=(const T val) {
|
||||
return elemenwise(val, std::divides<T>());
|
||||
}
|
||||
|
||||
NDView &operator/=(const NDView &other) { return elemenwise(other, std::divides<T>()); }
|
||||
NDView &operator/=(const NDView &other) {
|
||||
return elemenwise(other, std::divides<T>());
|
||||
}
|
||||
|
||||
template <size_t Size> NDView &operator=(const std::array<T, Size> &arr) {
|
||||
if (size() != static_cast<ssize_t>(arr.size()))
|
||||
throw std::runtime_error(LOCATION +
|
||||
"Array and NDView size mismatch");
|
||||
std::copy(arr.begin(), arr.end(), begin());
|
||||
return *this;
|
||||
}
|
||||
|
||||
NDView &operator=(const T val) {
|
||||
for (auto it = begin(); it != end(); ++it)
|
||||
@ -127,31 +154,33 @@ template <typename T, int64_t Ndim = 2> class NDView : public ArrayExpr<NDView<T
|
||||
}
|
||||
|
||||
auto &shape() const { return shape_; }
|
||||
auto shape(int64_t i) const { return shape_[i]; }
|
||||
auto shape(ssize_t i) const { return shape_[i]; }
|
||||
|
||||
T *data() { return buffer_; }
|
||||
void print_all() const;
|
||||
|
||||
private:
|
||||
T *buffer_{nullptr};
|
||||
std::array<int64_t, Ndim> strides_{};
|
||||
std::array<int64_t, Ndim> shape_{};
|
||||
std::array<ssize_t, Ndim> strides_{};
|
||||
std::array<ssize_t, Ndim> shape_{};
|
||||
uint64_t size_{};
|
||||
|
||||
template <class BinaryOperation> NDView &elemenwise(T val, BinaryOperation op) {
|
||||
template <class BinaryOperation>
|
||||
NDView &elemenwise(T val, BinaryOperation op) {
|
||||
for (uint64_t i = 0; i != size_; ++i) {
|
||||
buffer_[i] = op(buffer_[i], val);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
template <class BinaryOperation> NDView &elemenwise(const NDView &other, BinaryOperation op) {
|
||||
template <class BinaryOperation>
|
||||
NDView &elemenwise(const NDView &other, BinaryOperation op) {
|
||||
for (uint64_t i = 0; i != size_; ++i) {
|
||||
buffer_[i] = op(buffer_[i], other.buffer_[i]);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
template <typename T, int64_t Ndim> void NDView<T, Ndim>::print_all() const {
|
||||
template <typename T, ssize_t Ndim> void NDView<T, Ndim>::print_all() const {
|
||||
for (auto row = 0; row < shape_[0]; ++row) {
|
||||
for (auto col = 0; col < shape_[1]; ++col) {
|
||||
std::cout << std::setw(3);
|
||||
@ -161,9 +190,8 @@ template <typename T, int64_t Ndim> void NDView<T, Ndim>::print_all() const {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template <typename T, int64_t Ndim>
|
||||
std::ostream& operator <<(std::ostream& os, const NDView<T, Ndim>& arr){
|
||||
template <typename T, ssize_t Ndim>
|
||||
std::ostream &operator<<(std::ostream &os, const NDView<T, Ndim> &arr) {
|
||||
for (auto row = 0; row < arr.shape(0); ++row) {
|
||||
for (auto col = 0; col < arr.shape(1); ++col) {
|
||||
os << std::setw(3);
|
||||
@ -174,5 +202,8 @@ std::ostream& operator <<(std::ostream& os, const NDView<T, Ndim>& arr){
|
||||
return os;
|
||||
}
|
||||
|
||||
template <typename T> NDView<T, 1> make_view(std::vector<T> &vec) {
|
||||
return NDView<T, 1>(vec.data(), {static_cast<ssize_t>(vec.size())});
|
||||
}
|
||||
|
||||
} // namespace aare
|
@ -1,9 +1,8 @@
|
||||
#pragma once
|
||||
#include "aare/Dtype.hpp"
|
||||
#include "aare/defs.hpp"
|
||||
#include "aare/FileInterface.hpp"
|
||||
#include "aare/NumpyHelpers.hpp"
|
||||
|
||||
#include "aare/defs.hpp"
|
||||
|
||||
#include <filesystem>
|
||||
#include <iostream>
|
||||
@ -11,13 +10,12 @@
|
||||
|
||||
namespace aare {
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief NumpyFile class to read and write numpy files
|
||||
* @note derived from FileInterface
|
||||
* @note implements all the pure virtual functions from FileInterface
|
||||
* @note documentation for the functions can also be found in the FileInterface class
|
||||
* @note documentation for the functions can also be found in the FileInterface
|
||||
* class
|
||||
*/
|
||||
class NumpyFile : public FileInterface {
|
||||
|
||||
@ -28,26 +26,35 @@ class NumpyFile : public FileInterface {
|
||||
* @param mode file mode (r, w)
|
||||
* @param cfg file configuration
|
||||
*/
|
||||
explicit NumpyFile(const std::filesystem::path &fname, const std::string &mode = "r", FileConfig cfg = {});
|
||||
explicit NumpyFile(const std::filesystem::path &fname,
|
||||
const std::string &mode = "r", FileConfig cfg = {});
|
||||
|
||||
void write(Frame &frame);
|
||||
Frame read_frame() override { return get_frame(this->current_frame++); }
|
||||
Frame read_frame(size_t frame_number) override { return get_frame(frame_number); }
|
||||
Frame read_frame(size_t frame_number) override {
|
||||
return get_frame(frame_number);
|
||||
}
|
||||
|
||||
std::vector<Frame> read_n(size_t n_frames) override;
|
||||
void read_into(std::byte *image_buf) override { return get_frame_into(this->current_frame++, image_buf); }
|
||||
void read_into(std::byte *image_buf) override {
|
||||
return get_frame_into(this->current_frame++, image_buf);
|
||||
}
|
||||
void read_into(std::byte *image_buf, size_t n_frames) override;
|
||||
size_t frame_number(size_t frame_index) override { return frame_index; };
|
||||
size_t bytes_per_frame() override;
|
||||
size_t pixels_per_frame() override;
|
||||
void seek(size_t frame_number) override { this->current_frame = frame_number; }
|
||||
void seek(size_t frame_number) override {
|
||||
this->current_frame = frame_number;
|
||||
}
|
||||
size_t tell() override { return this->current_frame; }
|
||||
size_t total_frames() const override { return m_header.shape[0]; }
|
||||
size_t rows() const override { return m_header.shape[1]; }
|
||||
size_t cols() const override { return m_header.shape[2]; }
|
||||
size_t bitdepth() const override { return m_header.dtype.bitdepth(); }
|
||||
|
||||
DetectorType detector_type() const override { return DetectorType::Unknown; }
|
||||
DetectorType detector_type() const override {
|
||||
return DetectorType::Unknown;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief get the data type of the numpy file
|
||||
@ -69,8 +76,9 @@ 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<int64_t>(header_size), SEEK_SET)) {
|
||||
throw std::runtime_error(LOCATION + "Error seeking to the start of the data");
|
||||
if (fseek(fp, static_cast<long>(header_size), SEEK_SET)) {
|
||||
throw std::runtime_error(LOCATION +
|
||||
"Error seeking to the start of the data");
|
||||
}
|
||||
size_t rc = fread(arr.data(), sizeof(T), arr.size(), fp);
|
||||
if (rc != static_cast<size_t>(arr.size())) {
|
||||
@ -78,16 +86,20 @@ class NumpyFile : public FileInterface {
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
template <typename A, typename TYPENAME, A Ndim> void write(NDView<TYPENAME, Ndim> &frame) {
|
||||
template <typename A, typename TYPENAME, A Ndim>
|
||||
void write(NDView<TYPENAME, Ndim> &frame) {
|
||||
write_impl(frame.data(), frame.total_bytes());
|
||||
}
|
||||
template <typename A, typename TYPENAME, A Ndim> void write(NDArray<TYPENAME, Ndim> &frame) {
|
||||
template <typename A, typename TYPENAME, A Ndim>
|
||||
void write(NDArray<TYPENAME, Ndim> &frame) {
|
||||
write_impl(frame.data(), frame.total_bytes());
|
||||
}
|
||||
template <typename A, typename TYPENAME, A Ndim> void write(NDView<TYPENAME, Ndim> &&frame) {
|
||||
template <typename A, typename TYPENAME, A Ndim>
|
||||
void write(NDView<TYPENAME, Ndim> &&frame) {
|
||||
write_impl(frame.data(), frame.total_bytes());
|
||||
}
|
||||
template <typename A, typename TYPENAME, A Ndim> void write(NDArray<TYPENAME, Ndim> &&frame) {
|
||||
template <typename A, typename TYPENAME, A Ndim>
|
||||
void write(NDArray<TYPENAME, Ndim> &&frame) {
|
||||
write_impl(frame.data(), frame.total_bytes());
|
||||
}
|
||||
|
||||
|
@ -40,15 +40,18 @@ bool parse_bool(const std::string &in);
|
||||
|
||||
std::string get_value_from_map(const std::string &mapstr);
|
||||
|
||||
std::unordered_map<std::string, std::string> parse_dict(std::string in, const std::vector<std::string> &keys);
|
||||
std::unordered_map<std::string, std::string>
|
||||
parse_dict(std::string in, const std::vector<std::string> &keys);
|
||||
|
||||
template <typename T, size_t N> bool in_array(T val, const std::array<T, N> &arr) {
|
||||
template <typename T, size_t N>
|
||||
bool in_array(T val, const std::array<T, N> &arr) {
|
||||
return std::find(std::begin(arr), std::end(arr), val) != std::end(arr);
|
||||
}
|
||||
bool is_digits(const std::string &str);
|
||||
|
||||
aare::Dtype parse_descr(std::string typestring);
|
||||
size_t write_header(const std::filesystem::path &fname, const NumpyHeader &header);
|
||||
size_t write_header(const std::filesystem::path &fname,
|
||||
const NumpyHeader &header);
|
||||
size_t write_header(std::ostream &out, const NumpyHeader &header);
|
||||
|
||||
} // namespace NumpyHelpers
|
||||
|
@ -18,15 +18,15 @@ template <typename SUM_TYPE = double> class Pedestal {
|
||||
|
||||
uint32_t m_samples;
|
||||
NDArray<uint32_t, 2> m_cur_samples;
|
||||
|
||||
//TODO! in case of int needs to be changed to uint64_t
|
||||
|
||||
// TODO! in case of int needs to be changed to uint64_t
|
||||
NDArray<SUM_TYPE, 2> m_sum;
|
||||
NDArray<SUM_TYPE, 2> m_sum2;
|
||||
|
||||
//Cache mean since it is used over and over in the ClusterFinder
|
||||
//This optimization is related to the access pattern of the ClusterFinder
|
||||
//Relies on having more reads than pushes to the pedestal
|
||||
NDArray<SUM_TYPE, 2> m_mean;
|
||||
// Cache mean since it is used over and over in the ClusterFinder
|
||||
// This optimization is related to the access pattern of the ClusterFinder
|
||||
// Relies on having more reads than pushes to the pedestal
|
||||
NDArray<SUM_TYPE, 2> m_mean;
|
||||
|
||||
public:
|
||||
Pedestal(uint32_t rows, uint32_t cols, uint32_t n_samples = 1000)
|
||||
@ -42,9 +42,7 @@ template <typename SUM_TYPE = double> class Pedestal {
|
||||
}
|
||||
~Pedestal() = default;
|
||||
|
||||
NDArray<SUM_TYPE, 2> mean() {
|
||||
return m_mean;
|
||||
}
|
||||
NDArray<SUM_TYPE, 2> mean() { return m_mean; }
|
||||
|
||||
SUM_TYPE mean(const uint32_t row, const uint32_t col) const {
|
||||
return m_mean(row, col);
|
||||
@ -71,8 +69,6 @@ template <typename SUM_TYPE = double> class Pedestal {
|
||||
return variance_array;
|
||||
}
|
||||
|
||||
|
||||
|
||||
NDArray<SUM_TYPE, 2> std() {
|
||||
NDArray<SUM_TYPE, 2> standard_deviation_array({m_rows, m_cols});
|
||||
for (uint32_t i = 0; i < m_rows * m_cols; i++) {
|
||||
@ -83,29 +79,25 @@ template <typename SUM_TYPE = double> class Pedestal {
|
||||
return standard_deviation_array;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void clear() {
|
||||
m_sum = 0;
|
||||
m_sum2 = 0;
|
||||
m_cur_samples = 0;
|
||||
m_mean = 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void clear(const uint32_t row, const uint32_t col) {
|
||||
m_sum(row, col) = 0;
|
||||
m_sum2(row, col) = 0;
|
||||
m_cur_samples(row, col) = 0;
|
||||
m_mean(row, col) = 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
template <typename T> void push(NDView<T, 2> frame) {
|
||||
assert(frame.size() == m_rows * m_cols);
|
||||
|
||||
// TODO! move away from m_rows, m_cols
|
||||
if (frame.shape() != std::array<int64_t, 2>{m_rows, m_cols}) {
|
||||
if (frame.shape() != std::array<ssize_t, 2>{m_rows, m_cols}) {
|
||||
throw std::runtime_error(
|
||||
"Frame shape does not match pedestal shape");
|
||||
}
|
||||
@ -119,14 +111,14 @@ template <typename SUM_TYPE = double> class Pedestal {
|
||||
|
||||
/**
|
||||
* Push but don't update the cached mean. Speeds up the process
|
||||
* when intitializing the pedestal.
|
||||
*
|
||||
* when initializing the pedestal.
|
||||
*
|
||||
*/
|
||||
template <typename T> void push_no_update(NDView<T, 2> frame) {
|
||||
assert(frame.size() == m_rows * m_cols);
|
||||
|
||||
// TODO! move away from m_rows, m_cols
|
||||
if (frame.shape() != std::array<int64_t, 2>{m_rows, m_cols}) {
|
||||
if (frame.shape() != std::array<ssize_t, 2>{m_rows, m_cols}) {
|
||||
throw std::runtime_error(
|
||||
"Frame shape does not match pedestal shape");
|
||||
}
|
||||
@ -138,9 +130,6 @@ template <typename SUM_TYPE = double> class Pedestal {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
template <typename T> void push(Frame &frame) {
|
||||
assert(frame.rows() == static_cast<size_t>(m_rows) &&
|
||||
frame.cols() == static_cast<size_t>(m_cols));
|
||||
@ -165,10 +154,11 @@ template <typename SUM_TYPE = double> class Pedestal {
|
||||
m_sum2(row, col) += val * val;
|
||||
m_cur_samples(row, col)++;
|
||||
} else {
|
||||
m_sum(row, col) += val - m_sum(row, col) / m_cur_samples(row, col);
|
||||
m_sum2(row, col) += val * val - m_sum2(row, col) / m_cur_samples(row, col);
|
||||
m_sum(row, col) += val - m_sum(row, col) / m_samples;
|
||||
m_sum2(row, col) += val * val - m_sum2(row, col) / m_samples;
|
||||
}
|
||||
//Since we just did a push we know that m_cur_samples(row, col) is at least 1
|
||||
// Since we just did a push we know that m_cur_samples(row, col) is at
|
||||
// least 1
|
||||
m_mean(row, col) = m_sum(row, col) / m_cur_samples(row, col);
|
||||
}
|
||||
|
||||
@ -181,7 +171,8 @@ template <typename SUM_TYPE = double> class Pedestal {
|
||||
m_cur_samples(row, col)++;
|
||||
} else {
|
||||
m_sum(row, col) += val - m_sum(row, col) / m_cur_samples(row, col);
|
||||
m_sum2(row, col) += val * val - m_sum2(row, col) / m_cur_samples(row, col);
|
||||
m_sum2(row, col) +=
|
||||
val * val - m_sum2(row, col) / m_cur_samples(row, col);
|
||||
}
|
||||
}
|
||||
|
||||
@ -189,19 +180,16 @@ template <typename SUM_TYPE = double> class Pedestal {
|
||||
* @brief Update the mean of the pedestal. This is used after having done
|
||||
* push_no_update. It is not necessary to call this function after push.
|
||||
*/
|
||||
void update_mean(){
|
||||
m_mean = m_sum / m_cur_samples;
|
||||
}
|
||||
void update_mean() { m_mean = m_sum / m_cur_samples; }
|
||||
|
||||
template<typename T>
|
||||
void push_fast(const uint32_t row, const uint32_t col, const T val_){
|
||||
//Assume we reached the steady state where all pixels have
|
||||
//m_samples samples
|
||||
template <typename T>
|
||||
void push_fast(const uint32_t row, const uint32_t col, const T val_) {
|
||||
// Assume we reached the steady state where all pixels have
|
||||
// m_samples samples
|
||||
SUM_TYPE val = static_cast<SUM_TYPE>(val_);
|
||||
m_sum(row, col) += val - m_sum(row, col) / m_samples;
|
||||
m_sum2(row, col) += val * val - m_sum2(row, col) / m_samples;
|
||||
m_mean(row, col) = m_sum(row, col) / m_samples;
|
||||
}
|
||||
|
||||
};
|
||||
} // namespace aare
|
@ -1,7 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "aare/defs.hpp"
|
||||
#include "aare/NDArray.hpp"
|
||||
#include "aare/defs.hpp"
|
||||
|
||||
namespace aare {
|
||||
|
||||
@ -10,11 +10,11 @@ NDArray<ssize_t, 2> GenerateMoench05PixelMap();
|
||||
NDArray<ssize_t, 2> GenerateMoench05PixelMap1g();
|
||||
NDArray<ssize_t, 2> GenerateMoench05PixelMapOld();
|
||||
|
||||
//Matterhorn02
|
||||
NDArray<ssize_t, 2>GenerateMH02SingleCounterPixelMap();
|
||||
// Matterhorn02
|
||||
NDArray<ssize_t, 2> GenerateMH02SingleCounterPixelMap();
|
||||
NDArray<ssize_t, 3> GenerateMH02FourCounterPixelMap();
|
||||
|
||||
//Eiger
|
||||
NDArray<ssize_t, 2>GenerateEigerFlipRowsPixelMap();
|
||||
// Eiger
|
||||
NDArray<ssize_t, 2> GenerateEigerFlipRowsPixelMap();
|
||||
|
||||
} // namespace aare
|
205
include/aare/ProducerConsumerQueue.hpp
Normal file
205
include/aare/ProducerConsumerQueue.hpp
Normal file
@ -0,0 +1,205 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// @author Bo Hu (bhu@fb.com)
|
||||
// @author Jordan DeLong (delong.j@fb.com)
|
||||
|
||||
// Changes made by PSD Detector Group:
|
||||
// Copied: Line 34 constexpr std::size_t hardware_destructive_interference_size
|
||||
// = 128; from folly/lang/Align.h Changed extension to .hpp Changed namespace to
|
||||
// aare
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <cassert>
|
||||
#include <cstdlib>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
constexpr std::size_t hardware_destructive_interference_size = 128;
|
||||
namespace aare {
|
||||
|
||||
/*
|
||||
* ProducerConsumerQueue is a one producer and one consumer queue
|
||||
* without locks.
|
||||
*/
|
||||
template <class T> struct ProducerConsumerQueue {
|
||||
typedef T value_type;
|
||||
|
||||
ProducerConsumerQueue(const ProducerConsumerQueue &) = delete;
|
||||
ProducerConsumerQueue &operator=(const ProducerConsumerQueue &) = delete;
|
||||
|
||||
ProducerConsumerQueue(ProducerConsumerQueue &&other) {
|
||||
size_ = other.size_;
|
||||
records_ = other.records_;
|
||||
other.records_ = nullptr;
|
||||
readIndex_ = other.readIndex_.load(std::memory_order_acquire);
|
||||
writeIndex_ = other.writeIndex_.load(std::memory_order_acquire);
|
||||
}
|
||||
ProducerConsumerQueue &operator=(ProducerConsumerQueue &&other) {
|
||||
size_ = other.size_;
|
||||
records_ = other.records_;
|
||||
other.records_ = nullptr;
|
||||
readIndex_ = other.readIndex_.load(std::memory_order_acquire);
|
||||
writeIndex_ = other.writeIndex_.load(std::memory_order_acquire);
|
||||
return *this;
|
||||
}
|
||||
|
||||
ProducerConsumerQueue() : ProducerConsumerQueue(2){};
|
||||
// size must be >= 2.
|
||||
//
|
||||
// Also, note that the number of usable slots in the queue at any
|
||||
// given time is actually (size-1), so if you start with an empty queue,
|
||||
// isFull() will return true after size-1 insertions.
|
||||
explicit ProducerConsumerQueue(uint32_t size)
|
||||
: size_(size),
|
||||
records_(static_cast<T *>(std::malloc(sizeof(T) * size))),
|
||||
readIndex_(0), writeIndex_(0) {
|
||||
assert(size >= 2);
|
||||
if (!records_) {
|
||||
throw std::bad_alloc();
|
||||
}
|
||||
}
|
||||
|
||||
~ProducerConsumerQueue() {
|
||||
// We need to destruct anything that may still exist in our queue.
|
||||
// (No real synchronization needed at destructor time: only one
|
||||
// thread can be doing this.)
|
||||
if (!std::is_trivially_destructible<T>::value) {
|
||||
size_t readIndex = readIndex_;
|
||||
size_t endIndex = writeIndex_;
|
||||
while (readIndex != endIndex) {
|
||||
records_[readIndex].~T();
|
||||
if (++readIndex == size_) {
|
||||
readIndex = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::free(records_);
|
||||
}
|
||||
|
||||
template <class... Args> bool write(Args &&...recordArgs) {
|
||||
auto const currentWrite = writeIndex_.load(std::memory_order_relaxed);
|
||||
auto nextRecord = currentWrite + 1;
|
||||
if (nextRecord == size_) {
|
||||
nextRecord = 0;
|
||||
}
|
||||
if (nextRecord != readIndex_.load(std::memory_order_acquire)) {
|
||||
new (&records_[currentWrite]) T(std::forward<Args>(recordArgs)...);
|
||||
writeIndex_.store(nextRecord, std::memory_order_release);
|
||||
return true;
|
||||
}
|
||||
|
||||
// queue is full
|
||||
return false;
|
||||
}
|
||||
|
||||
// move (or copy) the value at the front of the queue to given variable
|
||||
bool read(T &record) {
|
||||
auto const currentRead = readIndex_.load(std::memory_order_relaxed);
|
||||
if (currentRead == writeIndex_.load(std::memory_order_acquire)) {
|
||||
// queue is empty
|
||||
return false;
|
||||
}
|
||||
|
||||
auto nextRecord = currentRead + 1;
|
||||
if (nextRecord == size_) {
|
||||
nextRecord = 0;
|
||||
}
|
||||
record = std::move(records_[currentRead]);
|
||||
records_[currentRead].~T();
|
||||
readIndex_.store(nextRecord, std::memory_order_release);
|
||||
return true;
|
||||
}
|
||||
|
||||
// pointer to the value at the front of the queue (for use in-place) or
|
||||
// nullptr if empty.
|
||||
T *frontPtr() {
|
||||
auto const currentRead = readIndex_.load(std::memory_order_relaxed);
|
||||
if (currentRead == writeIndex_.load(std::memory_order_acquire)) {
|
||||
// queue is empty
|
||||
return nullptr;
|
||||
}
|
||||
return &records_[currentRead];
|
||||
}
|
||||
|
||||
// queue must not be empty
|
||||
void popFront() {
|
||||
auto const currentRead = readIndex_.load(std::memory_order_relaxed);
|
||||
assert(currentRead != writeIndex_.load(std::memory_order_acquire));
|
||||
|
||||
auto nextRecord = currentRead + 1;
|
||||
if (nextRecord == size_) {
|
||||
nextRecord = 0;
|
||||
}
|
||||
records_[currentRead].~T();
|
||||
readIndex_.store(nextRecord, std::memory_order_release);
|
||||
}
|
||||
|
||||
bool isEmpty() const {
|
||||
return readIndex_.load(std::memory_order_acquire) ==
|
||||
writeIndex_.load(std::memory_order_acquire);
|
||||
}
|
||||
|
||||
bool isFull() const {
|
||||
auto nextRecord = writeIndex_.load(std::memory_order_acquire) + 1;
|
||||
if (nextRecord == size_) {
|
||||
nextRecord = 0;
|
||||
}
|
||||
if (nextRecord != readIndex_.load(std::memory_order_acquire)) {
|
||||
return false;
|
||||
}
|
||||
// queue is full
|
||||
return true;
|
||||
}
|
||||
|
||||
// * If called by consumer, then true size may be more (because producer may
|
||||
// be adding items concurrently).
|
||||
// * If called by producer, then true size may be less (because consumer may
|
||||
// be removing items concurrently).
|
||||
// * It is undefined to call this from any other thread.
|
||||
size_t sizeGuess() const {
|
||||
int ret = writeIndex_.load(std::memory_order_acquire) -
|
||||
readIndex_.load(std::memory_order_acquire);
|
||||
if (ret < 0) {
|
||||
ret += size_;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// maximum number of items in the queue.
|
||||
size_t capacity() const { return size_ - 1; }
|
||||
|
||||
private:
|
||||
using AtomicIndex = std::atomic<unsigned int>;
|
||||
|
||||
char pad0_[hardware_destructive_interference_size];
|
||||
// const uint32_t size_;
|
||||
uint32_t size_;
|
||||
// T *const records_;
|
||||
T *records_;
|
||||
|
||||
alignas(hardware_destructive_interference_size) AtomicIndex readIndex_;
|
||||
alignas(hardware_destructive_interference_size) AtomicIndex writeIndex_;
|
||||
|
||||
char pad1_[hardware_destructive_interference_size - sizeof(AtomicIndex)];
|
||||
};
|
||||
|
||||
} // namespace aare
|
@ -1,11 +1,10 @@
|
||||
#pragma once
|
||||
#include "aare/FileInterface.hpp"
|
||||
#include "aare/RawMasterFile.hpp"
|
||||
#include "aare/Frame.hpp"
|
||||
#include "aare/NDArray.hpp" //for pixel map
|
||||
#include "aare/RawMasterFile.hpp"
|
||||
#include "aare/RawSubFile.hpp"
|
||||
|
||||
|
||||
#include <optional>
|
||||
|
||||
namespace aare {
|
||||
@ -30,19 +29,12 @@ struct ModuleConfig {
|
||||
* Consider using that unless you need raw file specific functionality.
|
||||
*/
|
||||
class RawFile : public FileInterface {
|
||||
size_t n_subfiles{}; //f0,f1...fn
|
||||
size_t n_subfile_parts{}; // d0,d1...dn
|
||||
//TODO! move to vector of SubFile instead of pointers
|
||||
std::vector<std::vector<RawSubFile *>> subfiles; //subfiles[f0,f1...fn][d0,d1...dn]
|
||||
std::vector<xy> positions;
|
||||
std::vector<ModuleGeometry> m_module_pixel_0;
|
||||
std::vector<std::unique_ptr<RawSubFile>> m_subfiles;
|
||||
ModuleConfig cfg{0, 0};
|
||||
|
||||
RawMasterFile m_master;
|
||||
|
||||
size_t m_current_frame{};
|
||||
size_t m_rows{};
|
||||
size_t m_cols{};
|
||||
size_t m_current_subfile{};
|
||||
DetectorGeometry m_geometry;
|
||||
|
||||
public:
|
||||
/**
|
||||
@ -52,7 +44,7 @@ class RawFile : public FileInterface {
|
||||
|
||||
*/
|
||||
RawFile(const std::filesystem::path &fname, const std::string &mode = "r");
|
||||
virtual ~RawFile() override;
|
||||
virtual ~RawFile() override = default;
|
||||
|
||||
Frame read_frame() override;
|
||||
Frame read_frame(size_t frame_number) override;
|
||||
@ -60,10 +52,10 @@ class RawFile : public FileInterface {
|
||||
void read_into(std::byte *image_buf) override;
|
||||
void read_into(std::byte *image_buf, size_t n_frames) override;
|
||||
|
||||
//TODO! do we need to adapt the API?
|
||||
// TODO! do we need to adapt the API?
|
||||
void read_into(std::byte *image_buf, DetectorHeader *header);
|
||||
void read_into(std::byte *image_buf, size_t n_frames, DetectorHeader *header);
|
||||
|
||||
void read_into(std::byte *image_buf, size_t n_frames,
|
||||
DetectorHeader *header);
|
||||
|
||||
size_t frame_number(size_t frame_index) override;
|
||||
size_t bytes_per_frame() override;
|
||||
@ -76,24 +68,21 @@ class RawFile : public FileInterface {
|
||||
size_t cols() const override;
|
||||
size_t bitdepth() const override;
|
||||
xy geometry();
|
||||
size_t n_mod() const;
|
||||
|
||||
size_t n_modules() const;
|
||||
|
||||
RawMasterFile master() const;
|
||||
|
||||
|
||||
|
||||
DetectorType detector_type() const override;
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
* @brief read the frame at the given frame index into the image buffer
|
||||
* @param frame_number frame number to read
|
||||
* @param image_buf buffer to store the frame
|
||||
*/
|
||||
|
||||
void get_frame_into(size_t frame_index, std::byte *frame_buffer, DetectorHeader *header = nullptr);
|
||||
|
||||
void get_frame_into(size_t frame_index, std::byte *frame_buffer,
|
||||
DetectorHeader *header = nullptr);
|
||||
|
||||
/**
|
||||
* @brief get the frame at the given frame index
|
||||
@ -102,8 +91,6 @@ class RawFile : public FileInterface {
|
||||
*/
|
||||
Frame get_frame(size_t frame_index);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief read the header of the file
|
||||
* @param fname path to the data subfile
|
||||
@ -111,9 +98,6 @@ class RawFile : public FileInterface {
|
||||
*/
|
||||
static DetectorHeader read_header(const std::filesystem::path &fname);
|
||||
|
||||
void update_geometry_with_roi();
|
||||
int find_number_of_subfiles();
|
||||
|
||||
void open_subfiles();
|
||||
void find_geometry();
|
||||
};
|
||||
|
@ -1,5 +1,7 @@
|
||||
#pragma once
|
||||
#include "aare/defs.hpp"
|
||||
#include "aare/scan_parameters.hpp"
|
||||
|
||||
#include <filesystem>
|
||||
#include <fmt/format.h>
|
||||
#include <fstream>
|
||||
@ -39,40 +41,6 @@ class RawFileNameComponents {
|
||||
void set_old_scheme(bool old_scheme);
|
||||
};
|
||||
|
||||
class ScanParameters {
|
||||
bool m_enabled = false;
|
||||
std::string m_dac;
|
||||
int m_start = 0;
|
||||
int m_stop = 0;
|
||||
int m_step = 0;
|
||||
//TODO! add settleTime, requires string to time conversion
|
||||
|
||||
public:
|
||||
ScanParameters(const std::string &par);
|
||||
ScanParameters() = default;
|
||||
ScanParameters(const ScanParameters &) = default;
|
||||
ScanParameters &operator=(const ScanParameters &) = default;
|
||||
ScanParameters(ScanParameters &&) = default;
|
||||
int start() const;
|
||||
int stop() const;
|
||||
int step() const;
|
||||
const std::string &dac() const;
|
||||
bool enabled() const;
|
||||
void increment_stop();
|
||||
};
|
||||
|
||||
|
||||
struct ROI{
|
||||
int64_t xmin{};
|
||||
int64_t xmax{};
|
||||
int64_t ymin{};
|
||||
int64_t ymax{};
|
||||
|
||||
int64_t height() const { return ymax - ymin; }
|
||||
int64_t width() const { return xmax - xmin; }
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Class for parsing a master file either in our .json format or the old
|
||||
* .raw format
|
||||
@ -112,7 +80,6 @@ class RawMasterFile {
|
||||
|
||||
std::optional<ROI> m_roi;
|
||||
|
||||
|
||||
public:
|
||||
RawMasterFile(const std::filesystem::path &fpath);
|
||||
|
||||
@ -132,6 +99,7 @@ class RawMasterFile {
|
||||
|
||||
size_t total_frames_expected() const;
|
||||
xy geometry() const;
|
||||
size_t n_modules() const;
|
||||
|
||||
std::optional<size_t> analog_samples() const;
|
||||
std::optional<size_t> digital_samples() const;
|
||||
@ -139,10 +107,8 @@ class RawMasterFile {
|
||||
std::optional<size_t> number_of_rows() const;
|
||||
std::optional<uint8_t> quad() const;
|
||||
|
||||
|
||||
std::optional<ROI> roi() const;
|
||||
|
||||
|
||||
ScanParameters scan_parameters() const;
|
||||
|
||||
private:
|
||||
|
@ -10,23 +10,34 @@
|
||||
namespace aare {
|
||||
|
||||
/**
|
||||
* @brief Class to read a singe subfile written in .raw format. Used from RawFile to read
|
||||
* the entire detector. Can be used directly to read part of the image.
|
||||
* @brief Class to read a singe subfile written in .raw format. Used from
|
||||
* RawFile to read the entire detector. Can be used directly to read part of the
|
||||
* image.
|
||||
*/
|
||||
class RawSubFile {
|
||||
protected:
|
||||
std::ifstream m_file;
|
||||
DetectorType m_detector_type;
|
||||
size_t m_bitdepth;
|
||||
std::filesystem::path m_fname;
|
||||
std::filesystem::path m_path; //!< path to the subfile
|
||||
std::string m_base_name; //!< base name used for formatting file names
|
||||
size_t m_offset{}; //!< file index of the first file, allow starting at non
|
||||
//!< zero file
|
||||
size_t m_total_frames{}; //!< total number of frames in the series of files
|
||||
size_t m_rows{};
|
||||
size_t m_cols{};
|
||||
size_t m_bytes_per_frame{};
|
||||
size_t n_frames{};
|
||||
|
||||
int m_module_index{};
|
||||
size_t m_current_file_index{}; //!< The index of the open file
|
||||
size_t m_current_frame_index{}; //!< The index of the current frame (with
|
||||
//!< reference to all files)
|
||||
std::vector<size_t>
|
||||
m_last_frame_in_file{}; //!< Used for seeking to the correct file
|
||||
|
||||
uint32_t m_pos_row{};
|
||||
uint32_t m_pos_col{};
|
||||
|
||||
|
||||
|
||||
std::optional<NDArray<ssize_t, 2>> m_pixel_map;
|
||||
|
||||
public:
|
||||
@ -40,12 +51,14 @@ class RawSubFile {
|
||||
* @throws std::invalid_argument if the detector,type pair is not supported
|
||||
*/
|
||||
RawSubFile(const std::filesystem::path &fname, DetectorType detector,
|
||||
size_t rows, size_t cols, size_t bitdepth, uint32_t pos_row = 0, uint32_t pos_col = 0);
|
||||
size_t rows, size_t cols, size_t bitdepth, uint32_t pos_row = 0,
|
||||
uint32_t pos_col = 0);
|
||||
|
||||
~RawSubFile() = default;
|
||||
/**
|
||||
* @brief Seek to the given frame number
|
||||
* @note Puts the file pointer at the start of the header, not the start of the data
|
||||
* @note Puts the file pointer at the start of the header, not the start of
|
||||
* the data
|
||||
* @param frame_index frame position in file to seek to
|
||||
* @throws std::runtime_error if the frame number is out of range
|
||||
*/
|
||||
@ -53,20 +66,30 @@ class RawSubFile {
|
||||
size_t tell();
|
||||
|
||||
void read_into(std::byte *image_buf, DetectorHeader *header = nullptr);
|
||||
void read_into(std::byte *image_buf, size_t n_frames,
|
||||
DetectorHeader *header = nullptr);
|
||||
void get_part(std::byte *buffer, size_t frame_index);
|
||||
|
||||
|
||||
void read_header(DetectorHeader *header);
|
||||
|
||||
|
||||
size_t rows() const;
|
||||
size_t cols() const;
|
||||
|
||||
|
||||
size_t frame_number(size_t frame_index);
|
||||
|
||||
size_t bytes_per_frame() const { return m_bytes_per_frame; }
|
||||
size_t pixels_per_frame() const { return m_rows * m_cols; }
|
||||
size_t bytes_per_pixel() const { return m_bitdepth / 8; }
|
||||
size_t bytes_per_pixel() const { return m_bitdepth / bits_per_byte; }
|
||||
|
||||
size_t frames_in_file() const { return m_total_frames; }
|
||||
|
||||
private:
|
||||
template <typename T> void read_with_map(std::byte *image_buf);
|
||||
|
||||
void parse_fname(const std::filesystem::path &fname);
|
||||
void scan_files();
|
||||
void open_file(size_t file_index);
|
||||
std::filesystem::path fpath(size_t file_index) const;
|
||||
};
|
||||
|
||||
} // namespace aare
|
@ -7,7 +7,7 @@
|
||||
|
||||
#include "aare/NDArray.hpp"
|
||||
|
||||
const int MAX_CLUSTER_SIZE = 200;
|
||||
const int MAX_CLUSTER_SIZE = 50;
|
||||
namespace aare {
|
||||
|
||||
template <typename T> class VarClusterFinder {
|
||||
@ -28,7 +28,7 @@ template <typename T> class VarClusterFinder {
|
||||
};
|
||||
|
||||
private:
|
||||
const std::array<int64_t, 2> shape_;
|
||||
const std::array<ssize_t, 2> shape_;
|
||||
NDView<T, 2> original_;
|
||||
NDArray<int, 2> labeled_;
|
||||
NDArray<int, 2> peripheral_labeled_;
|
||||
@ -38,11 +38,13 @@ template <typename T> class VarClusterFinder {
|
||||
bool use_noise_map = false;
|
||||
int peripheralThresholdFactor_ = 5;
|
||||
int current_label;
|
||||
const std::array<int, 4> di{{0, -1, -1, -1}}; // row ### 8-neighbour by scaning from left to right
|
||||
const std::array<int, 4> dj{{-1, -1, 0, 1}}; // col ### 8-neighbour by scaning from top to bottom
|
||||
const std::array<int, 4> di{
|
||||
{0, -1, -1, -1}}; // row ### 8-neighbour by scaning from left to right
|
||||
const std::array<int, 4> dj{
|
||||
{-1, -1, 0, 1}}; // col ### 8-neighbour by scaning from top to bottom
|
||||
const std::array<int, 8> di_{{0, 0, -1, 1, -1, 1, -1, 1}}; // row
|
||||
const std::array<int, 8> dj_{{-1, 1, 0, 0, 1, -1, -1, 1}}; // col
|
||||
std::map<int, int> child; // heirachy: key: child; val: parent
|
||||
std::map<int, int> child; // heirachy: key: child; val: parent
|
||||
std::unordered_map<int, Hit> h_size;
|
||||
std::vector<Hit> hits;
|
||||
// std::vector<std::vector<int16_t>> row
|
||||
@ -50,7 +52,8 @@ template <typename T> class VarClusterFinder {
|
||||
|
||||
public:
|
||||
VarClusterFinder(Shape<2> shape, T threshold)
|
||||
: shape_(shape), labeled_(shape, 0), peripheral_labeled_(shape, 0), binary_(shape), threshold_(threshold) {
|
||||
: shape_(shape), labeled_(shape, 0), peripheral_labeled_(shape, 0),
|
||||
binary_(shape), threshold_(threshold) {
|
||||
hits.reserve(2000);
|
||||
}
|
||||
|
||||
@ -60,7 +63,9 @@ template <typename T> class VarClusterFinder {
|
||||
noiseMap = noise_map;
|
||||
use_noise_map = true;
|
||||
}
|
||||
void set_peripheralThresholdFactor(int factor) { peripheralThresholdFactor_ = factor; }
|
||||
void set_peripheralThresholdFactor(int factor) {
|
||||
peripheralThresholdFactor_ = factor;
|
||||
}
|
||||
void find_clusters(NDView<T, 2> img);
|
||||
void find_clusters_X(NDView<T, 2> img);
|
||||
void rec_FillHit(int clusterIndex, int i, int j);
|
||||
@ -144,7 +149,8 @@ template <typename T> int VarClusterFinder<T>::check_neighbours(int i, int j) {
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T> void VarClusterFinder<T>::find_clusters(NDView<T, 2> img) {
|
||||
template <typename T>
|
||||
void VarClusterFinder<T>::find_clusters(NDView<T, 2> img) {
|
||||
original_ = img;
|
||||
labeled_ = 0;
|
||||
peripheral_labeled_ = 0;
|
||||
@ -156,7 +162,8 @@ template <typename T> void VarClusterFinder<T>::find_clusters(NDView<T, 2> img)
|
||||
store_clusters();
|
||||
}
|
||||
|
||||
template <typename T> void VarClusterFinder<T>::find_clusters_X(NDView<T, 2> img) {
|
||||
template <typename T>
|
||||
void VarClusterFinder<T>::find_clusters_X(NDView<T, 2> img) {
|
||||
original_ = img;
|
||||
int clusterIndex = 0;
|
||||
for (int i = 0; i < shape_[0]; ++i) {
|
||||
@ -175,7 +182,8 @@ template <typename T> void VarClusterFinder<T>::find_clusters_X(NDView<T, 2> img
|
||||
h_size.clear();
|
||||
}
|
||||
|
||||
template <typename T> void VarClusterFinder<T>::rec_FillHit(int clusterIndex, int i, int j) {
|
||||
template <typename T>
|
||||
void VarClusterFinder<T>::rec_FillHit(int clusterIndex, int i, int j) {
|
||||
// printf("original_(%d, %d)=%f\n", i, j, original_(i,j));
|
||||
// printf("h_size[%d].size=%d\n", clusterIndex, h_size[clusterIndex].size);
|
||||
if (h_size[clusterIndex].size < MAX_CLUSTER_SIZE) {
|
||||
@ -203,11 +211,15 @@ template <typename T> void VarClusterFinder<T>::rec_FillHit(int clusterIndex, in
|
||||
} else {
|
||||
// if (h_size[clusterIndex].size < MAX_CLUSTER_SIZE){
|
||||
// h_size[clusterIndex].size += 1;
|
||||
// h_size[clusterIndex].rows[h_size[clusterIndex].size] = row;
|
||||
// h_size[clusterIndex].cols[h_size[clusterIndex].size] = col;
|
||||
// h_size[clusterIndex].enes[h_size[clusterIndex].size] = original_(row, col);
|
||||
// h_size[clusterIndex].rows[h_size[clusterIndex].size] =
|
||||
// row; h_size[clusterIndex].cols[h_size[clusterIndex].size]
|
||||
// = col;
|
||||
// h_size[clusterIndex].enes[h_size[clusterIndex].size] =
|
||||
// original_(row, col);
|
||||
// }// ? weather to include peripheral pixels
|
||||
original_(row, col) = 0; // remove peripheral pixels, to avoid potential influence for pedestal updating
|
||||
original_(row, col) =
|
||||
0; // remove peripheral pixels, to avoid potential influence
|
||||
// for pedestal updating
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -226,7 +238,7 @@ template <typename T> void VarClusterFinder<T>::single_pass(NDView<T, 2> img) {
|
||||
|
||||
template <typename T> void VarClusterFinder<T>::first_pass() {
|
||||
|
||||
for (size_t i = 0; i < original_.size(); ++i) {
|
||||
for (ssize_t i = 0; i < original_.size(); ++i) {
|
||||
if (use_noise_map)
|
||||
threshold_ = 5 * noiseMap(i);
|
||||
binary_(i) = (original_(i) > threshold_);
|
||||
@ -250,7 +262,7 @@ template <typename T> void VarClusterFinder<T>::first_pass() {
|
||||
|
||||
template <typename T> void VarClusterFinder<T>::second_pass() {
|
||||
|
||||
for (size_t i = 0; i != labeled_.size(); ++i) {
|
||||
for (ssize_t i = 0; i != labeled_.size(); ++i) {
|
||||
auto cl = labeled_(i);
|
||||
if (cl != 0) {
|
||||
auto it = child.find(cl);
|
||||
@ -275,8 +287,8 @@ template <typename T> void VarClusterFinder<T>::store_clusters() {
|
||||
for (int i = 0; i < shape_[0]; ++i) {
|
||||
for (int j = 0; j < shape_[1]; ++j) {
|
||||
if (labeled_(i, j) != 0 || false
|
||||
// (i-1 >= 0 and labeled_(i-1, j) != 0) or // another circle of peripheral pixels
|
||||
// (j-1 >= 0 and labeled_(i, j-1) != 0) or
|
||||
// (i-1 >= 0 and labeled_(i-1, j) != 0) or // another circle of
|
||||
// peripheral pixels (j-1 >= 0 and labeled_(i, j-1) != 0) or
|
||||
// (i+1 < shape_[0] and labeled_(i+1, j) != 0) or
|
||||
// (j+1 < shape_[1] and labeled_(i, j+1) != 0)
|
||||
) {
|
||||
|
112
include/aare/algorithm.hpp
Normal file
112
include/aare/algorithm.hpp
Normal file
@ -0,0 +1,112 @@
|
||||
|
||||
#pragma once
|
||||
#include <aare/NDArray.hpp>
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <vector>
|
||||
|
||||
namespace aare {
|
||||
/**
|
||||
* @brief Index of the last element that is smaller than val.
|
||||
* Requires a sorted array. Uses >= for ordering. If all elements
|
||||
* are smaller it returns the last element and if all elements are
|
||||
* larger it returns the first element.
|
||||
* @param first iterator to the first element
|
||||
* @param last iterator to the last element
|
||||
* @param val value to compare
|
||||
* @return index of the last element that is smaller than val
|
||||
*
|
||||
*/
|
||||
template <typename T>
|
||||
size_t last_smaller(const T *first, const T *last, T val) {
|
||||
for (auto iter = first + 1; iter != last; ++iter) {
|
||||
if (*iter >= val) {
|
||||
return std::distance(first, iter - 1);
|
||||
}
|
||||
}
|
||||
return std::distance(first, last - 1);
|
||||
}
|
||||
|
||||
template <typename T> size_t last_smaller(const NDArray<T, 1> &arr, T val) {
|
||||
return last_smaller(arr.begin(), arr.end(), val);
|
||||
}
|
||||
|
||||
template <typename T> size_t last_smaller(const std::vector<T> &vec, T val) {
|
||||
return last_smaller(vec.data(), vec.data() + vec.size(), val);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Index of the first element that is larger than val.
|
||||
* Requires a sorted array. Uses > for ordering. If all elements
|
||||
* are larger it returns the first element and if all elements are
|
||||
* smaller it returns the last element.
|
||||
* @param first iterator to the first element
|
||||
* @param last iterator to the last element
|
||||
* @param val value to compare
|
||||
* @return index of the first element that is larger than val
|
||||
*/
|
||||
template <typename T>
|
||||
size_t first_larger(const T *first, const T *last, T val) {
|
||||
for (auto iter = first; iter != last; ++iter) {
|
||||
if (*iter > val) {
|
||||
return std::distance(first, iter);
|
||||
}
|
||||
}
|
||||
return std::distance(first, last - 1);
|
||||
}
|
||||
|
||||
template <typename T> size_t first_larger(const NDArray<T, 1> &arr, T val) {
|
||||
return first_larger(arr.begin(), arr.end(), val);
|
||||
}
|
||||
|
||||
template <typename T> size_t first_larger(const std::vector<T> &vec, T val) {
|
||||
return first_larger(vec.data(), vec.data() + vec.size(), val);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Index of the nearest element to val.
|
||||
* Requires a sorted array. If there is no difference it takes the first
|
||||
* element.
|
||||
* @param first iterator to the first element
|
||||
* @param last iterator to the last element
|
||||
* @param val value to compare
|
||||
* @return index of the nearest element
|
||||
*/
|
||||
template <typename T>
|
||||
size_t nearest_index(const T *first, const T *last, T val) {
|
||||
auto iter = std::min_element(first, last, [val](T a, T b) {
|
||||
return std::abs(a - val) < std::abs(b - val);
|
||||
});
|
||||
return std::distance(first, iter);
|
||||
}
|
||||
|
||||
template <typename T> size_t nearest_index(const NDArray<T, 1> &arr, T val) {
|
||||
return nearest_index(arr.begin(), arr.end(), val);
|
||||
}
|
||||
|
||||
template <typename T> size_t nearest_index(const std::vector<T> &vec, T val) {
|
||||
return nearest_index(vec.data(), vec.data() + vec.size(), val);
|
||||
}
|
||||
|
||||
template <typename T, size_t N>
|
||||
size_t nearest_index(const std::array<T, N> &arr, T val) {
|
||||
return nearest_index(arr.data(), arr.data() + arr.size(), val);
|
||||
}
|
||||
|
||||
template <typename T> std::vector<T> cumsum(const std::vector<T> &vec) {
|
||||
std::vector<T> result(vec.size());
|
||||
std::partial_sum(vec.begin(), vec.end(), result.begin());
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename Container> bool all_equal(const Container &c) {
|
||||
if (!c.empty() &&
|
||||
std::all_of(begin(c), end(c),
|
||||
[c](const typename Container::value_type &element) {
|
||||
return element == c.front();
|
||||
}))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace aare
|
27
include/aare/decode.hpp
Normal file
27
include/aare/decode.hpp
Normal file
@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
|
||||
#include <aare/NDView.hpp>
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
namespace aare {
|
||||
|
||||
uint16_t adc_sar_05_decode64to16(uint64_t input);
|
||||
uint16_t adc_sar_04_decode64to16(uint64_t input);
|
||||
void adc_sar_05_decode64to16(NDView<uint64_t, 2> input,
|
||||
NDView<uint16_t, 2> output);
|
||||
void adc_sar_04_decode64to16(NDView<uint64_t, 2> input,
|
||||
NDView<uint16_t, 2> output);
|
||||
|
||||
/**
|
||||
* @brief Apply custom weights to a 16-bit input value. Will sum up
|
||||
* weights[i]**i for each bit i that is set in the input value.
|
||||
* @throws std::out_of_range if weights.size() < 16
|
||||
* @param input 16-bit input value
|
||||
* @param weights vector of weights, size must be less than or equal to 16
|
||||
*/
|
||||
double apply_custom_weights(uint16_t input, const NDView<double, 1> weights);
|
||||
|
||||
void apply_custom_weights(NDView<uint16_t, 1> input, NDView<double, 1> output,
|
||||
const NDView<double, 1> weights);
|
||||
|
||||
} // namespace aare
|
@ -1,20 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include "aare/Dtype.hpp"
|
||||
// #include "aare/utils/logger.hpp"
|
||||
#include "aare/type_traits.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <stdexcept>
|
||||
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
|
||||
/**
|
||||
* @brief LOCATION macro to get the current location in the code
|
||||
*/
|
||||
@ -22,24 +23,23 @@
|
||||
std::string(__FILE__) + std::string(":") + std::to_string(__LINE__) + \
|
||||
":" + std::string(__func__) + ":"
|
||||
|
||||
|
||||
|
||||
#ifdef AARE_CUSTOM_ASSERT
|
||||
#define AARE_ASSERT(expr)\
|
||||
if (expr)\
|
||||
{}\
|
||||
else\
|
||||
#define AARE_ASSERT(expr) \
|
||||
if (expr) { \
|
||||
} else \
|
||||
aare::assert_failed(LOCATION + " Assertion failed: " + #expr + "\n");
|
||||
#else
|
||||
#define AARE_ASSERT(cond)\
|
||||
do { (void)sizeof(cond); } while(0)
|
||||
#define AARE_ASSERT(cond) \
|
||||
do { \
|
||||
(void)sizeof(cond); \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
|
||||
namespace aare {
|
||||
|
||||
void assert_failed(const std::string &msg);
|
||||
inline constexpr size_t bits_per_byte = 8;
|
||||
|
||||
void assert_failed(const std::string &msg);
|
||||
|
||||
class DynamicCluster {
|
||||
public:
|
||||
@ -54,7 +54,7 @@ class DynamicCluster {
|
||||
|
||||
public:
|
||||
DynamicCluster(int cluster_sizeX_, int cluster_sizeY_,
|
||||
Dtype dt_ = Dtype(typeid(int32_t)))
|
||||
Dtype dt_ = Dtype(typeid(int32_t)))
|
||||
: cluster_sizeX(cluster_sizeX_), cluster_sizeY(cluster_sizeY_),
|
||||
dt(dt_) {
|
||||
m_data = new std::byte[cluster_sizeX * cluster_sizeY * dt.bytes()]{};
|
||||
@ -178,26 +178,59 @@ template <typename T> struct t_xy {
|
||||
};
|
||||
using xy = t_xy<uint32_t>;
|
||||
|
||||
|
||||
struct ModuleGeometry{
|
||||
int x{};
|
||||
int y{};
|
||||
/**
|
||||
* @brief Class to hold the geometry of a module. Where pixel 0 is located and
|
||||
* the size of the module
|
||||
*/
|
||||
struct ModuleGeometry {
|
||||
int origin_x{};
|
||||
int origin_y{};
|
||||
int height{};
|
||||
int width{};
|
||||
int row_index{};
|
||||
int col_index{};
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Class to hold the geometry of a detector. Number of modules, their
|
||||
* size and where pixel 0 for each module is located
|
||||
*/
|
||||
struct DetectorGeometry {
|
||||
int modules_x{};
|
||||
int modules_y{};
|
||||
int pixels_x{};
|
||||
int pixels_y{};
|
||||
int module_gap_row{};
|
||||
int module_gap_col{};
|
||||
std::vector<ModuleGeometry> module_pixel_0;
|
||||
|
||||
using dynamic_shape = std::vector<int64_t>;
|
||||
auto size() const { return module_pixel_0.size(); }
|
||||
};
|
||||
|
||||
//TODO! Can we uniform enums between the libraries?
|
||||
struct ROI {
|
||||
ssize_t xmin{};
|
||||
ssize_t xmax{};
|
||||
ssize_t ymin{};
|
||||
ssize_t ymax{};
|
||||
|
||||
ssize_t height() const { return ymax - ymin; }
|
||||
ssize_t width() const { return xmax - xmin; }
|
||||
bool contains(ssize_t x, ssize_t y) const {
|
||||
return x >= xmin && x < xmax && y >= ymin && y < ymax;
|
||||
}
|
||||
};
|
||||
|
||||
using dynamic_shape = std::vector<ssize_t>;
|
||||
|
||||
// TODO! Can we uniform enums between the libraries?
|
||||
|
||||
/**
|
||||
* @brief Enum class to identify different detectors.
|
||||
* @brief Enum class to identify different detectors.
|
||||
* The values are the same as in slsDetectorPackage
|
||||
* Different spelling to avoid confusion with the slsDetectorPackage
|
||||
*/
|
||||
enum class DetectorType {
|
||||
//Standard detectors match the enum values from slsDetectorPackage
|
||||
// Standard detectors match the enum values from slsDetectorPackage
|
||||
Generic,
|
||||
Eiger,
|
||||
Gotthard,
|
||||
@ -208,25 +241,21 @@ enum class DetectorType {
|
||||
Gotthard2,
|
||||
Xilinx_ChipTestBoard,
|
||||
|
||||
//Additional detectors used for defining processing. Variants of the standard ones.
|
||||
Moench03=100,
|
||||
// Additional detectors used for defining processing. Variants of the
|
||||
// standard ones.
|
||||
Moench03 = 100,
|
||||
Moench03_old,
|
||||
Unknown
|
||||
};
|
||||
|
||||
enum class TimingMode { Auto, Trigger };
|
||||
enum class FrameDiscardPolicy { NoDiscard, Discard, DiscardPartial };
|
||||
|
||||
template <class T> T StringTo(const std::string &arg) { return T(arg); }
|
||||
|
||||
template <class T> std::string ToString(T arg) { return T(arg); }
|
||||
|
||||
template <> DetectorType StringTo(const std::string & /*name*/);
|
||||
template <> std::string ToString(DetectorType arg);
|
||||
|
||||
template <> TimingMode StringTo(const std::string & /*mode*/);
|
||||
|
||||
template <> FrameDiscardPolicy StringTo(const std::string & /*mode*/);
|
||||
enum class BurstMode {
|
||||
Burst_Interal,
|
||||
Burst_External,
|
||||
Continuous_Internal,
|
||||
Continuous_External
|
||||
};
|
||||
|
||||
using DataTypeVariants = std::variant<uint16_t, uint32_t>;
|
||||
|
||||
|
15
include/aare/geo_helpers.hpp
Normal file
15
include/aare/geo_helpers.hpp
Normal file
@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
#include "aare/RawMasterFile.hpp" //ROI refactor away
|
||||
#include "aare/defs.hpp"
|
||||
namespace aare {
|
||||
|
||||
/**
|
||||
* @brief Update the detector geometry given a region of interest
|
||||
*
|
||||
* @param geo
|
||||
* @param roi
|
||||
* @return DetectorGeometry
|
||||
*/
|
||||
DetectorGeometry update_geometry_with_roi(DetectorGeometry geo, ROI roi);
|
||||
|
||||
} // namespace aare
|
141
include/aare/logger.hpp
Normal file
141
include/aare/logger.hpp
Normal file
@ -0,0 +1,141 @@
|
||||
#pragma once
|
||||
/*Utility to log to console*/
|
||||
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <sys/time.h>
|
||||
|
||||
namespace aare {
|
||||
|
||||
#define RED "\x1b[31m"
|
||||
#define GREEN "\x1b[32m"
|
||||
#define YELLOW "\x1b[33m"
|
||||
#define BLUE "\x1b[34m"
|
||||
#define MAGENTA "\x1b[35m"
|
||||
#define CYAN "\x1b[36m"
|
||||
#define GRAY "\x1b[37m"
|
||||
#define DARKGRAY "\x1b[30m"
|
||||
|
||||
#define BG_BLACK "\x1b[48;5;232m"
|
||||
#define BG_RED "\x1b[41m"
|
||||
#define BG_GREEN "\x1b[42m"
|
||||
#define BG_YELLOW "\x1b[43m"
|
||||
#define BG_BLUE "\x1b[44m"
|
||||
#define BG_MAGENTA "\x1b[45m"
|
||||
#define BG_CYAN "\x1b[46m"
|
||||
#define RESET "\x1b[0m"
|
||||
#define BOLD "\x1b[1m"
|
||||
|
||||
enum TLogLevel {
|
||||
logERROR,
|
||||
logWARNING,
|
||||
logINFOBLUE,
|
||||
logINFOGREEN,
|
||||
logINFORED,
|
||||
logINFOCYAN,
|
||||
logINFOMAGENTA,
|
||||
logINFO,
|
||||
logDEBUG, // constructors, destructors etc. should still give too much
|
||||
// output
|
||||
logDEBUG1,
|
||||
logDEBUG2,
|
||||
logDEBUG3,
|
||||
logDEBUG4,
|
||||
logDEBUG5
|
||||
};
|
||||
|
||||
// Compiler should optimize away anything below this value
|
||||
#ifndef AARE_LOG_LEVEL
|
||||
#define AARE_LOG_LEVEL \
|
||||
"LOG LEVEL NOT SET IN CMAKE" // This is configured in the main
|
||||
// CMakeLists.txt
|
||||
#endif
|
||||
|
||||
#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_AT__ \
|
||||
std::string(__SHORT_FORM_OF_FILE__) + std::string("::") + \
|
||||
std::string(__func__) + std::string("(): ")
|
||||
|
||||
class Logger {
|
||||
std::ostringstream os;
|
||||
TLogLevel m_level = AARE_LOG_LEVEL;
|
||||
|
||||
public:
|
||||
Logger() = default;
|
||||
explicit Logger(TLogLevel level) : m_level(level){};
|
||||
~Logger() {
|
||||
// output in the destructor to allow for << syntax
|
||||
os << RESET << '\n';
|
||||
std::clog << os.str() << std::flush; // Single write
|
||||
}
|
||||
|
||||
static TLogLevel &
|
||||
ReportingLevel() { // singelton eeh TODO! Do we need a runtime option?
|
||||
static TLogLevel reportingLevel = logDEBUG5;
|
||||
return reportingLevel;
|
||||
}
|
||||
|
||||
// Danger this buffer need as many elements as TLogLevel
|
||||
static const char *Color(TLogLevel level) noexcept {
|
||||
static const char *const colors[] = {
|
||||
RED BOLD, YELLOW BOLD, BLUE, GREEN, RED, CYAN, MAGENTA,
|
||||
RESET, RESET, RESET, RESET, RESET, RESET, RESET};
|
||||
// out of bounds
|
||||
if (level < 0 || level >= sizeof(colors) / sizeof(colors[0])) {
|
||||
return RESET;
|
||||
}
|
||||
return colors[level];
|
||||
}
|
||||
|
||||
// Danger this buffer need as many elements as TLogLevel
|
||||
static std::string ToString(TLogLevel level) {
|
||||
static const char *const buffer[] = {
|
||||
"ERROR", "WARNING", "INFO", "INFO", "INFO",
|
||||
"INFO", "INFO", "INFO", "DEBUG", "DEBUG1",
|
||||
"DEBUG2", "DEBUG3", "DEBUG4", "DEBUG5"};
|
||||
// out of bounds
|
||||
if (level < 0 || level >= sizeof(buffer) / sizeof(buffer[0])) {
|
||||
return "UNKNOWN";
|
||||
}
|
||||
return buffer[level];
|
||||
}
|
||||
|
||||
std::ostringstream &Get() {
|
||||
os << Color(m_level) << "- " << Timestamp() << " " << ToString(m_level)
|
||||
<< ": ";
|
||||
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;
|
||||
}
|
||||
};
|
||||
|
||||
// TODO! Do we need to keep the runtime option?
|
||||
#define LOG(level) \
|
||||
if (level > AARE_LOG_LEVEL) \
|
||||
; \
|
||||
else if (level > aare::Logger::ReportingLevel()) \
|
||||
; \
|
||||
else \
|
||||
aare::Logger(level).Get()
|
||||
|
||||
} // namespace aare
|
51
include/aare/scan_parameters.hpp
Normal file
51
include/aare/scan_parameters.hpp
Normal file
@ -0,0 +1,51 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
|
||||
namespace aare {
|
||||
|
||||
class ScanParameters {
|
||||
bool m_enabled = false;
|
||||
std::string m_dac;
|
||||
int m_start = 0;
|
||||
int m_stop = 0;
|
||||
int m_step = 0;
|
||||
// ns m_dac_settle_time{0};
|
||||
// TODO! add settleTime, requires string to time conversion
|
||||
|
||||
public:
|
||||
// "[enabled\ndac dac 4\nstart 500\nstop 2200\nstep 5\nsettleTime 100us\n]"
|
||||
// TODO: use StringTo<ScanParameters> and move this to to_string
|
||||
// add ways of setting the members of the class
|
||||
|
||||
ScanParameters(const std::string &par) {
|
||||
std::istringstream iss(par.substr(1, par.size() - 2));
|
||||
std::string line;
|
||||
while (std::getline(iss, line)) {
|
||||
if (line == "enabled") {
|
||||
m_enabled = true;
|
||||
} else if (line.find("dac") != std::string::npos) {
|
||||
m_dac = line.substr(4);
|
||||
} else if (line.find("start") != std::string::npos) {
|
||||
m_start = std::stoi(line.substr(6));
|
||||
} else if (line.find("stop") != std::string::npos) {
|
||||
m_stop = std::stoi(line.substr(5));
|
||||
} else if (line.find("step") != std::string::npos) {
|
||||
m_step = std::stoi(line.substr(5));
|
||||
}
|
||||
}
|
||||
};
|
||||
ScanParameters() = default;
|
||||
ScanParameters(const ScanParameters &) = default;
|
||||
ScanParameters &operator=(const ScanParameters &) = default;
|
||||
ScanParameters(ScanParameters &&) = default;
|
||||
int start() const { return m_start; };
|
||||
int stop() const { return m_stop; };
|
||||
int step() const { return m_step; };
|
||||
const std::string &dac() const { return m_dac; };
|
||||
bool enabled() const { return m_enabled; };
|
||||
void increment_stop() { m_stop += 1; };
|
||||
};
|
||||
|
||||
} // namespace aare
|
11
include/aare/string_utils.hpp
Normal file
11
include/aare/string_utils.hpp
Normal file
@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace aare {
|
||||
|
||||
std::string RemoveUnit(std::string &str);
|
||||
|
||||
void TrimWhiteSpaces(std::string &s);
|
||||
|
||||
} // namespace aare
|
288
include/aare/to_string.hpp
Normal file
288
include/aare/to_string.hpp
Normal file
@ -0,0 +1,288 @@
|
||||
#pragma once
|
||||
|
||||
#include "aare/defs.hpp"
|
||||
#include "aare/scan_parameters.hpp"
|
||||
#include "aare/string_utils.hpp"
|
||||
|
||||
#include <optional>
|
||||
#include <chrono>
|
||||
|
||||
|
||||
namespace aare {
|
||||
|
||||
// generic
|
||||
template <class T, typename = std::enable_if_t<!is_duration<T>::value>>
|
||||
std::string ToString(T arg) {
|
||||
return T(arg);
|
||||
}
|
||||
|
||||
template <typename T,
|
||||
std::enable_if_t<!is_duration<T>::value && !is_container<T>::value,
|
||||
int> = 0>
|
||||
T StringTo(const std::string &arg) {
|
||||
return T(arg);
|
||||
}
|
||||
|
||||
// time
|
||||
|
||||
/** Convert std::chrono::duration with specified output unit */
|
||||
template <typename T, typename Rep = double>
|
||||
typename std::enable_if<is_duration<T>::value, std::string>::type
|
||||
ToString(T t, const std::string &unit) {
|
||||
using std::chrono::duration;
|
||||
using std::chrono::duration_cast;
|
||||
std::ostringstream os;
|
||||
if (unit == "ns")
|
||||
os << duration_cast<duration<Rep, std::nano>>(t).count() << unit;
|
||||
else if (unit == "us")
|
||||
os << duration_cast<duration<Rep, std::micro>>(t).count() << unit;
|
||||
else if (unit == "ms")
|
||||
os << duration_cast<duration<Rep, std::milli>>(t).count() << unit;
|
||||
else if (unit == "s")
|
||||
os << duration_cast<duration<Rep>>(t).count() << unit;
|
||||
else
|
||||
throw std::runtime_error("Unknown unit: " + unit);
|
||||
return os.str();
|
||||
}
|
||||
|
||||
/** Convert std::chrono::duration automatically selecting the unit */
|
||||
template <typename From>
|
||||
typename std::enable_if<is_duration<From>::value, std::string>::type
|
||||
ToString(From t) {
|
||||
|
||||
using std::chrono::abs;
|
||||
using std::chrono::duration_cast;
|
||||
using std::chrono::microseconds;
|
||||
using std::chrono::milliseconds;
|
||||
using std::chrono::nanoseconds;
|
||||
auto tns = duration_cast<nanoseconds>(t);
|
||||
if (abs(tns) < microseconds(1)) {
|
||||
return ToString(tns, "ns");
|
||||
} else if (abs(tns) < milliseconds(1)) {
|
||||
return ToString(tns, "us");
|
||||
} else if (abs(tns) < milliseconds(99)) {
|
||||
return ToString(tns, "ms");
|
||||
} else {
|
||||
return ToString(tns, "s");
|
||||
}
|
||||
}
|
||||
template <class Rep, class Period>
|
||||
std::ostream &operator<<(std::ostream &os,
|
||||
const std::chrono::duration<Rep, Period> &d) {
|
||||
return os << ToString(d);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T StringTo(const std::string &t, const std::string &unit) {
|
||||
double tval{0};
|
||||
try {
|
||||
tval = std::stod(t);
|
||||
} catch (const std::invalid_argument &e) {
|
||||
throw std::invalid_argument("[ERROR] Could not convert string to time");
|
||||
}
|
||||
|
||||
using std::chrono::duration;
|
||||
using std::chrono::duration_cast;
|
||||
if (unit == "ns") {
|
||||
return duration_cast<T>(duration<double, std::nano>(tval));
|
||||
} else if (unit == "us") {
|
||||
return duration_cast<T>(duration<double, std::micro>(tval));
|
||||
} else if (unit == "ms") {
|
||||
return duration_cast<T>(duration<double, std::milli>(tval));
|
||||
} else if (unit == "s" || unit.empty()) {
|
||||
return duration_cast<T>(std::chrono::duration<double>(tval));
|
||||
} else {
|
||||
throw std::invalid_argument("[ERROR] Invalid unit in conversion from "
|
||||
"string to std::chrono::duration");
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, std::enable_if_t<is_duration<T>::value, int> = 0>
|
||||
T StringTo(const std::string &t) {
|
||||
std::string tmp{t};
|
||||
auto unit = RemoveUnit(tmp);
|
||||
return StringTo<T>(tmp, unit);
|
||||
}
|
||||
|
||||
template <> inline bool StringTo(const std::string &s) {
|
||||
int i = std::stoi(s, nullptr, 10);
|
||||
switch (i) {
|
||||
case 0:
|
||||
return false;
|
||||
case 1:
|
||||
return true;
|
||||
default:
|
||||
throw std::runtime_error("Unknown boolean. Expecting be 0 or 1.");
|
||||
}
|
||||
}
|
||||
|
||||
template <> inline uint8_t StringTo(const std::string &s) {
|
||||
int base = s.find("0x") != std::string::npos ? 16 : 10;
|
||||
int value = std::stoi(s, nullptr, base);
|
||||
if (value < std::numeric_limits<uint8_t>::min() ||
|
||||
value > std::numeric_limits<uint8_t>::max()) {
|
||||
throw std::runtime_error("Cannot scan uint8_t from string '" + s +
|
||||
"'. Value must be in range 0 - 255.");
|
||||
}
|
||||
return static_cast<uint8_t>(value);
|
||||
}
|
||||
|
||||
template <> inline uint16_t StringTo(const std::string &s) {
|
||||
int base = s.find("0x") != std::string::npos ? 16 : 10;
|
||||
int value = std::stoi(s, nullptr, base);
|
||||
if (value < std::numeric_limits<uint16_t>::min() ||
|
||||
value > std::numeric_limits<uint16_t>::max()) {
|
||||
throw std::runtime_error("Cannot scan uint16_t from string '" + s +
|
||||
"'. Value must be in range 0 - 65535.");
|
||||
}
|
||||
return static_cast<uint16_t>(value);
|
||||
}
|
||||
|
||||
template <> inline uint32_t StringTo(const std::string &s) {
|
||||
int base = s.find("0x") != std::string::npos ? 16 : 10;
|
||||
return std::stoul(s, nullptr, base);
|
||||
}
|
||||
|
||||
template <> inline uint64_t StringTo(const std::string &s) {
|
||||
int base = s.find("0x") != std::string::npos ? 16 : 10;
|
||||
return std::stoull(s, nullptr, base);
|
||||
}
|
||||
|
||||
template <> inline int StringTo(const std::string &s) {
|
||||
int base = s.find("0x") != std::string::npos ? 16 : 10;
|
||||
return std::stoi(s, nullptr, base);
|
||||
}
|
||||
|
||||
/*template <> inline size_t StringTo(const std::string &s) {
|
||||
int base = s.find("0x") != std::string::npos ? 16 : 10;
|
||||
return std::stoull(s, nullptr, base);
|
||||
}*/
|
||||
|
||||
// vector
|
||||
template <typename T> std::string ToString(const std::vector<T> &vec) {
|
||||
std::ostringstream oss;
|
||||
oss << "[";
|
||||
for (size_t i = 0; i < vec.size(); ++i) {
|
||||
oss << vec[i];
|
||||
if (i != vec.size() - 1)
|
||||
oss << ", ";
|
||||
}
|
||||
oss << "]";
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::ostream &operator<<(std::ostream &os, const std::vector<T> &v) {
|
||||
return os << ToString(v);
|
||||
}
|
||||
|
||||
template <typename Container,
|
||||
std::enable_if_t<is_container<Container>::value &&
|
||||
!is_std_string_v<Container> /*&&
|
||||
!is_map_v<Container>*/
|
||||
,
|
||||
int> = 0>
|
||||
Container StringTo(const std::string &s) {
|
||||
using Value = typename Container::value_type;
|
||||
|
||||
// strip outer brackets
|
||||
std::string str = s;
|
||||
str.erase(
|
||||
std::remove_if(str.begin(), str.end(),
|
||||
[](unsigned char c) { return c == '[' || c == ']'; }),
|
||||
str.end());
|
||||
|
||||
std::stringstream ss(str);
|
||||
std::string item;
|
||||
Container result;
|
||||
|
||||
while (std::getline(ss, item, ',')) {
|
||||
TrimWhiteSpaces(item);
|
||||
if (!item.empty()) {
|
||||
result.push_back(StringTo<Value>(item));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// map
|
||||
template <typename KeyType, typename ValueType>
|
||||
std::string ToString(const std::map<KeyType, ValueType> &m) {
|
||||
std::ostringstream os;
|
||||
os << '{';
|
||||
if (!m.empty()) {
|
||||
auto it = m.cbegin();
|
||||
os << ToString(it->first) << ": " << ToString(it->second);
|
||||
it++;
|
||||
while (it != m.cend()) {
|
||||
os << ", " << ToString(it->first) << ": " << ToString(it->second);
|
||||
it++;
|
||||
}
|
||||
}
|
||||
os << '}';
|
||||
return os.str();
|
||||
}
|
||||
|
||||
template <>
|
||||
inline std::map<std::string, std::string> StringTo(const std::string &s) {
|
||||
std::map<std::string, std::string> result;
|
||||
std::string str = s;
|
||||
|
||||
// Remove outer braces if present
|
||||
if (!str.empty() && str.front() == '{' && str.back() == '}') {
|
||||
str = str.substr(1, str.size() - 2);
|
||||
}
|
||||
|
||||
std::stringstream ss(str);
|
||||
std::string item;
|
||||
|
||||
while (std::getline(ss, item, ',')) {
|
||||
auto colon_pos = item.find(':');
|
||||
if (colon_pos == std::string::npos)
|
||||
throw std::runtime_error("Missing ':' in item: " + item);
|
||||
|
||||
std::string key = item.substr(0, colon_pos);
|
||||
std::string value = item.substr(colon_pos + 1);
|
||||
|
||||
TrimWhiteSpaces(key);
|
||||
TrimWhiteSpaces(value);
|
||||
|
||||
result[key] = value;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// optional
|
||||
template <class T> std::string ToString(const std::optional<T> &opt) {
|
||||
return opt ? ToString(*opt) : "nullopt";
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::ostream &operator<<(std::ostream &os, const std::optional<T> &opt) {
|
||||
if (opt)
|
||||
os << *opt;
|
||||
else
|
||||
os << "nullopt";
|
||||
return os;
|
||||
}
|
||||
|
||||
// enums
|
||||
template <> std::string ToString(DetectorType arg);
|
||||
template <> DetectorType StringTo(const std::string & /*name*/);
|
||||
|
||||
template <> std::string ToString(TimingMode arg);
|
||||
template <> TimingMode StringTo(const std::string & /*mode*/);
|
||||
|
||||
template <> std::string ToString(FrameDiscardPolicy arg);
|
||||
template <> FrameDiscardPolicy StringTo(const std::string & /*mode*/);
|
||||
|
||||
template <> std::string ToString(BurstMode arg);
|
||||
template <> BurstMode StringTo(const std::string & /*mode*/);
|
||||
|
||||
template <> std::string ToString(ROI arg);
|
||||
std::ostream &operator<<(std::ostream &os, const ROI &roi);
|
||||
|
||||
template <> std::string ToString(ScanParameters arg);
|
||||
std::ostream &operator<<(std::ostream &os, const ScanParameters &r);
|
||||
|
||||
} // namespace aare
|
72
include/aare/type_traits.hpp
Normal file
72
include/aare/type_traits.hpp
Normal file
@ -0,0 +1,72 @@
|
||||
#pragma once
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
namespace aare {
|
||||
|
||||
/**
|
||||
* Type trait to check if a template parameter is a std::chrono::duration
|
||||
*/
|
||||
|
||||
template <typename T, typename _ = void>
|
||||
struct is_duration : std::false_type {};
|
||||
|
||||
template <typename... Ts> struct is_duration_helper {};
|
||||
|
||||
template <typename T>
|
||||
struct is_duration<T,
|
||||
typename std::conditional<
|
||||
false,
|
||||
is_duration_helper<typename T::rep, typename T::period,
|
||||
decltype(std::declval<T>().min()),
|
||||
decltype(std::declval<T>().max()),
|
||||
decltype(std::declval<T>().zero())>,
|
||||
void>::type> : public std::true_type {};
|
||||
|
||||
/**
|
||||
* Type trait to evaluate if template parameter is
|
||||
* complying with a standard container
|
||||
*/
|
||||
template <typename T, typename _ = void>
|
||||
struct is_container : std::false_type {};
|
||||
|
||||
template <typename... Ts> struct is_container_helper {};
|
||||
|
||||
template <typename T>
|
||||
struct is_container<
|
||||
T, typename std::conditional<
|
||||
false,
|
||||
is_container_helper<
|
||||
typename std::remove_reference<T>::type::value_type,
|
||||
typename std::remove_reference<T>::type::size_type,
|
||||
typename std::remove_reference<T>::type::iterator,
|
||||
typename std::remove_reference<T>::type::const_iterator,
|
||||
decltype(std::declval<T>().size()),
|
||||
decltype(std::declval<T>().begin()),
|
||||
decltype(std::declval<T>().end()),
|
||||
decltype(std::declval<T>().cbegin()),
|
||||
decltype(std::declval<T>().cend()),
|
||||
decltype(std::declval<T>().empty())>,
|
||||
void>::type> : public std::true_type {};
|
||||
|
||||
/**
|
||||
* Type trait to evaluate if template parameter is
|
||||
* complying with a std::string
|
||||
*/
|
||||
template <typename T>
|
||||
inline constexpr bool is_std_string_v =
|
||||
std::is_same_v<std::decay_t<T>, std::string>;
|
||||
|
||||
/**
|
||||
* Type trait to evaluate if template parameter is
|
||||
* complying with std::map
|
||||
*/
|
||||
template <typename T> struct is_map : std::false_type {};
|
||||
|
||||
template <typename K, typename V, typename... Args>
|
||||
struct is_map<std::map<K, V, Args...>> : std::true_type {};
|
||||
|
||||
template <typename T>
|
||||
inline constexpr bool is_map_v = is_map<std::decay_t<T>>::value;
|
||||
|
||||
} // namespace aare
|
12
include/aare/utils/ifstream_helpers.hpp
Normal file
12
include/aare/utils/ifstream_helpers.hpp
Normal file
@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
namespace aare {
|
||||
|
||||
/**
|
||||
* @brief Get the error message from an ifstream object
|
||||
*/
|
||||
std::string ifstream_error_msg(std::ifstream &ifs);
|
||||
|
||||
} // namespace aare
|
18
include/aare/utils/par.hpp
Normal file
18
include/aare/utils/par.hpp
Normal file
@ -0,0 +1,18 @@
|
||||
#include <thread>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace aare {
|
||||
|
||||
template <typename F>
|
||||
void RunInParallel(F func, const std::vector<std::pair<int, int>> &tasks) {
|
||||
// auto tasks = split_task(0, y.shape(0), n_threads);
|
||||
std::vector<std::thread> threads;
|
||||
for (auto &task : tasks) {
|
||||
threads.push_back(std::thread(func, task.first, task.second));
|
||||
}
|
||||
for (auto &thread : threads) {
|
||||
thread.join();
|
||||
}
|
||||
}
|
||||
} // namespace aare
|
8
include/aare/utils/task.hpp
Normal file
8
include/aare/utils/task.hpp
Normal file
@ -0,0 +1,8 @@
|
||||
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace aare {
|
||||
std::vector<std::pair<int, int>> split_task(int first, int last, int n_threads);
|
||||
|
||||
} // namespace aare
|
18
patches/libzmq_cmake_version.patch
Normal file
18
patches/libzmq_cmake_version.patch
Normal file
@ -0,0 +1,18 @@
|
||||
diff --git a/CMakeLists.txt b/CMakeLists.txt
|
||||
index dd3d8eb9..c0187747 100644
|
||||
--- a/CMakeLists.txt
|
||||
+++ b/CMakeLists.txt
|
||||
@@ -1,11 +1,8 @@
|
||||
# CMake build script for ZeroMQ
|
||||
project(ZeroMQ)
|
||||
|
||||
-if(${CMAKE_SYSTEM_NAME} STREQUAL Darwin)
|
||||
- cmake_minimum_required(VERSION 3.0.2)
|
||||
-else()
|
||||
- cmake_minimum_required(VERSION 2.8.12)
|
||||
-endif()
|
||||
+cmake_minimum_required(VERSION 3.15)
|
||||
+message(STATUS "Patched cmake version")
|
||||
|
||||
include(CheckIncludeFiles)
|
||||
include(CheckCCompilerFlag)
|
13
patches/lmfit.patch
Normal file
13
patches/lmfit.patch
Normal file
@ -0,0 +1,13 @@
|
||||
diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt
|
||||
index 4efb7ed..6533660 100644
|
||||
--- a/lib/CMakeLists.txt
|
||||
+++ b/lib/CMakeLists.txt
|
||||
@@ -11,7 +11,7 @@ target_compile_definitions(${lib} PRIVATE "LMFIT_EXPORT") # for Windows DLL expo
|
||||
|
||||
target_include_directories(${lib}
|
||||
PUBLIC
|
||||
- $<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}/>
|
||||
+ $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/>
|
||||
$<INSTALL_INTERFACE:include/>
|
||||
)
|
||||
|
@ -1,15 +1,41 @@
|
||||
[tool.scikit-build.metadata.version]
|
||||
provider = "scikit_build_core.metadata.regex"
|
||||
input = "VERSION"
|
||||
regex = '^(?P<version>\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 = "2024.12.16.dev0"
|
||||
dynamic = ["version"]
|
||||
requires-python = ">=3.11"
|
||||
dependencies = [
|
||||
"numpy",
|
||||
"matplotlib",
|
||||
]
|
||||
|
||||
|
||||
[tool.cibuildwheel]
|
||||
|
||||
build = "cp{311,312,313}-manylinux_x86_64"
|
||||
|
||||
|
||||
|
||||
|
||||
[tool.scikit-build]
|
||||
cmake.verbose = true
|
||||
build.verbose = true
|
||||
cmake.build-type = "Release"
|
||||
install.components = ["python"]
|
||||
|
||||
[tool.scikit-build.cmake.define]
|
||||
AARE_PYTHON_BINDINGS = "ON"
|
||||
AARE_SYSTEM_LIBRARIES = "ON"
|
||||
AARE_INSTALL_PYTHONEXT = "ON"
|
||||
AARE_INSTALL_PYTHONEXT = "ON"
|
||||
|
||||
|
||||
[tool.pytest.ini_options]
|
||||
markers = [
|
||||
"files: marks tests that need additional data (deselect with '-m \"not files\"')",
|
||||
]
|
@ -1,12 +1,13 @@
|
||||
|
||||
find_package (Python 3.10 COMPONENTS Interpreter Development REQUIRED)
|
||||
find_package (Python 3.10 COMPONENTS Interpreter Development.Module REQUIRED)
|
||||
set(PYBIND11_FINDPYTHON ON) # Needed for RH8
|
||||
|
||||
# Download or find pybind11 depending on configuration
|
||||
if(AARE_FETCH_PYBIND11)
|
||||
FetchContent_Declare(
|
||||
pybind11
|
||||
GIT_REPOSITORY https://github.com/pybind/pybind11
|
||||
GIT_TAG v2.13.0
|
||||
GIT_TAG v2.13.6
|
||||
)
|
||||
FetchContent_MakeAvailable(pybind11)
|
||||
else()
|
||||
@ -28,12 +29,17 @@ target_link_libraries(_aare PRIVATE aare_core aare_compiler_flags)
|
||||
set( PYTHON_FILES
|
||||
aare/__init__.py
|
||||
aare/CtbRawFile.py
|
||||
aare/ClusterFinder.py
|
||||
aare/ClusterVector.py
|
||||
|
||||
aare/func.py
|
||||
aare/RawFile.py
|
||||
aare/transform.py
|
||||
aare/ScanParameters.py
|
||||
aare/utils.py
|
||||
)
|
||||
|
||||
|
||||
# Copy the python files to the build directory
|
||||
foreach(FILE ${PYTHON_FILES})
|
||||
configure_file(${FILE} ${CMAKE_BINARY_DIR}/${FILE} )
|
||||
@ -43,17 +49,30 @@ set_target_properties(_aare PROPERTIES
|
||||
LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/aare
|
||||
)
|
||||
|
||||
set(PYTHON_EXAMPLES
|
||||
examples/play.py
|
||||
examples/fits.py
|
||||
)
|
||||
|
||||
|
||||
# Copy the examples/scripts to the build directory
|
||||
configure_file(examples/play.py ${CMAKE_BINARY_DIR}/play.py)
|
||||
# Copy the python examples to the build directory
|
||||
foreach(FILE ${PYTHON_EXAMPLES})
|
||||
configure_file(${FILE} ${CMAKE_BINARY_DIR}/${FILE} )
|
||||
message(STATUS "Copying ${FILE} to ${CMAKE_BINARY_DIR}/${FILE}")
|
||||
endforeach(FILE ${PYTHON_EXAMPLES})
|
||||
|
||||
|
||||
if(AARE_INSTALL_PYTHONEXT)
|
||||
install(TARGETS _aare
|
||||
install(
|
||||
TARGETS _aare
|
||||
EXPORT "${TARGETS_EXPORT_NAME}"
|
||||
LIBRARY DESTINATION aare
|
||||
COMPONENT python
|
||||
)
|
||||
|
||||
install(FILES ${PYTHON_FILES} DESTINATION aare)
|
||||
install(
|
||||
FILES ${PYTHON_FILES}
|
||||
DESTINATION aare
|
||||
COMPONENT python
|
||||
)
|
||||
endif()
|
84
python/aare/ClusterFinder.py
Normal file
84
python/aare/ClusterFinder.py
Normal file
@ -0,0 +1,84 @@
|
||||
|
||||
# from ._aare import ClusterFinder_Cluster3x3i, ClusterFinder_Cluster2x2i, ClusterFinderMT_Cluster3x3i, ClusterFinderMT_Cluster2x2i, ClusterCollector_Cluster3x3i, ClusterCollector_Cluster2x2i
|
||||
|
||||
|
||||
# from ._aare import ClusterFileSink_Cluster3x3i, ClusterFileSink_Cluster2x2i
|
||||
|
||||
from . import _aare
|
||||
import numpy as np
|
||||
|
||||
_supported_cluster_sizes = [(2,2), (3,3), (5,5), (7,7), (9,9),]
|
||||
|
||||
# def _get_class()
|
||||
|
||||
def _type_to_char(dtype):
|
||||
if dtype == np.int32:
|
||||
return 'i'
|
||||
elif dtype == np.float32:
|
||||
return 'f'
|
||||
elif dtype == np.float64:
|
||||
return 'd'
|
||||
else:
|
||||
raise ValueError(f"Unsupported dtype: {dtype}. Only np.int32, np.float32, and np.float64 are supported.")
|
||||
|
||||
def _get_class(name, cluster_size, dtype):
|
||||
"""
|
||||
Helper function to get the class based on the name, cluster size, and dtype.
|
||||
"""
|
||||
try:
|
||||
class_name = f"{name}_Cluster{cluster_size[0]}x{cluster_size[1]}{_type_to_char(dtype)}"
|
||||
cls = getattr(_aare, class_name)
|
||||
except AttributeError:
|
||||
raise ValueError(f"Unsupported combination of type and cluster size: {dtype}/{cluster_size} when requesting {class_name}")
|
||||
return cls
|
||||
|
||||
|
||||
|
||||
def ClusterFinder(image_size, cluster_size, n_sigma=5, dtype = np.int32, capacity = 1024):
|
||||
"""
|
||||
Factory function to create a ClusterFinder object. Provides a cleaner syntax for
|
||||
the templated ClusterFinder in C++.
|
||||
"""
|
||||
cls = _get_class("ClusterFinder", cluster_size, dtype)
|
||||
return cls(image_size, n_sigma=n_sigma, capacity=capacity)
|
||||
|
||||
|
||||
|
||||
def ClusterFinderMT(image_size, cluster_size = (3,3), dtype=np.int32, n_sigma=5, capacity = 1024, n_threads = 3):
|
||||
"""
|
||||
Factory function to create a ClusterFinderMT object. Provides a cleaner syntax for
|
||||
the templated ClusterFinderMT in C++.
|
||||
"""
|
||||
|
||||
cls = _get_class("ClusterFinderMT", cluster_size, dtype)
|
||||
return cls(image_size, n_sigma=n_sigma, capacity=capacity, n_threads=n_threads)
|
||||
|
||||
|
||||
|
||||
def ClusterCollector(clusterfindermt, cluster_size = (3,3), dtype=np.int32):
|
||||
"""
|
||||
Factory function to create a ClusterCollector object. Provides a cleaner syntax for
|
||||
the templated ClusterCollector in C++.
|
||||
"""
|
||||
|
||||
cls = _get_class("ClusterCollector", cluster_size, dtype)
|
||||
return cls(clusterfindermt)
|
||||
|
||||
def ClusterFileSink(clusterfindermt, cluster_file, dtype=np.int32):
|
||||
"""
|
||||
Factory function to create a ClusterCollector object. Provides a cleaner syntax for
|
||||
the templated ClusterCollector in C++.
|
||||
"""
|
||||
|
||||
cls = _get_class("ClusterFileSink", clusterfindermt.cluster_size, dtype)
|
||||
return cls(clusterfindermt, cluster_file)
|
||||
|
||||
|
||||
def ClusterFile(fname, cluster_size=(3,3), dtype=np.int32):
|
||||
"""
|
||||
Factory function to create a ClusterFile object. Provides a cleaner syntax for
|
||||
the templated ClusterFile in C++.
|
||||
"""
|
||||
|
||||
cls = _get_class("ClusterFile", cluster_size, dtype)
|
||||
return cls(fname)
|
11
python/aare/ClusterVector.py
Normal file
11
python/aare/ClusterVector.py
Normal file
@ -0,0 +1,11 @@
|
||||
|
||||
|
||||
from ._aare import ClusterVector_Cluster3x3i
|
||||
import numpy as np
|
||||
|
||||
def ClusterVector(cluster_size, dtype = np.int32):
|
||||
|
||||
if dtype == np.int32 and cluster_size == (3,3):
|
||||
return ClusterVector_Cluster3x3i()
|
||||
else:
|
||||
raise ValueError(f"Unsupported dtype: {dtype}. Only np.int32 is supported.")
|
66
python/aare/Hdf5File.py
Normal file
66
python/aare/Hdf5File.py
Normal file
@ -0,0 +1,66 @@
|
||||
from . import _aare
|
||||
import numpy as np
|
||||
#from .ScanParameters import ScanParameters
|
||||
|
||||
class Hdf5File(_aare.Hdf5File):
|
||||
def __init__(self, fname, chunk_size = 1):
|
||||
super().__init__(fname)
|
||||
self._chunk_size = chunk_size
|
||||
|
||||
|
||||
def read(self) -> tuple:
|
||||
"""Read the entire file.
|
||||
Seeks to the beginning of the file before reading.
|
||||
|
||||
Returns:
|
||||
tuple: header, data
|
||||
"""
|
||||
self.seek(0)
|
||||
return self.read_n(self.total_frames)
|
||||
|
||||
# @property
|
||||
# def scan_parameters(self):
|
||||
# """Return the scan parameters.
|
||||
|
||||
# Returns:
|
||||
# ScanParameters: Scan parameters.
|
||||
# """
|
||||
# return ScanParameters(self.master.scan_parameters)
|
||||
|
||||
@property
|
||||
def master(self):
|
||||
"""Return the master file.
|
||||
|
||||
Returns:
|
||||
Hdf5MasterFile: Master file.
|
||||
"""
|
||||
return super().master
|
||||
|
||||
def __len__(self) -> int:
|
||||
"""Return the number of frames in the file.
|
||||
|
||||
Returns:
|
||||
int: Number of frames in file.
|
||||
"""
|
||||
return super().frames_in_file
|
||||
|
||||
def __enter__(self):
|
||||
return self
|
||||
|
||||
def __exit__(self, exc_type, exc_value, traceback):
|
||||
pass
|
||||
|
||||
def __iter__(self):
|
||||
return self
|
||||
|
||||
def __next__(self):
|
||||
try:
|
||||
if self._chunk_size == 1:
|
||||
return self.read_frame()
|
||||
else:
|
||||
return self.read_n(self._chunk_size)
|
||||
|
||||
|
||||
except RuntimeError:
|
||||
# TODO! find a good way to check that we actually have the right exception
|
||||
raise StopIteration
|
@ -2,14 +2,32 @@
|
||||
from . import _aare
|
||||
|
||||
|
||||
from ._aare import File, RawMasterFile, RawSubFile
|
||||
from ._aare import Pedestal_d, Pedestal_f, ClusterFinder, VarClusterFinder
|
||||
from ._aare import File, RawMasterFile, RawSubFile, Hdf5MasterFile, JungfrauDataFile
|
||||
from ._aare import Pedestal_d, Pedestal_f, ClusterFinder_Cluster3x3i, VarClusterFinder
|
||||
from ._aare import DetectorType
|
||||
from ._aare import ClusterFile
|
||||
from ._aare import hitmap
|
||||
from ._aare import ROI
|
||||
|
||||
# from ._aare import ClusterFinderMT, ClusterCollector, ClusterFileSink, ClusterVector_i
|
||||
|
||||
from .ClusterFinder import ClusterFinder, ClusterCollector, ClusterFinderMT, ClusterFileSink, ClusterFile
|
||||
from .ClusterVector import ClusterVector
|
||||
|
||||
|
||||
from ._aare import fit_gaus, fit_pol1, fit_scurve, fit_scurve2
|
||||
from ._aare import Interpolator
|
||||
from ._aare import calculate_eta2
|
||||
|
||||
|
||||
from ._aare import apply_custom_weights
|
||||
|
||||
from .CtbRawFile import CtbRawFile
|
||||
from .RawFile import RawFile
|
||||
from .Hdf5File import Hdf5File
|
||||
from .ScanParameters import ScanParameters
|
||||
|
||||
from .utils import random_pixels, random_pixel
|
||||
from .utils import random_pixels, random_pixel, flat_list, add_colorbar
|
||||
|
||||
|
||||
#make functions available in the top level API
|
||||
from .func import *
|
||||
|
1
python/aare/func.py
Normal file
1
python/aare/func.py
Normal file
@ -0,0 +1 @@
|
||||
from ._aare import gaus, pol1, scurve, scurve2
|
@ -2,6 +2,14 @@ import numpy as np
|
||||
from . import _aare
|
||||
|
||||
|
||||
class AdcSar04Transform64to16:
|
||||
def __call__(self, data):
|
||||
return _aare.adc_sar_04_decode64to16(data)
|
||||
|
||||
class AdcSar05Transform64to16:
|
||||
def __call__(self, data):
|
||||
return _aare.adc_sar_05_decode64to16(data)
|
||||
|
||||
class Moench05Transform:
|
||||
#Could be moved to C++ without changing the interface
|
||||
def __init__(self):
|
||||
@ -45,4 +53,6 @@ class Matterhorn02Transform:
|
||||
moench05 = Moench05Transform()
|
||||
moench05_1g = Moench05Transform1g()
|
||||
moench05_old = Moench05TransformOld()
|
||||
matterhorn02 = Matterhorn02Transform()
|
||||
matterhorn02 = Matterhorn02Transform()
|
||||
adc_sar_04_64to16 = AdcSar04Transform64to16()
|
||||
adc_sar_05_64to16 = AdcSar05Transform64to16()
|
@ -1,4 +1,6 @@
|
||||
import numpy as np
|
||||
import matplotlib.pyplot as plt
|
||||
from mpl_toolkits.axes_grid1 import make_axes_locatable
|
||||
|
||||
def random_pixels(n_pixels, xmin=0, xmax=512, ymin=0, ymax=1024):
|
||||
"""Return a list of random pixels.
|
||||
@ -20,4 +22,15 @@ def random_pixel(xmin=0, xmax=512, ymin=0, ymax=1024):
|
||||
Returns:
|
||||
tuple: (row, col)
|
||||
"""
|
||||
return random_pixels(1, xmin, xmax, ymin, ymax)[0]
|
||||
return random_pixels(1, xmin, xmax, ymin, ymax)[0]
|
||||
|
||||
def flat_list(xss):
|
||||
"""Flatten a list of lists."""
|
||||
return [x for xs in xss for x in xs]
|
||||
|
||||
def add_colorbar(ax, im, size="5%", pad=0.05):
|
||||
"""Add a colorbar with the same height as the image."""
|
||||
divider = make_axes_locatable(ax)
|
||||
cax = divider.append_axes("right", size=size, pad=pad)
|
||||
plt.colorbar(im, cax=cax)
|
||||
return ax, im, cax
|
79
python/examples/fits.py
Normal file
79
python/examples/fits.py
Normal file
@ -0,0 +1,79 @@
|
||||
import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
from aare import fit_gaus, fit_pol1
|
||||
from aare import gaus, pol1
|
||||
|
||||
textpm = f"±" #
|
||||
textmu = f"μ" #
|
||||
textsigma = f"σ" #
|
||||
|
||||
|
||||
|
||||
# ================================= Gauss fit =================================
|
||||
# Parameters
|
||||
mu = np.random.uniform(1, 100) # Mean of Gaussian
|
||||
sigma = np.random.uniform(4, 20) # Standard deviation
|
||||
num_points = 10000 # Number of points for smooth distribution
|
||||
noise_sigma = 100
|
||||
|
||||
# Generate Gaussian distribution
|
||||
data = np.random.normal(mu, sigma, num_points)
|
||||
|
||||
# Generate errors for each point
|
||||
errors = np.abs(np.random.normal(0, sigma, num_points)) # Errors with mean 0, std 0.5
|
||||
|
||||
# Create subplot
|
||||
fig0, ax0 = plt.subplots(1, 1, num=0, figsize=(12, 8))
|
||||
|
||||
x = np.histogram(data, bins=30)[1][:-1] + 0.05
|
||||
y = np.histogram(data, bins=30)[0]
|
||||
yerr = errors[:30]
|
||||
|
||||
|
||||
# Add the errors as error bars in the step plot
|
||||
ax0.errorbar(x, y, yerr=yerr, fmt=". ", capsize=5)
|
||||
ax0.grid()
|
||||
|
||||
par, err = fit_gaus(x, y, yerr)
|
||||
print(par, err)
|
||||
|
||||
x = np.linspace(x[0], x[-1], 1000)
|
||||
ax0.plot(x, gaus(x, par), marker="")
|
||||
ax0.set(xlabel="x", ylabel="Counts", title=f"A0 = {par[0]:0.2f}{textpm}{err[0]:0.2f}\n"
|
||||
f"{textmu} = {par[1]:0.2f}{textpm}{err[1]:0.2f}\n"
|
||||
f"{textsigma} = {par[2]:0.2f}{textpm}{err[2]:0.2f}\n"
|
||||
f"(init: {textmu}: {mu:0.2f}, {textsigma}: {sigma:0.2f})")
|
||||
fig0.tight_layout()
|
||||
|
||||
|
||||
|
||||
# ================================= pol1 fit =================================
|
||||
# Parameters
|
||||
n_points = 40
|
||||
|
||||
# Generate random slope and intercept (origin)
|
||||
slope = np.random.uniform(-10, 10) # Random slope between 0.5 and 2.0
|
||||
intercept = np.random.uniform(-10, 10) # Random intercept between -10 and 10
|
||||
|
||||
# Generate random x values
|
||||
x_values = np.random.uniform(-10, 10, n_points)
|
||||
|
||||
# Calculate y values based on the linear function y = mx + b + error
|
||||
errors = np.abs(np.random.normal(0, np.random.uniform(1, 5), n_points))
|
||||
var_points = np.random.normal(0, np.random.uniform(0.1, 2), n_points)
|
||||
y_values = slope * x_values + intercept + var_points
|
||||
|
||||
fig1, ax1 = plt.subplots(1, 1, num=1, figsize=(12, 8))
|
||||
ax1.errorbar(x_values, y_values, yerr=errors, fmt=". ", capsize=5)
|
||||
par, err = fit_pol1(x_values, y_values, errors)
|
||||
|
||||
|
||||
x = np.linspace(np.min(x_values), np.max(x_values), 1000)
|
||||
ax1.plot(x, pol1(x, par), marker="")
|
||||
ax1.set(xlabel="x", ylabel="y", title=f"a = {par[0]:0.2f}{textpm}{err[0]:0.2f}\n"
|
||||
f"b = {par[1]:0.2f}{textpm}{err[1]:0.2f}\n"
|
||||
f"(init: {slope:0.2f}, {intercept:0.2f})")
|
||||
fig1.tight_layout()
|
||||
|
||||
plt.show()
|
||||
|
@ -1,58 +1,89 @@
|
||||
import sys
|
||||
sys.path.append('/home/l_msdetect/erik/aare/build')
|
||||
|
||||
#Our normal python imports
|
||||
|
||||
from aare import RawSubFile, DetectorType, RawFile
|
||||
|
||||
from pathlib import Path
|
||||
import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
import boost_histogram as bh
|
||||
import time
|
||||
path = Path("/home/l_msdetect/erik/data/aare-test-data/raw/jungfrau/")
|
||||
f = RawSubFile(path/"jungfrau_single_d0_f0_0.raw", DetectorType.Jungfrau, 512, 1024, 16)
|
||||
|
||||
from aare import File, ClusterFinder, VarClusterFinder
|
||||
# f = RawFile(path/"jungfrau_single_master_0.json")
|
||||
|
||||
base = Path('/mnt/sls_det_storage/matterhorn_data/aare_test_data/')
|
||||
|
||||
f = File(base/'Moench03new/cu_half_speed_master_4.json')
|
||||
cf = ClusterFinder((400,400), (3,3))
|
||||
for i in range(1000):
|
||||
cf.push_pedestal_frame(f.read_frame())
|
||||
# from aare._aare import ClusterVector_i, Interpolator
|
||||
|
||||
fig, ax = plt.subplots()
|
||||
im = ax.imshow(cf.pedestal())
|
||||
cf.pedestal()
|
||||
cf.noise()
|
||||
# import pickle
|
||||
# import numpy as np
|
||||
# import matplotlib.pyplot as plt
|
||||
# import boost_histogram as bh
|
||||
# import torch
|
||||
# import math
|
||||
# import time
|
||||
|
||||
|
||||
|
||||
N = 500
|
||||
t0 = time.perf_counter()
|
||||
hist1 = bh.Histogram(bh.axis.Regular(40, -2, 4000))
|
||||
f.seek(0)
|
||||
# def gaussian_2d(mx, my, sigma = 1, res=100, grid_size = 2):
|
||||
# """
|
||||
# Generate a 2D gaussian as position mx, my, with sigma=sigma.
|
||||
# The gaussian is placed on a 2x2 pixel matrix with resolution
|
||||
# res in one dimesion.
|
||||
# """
|
||||
# x = torch.linspace(0, pixel_size*grid_size, res)
|
||||
# x,y = torch.meshgrid(x,x, indexing="ij")
|
||||
# return 1 / (2*math.pi*sigma**2) * \
|
||||
# torch.exp(-((x - my)**2 / (2*sigma**2) + (y - mx)**2 / (2*sigma**2)))
|
||||
|
||||
t0 = time.perf_counter()
|
||||
data = f.read_n(N)
|
||||
t_elapsed = time.perf_counter()-t0
|
||||
# scale = 1000 #Scale factor when converting to integer
|
||||
# pixel_size = 25 #um
|
||||
# grid = 2
|
||||
# resolution = 100
|
||||
# sigma_um = 10
|
||||
# xa = np.linspace(0,grid*pixel_size,resolution)
|
||||
# ticks = [0, 25, 50]
|
||||
|
||||
# hit = np.array((20,20))
|
||||
# etahist_fname = "/home/l_msdetect/erik/tmp/test_hist.pkl"
|
||||
|
||||
# local_resolution = 99
|
||||
# grid_size = 3
|
||||
# xaxis = np.linspace(0,grid_size*pixel_size, local_resolution)
|
||||
# t = gaussian_2d(hit[0],hit[1], grid_size = grid_size, sigma = 10, res = local_resolution)
|
||||
# pixels = t.reshape(grid_size, t.shape[0] // grid_size, grid_size, t.shape[1] // grid_size).sum(axis = 3).sum(axis = 1)
|
||||
# pixels = pixels.numpy()
|
||||
# pixels = (pixels*scale).astype(np.int32)
|
||||
# v = ClusterVector_i(3,3)
|
||||
# v.push_back(1,1, pixels)
|
||||
|
||||
# with open(etahist_fname, "rb") as f:
|
||||
# hist = pickle.load(f)
|
||||
# eta = hist.view().copy()
|
||||
# etabinsx = np.array(hist.axes.edges.T[0].flat)
|
||||
# etabinsy = np.array(hist.axes.edges.T[1].flat)
|
||||
# ebins = np.array(hist.axes.edges.T[2].flat)
|
||||
# p = Interpolator(eta, etabinsx[0:-1], etabinsy[0:-1], ebins[0:-1])
|
||||
|
||||
|
||||
n_bytes = data.itemsize*data.size
|
||||
|
||||
print(f'Reading {N} frames took {t_elapsed:.3f}s {N/t_elapsed:.0f} FPS, {n_bytes/1024**2:.4f} GB/s')
|
||||
|
||||
|
||||
for frame in data:
|
||||
a = cf.find_clusters(frame)
|
||||
|
||||
clusters = cf.steal_clusters()
|
||||
|
||||
# t_elapsed = time.perf_counter()-t0
|
||||
# print(f'Clustering {N} frames took {t_elapsed:.2f}s {N/t_elapsed:.0f} FPS')
|
||||
# #Generate the hit
|
||||
|
||||
|
||||
# t0 = time.perf_counter()
|
||||
# total_clusters = clusters.size
|
||||
|
||||
# hist1.fill(clusters.sum())
|
||||
|
||||
# t_elapsed = time.perf_counter()-t0
|
||||
# print(f'Filling histogram with the sum of {total_clusters} clusters took: {t_elapsed:.3f}s, {total_clusters/t_elapsed:.3g} clust/s')
|
||||
# print(f'Average number of clusters per frame {total_clusters/N:.3f}')
|
||||
# tmp = p.interpolate(v)
|
||||
# print(f'tmp:{tmp}')
|
||||
# pos = np.array((tmp['x'], tmp['y']))*25
|
||||
|
||||
|
||||
# print(pixels)
|
||||
# fig, ax = plt.subplots(figsize = (7,7))
|
||||
# ax.pcolormesh(xaxis, xaxis, t)
|
||||
# ax.plot(*pos, 'o')
|
||||
# ax.set_xticks([0,25,50,75])
|
||||
# ax.set_yticks([0,25,50,75])
|
||||
# ax.set_xlim(0,75)
|
||||
# ax.set_ylim(0,75)
|
||||
# ax.grid()
|
||||
# print(f'{hit=}')
|
||||
# print(f'{pos=}')
|
64
python/src/bind_Cluster.hpp
Normal file
64
python/src/bind_Cluster.hpp
Normal file
@ -0,0 +1,64 @@
|
||||
#include "aare/Cluster.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <filesystem>
|
||||
#include <fmt/format.h>
|
||||
#include <pybind11/numpy.h>
|
||||
#include <pybind11/pybind11.h>
|
||||
#include <pybind11/stl.h>
|
||||
#include <pybind11/stl_bind.h>
|
||||
|
||||
namespace py = pybind11;
|
||||
using pd_type = double;
|
||||
|
||||
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) {
|
||||
auto class_name = fmt::format("Cluster{}", typestr);
|
||||
|
||||
py::class_<Cluster<Type, ClusterSizeX, ClusterSizeY, CoordType>>(
|
||||
m, class_name.c_str(), py::buffer_protocol())
|
||||
|
||||
.def(py::init([](uint8_t x, uint8_t y, py::array_t<Type> data) {
|
||||
py::buffer_info buf_info = data.request();
|
||||
Cluster<Type, ClusterSizeX, ClusterSizeY, CoordType> cluster;
|
||||
cluster.x = x;
|
||||
cluster.y = y;
|
||||
auto r = data.template unchecked<1>(); // no bounds checks
|
||||
for (py::ssize_t i = 0; i < data.size(); ++i) {
|
||||
cluster.data[i] = r(i);
|
||||
}
|
||||
return cluster;
|
||||
}));
|
||||
|
||||
/*
|
||||
//TODO! Review if to keep or not
|
||||
.def_property(
|
||||
"data",
|
||||
[](ClusterType &c) -> py::array {
|
||||
return py::array(py::buffer_info(
|
||||
c.data, sizeof(Type),
|
||||
py::format_descriptor<Type>::format(), // Type
|
||||
// format
|
||||
1, // Number of dimensions
|
||||
{static_cast<ssize_t>(ClusterSizeX *
|
||||
ClusterSizeY)}, // Shape (flattened)
|
||||
{sizeof(Type)} // Stride (step size between elements)
|
||||
));
|
||||
},
|
||||
[](ClusterType &c, py::array_t<Type> arr) {
|
||||
py::buffer_info buf_info = arr.request();
|
||||
Type *ptr = static_cast<Type *>(buf_info.ptr);
|
||||
std::copy(ptr, ptr + ClusterSizeX * ClusterSizeY,
|
||||
c.data); // TODO dont iterate over centers!!!
|
||||
|
||||
});
|
||||
*/
|
||||
}
|
||||
|
||||
#pragma GCC diagnostic pop
|
44
python/src/bind_ClusterCollector.hpp
Normal file
44
python/src/bind_ClusterCollector.hpp
Normal file
@ -0,0 +1,44 @@
|
||||
#include "aare/ClusterCollector.hpp"
|
||||
#include "aare/ClusterFileSink.hpp"
|
||||
#include "aare/ClusterFinder.hpp"
|
||||
#include "aare/ClusterFinderMT.hpp"
|
||||
#include "aare/ClusterVector.hpp"
|
||||
#include "aare/NDView.hpp"
|
||||
#include "aare/Pedestal.hpp"
|
||||
#include "np_helper.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <filesystem>
|
||||
#include <pybind11/pybind11.h>
|
||||
#include <pybind11/stl.h>
|
||||
#include <pybind11/stl_bind.h>
|
||||
|
||||
namespace py = pybind11;
|
||||
using pd_type = double;
|
||||
|
||||
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) {
|
||||
auto class_name = fmt::format("ClusterCollector_{}", typestr);
|
||||
|
||||
using ClusterType = Cluster<T, ClusterSizeX, ClusterSizeY, CoordType>;
|
||||
|
||||
py::class_<ClusterCollector<ClusterType>>(m, class_name.c_str())
|
||||
.def(py::init<ClusterFinderMT<ClusterType, uint16_t, double> *>())
|
||||
.def("stop", &ClusterCollector<ClusterType>::stop)
|
||||
.def(
|
||||
"steal_clusters",
|
||||
[](ClusterCollector<ClusterType> &self) {
|
||||
auto v = new std::vector<ClusterVector<ClusterType>>(
|
||||
self.steal_clusters());
|
||||
return v; // TODO change!!!
|
||||
},
|
||||
py::return_value_policy::take_ownership);
|
||||
}
|
||||
|
||||
#pragma GCC diagnostic pop
|
93
python/src/bind_ClusterFile.hpp
Normal file
93
python/src/bind_ClusterFile.hpp
Normal file
@ -0,0 +1,93 @@
|
||||
#include "aare/CalculateEta.hpp"
|
||||
#include "aare/ClusterFile.hpp"
|
||||
#include "aare/defs.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <filesystem>
|
||||
#include <pybind11/iostream.h>
|
||||
#include <pybind11/numpy.h>
|
||||
#include <pybind11/pybind11.h>
|
||||
#include <pybind11/stl.h>
|
||||
#include <pybind11/stl/filesystem.h>
|
||||
#include <string>
|
||||
|
||||
// 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;
|
||||
|
||||
template <typename Type, uint8_t CoordSizeX, uint8_t CoordSizeY,
|
||||
typename CoordType = uint16_t>
|
||||
void define_ClusterFile(py::module &m, const std::string &typestr) {
|
||||
|
||||
using ClusterType = Cluster<Type, CoordSizeX, CoordSizeY, CoordType>;
|
||||
|
||||
auto class_name = fmt::format("ClusterFile_{}", typestr);
|
||||
|
||||
py::class_<ClusterFile<ClusterType>>(m, class_name.c_str())
|
||||
.def(py::init<const std::filesystem::path &, size_t,
|
||||
const std::string &>(),
|
||||
py::arg(), py::arg("chunk_size") = 1000, py::arg("mode") = "r")
|
||||
.def(
|
||||
"read_clusters",
|
||||
[](ClusterFile<ClusterType> &self, size_t n_clusters) {
|
||||
auto v = new ClusterVector<ClusterType>(
|
||||
self.read_clusters(n_clusters));
|
||||
return v;
|
||||
},
|
||||
py::return_value_policy::take_ownership)
|
||||
.def("read_frame",
|
||||
[](ClusterFile<ClusterType> &self) {
|
||||
auto v = new ClusterVector<ClusterType>(self.read_frame());
|
||||
return v;
|
||||
})
|
||||
.def("set_roi", &ClusterFile<ClusterType>::set_roi)
|
||||
.def(
|
||||
"set_noise_map",
|
||||
[](ClusterFile<ClusterType> &self, py::array_t<int32_t> noise_map) {
|
||||
auto view = make_view_2d(noise_map);
|
||||
self.set_noise_map(view);
|
||||
})
|
||||
|
||||
.def("set_gain_map",
|
||||
[](ClusterFile<ClusterType> &self, py::array_t<double> gain_map) {
|
||||
auto view = make_view_2d(gain_map);
|
||||
self.set_gain_map(view);
|
||||
})
|
||||
|
||||
.def("close", &ClusterFile<ClusterType>::close)
|
||||
.def("write_frame", &ClusterFile<ClusterType>::write_frame)
|
||||
.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();
|
||||
})
|
||||
.def("__iter__", [](ClusterFile<ClusterType> &self) { return &self; })
|
||||
.def("__next__", [](ClusterFile<ClusterType> &self) {
|
||||
auto v = new ClusterVector<ClusterType>(
|
||||
self.read_clusters(self.chunk_size()));
|
||||
if (v->size() == 0) {
|
||||
throw py::stop_iteration();
|
||||
}
|
||||
return v;
|
||||
});
|
||||
}
|
||||
|
||||
template <typename Type, uint8_t CoordSizeX, uint8_t CoordSizeY,
|
||||
typename CoordType = uint16_t>
|
||||
void register_calculate_eta(py::module &m) {
|
||||
using ClusterType = Cluster<Type, CoordSizeX, CoordSizeY, CoordType>;
|
||||
m.def("calculate_eta2",
|
||||
[](const aare::ClusterVector<ClusterType> &clusters) {
|
||||
auto eta2 = new NDArray<double, 2>(calculate_eta2(clusters));
|
||||
return return_image_data(eta2);
|
||||
});
|
||||
}
|
||||
|
||||
#pragma GCC diagnostic pop
|
37
python/src/bind_ClusterFileSink.hpp
Normal file
37
python/src/bind_ClusterFileSink.hpp
Normal file
@ -0,0 +1,37 @@
|
||||
#include "aare/ClusterCollector.hpp"
|
||||
#include "aare/ClusterFileSink.hpp"
|
||||
#include "aare/ClusterFinder.hpp"
|
||||
#include "aare/ClusterFinderMT.hpp"
|
||||
#include "aare/ClusterVector.hpp"
|
||||
#include "aare/NDView.hpp"
|
||||
#include "aare/Pedestal.hpp"
|
||||
#include "np_helper.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <filesystem>
|
||||
#include <pybind11/pybind11.h>
|
||||
#include <pybind11/stl.h>
|
||||
#include <pybind11/stl_bind.h>
|
||||
|
||||
namespace py = pybind11;
|
||||
using pd_type = double;
|
||||
|
||||
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) {
|
||||
auto class_name = fmt::format("ClusterFileSink_{}", typestr);
|
||||
|
||||
using ClusterType = Cluster<T, ClusterSizeX, ClusterSizeY, CoordType>;
|
||||
|
||||
py::class_<ClusterFileSink<ClusterType>>(m, class_name.c_str())
|
||||
.def(py::init<ClusterFinderMT<ClusterType, uint16_t, double> *,
|
||||
const std::filesystem::path &>())
|
||||
.def("stop", &ClusterFileSink<ClusterType>::stop);
|
||||
}
|
||||
|
||||
#pragma GCC diagnostic pop
|
77
python/src/bind_ClusterFinder.hpp
Normal file
77
python/src/bind_ClusterFinder.hpp
Normal file
@ -0,0 +1,77 @@
|
||||
#include "aare/ClusterCollector.hpp"
|
||||
#include "aare/ClusterFileSink.hpp"
|
||||
#include "aare/ClusterFinder.hpp"
|
||||
#include "aare/ClusterFinderMT.hpp"
|
||||
#include "aare/ClusterVector.hpp"
|
||||
#include "aare/NDView.hpp"
|
||||
#include "aare/Pedestal.hpp"
|
||||
#include "np_helper.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <filesystem>
|
||||
#include <pybind11/pybind11.h>
|
||||
#include <pybind11/stl.h>
|
||||
#include <pybind11/stl_bind.h>
|
||||
|
||||
namespace py = pybind11;
|
||||
using pd_type = double;
|
||||
|
||||
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) {
|
||||
auto class_name = fmt::format("ClusterFinder_{}", typestr);
|
||||
|
||||
using ClusterType = Cluster<T, ClusterSizeX, ClusterSizeY, CoordType>;
|
||||
|
||||
py::class_<ClusterFinder<ClusterType, uint16_t, pd_type>>(
|
||||
m, class_name.c_str())
|
||||
.def(py::init<Shape<2>, pd_type, size_t>(), py::arg("image_size"),
|
||||
py::arg("n_sigma") = 5.0, py::arg("capacity") = 1'000'000)
|
||||
.def("push_pedestal_frame",
|
||||
[](ClusterFinder<ClusterType, uint16_t, pd_type> &self,
|
||||
py::array_t<uint16_t> frame) {
|
||||
auto view = make_view_2d(frame);
|
||||
self.push_pedestal_frame(view);
|
||||
})
|
||||
.def("clear_pedestal",
|
||||
&ClusterFinder<ClusterType, uint16_t, pd_type>::clear_pedestal)
|
||||
.def_property_readonly(
|
||||
"pedestal",
|
||||
[](ClusterFinder<ClusterType, uint16_t, pd_type> &self) {
|
||||
auto pd = new NDArray<pd_type, 2>{};
|
||||
*pd = self.pedestal();
|
||||
return return_image_data(pd);
|
||||
})
|
||||
.def_property_readonly(
|
||||
"noise",
|
||||
[](ClusterFinder<ClusterType, uint16_t, pd_type> &self) {
|
||||
auto arr = new NDArray<pd_type, 2>{};
|
||||
*arr = self.noise();
|
||||
return return_image_data(arr);
|
||||
})
|
||||
.def(
|
||||
"steal_clusters",
|
||||
[](ClusterFinder<ClusterType, uint16_t, pd_type> &self,
|
||||
bool realloc_same_capacity) {
|
||||
ClusterVector<ClusterType> clusters =
|
||||
self.steal_clusters(realloc_same_capacity);
|
||||
return clusters;
|
||||
},
|
||||
py::arg("realloc_same_capacity") = false)
|
||||
.def(
|
||||
"find_clusters",
|
||||
[](ClusterFinder<ClusterType, uint16_t, pd_type> &self,
|
||||
py::array_t<uint16_t> frame, uint64_t frame_number) {
|
||||
auto view = make_view_2d(frame);
|
||||
self.find_clusters(view, frame_number);
|
||||
return;
|
||||
},
|
||||
py::arg(), py::arg("frame_number") = 0);
|
||||
}
|
||||
|
||||
#pragma GCC diagnostic pop
|
81
python/src/bind_ClusterFinderMT.hpp
Normal file
81
python/src/bind_ClusterFinderMT.hpp
Normal file
@ -0,0 +1,81 @@
|
||||
#include "aare/ClusterCollector.hpp"
|
||||
#include "aare/ClusterFileSink.hpp"
|
||||
#include "aare/ClusterFinder.hpp"
|
||||
#include "aare/ClusterFinderMT.hpp"
|
||||
#include "aare/ClusterVector.hpp"
|
||||
#include "aare/NDView.hpp"
|
||||
#include "aare/Pedestal.hpp"
|
||||
#include "np_helper.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <filesystem>
|
||||
#include <pybind11/pybind11.h>
|
||||
#include <pybind11/stl.h>
|
||||
#include <pybind11/stl_bind.h>
|
||||
|
||||
namespace py = pybind11;
|
||||
using pd_type = double;
|
||||
|
||||
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) {
|
||||
auto class_name = fmt::format("ClusterFinderMT_{}", typestr);
|
||||
|
||||
using ClusterType = Cluster<T, ClusterSizeX, ClusterSizeY, CoordType>;
|
||||
|
||||
py::class_<ClusterFinderMT<ClusterType, uint16_t, pd_type>>(
|
||||
m, class_name.c_str())
|
||||
.def(py::init<Shape<2>, pd_type, size_t, size_t>(),
|
||||
py::arg("image_size"), py::arg("n_sigma") = 5.0,
|
||||
py::arg("capacity") = 2048, py::arg("n_threads") = 3)
|
||||
.def("push_pedestal_frame",
|
||||
[](ClusterFinderMT<ClusterType, uint16_t, pd_type> &self,
|
||||
py::array_t<uint16_t> frame) {
|
||||
auto view = make_view_2d(frame);
|
||||
self.push_pedestal_frame(view);
|
||||
})
|
||||
.def(
|
||||
"find_clusters",
|
||||
[](ClusterFinderMT<ClusterType, uint16_t, pd_type> &self,
|
||||
py::array_t<uint16_t> frame, uint64_t frame_number) {
|
||||
auto view = make_view_2d(frame);
|
||||
self.find_clusters(view, frame_number);
|
||||
return;
|
||||
},
|
||||
py::arg(), py::arg("frame_number") = 0)
|
||||
.def_property_readonly(
|
||||
"cluster_size",
|
||||
[](ClusterFinderMT<ClusterType, uint16_t, pd_type> &self) {
|
||||
return py::make_tuple(ClusterSizeX, ClusterSizeY);
|
||||
})
|
||||
.def("clear_pedestal",
|
||||
&ClusterFinderMT<ClusterType, uint16_t, pd_type>::clear_pedestal)
|
||||
.def("sync", &ClusterFinderMT<ClusterType, uint16_t, pd_type>::sync)
|
||||
.def("stop", &ClusterFinderMT<ClusterType, uint16_t, pd_type>::stop)
|
||||
.def("start", &ClusterFinderMT<ClusterType, uint16_t, pd_type>::start)
|
||||
.def(
|
||||
"pedestal",
|
||||
[](ClusterFinderMT<ClusterType, uint16_t, pd_type> &self,
|
||||
size_t thread_index) {
|
||||
auto pd = new NDArray<pd_type, 2>{};
|
||||
*pd = self.pedestal(thread_index);
|
||||
return return_image_data(pd);
|
||||
},
|
||||
py::arg("thread_index") = 0)
|
||||
.def(
|
||||
"noise",
|
||||
[](ClusterFinderMT<ClusterType, uint16_t, pd_type> &self,
|
||||
size_t thread_index) {
|
||||
auto arr = new NDArray<pd_type, 2>{};
|
||||
*arr = self.noise(thread_index);
|
||||
return return_image_data(arr);
|
||||
},
|
||||
py::arg("thread_index") = 0);
|
||||
}
|
||||
|
||||
#pragma GCC diagnostic pop
|
107
python/src/bind_ClusterVector.hpp
Normal file
107
python/src/bind_ClusterVector.hpp
Normal file
@ -0,0 +1,107 @@
|
||||
#include "aare/ClusterCollector.hpp"
|
||||
#include "aare/ClusterFileSink.hpp"
|
||||
#include "aare/ClusterFinder.hpp"
|
||||
#include "aare/ClusterFinderMT.hpp"
|
||||
#include "aare/ClusterVector.hpp"
|
||||
#include "aare/NDView.hpp"
|
||||
#include "aare/Pedestal.hpp"
|
||||
#include "np_helper.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <filesystem>
|
||||
#include <pybind11/pybind11.h>
|
||||
#include <pybind11/stl.h>
|
||||
#include <pybind11/stl_bind.h>
|
||||
|
||||
namespace py = pybind11;
|
||||
using pd_type = double;
|
||||
|
||||
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) {
|
||||
using ClusterType = Cluster<Type, ClusterSizeX, ClusterSizeY, CoordType>;
|
||||
auto class_name = fmt::format("ClusterVector_{}", typestr);
|
||||
|
||||
py::class_<ClusterVector<
|
||||
Cluster<Type, ClusterSizeX, ClusterSizeY, CoordType>, void>>(
|
||||
m, class_name.c_str(),
|
||||
py::buffer_protocol())
|
||||
|
||||
.def(py::init()) // TODO change!!!
|
||||
|
||||
.def("push_back",
|
||||
[](ClusterVector<ClusterType> &self, const ClusterType &cluster) {
|
||||
self.push_back(cluster);
|
||||
})
|
||||
|
||||
.def("sum",
|
||||
[](ClusterVector<ClusterType> &self) {
|
||||
auto *vec = new std::vector<Type>(self.sum());
|
||||
return return_vector(vec);
|
||||
})
|
||||
.def("sum_2x2",
|
||||
[](ClusterVector<ClusterType> &self) {
|
||||
auto *vec = new std::vector<Type>(self.sum_2x2());
|
||||
return return_vector(vec);
|
||||
})
|
||||
.def_property_readonly("size", &ClusterVector<ClusterType>::size)
|
||||
.def("item_size", &ClusterVector<ClusterType>::item_size)
|
||||
.def_property_readonly("fmt",
|
||||
[typestr](ClusterVector<ClusterType> &self) {
|
||||
return fmt_format<ClusterType>;
|
||||
})
|
||||
|
||||
.def_property_readonly("cluster_size_x",
|
||||
&ClusterVector<ClusterType>::cluster_size_x)
|
||||
.def_property_readonly("cluster_size_y",
|
||||
&ClusterVector<ClusterType>::cluster_size_y)
|
||||
.def_property_readonly("capacity",
|
||||
&ClusterVector<ClusterType>::capacity)
|
||||
.def_property("frame_number", &ClusterVector<ClusterType>::frame_number,
|
||||
&ClusterVector<ClusterType>::set_frame_number)
|
||||
.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.item_size()} /* Strides (in bytes) for each index */
|
||||
);
|
||||
});
|
||||
|
||||
// Free functions using ClusterVector
|
||||
m.def("hitmap",
|
||||
[](std::array<size_t, 2> image_size, ClusterVector<ClusterType> &cv) {
|
||||
// Create a numpy array to hold the hitmap
|
||||
// The shape of the array is (image_size[0], image_size[1])
|
||||
// note that the python array is passed as [row, col] which
|
||||
// is the opposite of the clusters [x,y]
|
||||
py::array_t<int32_t> hitmap(image_size);
|
||||
auto r = hitmap.mutable_unchecked<2>();
|
||||
|
||||
// Initialize hitmap to 0
|
||||
for (py::ssize_t i = 0; i < r.shape(0); i++)
|
||||
for (py::ssize_t j = 0; j < r.shape(1); j++)
|
||||
r(i, j) = 0;
|
||||
|
||||
// Loop over the clusters and increment the hitmap
|
||||
// Skip out of bound clusters
|
||||
for (const auto &cluster : cv) {
|
||||
auto x = cluster.x;
|
||||
auto y = cluster.y;
|
||||
if (x < image_size[1] && y < image_size[0])
|
||||
r(cluster.y, cluster.x) += 1;
|
||||
}
|
||||
|
||||
return hitmap;
|
||||
});
|
||||
}
|
||||
|
||||
#pragma GCC diagnostic pop
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user