samcore 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. samcore-0.1.0/.github/actions/setup-conda-toolchain/action.yml +93 -0
  2. samcore-0.1.0/.github/workflows/tests.yml +69 -0
  3. samcore-0.1.0/.github/workflows/wheels.yml +183 -0
  4. samcore-0.1.0/.gitignore +40 -0
  5. samcore-0.1.0/.gitmodules +6 -0
  6. samcore-0.1.0/CMakeLists.txt +254 -0
  7. samcore-0.1.0/LICENSE +21 -0
  8. samcore-0.1.0/PKG-INFO +220 -0
  9. samcore-0.1.0/README.md +207 -0
  10. samcore-0.1.0/cmake/_version.py.in +2 -0
  11. samcore-0.1.0/cmake/dependencies.cmake +44 -0
  12. samcore-0.1.0/cmake/samcoreConfig.cmake.in +9 -0
  13. samcore-0.1.0/cmake/stubgen.cmake +46 -0
  14. samcore-0.1.0/default.nix +32 -0
  15. samcore-0.1.0/executables/CMakeLists.txt +5 -0
  16. samcore-0.1.0/executables/bench.cpp +70 -0
  17. samcore-0.1.0/executables/gen_data.cpp +287 -0
  18. samcore-0.1.0/external/pocketfft/README.md +277 -0
  19. samcore-0.1.0/external/pocketfft/meson.build +12 -0
  20. samcore-0.1.0/external/pocketfft/pocketfft_demo.cc +87 -0
  21. samcore-0.1.0/external/pocketfft/pocketfft_hdronly.h +3853 -0
  22. samcore-0.1.0/include/samcore/array.hpp +153 -0
  23. samcore-0.1.0/include/samcore/io/fileio.hpp +73 -0
  24. samcore-0.1.0/include/samcore/preprocessing.hpp +48 -0
  25. samcore-0.1.0/include/samcore/sam_dataset.hpp +164 -0
  26. samcore-0.1.0/include/samcore/sam_header.hpp +58 -0
  27. samcore-0.1.0/include/samcore/sam_labels.hpp +114 -0
  28. samcore-0.1.0/include/samcore/sam_scan.hpp +215 -0
  29. samcore-0.1.0/include/samcore/samcore.hpp +12 -0
  30. samcore-0.1.0/include/samcore/signal/kernels.hpp +106 -0
  31. samcore-0.1.0/include/samcore/utils.hpp +44 -0
  32. samcore-0.1.0/pyproject.toml +70 -0
  33. samcore-0.1.0/shell.nix +33 -0
  34. samcore-0.1.0/src/bindings/common.hpp +172 -0
  35. samcore-0.1.0/src/bindings/dataset.cpp +361 -0
  36. samcore-0.1.0/src/bindings/header.cpp +207 -0
  37. samcore-0.1.0/src/bindings/labels.cpp +225 -0
  38. samcore-0.1.0/src/bindings/module.cpp +18 -0
  39. samcore-0.1.0/src/bindings/scan.cpp +546 -0
  40. samcore-0.1.0/src/bindings/submodules.cpp +310 -0
  41. samcore-0.1.0/src/io/fileio_h5sam.cpp +297 -0
  42. samcore-0.1.0/src/io/fileio_h5samd.cpp +349 -0
  43. samcore-0.1.0/src/io/h5_common.hpp +241 -0
  44. samcore-0.1.0/src/io/h5_lazy.hpp +38 -0
  45. samcore-0.1.0/src/preprocessing.cpp +350 -0
  46. samcore-0.1.0/src/sam_dataset.cpp +314 -0
  47. samcore-0.1.0/src/sam_header.cpp +82 -0
  48. samcore-0.1.0/src/sam_labels.cpp +399 -0
  49. samcore-0.1.0/src/sam_scan.cpp +720 -0
  50. samcore-0.1.0/src/samcore/__init__.py +36 -0
  51. samcore-0.1.0/src/samcore/_dataset.py +549 -0
  52. samcore-0.1.0/src/samcore/_io.py +104 -0
  53. samcore-0.1.0/src/samcore/_labels.py +152 -0
  54. samcore-0.1.0/src/samcore/_scan.py +348 -0
  55. samcore-0.1.0/src/signal/filters.cpp +517 -0
  56. samcore-0.1.0/src/signal/spectral.cpp +302 -0
  57. samcore-0.1.0/src/utils.cpp +160 -0
  58. samcore-0.1.0/tests/CMakeLists.txt +33 -0
  59. samcore-0.1.0/tests/data/testdata.h5sam +0 -0
  60. samcore-0.1.0/tests/data/testdata.h5samd +0 -0
  61. samcore-0.1.0/tests/python/conftest.py +46 -0
  62. samcore-0.1.0/tests/python/test_core.py +547 -0
  63. samcore-0.1.0/tests/python/test_dataset.py +1482 -0
  64. samcore-0.1.0/tests/python/test_labels.py +891 -0
  65. samcore-0.1.0/tests/python/test_parity.py +399 -0
  66. samcore-0.1.0/tests/python/test_rotate_mirror.py +274 -0
  67. samcore-0.1.0/tests/python/test_signal.py +159 -0
  68. samcore-0.1.0/tests/test_array.cpp +61 -0
  69. samcore-0.1.0/tests/test_header.cpp +66 -0
  70. samcore-0.1.0/tests/test_io_h5sam.cpp +175 -0
  71. samcore-0.1.0/tests/test_io_h5samd.cpp +205 -0
  72. samcore-0.1.0/tests/test_labels.cpp +208 -0
  73. samcore-0.1.0/tests/test_omp_parity.cpp +89 -0
  74. samcore-0.1.0/tests/test_preprocessing.cpp +138 -0
  75. samcore-0.1.0/tests/test_sam_scan.cpp +188 -0
  76. samcore-0.1.0/tests/test_sam_scan_ops.cpp +317 -0
  77. samcore-0.1.0/tests/test_signal.cpp +131 -0
  78. samcore-0.1.0/tests/test_utils.cpp +118 -0
  79. samcore-0.1.0/version.txt +1 -0
@@ -0,0 +1,93 @@
1
+ name: Set up Clang/conda toolchain (Windows & macOS)
2
+ description: >-
3
+ Install Clang, the LLVM OpenMP runtime and HDF5 from conda-forge for
4
+ building samcore on Windows and macOS.
5
+
6
+ # samcore's OpenMP loops use unsigned (size_t) loop indices, which MSVC's
7
+ # OpenMP 2.0 implementation rejects with error C3016. GCC/Clang accept them,
8
+ # so the README recommends LLVM Clang, and this mirrors it: conda-forge
9
+ # provides Clang, llvm-openmp and HDF5 in one ABI-consistent environment.
10
+ #
11
+ # Windows: conda-forge does NOT ship the Windows SDK import libraries
12
+ # (kernel32.lib, ...) - the `vc`/`ucrt` packages only contain the runtime.
13
+ # clang and lld-link trust %INCLUDE%/%LIB% when set and then skip
14
+ # auto-detecting the SDK, so the MSVC developer environment must be
15
+ # initialized as well and the conda prefix prepended to it.
16
+ #
17
+ # macOS: AppleClang has no OpenMP, hence the conda-forge clang + libomp.
18
+ #
19
+ # On Linux runners every step below is a no-op.
20
+
21
+ runs:
22
+ using: composite
23
+ steps:
24
+ # Initializes the MSVC + Windows SDK %INCLUDE%/%LIB%/%PATH% environment.
25
+ - name: Initialize MSVC environment (Windows)
26
+ if: runner.os == 'Windows'
27
+ uses: ilammy/msvc-dev-cmd@v1
28
+ with:
29
+ arch: x64
30
+
31
+ - name: Create conda-forge toolchain environment
32
+ if: runner.os != 'Linux'
33
+ id: conda
34
+ uses: mamba-org/setup-micromamba@v3
35
+ with:
36
+ environment-name: samcore-build
37
+ create-args: >-
38
+ clangxx
39
+ llvm-openmp
40
+ hdf5=1.14
41
+ cmake
42
+ ninja
43
+ init-shell: none
44
+ cache-environment: true
45
+
46
+ - name: Export toolchain env (Windows)
47
+ if: runner.os == 'Windows'
48
+ shell: pwsh
49
+ run: |
50
+ $prefix = "${{ steps.conda.outputs.environment-path }}"
51
+ if (-not $env:INCLUDE -or -not $env:LIB) {
52
+ throw "MSVC environment (INCLUDE/LIB) was not initialized by the previous step"
53
+ }
54
+ "$prefix\bin" | Out-File -FilePath $env:GITHUB_PATH -Append
55
+ "$prefix\Library\bin" | Out-File -FilePath $env:GITHUB_PATH -Append
56
+ # Prepend the conda prefix to the MSVC-provided INCLUDE/LIB so the
57
+ # build finds omp.h / libomp / HDF5 while the Windows SDK import libs
58
+ # stay on the search path. LTO is disabled because clang's -flto
59
+ # needs lld-link on Windows (link.exe cannot consume LLVM bitcode).
60
+ @(
61
+ "HDF5_ROOT=$prefix\Library"
62
+ "CMAKE_PREFIX_PATH=$prefix\Library"
63
+ "INCLUDE=$prefix\Library\include;$env:INCLUDE"
64
+ "LIB=$prefix\Library\lib;$env:LIB"
65
+ "CC=$prefix\Library\bin\clang.exe"
66
+ "CXX=$prefix\Library\bin\clang++.exe"
67
+ "CMAKE_ARGS=-G Ninja -DSAMCORE_ENABLE_LTO=OFF"
68
+ ) | Out-File -FilePath $env:GITHUB_ENV -Append
69
+ Write-Host "Windows toolchain prefix: $prefix"
70
+
71
+ - name: Export toolchain env (macOS)
72
+ if: runner.os == 'macOS'
73
+ shell: bash
74
+ run: |
75
+ prefix="${{ steps.conda.outputs.environment-path }}"
76
+ if [ ! -x "$prefix/bin/clang++" ]; then
77
+ echo "clang++ not found in $prefix/bin" >&2
78
+ exit 1
79
+ fi
80
+ echo "$prefix/bin" >> "$GITHUB_PATH"
81
+ # CPATH/LIBRARY_PATH let the build find omp.h/libomp/HDF5 while
82
+ # CMAKE_PREFIX_PATH/HDF5_ROOT cover CMake's find_package.
83
+ {
84
+ echo "CC=$prefix/bin/clang"
85
+ echo "CXX=$prefix/bin/clang++"
86
+ echo "HDF5_ROOT=$prefix"
87
+ echo "CMAKE_PREFIX_PATH=$prefix"
88
+ echo "CPATH=$prefix/include"
89
+ echo "LIBRARY_PATH=$prefix/lib"
90
+ echo "SDKROOT=$(xcrun --show-sdk-path)"
91
+ echo "CMAKE_ARGS=-G Ninja"
92
+ } >> "$GITHUB_ENV"
93
+ echo "macOS toolchain prefix: $prefix"
@@ -0,0 +1,69 @@
1
+ name: Tests
2
+
3
+ on:
4
+ push:
5
+ branches: [main, master]
6
+ pull_request:
7
+ workflow_dispatch:
8
+
9
+ jobs:
10
+ python-tests:
11
+ name: Python ${{ matrix.python }} on ${{ matrix.os }}
12
+ runs-on: ${{ matrix.os }}
13
+ strategy:
14
+ fail-fast: false
15
+ matrix:
16
+ os: [ubuntu-latest, macos-latest, windows-latest]
17
+ python: ["3.9", "3.10", "3.11", "3.12", "3.13", "3.14"]
18
+ steps:
19
+ - uses: actions/checkout@v7
20
+ with:
21
+ submodules: recursive
22
+
23
+ - name: Install HDF5 (Linux)
24
+ if: runner.os == 'Linux'
25
+ run: sudo apt-get update && sudo apt-get -y install libhdf5-dev ninja-build
26
+
27
+ # Windows/macOS use the conda-forge LLVM toolchain (as the README
28
+ # recommends): MSVC's OpenMP 2.0 rejects the unsigned loop indices
29
+ # (error C3016) and AppleClang has no OpenMP. The action also installs
30
+ # HDF5 and llvm-openmp and exports the compiler env.
31
+ - uses: ./.github/actions/setup-conda-toolchain
32
+
33
+ - name: Set up Python
34
+ uses: actions/setup-python@v7
35
+ with:
36
+ python-version: ${{ matrix.python }}
37
+
38
+ - name: Build and install package
39
+ run: |
40
+ python -m pip install --upgrade pip
41
+ pip install .[test]
42
+
43
+ - name: Run Python tests
44
+ env:
45
+ SAMCORE_TEST_DATA_DIR: tests/data
46
+ run: pytest tests/python -v
47
+
48
+ cpp-tests:
49
+ name: C++ (sanitizers) on ${{ matrix.os }}
50
+ runs-on: ${{ matrix.os }}
51
+ strategy:
52
+ fail-fast: false
53
+ matrix:
54
+ os: [ubuntu-latest]
55
+ steps:
56
+ - uses: actions/checkout@v7
57
+ with:
58
+ submodules: recursive
59
+
60
+ - name: Install HDF5
61
+ run: sudo apt-get update && sudo apt-get -y install libhdf5-dev ninja-build
62
+
63
+ - name: Configure, build, test (debug + sanitizers)
64
+ run: |
65
+ cmake -B build-debug -G Ninja -DCMAKE_BUILD_TYPE=Debug
66
+ cmake --build build-debug
67
+ ASAN_OPTIONS=detect_leaks=1:halt_on_error=1 \
68
+ UBSAN_OPTIONS=halt_on_error=1 \
69
+ ctest --test-dir build-debug --output-on-failure
@@ -0,0 +1,183 @@
1
+ name: Build wheels & attach to release
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - "v*"
7
+ - "ci-test*"
8
+ workflow_dispatch:
9
+
10
+ permissions:
11
+ contents: read
12
+
13
+ jobs:
14
+ build-wheels:
15
+ name: Build wheel on ${{ matrix.os }}
16
+ runs-on: ${{ matrix.os }}
17
+ strategy:
18
+ fail-fast: false
19
+ matrix:
20
+ os:
21
+ - ubuntu-latest # manylinux/musllinux x86_64
22
+ - ubuntu-24.04-arm # manylinux/musllinux aarch64
23
+ - macos-latest
24
+ - windows-latest
25
+
26
+ steps:
27
+ - uses: actions/checkout@v7
28
+ with:
29
+ submodules: recursive
30
+
31
+ - uses: actions/setup-python@v7
32
+ with:
33
+ python-version: "3.12"
34
+
35
+ - name: Install cibuildwheel
36
+ run: python -m pip install --upgrade pip cibuildwheel
37
+
38
+ # HDF5 is required (find_package(HDF5 COMPONENTS CXX REQUIRED)). On
39
+ # Linux it is installed inside the manylinux/musllinux container (see
40
+ # CIBW_BEFORE_ALL_LINUX below). On Windows and macOS the conda-forge
41
+ # toolchain action installs HDF5, Clang and llvm-openmp and exports the
42
+ # compiler env (Clang is required on Windows because MSVC's OpenMP 2.0
43
+ # rejects the unsigned loop indices, error C3016; on macOS because
44
+ # AppleClang has no OpenMP).
45
+ - uses: ./.github/actions/setup-conda-toolchain
46
+
47
+ - name: Build wheels
48
+ run: python -m cibuildwheel --output-dir wheelhouse
49
+ env:
50
+ CIBW_BUILD: cp39-* cp310-* cp311-* cp312-* cp313-* cp314-*
51
+ CIBW_ARCHS_LINUX: auto
52
+ CIBW_ARCHS_MACOS: auto
53
+ # 64-bit only: the LLVM clang GNU driver cannot target 32-bit
54
+ # Windows against the win32 CPython the way MSVC's vcvars does.
55
+ CIBW_ARCHS_WINDOWS: AMD64
56
+ # HDF5 + ninja inside the Linux containers. CIBW_BEFORE_ALL_* runs
57
+ # once per container (before the whole CPython matrix), so the
58
+ # system packages are not re-installed for every Python version.
59
+ # manylinux is RHEL-based (yum on manylinux2014, dnf on
60
+ # manylinux_2_28+); musllinux is Alpine and uses apk, where HDF5's
61
+ # C++ headers ship in hdf5-dev (community repo).
62
+ CIBW_BEFORE_ALL_LINUX: |
63
+ if command -v apk >/dev/null 2>&1; then
64
+ apk add --no-cache --repository "https://dl-cdn.alpinelinux.org/alpine/v$(cut -d. -f1,2 /etc/alpine-release)/community" build-base cmake hdf5-dev ninja
65
+ elif command -v dnf >/dev/null 2>&1; then
66
+ dnf install -y hdf5-devel ninja-build
67
+ else
68
+ yum install -y hdf5-devel ninja-build
69
+ fi
70
+ # numpy is intentionally NOT installed here: pip's build isolation
71
+ # pulls it from [build-system].requires in pyproject.toml, and the
72
+ # wheel test env gets it as the package's declared dependency.
73
+ # Windows: bundle the HDF5/OpenMP runtime DLLs into the wheel
74
+ # (delvewheel is installed into the build env; HDF5_ROOT is exported
75
+ # by the toolchain action above). Commands on Windows run under
76
+ # cmd.exe, so use %VAR% syntax here, not PowerShell's $env:VAR.
77
+ CIBW_BEFORE_BUILD_WINDOWS: python -m pip install delvewheel
78
+ CIBW_REPAIR_WHEEL_COMMAND_WINDOWS: >-
79
+ delvewheel repair --add-path "%HDF5_ROOT%\bin" -w {dest_dir} {wheel}
80
+ CIBW_TEST_REQUIRES: pytest
81
+ CIBW_TEST_COMMAND: >-
82
+ pytest {project}/tests/python -v -p no:cacheprovider
83
+
84
+ - name: Show artifacts
85
+ shell: bash
86
+ run: ls -lh wheelhouse
87
+
88
+ - uses: actions/upload-artifact@v7
89
+ with:
90
+ name: wheels-${{ matrix.os }}
91
+ path: wheelhouse/*.whl
92
+
93
+ build-sdist:
94
+ name: Build sdist
95
+ runs-on: ubuntu-latest
96
+ steps:
97
+ - uses: actions/checkout@v7
98
+ with:
99
+ submodules: recursive
100
+
101
+ - uses: actions/setup-python@v7
102
+ with:
103
+ python-version: "3.12"
104
+
105
+ - name: Install HDF5
106
+ run: |
107
+ sudo apt-get update
108
+ sudo apt-get -y install libhdf5-dev ninja-build
109
+
110
+ - name: Build sdist
111
+ run: |
112
+ python -m pip install --upgrade pip build
113
+ python -m build --sdist --outdir dist
114
+
115
+ - uses: actions/upload-artifact@v7
116
+ with:
117
+ name: sdist
118
+ path: dist/*.tar.gz
119
+
120
+ publish:
121
+ name: Attach artifacts to GitHub Release
122
+ needs: [build-wheels, build-sdist]
123
+ runs-on: ubuntu-latest
124
+ # Run for every tag this workflow is triggered by ("v*" and "ci-test*").
125
+ # A tag-less run (workflow_dispatch on a branch) skips this job.
126
+ # Note: a job skipped via `if` cannot be re-run on its own, so the CI
127
+ # test tags must be allowed here too, otherwise publish stays skipped.
128
+ if: startsWith(github.ref, 'refs/tags/')
129
+ permissions:
130
+ contents: write
131
+ steps:
132
+ - uses: actions/download-artifact@v8
133
+ with:
134
+ pattern: wheels-*
135
+ path: dist
136
+ merge-multiple: true
137
+
138
+ - uses: actions/download-artifact@v8
139
+ with:
140
+ name: sdist
141
+ path: dist
142
+
143
+ - name: List artifacts
144
+ run: ls -lh dist
145
+
146
+ - name: Attach wheels to GitHub Release
147
+ uses: softprops/action-gh-release@v3
148
+ with:
149
+ files: dist/*
150
+ # ci-test* releases are marked prerelease so they never become
151
+ # the repository's "Latest" release.
152
+ prerelease: ${{ !startsWith(github.ref, 'refs/tags/v') }}
153
+
154
+ publish-pypi:
155
+ name: Publish to PyPI
156
+ needs: [build-wheels, build-sdist]
157
+ runs-on: ubuntu-latest
158
+ # Real releases only - the ci-test* tags must never reach PyPI.
159
+ if: startsWith(github.ref, 'refs/tags/v')
160
+ # Must match the "Environment name" configured for the trusted publisher
161
+ # on PyPI (see README). Add required reviewers here for a manual gate.
162
+ environment:
163
+ name: pypi
164
+ url: https://pypi.org/p/samcore
165
+ permissions:
166
+ id-token: write # OIDC trusted publishing (no API token/secret)
167
+ contents: read
168
+ steps:
169
+ - uses: actions/download-artifact@v8
170
+ with:
171
+ pattern: wheels-*
172
+ path: dist
173
+ merge-multiple: true
174
+
175
+ - uses: actions/download-artifact@v8
176
+ with:
177
+ name: sdist
178
+ path: dist
179
+
180
+ # Trusted Publishing: authenticates via the GitHub OIDC token. PEP 740
181
+ # attestations are enabled by default. `dist/` holds the wheels + sdist.
182
+ - name: Publish to PyPI
183
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,40 @@
1
+ # build artifacts
2
+ build/
3
+ build-*/
4
+ _skbuild/
5
+ dist/
6
+ *.egg-info/
7
+ *.so
8
+ *.dylib
9
+ *.dll
10
+ *.whl
11
+ result
12
+
13
+ # python
14
+ __pycache__/
15
+ *.py[cod]
16
+ .venv/
17
+ .pytest_cache/
18
+
19
+ # nix
20
+ .nix-shell/
21
+
22
+ # test data: keep the sample testdata files, ignore everything else
23
+ tests/data/*
24
+ !tests/data/testdata.h5sam
25
+ !tests/data/testdata.h5samd
26
+
27
+ # All markdown/text except README.md and version.txt
28
+ *.md
29
+ *.txt
30
+ !version.txt
31
+ !README.md
32
+
33
+ # Notebooks
34
+ *.ipynb
35
+
36
+ # Images
37
+ *.tiff
38
+ *.tif
39
+ *.TIFF
40
+ *.png
@@ -0,0 +1,6 @@
1
+ [submodule "external/pocketfft"]
2
+ path = external/pocketfft
3
+ # The upstream host (gitlab.mpcdf.mpg.de) is often unreachable from CI,
4
+ # so use the original author's GitHub mirror of the same repository. The
5
+ # pinned commit is the tip of its `cpp` branch and is byte-identical.
6
+ url = https://github.com/mreineck/pocketfft.git
@@ -0,0 +1,254 @@
1
+ cmake_minimum_required(VERSION 3.22)
2
+
3
+ # Read version from version.txt; register it as a configure dependency so
4
+ # the build re-runs automatically when the file changes.
5
+ set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS
6
+ "${CMAKE_SOURCE_DIR}/version.txt")
7
+ file(READ "${CMAKE_SOURCE_DIR}/version.txt" VERSION_STRING)
8
+ string(STRIP "${VERSION_STRING}" PROJECT_VERSION)
9
+
10
+ project(samcore VERSION ${PROJECT_VERSION} LANGUAGES CXX)
11
+
12
+ set(CMAKE_CXX_STANDARD 20)
13
+ set(CMAKE_CXX_STANDARD_REQUIRED True)
14
+ set(CMAKE_CXX_EXTENSIONS Off)
15
+
16
+ if(NOT CMAKE_BUILD_TYPE)
17
+ set(CMAKE_BUILD_TYPE Release)
18
+ endif()
19
+
20
+ option(SAMCORE_BUILD_TESTS "Build the libsamcore test suite" ${PROJECT_IS_TOP_LEVEL})
21
+ option(SAMCORE_BUILD_EXECUTABLES "Build executables (bench, gen_data)" OFF)
22
+ option(SAMCORE_BUILD_PYTHON "Build the samcore Python bindings (nanobind)" OFF)
23
+ option(SAMCORE_ENABLE_OPENMP "Enable OpenMP parallelism" ON)
24
+ option(SAMCORE_NATIVE_ARCH "Compile with -march=native" OFF)
25
+ option(SAMCORE_WARNINGS_AS_ERRORS "Treat warnings as errors" OFF)
26
+ option(SAMCORE_ENABLE_SANITIZERS "Enable ASan+UBSan in Debug builds" ON)
27
+ option(SAMCORE_ENABLE_FAST_MATH "Enable -ffast-math in Release builds" ON)
28
+ option(SAMCORE_ENABLE_LTO "Enable link-time optimization in Release builds" ON)
29
+
30
+ # Alpine's fortify-headers (musl) define libc functions as always_inline
31
+ # wrappers that GCC cannot inline under -flto ("function body can be
32
+ # overwritten at link time"), which breaks the link. Disable LTO on musl
33
+ # targets (e.g. musllinux wheels); glibc and the other platforms are fine.
34
+ if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
35
+ execute_process(COMMAND "${CMAKE_CXX_COMPILER}" -dumpmachine
36
+ OUTPUT_VARIABLE _samcore_target_triple
37
+ OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_QUIET)
38
+ if(_samcore_target_triple MATCHES "musl"
39
+ OR CMAKE_CXX_LIBRARY_ARCHITECTURE MATCHES "musl")
40
+ if(SAMCORE_ENABLE_LTO)
41
+ message(STATUS "samcore: disabling LTO for musl target (fortify-headers)")
42
+ endif()
43
+ set(SAMCORE_ENABLE_LTO OFF)
44
+ endif()
45
+ endif()
46
+
47
+ if(SAMCORE_NATIVE_ARCH)
48
+ add_compile_options(-march=native)
49
+ endif()
50
+
51
+ include(cmake/dependencies.cmake)
52
+
53
+ add_library(samcore
54
+ src/sam_header.cpp
55
+ src/sam_labels.cpp
56
+ src/sam_scan.cpp
57
+ src/sam_dataset.cpp
58
+ src/io/fileio_h5sam.cpp
59
+ src/io/fileio_h5samd.cpp
60
+ src/signal/filters.cpp
61
+ src/signal/spectral.cpp
62
+ src/preprocessing.cpp
63
+ src/utils.cpp
64
+ )
65
+ add_library(samcore::samcore ALIAS samcore)
66
+
67
+ target_include_directories(samcore
68
+ PUBLIC
69
+ $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
70
+ $<INSTALL_INTERFACE:include>
71
+ PRIVATE
72
+ # pocketfft is an internal dependency: no public header includes it, so
73
+ # it must not leak into the exported interface.
74
+ ${SAMCORE_POCKETFFT_INCLUDE_DIR})
75
+
76
+ target_link_libraries(samcore
77
+ PUBLIC
78
+ HDF5::HDF5
79
+ PRIVATE
80
+ # OpenMP is an implementation detail: apply it only in the build tree
81
+ # so the exported package does not reference OpenMP::OpenMP_CXX.
82
+ $<$<BOOL:${SAMCORE_ENABLE_OPENMP}>:$<BUILD_INTERFACE:OpenMP::OpenMP_CXX>>)
83
+
84
+ target_compile_definitions(samcore PUBLIC SAMCORE_VERSION="${PROJECT_VERSION}")
85
+
86
+ if(SAMCORE_ENABLE_OPENMP)
87
+ target_compile_definitions(samcore PUBLIC SAMCORE_HAS_OPENMP=1)
88
+ endif()
89
+
90
+ target_compile_options(samcore PRIVATE
91
+ $<$<CXX_COMPILER_ID:MSVC>:/W4;/permissive->
92
+ $<$<NOT:$<CXX_COMPILER_ID:MSVC>>:-Wall;-Wextra;-Wpedantic>
93
+ $<$<AND:$<NOT:$<CXX_COMPILER_ID:MSVC>>,$<BOOL:${SAMCORE_WARNINGS_AS_ERRORS}>>:-Werror>)
94
+
95
+ # ---- build modes ---------------------------------------------------------
96
+ # Build-interface targets so the flags reach every final link target
97
+ # (tests, executables, python module) without leaking into the installed
98
+ # package interface.
99
+ add_library(samcore_debug_flags INTERFACE)
100
+ target_compile_options(samcore_debug_flags INTERFACE
101
+ $<$<AND:$<COMPILE_LANG_AND_ID:CXX,GNU,Clang>,$<CONFIG:Debug>,$<BOOL:${SAMCORE_ENABLE_SANITIZERS}>>:-fsanitize=address,undefined;-fno-omit-frame-pointer>)
102
+ target_link_options(samcore_debug_flags INTERFACE
103
+ $<$<AND:$<COMPILE_LANG_AND_ID:CXX,GNU,Clang>,$<CONFIG:Debug>,$<BOOL:${SAMCORE_ENABLE_SANITIZERS}>>:-fsanitize=address,undefined>)
104
+
105
+ add_library(samcore_release_flags INTERFACE)
106
+ target_compile_options(samcore_release_flags INTERFACE
107
+ $<$<COMPILE_LANG_AND_ID:CXX,GNU,Clang>:$<$<CONFIG:Release>:-O2>>
108
+ $<$<AND:$<COMPILE_LANG_AND_ID:CXX,GNU,Clang>,$<CONFIG:Release>,$<BOOL:${SAMCORE_ENABLE_FAST_MATH}>>:-ffast-math>
109
+ $<$<AND:$<COMPILE_LANG_AND_ID:CXX,GNU,Clang>,$<CONFIG:Release>,$<BOOL:${SAMCORE_ENABLE_LTO}>>:-flto>)
110
+ target_link_options(samcore_release_flags INTERFACE
111
+ $<$<AND:$<COMPILE_LANG_AND_ID:CXX,GNU,Clang>,$<CONFIG:Release>,$<BOOL:${SAMCORE_ENABLE_LTO}>>:-flto>)
112
+
113
+ target_link_libraries(samcore PUBLIC
114
+ $<BUILD_INTERFACE:samcore_debug_flags>
115
+ $<BUILD_INTERFACE:samcore_release_flags>)
116
+
117
+ set_target_properties(samcore PROPERTIES
118
+ POSITION_INDEPENDENT_CODE ON
119
+ CXX_VISIBILITY_PRESET hidden
120
+ VISIBILITY_INLINES_HIDDEN ON)
121
+
122
+ # ---- install / export ----------------------------------------------------
123
+ include(GNUInstallDirs)
124
+ include(CMakePackageConfigHelpers)
125
+
126
+ install(TARGETS samcore
127
+ EXPORT samcoreTargets
128
+ ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
129
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
130
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
131
+ install(DIRECTORY include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
132
+
133
+ install(EXPORT samcoreTargets
134
+ FILE samcoreTargets.cmake
135
+ NAMESPACE samcore::
136
+ DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/samcore)
137
+
138
+ configure_package_config_file(
139
+ cmake/samcoreConfig.cmake.in
140
+ "${CMAKE_CURRENT_BINARY_DIR}/samcoreConfig.cmake"
141
+ INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/samcore)
142
+ write_basic_package_version_file(
143
+ "${CMAKE_CURRENT_BINARY_DIR}/samcoreConfigVersion.cmake"
144
+ VERSION ${PROJECT_VERSION}
145
+ COMPATIBILITY SameMajorVersion)
146
+
147
+ install(FILES
148
+ "${CMAKE_CURRENT_BINARY_DIR}/samcoreConfig.cmake"
149
+ "${CMAKE_CURRENT_BINARY_DIR}/samcoreConfigVersion.cmake"
150
+ DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/samcore)
151
+
152
+ # ---- tests & executables ---------------------------------------------------
153
+ if(SAMCORE_BUILD_TESTS)
154
+ enable_testing()
155
+ add_subdirectory(tests)
156
+ endif()
157
+
158
+ if(SAMCORE_BUILD_EXECUTABLES)
159
+ add_subdirectory(executables)
160
+ endif()
161
+
162
+ if(SAMCORE_BUILD_PYTHON)
163
+ # ---- nanobind extension (samcore python package) ------------------------
164
+ # Built via scikit-build-core (pip install .), which exposes nanobind to
165
+ # CMake automatically; the execute_process lookup is only a fallback for
166
+ # direct -DSAMCORE_BUILD_PYTHON=ON builds outside pip.
167
+ find_package(Python COMPONENTS Interpreter Development.Module REQUIRED)
168
+
169
+ find_package(nanobind CONFIG QUIET)
170
+ if(NOT nanobind_FOUND)
171
+ execute_process(COMMAND "${Python_EXECUTABLE}" -m nanobind --cmake_dir
172
+ OUTPUT_VARIABLE NB_CMAKE_DIR OUTPUT_STRIP_TRAILING_WHITESPACE)
173
+ if(NB_CMAKE_DIR)
174
+ list(APPEND CMAKE_PREFIX_PATH "${NB_CMAKE_DIR}")
175
+ endif()
176
+ find_package(nanobind CONFIG REQUIRED)
177
+ endif()
178
+
179
+ # Per-version extension (no abi3): keeps wheel builds uniform across the
180
+ # whole CPython range. Bindings are split per type under src/bindings/.
181
+ nanobind_add_module(_samcore NB_DOMAIN numpy
182
+ src/bindings/module.cpp
183
+ src/bindings/header.cpp
184
+ src/bindings/labels.cpp
185
+ src/bindings/scan.cpp
186
+ src/bindings/dataset.cpp
187
+ src/bindings/submodules.cpp)
188
+ target_link_libraries(_samcore PRIVATE samcore)
189
+
190
+ if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
191
+ target_compile_options(_samcore PRIVATE -w)
192
+ endif()
193
+
194
+ install(TARGETS _samcore LIBRARY DESTINATION samcore)
195
+
196
+ # package version, generated from version.txt (single source of truth)
197
+ configure_file("${CMAKE_SOURCE_DIR}/cmake/_version.py.in"
198
+ "${CMAKE_CURRENT_BINARY_DIR}/_version.py" @ONLY)
199
+ install(FILES "${CMAKE_CURRENT_BINARY_DIR}/_version.py"
200
+ DESTINATION samcore)
201
+
202
+ # ---- typed stubs (nanobind stubgen) -------------------------------------
203
+ # Generate .pyi stubs + the py.typed marker at build time and ship them
204
+ # in the wheel. stubgen must be able to import the assembled samcore
205
+ # package (pure Python modules + generated _version.py + the compiled
206
+ # extension), so stage an importable copy of the package in the build
207
+ # tree, run stubgen against it, and install the results. Stub generation
208
+ # is optional tooling and never fails the build (see cmake/stubgen.cmake);
209
+ # the install below uses OPTIONAL so a skipped run ships no stubs.
210
+ execute_process(
211
+ COMMAND "${Python_EXECUTABLE}" -c "import nanobind.stubgen"
212
+ RESULT_VARIABLE SAMCORE_HAS_NB_STUBGEN OUTPUT_QUIET ERROR_QUIET)
213
+ if(SAMCORE_HAS_NB_STUBGEN EQUAL 0)
214
+ set(SAMCORE_STUB_DIR "${CMAKE_CURRENT_BINARY_DIR}/_stub")
215
+ set(SAMCORE_STUB_PKG "${SAMCORE_STUB_DIR}/samcore")
216
+
217
+ add_custom_command(
218
+ OUTPUT "${SAMCORE_STUB_PKG}/py.typed"
219
+ COMMAND ${CMAKE_COMMAND} -E rm -rf "${SAMCORE_STUB_PKG}"
220
+ COMMAND ${CMAKE_COMMAND} -E copy_directory
221
+ "${CMAKE_CURRENT_SOURCE_DIR}/src/samcore" "${SAMCORE_STUB_PKG}"
222
+ COMMAND ${CMAKE_COMMAND} -E rm -rf "${SAMCORE_STUB_PKG}/__pycache__"
223
+ COMMAND ${CMAKE_COMMAND} -E copy
224
+ "${CMAKE_CURRENT_BINARY_DIR}/_version.py"
225
+ "${SAMCORE_STUB_PKG}/_version.py"
226
+ COMMAND ${CMAKE_COMMAND} -E copy
227
+ "$<TARGET_FILE:_samcore>"
228
+ "${SAMCORE_STUB_PKG}/$<TARGET_FILE_NAME:_samcore>"
229
+ COMMAND ${CMAKE_COMMAND} -DSTUB_DIR=${SAMCORE_STUB_DIR}
230
+ -DSTUB_PKG=${SAMCORE_STUB_PKG}
231
+ -DPYTHON=${Python_EXECUTABLE}
232
+ -P "${CMAKE_CURRENT_SOURCE_DIR}/cmake/stubgen.cmake"
233
+ DEPENDS _samcore "${CMAKE_CURRENT_BINARY_DIR}/_version.py"
234
+ COMMENT "Generating samcore .pyi stubs (nanobind stubgen)"
235
+ VERBATIM)
236
+ add_custom_target(samcore_stub ALL
237
+ DEPENDS "${SAMCORE_STUB_PKG}/py.typed")
238
+
239
+ install(FILES
240
+ "${SAMCORE_STUB_PKG}/__init__.pyi"
241
+ "${SAMCORE_STUB_PKG}/_samcore.pyi"
242
+ "${SAMCORE_STUB_PKG}/_scan.pyi"
243
+ "${SAMCORE_STUB_PKG}/_labels.pyi"
244
+ "${SAMCORE_STUB_PKG}/_dataset.pyi"
245
+ "${SAMCORE_STUB_PKG}/_io.pyi"
246
+ "${SAMCORE_STUB_PKG}/py.typed"
247
+ DESTINATION samcore
248
+ OPTIONAL)
249
+ else()
250
+ message(STATUS
251
+ "nanobind.stubgen not importable by ${Python_EXECUTABLE}; "
252
+ "skipping .pyi stub generation")
253
+ endif()
254
+ endif()
samcore-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Freiburg INATECH BDS
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.