snapshot-imager 0.2.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 (31) hide show
  1. snapshot_imager-0.2.0/.github/dependabot.yml +10 -0
  2. snapshot_imager-0.2.0/.github/workflows/ci.yml +124 -0
  3. snapshot_imager-0.2.0/.github/workflows/publish.yml +60 -0
  4. snapshot_imager-0.2.0/.gitignore +174 -0
  5. snapshot_imager-0.2.0/.pre-commit-config.yaml +16 -0
  6. snapshot_imager-0.2.0/LICENSE +21 -0
  7. snapshot_imager-0.2.0/PKG-INFO +157 -0
  8. snapshot_imager-0.2.0/README.md +115 -0
  9. snapshot_imager-0.2.0/docs/notebooks/imaging_demo.ipynb +0 -0
  10. snapshot_imager-0.2.0/pyproject.toml +102 -0
  11. snapshot_imager-0.2.0/setup.cfg +4 -0
  12. snapshot_imager-0.2.0/snapshot_imager/__init__.py +63 -0
  13. snapshot_imager-0.2.0/snapshot_imager/coordinates.py +161 -0
  14. snapshot_imager-0.2.0/snapshot_imager/core.py +204 -0
  15. snapshot_imager-0.2.0/snapshot_imager/data_models.py +136 -0
  16. snapshot_imager-0.2.0/snapshot_imager/imager.py +639 -0
  17. snapshot_imager-0.2.0/snapshot_imager/preprocessing.py +188 -0
  18. snapshot_imager-0.2.0/snapshot_imager.egg-info/PKG-INFO +157 -0
  19. snapshot_imager-0.2.0/snapshot_imager.egg-info/SOURCES.txt +29 -0
  20. snapshot_imager-0.2.0/snapshot_imager.egg-info/dependency_links.txt +1 -0
  21. snapshot_imager-0.2.0/snapshot_imager.egg-info/requires.txt +18 -0
  22. snapshot_imager-0.2.0/snapshot_imager.egg-info/scm_file_list.json +26 -0
  23. snapshot_imager-0.2.0/snapshot_imager.egg-info/scm_version.json +8 -0
  24. snapshot_imager-0.2.0/snapshot_imager.egg-info/top_level.txt +1 -0
  25. snapshot_imager-0.2.0/tests/conftest.py +92 -0
  26. snapshot_imager-0.2.0/tests/test_coordinates.py +139 -0
  27. snapshot_imager-0.2.0/tests/test_core.py +107 -0
  28. snapshot_imager-0.2.0/tests/test_data_models.py +90 -0
  29. snapshot_imager-0.2.0/tests/test_gpu.py +47 -0
  30. snapshot_imager-0.2.0/tests/test_imager.py +218 -0
  31. snapshot_imager-0.2.0/tests/test_preprocessing.py +127 -0
@@ -0,0 +1,10 @@
1
+ version: 2
2
+ updates:
3
+ # Keep GitHub Actions versions current (one grouped PR per month).
4
+ - package-ecosystem: github-actions
5
+ directory: /
6
+ schedule:
7
+ interval: monthly
8
+ groups:
9
+ actions:
10
+ patterns: ["*"]
@@ -0,0 +1,124 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ workflow_dispatch:
8
+
9
+ permissions:
10
+ contents: read
11
+
12
+ concurrency:
13
+ group: ${{ github.workflow }}-${{ github.ref }}
14
+ cancel-in-progress: ${{ github.event_name == 'pull_request' }}
15
+
16
+ jobs:
17
+ lint:
18
+ name: Lint
19
+ runs-on: ubuntu-latest
20
+ steps:
21
+ - uses: actions/checkout@v7
22
+
23
+ - uses: actions/setup-python@v7
24
+ with:
25
+ python-version: "3.13"
26
+
27
+ - name: Run pre-commit hooks
28
+ run: |
29
+ python -m pip install pre-commit
30
+ pre-commit run --all-files --show-diff-on-failure --color=always
31
+
32
+ tests:
33
+ name: Tests (${{ matrix.os }}, py${{ matrix.python-version }})
34
+ runs-on: ${{ matrix.os }}
35
+ strategy:
36
+ fail-fast: false
37
+ matrix:
38
+ os: [ubuntu-latest, macos-latest]
39
+ python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
40
+ # healpy (pulled in via hera_cal -> aipy) ships macOS arm64 wheels for
41
+ # Python <= 3.12 that bundle their own libomp and libunwind. Loading
42
+ # them alongside finufft's libomp segfaults in finufft's first OpenMP
43
+ # call. The cp313+ healpy wheels don't bundle them, so macOS is only
44
+ # tested on Python 3.13+.
45
+ exclude:
46
+ - os: macos-latest
47
+ python-version: "3.10"
48
+ - os: macos-latest
49
+ python-version: "3.11"
50
+ - os: macos-latest
51
+ python-version: "3.12"
52
+
53
+ steps:
54
+ - uses: actions/checkout@v7
55
+ with:
56
+ fetch-depth: 0 # setuptools-scm needs tags to compute the version
57
+
58
+ - uses: actions/setup-python@v7
59
+ with:
60
+ python-version: ${{ matrix.python-version }}
61
+ cache: pip
62
+ cache-dependency-path: pyproject.toml
63
+
64
+ - name: Install package
65
+ run: |
66
+ python -m pip install --upgrade pip
67
+ python -m pip install ".[test]"
68
+
69
+ - name: Run tests
70
+ run: |
71
+ python -m pytest --cov --cov-report=xml --cov-report=term-missing
72
+
73
+ - name: Upload coverage to Codecov
74
+ if: matrix.os == 'ubuntu-latest' && matrix.python-version == '3.13'
75
+ uses: codecov/codecov-action@v7
76
+ with:
77
+ token: ${{ secrets.CODECOV_TOKEN }}
78
+ files: ./coverage.xml
79
+ fail_ci_if_error: false
80
+
81
+ tests-min-deps:
82
+ # Install the lowest versions allowed by pyproject.toml so the declared
83
+ # dependency floors stay honest.
84
+ name: Tests (minimum dependency versions)
85
+ runs-on: ubuntu-latest
86
+ steps:
87
+ - uses: actions/checkout@v7
88
+ with:
89
+ fetch-depth: 0
90
+
91
+ - uses: actions/setup-python@v7
92
+ with:
93
+ python-version: "3.10"
94
+
95
+ - name: Install package with lowest direct dependencies
96
+ run: |
97
+ python -m pip install uv
98
+ uv pip install --system --resolution lowest-direct ".[test]"
99
+
100
+ - name: Run tests
101
+ run: python -m pytest
102
+
103
+ build:
104
+ name: Build distributions
105
+ runs-on: ubuntu-latest
106
+ steps:
107
+ - uses: actions/checkout@v7
108
+ with:
109
+ fetch-depth: 0
110
+
111
+ - uses: actions/setup-python@v7
112
+ with:
113
+ python-version: "3.13"
114
+
115
+ - name: Build sdist and wheel
116
+ run: |
117
+ python -m pip install build twine
118
+ python -m build
119
+ python -m twine check --strict dist/*
120
+
121
+ - name: Smoke-test the built wheel
122
+ run: |
123
+ python -m pip install dist/*.whl
124
+ cd /tmp && python -c "import snapshot_imager; print(snapshot_imager.__version__)"
@@ -0,0 +1,60 @@
1
+ name: Publish to PyPI
2
+
3
+ # Pushing a version tag (e.g. `git tag v0.2.0 && git push origin v0.2.0`)
4
+ # builds the sdist + wheel and uploads them to PyPI via trusted publishing.
5
+ on:
6
+ push:
7
+ tags: ["v*"]
8
+
9
+ permissions:
10
+ contents: read
11
+
12
+ jobs:
13
+ build:
14
+ name: Build distributions
15
+ runs-on: ubuntu-latest
16
+ steps:
17
+ - uses: actions/checkout@v7
18
+ with:
19
+ fetch-depth: 0 # setuptools-scm needs tags to compute the version
20
+
21
+ - uses: actions/setup-python@v7
22
+ with:
23
+ python-version: "3.13"
24
+
25
+ - name: Build sdist and wheel
26
+ run: |
27
+ python -m pip install build twine
28
+ python -m build
29
+ python -m twine check --strict dist/*
30
+
31
+ - name: Check that the built version matches the tag
32
+ run: |
33
+ expected="${GITHUB_REF_NAME#v}"
34
+ ls dist
35
+ test -f "dist/snapshot_imager-${expected}.tar.gz" || {
36
+ echo "::error::Built version does not match tag ${GITHUB_REF_NAME}"
37
+ exit 1
38
+ }
39
+
40
+ - uses: actions/upload-artifact@v7
41
+ with:
42
+ name: dist
43
+ path: dist/
44
+
45
+ publish:
46
+ name: Upload to PyPI
47
+ needs: build
48
+ runs-on: ubuntu-latest
49
+ environment:
50
+ name: pypi
51
+ url: https://pypi.org/p/snapshot-imager
52
+ permissions:
53
+ id-token: write # required for PyPI trusted publishing
54
+ steps:
55
+ - uses: actions/download-artifact@v8
56
+ with:
57
+ name: dist
58
+ path: dist/
59
+
60
+ - uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,174 @@
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+
6
+ # C extensions
7
+ *.so
8
+
9
+ # Distribution / packaging
10
+ .Python
11
+ build/
12
+ develop-eggs/
13
+ dist/
14
+ downloads/
15
+ eggs/
16
+ .eggs/
17
+ lib/
18
+ lib64/
19
+ parts/
20
+ sdist/
21
+ var/
22
+ wheels/
23
+ share/python-wheels/
24
+ *.egg-info/
25
+ .installed.cfg
26
+ *.egg
27
+ MANIFEST
28
+
29
+ # PyInstaller
30
+ # Usually these files are written by a python script from a template
31
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
32
+ *.manifest
33
+ *.spec
34
+
35
+ # Installer logs
36
+ pip-log.txt
37
+ pip-delete-this-directory.txt
38
+
39
+ # Unit test / coverage reports
40
+ htmlcov/
41
+ .tox/
42
+ .nox/
43
+ .coverage
44
+ .coverage.*
45
+ .cache
46
+ nosetests.xml
47
+ coverage.xml
48
+ *.cover
49
+ *.py,cover
50
+ .hypothesis/
51
+ .pytest_cache/
52
+ cover/
53
+
54
+ # Translations
55
+ *.mo
56
+ *.pot
57
+
58
+ # Django stuff:
59
+ *.log
60
+ local_settings.py
61
+ db.sqlite3
62
+ db.sqlite3-journal
63
+
64
+ # Flask stuff:
65
+ instance/
66
+ .webassets-cache
67
+
68
+ # Scrapy stuff:
69
+ .scrapy
70
+
71
+ # Sphinx documentation
72
+ docs/_build/
73
+
74
+ # PyBuilder
75
+ .pybuilder/
76
+ target/
77
+
78
+ # Jupyter Notebook
79
+ .ipynb_checkpoints
80
+
81
+ # IPython
82
+ profile_default/
83
+ ipython_config.py
84
+
85
+ # pyenv
86
+ # For a library or package, you might want to ignore these files since the code is
87
+ # intended to run in multiple environments; otherwise, check them in:
88
+ # .python-version
89
+
90
+ # pipenv
91
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
92
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
93
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
94
+ # install all needed dependencies.
95
+ #Pipfile.lock
96
+
97
+ # UV
98
+ # Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
99
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
100
+ # commonly ignored for libraries.
101
+ #uv.lock
102
+
103
+ # poetry
104
+ # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
105
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
106
+ # commonly ignored for libraries.
107
+ # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
108
+ #poetry.lock
109
+
110
+ # pdm
111
+ # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
112
+ #pdm.lock
113
+ # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
114
+ # in version control.
115
+ # https://pdm.fming.dev/latest/usage/project/#working-with-version-control
116
+ .pdm.toml
117
+ .pdm-python
118
+ .pdm-build/
119
+
120
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
121
+ __pypackages__/
122
+
123
+ # Celery stuff
124
+ celerybeat-schedule
125
+ celerybeat.pid
126
+
127
+ # SageMath parsed files
128
+ *.sage.py
129
+
130
+ # Environments
131
+ .env
132
+ .venv
133
+ env/
134
+ venv/
135
+ ENV/
136
+ env.bak/
137
+ venv.bak/
138
+
139
+ # Spyder project settings
140
+ .spyderproject
141
+ .spyproject
142
+
143
+ # Rope project settings
144
+ .ropeproject
145
+
146
+ # mkdocs documentation
147
+ /site
148
+
149
+ # mypy
150
+ .mypy_cache/
151
+ .dmypy.json
152
+ dmypy.json
153
+
154
+ # Pyre type checker
155
+ .pyre/
156
+
157
+ # pytype static type analyzer
158
+ .pytype/
159
+
160
+ # Cython debug symbols
161
+ cython_debug/
162
+
163
+ # PyCharm
164
+ # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
165
+ # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
166
+ # and can be added to the global gitignore or merged into this file. For a more nuclear
167
+ # option (not recommended) you can uncomment the following to ignore the entire idea folder.
168
+ #.idea/
169
+
170
+ # Ruff stuff:
171
+ .ruff_cache/
172
+
173
+ # PyPI configuration file
174
+ .pypirc
@@ -0,0 +1,16 @@
1
+ repos:
2
+ - repo: https://github.com/pre-commit/pre-commit-hooks
3
+ rev: v6.0.0
4
+ hooks:
5
+ - id: check-added-large-files
6
+ - id: check-ast
7
+ - id: check-merge-conflict
8
+ - id: check-toml
9
+ - id: check-yaml
10
+ - id: debug-statements
11
+
12
+ - repo: https://github.com/astral-sh/ruff-pre-commit
13
+ rev: v0.16.8
14
+ hooks:
15
+ - id: ruff-check
16
+ args: [--fix]
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Tyler Cox
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.
@@ -0,0 +1,157 @@
1
+ Metadata-Version: 2.4
2
+ Name: snapshot_imager
3
+ Version: 0.2.0
4
+ Summary: Snapshot imaging of radio interferometric visibilities using non-uniform FFTs
5
+ Author-email: Tyler Cox <tyler.a.cox@berkeley.edu>
6
+ Maintainer-email: Tyler Cox <tyler.a.cox@berkeley.edu>
7
+ License-Expression: MIT
8
+ Project-URL: Homepage, https://github.com/HERA-Team/snapshot_imager
9
+ Project-URL: Repository, https://github.com/HERA-Team/snapshot_imager
10
+ Project-URL: Issues, https://github.com/HERA-Team/snapshot_imager/issues
11
+ Keywords: radio astronomy,visibility,imaging,interferometry,nufft,HERA
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: Intended Audience :: Science/Research
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3 :: Only
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Programming Language :: Python :: 3.14
22
+ Classifier: Topic :: Scientific/Engineering :: Astronomy
23
+ Requires-Python: >=3.10
24
+ Description-Content-Type: text/markdown
25
+ License-File: LICENSE
26
+ Requires-Dist: numpy>=2.0
27
+ Requires-Dist: astropy>=6.1
28
+ Requires-Dist: finufft>=2.3
29
+ Requires-Dist: tqdm>=4.40
30
+ Requires-Dist: hera-calibration>=3.7.4
31
+ Provides-Extra: gpu
32
+ Requires-Dist: cupy-cuda12x; extra == "gpu"
33
+ Requires-Dist: cufinufft>=2.4; extra == "gpu"
34
+ Provides-Extra: test
35
+ Requires-Dist: pytest>=7.0; extra == "test"
36
+ Requires-Dist: pytest-cov; extra == "test"
37
+ Provides-Extra: dev
38
+ Requires-Dist: snapshot_imager[test]; extra == "dev"
39
+ Requires-Dist: pre-commit; extra == "dev"
40
+ Requires-Dist: ruff; extra == "dev"
41
+ Dynamic: license-file
42
+
43
+ # snapshot_imager
44
+
45
+ [![CI](https://github.com/HERA-Team/snapshot_imager/actions/workflows/ci.yml/badge.svg)](https://github.com/HERA-Team/snapshot_imager/actions/workflows/ci.yml)
46
+ [![codecov](https://codecov.io/gh/HERA-Team/snapshot_imager/graph/badge.svg)](https://codecov.io/gh/HERA-Team/snapshot_imager)
47
+ [![PyPI](https://img.shields.io/pypi/v/snapshot-imager.svg)](https://pypi.org/project/snapshot-imager/)
48
+
49
+ `snapshot_imager` is a Python package for radio interferometric snapshot imaging using Non-Uniform Fast Fourier Transforms (NUFFT). It is designed to efficiently produce dirty image cubes from visibility data, with support for multiple NUFFT strategies (Type 1, Type 3, and multi-frequency synthesis) and optional GPU acceleration via CuPy and cuFINUFFT.
50
+
51
+ ## Installation
52
+
53
+ Install the latest release from PyPI:
54
+
55
+ ```bash
56
+ pip install snapshot-imager
57
+ ```
58
+
59
+ For GPU support (CUDA 12), install the `gpu` extra, which pulls in `cupy-cuda12x` and `cufinufft`:
60
+
61
+ ```bash
62
+ pip install "snapshot-imager[gpu]"
63
+ ```
64
+
65
+ For other CUDA versions, install the matching CuPy wheel (e.g. `cupy-cuda11x` or `cupy-cuda13x`) and `cufinufft` yourself.
66
+
67
+ > **macOS note:** on Apple-silicon Macs, use Python 3.13 or newer. The healpy wheels
68
+ > for Python 3.10–3.12 (installed via `hera_cal`) bundle their own OpenMP runtime,
69
+ > which conflicts with FINUFFT's and crashes on the first imaging call.
70
+
71
+ To install from source:
72
+
73
+ ```bash
74
+ git clone https://github.com/HERA-Team/snapshot_imager.git
75
+ cd snapshot_imager
76
+ pip install .
77
+ ```
78
+
79
+ ## Basic Usage
80
+
81
+ The typical workflow is to unpack HERA `DataContainer` objects into an `ImagingData` container, then pass that to one of the imaging functions.
82
+
83
+ ```python
84
+ import numpy as np
85
+ from snapshot_imager import unpack_data_containers, snapshot_imager_type1
86
+
87
+ # data, flags, and nsamples are hera_cal DataContainer objects
88
+ imaging_data = unpack_data_containers(
89
+ data=data,
90
+ flags=flags,
91
+ nsamples=nsamples,
92
+ pol="ee",
93
+ antpos=antpos,
94
+ freqs=freqs,
95
+ )
96
+
97
+ # Produce a (ntimes, nfreqs, npix, npix) image cube
98
+ result = snapshot_imager_type1(
99
+ imaging_data,
100
+ npix=256,
101
+ fov=10.0, # Field of view in degrees
102
+ use_cupy=False, # Set to True to use GPU acceleration
103
+ )
104
+
105
+ print(result.images.shape) # (ntimes, nfreqs, npix, npix)
106
+ ```
107
+
108
+ The returned `ImageResult` contains the image cube along with the corresponding `l_coords` and `m_coords` (direction cosines) for plotting or downstream analysis.
109
+
110
+ ## GPU Acceleration
111
+
112
+ `snapshot_imager` supports GPU-accelerated imaging via [CuPy](https://cupy.dev/) and [cuFINUFFT](https://github.com/flatironinstitute/finufft). Simply pass `use_cupy=True` to any imaging function:
113
+
114
+ ```python
115
+ result = snapshot_imager_type1(imaging_data, npix=256, fov=10.0, use_cupy=True)
116
+ ```
117
+
118
+ If CuPy or cuFINUFFT are not available, the package will automatically fall back to the CPU implementation with a warning.
119
+
120
+ ## Development
121
+
122
+ Set up a development environment with the test and lint tools, and install the git hooks:
123
+
124
+ ```bash
125
+ pip install -e ".[dev]"
126
+ pre-commit install
127
+ ```
128
+
129
+ Run the test suite (GPU tests are skipped automatically when no GPU is available):
130
+
131
+ ```bash
132
+ pytest --cov
133
+ ```
134
+
135
+ Lint with [ruff](https://docs.astral.sh/ruff/) (this also runs on every commit via pre-commit, and in CI):
136
+
137
+ ```bash
138
+ pre-commit run --all-files
139
+ ```
140
+
141
+ ## Releasing
142
+
143
+ Versions are derived from git tags by [setuptools-scm](https://setuptools-scm.readthedocs.io/), so there is no version string to bump in the code. To publish a release to PyPI:
144
+
145
+ 1. Make sure CI is passing on `main`.
146
+ 2. Tag the release commit and push the tag:
147
+
148
+ ```bash
149
+ git tag -a v0.2.0 -m "v0.2.0"
150
+ git push origin v0.2.0
151
+ ```
152
+
153
+ 3. The [Publish to PyPI](.github/workflows/publish.yml) workflow builds the sdist and wheel and uploads them using PyPI trusted publishing. Optionally, create a GitHub release from the tag to record release notes.
154
+
155
+ ## License
156
+
157
+ MIT
@@ -0,0 +1,115 @@
1
+ # snapshot_imager
2
+
3
+ [![CI](https://github.com/HERA-Team/snapshot_imager/actions/workflows/ci.yml/badge.svg)](https://github.com/HERA-Team/snapshot_imager/actions/workflows/ci.yml)
4
+ [![codecov](https://codecov.io/gh/HERA-Team/snapshot_imager/graph/badge.svg)](https://codecov.io/gh/HERA-Team/snapshot_imager)
5
+ [![PyPI](https://img.shields.io/pypi/v/snapshot-imager.svg)](https://pypi.org/project/snapshot-imager/)
6
+
7
+ `snapshot_imager` is a Python package for radio interferometric snapshot imaging using Non-Uniform Fast Fourier Transforms (NUFFT). It is designed to efficiently produce dirty image cubes from visibility data, with support for multiple NUFFT strategies (Type 1, Type 3, and multi-frequency synthesis) and optional GPU acceleration via CuPy and cuFINUFFT.
8
+
9
+ ## Installation
10
+
11
+ Install the latest release from PyPI:
12
+
13
+ ```bash
14
+ pip install snapshot-imager
15
+ ```
16
+
17
+ For GPU support (CUDA 12), install the `gpu` extra, which pulls in `cupy-cuda12x` and `cufinufft`:
18
+
19
+ ```bash
20
+ pip install "snapshot-imager[gpu]"
21
+ ```
22
+
23
+ For other CUDA versions, install the matching CuPy wheel (e.g. `cupy-cuda11x` or `cupy-cuda13x`) and `cufinufft` yourself.
24
+
25
+ > **macOS note:** on Apple-silicon Macs, use Python 3.13 or newer. The healpy wheels
26
+ > for Python 3.10–3.12 (installed via `hera_cal`) bundle their own OpenMP runtime,
27
+ > which conflicts with FINUFFT's and crashes on the first imaging call.
28
+
29
+ To install from source:
30
+
31
+ ```bash
32
+ git clone https://github.com/HERA-Team/snapshot_imager.git
33
+ cd snapshot_imager
34
+ pip install .
35
+ ```
36
+
37
+ ## Basic Usage
38
+
39
+ The typical workflow is to unpack HERA `DataContainer` objects into an `ImagingData` container, then pass that to one of the imaging functions.
40
+
41
+ ```python
42
+ import numpy as np
43
+ from snapshot_imager import unpack_data_containers, snapshot_imager_type1
44
+
45
+ # data, flags, and nsamples are hera_cal DataContainer objects
46
+ imaging_data = unpack_data_containers(
47
+ data=data,
48
+ flags=flags,
49
+ nsamples=nsamples,
50
+ pol="ee",
51
+ antpos=antpos,
52
+ freqs=freqs,
53
+ )
54
+
55
+ # Produce a (ntimes, nfreqs, npix, npix) image cube
56
+ result = snapshot_imager_type1(
57
+ imaging_data,
58
+ npix=256,
59
+ fov=10.0, # Field of view in degrees
60
+ use_cupy=False, # Set to True to use GPU acceleration
61
+ )
62
+
63
+ print(result.images.shape) # (ntimes, nfreqs, npix, npix)
64
+ ```
65
+
66
+ The returned `ImageResult` contains the image cube along with the corresponding `l_coords` and `m_coords` (direction cosines) for plotting or downstream analysis.
67
+
68
+ ## GPU Acceleration
69
+
70
+ `snapshot_imager` supports GPU-accelerated imaging via [CuPy](https://cupy.dev/) and [cuFINUFFT](https://github.com/flatironinstitute/finufft). Simply pass `use_cupy=True` to any imaging function:
71
+
72
+ ```python
73
+ result = snapshot_imager_type1(imaging_data, npix=256, fov=10.0, use_cupy=True)
74
+ ```
75
+
76
+ If CuPy or cuFINUFFT are not available, the package will automatically fall back to the CPU implementation with a warning.
77
+
78
+ ## Development
79
+
80
+ Set up a development environment with the test and lint tools, and install the git hooks:
81
+
82
+ ```bash
83
+ pip install -e ".[dev]"
84
+ pre-commit install
85
+ ```
86
+
87
+ Run the test suite (GPU tests are skipped automatically when no GPU is available):
88
+
89
+ ```bash
90
+ pytest --cov
91
+ ```
92
+
93
+ Lint with [ruff](https://docs.astral.sh/ruff/) (this also runs on every commit via pre-commit, and in CI):
94
+
95
+ ```bash
96
+ pre-commit run --all-files
97
+ ```
98
+
99
+ ## Releasing
100
+
101
+ Versions are derived from git tags by [setuptools-scm](https://setuptools-scm.readthedocs.io/), so there is no version string to bump in the code. To publish a release to PyPI:
102
+
103
+ 1. Make sure CI is passing on `main`.
104
+ 2. Tag the release commit and push the tag:
105
+
106
+ ```bash
107
+ git tag -a v0.2.0 -m "v0.2.0"
108
+ git push origin v0.2.0
109
+ ```
110
+
111
+ 3. The [Publish to PyPI](.github/workflows/publish.yml) workflow builds the sdist and wheel and uploads them using PyPI trusted publishing. Optionally, create a GitHub release from the tag to record release notes.
112
+
113
+ ## License
114
+
115
+ MIT