././@PaxHeader 0000000 0000000 0000000 00000000034 00000000000 010212 x ustar 00 28 mtime=1739993677.3931267
astropy_healpix-1.1.2/ 0000755 0001751 0000166 00000000000 14755431115 014337 5 ustar 00runner docker ././@PaxHeader 0000000 0000000 0000000 00000000034 00000000000 010212 x ustar 00 28 mtime=1739993677.3791268
astropy_healpix-1.1.2/.github/ 0000755 0001751 0000166 00000000000 14755431115 015677 5 ustar 00runner docker ././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1739993667.0
astropy_healpix-1.1.2/.github/dependabot.yml 0000644 0001751 0000166 00000000160 14755431103 020521 0 ustar 00runner docker version: 2
updates:
- package-ecosystem: github-actions
directory: /
schedule:
interval: weekly
././@PaxHeader 0000000 0000000 0000000 00000000033 00000000000 010211 x ustar 00 27 mtime=1739993677.380127
astropy_healpix-1.1.2/.github/workflows/ 0000755 0001751 0000166 00000000000 14755431115 017734 5 ustar 00runner docker ././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1739993667.0
astropy_healpix-1.1.2/.github/workflows/ci_cron_weekly.yml 0000644 0001751 0000166 00000001464 14755431103 023455 0 ustar 00runner docker # GitHub Actions workflow that runs on a cron schedule.
name: Cron Scheduled CI Tests
on:
schedule:
# run at 6am UTC on Mondays
- cron: '0 6 * * 1'
jobs:
# Testing links in documents is a good example of something to run on a schedule
# to catch links that stop working for some reason.
doc_test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Set up Python to build docs with sphinx
uses: actions/setup-python@42375524e23c412d93fb67b49958b491fce71c38 # v5.4.0
with:
python-version: 3.10
- name: Install base dependencies
run: |
python -m pip install --upgrade pip
python -m pip install tox
- name: Check links in docs using tox
run: |
tox -e linkcheck
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1739993667.0
astropy_healpix-1.1.2/.github/workflows/ci_tests.yml 0000644 0001751 0000166 00000005665 14755431103 022305 0 ustar 00runner docker # GitHub Actions workflow for testing and continuous integration.
#
# This file performs testing using tox and tox.ini to define and configure the test environments.
name: CI
on:
push:
branches:
- main # GitHub now defaults to 'main' as the name of the primary branch. Change this as needed.
# tags: # run CI if specific tags are pushed
pull_request:
# branches: # only build on PRs against 'main' if you need to further limit when CI is run.
# - main
jobs:
# Github Actions supports ubuntu, windows, and macos virtual environments:
# https://help.github.com/en/actions/reference/virtual-environments-for-github-hosted-runners
ci_tests:
name: ${{ matrix.name }}
runs-on: ${{ matrix.os }}
strategy:
matrix:
include:
- name: Code style checks
os: ubuntu-latest
python: 3.x
toxenv: codestyle
- name: Python 3.10 with minimal dependencies
os: ubuntu-latest
python: '3.10'
toxenv: py310-test
- name: Python 3.11 with all optional dependencies and coverage checking
os: ubuntu-latest
python: '3.11'
toxenv: py311-test-alldeps-cov
- name: macOS - Python 3.11 with all optional dependencies
os: macos-latest
python: '3.11'
toxenv: py311-test-alldeps
- name: Windows - Python 3.11 with minimal dependencies
os: windows-latest
python: '3.11'
toxenv: py311-test
- name: Python 3.12 with all optional dependencies and pre-releases
os: ubuntu-latest
python: '3.12'
toxenv: py312-test-alldeps-predeps
- name: Python 3.10 with oldest supported version of all dependencies
os: ubuntu-22.04
python: '3.10'
toxenv: py310-test-oldestdeps
- name: Python 3.13 with latest dev versions of key dependencies
os: ubuntu-latest
python: '3.13'
toxenv: py313-test-devdeps
# - name: Test building of Sphinx docs
# os: ubuntu-latest
# python: 3.x
# toxenv: build_docs
steps:
- name: Checkout code
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
fetch-depth: 0
- name: Set up Python ${{ matrix.python }} on ${{ matrix.os }}
uses: actions/setup-python@42375524e23c412d93fb67b49958b491fce71c38 # v5.4.0
with:
python-version: ${{ matrix.python }}
- name: Install base dependencies
run: |
python -m pip install --upgrade pip
python -m pip install tox
- name: Test with tox
run: |
tox -e ${{ matrix.toxenv }}
- name: Upload coverage to codecov
if: "contains(matrix.toxenv, '-cov')"
uses: codecov/codecov-action@13ce06bfc6bbe3ecf90edbbf1bc32fe5978ca1d3 # v5.3.1
with:
file: ./coverage.xml
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1739993667.0
astropy_healpix-1.1.2/.github/workflows/publish.yml 0000644 0001751 0000166 00000002046 14755431103 022124 0 ustar 00runner docker name: Publish
on:
schedule:
# run every day at 4am UTC
- cron: '0 4 * * *'
workflow_dispatch:
pull_request:
push:
branches:
- main
tags:
- v*
jobs:
publish:
if: (github.event_name != 'pull_request' || contains(github.event.pull_request.labels.*.name, 'Build wheels'))
uses: OpenAstronomy/github-actions-workflows/.github/workflows/publish.yml@8c0fde6f7e926df6ed7057255d29afa9c1ad5320 # v1.16.0
secrets:
pypi_token: ${{ secrets.pypi_token }}
anaconda_token: ${{ secrets.anaconda_token }}
with:
targets: |
- cp3*-manylinux_x86_64
- cp3*-musllinux_x86_64
- cp3*-win32
- cp3*-win_amd64
- cp3*-macosx_x86_64
- cp3*-macosx_arm64
- target: cp3*-manylinux_aarch64
runs-on: ubuntu-24.04-arm
# Developer wheels
upload_to_anaconda: ${{ (github.event_name == 'schedule' || github.event_name == 'workflow_dispatch') }}
anaconda_user: astropy
anaconda_package: astropy-healpix
anaconda_keep_n_latest: 10
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1739993667.0
astropy_healpix-1.1.2/.gitignore 0000644 0001751 0000166 00000001466 14755431103 016333 0 ustar 00runner docker # Compiled files
*.py[cod]
*.a
*.o
*.so
__pycache__
# Ignore .c files by default to avoid including generated code. If you want to
# add a non-generated .c extension, use `git add -f filename.c`.
#*.c
# Other generated files
*/_compiler.c
*/version.py
*/cython_version.py
htmlcov
.coverage
MANIFEST
.ipynb_checkpoints
# Sphinx
docs/api
docs/_build
# Eclipse editor project files
.project
.pydevproject
.settings
# Pycharm editor project files
.idea
# Floobits project files
.floo
.flooignore
# Visual Studio Code project files
.vscode
# Packages/installer info
*.egg
*.egg-info
dist
build
eggs
.eggs
parts
bin
var
sdist
develop-eggs
.installed.cfg
distribute-*.tar.gz
# Other
.cache
.tox
.*.sw[op]
*~
.project
.pydevproject
.settings
pip-wheel-metadata/
# Mac OSX
.DS_Store
# pytest cache folder
v
.hypothesis
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1739993667.0
astropy_healpix-1.1.2/.readthedocs.yml 0000644 0001751 0000166 00000000411 14755431103 017416 0 ustar 00runner docker version: 2
build:
os: ubuntu-22.04
tools:
python: "3.11"
python:
install:
- method: pip
path: .
extra_requirements:
- docs
- all
formats: []
sphinx:
builder: html
configuration: docs/conf.py
fail_on_warning: true
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1739993667.0
astropy_healpix-1.1.2/CHANGES.rst 0000644 0001751 0000166 00000007362 14755431103 016146 0 ustar 00runner docker .. _changes:
*******
Changes
*******
1.1.2 (2025-02-19)
==================
- Correctly handle non-finite longitude and latitude values in
``bilinear_interpolation_weights``. In previous versions, this function would
have tripped a C assertion and fatally aborted the Python interpreter. Now,
it will return NaN. [#252]
1.1.1 (2025-02-18)
==================
- Remove dependency on obsolete Astropy test runner function. [#249]
1.1.0 (2025-02-03)
==================
- Fix unit tests so that they pass under Numpy 1.x and 2.x. [#234]
- Require python >= 3.10, numpy >= 1.25, and astropy >= 5. [#239]
- Fix broken unit tests that were reported by Debian. [#237]
1.0.3 (2024-04-05)
==================
- Require astropy 4 or newer. [#219]
1.0.2 (2023-12-12)
==================
- lonlat_to_healpix now correctly returns -1 if the longitude or latitude is
NaN or infinite. [#208]
1.0.1 (2023-11-28)
==================
- Allow building using any version of Numpy between 1.25 and 2. [#201]
- Build wheels for PyPI. [#200]
1.0.0 (2023-08-21)
==================
- Drop support for Python 3.7 and 3.8, which are not supported by the latest
minor release of Numpy (1.25).
- Build binary wheels using the Python limited API.
- Remove warning about API stability. The API is now considered stable.
0.7 (2022-09-15)
================
- Added new methods ``healpix_to_xyz`` and ``xyz_to_healpix`` to
the high level interface. [#153]
- The ``frame`` keyword argument for the high-level ``HEALPix`` class may now
be a frame name, frame instance, or frame class. [#156]
- On instantiation, the ``HEALPix`` class checks the ``order`` argument. [#162]
- Drop support for Python 3.6, which has passed end-of-life. [#166]
0.6 (2021-03-10)
================
- Update package infrastructure to follow APE17 guidelines. [#142]
- Added new functions ``healpix_to_xyz`` and ``xyz_to_healpix`` to
convert to/from cartesian coordinates. [#141]
- Add ``HEALPix.level`` property to the high-level interface.
This is a shortcut for the ``nside_to_level`` function. [#147]
0.5 (2019-11-25)
================
- Update package infrastructure to use ``setup.cfg``. [#134]
- Make sure that Numpy is declared as a build-time dependency. [#134]
- Update astropy-helpers to v3.2.2. [#134]
- Update minimum required Python version to 3.6. [#125]
- Add ``HEALPix.from_header``. [#127]
- Clean up C code to avoid compilation warnings. [#118, #119, #120, #121, #122, #123]
- Fix unit tests on 32-bit architectures. [#117]
- Fix compatibility with Numpy 1.16 and later. [#116]
0.4 (2018-12-18)
================
- Healpix rangesearch cleanup [#113]
- Update astropy-helpers to v2.0.8 [#112]
- Rewrite core module in C to make ``healpix_to_lonlat`` and
``lonlat_to_healpix`` broadcastable over both pixel index and nside. [#110]
0.3.1 (2018-10-24)
==================
- Ensure .c files are included in tar file.
0.3 (2018-10-24)
================
- Remove OpenMP from astropy-healpix [#108]
- Fix bilinear interpolation of invalid values [#106]
- Add uniq to (level, ipix) and inverse function [#105]
- compute z more stably; improve on z2dec [#101]
- use more stable cos(Dec) term [#94]
- Fix get_interp_weights for phi=None case [#89]
- Add pix2vec, vec2pix, ang2vec [#73]
- Add ``pixel_resolution_to_nside`` function. [#31]
0.2 (2017-10-15)
================
- Expand benchmarks to include ang2pix, nest2ring and ring2nest. [#62]
- Use OpenMP to parallelize the Cython wrappers. [#59]
- Renamed the ``healpix_neighbours`` function to ``neighbours`` and added
a wrapper to the high-level class. [#61]
- Fix bilinear interpolation which was being done incorrectly, and added
a new ``bilinear_interpolation_weights`` function to get the interpolation
weights. [#63]
0.1 (2017-10-01)
================
- Initial release
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1739993667.0
astropy_healpix-1.1.2/LICENSE.md 0000644 0001751 0000166 00000002727 14755431103 015750 0 ustar 00runner docker Copyright (c) 2016-2018, Astropy Developers
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this
list of conditions and the following disclaimer in the documentation and/or
other materials provided with the distribution.
* Neither the name of the Astropy Team nor the names of its contributors may be
used to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1739993667.0
astropy_healpix-1.1.2/MANIFEST.in 0000644 0001751 0000166 00000000450 14755431103 016071 0 ustar 00runner docker include README.rst
include CHANGES.rst
include setup.cfg
include LICENSE.md
include pyproject.toml
recursive-include astropy_healpix *.c *.h
recursive-include docs *
recursive-include licenses *
recursive-include scripts *
prune build
prune docs/_build
prune docs/api
global-exclude *.pyc *.o
././@PaxHeader 0000000 0000000 0000000 00000000034 00000000000 010212 x ustar 00 28 mtime=1739993677.3931267
astropy_healpix-1.1.2/PKG-INFO 0000644 0001751 0000166 00000010041 14755431115 015430 0 ustar 00runner docker Metadata-Version: 2.2
Name: astropy_healpix
Version: 1.1.2
Summary: BSD-licensed HEALPix for Astropy
Home-page: https://github.com/astropy/astropy-healpix
Author: Astropy Developers
Author-email: astropy.team@gmail.com
License: BSD 3-Clause
Keywords: astronomy,astrophysics,astropy,healpix,coordinates
Classifier: Intended Audience :: Science/Research
Classifier: License :: OSI Approved :: BSD License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: C
Classifier: Programming Language :: Cython
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: Implementation :: CPython
Classifier: Topic :: Scientific/Engineering :: Astronomy
Classifier: Topic :: Scientific/Engineering :: Physics
Requires-Python: >=3.10
Description-Content-Type: text/x-rst
License-File: LICENSE.md
Requires-Dist: numpy>=1.25
Requires-Dist: astropy>=5
Provides-Extra: test
Requires-Dist: pytest-astropy; extra == "test"
Requires-Dist: hypothesis; extra == "test"
Provides-Extra: docs
Requires-Dist: sphinx-astropy; extra == "docs"
Requires-Dist: matplotlib; extra == "docs"
astropy healpix
---------------
.. image:: https://img.shields.io/badge/powered%20by-AstroPy-orange.svg?style=flat
:target: https://www.astropy.org
:alt: Powered by Astropy Badge
.. image:: https://github.com/astropy/astropy-healpix/workflows/CI/badge.svg
:target: https://github.com/astropy/astropy-healpix/actions
:alt: GitHub Actions CI Status
.. image:: https://codecov.io/gh/astropy/astropy-healpix/branch/main/graph/badge.svg
:target: https://codecov.io/gh/astropy/astropy-healpix
:alt: Coverage Status
.. image:: https://readthedocs.org/projects/astropy-healpix/badge/?version=latest
:target: http://astropy-healpix.readthedocs.io/en/latest/?badge=latest
:alt: Doc
This is a BSD-licensed HEALPix package developed by the Astropy project
and based on C code written by Dustin Lang in `astrometry.net `__. See the
`Documentation `__ for
information about installing and using this package.
License
-------
This project is Copyright (c) Astropy Developers and licensed under
the terms of the BSD 3-Clause license. This package is based upon
the `Astropy package template `_
which is licensed under the BSD 3-clause license. See the licenses folder for
more information.
Contributing
------------
We love contributions! astropy-healpix is open source,
built on open source, and we'd love to have you hang out in our community.
**Imposter syndrome disclaimer**: We want your help. No, really.
There may be a little voice inside your head that is telling you that you're not
ready to be an open source contributor; that your skills aren't nearly good
enough to contribute. What could you possibly offer a project like this one?
We assure you - the little voice in your head is wrong. If you can write code at
all, you can contribute code to open source. Contributing to open source
projects is a fantastic way to advance one's coding skills. Writing perfect code
isn't the measure of a good developer (that would disqualify all of us!); it's
trying to create something, making mistakes, and learning from those
mistakes. That's how we all improve, and we are happy to help others learn.
Being an open source contributor doesn't just mean writing code, either. You can
help out by writing documentation, tests, or even giving feedback about the
project (and yes - that includes giving feedback about the contribution
process). Some of these contributions may be the most valuable to the project as
a whole, because you're coming to the project with fresh eyes, so you can see
the errors and assumptions that seasoned contributors have glossed over.
Note: This disclaimer was originally written by
`Adrienne Lowe `_ for a
`PyCon talk `_, and was adapted by
astropy-healpix based on its use in the README file for the
`MetPy project `_.
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1739993667.0
astropy_healpix-1.1.2/README.rst 0000644 0001751 0000166 00000005674 14755431103 016037 0 ustar 00runner docker astropy healpix
---------------
.. image:: https://img.shields.io/badge/powered%20by-AstroPy-orange.svg?style=flat
:target: https://www.astropy.org
:alt: Powered by Astropy Badge
.. image:: https://github.com/astropy/astropy-healpix/workflows/CI/badge.svg
:target: https://github.com/astropy/astropy-healpix/actions
:alt: GitHub Actions CI Status
.. image:: https://codecov.io/gh/astropy/astropy-healpix/branch/main/graph/badge.svg
:target: https://codecov.io/gh/astropy/astropy-healpix
:alt: Coverage Status
.. image:: https://readthedocs.org/projects/astropy-healpix/badge/?version=latest
:target: http://astropy-healpix.readthedocs.io/en/latest/?badge=latest
:alt: Doc
This is a BSD-licensed HEALPix package developed by the Astropy project
and based on C code written by Dustin Lang in `astrometry.net `__. See the
`Documentation `__ for
information about installing and using this package.
License
-------
This project is Copyright (c) Astropy Developers and licensed under
the terms of the BSD 3-Clause license. This package is based upon
the `Astropy package template `_
which is licensed under the BSD 3-clause license. See the licenses folder for
more information.
Contributing
------------
We love contributions! astropy-healpix is open source,
built on open source, and we'd love to have you hang out in our community.
**Imposter syndrome disclaimer**: We want your help. No, really.
There may be a little voice inside your head that is telling you that you're not
ready to be an open source contributor; that your skills aren't nearly good
enough to contribute. What could you possibly offer a project like this one?
We assure you - the little voice in your head is wrong. If you can write code at
all, you can contribute code to open source. Contributing to open source
projects is a fantastic way to advance one's coding skills. Writing perfect code
isn't the measure of a good developer (that would disqualify all of us!); it's
trying to create something, making mistakes, and learning from those
mistakes. That's how we all improve, and we are happy to help others learn.
Being an open source contributor doesn't just mean writing code, either. You can
help out by writing documentation, tests, or even giving feedback about the
project (and yes - that includes giving feedback about the contribution
process). Some of these contributions may be the most valuable to the project as
a whole, because you're coming to the project with fresh eyes, so you can see
the errors and assumptions that seasoned contributors have glossed over.
Note: This disclaimer was originally written by
`Adrienne Lowe `_ for a
`PyCon talk `_, and was adapted by
astropy-healpix based on its use in the README file for the
`MetPy project `_.
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1739993667.0
astropy_healpix-1.1.2/RELEASING.rst 0000644 0001751 0000166 00000001445 14755431103 016403 0 ustar 00runner docker Release instructions
====================
Once the package is ready to release, use ``git tag`` to tag the
release::
git tag -m
e.g::
git tag -m v0.1 v0.1
You can also include the ``-s`` flag to sign the tag if you have
PGP keys set up. Then, push the tag to GitHub, e.g.::
git push upstream v0.1
and the build should happen automatically on GitHub Actions. You can
follow the progress of the build here:
https://github.com/astropy/astropy-healpix/actions/workflows/publish.yml
If there are any failures, you can always delete the tag, fix the
issues, tag the release again, and push the tag to GitHub.
See the `OpenAstronomy GitHub Actions Workflows
`_
for more details about the GitHub Actions set-up.
././@PaxHeader 0000000 0000000 0000000 00000000034 00000000000 010212 x ustar 00 28 mtime=1739993677.3821268
astropy_healpix-1.1.2/astropy_healpix/ 0000755 0001751 0000166 00000000000 14755431115 017552 5 ustar 00runner docker ././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1739993667.0
astropy_healpix-1.1.2/astropy_healpix/__init__.py 0000644 0001751 0000166 00000000411 14755431103 021654 0 ustar 00runner docker # Licensed under a 3-clause BSD style license - see LICENSE.rst
"""
BSD-licensed HEALPix for Astropy.
"""
from .high_level import * # noqa
from .core import * # noqa
try:
from .version import version as __version__
except ImportError:
__version__ = ''
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1739993677.0
astropy_healpix-1.1.2/astropy_healpix/_compiler.c 0000644 0001751 0000166 00000005240 14755431115 021670 0 ustar 00runner docker #include
/***************************************************************************
* Macros for determining the compiler version.
*
* These are borrowed from boost, and majorly abridged to include only
* the compilers we care about.
***************************************************************************/
#define STRINGIZE(X) DO_STRINGIZE(X)
#define DO_STRINGIZE(X) #X
#if defined __clang__
/* Clang C++ emulates GCC, so it has to appear early. */
# define COMPILER "Clang version " __clang_version__
#elif defined(__INTEL_COMPILER) || defined(__ICL) || defined(__ICC) || defined(__ECC)
/* Intel */
# if defined(__INTEL_COMPILER)
# define INTEL_VERSION __INTEL_COMPILER
# elif defined(__ICL)
# define INTEL_VERSION __ICL
# elif defined(__ICC)
# define INTEL_VERSION __ICC
# elif defined(__ECC)
# define INTEL_VERSION __ECC
# endif
# define COMPILER "Intel C compiler version " STRINGIZE(INTEL_VERSION)
#elif defined(__GNUC__)
/* gcc */
# define COMPILER "GCC version " __VERSION__
#elif defined(__SUNPRO_CC)
/* Sun Workshop Compiler */
# define COMPILER "Sun compiler version " STRINGIZE(__SUNPRO_CC)
#elif defined(_MSC_VER)
/* Microsoft Visual C/C++
Must be last since other compilers define _MSC_VER for compatibility as well */
# if _MSC_VER < 1200
# define COMPILER_VERSION 5.0
# elif _MSC_VER < 1300
# define COMPILER_VERSION 6.0
# elif _MSC_VER == 1300
# define COMPILER_VERSION 7.0
# elif _MSC_VER == 1310
# define COMPILER_VERSION 7.1
# elif _MSC_VER == 1400
# define COMPILER_VERSION 8.0
# elif _MSC_VER == 1500
# define COMPILER_VERSION 9.0
# elif _MSC_VER == 1600
# define COMPILER_VERSION 10.0
# else
# define COMPILER_VERSION _MSC_VER
# endif
# define COMPILER "Microsoft Visual C++ version " STRINGIZE(COMPILER_VERSION)
#else
/* Fallback */
# define COMPILER "Unknown compiler"
#endif
/***************************************************************************
* Module-level
***************************************************************************/
struct module_state {
/* The Sun compiler can't handle empty structs */
#if defined(__SUNPRO_C) || defined(_MSC_VER)
int _dummy;
#endif
};
static struct PyModuleDef moduledef = {
PyModuleDef_HEAD_INIT,
"compiler_version",
NULL,
sizeof(struct module_state),
NULL,
NULL,
NULL,
NULL,
NULL
};
#define INITERROR return NULL
PyMODINIT_FUNC
PyInit_compiler_version(void)
{
PyObject* m;
m = PyModule_Create(&moduledef);
if (m == NULL)
INITERROR;
PyModule_AddStringConstant(m, "compiler", COMPILER);
return m;
}
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1739993667.0
astropy_healpix-1.1.2/astropy_healpix/_core.c 0000644 0001751 0000166 00000037715 14755431103 021017 0 ustar 00runner docker #include
#include
#include
#include
#include "healpix.h"
#include "healpix-utils.h"
#include "interpolation.h"
/* FIXME: We need npy_set_floatstatus_invalid(), but unlike most of the Numpy
* C API it is only available on some platforms if you explicitly link against
* Numpy, which is not typically done for building C extensions. This bundled
* header file contains a static definition of _npy_set_floatstatus_invalid().
*/
#include "ieee754.h"
/* FIXME:
* The Numpy C-API defines PyArrayDescr_Type as:
*
* #define PyArrayDescr_Type (*(PyTypeObject *)PyArray_API[3])
*
* and then in some places we need to take its address, &PyArrayDescr_Type.
* This is fine in GCC 10 and Clang, but earlier versions of GCC complain:
*
* error: dereferencing pointer to incomplete type 'PyTypeObject'
* {aka 'struct _typeobject'}
*
* As a workaround, provide a faux forward declaration for PyTypeObject.
* See https://github.com/numpy/numpy/issues/16970.
*
* Drop this when supporting gcc < 10 becomes irrelevant.
*/
#ifndef PYPY_VERSION
struct _typeobject {
int _placeholder;
};
#endif
#define INVALID_INDEX (-1)
/* Data structure for storing function pointers for routines that are specific
* to the HEALPix ordering scheme. When we create the ufuncs using
* PyUFunc_FromFuncAndData, we will set them up to pass a pointer to this
* data structure through as the void *data parameter for the loop functions
* defined below. */
typedef struct {
int64_t (*order_to_xy)(int64_t, int);
int64_t (*xy_to_order)(int64_t, int);
} order_funcs;
static order_funcs
nested_order_funcs = {healpixl_nested_to_xy, healpixl_xy_to_nested},
ring_order_funcs = {healpixl_ring_to_xy, healpixl_xy_to_ring};
static void *no_ufunc_data[] = {NULL},
*nested_ufunc_data[] = {&nested_order_funcs},
*ring_ufunc_data[] = {&ring_order_funcs};
static int64_t nside2npix(int nside)
{
return 12 * ((int64_t) nside) * ((int64_t) nside);
}
static int pixel_nside_valid(int64_t pixel, int nside)
{
return pixel >= 0 && pixel < nside2npix(nside);
}
static void healpix_to_lonlat_loop(
char **args, const npy_intp *dimensions, const npy_intp *steps, void *data)
{
order_funcs *funcs = data;
npy_intp i, n = dimensions[0];
for (i = 0; i < n; i ++)
{
int64_t pixel = *(int64_t *) &args[0][i * steps[0]];
int nside = *(int *) &args[1][i * steps[1]];
double dx = *(double *) &args[2][i * steps[2]];
double dy = *(double *) &args[3][i * steps[3]];
double *lon = (double *) &args[4][i * steps[4]];
double *lat = (double *) &args[5][i * steps[5]];
int64_t xy = INVALID_INDEX;
if (pixel_nside_valid(pixel, nside))
xy = funcs->order_to_xy(pixel, nside);
if (xy >= 0)
healpixl_to_radec(xy, nside, dx, dy, lon, lat);
else
{
*lon = *lat = NPY_NAN;
_npy_set_floatstatus_invalid();
}
}
}
static void lonlat_to_healpix_loop(
char **args, const npy_intp *dimensions, const npy_intp *steps, void *data)
{
order_funcs *funcs = data;
npy_intp i, n = dimensions[0];
for (i = 0; i < n; i ++)
{
double lon = *(double *) &args[0][i * steps[0]];
double lat = *(double *) &args[1][i * steps[1]];
int nside = *(int *) &args[2][i * steps[2]];
int64_t *pixel = (int64_t *) &args[3][i * steps[3]];
double *dx = (double *) &args[4][i * steps[4]];
double *dy = (double *) &args[5][i * steps[5]];
int64_t xy = INVALID_INDEX;
if (isfinite(lon) && isfinite(lat))
xy = radec_to_healpixlf(lon, lat, nside, dx, dy);
if (xy >= 0)
*pixel = funcs->xy_to_order(xy, nside);
else {
*pixel = INVALID_INDEX;
*dx = *dy = NPY_NAN;
_npy_set_floatstatus_invalid();
}
}
}
static void healpix_to_xyz_loop(
char **args, const npy_intp *dimensions, const npy_intp *steps, void *data)
{
order_funcs *funcs = data;
npy_intp i, n = dimensions[0];
for (i = 0; i < n; i ++)
{
int64_t pixel = *(int64_t *) &args[0][i * steps[0]];
int nside = *(int *) &args[1][i * steps[1]];
double dx = *(double *) &args[2][i * steps[2]];
double dy = *(double *) &args[3][i * steps[3]];
double *x = (double *) &args[4][i * steps[4]];
double *y = (double *) &args[5][i * steps[5]];
double *z = (double *) &args[6][i * steps[6]];
int64_t xy = INVALID_INDEX;
if (pixel_nside_valid(pixel, nside))
xy = funcs->order_to_xy(pixel, nside);
if (xy >= 0)
healpixl_to_xyz(xy, nside, dx, dy, x, y, z);
else
{
*x = *y = *z = NPY_NAN;
_npy_set_floatstatus_invalid();
}
}
}
static void xyz_to_healpix_loop(
char **args, const npy_intp *dimensions, const npy_intp *steps, void *data)
{
order_funcs *funcs = data;
npy_intp i, n = dimensions[0];
for (i = 0; i < n; i ++)
{
double x = *(double *) &args[0][i * steps[0]];
double y = *(double *) &args[1][i * steps[1]];
double z = *(double *) &args[2][i * steps[2]];
int nside = *(int *) &args[3][i * steps[3]];
int64_t *pixel = (int64_t *) &args[4][i * steps[4]];
double *dx = (double *) &args[5][i * steps[5]];
double *dy = (double *) &args[6][i * steps[6]];
int64_t xy = INVALID_INDEX;
if (isfinite(x) && isfinite(y) && isfinite(z)) {
/* xyztohealpixlf expects a unit vector */
double norm = sqrt(x*x + y*y + z*z);
x /= norm;
y /= norm;
z /= norm;
xy = xyztohealpixlf(x, y, z, nside, dx, dy);
}
if (xy >= 0)
*pixel = funcs->xy_to_order(xy, nside);
else {
*pixel = INVALID_INDEX;
*dx = *dy = NPY_NAN;
_npy_set_floatstatus_invalid();
}
}
}
static void nested_to_ring_loop(
char **args, const npy_intp *dimensions, const npy_intp *steps, void *data)
{
npy_intp i, n = dimensions[0];
for (i = 0; i < n; i ++)
{
int64_t nested = *(int64_t *) &args[0][i * steps[0]];
int nside = *(int *) &args[1][i * steps[1]];
int64_t *ring = (int64_t *) &args[2][i * steps[2]];
int64_t xy = INVALID_INDEX;
if (pixel_nside_valid(nested, nside))
xy = healpixl_nested_to_xy(nested, nside);
if (xy >= 0)
*ring = healpixl_xy_to_ring(xy, nside);
else {
*ring = INVALID_INDEX;
_npy_set_floatstatus_invalid();
}
}
}
static void ring_to_nested_loop(
char **args, const npy_intp *dimensions, const npy_intp *steps, void *data)
{
npy_intp i, n = dimensions[0];
for (i = 0; i < n; i ++)
{
int64_t ring = *(int64_t *) &args[0][i * steps[0]];
int nside = *(int *) &args[1][i * steps[1]];
int64_t *nested = (int64_t *) &args[2][i * steps[2]];
int64_t xy = INVALID_INDEX;
if (pixel_nside_valid(ring, nside))
xy = healpixl_ring_to_xy(ring, nside);
if (xy >= 0)
*nested = healpixl_xy_to_nested(xy, nside);
else {
*nested = INVALID_INDEX;
_npy_set_floatstatus_invalid();
}
}
}
static void bilinear_interpolation_weights_loop(
char **args, const npy_intp *dimensions, const npy_intp *steps, void *data)
{
npy_intp i, n = dimensions[0];
for (i = 0; i < n; i ++)
{
double lon = *(double *) &args[0][i * steps[0]];
double lat = *(double *) &args[1][i * steps[1]];
int nside = *(int *) &args[2][i * steps[2]];
int64_t indices[4];
double weights[4];
int j;
if (isfinite(lon) && isfinite(lat)) {
interpolate_weights(lon, lat, indices, weights, nside);
for (j = 0; j < 4; j ++)
{
*(int64_t *) &args[3 + j][i * steps[3 + j]] = indices[j];
*(double *) &args[7 + j][i * steps[7 + j]] = weights[j];
}
} else {
for (j = 0; j < 4; j ++)
{
*(int64_t *) &args[3 + j][i * steps[3 + j]] = INVALID_INDEX;
*(double *) &args[7 + j][i * steps[7 + j]] = NPY_NAN;
}
_npy_set_floatstatus_invalid();
}
}
}
static void neighbours_loop(
char **args, const npy_intp *dimensions, const npy_intp *steps, void *data)
{
order_funcs *funcs = data;
npy_intp i, n = dimensions[0];
for (i = 0; i < n; i ++)
{
int64_t pixel = *(int64_t *) &args[0][i * steps[0]];
int nside = *(int *) &args[1][i * steps[1]];
int64_t neighbours[] = {
INVALID_INDEX, INVALID_INDEX, INVALID_INDEX, INVALID_INDEX,
INVALID_INDEX, INVALID_INDEX, INVALID_INDEX, INVALID_INDEX};
int j;
int64_t xy = INVALID_INDEX;
if (pixel_nside_valid(pixel, nside))
xy = funcs->order_to_xy(pixel, nside);
if (xy >= 0)
healpixl_get_neighbours(xy, neighbours, nside);
for (j = 0; j < 8; j ++)
{
int k = 4 - j;
if (k < 0)
k += 8;
xy = neighbours[k];
if (xy >= 0)
pixel = funcs->xy_to_order(xy, nside);
else {
pixel = INVALID_INDEX;
_npy_set_floatstatus_invalid();
}
*(int64_t *) &args[2 + j][i * steps[2 + j]] = pixel;
}
}
}
static PyObject *healpix_cone_search(
PyObject *self, PyObject *args, PyObject *kwargs)
{
PyObject *result;
static char *kws[] = {"lon", "lat", "radius", "nside", "order", NULL};
double lon, lat, radius;
int nside;
char *order;
int64_t *indices, n_indices;
int64_t *result_data;
npy_intp dims[1];
if (!PyArg_ParseTupleAndKeywords(
args, kwargs, "dddis", kws, &lon, &lat, &radius, &nside, &order))
return NULL;
n_indices = healpix_rangesearch_radec_simple(
lon, lat, radius, nside, 0, &indices);
if (!indices)
{
PyErr_SetString(
PyExc_RuntimeError, "healpix_rangesearch_radec_simple failed");
return NULL;
}
dims[0] = n_indices;
result = PyArray_SimpleNew(1, dims, NPY_INT64);
if (result)
{
result_data = PyArray_DATA((PyArrayObject *) result);
if (strcmp(order, "nested") == 0)
{
int i;
for (i = 0; i < n_indices; i ++)
result_data[i] = healpixl_xy_to_nested(indices[i], nside);
} else {
int i;
for (i = 0; i < n_indices; i ++)
result_data[i] = healpixl_xy_to_ring(indices[i], nside);
}
}
free(indices);
return result;
}
static PyMethodDef methods[] = {
{"healpix_cone_search", (PyCFunction) healpix_cone_search, METH_VARARGS | METH_KEYWORDS, NULL},
{NULL, NULL, 0, NULL}
};
static PyModuleDef moduledef = {
PyModuleDef_HEAD_INIT,
"_core", NULL, -1, methods
};
static PyUFuncGenericFunction
healpix_to_lonlat_loops [] = {healpix_to_lonlat_loop},
lonlat_to_healpix_loops [] = {lonlat_to_healpix_loop},
healpix_to_xyz_loops [] = {healpix_to_xyz_loop},
xyz_to_healpix_loops [] = {xyz_to_healpix_loop},
nested_to_ring_loops [] = {nested_to_ring_loop},
ring_to_nested_loops [] = {ring_to_nested_loop},
bilinear_interpolation_weights_loops[] = {bilinear_interpolation_weights_loop},
neighbours_loops [] = {neighbours_loop};
static char
healpix_to_lonlat_types[] = {
NPY_INT64, NPY_INT, NPY_DOUBLE, NPY_DOUBLE, NPY_DOUBLE, NPY_DOUBLE},
lonlat_to_healpix_types[] = {
NPY_DOUBLE, NPY_DOUBLE, NPY_INT, NPY_INT64, NPY_DOUBLE, NPY_DOUBLE},
healpix_to_xyz_types[] = {
NPY_INT64, NPY_INT, NPY_DOUBLE, NPY_DOUBLE, NPY_DOUBLE, NPY_DOUBLE, NPY_DOUBLE},
xyz_to_healpix_types[] = {
NPY_DOUBLE, NPY_DOUBLE, NPY_DOUBLE, NPY_INT, NPY_INT64, NPY_DOUBLE, NPY_DOUBLE},
healpix_to_healpix_types[] = {
NPY_INT64, NPY_INT, NPY_INT64},
bilinear_interpolation_weights_types[] = {
NPY_DOUBLE, NPY_DOUBLE, NPY_INT,
NPY_INT64, NPY_INT64, NPY_INT64, NPY_INT64,
NPY_DOUBLE, NPY_DOUBLE, NPY_DOUBLE, NPY_DOUBLE},
neighbours_types[] = {
NPY_INT64, NPY_INT,
NPY_INT64, NPY_INT64, NPY_INT64, NPY_INT64,
NPY_INT64, NPY_INT64, NPY_INT64, NPY_INT64};
PyMODINIT_FUNC PyInit__core(void)
{
PyObject *module;
import_array();
import_umath();
module = PyModule_Create(&moduledef);
PyModule_AddObject(
module, "healpix_nested_to_lonlat", PyUFunc_FromFuncAndData(
healpix_to_lonlat_loops, nested_ufunc_data,
healpix_to_lonlat_types, 1, 4, 2, PyUFunc_None,
"healpix_nested_to_lonlat", NULL, 0));
PyModule_AddObject(
module, "healpix_ring_to_lonlat", PyUFunc_FromFuncAndData(
healpix_to_lonlat_loops, ring_ufunc_data,
healpix_to_lonlat_types, 1, 4, 2, PyUFunc_None,
"healpix_ring_to_lonlat", NULL, 0));
PyModule_AddObject(
module, "lonlat_to_healpix_nested", PyUFunc_FromFuncAndData(
lonlat_to_healpix_loops, nested_ufunc_data,
lonlat_to_healpix_types, 1, 3, 3, PyUFunc_None,
"lonlat_to_healpix_nested", NULL, 0));
PyModule_AddObject(
module, "lonlat_to_healpix_ring", PyUFunc_FromFuncAndData(
lonlat_to_healpix_loops, ring_ufunc_data,
lonlat_to_healpix_types, 1, 3, 3, PyUFunc_None,
"lonlat_to_healpix_ring", NULL, 0));
PyModule_AddObject(
module, "healpix_nested_to_xyz", PyUFunc_FromFuncAndData(
healpix_to_xyz_loops, nested_ufunc_data,
healpix_to_xyz_types, 1, 4, 3, PyUFunc_None,
"healpix_nested_to_xyz", NULL, 0));
PyModule_AddObject(
module, "healpix_ring_to_xyz", PyUFunc_FromFuncAndData(
healpix_to_xyz_loops, ring_ufunc_data,
healpix_to_xyz_types, 1, 4, 3, PyUFunc_None,
"healpix_ring_to_xyz", NULL, 0));
PyModule_AddObject(
module, "xyz_to_healpix_nested", PyUFunc_FromFuncAndData(
xyz_to_healpix_loops, nested_ufunc_data,
xyz_to_healpix_types, 1, 4, 3, PyUFunc_None,
"xyz_to_healpix_nested", NULL, 0));
PyModule_AddObject(
module, "xyz_to_healpix_ring", PyUFunc_FromFuncAndData(
xyz_to_healpix_loops, ring_ufunc_data,
xyz_to_healpix_types, 1, 4, 3, PyUFunc_None,
"xyz_to_healpix_ring", NULL, 0));
PyModule_AddObject(
module, "nested_to_ring", PyUFunc_FromFuncAndData(
nested_to_ring_loops, no_ufunc_data,
healpix_to_healpix_types, 1, 2, 1, PyUFunc_None,
"nested_to_ring", NULL, 0));
PyModule_AddObject(
module, "ring_to_nested", PyUFunc_FromFuncAndData(
ring_to_nested_loops, no_ufunc_data,
healpix_to_healpix_types, 1, 2, 1, PyUFunc_None,
"ring_to_nested", NULL, 0));
PyModule_AddObject(
module, "bilinear_interpolation_weights", PyUFunc_FromFuncAndData(
bilinear_interpolation_weights_loops, no_ufunc_data,
bilinear_interpolation_weights_types, 1, 3, 8, PyUFunc_None,
"bilinear_interpolation_weights", NULL, 0));
PyModule_AddObject(
module, "neighbours_nested", PyUFunc_FromFuncAndData(
neighbours_loops, nested_ufunc_data,
neighbours_types, 1, 2, 8, PyUFunc_None,
"neighbours_nested", NULL, 0));
PyModule_AddObject(
module, "neighbours_ring", PyUFunc_FromFuncAndData(
neighbours_loops, ring_ufunc_data,
neighbours_types, 1, 2, 8, PyUFunc_None,
"neighbours_ring", NULL, 0));
return module;
}
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1739993667.0
astropy_healpix-1.1.2/astropy_healpix/bench.py 0000644 0001751 0000166 00000015062 14755431103 021204 0 ustar 00runner docker # Licensed under a 3-clause BSD style license - see LICENSE.rst
"""Benchmarks for this package.
To run all benchmarks and print a report to the console::
python -m healpix.bench
You can also run the benchmarks first, save the results dict
to disk as a JSON file (or share it with others) and then
print the results later, or compare them with other results.
We should now that this is not very comprehensive / flexible.
If your application depends on performance of HEALPix computations,
you should write benchmarks with cases relevant for that application
and check if HEALPix computations are really the bottleneck and if
this package is fast enough for you or not.
"""
import timeit
from astropy.table import Table
# NOTE: If healpy is installed, we use it in the benchmarks, but healpy is not
# a formal dependency of astropy-healpix.
try:
import healpy as hp # noqa
except ImportError:
HEALPY_INSTALLED = False
else:
HEALPY_INSTALLED = True
# Copied from https://github.com/kwgoodman/bottleneck/blob/master/bottleneck/benchmark/autotimeit.py
def autotimeit(stmt, setup='pass', repeat=3, mintime=0.2):
timer = timeit.Timer(stmt, setup)
number, time1 = autoscaler(timer, mintime)
time2 = timer.repeat(repeat=repeat - 1, number=number)
return min(time2 + [time1]) / number
# Copied from https://github.com/kwgoodman/bottleneck/blob/master/bottleneck/benchmark/autotimeit.py
def autoscaler(timer, mintime):
number = 1
for i in range(12):
time = timer.timeit(number)
if time > mintime:
return number, time
number *= 10
raise RuntimeError('function is too fast to test')
def get_import(package, function):
if package == 'astropy_healpix':
return f'from astropy_healpix.healpy import {function}'
else:
return f'from healpy import {function}'
def bench_pix2ang(size=None, nside=None, nest=None, package=None, fast=False):
setup = '\n'.join([
get_import(package, 'pix2ang'),
'import numpy as np',
f'nside={nside}',
f'ipix=(np.random.random({size}) * 12 * nside ** 2).astype(np.int64)',
f'nest={nest}'])
stmt = 'pix2ang(nside, ipix, nest)'
return autotimeit(stmt=stmt, setup=setup, repeat=1, mintime=0 if fast else 0.1)
def bench_ang2pix(size=None, nside=None, nest=None, package=None, fast=False):
setup = '\n'.join([
get_import(package, 'ang2pix'),
'import numpy as np',
f'nside={nside}',
f'lon=360 * np.random.random({size})',
f'lat=180 * np.random.random({size}) - 90',
f'nest={nest}'])
stmt = 'ang2pix(nside, lon, lat, nest, lonlat=True)'
return autotimeit(stmt=stmt, setup=setup, repeat=1, mintime=0 if fast else 0.1)
def bench_nest2ring(size=None, nside=None, package=None, fast=False):
setup = '\n'.join([
get_import(package, 'nest2ring'),
'import numpy as np',
f'nside={nside}',
f'ipix=(np.random.random({size}) * 12 * nside ** 2).astype(np.int64)'])
stmt = 'nest2ring(nside, ipix)'
return autotimeit(stmt=stmt, setup=setup, repeat=1, mintime=0 if fast else 0.1)
def bench_ring2nest(size=None, nside=None, package=None, fast=False):
setup = '\n'.join([
get_import(package, 'ring2nest'),
'import numpy as np',
f'nside={nside}',
f'ipix=(np.random.random({size}) * 12 * nside ** 2).astype(np.int64)'])
stmt = 'ring2nest(nside, ipix)'
return autotimeit(stmt=stmt, setup=setup, repeat=1, mintime=0 if fast else 0.1)
def bench_get_interp_weights(size=None, nside=None, nest=None, package=None, fast=False):
setup = '\n'.join([
get_import(package, 'get_interp_weights'),
'import numpy as np',
f'nside={nside}',
f'lon=360 * np.random.random({size})',
f'lat=180 * np.random.random({size}) - 90',
f'nest={nest}'])
stmt = 'get_interp_weights(nside, lon, lat, nest=nest, lonlat=True)'
return autotimeit(stmt=stmt, setup=setup, repeat=1, mintime=0 if fast else 0.1)
def run_single(name, benchmark, fast=False, **kwargs):
time_self = benchmark(package='astropy_healpix', fast=fast, **kwargs)
results_single = dict(function=name, time_self=time_self, **kwargs)
if HEALPY_INSTALLED:
time_healpy = bench_ang2pix(package='healpy', fast=fast, **kwargs)
results_single['time_healpy'] = time_healpy
return results_single
def bench_run(fast=False):
"""Run all benchmarks. Return results as a dict."""
results = []
if fast:
SIZES = [10, 1_000, 100_000]
else:
SIZES = [10, 1_000, 1_000_000]
for nest in [True, False]:
for size in SIZES:
for nside in [1, 128]:
results.append(run_single('pix2ang', bench_pix2ang, fast=fast,
size=size, nside=nside, nest=nest))
for nest in [True, False]:
for size in SIZES:
for nside in [1, 128]:
results.append(run_single('ang2pix', bench_ang2pix, fast=fast,
size=size, nside=nside, nest=nest))
for size in SIZES:
for nside in [1, 128]:
results.append(run_single('nest2ring', bench_nest2ring, fast=fast,
size=size, nside=nside))
for size in SIZES:
for nside in [1, 128]:
results.append(run_single('ring2nest', bench_ring2nest, fast=fast,
size=size, nside=nside))
for nest in [True, False]:
for size in SIZES:
for nside in [1, 128]:
results.append(run_single('get_interp_weights', bench_get_interp_weights,
fast=fast, size=size,
nside=nside, nest=nest))
return results
def bench_report(results):
"""Print a report for given benchmark results to the console."""
table = Table(names=['function', 'nest', 'nside', 'size',
'time_healpy', 'time_self', 'ratio'],
dtype=['S20', bool, int, int, float, float, float], masked=True)
for row in results:
table.add_row(row)
table['time_self'].format = '10.7f'
if HEALPY_INSTALLED:
table['ratio'] = table['time_self'] / table['time_healpy']
table['time_healpy'].format = '10.7f'
table['ratio'].format = '7.2f'
table.pprint(max_lines=-1)
def main(fast=False):
"""Run all benchmarks and print report to the console."""
print('Running benchmarks...\n')
results = bench_run(fast=fast)
bench_report(results)
if __name__ == '__main__':
main()
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1739993667.0
astropy_healpix-1.1.2/astropy_healpix/conftest.py 0000644 0001751 0000166 00000002717 14755431103 021755 0 ustar 00runner docker """Configure Test Suite.
This file is used to configure the behavior of pytest when using the Astropy
test infrastructure. It needs to live inside the package in order for it to
get picked up when running the tests inside an interpreter using
`astropy_healpix.test()`.
"""
import os
import numpy as np
try:
from pytest_astropy_header.display import PYTEST_HEADER_MODULES, TESTED_VERSIONS
ASTROPY_HEADER = True
except ImportError:
ASTROPY_HEADER = False
def pytest_configure(config):
"""Configure Pytest with Astropy.
Parameters
----------
config : pytest configuration
"""
if ASTROPY_HEADER:
config.option.astropy_header = True
# Customize the following lines to add/remove entries from the list of
# packages for which version numbers are displayed when running the tests.
PYTEST_HEADER_MODULES.pop('h5py', None)
PYTEST_HEADER_MODULES.pop('Pandas', None)
PYTEST_HEADER_MODULES['Astropy'] = 'astropy'
PYTEST_HEADER_MODULES['healpy'] = 'healpy'
from . import __version__
packagename = os.path.basename(os.path.dirname(__file__))
TESTED_VERSIONS[packagename] = __version__
# Set the Numpy print style to a fixed version to make doctest outputs
# reproducible.
try:
np.set_printoptions(legacy='1.13')
except TypeError:
# On older versions of Numpy, the unrecognized 'legacy' option will
# raise a TypeError.
pass
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1739993667.0
astropy_healpix-1.1.2/astropy_healpix/core.py 0000644 0001751 0000166 00000053123 14755431103 021055 0 ustar 00runner docker # Licensed under a 3-clause BSD style license - see LICENSE.rst
import math
import numpy as np
from astropy import units as u
from astropy.coordinates import Longitude, Latitude
from . import _core
__all__ = [
'nside_to_pixel_area',
'nside_to_pixel_resolution',
'pixel_resolution_to_nside',
'nside_to_npix',
'npix_to_nside',
'level_to_nside',
'nside_to_level',
'level_ipix_to_uniq',
'uniq_to_level_ipix',
'lonlat_to_healpix',
'healpix_to_lonlat',
'xyz_to_healpix',
'healpix_to_xyz',
'bilinear_interpolation_weights',
'interpolate_bilinear_lonlat',
'boundaries_lonlat',
'neighbours',
]
_NUMPY_COPY_IF_NEEDED = False if np.__version__.startswith("1.") else None
def _validate_order(order):
# We also support upper-case, to support directly the values
# ORDERING = {'RING', 'NESTED'} in FITS headers
# This is currently undocumented in the docstrings.
if order == 'nested' or order == 'NESTED':
return 'nested'
elif order == 'ring' or order == 'RING':
return 'ring'
else:
raise ValueError("order must be 'nested' or 'ring'")
def _validate_offset(label, offset):
offset = np.asarray(offset)
if np.any((offset < 0) | (offset > 1)):
raise ValueError(f'd{label} must be in the range [0:1]')
def _validate_level(level):
if np.any(level < 0):
raise ValueError('level must be positive')
def _validate_nside(nside):
log_2_nside = np.round(np.log2(nside))
if not np.all(2 ** log_2_nside == nside):
raise ValueError('nside must be a power of two')
def _validate_npix(level, ipix):
if not np.all(ipix < (3 << 2*(level + 1))):
raise ValueError('ipix for a specific level must be inferior to npix')
def level_to_nside(level):
"""
Find the pixel dimensions of the top-level HEALPix tiles.
This is given by ``nside = 2**level``.
Parameters
----------
level : int
The resolution level
Returns
-------
nside : int
The number of pixels on the side of one of the 12 'top-level' HEALPix tiles.
"""
level = np.asarray(level, dtype=np.int64)
_validate_level(level)
return 2 ** level
def nside_to_level(nside):
"""
Find the HEALPix level for a given nside.
This is given by ``level = log2(nside)``.
This function is the inverse of `level_to_nside`.
Parameters
----------
nside : int
The number of pixels on the side of one of the 12 'top-level' HEALPix tiles.
Must be a power of two.
Returns
-------
level : int
The level of the HEALPix cells
"""
nside = np.asarray(nside, dtype=np.int64)
_validate_nside(nside)
return np.log2(nside).astype(np.int64)
def uniq_to_level_ipix(uniq):
"""
Convert a HEALPix cell uniq number to its (level, ipix) equivalent.
A uniq number is a 64 bits integer equaling to : ipix + 4*(4**level). Please read
this `paper `_
for more details about uniq numbers.
Parameters
----------
uniq : int
The uniq number of a HEALPix cell.
Returns
-------
level, ipix: int, int
The level and index of the HEALPix cell computed from ``uniq``.
"""
uniq = np.asarray(uniq, dtype=np.int64)
level = (np.log2(uniq//4)) // 2
level = level.astype(np.int64)
_validate_level(level)
ipix = uniq - (1 << 2*(level + 1))
_validate_npix(level, ipix)
return level, ipix
def level_ipix_to_uniq(level, ipix):
"""
Convert a level and HEALPix index into a uniq number representing the cell.
This function is the inverse of `uniq_to_level_ipix`.
Parameters
----------
level : int
The level of the HEALPix cell
ipix : int
The index of the HEALPix cell
Returns
-------
uniq : int
The uniq number representing the HEALPix cell.
"""
level = np.asarray(level, dtype=np.int64)
ipix = np.asarray(ipix, dtype=np.int64)
_validate_level(level)
_validate_npix(level, ipix)
return ipix + (1 << 2*(level + 1))
def nside_to_pixel_area(nside):
"""
Find the area of HEALPix pixels given the pixel dimensions of one of
the 12 'top-level' HEALPix tiles.
Parameters
----------
nside : int
The number of pixels on the side of one of the 12 'top-level' HEALPix tiles.
Returns
-------
pixel_area : :class:`~astropy.units.Quantity`
The area of the HEALPix pixels
"""
nside = np.asanyarray(nside, dtype=np.int64)
_validate_nside(nside)
npix = 12 * nside * nside
pixel_area = 4 * math.pi / npix * u.sr
return pixel_area
def nside_to_pixel_resolution(nside):
"""
Find the resolution of HEALPix pixels given the pixel dimensions of one of
the 12 'top-level' HEALPix tiles.
Parameters
----------
nside : int
The number of pixels on the side of one of the 12 'top-level' HEALPix tiles.
Returns
-------
resolution : :class:`~astropy.units.Quantity`
The resolution of the HEALPix pixels
See also
--------
pixel_resolution_to_nside
"""
nside = np.asanyarray(nside, dtype=np.int64)
_validate_nside(nside)
return (nside_to_pixel_area(nside) ** 0.5).to(u.arcmin)
def pixel_resolution_to_nside(resolution, round='nearest'):
"""Find closest HEALPix nside for a given angular resolution.
This function is the inverse of `nside_to_pixel_resolution`,
for the default rounding scheme of ``round='nearest'``.
If you choose ``round='up'``, you'll get HEALPix pixels that
have at least the requested resolution (usually a bit better
due to rounding).
Pixel resolution is defined as square root of pixel area.
Parameters
----------
resolution : `~astropy.units.Quantity`
Angular resolution
round : {'up', 'nearest', 'down'}
Which way to round
Returns
-------
nside : int
The number of pixels on the side of one of the 12 'top-level' HEALPix tiles.
Always a power of 2.
Examples
--------
>>> from astropy import units as u
>>> from astropy_healpix import pixel_resolution_to_nside
>>> pixel_resolution_to_nside(13 * u.arcmin)
256
>>> pixel_resolution_to_nside(13 * u.arcmin, round='up')
512
"""
resolution = resolution.to(u.rad).value
pixel_area = resolution * resolution
npix = 4 * math.pi / pixel_area
nside = np.sqrt(npix / 12)
# Now we have to round to the closest ``nside``
# Since ``nside`` must be a power of two,
# we first compute the corresponding ``level = log2(nside)`
# round the level and then go back to nside
level = np.log2(nside)
if round == 'up':
level = np.ceil(level)
elif round == 'nearest':
level = np.round(level)
elif round == 'down':
level = np.floor(level)
else:
raise ValueError(f'Invalid value for round: {round!r}')
# For very low requested resolution (i.e. large angle values), we
# return ``level=0``, i.e. ``nside=1``, i.e. the lowest resolution
# that exists with HEALPix
level = np.clip(level.astype(int), 0, None)
return level_to_nside(level)
def nside_to_npix(nside):
"""
Find the number of pixels corresponding to a HEALPix resolution.
Parameters
----------
nside : int
The number of pixels on the side of one of the 12 'top-level' HEALPix tiles.
Returns
-------
npix : int
The number of pixels in the HEALPix map.
"""
nside = np.asanyarray(nside, dtype=np.int64)
_validate_nside(nside)
return 12 * nside ** 2
def npix_to_nside(npix):
"""
Find the number of pixels on the side of one of the 12 'top-level' HEALPix
tiles given a total number of pixels.
Parameters
----------
npix : int
The number of pixels in the HEALPix map.
Returns
-------
nside : int
The number of pixels on the side of one of the 12 'top-level' HEALPix tiles.
"""
npix = np.asanyarray(npix, dtype=np.int64)
if not np.all(npix % 12 == 0):
raise ValueError('Number of pixels must be divisible by 12')
square_root = np.sqrt(npix / 12)
if not np.all(square_root ** 2 == npix / 12):
raise ValueError('Number of pixels is not of the form 12 * nside ** 2')
return np.round(square_root).astype(int)
def healpix_to_lonlat(healpix_index, nside, dx=None, dy=None, order='ring'):
"""
Convert HEALPix indices (optionally with offsets) to longitudes/latitudes.
If no offsets (``dx`` and ``dy``) are provided, the coordinates will default
to those at the center of the HEALPix pixels.
Parameters
----------
healpix_index : int or `~numpy.ndarray`
HEALPix indices (as a scalar or array)
nside : int or `~numpy.ndarray`
Number of pixels along the side of each of the 12 top-level HEALPix tiles
dx, dy : float or `~numpy.ndarray`, optional
Offsets inside the HEALPix pixel, which must be in the range [0:1],
where 0.5 is the center of the HEALPix pixels (as scalars or arrays)
order : { 'nested' | 'ring' }, optional
Order of HEALPix pixels
Returns
-------
lon : :class:`~astropy.coordinates.Longitude`
The longitude values
lat : :class:`~astropy.coordinates.Latitude`
The latitude values
"""
_validate_nside(nside)
if _validate_order(order) == 'ring':
func = _core.healpix_ring_to_lonlat
else: # _validate_order(order) == 'nested'
func = _core.healpix_nested_to_lonlat
if dx is None:
dx = 0.5
else:
_validate_offset('x', dx)
if dy is None:
dy = 0.5
else:
_validate_offset('y', dy)
nside = np.asarray(nside, dtype=np.intc)
lon, lat = func(healpix_index, nside, dx, dy)
lon = Longitude(lon, unit=u.rad, copy=_NUMPY_COPY_IF_NEEDED)
lat = Latitude(lat, unit=u.rad, copy=_NUMPY_COPY_IF_NEEDED)
return lon, lat
def lonlat_to_healpix(lon, lat, nside, return_offsets=False, order='ring'):
"""
Convert longitudes/latitudes to HEALPix indices
Parameters
----------
lon, lat : :class:`~astropy.units.Quantity`
The longitude and latitude values as :class:`~astropy.units.Quantity`
instances with angle units.
nside : int or `~numpy.ndarray`
Number of pixels along the side of each of the 12 top-level HEALPix tiles
order : { 'nested' | 'ring' }
Order of HEALPix pixels
return_offsets : bool, optional
If `True`, the returned values are the HEALPix pixel indices as well as
``dx`` and ``dy``, the fractional positions inside the pixels. If
`False` (the default), only the HEALPix pixel indices is returned.
Returns
-------
healpix_index : int or `~numpy.ndarray`
The HEALPix indices
dx, dy : `~numpy.ndarray`
Offsets inside the HEALPix pixel in the range [0:1], where 0.5 is the
center of the HEALPix pixels
"""
if _validate_order(order) == 'ring':
func = _core.lonlat_to_healpix_ring
else: # _validate_order(order) == 'nested'
func = _core.lonlat_to_healpix_nested
nside = np.asarray(nside, dtype=np.intc)
lon = lon.to_value(u.rad)
lat = lat.to_value(u.rad)
healpix_index, dx, dy = func(lon, lat, nside)
if return_offsets:
return healpix_index, dx, dy
else:
return healpix_index
def healpix_to_xyz(healpix_index, nside, dx=None, dy=None, order='ring'):
"""
Convert HEALPix indices (optionally with offsets) to Cartesian coordinates.
If no offsets (``dx`` and ``dy``) are provided, the coordinates will default
to those at the center of the HEALPix pixels.
Parameters
----------
healpix_index : int or `~numpy.ndarray`
HEALPix indices (as a scalar or array)
nside : int or `~numpy.ndarray`
Number of pixels along the side of each of the 12 top-level HEALPix tiles
dx, dy : float or `~numpy.ndarray`, optional
Offsets inside the HEALPix pixel, which must be in the range [0:1],
where 0.5 is the center of the HEALPix pixels (as scalars or arrays)
order : { 'nested' | 'ring' }, optional
Order of HEALPix pixels
Returns
-------
x, y, z : float or `~numpy.ndarray`
The Cartesian coordinate components
"""
_validate_nside(nside)
if _validate_order(order) == 'ring':
func = _core.healpix_ring_to_xyz
else: # _validate_order(order) == 'nested'
func = _core.healpix_nested_to_xyz
if dx is None:
dx = 0.5
else:
_validate_offset('x', dx)
if dy is None:
dy = 0.5
else:
_validate_offset('y', dy)
nside = np.asarray(nside, dtype=np.intc)
return func(healpix_index, nside, dx, dy)
def xyz_to_healpix(x, y, z, nside, return_offsets=False, order='ring'):
"""
Convert longitudes/latitudes to HEALPix indices
Parameters
----------
x, y, z : float or `~numpy.ndarray`
The Cartesian coordinate components
nside : int or `~numpy.ndarray`
Number of pixels along the side of each of the 12 top-level HEALPix tiles
order : { 'nested' | 'ring' }
Order of HEALPix pixels
return_offsets : bool, optional
If `True`, the returned values are the HEALPix pixel indices as well as
``dx`` and ``dy``, the fractional positions inside the pixels. If
`False` (the default), only the HEALPix pixel indices is returned.
Returns
-------
healpix_index : int or `~numpy.ndarray`
The HEALPix indices
dx, dy : `~numpy.ndarray`
Offsets inside the HEALPix pixel in the range [0:1], where 0.5 is the
center of the HEALPix pixels
"""
if _validate_order(order) == 'ring':
func = _core.xyz_to_healpix_ring
else: # _validate_order(order) == 'nested'
func = _core.xyz_to_healpix_nested
nside = np.asarray(nside, dtype=np.intc)
healpix_index, dx, dy = func(x, y, z, nside)
if return_offsets:
return healpix_index, dx, dy
else:
return healpix_index
def nested_to_ring(nested_index, nside):
"""
Convert a HEALPix 'nested' index to a HEALPix 'ring' index
Parameters
----------
nested_index : int or `~numpy.ndarray`
Healpix index using the 'nested' ordering
nside : int or `~numpy.ndarray`
Number of pixels along the side of each of the 12 top-level HEALPix tiles
Returns
-------
ring_index : int or `~numpy.ndarray`
Healpix index using the 'ring' ordering
"""
nside = np.asarray(nside, dtype=np.intc)
return _core.nested_to_ring(nested_index, nside)
def ring_to_nested(ring_index, nside):
"""
Convert a HEALPix 'ring' index to a HEALPix 'nested' index
Parameters
----------
ring_index : int or `~numpy.ndarray`
Healpix index using the 'ring' ordering
nside : int or `~numpy.ndarray`
Number of pixels along the side of each of the 12 top-level HEALPix tiles
Returns
-------
nested_index : int or `~numpy.ndarray`
Healpix index using the 'nested' ordering
"""
nside = np.asarray(nside, dtype=np.intc)
return _core.ring_to_nested(ring_index, nside)
def bilinear_interpolation_weights(lon, lat, nside, order='ring'):
"""
Get the four neighbours for each (lon, lat) position and the weight
associated with each one for bilinear interpolation.
Parameters
----------
lon, lat : :class:`~astropy.units.Quantity`
The longitude and latitude values as :class:`~astropy.units.Quantity`
instances with angle units.
nside : int
Number of pixels along the side of each of the 12 top-level HEALPix tiles
order : { 'nested' | 'ring' }
Order of HEALPix pixels
Returns
-------
indices : `~numpy.ndarray`
2-D array with shape (4, N) giving the four indices to use for the
interpolation
weights : `~numpy.ndarray`
2-D array with shape (4, N) giving the four weights to use for the
interpolation
"""
lon = lon.to_value(u.rad)
lat = lat.to_value(u.rad)
_validate_nside(nside)
nside = np.asarray(nside, dtype=np.intc)
result = _core.bilinear_interpolation_weights(lon, lat, nside)
indices = np.stack(result[:4])
weights = np.stack(result[4:])
if _validate_order(order) == 'nested':
indices = ring_to_nested(indices, nside)
return indices, weights
def interpolate_bilinear_lonlat(lon, lat, values, order='ring'):
"""
Interpolate values at specific longitudes/latitudes using bilinear interpolation
Parameters
----------
lon, lat : :class:`~astropy.units.Quantity`
The longitude and latitude values as :class:`~astropy.units.Quantity` instances
with angle units.
values : `~numpy.ndarray`
Array with the values in each HEALPix pixel. The first dimension should
have length 12 * nside ** 2 (and nside is determined automatically from
this).
order : { 'nested' | 'ring' }
Order of HEALPix pixels
Returns
-------
result : float `~numpy.ndarray`
The interpolated values
"""
nside = npix_to_nside(values.shape[0])
indices, weights = bilinear_interpolation_weights(lon, lat, nside, order=order)
values = values[indices]
# At this point values has shape (N, M) where both N and M might be several
# dimensions, and weights has shape (N,), so we need to transpose in order
# to benefit from broadcasting, then transpose back so that the dimension
# with length 4 is at the start again, ready for summing.
result = (values.T * weights.T).T
return result.sum(axis=0)
def neighbours(healpix_index, nside, order='ring'):
"""
Find all the HEALPix pixels that are the neighbours of a HEALPix pixel
Parameters
----------
healpix_index : `~numpy.ndarray`
Array of HEALPix pixels
nside : int
Number of pixels along the side of each of the 12 top-level HEALPix tiles
order : { 'nested' | 'ring' }
Order of HEALPix pixels
Returns
-------
neigh : `~numpy.ndarray`
Array giving the neighbours starting SW and rotating clockwise. This has
one extra dimension compared to ``healpix_index`` - the first dimension -
which is set to 8. For example if healpix_index has shape (2, 3),
``neigh`` has shape (8, 2, 3).
Notes
-----
Some HEALPix pixels do not have all 8 neighbours. In these cases, the
corresponding entry in the returned array has the value of -1 and Numpy
may print an invalid value warning. To suppress the warning, use
:class:`numpy.errstate`.
"""
_validate_nside(nside)
nside = np.asarray(nside, dtype=np.intc)
if _validate_order(order) == 'ring':
func = _core.neighbours_ring
else: # _validate_order(order) == 'nested'
func = _core.neighbours_nested
return np.stack(func(healpix_index, nside))
def healpix_cone_search(lon, lat, radius, nside, order='ring'):
"""
Find all the HEALPix pixels within a given radius of a longitude/latitude.
Note that this returns all pixels that overlap, including partially, with
the search cone. This function can only be used for a single lon/lat pair at
a time, since different calls to the function may result in a different
number of matches.
Parameters
----------
lon, lat : :class:`~astropy.units.Quantity`
The longitude and latitude to search around
radius : :class:`~astropy.units.Quantity`
The search radius
nside : int
Number of pixels along the side of each of the 12 top-level HEALPix tiles
order : { 'nested' | 'ring' }
Order of HEALPix pixels
Returns
-------
healpix_index : `~numpy.ndarray`
1-D array with all the matching HEALPix pixel indices.
"""
lon = lon.to_value(u.deg)
lat = lat.to_value(u.deg)
radius = radius.to_value(u.deg)
_validate_nside(nside)
order = _validate_order(order)
return _core.healpix_cone_search(lon, lat, radius, nside, order)
def boundaries_lonlat(healpix_index, step, nside, order='ring'):
"""
Return the longitude and latitude of the edges of HEALPix pixels
This returns the longitude and latitude of points along the edge of each
HEALPIX pixel. The number of points returned for each pixel is ``4 * step``,
so setting ``step`` to 1 returns just the corners.
Parameters
----------
healpix_index : `~numpy.ndarray`
1-D array of HEALPix pixels
step : int
The number of steps to take along each edge.
nside : int
Number of pixels along the side of each of the 12 top-level HEALPix tiles
order : { 'nested' | 'ring' }
Order of HEALPix pixels
Returns
-------
lon, lat : :class:`~astropy.units.Quantity`
The longitude and latitude, as 2-D arrays where the first dimension is
the same as the ``healpix_index`` input, and the second dimension has
size ``4 * step``.
"""
healpix_index = np.asarray(healpix_index, dtype=np.int64)
step = int(step)
if step < 1:
raise ValueError('step must be at least 1')
# PERF: this could be optimized by writing a Cython routine to do this to
# avoid allocating temporary arrays
frac = np.linspace(0., 1., step + 1)[:-1]
dx = np.hstack([1 - frac, np.repeat(0, step), frac, np.repeat(1, step)])
dy = np.hstack([np.repeat(1, step), 1 - frac, np.repeat(0, step), frac])
healpix_index, dx, dy = np.broadcast_arrays(healpix_index.reshape(-1, 1), dx, dy)
lon, lat = healpix_to_lonlat(healpix_index.ravel(), nside, dx.ravel(), dy.ravel(), order=order)
lon = lon.reshape(-1, 4 * step)
lat = lat.reshape(-1, 4 * step)
return lon, lat
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1739993667.0
astropy_healpix-1.1.2/astropy_healpix/healpy.py 0000644 0001751 0000166 00000014574 14755431103 021416 0 ustar 00runner docker # Licensed under a 3-clause BSD style license - see LICENSE.rst
"""
This submodule provides a healpy-compatible interface.
"""
import numpy as np
from astropy import units as u
from astropy.coordinates.representation import CartesianRepresentation, UnitSphericalRepresentation
from .core import (nside_to_pixel_resolution, nside_to_pixel_area,
nside_to_npix, npix_to_nside, nested_to_ring, ring_to_nested,
level_to_nside, lonlat_to_healpix, healpix_to_lonlat,
xyz_to_healpix, healpix_to_xyz,
boundaries_lonlat, bilinear_interpolation_weights,
interpolate_bilinear_lonlat, _NUMPY_COPY_IF_NEEDED)
RAD2DEG = 180 / np.pi
PI_2 = np.pi / 2
__all__ = ['nside2resol',
'nside2pixarea',
'nside2npix',
'npix2nside',
'pix2ang',
'ang2pix',
'pix2vec',
'vec2pix',
'order2nside',
'nest2ring',
'ring2nest',
'boundaries',
'vec2ang',
'ang2vec',
'get_interp_weights',
'get_interp_val']
def _lonlat_to_healpy(lon, lat, lonlat=False):
# We use in-place operations below to avoid making temporary arrays - this
# is safe because the lon/lat arrays returned from healpix_to_lonlat are
# new and not used elsewhere.
if lonlat:
return lon.to(u.deg).value, lat.to(u.deg).value
else:
lat, lon = lat.to(u.rad).value, lon.to(u.rad).value
if np.isscalar(lon):
return PI_2 - lat, lon
else:
lat = np.subtract(PI_2, lat, out=lat)
return lat, lon
def _healpy_to_lonlat(theta, phi, lonlat=False):
# Unlike in _lonlat_to_healpy, we don't use in-place operations since we
# don't want to modify theta and phi since the user may be using them
# elsewhere.
if lonlat:
lon = np.asarray(theta) / RAD2DEG
lat = np.asarray(phi) / RAD2DEG
else:
lat = PI_2 - np.asarray(theta)
lon = np.asarray(phi)
return u.Quantity(lon, u.rad, copy=_NUMPY_COPY_IF_NEEDED), u.Quantity(
lat, u.rad, copy=_NUMPY_COPY_IF_NEEDED)
def nside2resol(nside, arcmin=False):
"""Drop-in replacement for healpy `~healpy.pixelfunc.nside2resol`."""
resolution = nside_to_pixel_resolution(nside)
if arcmin:
return resolution.to(u.arcmin).value
else:
return resolution.to(u.rad).value
def nside2pixarea(nside, degrees=False):
"""Drop-in replacement for healpy `~healpy.pixelfunc.nside2pixarea`."""
area = nside_to_pixel_area(nside)
if degrees:
return area.to(u.deg ** 2).value
else:
return area.to(u.sr).value
def nside2npix(nside):
"""Drop-in replacement for healpy `~healpy.pixelfunc.nside2npix`."""
return nside_to_npix(nside)
def npix2nside(npix):
"""Drop-in replacement for healpy `~healpy.pixelfunc.npix2nside`."""
return npix_to_nside(npix)
def order2nside(order):
"""Drop-in replacement for healpy `~healpy.pixelfunc.order2nside`."""
return level_to_nside(order)
def pix2ang(nside, ipix, nest=False, lonlat=False):
"""Drop-in replacement for healpy `~healpy.pixelfunc.pix2ang`."""
lon, lat = healpix_to_lonlat(ipix, nside, order='nested' if nest else 'ring')
return _lonlat_to_healpy(lon, lat, lonlat=lonlat)
def ang2pix(nside, theta, phi, nest=False, lonlat=False):
"""Drop-in replacement for healpy `~healpy.pixelfunc.ang2pix`."""
lon, lat = _healpy_to_lonlat(theta, phi, lonlat=lonlat)
return lonlat_to_healpix(lon, lat, nside, order='nested' if nest else 'ring')
def pix2vec(nside, ipix, nest=False):
"""Drop-in replacement for healpy `~healpy.pixelfunc.pix2vec`."""
return healpix_to_xyz(ipix, nside, order='nested' if nest else 'ring')
def vec2pix(nside, x, y, z, nest=False):
"""Drop-in replacement for healpy `~healpy.pixelfunc.vec2pix`."""
return xyz_to_healpix(x, y, z, nside, order='nested' if nest else 'ring')
def nest2ring(nside, ipix):
"""Drop-in replacement for healpy `~healpy.pixelfunc.nest2ring`."""
ipix = np.atleast_1d(ipix).astype(np.int64, copy=_NUMPY_COPY_IF_NEEDED)
return nested_to_ring(ipix, nside)
def ring2nest(nside, ipix):
"""Drop-in replacement for healpy `~healpy.pixelfunc.ring2nest`."""
ipix = np.atleast_1d(ipix).astype(np.int64, copy=_NUMPY_COPY_IF_NEEDED)
return ring_to_nested(ipix, nside)
def boundaries(nside, pix, step=1, nest=False):
"""Drop-in replacement for healpy `~healpy.boundaries`."""
pix = np.asarray(pix)
if pix.ndim > 1:
# For consistency with healpy we only support scalars or 1D arrays
raise ValueError("Array has to be one dimensional")
lon, lat = boundaries_lonlat(pix, step, nside, order='nested' if nest else 'ring')
rep_sph = UnitSphericalRepresentation(lon, lat)
rep_car = rep_sph.to_cartesian().xyz.value.swapaxes(0, 1)
if rep_car.shape[0] == 1:
return rep_car[0]
else:
return rep_car
def vec2ang(vectors, lonlat=False):
"""Drop-in replacement for healpy `~healpy.pixelfunc.vec2ang`."""
x, y, z = vectors.transpose()
rep_car = CartesianRepresentation(x, y, z)
rep_sph = rep_car.represent_as(UnitSphericalRepresentation)
return _lonlat_to_healpy(rep_sph.lon.ravel(), rep_sph.lat.ravel(), lonlat=lonlat)
def ang2vec(theta, phi, lonlat=False):
"""Drop-in replacement for healpy `~healpy.pixelfunc.ang2vec`."""
lon, lat = _healpy_to_lonlat(theta, phi, lonlat=lonlat)
rep_sph = UnitSphericalRepresentation(lon, lat)
rep_car = rep_sph.represent_as(CartesianRepresentation)
return rep_car.xyz.value
def get_interp_weights(nside, theta, phi=None, nest=False, lonlat=False):
"""
Drop-in replacement for healpy `~healpy.pixelfunc.get_interp_weights`.
Although note that the order of the weights and pixels may differ.
"""
# if phi is not given, theta is interpreted as pixel number
if phi is None:
theta, phi = pix2ang(nside, ipix=theta, nest=nest)
lon, lat = _healpy_to_lonlat(theta, phi, lonlat=lonlat)
return bilinear_interpolation_weights(lon, lat, nside, order='nested' if nest else 'ring')
def get_interp_val(m, theta, phi, nest=False, lonlat=False):
"""
Drop-in replacement for healpy `~healpy.pixelfunc.get_interp_val`.
"""
lon, lat = _healpy_to_lonlat(theta, phi, lonlat=lonlat)
return interpolate_bilinear_lonlat(lon, lat, m, order='nested' if nest else 'ring')
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1739993667.0
astropy_healpix-1.1.2/astropy_healpix/high_level.py 0000644 0001751 0000166 00000053246 14755431103 022241 0 ustar 00runner docker # Licensed under a 3-clause BSD style license - see LICENSE.rst
import inspect
import os
from astropy.coordinates import (BaseCoordinateFrame, frame_transform_graph,
SkyCoord, UnitSphericalRepresentation)
from .core import (nside_to_pixel_area, nside_to_pixel_resolution,
nside_to_level, nside_to_npix, npix_to_nside,
healpix_to_lonlat, lonlat_to_healpix,
healpix_to_xyz, xyz_to_healpix,
bilinear_interpolation_weights, interpolate_bilinear_lonlat,
ring_to_nested, nested_to_ring, healpix_cone_search,
boundaries_lonlat, neighbours, _validate_order,
_NUMPY_COPY_IF_NEEDED)
from .utils import parse_input_healpix_data
__all__ = ['HEALPix']
NO_FRAME_MESSAGE = """
No frame was specified when initializing HEALPix, so SkyCoord objects cannot be
returned. Either specify a frame when initializing HEALPix or use the {0}
method.
""".replace(os.linesep, ' ').strip()
class NoFrameError(Exception):
def __init__(self, alternative_method):
super().__init__(NO_FRAME_MESSAGE.format(alternative_method))
def _get_frame(frame):
"""
Get a frame instance or None, from the input `frame`, which could be a
frame name string, frame instance, or frame class.
Adapted from
:meth:`astropy.coordinates.sky_coordinate_parsers._get_frame_class`.
"""
if frame is None or isinstance(frame, BaseCoordinateFrame):
return frame
elif isinstance(frame, str):
frame_cls = frame_transform_graph.lookup_name(frame)
if frame_cls is None:
frame_names = frame_transform_graph.get_names()
raise ValueError('Coordinate frame name "{}" is not a known '
'coordinate frame ({})'
.format(frame, sorted(frame_names)))
return frame_cls()
elif inspect.isclass(frame) and issubclass(frame, BaseCoordinateFrame):
return frame()
else:
raise ValueError("Coordinate frame must be a frame name, frame "
"instance, frame class, or None, not a '{}'"
.format(frame.__class__.__name__))
class HEALPix:
"""
A HEALPix pixellization.
Parameters
----------
nside : int
Number of pixels along the side of each of the 12 top-level HEALPix tiles
order : { 'nested' | 'ring' }, optional
Order of HEALPix pixels. Input string can be lower or upper case.
frame : str or :class:`~astropy.coordinates.BaseCoordinateFrame`, optional
The celestial coordinate frame of the pixellization. This can be
ommitted, in which case the pixellization will not be attached to any
particular celestial frame, and the methods ending in _skycoord will
not work (but the _lonlat methods will still work and continue to
return generic longitudes/latitudes). The frame may be passed as a
string (such as ``galactic``), as a frame class, or as an instance of
a frame class.
Raises
------
ValueError
If 'order' is not one of the allowed options.
"""
def __init__(self, nside=None, order='ring', frame=None):
if nside is None:
raise ValueError('nside has not been set')
self.nside = nside
self.order = _validate_order(order)
self.frame = _get_frame(frame)
@classmethod
def from_header(cls, input_data, field=0, hdu_in=None, nested=None):
"""
Parameters
----------
input_data : str or `~astropy.io.fits.TableHDU` or `~astropy.io.fits.BinTableHDU` or tuple
The input data to reproject. This can be:
* The name of a HEALPIX FITS file
* A `~astropy.io.fits.TableHDU` or `~astropy.io.fits.BinTableHDU`
instance
* A tuple where the first element is a `~numpy.ndarray` and the
second element is a `~astropy.coordinates.BaseCoordinateFrame`
instance or a string alias for a coordinate frame.
hdu_in : int or str, optional
If ``input_data`` is a FITS file, specifies the HDU to use.
(the default HDU for HEALPIX data is 1, unlike with image files where
it is generally 0)
nested : bool, optional
The order of the healpix_data, either nested (True) or ring (False).
If a FITS file is passed in, this is determined from the header.
Returns
-------
healpix : `~astropy_healpix.HEALPix`
A HEALPix pixellization corresponding to the input data.
"""
array_in, frame, nested = parse_input_healpix_data(
input_data, field=field, hdu_in=hdu_in, nested=nested)
nside = npix_to_nside(len(array_in))
order = 'nested' if nested else 'ring'
return cls(nside=nside, order=order, frame=frame)
@property
def pixel_area(self):
"""
The area of a single HEALPix pixel.
"""
return nside_to_pixel_area(self.nside)
@property
def pixel_resolution(self):
"""
The resolution of a single HEALPix pixel.
"""
return nside_to_pixel_resolution(self.nside)
@property
def npix(self):
"""
The number of pixels in the pixellization of the sphere.
"""
return nside_to_npix(self.nside)
@property
def level(self):
"""
The HEALPix level.
"""
return nside_to_level(self.nside)
def healpix_to_lonlat(self, healpix_index, dx=None, dy=None):
"""
Convert HEALPix indices (optionally with offsets) to longitudes/latitudes
Parameters
----------
healpix_index : `~numpy.ndarray`
1-D array of HEALPix indices
dx, dy : `~numpy.ndarray`, optional
1-D arrays of offsets inside the HEALPix pixel, which must be in
the range [0:1] (0.5 is the center of the HEALPix pixels). If not
specified, the position at the center of the pixel is used.
Returns
-------
lon : :class:`~astropy.coordinates.Longitude`
The longitude values
lat : :class:`~astropy.coordinates.Latitude`
The latitude values
"""
return healpix_to_lonlat(healpix_index, self.nside, dx=dx, dy=dy, order=self.order)
def lonlat_to_healpix(self, lon, lat, return_offsets=False):
"""
Convert longitudes/latitudes to HEALPix indices (optionally with offsets)
Parameters
----------
lon, lat : :class:`~astropy.units.Quantity`
The longitude and latitude values as :class:`~astropy.units.Quantity` instances
with angle units.
return_offsets : bool
If `True`, the returned values are the HEALPix pixel as well as
``dx`` and ``dy``, the fractional positions inside the pixel. If
`False` (the default), only the HEALPix pixel is returned.
Returns
-------
healpix_index : `~numpy.ndarray`
1-D array of HEALPix indices
dx, dy : `~numpy.ndarray`
1-D arrays of offsets inside the HEALPix pixel in the range [0:1] (0.5
is the center of the HEALPix pixels). This is returned if
``return_offsets`` is `True`.
"""
return lonlat_to_healpix(lon, lat, self.nside,
return_offsets=return_offsets, order=self.order)
def healpix_to_xyz(self, healpix_index, dx=None, dy=None):
"""
Convert HEALPix indices (optionally with offsets) to Cartesian coordinates
Parameters
----------
healpix_index : `~numpy.ndarray`
1-D array of HEALPix indices
dx, dy : `~numpy.ndarray`, optional
1-D arrays of offsets inside the HEALPix pixel, which must be in
the range [0:1] (0.5 is the center of the HEALPix pixels). If not
specified, the position at the center of the pixel is used.
Returns
-------
x : :class:`~numpy.ndarray`
The x coordinates
y : :class:`~numpy.ndarray`
The y coordinates
z : :class:`~numpy.ndarray`
The z coordinates
"""
return healpix_to_xyz(healpix_index, self.nside, dx=dx, dy=dy, order=self.order)
def xyz_to_healpix(self, x, y, z, return_offsets=False):
"""
Convert Cartesian coordinates to HEALPix indices (optionally with offsets)
Parameters
----------
x : :class:`~numpy.ndarray`
The x coordinates
y : :class:`~numpy.ndarray`
The y coordinates
z : :class:`~numpy.ndarray`
The z coordinates
return_offsets : bool
If `True`, the returned values are the HEALPix pixel as well as
``dx`` and ``dy``, the fractional positions inside the pixel. If
`False` (the default), only the HEALPix pixel is returned.
Returns
-------
healpix_index : `~numpy.ndarray`
1-D array of HEALPix indices
dx, dy : `~numpy.ndarray`
1-D arrays of offsets inside the HEALPix pixel in the range [0:1] (0.5
is the center of the HEALPix pixels). This is returned if
``return_offsets`` is `True`.
"""
return xyz_to_healpix(x, y, z, self.nside,
return_offsets=return_offsets, order=self.order)
def nested_to_ring(self, nested_index):
"""
Convert a healpix 'nested' index to a healpix 'ring' index
Parameters
----------
nested_index : `~numpy.ndarray`
Healpix index using the 'nested' ordering
Returns
-------
ring_index : `~numpy.ndarray`
Healpix index using the 'ring' ordering
"""
return nested_to_ring(nested_index, self.nside)
def ring_to_nested(self, ring_index):
"""
Convert a healpix 'ring' index to a healpix 'nested' index
Parameters
----------
ring_index : `~numpy.ndarray`
Healpix index using the 'ring' ordering
Returns
-------
nested_index : `~numpy.ndarray`
Healpix index using the 'nested' ordering
"""
return ring_to_nested(ring_index, self.nside)
def bilinear_interpolation_weights(self, lon, lat):
"""
Get the four neighbours for each (lon, lat) position and the weight
associated with each one for bilinear interpolation.
Parameters
----------
lon, lat : :class:`~astropy.units.Quantity`
The longitude and latitude values as
:class:`~astropy.units.Quantity` instances with angle units.
Returns
-------
indices : `~numpy.ndarray`
2-D array with shape (4, N) giving the four indices to use for the
interpolation
weights : `~numpy.ndarray`
2-D array with shape (4, N) giving the four weights to use for the
interpolation
"""
return bilinear_interpolation_weights(lon, lat, self.nside, order=self.order)
def interpolate_bilinear_lonlat(self, lon, lat, values):
"""
Interpolate values at specific longitudes/latitudes using bilinear interpolation
If a position does not have four neighbours, this currently returns NaN.
Parameters
----------
lon, lat : :class:`~astropy.units.Quantity`
The longitude and latitude values as :class:`~astropy.units.Quantity` instances
with angle units.
values : `~numpy.ndarray`
1-D array with the values in each HEALPix pixel. This must have a
length of the form 12 * nside ** 2 (and nside is determined
automatically from this).
Returns
-------
result : `~numpy.ndarray`
1-D array of interpolated values
"""
if len(values) != self.npix:
raise ValueError('values must be an array of length {} (got {})'
.format(self.npix, len(values)))
return interpolate_bilinear_lonlat(lon, lat, values, order=self.order)
def cone_search_lonlat(self, lon, lat, radius):
"""
Find all the HEALPix pixels within a given radius of a longitude/latitude.
Note that this returns all pixels that overlap, including partially, with
the search cone. This function can only be used for a single lon/lat pair at
a time, since different calls to the function may result in a different
number of matches.
Parameters
----------
lon, lat : :class:`~astropy.units.Quantity`
The longitude and latitude to search around
radius : :class:`~astropy.units.Quantity`
The search radius
Returns
-------
healpix_index : `~numpy.ndarray`
1-D array with all the matching HEALPix pixel indices.
"""
if not lon.isscalar or not lat.isscalar or not radius.isscalar:
raise ValueError('The longitude, latitude and radius must be '
'scalar Quantity objects')
return healpix_cone_search(lon, lat, radius, self.nside, order=self.order)
def boundaries_lonlat(self, healpix_index, step):
"""
Return the longitude and latitude of the edges of HEALPix pixels
This returns the longitude and latitude of points along the edge of each
HEALPIX pixel. The number of points returned for each pixel is ``4 * step``,
so setting ``step`` to 1 returns just the corners.
Parameters
----------
healpix_index : `~numpy.ndarray`
1-D array of HEALPix pixels
step : int
The number of steps to take along each edge.
Returns
-------
lon, lat : :class:`~astropy.units.Quantity`
The longitude and latitude, as 2-D arrays where the first dimension is
the same as the ``healpix_index`` input, and the second dimension has
size ``4 * step``.
"""
return boundaries_lonlat(healpix_index, step, self.nside, order=self.order)
def neighbours(self, healpix_index):
"""
Find all the HEALPix pixels that are the neighbours of a HEALPix pixel
Parameters
----------
healpix_index : `~numpy.ndarray`
Array of HEALPix pixels
Returns
-------
neigh : `~numpy.ndarray`
Array giving the neighbours starting SW and rotating clockwise. This has
one extra dimension compared to ``healpix_index`` - the first dimension -
which is set to 8. For example if healpix_index has shape (2, 3),
``neigh`` has shape (8, 2, 3).
Notes
-----
Some HEALPix pixels do not have all 8 neighbours. In these cases, the
corresponding entry in the returned array has the value of -1 and Numpy
may print an invalid value warning. To suppress the warning, use
:class:`numpy.errstate`.
"""
return neighbours(healpix_index, self.nside, order=self.order)
def healpix_to_skycoord(self, healpix_index, dx=None, dy=None):
"""
Convert HEALPix indices (optionally with offsets) to celestial coordinates.
Note that this method requires that a celestial frame was specified when
initializing HEALPix. If you don't know or need the celestial frame, you
can instead use :meth:`~astropy_healpix.HEALPix.healpix_to_lonlat`.
Parameters
----------
healpix_index : `~numpy.ndarray`
1-D array of HEALPix indices
dx, dy : `~numpy.ndarray`, optional
1-D arrays of offsets inside the HEALPix pixel, which must be in
the range [0:1] (0.5 is the center of the HEALPix pixels). If not
specified, the position at the center of the pixel is used.
Returns
-------
coord : :class:`~astropy.coordinates.SkyCoord`
The resulting celestial coordinates
"""
if self.frame is None:
raise NoFrameError("healpix_to_skycoord")
lon, lat = self.healpix_to_lonlat(healpix_index, dx=dx, dy=dy)
representation = UnitSphericalRepresentation(lon, lat, copy=_NUMPY_COPY_IF_NEEDED)
return SkyCoord(self.frame.realize_frame(representation))
def skycoord_to_healpix(self, skycoord, return_offsets=False):
"""
Convert celestial coordinates to HEALPix indices (optionally with offsets).
Note that this method requires that a celestial frame was specified when
initializing HEALPix. If you don't know or need the celestial frame, you
can instead use :meth:`~astropy_healpix.HEALPix.lonlat_to_healpix`.
Parameters
----------
skycoord : :class:`~astropy.coordinates.SkyCoord`
The celestial coordinates to convert
return_offsets : bool
If `True`, the returned values are the HEALPix pixel as well as
``dx`` and ``dy``, the fractional positions inside the pixel. If
`False` (the default), only the HEALPix pixel is returned.
Returns
-------
healpix_index : `~numpy.ndarray`
1-D array of HEALPix indices
dx, dy : `~numpy.ndarray`
1-D arrays of offsets inside the HEALPix pixel in the range [0:1] (0.5
is the center of the HEALPix pixels). This is returned if
``return_offsets`` is `True`.
"""
if self.frame is None:
raise NoFrameError("skycoord_to_healpix")
skycoord = skycoord.transform_to(self.frame)
representation = skycoord.represent_as(UnitSphericalRepresentation)
lon, lat = representation.lon, representation.lat
return self.lonlat_to_healpix(lon, lat, return_offsets=return_offsets)
def interpolate_bilinear_skycoord(self, skycoord, values):
"""
Interpolate values at specific celestial coordinates using bilinear interpolation.
If a position does not have four neighbours, this currently returns NaN.
Note that this method requires that a celestial frame was specified when
initializing HEALPix. If you don't know or need the celestial frame, you
can instead use :meth:`~astropy_healpix.HEALPix.interpolate_bilinear_lonlat`.
Parameters
----------
skycoord : :class:`~astropy.coordinates.SkyCoord`
The celestial coordinates at which to interpolate
values : `~numpy.ndarray`
1-D array with the values in each HEALPix pixel. This must have a
length of the form 12 * nside ** 2 (and nside is determined
automatically from this).
Returns
-------
result : `~numpy.ndarray`
1-D array of interpolated values
"""
if self.frame is None:
raise NoFrameError("interpolate_bilinear_skycoord")
skycoord = skycoord.transform_to(self.frame)
representation = skycoord.represent_as(UnitSphericalRepresentation)
lon, lat = representation.lon, representation.lat
return self.interpolate_bilinear_lonlat(lon, lat, values)
def cone_search_skycoord(self, skycoord, radius):
"""
Find all the HEALPix pixels within a given radius of a celestial position.
Note that this returns all pixels that overlap, including partially,
with the search cone. This function can only be used for a single
celestial position at a time, since different calls to the function may
result in a different number of matches.
This method requires that a celestial frame was specified when
initializing HEALPix. If you don't know or need the celestial frame,
you can instead use :meth:`~astropy_healpix.HEALPix.cone_search_lonlat`.
Parameters
----------
skycoord : :class:`~astropy.coordinates.SkyCoord`
The celestial coordinates to use for the cone search
radius : :class:`~astropy.units.Quantity`
The search radius
Returns
-------
healpix_index : `~numpy.ndarray`
1-D array with all the matching HEALPix pixel indices.
"""
if self.frame is None:
raise NoFrameError("cone_search_skycoord")
skycoord = skycoord.transform_to(self.frame)
representation = skycoord.represent_as(UnitSphericalRepresentation)
lon, lat = representation.lon, representation.lat
return self.cone_search_lonlat(lon, lat, radius)
def boundaries_skycoord(self, healpix_index, step):
"""
Return the celestial coordinates of the edges of HEALPix pixels
This returns the celestial coordinates of points along the edge of each
HEALPIX pixel. The number of points returned for each pixel is ``4 * step``,
so setting ``step`` to 1 returns just the corners.
This method requires that a celestial frame was specified when
initializing HEALPix. If you don't know or need the celestial frame,
you can instead use :meth:`~astropy_healpix.HEALPix.boundaries_lonlat`.
Parameters
----------
healpix_index : `~numpy.ndarray`
1-D array of HEALPix pixels
step : int
The number of steps to take along each edge.
Returns
-------
skycoord : :class:`~astropy.coordinates.SkyCoord`
The celestial coordinates of the HEALPix pixel boundaries
"""
if self.frame is None:
raise NoFrameError("boundaries_skycoord")
lon, lat = self.boundaries_lonlat(healpix_index, step)
representation = UnitSphericalRepresentation(lon, lat, copy=_NUMPY_COPY_IF_NEEDED)
return SkyCoord(self.frame.realize_frame(representation))
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1739993667.0
astropy_healpix-1.1.2/astropy_healpix/interpolation.c 0000644 0001751 0000166 00000015540 14755431103 022607 0 ustar 00runner docker #include
#include "interpolation.h"
#include "healpix.h"
// Old versions of MSVC do not support C99 and therefore
// do not define NAN in math.h.
#ifndef NAN
static const union {
unsigned long integer;
float value;
} type_punned_nan = {0xFFFFFFFFFFFFFFFFul};
#define NAN (type_punned_nan.value)
#endif
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
void interpolate_weights(double lon, double lat, int64_t *ring_indices,
double *weights, int Nside) {
// Given a longitude and a latitude, Nside, and pre-allocated arrays of 4
// elements ring_indices and weights, find the ring index of the four nearest
// neighbours and the weights to use for each neighbour to interpolate.
int64_t xy_index, npix;
int64_t ring1, ring2, ring3, ring4;
double lon1, lat1, lon2, lat2;
double lon3, lat3, lon4, lat4;
double xfrac1, xfrac2, yfrac, lon_frac;
int ring_number, longitude_index, n_in_ring;
// Find the xy index of the pixel in which the coordinates fall
xy_index = radec_to_healpixl(lon, lat, Nside);
// Find the lon/lat of the center of that pixel
healpixl_to_radec(xy_index, Nside, 0.5, 0.5, &lon1, &lat1);
// Take into account possible wrapping so that the pixel longitude/latitude
// are close to the requested longitude/latitude
if (lon - lon1 > M_PI)
lon1 += 2 * M_PI;
if (lon1 - lon > M_PI)
lon1 -= 2 * M_PI;
// Convert to a ring index and decompose into ring number and longitude index
ring1 = healpixl_xy_to_ring(xy_index, Nside);
if (ring1 < 0)
{
int i;
for (i = 0; i < 4; i ++)
{
ring_indices[i] = -1;
weights[i] = NAN;
}
return;
}
healpixl_decompose_ring(ring1, Nside, &ring_number, &longitude_index);
// Figure out how many pixels are in the ring
if (ring_number < Nside) {
n_in_ring = 4 * ring_number;
} else if (ring_number < 3 * Nside) {
n_in_ring = 4 * Nside;
} else {
n_in_ring = (int)(4 * (4 * (int64_t)Nside - (int64_t)ring_number));
}
// We now want to find the next index in the ring so that the point to
// interpolate is between the two. First we check what direction to look in by
// finding the longitude/latitude of the center of the HEALPix pixel.
if (lon < lon1) { // Go to the left
if (longitude_index == 0) {
ring2 = ring1 + n_in_ring - 1;
} else {
ring2 = ring1 - 1;
}
} else { // Go to the right
if (longitude_index == n_in_ring - 1) {
ring2 = ring1 - n_in_ring + 1;
} else {
ring2 = ring1 + 1;
}
}
// Find the lon/lat of the new pixel
xy_index = healpixl_ring_to_xy(ring2, Nside);
healpixl_to_radec(xy_index, Nside, 0.5, 0.5, &lon2, &lat2);
// Take into account possible wrapping so that the pixel longitude/latitude
// are close to the requested longitude/latitude
if (lon - lon2 > M_PI)
lon2 += 2 * M_PI;
if (lon2 - lon > M_PI)
lon2 -= 2 * M_PI;
// Now check whether we are moving up or down in terms of ring index
if (lat > lat1) { // Move up (0 index is at the top)
ring_number -= 1;
} else { // Move down
ring_number += 1;
}
if (ring_number > 0 && ring_number < 4 * Nside) {
// Now figure out again how many pixels are in the ring
if (ring_number < Nside) {
n_in_ring = 4 * ring_number;
} else if (ring_number < 3 * Nside) {
n_in_ring = 4 * Nside;
} else {
n_in_ring = (int)(4 * (4 * (int64_t)Nside - (int64_t)ring_number));
}
// Now determine the longitude index in which the requested longitude falls.
// In all regions, the longitude elements are spaced by 360 / n_in_ring. For
// convenience we convert the longitude index so that the spacing is 1.
lon_frac = lon * n_in_ring / (2 * M_PI);
// In the equatorial region, the first ring starts at 0.5 and the second at
// 0 (in lon_frac space). The ring number is 1-based and the first ring in
// the equatorial region is even. In this ring we can simply take
// int(lon_frac) to get the longitude index but in the odd rings we need to
// adjust lon_frac
if (n_in_ring == 4 * Nside && ring_number % 2 == 1) { // Equatorial region
lon_frac += 0.5;
}
// Find the longitude index of the closest pixel
longitude_index = (int)lon_frac;
if (longitude_index == n_in_ring) {
longitude_index -= 1;
}
// Find the longitude/latitude and ring index of this pixel
ring3 = healpixl_compose_ring(ring_number, longitude_index, Nside);
xy_index = healpixl_ring_to_xy(ring3, Nside);
healpixl_to_radec(xy_index, Nside, 0.5, 0.5, &lon3, &lat3);
// Take into account possible wrapping so that the pixel longitude/latitude
// are close to the requested longitude/latitude
if (lon - lon3 > M_PI)
lon3 += 2 * M_PI;
if (lon3 - lon > M_PI)
lon3 -= 2 * M_PI;
// Finally we can find the fourth pixel as before
if (lon < lon3) { // Go to the left
if (longitude_index == 0) {
ring4 = ring3 + n_in_ring - 1;
} else {
ring4 = ring3 - 1;
}
} else { // Go to the right
if (longitude_index == n_in_ring - 1) {
ring4 = ring3 - n_in_ring + 1;
} else {
ring4 = ring3 + 1;
}
}
xy_index = healpixl_ring_to_xy(ring4, Nside);
healpixl_to_radec(xy_index, Nside, 0.5, 0.5, &lon4, &lat4);
// Take into account possible wrapping so that the pixel longitude/latitude
// are close to the requested longitude/latitude
if (lon - lon4 > M_PI)
lon4 += 2 * M_PI;
if (lon4 - lon > M_PI)
lon4 -= 2 * M_PI;
// Determine the interpolation weights
xfrac1 = (lon - lon1) / (lon2 - lon1);
xfrac2 = (lon - lon3) / (lon4 - lon3);
yfrac = (lat - lat1) / (lat3 - lat1);
weights[0] = (1 - xfrac1) * (1 - yfrac);
weights[1] = xfrac1 * (1 - yfrac);
weights[2] = (1 - xfrac2) * yfrac;
weights[3] = xfrac2 * yfrac;
} else {
// In the case where we are inside the four top/bottom-most
// values, we effectively place a value at the pole that
// is the average of the four values, and the interpolation
// is the weighted average of this polar value and the
// value interpolated along the ring.
xfrac1 = (lon - lon1) / (lon2 - lon1);
yfrac = (lat - lat1) / (0.5 * M_PI - lat1);
if (ring_number == 0) {
ring3 = (ring1 + 2) % 4;
ring4 = (ring2 + 2) % 4;
yfrac = (lat - lat1) / (0.5 * M_PI - lat1);
} else {
npix = 12 * (int64_t)Nside * (int64_t)Nside;
ring3 = ((ring1 - (npix - 4)) + 2) % 4 + npix - 4;
ring4 = ((ring2 - (npix - 4)) + 2) % 4 + npix - 4;
yfrac = (lat - lat1) / (-0.5 * M_PI - lat1);
}
weights[0] = (1 - xfrac1) * (1 - yfrac) + 0.25 * yfrac;
weights[1] = xfrac1 * (1 - yfrac) + 0.25 * yfrac;
weights[2] = 0.25 * yfrac;
weights[3] = 0.25 * yfrac;
}
ring_indices[0] = ring1;
ring_indices[1] = ring2;
ring_indices[2] = ring3;
ring_indices[3] = ring4;
}
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1739993667.0
astropy_healpix-1.1.2/astropy_healpix/interpolation.h 0000644 0001751 0000166 00000000535 14755431103 022612 0 ustar 00runner docker #ifndef ASTROPY_HEALPIX_INTERPOLATION_INCL
#define ASTROPY_HEALPIX_INTERPOLATION_INCL
#ifdef _MSC_VER
#if _MSC_VER >= 1600
#include
#else
#include
#endif
#else
#include
#endif
void interpolate_weights(double lon, double lat, int64_t *ring_indices,
double *weights, int Nside);
#endif
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1739993667.0
astropy_healpix-1.1.2/astropy_healpix/setup_package.py 0000644 0001751 0000166 00000002305 14755431103 022734 0 ustar 00runner docker # Licensed under a 3-clause BSD style license - see LICENSE.rst
import os
import numpy as np
HEALPIX_ROOT = os.path.relpath(os.path.dirname(__file__))
C_FILES = ['bl.c',
'healpix-utils.c',
'healpix.c',
'mathutil.c',
'permutedsort.c',
'qsort_reentrant.c',
'starutil.c']
C_DIR = os.path.join('cextern', 'astrometry.net')
C_DIRS = [np.get_include(), C_DIR, HEALPIX_ROOT,
os.path.join('cextern', 'numpy')]
def get_extensions():
from setuptools import Extension
libraries = []
sources = [os.path.join(C_DIR, filename) for filename in C_FILES]
sources.append(os.path.join(HEALPIX_ROOT, 'interpolation.c'))
sources.append(os.path.join(HEALPIX_ROOT, '_core.c'))
extension = Extension(
name="astropy_healpix._core",
sources=sources,
include_dirs=C_DIRS,
libraries=libraries,
language="c",
extra_compile_args=['-O2'],
py_limited_api=True,
define_macros=[('Py_LIMITED_API', 0x030A0000),
('NPY_TARGET_VERSION', 'NPY_1_19_API_VERSION'),
('NPY_NO_DEPRECATED_API', 'NPY_1_19_API_VERSION')])
return [extension]
././@PaxHeader 0000000 0000000 0000000 00000000033 00000000000 010211 x ustar 00 27 mtime=1739993677.384127
astropy_healpix-1.1.2/astropy_healpix/tests/ 0000755 0001751 0000166 00000000000 14755431115 020714 5 ustar 00runner docker ././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1739993667.0
astropy_healpix-1.1.2/astropy_healpix/tests/__init__.py 0000644 0001751 0000166 00000000100 14755431103 023011 0 ustar 00runner docker # Licensed under a 3-clause BSD style license - see LICENSE.rst
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1739993667.0
astropy_healpix-1.1.2/astropy_healpix/tests/test_bench.py 0000644 0001751 0000166 00000000101 14755431103 023371 0 ustar 00runner docker from ..bench import main
def test_bench():
main(fast=True)
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1739993667.0
astropy_healpix-1.1.2/astropy_healpix/tests/test_core.py 0000644 0001751 0000166 00000030562 14755431103 023260 0 ustar 00runner docker # Licensed under a 3-clause BSD style license - see LICENSE.rst
from itertools import product
import pytest
import numpy as np
from numpy.testing import assert_allclose, assert_equal
from astropy import units as u
from astropy.coordinates import Longitude, Latitude
from ..core import (nside_to_pixel_area, nside_to_pixel_resolution, pixel_resolution_to_nside,
nside_to_npix, npix_to_nside, healpix_to_lonlat,
lonlat_to_healpix, interpolate_bilinear_lonlat,
neighbours, healpix_cone_search, boundaries_lonlat,
level_to_nside, nside_to_level,
nested_to_ring, ring_to_nested,
level_ipix_to_uniq, uniq_to_level_ipix,
bilinear_interpolation_weights)
def test_level_to_nside():
assert level_to_nside(5) == 2 ** 5
with pytest.raises(ValueError) as exc:
level_to_nside(-1)
assert exc.value.args[0] == 'level must be positive'
def test_nside_to_level():
assert nside_to_level(1024) == 10
with pytest.raises(ValueError) as exc:
nside_to_level(511)
assert exc.value.args[0] == 'nside must be a power of two'
def test_level_ipix_to_uniq():
assert 11 + 4*4**0 == level_ipix_to_uniq(0, 11)
assert 62540 + 4*4**15 == level_ipix_to_uniq(15, 62540)
with pytest.raises(ValueError) as exc:
level_ipix_to_uniq(1, 49)
assert exc.value.args[0] == 'ipix for a specific level must be inferior to npix'
@pytest.mark.parametrize("level", [
0, 5, 10, 15, 20, 22, 25, 26, 27, 28, 29
])
def test_uniq_to_level_ipix(level):
npix = 3 << 2*(level + 1)
# Take 10 pixel indices between 0 and npix - 1
size = 10
ipix = np.arange(size, dtype=np.int64) * (npix // size)
level = np.ones(size) * level
level_res, ipix_res = uniq_to_level_ipix(level_ipix_to_uniq(level, ipix))
assert np.all(level_res == level) & np.all(ipix_res == ipix)
def test_nside_to_pixel_area():
resolution = nside_to_pixel_area(256)
assert_allclose(resolution.value, 1.5978966540475428e-05)
assert resolution.unit == u.sr
def test_nside_to_pixel_resolution():
resolution = nside_to_pixel_resolution(256)
assert_allclose(resolution.value, 13.741945647269624)
assert resolution.unit == u.arcmin
def test_pixel_resolution_to_nside():
# Check the different rounding options
nside = pixel_resolution_to_nside(13 * u.arcmin, round='nearest')
assert nside == 256
nside = pixel_resolution_to_nside(13 * u.arcmin, round='up')
assert nside == 512
nside = pixel_resolution_to_nside(13 * u.arcmin, round='down')
assert nside == 256
# Check that it works with arrays
nside = pixel_resolution_to_nside([1e3, 10, 1e-3] * u.deg, round='nearest')
assert_equal(nside, [1, 8, 65536])
with pytest.raises(ValueError) as exc:
pixel_resolution_to_nside(13 * u.arcmin, round='peaches')
assert exc.value.args[0] == "Invalid value for round: 'peaches'"
with pytest.raises(AttributeError) as exc:
pixel_resolution_to_nside(13)
assert exc.value.args[0] == "'int' object has no attribute 'to'"
def test_nside_to_npix():
npix = nside_to_npix(4)
assert npix == 192
npix = nside_to_npix([4, 4])
assert_equal(npix, 192)
with pytest.raises(ValueError) as exc:
nside_to_npix(15)
assert exc.value.args[0] == 'nside must be a power of two'
def test_npix_to_nside():
nside = npix_to_nside(192)
assert nside == 4
nside = npix_to_nside([192, 192])
assert_equal(nside, 4)
with pytest.raises(ValueError) as exc:
npix_to_nside(7)
assert exc.value.args[0] == 'Number of pixels must be divisible by 12'
with pytest.raises(ValueError) as exc:
npix_to_nside(12 * 8 * 9)
assert exc.value.args[0] == 'Number of pixels is not of the form 12 * nside ** 2'
# For the following tests, the numerical accuracy of this function is already
# tested in test_cython_api.py, so we focus here on functionality specific to
# the Python functions.
@pytest.mark.parametrize('order', ['nested', 'ring'])
def test_healpix_to_lonlat(order):
lon, lat = healpix_to_lonlat([1, 2, 3], 4, order=order)
assert isinstance(lon, Longitude)
assert isinstance(lat, Latitude)
index = lonlat_to_healpix(lon, lat, 4, order=order)
assert_equal(index, [1, 2, 3])
lon, lat = healpix_to_lonlat([1, 2, 3], 4,
dx=[0.1, 0.2, 0.3],
dy=[0.5, 0.4, 0.7], order=order)
assert isinstance(lon, Longitude)
assert isinstance(lat, Latitude)
index, dx, dy = lonlat_to_healpix(lon, lat, 4, order=order, return_offsets=True)
assert_equal(index, [1, 2, 3])
assert_allclose(dx, [0.1, 0.2, 0.3])
assert_allclose(dy, [0.5, 0.4, 0.7])
def test_healpix_to_lonlat_invalid():
dx = [0.1, 0.4, 0.9]
dy = [0.4, 0.3, 0.2]
with pytest.warns(RuntimeWarning, match='invalid value'):
lon, lat = healpix_to_lonlat([-1, 2, 3], 4)
with pytest.warns(RuntimeWarning, match='invalid value'):
lon, lat = healpix_to_lonlat([192, 2, 3], 4)
with pytest.raises(ValueError) as exc:
lon, lat = healpix_to_lonlat([1, 2, 3], 5)
assert exc.value.args[0] == 'nside must be a power of two'
with pytest.raises(ValueError) as exc:
lon, lat = healpix_to_lonlat([1, 2, 3], 4, order='banana')
assert exc.value.args[0] == "order must be 'nested' or 'ring'"
with pytest.raises(ValueError) as exc:
lon, lat = healpix_to_lonlat([1, 2, 3], 4, dx=[-0.1, 0.4, 0.5], dy=dy)
assert exc.value.args[0] == 'dx must be in the range [0:1]'
with pytest.raises(ValueError) as exc:
lon, lat = healpix_to_lonlat([1, 2, 3], 4, dx=dx, dy=[-0.1, 0.4, 0.5])
assert exc.value.args[0] == 'dy must be in the range [0:1]'
def test_healpix_to_lonlat_shape():
lon, lat = healpix_to_lonlat(2, 8)
assert lon.isscalar and lat.isscalar
lon, lat = healpix_to_lonlat([[1, 2, 3], [3, 4, 4]], 8)
assert lon.shape == (2, 3) and lat.shape == (2, 3)
lon, lat = healpix_to_lonlat([[1], [2], [3]], nside=8, dx=0.2, dy=[[0.1, 0.3]])
assert lon.shape == (3, 2) and lat.shape == (3, 2)
def test_lonlat_to_healpix_shape():
healpix_index = lonlat_to_healpix(2 * u.deg, 3 * u.deg, 8)
assert np.can_cast(healpix_index, np.int64)
lon, lat = np.ones((2, 4)) * u.deg, np.zeros((2, 4)) * u.deg
healpix_index = lonlat_to_healpix(lon, lat, 8)
assert healpix_index.shape == (2, 4)
healpix_index, dx, dy = lonlat_to_healpix(2 * u.deg, 3 * u.deg, 8, return_offsets=True)
assert np.can_cast(healpix_index, np.int64)
assert isinstance(dx, float)
assert isinstance(dy, float)
lon, lat = np.ones((2, 4)) * u.deg, np.zeros((2, 4)) * u.deg
healpix_index, dx, dy = lonlat_to_healpix(lon, lat, 8, return_offsets=True)
assert healpix_index.shape == (2, 4)
assert dx.shape == (2, 4)
assert dy.shape == (2, 4)
def test_lonlat_to_healpix_invalid():
"""Check that if we pass NaN values for example, the index is set to -1"""
with pytest.warns(RuntimeWarning, match='invalid value'):
ipix = lonlat_to_healpix(np.nan * u.deg, np.nan * u.deg,
nside=1, order='nested')
assert ipix == -1
@pytest.mark.parametrize('function', [nested_to_ring, ring_to_nested])
def test_nested_ring_shape(function):
index = function(1, 8)
assert np.can_cast(index, np.int64)
index = function([[1, 2, 3], [2, 3, 4]], 8)
assert index.shape == (2, 3)
@pytest.mark.parametrize('order', ['nested', 'ring'])
def test_bilinear_interpolation_weights(order):
indices, weights = bilinear_interpolation_weights(100 * u.deg, 10 * u.deg,
nside=4, order=order)
if order == 'nested':
indices = nested_to_ring(indices, nside=4)
assert_equal(indices, [76, 77, 60, 59])
assert_allclose(weights, [0.532723, 0.426179, 0.038815, 0.002283], atol=1e-6)
def test_bilinear_interpolation_weights_invalid():
with pytest.raises(ValueError) as exc:
bilinear_interpolation_weights(1 * u.deg, 2 * u.deg, nside=5)
assert exc.value.args[0] == 'nside must be a power of two'
with pytest.raises(ValueError) as exc:
bilinear_interpolation_weights(3 * u.deg, 4 * u.deg,
nside=4, order='banana')
assert exc.value.args[0] == "order must be 'nested' or 'ring'"
def test_bilinear_interpolation_weights_shape():
indices, weights = bilinear_interpolation_weights(3 * u.deg, 4 * u.deg, nside=8)
assert indices.shape == (4,)
assert weights.shape == (4,)
indices, weights = bilinear_interpolation_weights([[1, 2, 3], [2, 3, 4]] * u.deg,
[[1, 2, 3], [2, 3, 4]] * u.deg, nside=8)
assert indices.shape == (4, 2, 3)
assert weights.shape == (4, 2, 3)
@pytest.mark.parametrize('order', ['nested', 'ring'])
def test_interpolate_bilinear_lonlat(order):
values = np.ones(192) * 3
result = interpolate_bilinear_lonlat([1, 3, 4] * u.deg, [3, 2, 6] * u.deg,
values, order=order)
assert_allclose(result, [3, 3, 3])
def test_interpolate_bilinear_invalid():
values = np.ones(133)
with pytest.raises(ValueError) as exc:
interpolate_bilinear_lonlat([1, 3, 4] * u.deg, [3, 2, 6] * u.deg, values)
assert exc.value.args[0] == 'Number of pixels must be divisible by 12'
values = np.ones(192)
with pytest.raises(ValueError) as exc:
interpolate_bilinear_lonlat([1, 3, 4] * u.deg, [3, 2, 6] * u.deg,
values, order='banana')
assert exc.value.args[0] == "order must be 'nested' or 'ring'"
with pytest.warns(RuntimeWarning, match='invalid value'):
result = interpolate_bilinear_lonlat([0, np.nan] * u.deg,
[0, np.nan] * u.deg, values,
order='nested')
assert result.shape == (2,)
assert result[0] == 1
assert np.isnan(result[1])
def test_interpolate_bilinear_lonlat_shape():
values = np.ones(192) * 3
result = interpolate_bilinear_lonlat(3 * u.deg, 4 * u.deg, values)
assert isinstance(result, float)
result = interpolate_bilinear_lonlat([[1, 2, 3], [2, 3, 4]] * u.deg,
[[1, 2, 3], [2, 3, 4]] * u.deg, values)
assert result.shape == (2, 3)
values = np.ones((192, 50)) * 3
lon = np.ones((3, 6, 5)) * u.deg
lat = np.ones((3, 6, 5)) * u.deg
result = interpolate_bilinear_lonlat(lon, lat, values)
assert result.shape == (3, 6, 5, 50)
@pytest.mark.parametrize('order', ['nested', 'ring'])
def test_neighbours(order):
neigh = neighbours([1, 2, 3], 4, order=order)
if order == 'nested':
expected = [[0, 71, 2],
[2, 77, 8],
[3, 8, 9],
[6, 9, 12],
[4, 3, 6],
[94, 1, 4],
[91, 0, 1],
[90, 69, 0]]
else:
expected = [[6, 8, 10],
[5, 7, 9],
[0, 1, 2],
[3, 0, 1],
[2, 3, 0],
[8, 10, 4],
[7, 9, 11],
[16, 19, 22]]
assert_equal(neigh, expected)
def test_neighbours_invalid():
with pytest.warns(RuntimeWarning, match='invalid value'):
neighbours([-1, 2, 3], 4)
with pytest.warns(RuntimeWarning, match='invalid value'):
neighbours([192, 2, 3], 4)
with pytest.raises(ValueError) as exc:
neighbours([1, 2, 3], 5)
assert exc.value.args[0] == 'nside must be a power of two'
with pytest.raises(ValueError) as exc:
neighbours([1, 2, 3], 4, order='banana')
assert exc.value.args[0] == "order must be 'nested' or 'ring'"
def test_neighbours_shape():
neigh = neighbours([[1, 2, 3], [2, 3, 4]], 4)
assert neigh.shape == (8, 2, 3)
@pytest.mark.parametrize('order', ['nested', 'ring'])
def test_healpix_cone_search(order):
indices = healpix_cone_search(10 * u.deg, 20 * u.deg, 1 * u.deg,
nside=256, order=order)
assert len(indices) == 80
@pytest.mark.parametrize(('step', 'order'), product([1, 4, 10], ['nested', 'ring']))
def test_boundaries_lonlat(step, order):
lon, lat = boundaries_lonlat([10, 20, 30], step, 256, order=order)
assert lon.shape == (3, 4 * step)
assert lat.shape == (3, 4 * step)
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1739993667.0
astropy_healpix-1.1.2/astropy_healpix/tests/test_healpy.py 0000644 0001751 0000166 00000031151 14755431103 023605 0 ustar 00runner docker # Licensed under a 3-clause BSD style license - see LICENSE.rst
from itertools import product
from astropy.coordinates import angular_separation
from astropy import units as u
import pytest
import numpy as np
from numpy.testing import assert_equal, assert_allclose
from .. import healpy as hp_compat
from ..high_level import xyz_to_healpix
from hypothesis import given, settings, example
from hypothesis.strategies import integers, floats, booleans, tuples
from hypothesis.extra.numpy import arrays
# NOTE: If healpy is installed, we use it in these tests, but healpy is not a
# formal dependency of astropy-healpix.
hp = pytest.importorskip('healpy')
NSIDE_VALUES = [2 ** n for n in range(1, 6)]
@pytest.mark.parametrize(('nside', 'degrees'), product(NSIDE_VALUES, (False, True)))
def test_nside2pixarea(nside, degrees):
actual = hp_compat.nside2pixarea(nside=nside, degrees=degrees)
expected = hp.nside2pixarea(nside=nside, degrees=degrees)
assert_allclose(actual, expected)
@pytest.mark.parametrize(('nside', 'arcmin'), product(NSIDE_VALUES, (False, True)))
def test_nside2resol(nside, arcmin):
actual = hp_compat.nside2resol(nside=nside, arcmin=arcmin)
expected = hp.nside2resol(nside=nside, arcmin=arcmin)
assert_allclose(actual, expected)
@pytest.mark.parametrize('nside', NSIDE_VALUES)
def test_nside2npix(nside):
actual = hp_compat.nside2npix(nside)
expected = hp.nside2npix(nside)
assert_equal(actual, expected)
@pytest.mark.parametrize('level', [0, 3, 7])
def test_order2nside(level):
actual = hp_compat.order2nside(level)
expected = hp.order2nside(level)
assert_equal(actual, expected)
@pytest.mark.parametrize('npix', [12 * 2 ** (2 * n) for n in range(1, 6)])
def test_npix2nside(npix):
actual = hp_compat.npix2nside(npix)
expected = hp.npix2nside(npix)
assert_equal(actual, expected)
# For the test below, we exclude latitudes that fall exactly on the pole or
# the equator since points that fall at exact boundaries are ambiguous.
@given(nside_pow=integers(0, 29), nest=booleans(), lonlat=booleans(),
lon=floats(0, 360, allow_nan=False, allow_infinity=False).filter(
lambda lon: abs(lon) > 1e-10),
lat=floats(-90, 90, allow_nan=False, allow_infinity=False).filter(
lambda lat: abs(lat) < 89.99 and abs(lat) > 1e-10))
@settings(max_examples=2000, derandomize=True, deadline=None)
def test_ang2pix(nside_pow, lon, lat, nest, lonlat):
nside = 2 ** nside_pow
if lonlat:
theta, phi = lon, lat
else:
theta, phi = np.pi / 2. - np.radians(lat), np.radians(lon)
ipix1 = hp_compat.ang2pix(nside, theta, phi, nest=nest, lonlat=lonlat)
ipix2 = hp.ang2pix(nside, theta, phi, nest=nest, lonlat=lonlat)
assert ipix1 == ipix2
def test_ang2pix_shape():
ipix = hp_compat.ang2pix(8, 1., 2.)
assert np.can_cast(ipix, np.int64)
ipix = hp_compat.ang2pix(8, [[1., 2.], [3., 4.]], [[1., 2.], [3., 4.]])
assert ipix.shape == (2, 2)
def test_pix2ang_shape():
lon, lat = hp_compat.pix2ang(8, 1)
assert isinstance(lon, float)
assert isinstance(lat, float)
lon, lat = hp_compat.pix2ang(8, [[1, 2, 3], [4, 5, 6]])
assert lon.shape == (2, 3)
assert lat.shape == (2, 3)
@given(nside_pow=integers(0, 29), nest=booleans(), lonlat=booleans(),
frac=floats(0, 1, allow_nan=False, allow_infinity=False).filter(lambda x: x < 1))
@settings(max_examples=2000, derandomize=True, deadline=None)
@example(nside_pow=29, frac=0.1666666694606345, nest=False, lonlat=False)
@example(nside_pow=27, frac=2./3., nest=True, lonlat=False)
def test_pix2ang(nside_pow, frac, nest, lonlat):
nside = 2 ** nside_pow
ipix = int(frac * 12 * nside ** 2)
theta1, phi1 = hp_compat.pix2ang(nside, ipix, nest=nest, lonlat=lonlat)
theta2, phi2 = hp.pix2ang(nside, ipix, nest=nest, lonlat=lonlat)
if lonlat:
assert_allclose(phi1, phi2, atol=1e-8)
if abs(phi1) < 90:
assert_allclose(theta1, theta2, atol=1e-10)
else:
assert_allclose(theta1, theta2, atol=1e-8)
if theta1 > 0:
assert_allclose(phi1, phi2, atol=1e-10)
def not_on_boundaries(args):
"""Skip vectors that are on the boundary of a pixel where ipix is ambiguous."""
nside_pow, nest, x, y, z = args
nside = 2 ** nside_pow
_, dx, dy = xyz_to_healpix(
x, y, z, nside, return_offsets=True, order="nested" if nest else "ring"
)
return 0 < dx < 1 and 0 < dy < 1
@given(
args=tuples(
integers(0, 29), booleans(),
floats(-1, 1, allow_nan=False, allow_infinity=False).filter(lambda x: abs(x) > 1e-10),
floats(-1, 1, allow_nan=False, allow_infinity=False).filter(lambda y: abs(y) > 1e-10),
floats(-1, 1, allow_nan=False, allow_infinity=False).filter(lambda z: abs(z) > 1e-10)
).filter(not_on_boundaries)
)
@settings(max_examples=2000, derandomize=True, deadline=None)
def test_vec2pix(args):
nside_pow, nest, x, y, z = args
nside = 2 ** nside_pow
ipix1 = hp_compat.vec2pix(nside, x, y, z, nest=nest)
ipix2 = hp.vec2pix(nside, x, y, z, nest=nest)
assert ipix1 == ipix2
@given(nside_pow=integers(0, 29), nest=booleans(),
frac=floats(0, 1, allow_nan=False, allow_infinity=False).filter(lambda x: x < 1))
@settings(max_examples=2000, derandomize=True, deadline=None)
@example(nside_pow=29, frac=0.1666666694606345, nest=False)
def test_pix2vec(nside_pow, frac, nest):
nside = 2 ** nside_pow
ipix = int(frac * 12 * nside ** 2)
xyz1 = hp_compat.pix2vec(nside, ipix, nest=nest)
xyz2 = hp.pix2vec(nside, ipix, nest=nest)
assert_allclose(xyz1, xyz2, atol=1e-8)
def test_vec2pix_shape():
ipix = hp_compat.vec2pix(8, 1., 2., 3.)
assert np.can_cast(ipix, np.int64)
ipix = hp_compat.vec2pix(8, [[1., 2.], [3., 4.]], [[5., 6.], [7., 8.]], [[9., 10.], [11., 12.]])
assert ipix.shape == (2, 2)
def test_pix2vec_shape():
x, y, z = hp_compat.pix2vec(8, 1)
assert isinstance(x, float)
assert isinstance(y, float)
assert isinstance(z, float)
x, y, z = hp_compat.pix2vec(8, [[1, 2, 3], [4, 5, 6]])
assert x.shape == (2, 3)
assert y.shape == (2, 3)
assert z.shape == (2, 3)
@given(nside_pow=integers(0, 29),
frac=floats(0, 1, allow_nan=False, allow_infinity=False).filter(lambda x: x < 1))
@settings(max_examples=2000, derandomize=True, deadline=None)
def test_nest2ring(nside_pow, frac):
nside = 2 ** nside_pow
nest = int(frac * 12 * nside ** 2)
ring1 = hp_compat.nest2ring(nside, nest)
ring2 = hp.nest2ring(nside, nest)
assert ring1 == ring2
@given(nside_pow=integers(0, 29),
frac=floats(0, 1, allow_nan=False, allow_infinity=False).filter(lambda x: x < 1))
@settings(max_examples=2000, derandomize=True, deadline=None)
@example(nside_pow=29, frac=0.16666666697710755)
def test_ring2nest(nside_pow, frac):
nside = 2 ** nside_pow
ring = int(frac * 12 * nside ** 2)
nest1 = hp_compat.ring2nest(nside, ring)
nest2 = hp.ring2nest(nside, ring)
assert nest1 == nest2
@given(nside_pow=integers(0, 29), step=integers(1, 10), nest=booleans(),
frac=floats(0, 1, allow_nan=False, allow_infinity=False).filter(lambda x: x < 1))
@settings(max_examples=500, derandomize=True, deadline=None)
def test_boundaries(nside_pow, frac, step, nest):
nside = 2 ** nside_pow
pix = int(frac * 12 * nside ** 2)
b1 = hp_compat.boundaries(nside, pix, step=step, nest=nest)
b2 = hp.boundaries(nside, pix, step=step, nest=nest)
assert_allclose(b1, b2, atol=1e-8)
def test_boundaries_shape():
pix = 1
b1 = hp_compat.boundaries(8, pix, step=4)
b2 = hp.boundaries(8, pix, step=4)
assert b1.shape == b2.shape
pix = [1, 2, 3, 4, 5]
b1 = hp_compat.boundaries(8, pix, step=4)
b2 = hp.boundaries(8, pix, step=4)
assert b1.shape == b2.shape
def not_at_origin(vec):
return np.linalg.norm(vec) > 0
@given(vectors=arrays(float, (3,), elements=floats(-1, 1)).filter(not_at_origin),
lonlat=booleans(), ndim=integers(0, 4))
@settings(max_examples=500, derandomize=True, deadline=None)
def test_vec2ang(vectors, lonlat, ndim):
vectors = np.broadcast_to(vectors, (2,) * ndim + (3,))
lon1, lat1 = hp_compat.vec2ang(vectors, lonlat=lonlat)
lon2, lat2 = hp.vec2ang(vectors, lonlat=lonlat)
if not lonlat:
lon1, lat1 = np.rad2deg(lat1), 90 - np.rad2deg(lon1)
lon2, lat2 = np.rad2deg(lat2), 90 - np.rad2deg(lon2)
sep = angular_separation(
lon1 * u.deg, lat1 * u.deg, lon2 * u.deg, lat2 * u.deg
).to_value(u.rad)
assert_allclose(sep, 0, atol=1e-8)
@given(lonlat=booleans(),
lon=floats(0, 360, allow_nan=False, allow_infinity=False).filter(
lambda lon: abs(lon) > 1e-10),
lat=floats(-90, 90, allow_nan=False, allow_infinity=False).filter(
lambda lat: abs(lat) < 89.99 and abs(lat) > 1e-10))
@settings(max_examples=2000, derandomize=True, deadline=None)
def test_ang2vec(lon, lat, lonlat):
if lonlat:
theta, phi = lon, lat
else:
theta, phi = np.pi / 2. - np.radians(lat), np.radians(lon)
xyz1 = hp_compat.ang2vec(theta, phi, lonlat=lonlat)
xyz2 = hp.ang2vec(theta, phi, lonlat=lonlat)
assert_allclose(xyz1, xyz2, atol=1e-10)
# The following fails, need to investigate:
# @example(nside_pow=29, lon=1.0000000028043134e-05, lat=1.000000000805912e-05,
# nest=False, lonlat=False)
#
@given(nside_pow=integers(0, 28), nest=booleans(), lonlat=booleans(),
lon=floats(0, 360, allow_nan=False, allow_infinity=False).filter(
lambda lon: abs(lon) > 1e-5),
lat=floats(-90, 90, allow_nan=False, allow_infinity=False).filter(
lambda lat: abs(lat) < 89.99 and abs(lat) > 1e-5))
@settings(max_examples=500, derandomize=True, deadline=None)
@example(nside_pow=27, lon=1.0000000028043134e-05, lat=-41.81031451395941, nest=False, lonlat=False)
@example(nside_pow=6, lon=1.6345238095238293, lat=69.42254649458224, nest=False, lonlat=False)
@example(nside_pow=15, lon=1.0000000028043134e-05, lat=1.000000000805912e-05,
nest=False, lonlat=False)
@example(nside_pow=0, lon=315.0000117809725, lat=1.000000000805912e-05, nest=False, lonlat=False)
@example(nside_pow=0, lon=1.0000000028043134e-05, lat=-41.81031489577861, nest=False, lonlat=False)
@example(nside_pow=0, lon=35.559942143736414, lat=-41.8103252622604, nest=False, lonlat=False)
@example(nside_pow=28, lon=359.9999922886491, lat=-41.81031470486902, nest=False, lonlat=False)
@example(nside_pow=0, lon=1.0000000028043134e-05, lat=-41.81031489577861, nest=False, lonlat=False)
@example(nside_pow=27, lon=1.0000000028043134e-05, lat=-41.81031451395941, nest=False, lonlat=False)
@example(nside_pow=26, lon=359.9999986588955, lat=41.81031489577861, nest=False, lonlat=False)
@example(nside_pow=27, lon=359.999997317791, lat=-41.81031451395943, nest=False, lonlat=False)
@example(nside_pow=27, lon=1.0000000028043134e-05, lat=89.80224636153702, nest=False, lonlat=False)
def test_interp_weights(nside_pow, lon, lat, nest, lonlat):
nside = 2 ** nside_pow
if lonlat:
theta, phi = lon, lat
else:
theta, phi = np.pi / 2. - np.radians(lat), np.radians(lon)
indices1, weights1 = hp_compat.get_interp_weights(nside, theta, phi, nest=nest, lonlat=lonlat)
indices2, weights2 = hp.get_interp_weights(nside, theta, phi, nest=nest, lonlat=lonlat)
# Ignore neighbours with weights < 1e-6 - we have to exclude these otherwise
# in some corner cases there will be different low-probability neighbours.
keep = weights1 > 1e-6
indices1, weights1 = indices1[keep], weights1[keep]
keep = weights2 > 1e-6
indices2, weights2 = indices2[keep], weights2[keep]
order1 = np.argsort(indices1)
order2 = np.argsort(indices2)
assert_equal(indices1[order1], indices2[order2])
assert_allclose(weights1[order1], weights2[order2], atol=1e-5)
# Make an array that can be useful up to the highest nside tested below
NSIDE_POW_MAX = 8
VALUES = np.random.random(12 * NSIDE_POW_MAX ** 2)
@given(nside_pow=integers(0, NSIDE_POW_MAX), nest=booleans(), lonlat=booleans(),
lon=floats(0, 360, allow_nan=False, allow_infinity=False).filter(
lambda lon: abs(lon) > 1e-5),
lat=floats(-90, 90, allow_nan=False, allow_infinity=False).filter(
lambda lat: abs(lat) < 89.99 and abs(lat) > 1e-5))
@settings(max_examples=500, derandomize=True, deadline=None)
def test_interp_val(nside_pow, lon, lat, nest, lonlat):
nside = 2 ** nside_pow
if lonlat:
theta, phi = lon, lat
else:
theta, phi = np.pi / 2. - np.radians(lat), np.radians(lon)
m = VALUES[:12 * nside ** 2]
value1 = hp_compat.get_interp_val(m, theta, phi, nest=nest, lonlat=lonlat)
value2 = hp.get_interp_val(m, theta, phi, nest=nest, lonlat=lonlat)
assert_allclose(value1, value2, rtol=0.1, atol=1.e-10)
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1739993667.0
astropy_healpix-1.1.2/astropy_healpix/tests/test_high_level.py 0000644 0001751 0000166 00000020353 14755431103 024433 0 ustar 00runner docker # Licensed under a 3-clause BSD style license - see LICENSE.rst
import pytest
import numpy as np
from numpy.testing import assert_allclose, assert_equal
from astropy import units as u
from astropy.coordinates import Longitude, Latitude, Galactic, SkyCoord
from ..high_level import HEALPix
class TestHEALPix:
def setup_class(self):
self.pix = HEALPix(nside=256, order='nested')
def test_pixel_area(self):
pixel_area = self.pix.pixel_area
assert_allclose(pixel_area.value, 1.5978966540475428e-05)
assert pixel_area.unit == u.sr
def test_pixel_resolution(self):
pixel_resolution = self.pix.pixel_resolution
assert_allclose(pixel_resolution.value, 13.741945647269624)
assert pixel_resolution.unit == u.arcmin
def test_level(self):
assert self.pix.level == 8
def test_npix(self):
assert self.pix.npix == 12 * 256 ** 2
# For the following tests, the numerical accuracy of this function is
# already tested in test_cython_api.py, so we focus here on functionality
# specific to the high-level functions.
def test_healpix_to_lonlat(self):
lon, lat = self.pix.healpix_to_lonlat([1, 2, 3])
assert isinstance(lon, Longitude)
assert isinstance(lat, Latitude)
index = self.pix.lonlat_to_healpix(lon, lat)
assert_equal(index, [1, 2, 3])
lon, lat = self.pix.healpix_to_lonlat([1, 2, 3],
dx=[0.1, 0.2, 0.3],
dy=[0.5, 0.4, 0.7])
assert isinstance(lon, Longitude)
assert isinstance(lat, Latitude)
index, dx, dy = self.pix.lonlat_to_healpix(lon, lat, return_offsets=True)
assert_equal(index, [1, 2, 3])
assert_allclose(dx, [0.1, 0.2, 0.3])
assert_allclose(dy, [0.5, 0.4, 0.7])
def test_healpix_to_xyz(self):
x, y, z = self.pix.healpix_to_xyz([1, 2, 3])
assert isinstance(x, np.ndarray)
assert isinstance(y, np.ndarray)
assert isinstance(z, np.ndarray)
index = self.pix.xyz_to_healpix(x, y, z)
assert_equal(index, [1, 2, 3])
x, y, z = self.pix.healpix_to_xyz([1, 2, 3],
dx=[0.1, 0.2, 0.3],
dy=[0.5, 0.4, 0.7])
assert isinstance(x, np.ndarray)
assert isinstance(y, np.ndarray)
assert isinstance(z, np.ndarray)
index, dx, dy = self.pix.xyz_to_healpix(x, y, z, return_offsets=True)
assert_equal(index, [1, 2, 3])
assert_allclose(dx, [0.1, 0.2, 0.3])
assert_allclose(dy, [0.5, 0.4, 0.7])
def test_nested_to_ring(self):
nested_index_1 = [1, 3, 22]
ring_index = self.pix.nested_to_ring(nested_index_1)
nested_index_2 = self.pix.ring_to_nested(ring_index)
assert_equal(nested_index_1, nested_index_2)
def test_bilinear_interpolation_weights(self):
indices, weights = self.pix.bilinear_interpolation_weights([1, 3, 4] * u.deg,
[3, 2, 6] * u.deg)
assert indices.shape == (4, 3)
assert weights.shape == (4, 3)
def test_interpolate_bilinear_lonlat(self):
values = np.ones(12 * 256 ** 2) * 3
result = self.pix.interpolate_bilinear_lonlat([1, 3, 4] * u.deg,
[3, 2, 6] * u.deg, values)
assert_allclose(result, [3, 3, 3])
def test_interpolate_bilinear_lonlat_invalid(self):
values = np.ones(222) * 3
with pytest.raises(ValueError) as exc:
self.pix.interpolate_bilinear_lonlat([1, 3, 4] * u.deg,
[3, 2, 6] * u.deg, values)
assert exc.value.args[0] == 'values must be an array of length 786432 (got 222)'
def test_cone_search_lonlat(self):
lon, lat = 1 * u.deg, 4 * u.deg
result = self.pix.cone_search_lonlat(lon, lat, 1 * u.deg)
assert len(result) == 77
def test_cone_search_lonlat_invalid(self):
lon, lat = [1, 2] * u.deg, [3, 4] * u.deg
with pytest.raises(ValueError) as exc:
self.pix.cone_search_lonlat(lon, lat, 1 * u.deg)
assert exc.value.args[0] == ('The longitude, latitude and radius must '
'be scalar Quantity objects')
def test_boundaries_lonlat(self):
lon, lat = self.pix.boundaries_lonlat([10, 20, 30], 4)
assert lon.shape == (3, 16)
assert lat.shape == (3, 16)
def test_neighbours(self):
neigh = self.pix.neighbours([10, 20, 30])
assert neigh.shape == (8, 3)
class TestCelestialHEALPix:
def setup_class(self):
self.pix = HEALPix(nside=256, order='nested', frame=Galactic())
def test_healpix_from_header(self):
"""Test instantiation from a FITS header.
Notes
-----
We don't need to test all possible options, because
:meth:`~astropy_healpix.HEALPix.from_header` is just a wrapper around
:meth:`~astropy_healpix.utils.parse_input_healpix_data`, which is
tested exhaustively in :mod:`~astropy_healpix.tests.test_utils`.
"""
pix = HEALPix.from_header(
(np.empty(self.pix.npix), 'G'),
nested=self.pix.order == 'nested')
assert pix.nside == self.pix.nside
assert type(pix.frame) == type(self.pix.frame) # noqa
assert pix.order == self.pix.order
def test_healpix_to_skycoord(self):
coord = self.pix.healpix_to_skycoord([1, 2, 3])
assert isinstance(coord, SkyCoord)
assert isinstance(coord.frame, Galactic)
# Make sure that the skycoord_to_healpix method converts coordinates
# to the frame of the HEALPix
coord = coord.transform_to('fk5')
index = self.pix.skycoord_to_healpix(coord)
assert_equal(index, [1, 2, 3])
coord = self.pix.healpix_to_skycoord([1, 2, 3],
dx=[0.1, 0.2, 0.3],
dy=[0.5, 0.4, 0.7])
assert isinstance(coord, SkyCoord)
assert isinstance(coord.frame, Galactic)
# Make sure that the skycoord_to_healpix method converts coordinates
# to the frame of the HEALPix
coord = coord.transform_to('fk5')
index, dx, dy = self.pix.skycoord_to_healpix(coord, return_offsets=True)
assert_equal(index, [1, 2, 3])
assert_allclose(dx, [0.1, 0.2, 0.3])
assert_allclose(dy, [0.5, 0.4, 0.7])
def test_interpolate_bilinear_skycoord(self):
values = np.ones(12 * 256 ** 2) * 3
coord = SkyCoord([1, 2, 3] * u.deg, [4, 3, 1] * u.deg, frame='fk4')
result = self.pix.interpolate_bilinear_skycoord(coord, values)
assert_allclose(result, [3, 3, 3])
# Make sure that coordinate system is correctly taken into account
values = np.arange(12 * 256 ** 2) * 3
coord = SkyCoord([1, 2, 3] * u.deg, [4, 3, 1] * u.deg, frame='fk4')
result1 = self.pix.interpolate_bilinear_skycoord(coord, values)
result2 = self.pix.interpolate_bilinear_skycoord(coord.icrs, values)
assert_allclose(result1, result2)
def test_cone_search_skycoord(self):
coord = SkyCoord(1 * u.deg, 4 * u.deg, frame='galactic')
result1 = self.pix.cone_search_skycoord(coord, 1 * u.deg)
assert len(result1) == 77
result2 = self.pix.cone_search_skycoord(coord.icrs, 1 * u.deg)
assert_allclose(result1, result2)
def test_boundaries_skycoord(self):
coord = self.pix.boundaries_skycoord([10, 20, 30], 4)
assert coord.shape == (3, 16)
class TestCelestialHEALPixFrameAsClass(TestCelestialHEALPix):
def setup_class(self):
self.pix = HEALPix(nside=256, order='nested', frame=Galactic)
class TestCelestialHEALPixFrameAsString(TestCelestialHEALPix):
def setup_class(self):
self.pix = HEALPix(nside=256, order='nested', frame='galactic')
def test_invalid_frame_name():
with pytest.raises(ValueError, match='Coordinate frame name "foobar"'):
HEALPix(nside=256, frame='foobar')
def test_invalid_frame_type():
with pytest.raises(ValueError, match='Coordinate frame must be a'):
HEALPix(nside=256, frame=('obviously', 'not', 'a', 'frame'))
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1739993667.0
astropy_healpix-1.1.2/astropy_healpix/tests/test_utils.py 0000644 0001751 0000166 00000003300 14755431103 023456 0 ustar 00runner docker import numpy as np
import pytest
from astropy.coordinates import FK5, Galactic
from astropy.io import fits
from ..utils import parse_coord_system, parse_input_healpix_data
def test_parse_coord_system():
frame = parse_coord_system(Galactic())
assert isinstance(frame, Galactic)
frame = parse_coord_system('fk5')
assert isinstance(frame, FK5)
with pytest.raises(ValueError) as exc:
frame = parse_coord_system('e')
assert exc.value.args[0] == "Ecliptic coordinate frame not yet supported"
frame = parse_coord_system('g')
assert isinstance(frame, Galactic)
with pytest.raises(ValueError) as exc:
frame = parse_coord_system('spam')
assert exc.value.args[0] == "Could not determine frame for system=spam"
def test_parse_input_healpix_data(tmpdir):
data = np.arange(3072)
col = fits.Column(array=data, name='flux', format="E")
hdu = fits.BinTableHDU.from_columns([col])
hdu.header['NSIDE'] = 512
hdu.header['COORDSYS'] = "G"
# As HDU
array, coordinate_system, nested = parse_input_healpix_data(hdu)
np.testing.assert_allclose(array, data)
# As filename
filename = tmpdir.join('test.fits').strpath
hdu.writeto(filename)
array, coordinate_system, nested = parse_input_healpix_data(filename)
np.testing.assert_allclose(array, data)
# As array
array, coordinate_system, nested = parse_input_healpix_data((data, "galactic"))
np.testing.assert_allclose(array, data)
# Invalid
with pytest.raises(TypeError) as exc:
parse_input_healpix_data(data)
assert exc.value.args[0] == ("input_data should either be an HDU object or "
"a tuple of (array, frame)")
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1739993667.0
astropy_healpix-1.1.2/astropy_healpix/utils.py 0000644 0001751 0000166 00000003453 14755431103 021266 0 ustar 00runner docker import numpy as np
from astropy.io import fits
from astropy.io.fits import TableHDU, BinTableHDU
from astropy.coordinates import BaseCoordinateFrame, frame_transform_graph, Galactic, ICRS
FRAMES = {
'g': Galactic(),
'c': ICRS()
}
def parse_coord_system(system):
if isinstance(system, BaseCoordinateFrame):
return system
elif isinstance(system, str):
system = system.lower()
if system == 'e':
raise ValueError("Ecliptic coordinate frame not yet supported")
elif system in FRAMES:
return FRAMES[system]
else:
system_new = frame_transform_graph.lookup_name(system)
if system_new is None:
raise ValueError(f"Could not determine frame for system={system}")
else:
return system_new()
def parse_input_healpix_data(input_data, field=0, hdu_in=None, nested=None):
"""
Parse input HEALPIX data to return a Numpy array and coordinate frame object.
"""
if isinstance(input_data, (TableHDU, BinTableHDU)):
data = input_data.data
header = input_data.header
coordinate_system_in = parse_coord_system(header['COORDSYS'])
array_in = data[data.columns[field].name].ravel()
if 'ORDERING' in header:
nested = header['ORDERING'].lower() == 'nested'
elif isinstance(input_data, str):
hdu = fits.open(input_data)[hdu_in or 1]
return parse_input_healpix_data(hdu, field=field)
elif isinstance(input_data, tuple) and isinstance(input_data[0], np.ndarray):
array_in = input_data[0]
coordinate_system_in = parse_coord_system(input_data[1])
else:
raise TypeError("input_data should either be an HDU object or a tuple of (array, frame)")
return array_in, coordinate_system_in, nested
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1739993677.0
astropy_healpix-1.1.2/astropy_healpix/version.py 0000644 0001751 0000166 00000000521 14755431115 021607 0 ustar 00runner docker # Note that we need to fall back to the hard-coded version if either
# setuptools_scm can't be imported or setuptools_scm can't determine the
# version, so we catch the generic 'Exception'.
try:
from setuptools_scm import get_version
version = get_version(root='..', relative_to=__file__)
except Exception:
version = '1.1.2'
././@PaxHeader 0000000 0000000 0000000 00000000034 00000000000 010212 x ustar 00 28 mtime=1739993677.3921268
astropy_healpix-1.1.2/astropy_healpix.egg-info/ 0000755 0001751 0000166 00000000000 14755431115 021244 5 ustar 00runner docker ././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1739993677.0
astropy_healpix-1.1.2/astropy_healpix.egg-info/PKG-INFO 0000644 0001751 0000166 00000010041 14755431115 022335 0 ustar 00runner docker Metadata-Version: 2.2
Name: astropy_healpix
Version: 1.1.2
Summary: BSD-licensed HEALPix for Astropy
Home-page: https://github.com/astropy/astropy-healpix
Author: Astropy Developers
Author-email: astropy.team@gmail.com
License: BSD 3-Clause
Keywords: astronomy,astrophysics,astropy,healpix,coordinates
Classifier: Intended Audience :: Science/Research
Classifier: License :: OSI Approved :: BSD License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: C
Classifier: Programming Language :: Cython
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: Implementation :: CPython
Classifier: Topic :: Scientific/Engineering :: Astronomy
Classifier: Topic :: Scientific/Engineering :: Physics
Requires-Python: >=3.10
Description-Content-Type: text/x-rst
License-File: LICENSE.md
Requires-Dist: numpy>=1.25
Requires-Dist: astropy>=5
Provides-Extra: test
Requires-Dist: pytest-astropy; extra == "test"
Requires-Dist: hypothesis; extra == "test"
Provides-Extra: docs
Requires-Dist: sphinx-astropy; extra == "docs"
Requires-Dist: matplotlib; extra == "docs"
astropy healpix
---------------
.. image:: https://img.shields.io/badge/powered%20by-AstroPy-orange.svg?style=flat
:target: https://www.astropy.org
:alt: Powered by Astropy Badge
.. image:: https://github.com/astropy/astropy-healpix/workflows/CI/badge.svg
:target: https://github.com/astropy/astropy-healpix/actions
:alt: GitHub Actions CI Status
.. image:: https://codecov.io/gh/astropy/astropy-healpix/branch/main/graph/badge.svg
:target: https://codecov.io/gh/astropy/astropy-healpix
:alt: Coverage Status
.. image:: https://readthedocs.org/projects/astropy-healpix/badge/?version=latest
:target: http://astropy-healpix.readthedocs.io/en/latest/?badge=latest
:alt: Doc
This is a BSD-licensed HEALPix package developed by the Astropy project
and based on C code written by Dustin Lang in `astrometry.net `__. See the
`Documentation `__ for
information about installing and using this package.
License
-------
This project is Copyright (c) Astropy Developers and licensed under
the terms of the BSD 3-Clause license. This package is based upon
the `Astropy package template `_
which is licensed under the BSD 3-clause license. See the licenses folder for
more information.
Contributing
------------
We love contributions! astropy-healpix is open source,
built on open source, and we'd love to have you hang out in our community.
**Imposter syndrome disclaimer**: We want your help. No, really.
There may be a little voice inside your head that is telling you that you're not
ready to be an open source contributor; that your skills aren't nearly good
enough to contribute. What could you possibly offer a project like this one?
We assure you - the little voice in your head is wrong. If you can write code at
all, you can contribute code to open source. Contributing to open source
projects is a fantastic way to advance one's coding skills. Writing perfect code
isn't the measure of a good developer (that would disqualify all of us!); it's
trying to create something, making mistakes, and learning from those
mistakes. That's how we all improve, and we are happy to help others learn.
Being an open source contributor doesn't just mean writing code, either. You can
help out by writing documentation, tests, or even giving feedback about the
project (and yes - that includes giving feedback about the contribution
process). Some of these contributions may be the most valuable to the project as
a whole, because you're coming to the project with fresh eyes, so you can see
the errors and assumptions that seasoned contributors have glossed over.
Note: This disclaimer was originally written by
`Adrienne Lowe `_ for a
`PyCon talk `_, and was adapted by
astropy-healpix based on its use in the README file for the
`MetPy project `_.
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1739993677.0
astropy_healpix-1.1.2/astropy_healpix.egg-info/SOURCES.txt 0000644 0001751 0000166 00000005106 14755431115 023132 0 ustar 00runner docker .gitignore
.readthedocs.yml
CHANGES.rst
LICENSE.md
MANIFEST.in
README.rst
RELEASING.rst
environment-dev.yml
pyproject.toml
setup.cfg
setup.py
tox.ini
.github/dependabot.yml
.github/workflows/ci_cron_weekly.yml
.github/workflows/ci_tests.yml
.github/workflows/publish.yml
astropy_healpix/__init__.py
astropy_healpix/_compiler.c
astropy_healpix/_core.c
astropy_healpix/bench.py
astropy_healpix/conftest.py
astropy_healpix/core.py
astropy_healpix/healpy.py
astropy_healpix/high_level.py
astropy_healpix/interpolation.c
astropy_healpix/interpolation.h
astropy_healpix/setup_package.py
astropy_healpix/utils.py
astropy_healpix/version.py
astropy_healpix.egg-info/PKG-INFO
astropy_healpix.egg-info/SOURCES.txt
astropy_healpix.egg-info/dependency_links.txt
astropy_healpix.egg-info/not-zip-safe
astropy_healpix.egg-info/requires.txt
astropy_healpix.egg-info/top_level.txt
astropy_healpix/tests/__init__.py
astropy_healpix/tests/test_bench.py
astropy_healpix/tests/test_core.py
astropy_healpix/tests/test_healpy.py
astropy_healpix/tests/test_high_level.py
astropy_healpix/tests/test_utils.py
cextern/.gitignore
cextern/README.md
cextern/astrometry.net/CuTest.c
cextern/astrometry.net/CuTest.h
cextern/astrometry.net/LICENSE
cextern/astrometry.net/Makefile
cextern/astrometry.net/an-bool.h
cextern/astrometry.net/bl-nl.c
cextern/astrometry.net/bl-nl.h
cextern/astrometry.net/bl-nl.inc
cextern/astrometry.net/bl-nl.ph
cextern/astrometry.net/bl.c
cextern/astrometry.net/bl.h
cextern/astrometry.net/bl.inc
cextern/astrometry.net/bl.ph
cextern/astrometry.net/example.c
cextern/astrometry.net/healpix-utils.c
cextern/astrometry.net/healpix-utils.h
cextern/astrometry.net/healpix.c
cextern/astrometry.net/healpix.h
cextern/astrometry.net/keywords.h
cextern/astrometry.net/mathutil.c
cextern/astrometry.net/mathutil.h
cextern/astrometry.net/mathutil.inc
cextern/astrometry.net/os-features.h
cextern/astrometry.net/permutedsort.c
cextern/astrometry.net/permutedsort.h
cextern/astrometry.net/qsort_reentrant.c
cextern/astrometry.net/starutil.c
cextern/astrometry.net/starutil.h
cextern/astrometry.net/starutil.inc
cextern/astrometry.net/stdint_msc.h
cextern/astrometry.net/test_healpix-main.c
cextern/astrometry.net/test_healpix.c
cextern/numpy/ieee754.h
docs/Makefile
docs/about.rst
docs/api.rst
docs/boundaries.rst
docs/cone_search.rst
docs/conf.py
docs/coordinates.rst
docs/getting_started.rst
docs/healpy_compat.rst
docs/index.rst
docs/installation.rst
docs/interpolation.rst
docs/make.bat
docs/performance.rst
docs/references.txt
docs/_templates/autosummary/base.rst
docs/_templates/autosummary/class.rst
docs/_templates/autosummary/module.rst ././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1739993677.0
astropy_healpix-1.1.2/astropy_healpix.egg-info/dependency_links.txt 0000644 0001751 0000166 00000000001 14755431115 025312 0 ustar 00runner docker
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1739993677.0
astropy_healpix-1.1.2/astropy_healpix.egg-info/not-zip-safe 0000644 0001751 0000166 00000000001 14755431115 023472 0 ustar 00runner docker
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1739993677.0
astropy_healpix-1.1.2/astropy_healpix.egg-info/requires.txt 0000644 0001751 0000166 00000000133 14755431115 023641 0 ustar 00runner docker numpy>=1.25
astropy>=5
[docs]
sphinx-astropy
matplotlib
[test]
pytest-astropy
hypothesis
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1739993677.0
astropy_healpix-1.1.2/astropy_healpix.egg-info/top_level.txt 0000644 0001751 0000166 00000000020 14755431115 023766 0 ustar 00runner docker astropy_healpix
././@PaxHeader 0000000 0000000 0000000 00000000033 00000000000 010211 x ustar 00 27 mtime=1739993677.384127
astropy_healpix-1.1.2/cextern/ 0000755 0001751 0000166 00000000000 14755431115 016007 5 ustar 00runner docker ././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1739993667.0
astropy_healpix-1.1.2/cextern/.gitignore 0000644 0001751 0000166 00000000063 14755431103 017773 0 ustar 00runner docker astrometry.net/test_healpix
astrometry.net/example
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1739993667.0
astropy_healpix-1.1.2/cextern/README.md 0000644 0001751 0000166 00000001105 14755431103 017260 0 ustar 00runner docker # astropy-healpix/cextern/
The `astropy-healpix` Python package is a wrapper around a C library.
See http://astropy-healpix.readthedocs.io/en/latest/about.html
This README gives some technical details on the C code here.
- The main file is `healpix.h` and `healpix.c`, start reading there first.
- For the Python `astropy-healpix` packge, the C code is built via `setup.py`
- However, to help work on the C code and test it directly, a `Makefile`
is included here.
- For testing, a copy of `CuTest.h` and `CuTest.c` from here is bundled:
https://github.com/asimjalis/cutest
././@PaxHeader 0000000 0000000 0000000 00000000034 00000000000 010212 x ustar 00 28 mtime=1739993677.3891268
astropy_healpix-1.1.2/cextern/astrometry.net/ 0000755 0001751 0000166 00000000000 14755431115 021005 5 ustar 00runner docker ././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1739993667.0
astropy_healpix-1.1.2/cextern/astrometry.net/CuTest.c 0000644 0001751 0000166 00000017736 14755431103 022373 0 ustar 00runner docker #include
#include
#include
#include
#include
#include
#include "CuTest.h"
/*-------------------------------------------------------------------------*
* CuStr
*-------------------------------------------------------------------------*/
char* CuStrAlloc(int size)
{
char* newStr = (char*) malloc( sizeof(char) * (size) );
return newStr;
}
char* CuStrCopy(const char* old)
{
int len = strlen(old);
char* newStr = CuStrAlloc(len + 1);
strcpy(newStr, old);
return newStr;
}
/*-------------------------------------------------------------------------*
* CuString
*-------------------------------------------------------------------------*/
void CuStringInit(CuString* str)
{
str->length = 0;
str->size = STRING_MAX;
str->buffer = (char*) malloc(sizeof(char) * str->size);
str->buffer[0] = '\0';
}
CuString* CuStringNew(void)
{
CuString* str = (CuString*) malloc(sizeof(CuString));
str->length = 0;
str->size = STRING_MAX;
str->buffer = (char*) malloc(sizeof(char) * str->size);
str->buffer[0] = '\0';
return str;
}
void CuStringDelete(CuString *str)
{
if (!str) return;
free(str->buffer);
free(str);
}
void CuStringResize(CuString* str, int newSize)
{
str->buffer = (char*) realloc(str->buffer, sizeof(char) * newSize);
str->size = newSize;
}
void CuStringAppend(CuString* str, const char* text)
{
int length;
if (text == NULL) {
text = "NULL";
}
length = strlen(text);
if (str->length + length + 1 >= str->size)
CuStringResize(str, str->length + length + 1 + STRING_INC);
str->length += length;
strcat(str->buffer, text);
}
void CuStringAppendChar(CuString* str, char ch)
{
char text[2];
text[0] = ch;
text[1] = '\0';
CuStringAppend(str, text);
}
void CuStringAppendFormat(CuString* str, const char* format, ...)
{
va_list argp;
char buf[HUGE_STRING_LEN];
va_start(argp, format);
vsprintf(buf, format, argp);
va_end(argp);
CuStringAppend(str, buf);
}
void CuStringInsert(CuString* str, const char* text, int pos)
{
int length = strlen(text);
if (pos > str->length)
pos = str->length;
if (str->length + length + 1 >= str->size)
CuStringResize(str, str->length + length + 1 + STRING_INC);
memmove(str->buffer + pos + length, str->buffer + pos, (str->length - pos) + 1);
str->length += length;
memcpy(str->buffer + pos, text, length);
}
/*-------------------------------------------------------------------------*
* CuTest
*-------------------------------------------------------------------------*/
void CuTestInit(CuTest* t, const char* name, TestFunction function)
{
t->name = CuStrCopy(name);
t->failed = 0;
t->ran = 0;
t->message = NULL;
t->function = function;
t->jumpBuf = NULL;
}
CuTest* CuTestNew(const char* name, TestFunction function)
{
CuTest* tc = CU_ALLOC(CuTest);
CuTestInit(tc, name, function);
return tc;
}
void CuTestDelete(CuTest *t)
{
if (!t) return;
free(t->name);
free(t);
}
void CuTestRun(CuTest* tc)
{
jmp_buf buf;
tc->jumpBuf = &buf;
if (setjmp(buf) == 0)
{
tc->ran = 1;
(tc->function)(tc);
}
tc->jumpBuf = 0;
}
static void CuFailInternal(CuTest* tc, const char* file, int line, CuString* string)
{
char buf[HUGE_STRING_LEN];
sprintf(buf, "%s:%d: ", file, line);
CuStringInsert(string, buf, 0);
tc->failed = 1;
tc->message = string->buffer;
if (tc->jumpBuf != 0) longjmp(*(tc->jumpBuf), 0);
}
void CuFail_Line(CuTest* tc, const char* file, int line, const char* message2, const char* message)
{
CuString string;
CuStringInit(&string);
if (message2 != NULL)
{
CuStringAppend(&string, message2);
CuStringAppend(&string, ": ");
}
CuStringAppend(&string, message);
CuFailInternal(tc, file, line, &string);
}
void CuAssert_Line(CuTest* tc, const char* file, int line, const char* message, int condition)
{
if (condition) return;
CuFail_Line(tc, file, line, NULL, message);
}
void CuAssertStrEquals_LineMsg(CuTest* tc, const char* file, int line, const char* message,
const char* expected, const char* actual)
{
CuString string;
if ((expected == NULL && actual == NULL) ||
(expected != NULL && actual != NULL &&
strcmp(expected, actual) == 0))
{
return;
}
CuStringInit(&string);
if (message != NULL)
{
CuStringAppend(&string, message);
CuStringAppend(&string, ": ");
}
CuStringAppend(&string, "expected <");
CuStringAppend(&string, expected);
CuStringAppend(&string, "> but was <");
CuStringAppend(&string, actual);
CuStringAppend(&string, ">");
CuFailInternal(tc, file, line, &string);
}
void CuAssertIntEquals_LineMsg(CuTest* tc, const char* file, int line, const char* message,
int expected, int actual)
{
char buf[STRING_MAX];
if (expected == actual) return;
sprintf(buf, "expected <%d> but was <%d>", expected, actual);
CuFail_Line(tc, file, line, message, buf);
}
void CuAssertDblEquals_LineMsg(CuTest* tc, const char* file, int line, const char* message,
double expected, double actual, double delta)
{
char buf[STRING_MAX];
if (fabs(expected - actual) <= delta) return;
/* sprintf(buf, "expected <%lf> but was <%lf>", expected, actual); */
sprintf(buf, "expected <%f> but was <%f>", expected, actual);
CuFail_Line(tc, file, line, message, buf);
}
void CuAssertPtrEquals_LineMsg(CuTest* tc, const char* file, int line, const char* message,
void* expected, void* actual)
{
char buf[STRING_MAX];
if (expected == actual) return;
sprintf(buf, "expected pointer <0x%p> but was <0x%p>", expected, actual);
CuFail_Line(tc, file, line, message, buf);
}
/*-------------------------------------------------------------------------*
* CuSuite
*-------------------------------------------------------------------------*/
void CuSuiteInit(CuSuite* testSuite)
{
testSuite->count = 0;
testSuite->failCount = 0;
memset(testSuite->list, 0, sizeof(testSuite->list));
}
CuSuite* CuSuiteNew(void)
{
CuSuite* testSuite = CU_ALLOC(CuSuite);
CuSuiteInit(testSuite);
return testSuite;
}
void CuSuiteDelete(CuSuite *testSuite)
{
unsigned int n;
for (n=0; n < MAX_TEST_CASES; n++)
{
if (testSuite->list[n])
{
CuTestDelete(testSuite->list[n]);
}
}
free(testSuite);
}
void CuSuiteAdd(CuSuite* testSuite, CuTest *testCase)
{
assert(testSuite->count < MAX_TEST_CASES);
testSuite->list[testSuite->count] = testCase;
testSuite->count++;
}
void CuSuiteAddSuite(CuSuite* testSuite, CuSuite* testSuite2)
{
int i;
for (i = 0 ; i < testSuite2->count ; ++i)
{
CuTest* testCase = testSuite2->list[i];
CuSuiteAdd(testSuite, testCase);
}
}
void CuSuiteRun(CuSuite* testSuite)
{
int i;
for (i = 0 ; i < testSuite->count ; ++i)
{
CuTest* testCase = testSuite->list[i];
CuTestRun(testCase);
if (testCase->failed) { testSuite->failCount += 1; }
}
}
void CuSuiteSummary(CuSuite* testSuite, CuString* summary)
{
int i;
for (i = 0 ; i < testSuite->count ; ++i)
{
CuTest* testCase = testSuite->list[i];
CuStringAppend(summary, testCase->failed ? "F" : ".");
}
CuStringAppend(summary, "\n\n");
}
void CuSuiteDetails(CuSuite* testSuite, CuString* details)
{
int i;
int failCount = 0;
if (testSuite->failCount == 0)
{
int passCount = testSuite->count - testSuite->failCount;
const char* testWord = passCount == 1 ? "test" : "tests";
CuStringAppendFormat(details, "OK (%d %s)\n", passCount, testWord);
}
else
{
if (testSuite->failCount == 1)
CuStringAppend(details, "There was 1 failure:\n");
else
CuStringAppendFormat(details, "There were %d failures:\n", testSuite->failCount);
for (i = 0 ; i < testSuite->count ; ++i)
{
CuTest* testCase = testSuite->list[i];
if (testCase->failed)
{
failCount++;
CuStringAppendFormat(details, "%d) %s: %s\n",
failCount, testCase->name, testCase->message);
}
}
CuStringAppend(details, "\n!!!FAILURES!!!\n");
CuStringAppendFormat(details, "Runs: %d ", testSuite->count);
CuStringAppendFormat(details, "Passes: %d ", testSuite->count - testSuite->failCount);
CuStringAppendFormat(details, "Fails: %d\n", testSuite->failCount);
}
}
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1739993667.0
astropy_healpix-1.1.2/cextern/astrometry.net/CuTest.h 0000644 0001751 0000166 00000007733 14755431103 022374 0 ustar 00runner docker #ifndef CU_TEST_H
#define CU_TEST_H
#include
#include
/* CuString */
char* CuStrAlloc(int size);
char* CuStrCopy(const char* old);
#define CU_ALLOC(TYPE) ((TYPE*) malloc(sizeof(TYPE)))
#define HUGE_STRING_LEN 8192
#define STRING_MAX 256
#define STRING_INC 256
typedef struct
{
int length;
int size;
char* buffer;
} CuString;
void CuStringInit(CuString* str);
CuString* CuStringNew(void);
void CuStringRead(CuString* str, const char* path);
void CuStringAppend(CuString* str, const char* text);
void CuStringAppendChar(CuString* str, char ch);
void CuStringAppendFormat(CuString* str, const char* format, ...);
void CuStringInsert(CuString* str, const char* text, int pos);
void CuStringResize(CuString* str, int newSize);
void CuStringDelete(CuString* str);
/* CuTest */
typedef struct CuTest CuTest;
typedef void (*TestFunction)(CuTest *);
struct CuTest
{
char* name;
TestFunction function;
int failed;
int ran;
const char* message;
jmp_buf *jumpBuf;
};
void CuTestInit(CuTest* t, const char* name, TestFunction function);
CuTest* CuTestNew(const char* name, TestFunction function);
void CuTestRun(CuTest* tc);
void CuTestDelete(CuTest *t);
/* Internal versions of assert functions -- use the public versions */
void CuFail_Line(CuTest* tc, const char* file, int line, const char* message2, const char* message);
void CuAssert_Line(CuTest* tc, const char* file, int line, const char* message, int condition);
void CuAssertStrEquals_LineMsg(CuTest* tc,
const char* file, int line, const char* message,
const char* expected, const char* actual);
void CuAssertIntEquals_LineMsg(CuTest* tc,
const char* file, int line, const char* message,
int expected, int actual);
void CuAssertDblEquals_LineMsg(CuTest* tc,
const char* file, int line, const char* message,
double expected, double actual, double delta);
void CuAssertPtrEquals_LineMsg(CuTest* tc,
const char* file, int line, const char* message,
void* expected, void* actual);
/* public assert functions */
#define CuFail(tc, ms) CuFail_Line( (tc), __FILE__, __LINE__, NULL, (ms))
#define CuAssert(tc, ms, cond) CuAssert_Line((tc), __FILE__, __LINE__, (ms), (cond))
#define CuAssertTrue(tc, cond) CuAssert_Line((tc), __FILE__, __LINE__, "assert failed", (cond))
#define CuAssertStrEquals(tc,ex,ac) CuAssertStrEquals_LineMsg((tc),__FILE__,__LINE__,NULL,(ex),(ac))
#define CuAssertStrEquals_Msg(tc,ms,ex,ac) CuAssertStrEquals_LineMsg((tc),__FILE__,__LINE__,(ms),(ex),(ac))
#define CuAssertIntEquals(tc,ex,ac) CuAssertIntEquals_LineMsg((tc),__FILE__,__LINE__,NULL,(ex),(ac))
#define CuAssertIntEquals_Msg(tc,ms,ex,ac) CuAssertIntEquals_LineMsg((tc),__FILE__,__LINE__,(ms),(ex),(ac))
#define CuAssertDblEquals(tc,ex,ac,dl) CuAssertDblEquals_LineMsg((tc),__FILE__,__LINE__,NULL,(ex),(ac),(dl))
#define CuAssertDblEquals_Msg(tc,ms,ex,ac,dl) CuAssertDblEquals_LineMsg((tc),__FILE__,__LINE__,(ms),(ex),(ac),(dl))
#define CuAssertPtrEquals(tc,ex,ac) CuAssertPtrEquals_LineMsg((tc),__FILE__,__LINE__,NULL,(ex),(ac))
#define CuAssertPtrEquals_Msg(tc,ms,ex,ac) CuAssertPtrEquals_LineMsg((tc),__FILE__,__LINE__,(ms),(ex),(ac))
#define CuAssertPtrNotNull(tc,p) CuAssert_Line((tc),__FILE__,__LINE__,"null pointer unexpected",(p != NULL))
#define CuAssertPtrNotNullMsg(tc,msg,p) CuAssert_Line((tc),__FILE__,__LINE__,(msg),(p != NULL))
/* CuSuite */
#define MAX_TEST_CASES 1024
#define SUITE_ADD_TEST(SUITE,TEST) CuSuiteAdd(SUITE, CuTestNew(#TEST, TEST))
typedef struct
{
int count;
CuTest* list[MAX_TEST_CASES];
int failCount;
} CuSuite;
void CuSuiteInit(CuSuite* testSuite);
CuSuite* CuSuiteNew(void);
void CuSuiteDelete(CuSuite *testSuite);
void CuSuiteAdd(CuSuite* testSuite, CuTest *testCase);
void CuSuiteAddSuite(CuSuite* testSuite, CuSuite* testSuite2);
void CuSuiteRun(CuSuite* testSuite);
void CuSuiteSummary(CuSuite* testSuite, CuString* summary);
void CuSuiteDetails(CuSuite* testSuite, CuString* details);
#endif /* CU_TEST_H */
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1739993667.0
astropy_healpix-1.1.2/cextern/astrometry.net/LICENSE 0000644 0001751 0000166 00000002751 14755431103 022014 0 ustar 00runner docker
Copyright (c) 2006-2015, Astrometry.net Developers
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the Astrometry.net Team nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1739993667.0
astropy_healpix-1.1.2/cextern/astrometry.net/Makefile 0000644 0001751 0000166 00000000503 14755431103 022440 0 ustar 00runner docker
all: test_healpix
OBJS = healpix-utils.o healpix.o starutil.o permutedsort.o mathutil.o bl.o qsort_reentrant.o
HEADERS = healpix-utils.h healpix.h
$(OBJS): %.o: %.c $(HEADERS)
$(CC) -o $@ -c $<
%.o: %.c
$(CC) -o $@ -c $<
test_healpix: test_healpix-main.c test_healpix.c $(OBJS) CuTest.o
example: example.c $(OBJS)
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1739993667.0
astropy_healpix-1.1.2/cextern/astrometry.net/an-bool.h 0000644 0001751 0000166 00000000707 14755431103 022506 0 ustar 00runner docker /*
# This file is part of the Astrometry.net suite.
# Licensed under a 3-clause BSD style license - see LICENSE
*/
#ifndef AN_BOOL_H
#define AN_BOOL_H
#ifdef _MSC_VER
#if _MSC_VER >= 1600
#include
#else
#include
#endif
#else
#include
#endif
#ifndef TRUE
#define TRUE 1
#endif
#ifndef FALSE
#define FALSE 0
#endif
// This helps unconfuse SWIG; it doesn't seem to like uint8_t
typedef unsigned char anbool;
#endif
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1739993667.0
astropy_healpix-1.1.2/cextern/astrometry.net/bl-nl.c 0000644 0001751 0000166 00000025007 14755431103 022156 0 ustar 00runner docker /*
# This file is part of the Astrometry.net suite.
# Licensed under a 3-clause BSD style license - see LICENSE
*/
/**
Defined:
--nl
--number
--NL_PRINT(x) prints number 'x'
Note:
--You can't declare multiple "number" variables like this:
number n1, n2;
Instead, do:
number n1;
number n2;
This is because "number" may be a pointer type.
*/
#include "bl-nl.ph"
#define NODE_NUMDATA(node) ((number*)NODE_DATA(node))
number* NLF(to_array)(nl* list) {
number* arr;
size_t N;
if (!list)
return NULL;
N = NLF(size)(list);
arr = malloc(N * sizeof(number));
bl_copy(list, 0, N, arr);
return arr;
}
#define InlineDefine InlineDefineC
#include "bl-nl.inc"
#undef InlineDefine
static int NLF(compare_ascending)(const void* v1, const void* v2) {
number i1 = *(number*)v1;
number i2 = *(number*)v2;
if (i1 > i2) return 1;
else if (i1 < i2) return -1;
else return 0;
}
static int NLF(compare_descending)(const void* v1, const void* v2) {
number i1 = *(number*)v1;
number i2 = *(number*)v2;
if (i1 > i2) return -1;
else if (i1 < i2) return 1;
else return 0;
}
void NLF(reverse)(nl* list) {
bl_reverse(list);
}
void NLF(append_array)(nl* list, const number* data, size_t ndata) {
size_t i;
for (i=0; iblocksize);
N1 = NLF(size)(list1);
N2 = NLF(size)(list2);
i1 = i2 = 0;
getv1 = getv2 = 1;
while (i1 < N1 && i2 < N2) {
if (getv1) {
v1 = NLF(get)(list1, i1);
getv1 = 0;
}
if (getv2) {
getv2 = 0;
v2 = NLF(get)(list2, i2);
}
if (v1 <= v2) {
NLF(append)(res, v1);
i1++;
getv1 = 1;
} else {
NLF(append)(res, v2);
i2++;
getv2 = 1;
}
}
for (; i1N-1);
bl_remove_index(nlist, nlist->N-1);
return ret;
}
nl* NLF(dupe)(nl* nlist) {
nl* ret = NLF(new)(nlist->blocksize);
size_t i;
for (i=0; iN; i++)
NLF(push)(ret, NLF(get)(nlist, i));
return ret;
}
ptrdiff_t NLF(remove_value)(nl* nlist, const number value) {
bl* list = nlist;
bl_node *node, *prev;
size_t istart = 0;
for (node=list->head, prev=NULL;
node;
prev=node, node=node->next) {
int i;
number* idat;
idat = NODE_DATA(node);
for (i=0; iN; i++)
if (idat[i] == value) {
bl_remove_from_node(list, node, prev, i);
list->last_access = prev;
list->last_access_n = istart;
return istart + i;
}
istart += node->N;
}
return BL_NOT_FOUND;
}
void NLF(remove_all)(nl* list) {
bl_remove_all(list);
}
void NLF(remove_index_range)(nl* list, size_t start, size_t length) {
bl_remove_index_range(list, start, length);
}
void NLF(set)(nl* list, size_t index, const number value) {
bl_set(list, index, &value);
}
/*
void dl_set(dl* list, int index, double value) {
int i;
int nadd = (index+1) - list->N;
if (nadd > 0) {
// enlarge the list to hold 'nadd' more entries.
for (i=0; iN;
ptrdiff_t mid;
while (lower < (upper-1)) {
mid = (upper + lower) / 2;
if (n >= iarray[mid])
lower = mid;
else
upper = mid;
}
return lower;
}
// find the first node for which n <= the last element.
static bl_node* NLF(findnodecontainingsorted)(const nl* list, const number n,
size_t* p_nskipped) {
bl_node *node;
size_t nskipped;
//bl_node *prev;
//int prevnskipped;
// check if we can use the jump accessor or if we have to start at
// the beginning...
if (list->last_access && list->last_access->N &&
// is the value we're looking for >= the first element?
(n >= *NODE_NUMDATA(list->last_access))) {
node = list->last_access;
nskipped = list->last_access_n;
} else {
node = list->head;
nskipped = 0;
}
/*
// find the first node for which n < the first element. The
// previous node will contain the value (if it exists).
for (prev=node, prevnskipped=nskipped;
node && (n < *NODE_NUMDATA(node));) {
prev=node;
prevnskipped=nskipped;
nskipped+=node->N;
node=node->next;
}
if (prev && n <= NODE_NUMDATA(prev)[prev->N-1]) {
if (p_nskipped)
*p_nskipped = prevnskipped;
return prev;
}
if (node && n <= NODE_NUMDATA(node)[node->N-1]) {
if (p_nskipped)
*p_nskipped = nskipped;
return node;
}
return NULL;
*/
/*
if (!node && prev && n > NODE_NUMDATA(prev)[prev->N-1])
return NULL;
if (p_nskipped)
*p_nskipped = prevnskipped;
return prev;
*/
for (; node && (n > NODE_NUMDATA(node)[node->N-1]); node=node->next)
nskipped += node->N;
if (p_nskipped)
*p_nskipped = nskipped;
return node;
}
static ptrdiff_t NLF(insertascending)(nl* list, const number n, int unique) {
bl_node *node;
size_t ind;
size_t nskipped;
node = NLF(findnodecontainingsorted)(list, n, &nskipped);
if (!node) {
NLF(append)(list, n);
return list->N-1;
}
/*
for (; node && (n > NODE_NUMDATA(node)[node->N-1]); node=node->next)
nskipped += node->N;
if (!node) {
// either we're adding the first element, or we're appending since
// n is bigger than the largest element in the list.
NLF(append)(list, n);
return list->N-1;
}
*/
// find where in the node it should be inserted...
ind = 1 + NLF(binarysearch)(node, n);
// check if it's a duplicate...
if (unique && ind > 0 && (n == NODE_NUMDATA(node)[ind-1]))
return BL_NOT_FOUND;
// set the jump accessors...
list->last_access = node;
list->last_access_n = nskipped;
// ... so that this runs in O(1).
bl_insert(list, nskipped + ind, &n);
return nskipped + ind;
}
size_t NLF(insert_ascending)(nl* list, const number n) {
return NLF(insertascending)(list, n, 0);
}
ptrdiff_t NLF(insert_unique_ascending)(nl* list, const number n) {
return NLF(insertascending)(list, n, 1);
}
size_t NLF(insert_descending)(nl* list, const number n) {
return bl_insert_sorted(list, &n, NLF(compare_descending));
}
void NLF(insert)(nl* list, size_t indx, const number data) {
bl_insert(list, indx, &data);
}
void NLF(copy)(nl* list, size_t start, size_t length, number* vdest) {
bl_copy(list, start, length, vdest);
}
void NLF(print)(nl* list) {
bl_node* n;
for (n=list->head; n; n=n->next) {
int i;
printf("[ ");
for (i=0; iN; i++) {
if (i > 0)
printf(", ");
NL_PRINT(NODE_NUMDATA(n)[i]);
}
printf("] ");
}
}
ptrdiff_t NLF(index_of)(nl* list, const number data) {
bl_node* n;
number* idata;
size_t npast = 0;
for (n=list->head; n; n=n->next) {
int i;
idata = NODE_NUMDATA(n);
for (i=0; iN; i++)
if (idata[i] == data)
return npast + i;
npast += n->N;
}
return BL_NOT_FOUND;
}
int NLF(contains)(nl* list, const number data) {
return (NLF(index_of)(list, data) != BL_NOT_FOUND);
}
int NLF(sorted_contains)(nl* list, const number n) {
return NLF(sorted_index_of)(list, n) != BL_NOT_FOUND;
}
ptrdiff_t NLF(sorted_index_of)(nl* list, const number n) {
bl_node *node;
ptrdiff_t lower;
size_t nskipped;
node = NLF(findnodecontainingsorted)(list, n, &nskipped);
if (!node)
return BL_NOT_FOUND;
//if (!node && (n > NODE_NUMDATA(prev)[prev->N-1]))
//return -1;
//node = prev;
/*
// find the first node for which n <= the last element. That node
// will contain the value (if it exists)
for (; node && (n > NODE_NUMDATA(node)[node->N-1]); node=node->next)
nskipped += node->N;
if (!node)
return -1;
*/
// update jump accessors...
list->last_access = node;
list->last_access_n = nskipped;
// find within the node...
lower = NLF(binarysearch)(node, n);
if (lower == BL_NOT_FOUND)
return BL_NOT_FOUND;
if (n == NODE_NUMDATA(node)[lower])
return nskipped + lower;
return BL_NOT_FOUND;
}
#undef NLF
#undef NODE_NUMDATA
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1739993667.0
astropy_healpix-1.1.2/cextern/astrometry.net/bl-nl.h 0000644 0001751 0000166 00000005437 14755431103 022170 0 ustar 00runner docker /*
# This file is part of the Astrometry.net suite.
# Licensed under a 3-clause BSD style license - see LICENSE
*/
/**
Common header for lists of numerical types.
Expects "nl" to be #defined to the list type.
Expects "number" to be #defined to the numerical type.
*/
#include "bl-nl.ph"
typedef bl nl;
// The "const number"s in here are mostly for pl.
Malloc nl* NLF(new)(int blocksize);
Pure InlineDeclare size_t NLF(size)(const nl* list);
void NLF(new_existing)(nl* list, int blocksize);
void NLF(init)(nl* list, int blocksize);
void NLF(reverse)(nl* list);
void NLF(remove_all)(nl* list);
void NLF(remove_all_reuse)(nl* list);
void NLF(free)(nl* list);
number* NLF(append)(nl* list, const number data);
void NLF(append_list)(nl* list, nl* list2);
void NLF(append_array)(nl* list, const number* data, size_t ndata);
void NLF(merge_lists)(nl* list1, nl* list2);
void NLF(push)(nl* list, const number data);
number NLF(pop)(nl* list);
int NLF(contains)(nl* list, const number data);
// Assuming the list is sorted in ascending order,
// does it contain the given number?
int NLF(sorted_contains)(nl* list, const number data);
// Or -1 if not found.
ptrdiff_t NLF(sorted_index_of)(nl* list, const number data);
#if DEFINE_SORT
void NLF(sort)(nl* list, int ascending);
#endif
Malloc number* NLF(to_array)(nl* list);
// Returns the index in the list of the given number, or -1 if it
// is not found.
ptrdiff_t NLF(index_of)(nl* list, const number data);
InlineDeclare number NLF(get)(nl* list, size_t n);
InlineDeclare number NLF(get_const)(const nl* list, size_t n);
InlineDeclare number* NLF(access)(nl* list, size_t n);
/**
Copy from the list, starting at index "start" for length "length",
into the provided array.
*/
void NLF(copy)(nl* list, size_t start, size_t length, number* vdest);
nl* NLF(dupe)(nl* list);
void NLF(print)(nl* list);
void NLF(insert)(nl* list, size_t indx, const number data);
size_t NLF(insert_ascending)(nl* list, const number n);
size_t NLF(insert_descending)(nl* list, const number n);
// Returns the index at which the element was added, or -1 if it's a duplicate.
ptrdiff_t NLF(insert_unique_ascending)(nl* list, const number p);
void NLF(set)(nl* list, size_t ind, const number value);
void NLF(remove)(nl* list, size_t ind);
void NLF(remove_index_range)(nl* list, size_t start, size_t length);
// See also sorted_index_of, which should be faster.
// Or -1 if not found
ptrdiff_t NLF(find_index_ascending)(nl* list, const number value);
nl* NLF(merge_ascending)(nl* list1, nl* list2);
// returns the index of the removed value, or -1 if it didn't
// exist in the list.
ptrdiff_t NLF(remove_value)(nl* list, const number value);
int NLF(check_consistency)(nl* list);
int NLF(check_sorted_ascending)(nl* list, int isunique);
int NLF(check_sorted_descending)(nl* list, int isunique);
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1739993667.0
astropy_healpix-1.1.2/cextern/astrometry.net/bl-nl.inc 0000644 0001751 0000166 00000001052 14755431103 022477 0 ustar 00runner docker /*
# This file is part of the Astrometry.net suite.
# Licensed under a 3-clause BSD style license - see LICENSE
*/
#include "bl-nl.ph"
InlineDefine
number NLF(get)(nl* list, size_t n) {
number* ptr = (number*)bl_access(list, n);
return *ptr;
}
InlineDefine
number NLF(get_const)(const nl* list, size_t n) {
number* ptr = (number*)bl_access_const(list, n);
return *ptr;
}
InlineDefine
size_t NLF(size)(const nl* list) {
return bl_size(list);
}
InlineDefine
number* NLF(access)(nl* list, size_t j) {
return (number*)bl_access(list, j);
}
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1739993667.0
astropy_healpix-1.1.2/cextern/astrometry.net/bl-nl.ph 0000644 0001751 0000166 00000000427 14755431103 022342 0 ustar 00runner docker /*
# This file is part of the Astrometry.net suite.
# Licensed under a 3-clause BSD style license - see LICENSE
*/
//#define NODE_NUMDATA(node) ((number*)NODE_DATA(node))
#define NLFGLUE2(n,f) n ## _ ## f
#define NLFGLUE(n,f) NLFGLUE2(n,f)
#define NLF(func) NLFGLUE(nl, func)
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1739993667.0
astropy_healpix-1.1.2/cextern/astrometry.net/bl.c 0000644 0001751 0000166 00000077620 14755431103 021557 0 ustar 00runner docker /*
# This file is part of the Astrometry.net suite.
# Licensed under a 3-clause BSD style license - see LICENSE
*/
#define _GNU_SOURCE /* for GNU extension vasprintf() */
#include
#include
#include
#include
#include
#include "bl.h"
#include "keywords.h"
#include "bl.ph"
static bl_node* bl_new_node(bl* list);
static void bl_remove_from_node(bl* list, bl_node* node,
bl_node* prev, int index_in_node);
// NOTE: this should be replaced by a proper implementation!
#ifdef _MSC_VER
int vasprintf(char **strp, const char *fmt, va_list ap) {return -1;}
#endif
// Defined in bl.ph (private header):
// free_node
// NODE_DATA
// NODE_CHARDATA
// NODE_INTDATA
// NODE_DOUBLEDATA
// Defined in bl.inc (inlined functions):
// bl_size
// bl_access
// il_size
// il_get
// NOTE!, if you make changes here, also see bl-sort.c !
//#define DEFINE_SORT 1
#define DEFINE_SORT 0
#define nl il
#define number int
#define NL_PRINT(x) printf("%i", x)
#include "bl-nl.c"
#undef nl
#undef number
#undef NL_PRINT
#define nl ll
#define number int64_t
#define NL_PRINT(x) printf("%lli", (long long int)x)
#include "bl-nl.c"
#undef nl
#undef number
#undef NL_PRINT
#define nl fl
#define number float
#define NL_PRINT(x) printf("%f", (float)x)
#include "bl-nl.c"
#undef nl
#undef number
#undef NL_PRINT
#define nl dl
#define number double
#define NL_PRINT(x) printf("%g", x)
#include "bl-nl.c"
#undef nl
#undef number
#undef NL_PRINT
#undef DEFINE_SORT
#define DEFINE_SORT 0
#define nl pl
#define number void*
#define NL_PRINT(x) printf("%p", x)
#include "bl-nl.c"
#undef nl
#undef number
#undef NL_PRINT
#undef DEFINE_SORT
Pure int bl_datasize(const bl* list) {
if (!list)
return 0;
return list->datasize;
}
void bl_split(bl* src, bl* dest, size_t split) {
bl_node* node;
size_t nskipped;
size_t ind;
size_t ntaken = src->N - split;
node = find_node(src, split, &nskipped);
ind = split - nskipped;
if (ind == 0) {
// this whole node belongs to "dest".
if (split) {
// we need to get the previous node...
bl_node* last = find_node(src, split-1, NULL);
last->next = NULL;
src->tail = last;
} else {
// we've removed everything from "src".
src->head = NULL;
src->tail = NULL;
}
} else {
// create a new node to hold the second half of the items in "node".
bl_node* newnode = bl_new_node(dest);
newnode->N = (node->N - ind);
newnode->next = node->next;
memcpy(NODE_CHARDATA(newnode),
NODE_CHARDATA(node) + (ind * src->datasize),
newnode->N * src->datasize);
node->N -= (node->N - ind);
node->next = NULL;
src->tail = node;
// to make the code outside this block work...
node = newnode;
}
// append it to "dest".
if (dest->tail) {
dest->tail->next = node;
dest->N += ntaken;
} else {
dest->head = node;
dest->tail = node;
dest->N += ntaken;
}
// adjust "src".
src->N -= ntaken;
src->last_access = NULL;
}
void bl_init(bl* list, int blocksize, int datasize) {
list->head = NULL;
list->tail = NULL;
list->N = 0;
list->blocksize = blocksize;
list->datasize = datasize;
list->last_access = NULL;
list->last_access_n = 0;
}
bl* bl_new(int blocksize, int datasize) {
bl* rtn;
rtn = malloc(sizeof(bl));
if (!rtn) {
printf("Couldn't allocate memory for a bl.\n");
return NULL;
}
bl_init(rtn, blocksize, datasize);
return rtn;
}
void bl_free(bl* list) {
if (!list) return;
bl_remove_all(list);
free(list);
}
void bl_remove_all(bl* list) {
bl_node *n, *lastnode;
lastnode = NULL;
for (n=list->head; n; n=n->next) {
if (lastnode)
bl_free_node(lastnode);
lastnode = n;
}
if (lastnode)
bl_free_node(lastnode);
list->head = NULL;
list->tail = NULL;
list->N = 0;
list->last_access = NULL;
list->last_access_n = 0;
}
void bl_remove_all_but_first(bl* list) {
bl_node *n, *lastnode;
lastnode = NULL;
if (list->head) {
for (n=list->head->next; n; n=n->next) {
if (lastnode)
bl_free_node(lastnode);
lastnode = n;
}
if (lastnode)
bl_free_node(lastnode);
list->head->next = NULL;
list->head->N = 0;
list->tail = list->head;
} else {
list->head = NULL;
list->tail = NULL;
}
list->N = 0;
list->last_access = NULL;
list->last_access_n = 0;
}
static void bl_remove_from_node(bl* list, bl_node* node,
bl_node* prev, int index_in_node) {
// if we're removing the last element at this node, then
// remove this node from the linked list.
if (node->N == 1) {
// if we're removing the first node...
if (prev == NULL) {
list->head = node->next;
// if it's the first and only node...
if (list->head == NULL) {
list->tail = NULL;
}
} else {
// if we're removing the last element from
// the tail node...
if (node == list->tail) {
list->tail = prev;
}
prev->next = node->next;
}
bl_free_node(node);
} else {
int ncopy;
// just remove this element...
ncopy = node->N - index_in_node - 1;
if (ncopy > 0) {
memmove(NODE_CHARDATA(node) + index_in_node * list->datasize,
NODE_CHARDATA(node) + (index_in_node+1) * list->datasize,
ncopy * list->datasize);
}
node->N--;
}
list->N--;
}
void bl_remove_index(bl* list, size_t index) {
// find the node (and previous node) at which element 'index'
// can be found.
bl_node *node, *prev;
size_t nskipped = 0;
for (node=list->head, prev=NULL;
node;
prev=node, node=node->next) {
if (index < (nskipped + node->N))
break;
nskipped += node->N;
}
assert(node);
bl_remove_from_node(list, node, prev, index-nskipped);
list->last_access = NULL;
list->last_access_n = 0;
}
void bl_remove_index_range(bl* list, size_t start, size_t length) {
// find the node (and previous node) at which element 'start'
// can be found.
bl_node *node, *prev;
size_t nskipped = 0;
list->last_access = NULL;
list->last_access_n = 0;
for (node=list->head, prev=NULL;
node;
prev=node, node=node->next) {
if (start < (nskipped + node->N))
break;
nskipped += node->N;
}
// begin by removing any indices that are at the end of a block.
if (start > nskipped) {
// we're not removing everything at this node.
size_t istart;
size_t n;
istart = start - nskipped;
if ((istart + length) < node->N) {
// we're removing a chunk of elements from the middle of this
// block. move elements from the end into the removed chunk.
memmove(NODE_CHARDATA(node) + istart * list->datasize,
NODE_CHARDATA(node) + (istart + length) * list->datasize,
(node->N - (istart + length)) * list->datasize);
// we're done!
node->N -= length;
list->N -= length;
return;
} else {
// we're removing everything from 'istart' to the end of this
// block. just change the "N" values.
n = (node->N - istart);
node->N -= n;
list->N -= n;
length -= n;
start += n;
nskipped = start;
prev = node;
node = node->next;
}
}
// remove complete blocks.
for (;;) {
size_t n;
bl_node* todelete;
if (length == 0 || length < node->N)
break;
// we're skipping this whole block.
n = node->N;
length -= n;
start += n;
list->N -= n;
nskipped += n;
todelete = node;
node = node->next;
bl_free_node(todelete);
}
if (prev)
prev->next = node;
else
list->head = node;
if (!node)
list->tail = prev;
// remove indices from the beginning of the last block.
// note that we may have removed everything from the tail of the list,
// no "node" may be null.
if (node && length>0) {
//printf("removing %i from end.\n", length);
memmove(NODE_CHARDATA(node),
NODE_CHARDATA(node) + length * list->datasize,
(node->N - length) * list->datasize);
node->N -= length;
list->N -= length;
}
}
static void clear_list(bl* list) {
list->head = NULL;
list->tail = NULL;
list->N = 0;
list->last_access = NULL;
list->last_access_n = 0;
}
void bl_append_list(bl* list1, bl* list2) {
list1->last_access = NULL;
list1->last_access_n = 0;
if (list1->datasize != list2->datasize) {
printf("Error: cannot append bls with different data sizes!\n");
assert(0);
exit(0);
}
if (list1->blocksize != list2->blocksize) {
printf("Error: cannot append bls with different block sizes!\n");
assert(0);
exit(0);
}
// if list1 is empty, then just copy over list2's head and tail.
if (list1->head == NULL) {
list1->head = list2->head;
list1->tail = list2->tail;
list1->N = list2->N;
// remove everything from list2 (to avoid sharing nodes)
clear_list(list2);
return;
}
// if list2 is empty, then do nothing.
if (list2->head == NULL)
return;
// otherwise, append list2's head to list1's tail.
list1->tail->next = list2->head;
list1->tail = list2->tail;
list1->N += list2->N;
// remove everything from list2 (to avoid sharing nodes)
clear_list(list2);
}
static bl_node* bl_new_node(bl* list) {
bl_node* rtn;
// merge the mallocs for the node and its data into one malloc.
rtn = malloc(sizeof(bl_node) + list->datasize * list->blocksize);
if (!rtn) {
printf("Couldn't allocate memory for a bl node!\n");
return NULL;
}
//rtn->data = (char*)rtn + sizeof(bl_node);
rtn->N = 0;
rtn->next = NULL;
return rtn;
}
static void bl_append_node(bl* list, bl_node* node) {
node->next = NULL;
if (!list->head) {
// first node to be added.
list->head = node;
list->tail = node;
} else {
list->tail->next = node;
list->tail = node;
}
list->N += node->N;
}
/*
* Append an item to this bl node. If this node is full, then create a new
* node and insert it into the list.
*
* Returns the location where the new item was copied.
*/
void* bl_node_append(bl* list, bl_node* node, const void* data) {
void* dest;
if (node->N == list->blocksize) {
// create a new node and insert it after the current node.
bl_node* newnode;
newnode = bl_new_node(list);
newnode->next = node->next;
node->next = newnode;
if (list->tail == node)
list->tail = newnode;
node = newnode;
}
// space remains at this node. add item.
dest = NODE_CHARDATA(node) + node->N * list->datasize;
if (data)
memcpy(dest, data, list->datasize);
node->N++;
list->N++;
return dest;
}
void* bl_append(bl* list, const void* data) {
if (!list->tail)
// empty list; create a new node.
bl_append_node(list, bl_new_node(list));
// append the item to the tail. if the tail node is full, a new tail node may be created.
return bl_node_append(list, list->tail, data);
}
void* bl_push(bl* list, const void* data) {
return bl_append(list, data);
}
void bl_pop(bl* list, void* into) {
assert(list->N > 0);
bl_get(list, list->N-1, into);
bl_remove_index(list, list->N-1);
}
void bl_print_structure(bl* list) {
bl_node* n;
printf("bl: head %p, tail %p, N %zu\n", list->head, list->tail, list->N);
for (n=list->head; n; n=n->next) {
printf("[N=%i] ", n->N);
}
printf("\n");
}
void bl_get(bl* list, size_t n, void* dest) {
char* src;
assert(list->N > 0);
src = bl_access(list, n);
memcpy(dest, src, list->datasize);
}
static void bl_find_ind_and_element(bl* list, const void* data,
int (*compare)(const void* v1, const void* v2),
void** presult, ptrdiff_t* pindex) {
ptrdiff_t lower, upper;
int cmp = -2;
void* result;
lower = -1;
upper = list->N;
while (lower < (upper-1)) {
ptrdiff_t mid;
mid = (upper + lower) / 2;
cmp = compare(data, bl_access(list, mid));
if (cmp >= 0) {
lower = mid;
} else {
upper = mid;
}
}
if (lower == -1 || compare(data, (result = bl_access(list, lower)))) {
*presult = NULL;
if (pindex)
*pindex = -1;
return;
}
*presult = result;
if (pindex)
*pindex = lower;
}
/**
* Finds a node for which the given compare() function
* returns zero when passed the given 'data' pointer
* and elements from the list.
*/
void* bl_find(bl* list, const void* data,
int (*compare)(const void* v1, const void* v2)) {
void* rtn;
bl_find_ind_and_element(list, data, compare, &rtn, NULL);
return rtn;
}
ptrdiff_t bl_find_index(bl* list, const void* data,
int (*compare)(const void* v1, const void* v2)) {
void* val;
ptrdiff_t ind;
bl_find_ind_and_element(list, data, compare, &val, &ind);
return ind;
}
size_t bl_insert_sorted(bl* list, const void* data,
int (*compare)(const void* v1, const void* v2)) {
ptrdiff_t lower, upper;
lower = -1;
upper = list->N;
while (lower < (upper-1)) {
ptrdiff_t mid;
int cmp;
mid = (upper + lower) / 2;
cmp = compare(data, bl_access(list, mid));
if (cmp >= 0) {
lower = mid;
} else {
upper = mid;
}
}
bl_insert(list, lower+1, data);
return lower+1;
}
ptrdiff_t bl_insert_unique_sorted(bl* list, const void* data,
int (*compare)(const void* v1, const void* v2)) {
// This is just straightforward binary search - really should
// use the block structure...
ptrdiff_t lower, upper;
lower = -1;
upper = list->N;
while (lower < (upper-1)) {
ptrdiff_t mid;
int cmp;
mid = (upper + lower) / 2;
cmp = compare(data, bl_access(list, mid));
if (cmp >= 0) {
lower = mid;
} else {
upper = mid;
}
}
if (lower >= 0) {
if (compare(data, bl_access(list, lower)) == 0) {
return BL_NOT_FOUND;
}
}
bl_insert(list, lower+1, data);
return lower+1;
}
void bl_set(bl* list, size_t index, const void* data) {
bl_node* node;
size_t nskipped;
void* dataloc;
node = find_node(list, index, &nskipped);
dataloc = NODE_CHARDATA(node) + (index - nskipped) * list->datasize;
memcpy(dataloc, data, list->datasize);
// update the last_access member...
list->last_access = node;
list->last_access_n = nskipped;
}
/**
* Insert the element "data" into the list, such that its index is "index".
* All elements that previously had indices "index" and above are moved
* one position to the right.
*/
void bl_insert(bl* list, size_t index, const void* data) {
bl_node* node;
size_t nskipped;
if (list->N == index) {
bl_append(list, data);
return;
}
node = find_node(list, index, &nskipped);
list->last_access = node;
list->last_access_n = nskipped;
// if the node is full:
// if we're inserting at the end of this node, then create a new node.
// else, shift all but the last element, add in this element, and
// add the last element to a new node.
if (node->N == list->blocksize) {
int localindex, nshift;
bl_node* next = node->next;
bl_node* destnode;
localindex = index - nskipped;
// if the next node exists and is not full, then insert the overflowing
// element at the front. otherwise, create a new node.
if (next && (next->N < list->blocksize)) {
// shift the existing elements up by one position...
memmove(NODE_CHARDATA(next) + list->datasize,
NODE_CHARDATA(next),
next->N * list->datasize);
destnode = next;
} else {
// create and insert a new node.
bl_node* newnode = bl_new_node(list);
newnode->next = next;
node->next = newnode;
if (!newnode->next)
list->tail = newnode;
destnode = newnode;
}
if (localindex == node->N) {
// the new element becomes the first element in the destination node.
memcpy(NODE_CHARDATA(destnode), data, list->datasize);
} else {
// the last element in this node is added to the destination node.
memcpy(NODE_CHARDATA(destnode), NODE_CHARDATA(node) + (node->N-1)*list->datasize, list->datasize);
// shift the end portion of this node up by one...
nshift = node->N - localindex - 1;
memmove(NODE_CHARDATA(node) + (localindex+1) * list->datasize,
NODE_CHARDATA(node) + localindex * list->datasize,
nshift * list->datasize);
// insert the new element...
memcpy(NODE_CHARDATA(node) + localindex * list->datasize, data, list->datasize);
}
destnode->N++;
list->N++;
} else {
// shift...
int localindex, nshift;
localindex = index - nskipped;
nshift = node->N - localindex;
memmove(NODE_CHARDATA(node) + (localindex+1) * list->datasize,
NODE_CHARDATA(node) + localindex * list->datasize,
nshift * list->datasize);
// insert...
memcpy(NODE_CHARDATA(node) + localindex * list->datasize,
data, list->datasize);
node->N++;
list->N++;
}
}
void* bl_access_const(const bl* list, size_t n) {
bl_node* node;
size_t nskipped;
node = find_node(list, n, &nskipped);
// grab the element.
return NODE_CHARDATA(node) + (n - nskipped) * list->datasize;
}
void bl_copy(bl* list, size_t start, size_t length, void* vdest) {
bl_node* node;
size_t nskipped;
char* dest;
if (length <= 0)
return;
node = find_node(list, start, &nskipped);
// we've found the node containing "start". keep copying elements and
// moving down the list until we've copied all "length" elements.
dest = vdest;
while (length > 0) {
size_t take, avail;
char* src;
// number of elements we want to take.
take = length;
// number of elements available at this node.
avail = node->N - (start - nskipped);
if (take > avail)
take = avail;
src = NODE_CHARDATA(node) + (start - nskipped) * list->datasize;
memcpy(dest, src, take * list->datasize);
dest += take * list->datasize;
start += take;
length -= take;
nskipped += node->N;
node = node->next;
}
// update the last_access member...
list->last_access = node;
list->last_access_n = nskipped;
}
int bl_check_consistency(bl* list) {
bl_node* node;
size_t N;
int tailok = 1;
int nempty = 0;
int nnull = 0;
// if one of head or tail is NULL, they had both better be NULL!
if (!list->head)
nnull++;
if (!list->tail)
nnull++;
if (nnull == 1) {
fprintf(stderr, "bl_check_consistency: head is %p, and tail is %p.\n",
list->head, list->tail);
return 1;
}
N = 0;
for (node=list->head; node; node=node->next) {
N += node->N;
if (!node->N) {
// this block is empty.
nempty++;
}
// are we at the last node?
if (!node->next) {
tailok = (list->tail == node) ? 1 : 0;
}
}
if (!tailok) {
fprintf(stderr, "bl_check_consistency: tail pointer is wrong.\n");
return 1;
}
if (nempty) {
fprintf(stderr, "bl_check_consistency: %i empty blocks.\n", nempty);
return 1;
}
if (N != list->N) {
fprintf(stderr, "bl_check_consistency: list->N is %zu, but sum of blocks is %zu.\n",
list->N, N);
return 1;
}
return 0;
}
int bl_check_sorted(bl* list,
int (*compare)(const void* v1, const void* v2),
int isunique) {
size_t i, N;
size_t nbad = 0;
void* v2 = NULL;
N = bl_size(list);
if (N)
v2 = bl_access(list, 0);
for (i=1; i= 0) {
nbad++;
}
} else {
if (cmp > 0) {
nbad++;
}
}
}
if (nbad) {
fprintf(stderr, "bl_check_sorted: %zu are out of order.\n", nbad);
return 1;
}
return 0;
}
static void memswap(void* v1, void* v2, int len) {
unsigned char tmp;
unsigned char* c1 = v1;
unsigned char* c2 = v2;
int i;
for (i=0; ihead; node; node=node->next) {
for (i=0; i<(node->N/2); i++) {
memswap(NODE_CHARDATA(node) + i * list->datasize,
NODE_CHARDATA(node) + (node->N - 1 - i) * list->datasize,
list->datasize);
}
pl_append(blocks, node);
}
// reverse the blocks
lastnode = NULL;
for (i=pl_size(blocks)-1; i>=0; i--) {
node = pl_get(blocks, i);
if (lastnode)
lastnode->next = node;
lastnode = node;
}
if (lastnode)
lastnode->next = NULL;
pl_free(blocks);
// swap head and tail
node = list->head;
list->head = list->tail;
list->tail = node;
list->last_access = NULL;
list->last_access_n = 0;
}
void* bl_extend(bl* list) {
return bl_append(list, NULL);
}
// special-case pointer list accessors...
int bl_compare_pointers_ascending(const void* v1, const void* v2) {
void* p1 = *(void**)v1;
void* p2 = *(void**)v2;
if (p1 > p2) return 1;
else if (p1 < p2) return -1;
else return 0;
}
void pl_free_elements(pl* list) {
size_t i;
for (i=0; iN;
while (lower < (upper-1)) {
ptrdiff_t mid;
int cmp;
mid = (upper + lower) / 2;
cmp = compare(data, pl_get(list, mid));
if (cmp >= 0) {
lower = mid;
} else {
upper = mid;
}
}
bl_insert(list, lower+1, &data);
return lower+1;
}
/*
void pl_set(pl* list, int index, void* data) {
int i;
int nadd = (index+1) - list->N;
if (nadd > 0) {
// enlarge the list to hold 'nadd' more entries.
for (i=0; i=0; i--) {
char* s = sl_get(lst, i);
if (strcmp(s, str) == 0)
return i;
}
return BL_NOT_FOUND;
}
// Returns 0 if the string is not in the sl, 1 otherwise.
// (same as sl_index_of(lst, str) > -1)
int sl_contains(sl* lst, const char* str) {
return (sl_index_of(lst, str) > -1);
}
void sl_reverse(sl* list) {
bl_reverse(list);
}
char* sl_append(sl* list, const char* data) {
char* copy;
if (data) {
copy = strdup(data);
assert(copy);
} else
copy = NULL;
pl_append(list, copy);
return copy;
}
void sl_append_array(sl* list, const char**strings, size_t n) {
size_t i;
for (i=0; i= 0);
copy = strdup(value);
if (index < list->N) {
// we're replacing an existing value - free it!
free(sl_get(list, index));
bl_set(list, index, ©);
} else {
// pad
size_t i;
for (i=list->N; iN);
assert(start >= 0);
assert(length >= 0);
for (i=0; ihead; n; n=n->next) {
printf("[\n");
for (i=0; iN; i++)
printf(" \"%s\"\n", ((char**)NODE_DATA(n))[i]);
printf("]\n");
}
}
static char* sljoin(sl* list, const char* join, int forward) {
size_t start, end, inc;
size_t len = 0;
size_t i, N;
char* rtn;
size_t offset;
size_t JL;
if (sl_size(list) == 0)
return strdup("");
// step through the list forward or backward?
if (forward) {
start = 0;
end = sl_size(list);
inc = 1;
} else {
start = sl_size(list) - 1;
end = -1;
inc = -1;
}
JL = strlen(join);
N = sl_size(list);
for (i=0; i