From d2e2e47d893cd51d409042ec8951d6d7fba84e84 Mon Sep 17 00:00:00 2001 From: Stuart Mumford Date: Mon, 5 Sep 2022 14:13:39 +0100 Subject: [PATCH 001/366] Add GitHub changelog settings --- .github/release.yml | 17 +++++++++++++ .github/workflows/update-changelog.yaml | 33 +++++++++++++++++++++++++ 2 files changed, 50 insertions(+) create mode 100644 .github/release.yml create mode 100644 .github/workflows/update-changelog.yaml diff --git a/.github/release.yml b/.github/release.yml new file mode 100644 index 000000000..a55cbf7f7 --- /dev/null +++ b/.github/release.yml @@ -0,0 +1,17 @@ +changelog: + exclude: + authors: + - pre-commit-ci + categories: + - title: Bug Fixes + labels: + - bug + - title: New Features + labels: + - enhancement + - title: Documentation + labels: + - Documentation + - title: Other Changes + labels: + - "*" diff --git a/.github/workflows/update-changelog.yaml b/.github/workflows/update-changelog.yaml new file mode 100644 index 000000000..1432b5e02 --- /dev/null +++ b/.github/workflows/update-changelog.yaml @@ -0,0 +1,33 @@ +# This workflow takes the GitHub release notes an updates the changelog on the +# main branch with the body of the release notes, thereby keeping a log in +# the git repo of the changes. + +name: "Update Changelog" + +on: + release: + types: [released] + +jobs: + update: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v2 + with: + ref: main + + - name: Update Changelog + uses: stefanzweifel/changelog-updater-action@v1 + with: + release-notes: ${{ github.event.release.body }} + latest-version: ${{ github.event.release.name }} + path-to-changelog: CHANGES.md + + - name: Commit updated CHANGELOG + uses: stefanzweifel/git-auto-commit-action@v4 + with: + branch: main + commit_message: Update CHANGELOG + file_pattern: CHANGES.md From 816207cfb42ddc72f0c717a0a6853b8783001919 Mon Sep 17 00:00:00 2001 From: Stuart Mumford Date: Mon, 5 Sep 2022 14:16:39 +0100 Subject: [PATCH 002/366] Initial pre-commit config --- .pre-commit-config.yaml | 65 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 .pre-commit-config.yaml diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 000000000..78a24f42d --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,65 @@ +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.2.0 + hooks: + - id: check-added-large-files + - id: check-case-conflict + - id: check-yaml + - id: debug-statements + - id: end-of-file-fixer + exclude: ".*(data.*|extern.*|licenses.*|_parsetab.py)$" + - id: trailing-whitespace + exclude: ".*(data.*|extern.*|licenses.*|_parsetab.py|test_cds.py)$" + + # - repo: https://github.com/pycqa/isort + # rev: 5.10.1 + # hooks: + # - id: isort + # name: isort (python) + # + # - repo: https://github.com/asottile/pyupgrade + # rev: v2.34.0 + # hooks: + # - id: pyupgrade + # args: ["--py38-plus"] + # exclude: ".*(extern.*|_parsetab.py|_lextab.py)$" + + # We list the warnings/errors to check for here rather than in setup.cfg because + # we don't want these options to apply whenever anyone calls flake8 from the + # command-line or their code editor - in this case all warnings/errors should be + # checked for. The warnings/errors we check for here are: + # E101 - mix of tabs and spaces + # W191 - use of tabs + # E201 - whitespace after '(' + # E202 - whitespace before ')' + # W291 - trailing whitespace + # W292 - no newline at end of file + # W293 - trailing whitespace + # W391 - blank line at end of file + # E111 - 4 spaces per indentation level + # E112 - 4 spaces per indentation level + # E113 - 4 spaces per indentation level + # E301 - expected 1 blank line, found 0 + # E302 - expected 2 blank lines, found 0 + # E303 - too many blank lines (3) + # E304 - blank lines found after function decorator + # E305 - expected 2 blank lines after class or function definition + # E306 - expected 1 blank line before a nested definition + # E502 - the backslash is redundant between brackets + # E722 - do not use bare except + # E901 - SyntaxError or IndentationError + # E902 - IOError + # E999: SyntaxError -- failed to compile a file into an Abstract Syntax Tree + # F822: undefined name in __all__ + # F823: local variable name referenced before assignment + - repo: https://gitlab.com/pycqa/flake8 + rev: 3.9.2 + hooks: + - id: flake8 + args: + [ + "--count", + "--select", + "E101,W191,E201,E202,W291,W292,W293,W391,E111,E112,E113,E30,E502,E722,E901,E902,E999,F822,F823", + ] + exclude: ".*(data.*|extern.*|cextern)$" From 1d5e5c8d64332a38b69c470802ad4068eb6e60f1 Mon Sep 17 00:00:00 2001 From: Stuart Mumford Date: Mon, 5 Sep 2022 14:17:09 +0100 Subject: [PATCH 003/366] Remove old docs templates which are now in the theme --- docs/_templates/autosummary/base.rst | 2 -- docs/_templates/autosummary/class.rst | 2 -- docs/_templates/autosummary/module.rst | 2 -- 3 files changed, 6 deletions(-) delete mode 100644 docs/_templates/autosummary/base.rst delete mode 100644 docs/_templates/autosummary/class.rst delete mode 100644 docs/_templates/autosummary/module.rst diff --git a/docs/_templates/autosummary/base.rst b/docs/_templates/autosummary/base.rst deleted file mode 100644 index 9cabaf523..000000000 --- a/docs/_templates/autosummary/base.rst +++ /dev/null @@ -1,2 +0,0 @@ -{% extends "autosummary_core/base.rst" %} -{# The template this is inherited from is in astropy/sphinx/ext/templates/autosummary_core. If you want to modify this template, it is strongly recommended that you still inherit from the astropy template. #} \ No newline at end of file diff --git a/docs/_templates/autosummary/class.rst b/docs/_templates/autosummary/class.rst deleted file mode 100644 index 6b214a5c0..000000000 --- a/docs/_templates/autosummary/class.rst +++ /dev/null @@ -1,2 +0,0 @@ -{% extends "autosummary_core/class.rst" %} -{# The template this is inherited from is in astropy/sphinx/ext/templates/autosummary_core. If you want to modify this template, it is strongly recommended that you still inherit from the astropy template. #} \ No newline at end of file diff --git a/docs/_templates/autosummary/module.rst b/docs/_templates/autosummary/module.rst deleted file mode 100644 index f38315b22..000000000 --- a/docs/_templates/autosummary/module.rst +++ /dev/null @@ -1,2 +0,0 @@ -{% extends "autosummary_core/module.rst" %} -{# The template this is inherited from is in astropy/sphinx/ext/templates/autosummary_core. If you want to modify this template, it is strongly recommended that you still inherit from the astropy template. #} \ No newline at end of file From e9748cb741e9cbb4f5d5fb4cd950fab830f2f1c1 Mon Sep 17 00:00:00 2001 From: Stuart Mumford Date: Mon, 5 Sep 2022 14:17:36 +0100 Subject: [PATCH 004/366] Pre-commit cleanup --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index b46535de9..09684c6cd 100644 --- a/tox.ini +++ b/tox.ini @@ -16,7 +16,7 @@ passenv = setenv = HOME = {envtmpdir} MPLBACKEND = Agg - PYTEST_COMMAND = pytest --arraydiff --arraydiff-default-format=fits --pyargs reproject --cov reproject --cov-config={toxinidir}/setup.cfg {toxinidir}/docs + PYTEST_COMMAND = pytest --arraydiff --arraydiff-default-format=fits --pyargs reproject --cov reproject --cov-config={toxinidir}/setup.cfg {toxinidir}/docs changedir = .tmp/{envname} deps = From 78646a31996fb51dc1a4603135659b9512dcbc2c Mon Sep 17 00:00:00 2001 From: Stuart Mumford Date: Mon, 5 Sep 2022 14:24:14 +0100 Subject: [PATCH 005/366] update isort to be closer to core --- .pre-commit-config.yaml | 12 ++++++------ pyproject.toml | 12 ++++++++++++ reproject/__init__.py | 5 +++-- reproject/adaptive/core.py | 7 ++----- reproject/adaptive/deforest.pyx | 7 +++++-- reproject/adaptive/high_level.py | 3 ++- reproject/adaptive/tests/test_core.py | 4 ++-- reproject/interpolation/core.py | 4 ++-- reproject/interpolation/tests/test_core.py | 2 +- reproject/mosaicking/tests/test_coadd.py | 3 +-- reproject/mosaicking/wcs_helpers.py | 5 +++-- reproject/spherical_intersect/core.py | 2 +- reproject/spherical_intersect/setup_package.py | 3 ++- .../spherical_intersect/tests/test_high_level.py | 1 + reproject/tests/test_high_level.py | 2 +- reproject/tests/test_utils.py | 2 +- reproject/utils.py | 3 +-- setup.cfg | 12 ------------ 18 files changed, 46 insertions(+), 43 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 78a24f42d..a10f5dc9a 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -11,12 +11,12 @@ repos: - id: trailing-whitespace exclude: ".*(data.*|extern.*|licenses.*|_parsetab.py|test_cds.py)$" - # - repo: https://github.com/pycqa/isort - # rev: 5.10.1 - # hooks: - # - id: isort - # name: isort (python) - # + - repo: https://github.com/pycqa/isort + rev: 5.10.1 + hooks: + - id: isort + name: isort (python) + # - repo: https://github.com/asottile/pyupgrade # rev: v2.34.0 # hooks: diff --git a/pyproject.toml b/pyproject.toml index 3da100ea9..f4425af49 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,3 +9,15 @@ build-backend = 'setuptools.build_meta' [tool.cibuildwheel] skip = "cp36-* pp* *-musllinux* cp310-win32" + +[tool.isort] + extend_skip_glob = [ + "docs/*", + "setup.py"] + line_length = 100 + known_third_party = ["astropy"] + known_first_party = ["reproject"] + multi_line_output = 4 + group_by_package = true + indented_import_headings = false + length_sort_sections = ["future", "stdlib"] diff --git a/reproject/__init__.py b/reproject/__init__.py index d69541a01..68216c3e5 100644 --- a/reproject/__init__.py +++ b/reproject/__init__.py @@ -7,10 +7,11 @@ # should keep this content at the top. # ---------------------------------------------------------------------------- from ._astropy_init import * # noqa + # ---------------------------------------------------------------------------- if not _ASTROPY_SETUP_: # noqa + from .adaptive import reproject_adaptive # noqa + from .healpix import reproject_from_healpix, reproject_to_healpix # noqa from .interpolation import reproject_interp # noqa from .spherical_intersect import reproject_exact # noqa - from .healpix import reproject_from_healpix, reproject_to_healpix # noqa - from .adaptive import reproject_adaptive # noqa diff --git a/reproject/adaptive/core.py b/reproject/adaptive/core.py index 771715cd1..e8c7d5758 100644 --- a/reproject/adaptive/core.py +++ b/reproject/adaptive/core.py @@ -1,13 +1,10 @@ # Licensed under a 3-clause BSD style license - see LICENSE.rst -from __future__ import (absolute_import, division, print_function, - unicode_literals) +from __future__ import division, print_function, absolute_import, unicode_literals import numpy as np +from ..wcs_utils import efficient_pixel_to_pixel, efficient_pixel_to_pixel_with_roundtrip from .deforest import map_coordinates -from ..wcs_utils import (efficient_pixel_to_pixel_with_roundtrip, - efficient_pixel_to_pixel) - __all__ = ['_reproject_adaptive_2d'] diff --git a/reproject/adaptive/deforest.pyx b/reproject/adaptive/deforest.pyx index 4982c54b9..1185d52f0 100644 --- a/reproject/adaptive/deforest.pyx +++ b/reproject/adaptive/deforest.pyx @@ -29,11 +29,14 @@ # POSSIBILITY OF SUCH DAMAGE. import numpy as np -cimport numpy as np + cimport cython -from libc.math cimport sin, cos, atan2, sqrt, floor, ceil, round, exp, fabs +cimport numpy as np +from libc.math cimport atan2, ceil, cos, exp, fabs, floor, round, sin, sqrt + import sys + cdef double pi = np.pi cdef double nan = np.nan diff --git a/reproject/adaptive/high_level.py b/reproject/adaptive/high_level.py index 3d6693d50..148453a27 100644 --- a/reproject/adaptive/high_level.py +++ b/reproject/adaptive/high_level.py @@ -1,7 +1,8 @@ # Licensed under a 3-clause BSD style license - see LICENSE.rst -import astropy.utils import warnings +import astropy.utils + from ..utils import parse_input_data, parse_output_projection from .core import _reproject_adaptive_2d diff --git a/reproject/adaptive/tests/test_core.py b/reproject/adaptive/tests/test_core.py index 5715e6dfe..0018b43be 100644 --- a/reproject/adaptive/tests/test_core.py +++ b/reproject/adaptive/tests/test_core.py @@ -10,8 +10,8 @@ from astropy.wcs.wcsapi import HighLevelWCSWrapper, SlicedLowLevelWCS from numpy.testing import assert_allclose -from ..high_level import reproject_adaptive from ...tests.helpers import array_footprint_to_hdulist +from ..high_level import reproject_adaptive DATA = os.path.join(os.path.dirname(__file__), '..', '..', 'tests', 'data') @@ -558,8 +558,8 @@ def test_boundary_modes(x_cyclic, y_cyclic, rotated): def prepare_test_data(file_format): pytest.importorskip('sunpy', minversion='2.1.0') - from sunpy.map import Map from sunpy.coordinates.ephemeris import get_body_heliographic_stonyhurst + from sunpy.map import Map if file_format == 'fits': map_aia = Map(os.path.join(DATA, 'aia_171_level1.fits')) diff --git a/reproject/interpolation/core.py b/reproject/interpolation/core.py index 5e98ccd9c..d87f1a7b1 100644 --- a/reproject/interpolation/core.py +++ b/reproject/interpolation/core.py @@ -4,8 +4,8 @@ from astropy.wcs import WCS from ..array_utils import map_coordinates -from ..wcs_utils import (efficient_pixel_to_pixel_with_roundtrip, - efficient_pixel_to_pixel, has_celestial) +from ..wcs_utils import ( + efficient_pixel_to_pixel, efficient_pixel_to_pixel_with_roundtrip, has_celestial) def _validate_wcs(wcs_in, wcs_out, shape_out): diff --git a/reproject/interpolation/tests/test_core.py b/reproject/interpolation/tests/test_core.py index 7c33538bd..defe956df 100644 --- a/reproject/interpolation/tests/test_core.py +++ b/reproject/interpolation/tests/test_core.py @@ -506,8 +506,8 @@ def test_reproject_roundtrip(file_format): # The observer handling changed in 2.1. pytest.importorskip('sunpy', minversion='2.1.0') - from sunpy.map import Map from sunpy.coordinates.ephemeris import get_body_heliographic_stonyhurst + from sunpy.map import Map if file_format == 'fits': map_aia = Map(get_pkg_data_filename('data/aia_171_level1.fits', package='reproject.tests')) diff --git a/reproject/mosaicking/tests/test_coadd.py b/reproject/mosaicking/tests/test_coadd.py index d531ac906..eb536f240 100644 --- a/reproject/mosaicking/tests/test_coadd.py +++ b/reproject/mosaicking/tests/test_coadd.py @@ -5,10 +5,9 @@ import numpy as np import pytest -from astropy.wcs import WCS from astropy.io import fits from astropy.io.fits import Header - +from astropy.wcs import WCS from numpy.testing import assert_allclose from reproject import reproject_exact, reproject_interp diff --git a/reproject/mosaicking/wcs_helpers.py b/reproject/mosaicking/wcs_helpers.py index 6ead621ad..58e44a615 100644 --- a/reproject/mosaicking/wcs_helpers.py +++ b/reproject/mosaicking/wcs_helpers.py @@ -3,8 +3,9 @@ import numpy as np from astropy import units as u from astropy.coordinates import SkyCoord, frame_transform_graph -from astropy.wcs.utils import (celestial_frame_to_wcs, pixel_to_skycoord, proj_plane_pixel_scales, - skycoord_to_pixel, wcs_to_celestial_frame) +from astropy.wcs.utils import ( + celestial_frame_to_wcs, pixel_to_skycoord, proj_plane_pixel_scales, skycoord_to_pixel, + wcs_to_celestial_frame) from ..utils import parse_input_shape diff --git a/reproject/spherical_intersect/core.py b/reproject/spherical_intersect/core.py index 02ea22ed7..3d2b20899 100644 --- a/reproject/spherical_intersect/core.py +++ b/reproject/spherical_intersect/core.py @@ -4,8 +4,8 @@ import warnings import numpy as np -from astropy.wcs import WCS from astropy import units as u +from astropy.wcs import WCS from astropy.wcs.utils import proj_plane_pixel_area diff --git a/reproject/spherical_intersect/setup_package.py b/reproject/spherical_intersect/setup_package.py index 00d3bd3a7..eab4fd250 100644 --- a/reproject/spherical_intersect/setup_package.py +++ b/reproject/spherical_intersect/setup_package.py @@ -1,6 +1,7 @@ import os -from setuptools import Extension + import numpy as np +from setuptools import Extension REPROJECT_ROOT = os.path.relpath(os.path.dirname(__file__)) diff --git a/reproject/spherical_intersect/tests/test_high_level.py b/reproject/spherical_intersect/tests/test_high_level.py index 67216a06d..0ae8b0fb0 100644 --- a/reproject/spherical_intersect/tests/test_high_level.py +++ b/reproject/spherical_intersect/tests/test_high_level.py @@ -1,6 +1,7 @@ # Licensed under a 3-clause BSD style license - see LICENSE.rst import warnings + import numpy as np import pytest from astropy.io import fits diff --git a/reproject/tests/test_high_level.py b/reproject/tests/test_high_level.py index d5bf0bb60..ddbc7bc48 100644 --- a/reproject/tests/test_high_level.py +++ b/reproject/tests/test_high_level.py @@ -8,7 +8,7 @@ from astropy.utils.data import get_pkg_data_filename from astropy.wcs import WCS -from .. import reproject_exact, reproject_interp, reproject_adaptive +from .. import reproject_adaptive, reproject_exact, reproject_interp # TODO: add reference comparisons diff --git a/reproject/tests/test_utils.py b/reproject/tests/test_utils.py index c4463137f..1e025b3c2 100644 --- a/reproject/tests/test_utils.py +++ b/reproject/tests/test_utils.py @@ -1,9 +1,9 @@ import numpy as np import pytest from astropy.io import fits +from astropy.nddata import NDData from astropy.utils.data import get_pkg_data_filename from astropy.wcs import WCS -from astropy.nddata import NDData from reproject.utils import parse_input_data, parse_input_shape, parse_output_projection diff --git a/reproject/utils.py b/reproject/utils.py index ff1e542ab..9a11d08fa 100644 --- a/reproject/utils.py +++ b/reproject/utils.py @@ -1,6 +1,5 @@ -import numpy as np - import astropy.nddata +import numpy as np from astropy.io import fits from astropy.io.fits import CompImageHDU, HDUList, Header, ImageHDU, PrimaryHDU from astropy.wcs import WCS diff --git a/setup.cfg b/setup.cfg index 3a1c41e21..cef474a4c 100644 --- a/setup.cfg +++ b/setup.cfg @@ -94,17 +94,5 @@ exclude_lines = # Don't complain about IPython completion helper def _ipython_key_completions_ -[isort] -line_length = 100 -not_skip = __init__.py -sections = FUTURE, STDLIB, THIRDPARTY, ASTROPY, FIRSTPARTY, LOCALFOLDER -default_section = THIRDPARTY -known_first_party = reproject -multi_line_output = 0 -balanced_wrapping = True -include_trailing_comma = False -length_sort = False -length_sort_stdlib = True - [flake8] max-line-length = 100 From 36c2a13000004bc47391a93e8b782f0c32179fa4 Mon Sep 17 00:00:00 2001 From: Stuart Mumford Date: Mon, 5 Sep 2022 14:24:55 +0100 Subject: [PATCH 006/366] run pyupgrade --- .pre-commit-config.yaml | 12 ++++++------ docs/conf.py | 9 ++++----- reproject/adaptive/core.py | 1 - reproject/healpix/utils.py | 2 +- reproject/mosaicking/wcs_helpers.py | 2 +- reproject/utils.py | 2 +- 6 files changed, 13 insertions(+), 15 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index a10f5dc9a..7a30502cf 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -17,12 +17,12 @@ repos: - id: isort name: isort (python) - # - repo: https://github.com/asottile/pyupgrade - # rev: v2.34.0 - # hooks: - # - id: pyupgrade - # args: ["--py38-plus"] - # exclude: ".*(extern.*|_parsetab.py|_lextab.py)$" + - repo: https://github.com/asottile/pyupgrade + rev: v2.34.0 + hooks: + - id: pyupgrade + args: ["--py38-plus"] + exclude: ".*(extern.*|_parsetab.py|_lextab.py)$" # We list the warnings/errors to check for here rather than in setup.cfg because # we don't want these options to apply whenever anyone calls flake8 from the diff --git a/docs/conf.py b/docs/conf.py index b6a7ab58b..25ee35a03 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Licensed under a 3-clause BSD style license - see LICENSE.rst # # Astropy documentation build configuration file. @@ -65,7 +64,7 @@ # This does not *have* to match the package name, but typically does project = setup_cfg['name'] author = setup_cfg['author'] -copyright = '{0}, {1}'.format( +copyright = '{}, {}'.format( datetime.datetime.now().year, setup_cfg['author']) # The version info for the project you're documenting, acts as replacement for @@ -119,7 +118,7 @@ # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". -html_title = '{0} v{1}'.format(project, release) +html_title = f'{project} v{release}' # Output file base name for HTML help builder. htmlhelp_basename = project + 'doc' @@ -129,7 +128,7 @@ # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass [howto/manual]). -latex_documents = [('index', project + '.tex', project + u' Documentation', +latex_documents = [('index', project + '.tex', project + ' Documentation', author, 'manual')] @@ -137,7 +136,7 @@ # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). -man_pages = [('index', project.lower(), project + u' Documentation', +man_pages = [('index', project.lower(), project + ' Documentation', [author], 1)] diff --git a/reproject/adaptive/core.py b/reproject/adaptive/core.py index e8c7d5758..04c7e9976 100644 --- a/reproject/adaptive/core.py +++ b/reproject/adaptive/core.py @@ -1,5 +1,4 @@ # Licensed under a 3-clause BSD style license - see LICENSE.rst -from __future__ import division, print_function, absolute_import, unicode_literals import numpy as np diff --git a/reproject/healpix/utils.py b/reproject/healpix/utils.py index 117e0df45..8c35110bc 100644 --- a/reproject/healpix/utils.py +++ b/reproject/healpix/utils.py @@ -21,7 +21,7 @@ def parse_coord_system(system): else: system_new = frame_transform_graph.lookup_name(system) if system_new is None: - raise ValueError("Could not determine frame for system={}".format(system)) + raise ValueError(f"Could not determine frame for system={system}") else: return system_new() diff --git a/reproject/mosaicking/wcs_helpers.py b/reproject/mosaicking/wcs_helpers.py index 58e44a615..399411ee0 100644 --- a/reproject/mosaicking/wcs_helpers.py +++ b/reproject/mosaicking/wcs_helpers.py @@ -83,7 +83,7 @@ def find_optimal_celestial_wcs(input_data, frame=None, auto_rotate=False, for shape, wcs in input_shapes: if len(shape) != 2: - raise ValueError("Input data is not 2-dimensional (got shape {!r})".format(shape)) + raise ValueError(f"Input data is not 2-dimensional (got shape {shape!r})") if wcs.naxis != 2: raise ValueError("Input WCS is not 2-dimensional") diff --git a/reproject/utils.py b/reproject/utils.py index 9a11d08fa..d063badef 100644 --- a/reproject/utils.py +++ b/reproject/utils.py @@ -108,7 +108,7 @@ def parse_output_projection(output_projection, shape_out=None, output_array=None if isinstance(output_projection, Header): wcs_out = WCS(output_projection) try: - shape_out = [output_projection['NAXIS{}'.format(i + 1)] + shape_out = [output_projection[f'NAXIS{i + 1}'] for i in range(output_projection['NAXIS'])][::-1] except KeyError: if shape_out is None: From c32685f2835ca1fbc127a000212b39cd2b7e6094 Mon Sep 17 00:00:00 2001 From: Stuart Mumford Date: Mon, 5 Sep 2022 14:25:33 +0100 Subject: [PATCH 007/366] Configure pre-commit.ci --- .pre-commit-config.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 7a30502cf..34439a888 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -63,3 +63,6 @@ repos: "E101,W191,E201,E202,W291,W292,W293,W391,E111,E112,E113,E30,E502,E722,E901,E902,E999,F822,F823", ] exclude: ".*(data.*|extern.*|cextern)$" + +ci: + autofix_prs: false From b9f384aeca58fccc278dc8023dcfd240edf4b169 Mon Sep 17 00:00:00 2001 From: Stuart Mumford Date: Mon, 5 Sep 2022 14:29:50 +0100 Subject: [PATCH 008/366] Add release instructions --- RELEASE_INSTRUCTIONS.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 RELEASE_INSTRUCTIONS.md diff --git a/RELEASE_INSTRUCTIONS.md b/RELEASE_INSTRUCTIONS.md new file mode 100644 index 000000000..37efcf733 --- /dev/null +++ b/RELEASE_INSTRUCTIONS.md @@ -0,0 +1,21 @@ +Making a New Reproject Release +============================== + +A new release of reproject is almost fully automated. +As a mantainer it should be nice and simple to do, especially if all merged PRs +have nice titles and are correctly labelled. + +Here is the process to follow to make a new release: + +* Go through all the PRs since the last release, make sure they have + descriptive titles (these will become the changelog entry) and are labelled + correctly. +* Go to the GitHub releases interface and draft a new release, tags should drop + the trailing `.0` for historical reasons on major releases. +* Use the GitHub autochange log generator, this should use the configuration in + `.github/release.yml` to make headings based on labels. +* Edit the draft release notes as required, particularly to call out major + changes at the top. +* Publish the release. +* Have a beverage of your choosing. (Note the wheels take a very long time to + build). From 10163e98474c930b2e4e483c477c1e7624f73718 Mon Sep 17 00:00:00 2001 From: Stuart Mumford Date: Mon, 5 Sep 2022 14:30:56 +0100 Subject: [PATCH 009/366] Change tox to use pre-commit. --- tox.ini | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/tox.ini b/tox.ini index 09684c6cd..cae65cd5a 100644 --- a/tox.ini +++ b/tox.ini @@ -65,7 +65,9 @@ commands = [testenv:codestyle] skip_install = true -changedir = . -description = check code style with flake8 -deps = flake8 -commands = flake8 reproject --count +description = Run all style and file checks with pre-commit +deps = + pre-commit +commands = + pre-commit install-hooks + pre-commit run --color always --all-files --show-diff-on-failure From f15efd0a22c65253740760e2659b08f16aa3911a Mon Sep 17 00:00:00 2001 From: Stuart Mumford Date: Mon, 5 Sep 2022 14:34:33 +0100 Subject: [PATCH 010/366] Remove codestyle job from actions now pre-commit.ci is running it --- .github/workflows/ci_workflows.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/ci_workflows.yml b/.github/workflows/ci_workflows.yml index ba915ab50..adee4ca01 100644 --- a/.github/workflows/ci_workflows.yml +++ b/.github/workflows/ci_workflows.yml @@ -14,7 +14,6 @@ jobs: uses: OpenAstronomy/github-actions-workflows/.github/workflows/tox.yml@v1 with: envs: | - - linux: codestyle - macos: py38-test-oldestdeps - macos: py39-test - macos: py310-test From e703840064a2dcdf139e10e11834a1549624c086 Mon Sep 17 00:00:00 2001 From: Stuart Mumford Date: Mon, 5 Sep 2022 14:35:33 +0100 Subject: [PATCH 011/366] Exclude FITS files from some pre-commit --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 34439a888..38f15b5e4 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -7,9 +7,9 @@ repos: - id: check-yaml - id: debug-statements - id: end-of-file-fixer - exclude: ".*(data.*|extern.*|licenses.*|_parsetab.py)$" + exclude: ".*(data.*|extern.*|licenses.*|.*.fits)$" - id: trailing-whitespace - exclude: ".*(data.*|extern.*|licenses.*|_parsetab.py|test_cds.py)$" + exclude: ".*(data.*|extern.*|licenses.*|.*.fits)$" - repo: https://github.com/pycqa/isort rev: 5.10.1 From a659a260bdd7635cddc8f33c4ea04a3b6d8c1f84 Mon Sep 17 00:00:00 2001 From: Stuart Mumford Date: Mon, 5 Sep 2022 15:33:39 +0100 Subject: [PATCH 012/366] Down with setup.py --- pyproject.toml | 23 +++++++------- setup.cfg | 3 ++ setup.py | 81 -------------------------------------------------- 3 files changed, 16 insertions(+), 91 deletions(-) delete mode 100755 setup.py diff --git a/pyproject.toml b/pyproject.toml index f4425af49..c31477676 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,17 +7,20 @@ requires = ["setuptools", "cython==0.29.21"] build-backend = 'setuptools.build_meta' +[tool.setuptools_scm] +write_to = "reproject/version.py" + [tool.cibuildwheel] skip = "cp36-* pp* *-musllinux* cp310-win32" [tool.isort] - extend_skip_glob = [ - "docs/*", - "setup.py"] - line_length = 100 - known_third_party = ["astropy"] - known_first_party = ["reproject"] - multi_line_output = 4 - group_by_package = true - indented_import_headings = false - length_sort_sections = ["future", "stdlib"] +extend_skip_glob = [ + "docs/*", + "setup.py"] +line_length = 100 +known_third_party = ["astropy"] +known_first_party = ["reproject"] +multi_line_output = 4 +group_by_package = true +indented_import_headings = false +length_sort_sections = ["future", "stdlib"] diff --git a/setup.cfg b/setup.cfg index cef474a4c..0d05072e8 100644 --- a/setup.cfg +++ b/setup.cfg @@ -44,6 +44,9 @@ reproject.mosaicking.tests = reference/* reproject.spherical_intersect = overlapArea.h, reproject_slice_c.h, mNaN.h reproject.tests = data/* +[extension-helpers] +use_extension_helpers = true + [tool:pytest] minversion = 3.1 testpaths = "reproject" "docs" diff --git a/setup.py b/setup.py deleted file mode 100755 index 0ac3c8f00..000000000 --- a/setup.py +++ /dev/null @@ -1,81 +0,0 @@ -#!/usr/bin/env python -# Licensed under a 3-clause BSD style license - see LICENSE.rst - -# NOTE: The configuration for the package, including the name, version, and -# other information are set in the setup.cfg file. - -import os -import sys - -from setuptools import setup - -from extension_helpers import get_extensions - - -# First provide helpful messages if contributors try and run legacy commands -# for tests or docs. - -TEST_HELP = """ -Note: running tests is no longer done using 'python setup.py test'. Instead -you will need to run: - - tox -e test - -If you don't already have tox installed, you can install it with: - - pip install tox - -If you only want to run part of the test suite, you can also use pytest -directly with:: - - pip install -e .[test] - pytest - -For more information, see: - - http://docs.astropy.org/en/latest/development/testguide.html#running-tests -""" - -if 'test' in sys.argv: - print(TEST_HELP) - sys.exit(1) - -DOCS_HELP = """ -Note: building the documentation is no longer done using -'python setup.py build_docs'. Instead you will need to run: - - tox -e build_docs - -If you don't already have tox installed, you can install it with: - - pip install tox - -You can also build the documentation with Sphinx directly using:: - - pip install -e .[docs] - cd docs - make html - -For more information, see: - - http://docs.astropy.org/en/latest/install.html#builddocs -""" - -if 'build_docs' in sys.argv or 'build_sphinx' in sys.argv: - print(DOCS_HELP) - sys.exit(1) - -VERSION_TEMPLATE = """ -# 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 = '{version}' -""".lstrip() - -setup(use_scm_version={'write_to': os.path.join('reproject', 'version.py'), - 'write_to_template': VERSION_TEMPLATE}, - ext_modules=get_extensions()) From d6e01fd215776be8d37aa8117502019f7acc3420 Mon Sep 17 00:00:00 2001 From: Stuart Mumford Date: Mon, 5 Sep 2022 15:36:29 +0100 Subject: [PATCH 013/366] Clean out old astropy init --- reproject/__init__.py | 18 +++++------------- reproject/_astropy_init.py | 25 ------------------------- 2 files changed, 5 insertions(+), 38 deletions(-) delete mode 100644 reproject/_astropy_init.py diff --git a/reproject/__init__.py b/reproject/__init__.py index 68216c3e5..2cef31f4c 100644 --- a/reproject/__init__.py +++ b/reproject/__init__.py @@ -2,16 +2,8 @@ """ Astropy affiliated package for image reprojection (resampling). """ - -# Affiliated packages may add whatever they like to this file, but -# should keep this content at the top. -# ---------------------------------------------------------------------------- -from ._astropy_init import * # noqa - -# ---------------------------------------------------------------------------- - -if not _ASTROPY_SETUP_: # noqa - from .adaptive import reproject_adaptive # noqa - from .healpix import reproject_from_healpix, reproject_to_healpix # noqa - from .interpolation import reproject_interp # noqa - from .spherical_intersect import reproject_exact # noqa +from .adaptive import reproject_adaptive # noqa +from .healpix import reproject_from_healpix, reproject_to_healpix # noqa +from .interpolation import reproject_interp # noqa +from .spherical_intersect import reproject_exact # noqa +from .version import __version__ diff --git a/reproject/_astropy_init.py b/reproject/_astropy_init.py deleted file mode 100644 index fa37a6371..000000000 --- a/reproject/_astropy_init.py +++ /dev/null @@ -1,25 +0,0 @@ -# Licensed under a 3-clause BSD style license - see LICENSE.rst - -__all__ = ['__version__'] - -# this indicates whether or not we are in the package's setup.py -try: - _ASTROPY_SETUP_ -except NameError: - import builtins - builtins._ASTROPY_SETUP_ = False - -try: - from .version import version as __version__ -except ImportError: - __version__ = '' - - -if not _ASTROPY_SETUP_: # noqa - import os - - # Create the test function for self test - from astropy.tests.runner import TestRunner - test = TestRunner.make_test_runner_in(os.path.dirname(__file__)) - test.__test__ = False - __all__ += ['test'] From f7fece87536ce3621f751bba5fc190ed3bb2d5cd Mon Sep 17 00:00:00 2001 From: Stuart Mumford Date: Mon, 5 Sep 2022 16:37:22 +0100 Subject: [PATCH 014/366] Change version scheme [ci skip] --- RELEASE_INSTRUCTIONS.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/RELEASE_INSTRUCTIONS.md b/RELEASE_INSTRUCTIONS.md index 37efcf733..527b7b6f8 100644 --- a/RELEASE_INSTRUCTIONS.md +++ b/RELEASE_INSTRUCTIONS.md @@ -10,8 +10,9 @@ Here is the process to follow to make a new release: * Go through all the PRs since the last release, make sure they have descriptive titles (these will become the changelog entry) and are labelled correctly. -* Go to the GitHub releases interface and draft a new release, tags should drop - the trailing `.0` for historical reasons on major releases. +* Go to the GitHub releases interface and draft a new release, new tags should + drop include the trailing `.0` on major releases. (Releases prior to 0.10.0 + didn't.) * Use the GitHub autochange log generator, this should use the configuration in `.github/release.yml` to make headings based on labels. * Edit the draft release notes as required, particularly to call out major From c2bc9e5a2cf41c619211837919d2ab3641b919f0 Mon Sep 17 00:00:00 2001 From: Stuart Mumford Date: Mon, 5 Sep 2022 18:49:12 +0100 Subject: [PATCH 015/366] typo [ci skip] --- RELEASE_INSTRUCTIONS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RELEASE_INSTRUCTIONS.md b/RELEASE_INSTRUCTIONS.md index 527b7b6f8..f4e5b7694 100644 --- a/RELEASE_INSTRUCTIONS.md +++ b/RELEASE_INSTRUCTIONS.md @@ -11,7 +11,7 @@ Here is the process to follow to make a new release: descriptive titles (these will become the changelog entry) and are labelled correctly. * Go to the GitHub releases interface and draft a new release, new tags should - drop include the trailing `.0` on major releases. (Releases prior to 0.10.0 + include the trailing `.0` on major releases. (Releases prior to 0.10.0 didn't.) * Use the GitHub autochange log generator, this should use the configuration in `.github/release.yml` to make headings based on labels. From 07b565a869cec8cc790fcc8bbf6b3ccc55a7ea2c Mon Sep 17 00:00:00 2001 From: Sayfog Date: Thu, 2 Jan 2020 23:38:37 +1100 Subject: [PATCH 016/366] Add support for reprojection in multiple blocks Quick and dirty implementation of blocked reproject_interp - issue #37 Revert to original reproject source Blocked wrapper that works with non-Healpix reprojection functions broken out reprojrection from block generation in prep for multiprocessing Completed functionality of block reproject wrapper for non-healpix methods Formatting changes to match original source Added memory usage instrumentation Fix memory leak from storing futures in multiprocessing option Fixed process pool args parsing and switched to dicts Removed test code from blocked helper func Scaffold seamless wrapper insertion in reproj_interpolate as per pull request #214 Remove errorenously added testing script Integrated blocked reprojection in interpolate function Removed profiling imports from utils Formatting fixes Formatting fixes PEP8 tox edition Fixes for the blocked to match non-blocked behaviour Fixes for wcsapi input testcases Fix WCS slicing axis incorrectly swapped Add naive tests for blocked and parallel reproj codestyle fixes for blocked test Fix issues blocked reprojection with footprint Style fixes for return fp blocked fixes Update blocked corner case test to be more useful Revert "Squashed commit of the following:" This reverts commit f554ce976c2a55bb529f180757445d417eb267ad. Revert "Revert "Squashed commit of the following:"" This reverts commit fa384e4c4b883713950867e3bca0ba9ad124ff4b. Manually re-add blocked code to reproj interp Manually fix up blocked tests Fix blocked tests to use get_pkg_data function Fix codestyle issues Address core comments made by @keflavich --- reproject/interpolation/high_level.py | 43 ++++++--- reproject/interpolation/tests/test_core.py | 51 ++++++++++ reproject/utils.py | 104 +++++++++++++++++++++ 3 files changed, 187 insertions(+), 11 deletions(-) diff --git a/reproject/interpolation/high_level.py b/reproject/interpolation/high_level.py index 88ece106b..b494d660e 100644 --- a/reproject/interpolation/high_level.py +++ b/reproject/interpolation/high_level.py @@ -1,8 +1,8 @@ # Licensed under a 3-clause BSD style license - see LICENSE.rst - +import os from astropy.utils import deprecated_renamed_argument -from ..utils import parse_input_data, parse_output_projection +from ..utils import parse_input_data, parse_output_projection, reproject_blocked from .core import _reproject_full __all__ = ['reproject_interp'] @@ -17,16 +17,12 @@ @deprecated_renamed_argument('independent_celestial_slices', None, since='0.6') def reproject_interp(input_data, output_projection, shape_out=None, hdu_in=0, order='bilinear', independent_celestial_slices=False, - output_array=None, return_footprint=True, - roundtrip_coords=True): + output_array=None, return_footprint=True, output_footprint=None, + block_size=None, parallel=False, roundtrip_coords=True): """ Reproject data to a new projection using interpolation (this is typically the fastest way to reproject an image). - The output pixel grid is transformed to the input pixel grid, and the - data values in ``input_data`` interpolated on to these coordinates to get - the reprojected data on the output grid. - Parameters ---------- input_data @@ -69,6 +65,11 @@ def reproject_interp(input_data, output_projection, shape_out=None, hdu_in=0, extremely large files. return_footprint : bool Whether to return the footprint in addition to the output array. + block_size : None or tuple of (int, int) + If not none, a blocked projection will be performed where the output space is + reprojected to one block at a time, this is useful for memory limited scenarios + such as dealing with very large arrays or high resolution output spaces. + parallel : bool or int roundtrip_coords : bool Whether to verify that coordinate transformations are defined in both directions. @@ -90,6 +91,26 @@ def reproject_interp(input_data, output_projection, shape_out=None, hdu_in=0, if isinstance(order, str): order = ORDER[order] - return _reproject_full(array_in, wcs_in, wcs_out, shape_out=shape_out, - order=order, array_out=output_array, - return_footprint=return_footprint, roundtrip_coords=roundtrip_coords) + # if either of these are not default, it means a blocked method must be used + if block_size is not None or parallel is not False: + # if parallel is set but block size isn't, we'll choose + # block size so each thread gets one block each + if parallel is not False and block_size is None: + block_size = shape_out.copy() + # each thread gets an equal sized strip of output area to process + block_size[0] = shape_out[0] // os.cpu_count() + + # given we have cases where modern system have many cpu cores some sanity clamping is + # to avoid 0 length block sizes when num_cpu_cores is greater than the side of the image + for dim_idx in range(min(len(shape_out), 2)): + if block_size[dim_idx] == 0: + block_size[dim_idx] = shape_out[dim_idx] + + return reproject_blocked(_reproject_full, array_in=array_in, wcs_in=wcs_in, wcs_out=wcs_out, + shape_out=shape_out, output_array=output_array, parallel=parallel, + block_size=block_size, return_footprint=return_footprint, + output_footprint=output_footprint) + else: + return _reproject_full(array_in, wcs_in, wcs_out, shape_out=shape_out, + order=order, array_out=output_array, + return_footprint=return_footprint, roundtrip_coords=roundtrip_coords) diff --git a/reproject/interpolation/tests/test_core.py b/reproject/interpolation/tests/test_core.py index defe956df..5223800df 100644 --- a/reproject/interpolation/tests/test_core.py +++ b/reproject/interpolation/tests/test_core.py @@ -581,3 +581,54 @@ def test_identity_with_offset(roundtrip_coords): expected = np.pad(array_in, 1, 'constant', constant_values=np.nan) assert_allclose(expected, array_out, atol=1e-10) + + +@pytest.mark.parametrize('parallel', [True, 0, 2, False]) +@pytest.mark.parametrize('block_size', [[10, 10], [500, 500], [500, 100], None]) +def test_blocked_against_single(parallel, block_size): + hdu1 = fits.open(get_pkg_data_filename('galactic_center/gc_2mass_k.fits'))[0] + hdu2 = fits.open(get_pkg_data_filename('galactic_center/gc_msx_e.fits'))[0] + + array_reference, footprint_reference = reproject_interp(hdu2, hdu1.header, + parallel=False, block_size=None) + + array_test, footprint_test = reproject_interp(hdu2, hdu1.header, + parallel=parallel, block_size=block_size) + + np.testing.assert_allclose(array_test, array_reference, equal_nan=True) + np.testing.assert_allclose(footprint_test, footprint_reference, equal_nan=True) + + +def test_blocked_corner_cases(): + + """ + When doing blocked there are a few checks designed to sanity clamp/preserve + values. Even though the blocking process only tiles in a 2d manner 3d information + about the image needs to be preserved and transformed correctly. Additonally + when automatically determining block size based on CPU cores zeros can appear on + machines where num_cores > x or y dim of output image. So make sure it correctly + functions when 0 block size goes in + """ + + # Read in the input cube + hdu_in = fits.open( + get_pkg_data_filename('data/equatorial_3d.fits', package='reproject.tests'))[0] + + # Define the output header - this should be the same for all versions of + # this test to make sure we can use a single reference file. + header_out = hdu_in.header.copy() + header_out['NAXIS1'] = 10 + header_out['NAXIS2'] = 9 + header_out['CTYPE1'] = 'GLON-SIN' + header_out['CTYPE2'] = 'GLAT-SIN' + header_out['CRVAL1'] = 163.16724 + header_out['CRVAL2'] = -15.777405 + header_out['CRPIX1'] = 6 + header_out['CRPIX2'] = 5 + + array_reference = reproject_interp(hdu_in, header_out, return_footprint=False) + + array_test = reproject_interp(hdu_in, header_out, parallel=True, + block_size=[0, 4], return_footprint=False) + + np.testing.assert_allclose(array_test, array_reference, equal_nan=True, verbose=True) diff --git a/reproject/utils.py b/reproject/utils.py index d063badef..dce3e6cc6 100644 --- a/reproject/utils.py +++ b/reproject/utils.py @@ -1,9 +1,13 @@ +from concurrent import futures +import numpy as np import astropy.nddata import numpy as np from astropy.io import fits from astropy.io.fits import CompImageHDU, HDUList, Header, ImageHDU, PrimaryHDU from astropy.wcs import WCS from astropy.wcs.wcsapi import BaseHighLevelWCS +from astropy.wcs.wcsapi import SlicedLowLevelWCS +from astropy.wcs.wcsapi.high_level_wcs_wrapper import HighLevelWCSWrapper __all__ = ['parse_input_data', 'parse_input_shape', 'parse_input_weights', 'parse_output_projection'] @@ -133,3 +137,103 @@ def parse_output_projection(output_projection, shape_out=None, output_array=None raise ValueError("The shape of the output image should not be an " "empty tuple") return wcs_out, shape_out + + +def _block(reproject_func, array_in, wcs_in, wcs_out_sub, shape_out, i_range, j_range, + return_footprint): + # i and j range must be passed through for multiprocessing to know where to reinsert patches + result = reproject_func(array_in, wcs_in, wcs_out_sub, + shape_out=shape_out, return_footprint=return_footprint) + + res_arr = None + res_fp = None + + if return_footprint: + res_arr, res_fp = result + else: + res_arr = result + + return {'i': i_range, 'j': j_range, 'res_arr': res_arr, 'res_fp': res_fp} + + +def reproject_blocked(reproject_func, array_in, wcs_in, shape_out, wcs_out, block_size, + output_array=None, + return_footprint=True, output_footprint=None, parallel=True): + if output_array is None: + output_array = np.zeros(shape_out, dtype=float) + if output_footprint is None and return_footprint: + output_footprint = np.zeros(shape_out, dtype=float) + + # setup variables needed for multiprocessing if required + proc_pool = None + blocks_futures = [] + + if parallel or type(parallel) is int: + if type(parallel) is int and parallel > 0: + proc_pool = futures.ProcessPoolExecutor(max_workers=parallel) + else: + proc_pool = futures.ProcessPoolExecutor() + + sequential_blocks_done = 0 + for imin in range(0, output_array.shape[0], block_size[0]): + imax = min(imin + block_size[0], output_array.shape[0]) + for jmin in range(0, output_array.shape[1], block_size[1]): + jmax = min(jmin + block_size[1], output_array.shape[1]) + shape_out_sub = (imax - imin, jmax - jmin) + # if the output has more than two dims, just append them on the end of the + # shape to it still matches the base WCS + for dim in range(2, len(output_array.shape)): + shape_out_sub = shape_out_sub + (output_array.shape[dim],) + + slices = [slice(imin, imax), slice(jmin, jmax)] + wcs_out_sub = HighLevelWCSWrapper(SlicedLowLevelWCS(wcs_out, slices=slices)) + + if proc_pool is None: + # if sequential input data and reinsert block into main array immediately + completed_block = _block(reproject_func=reproject_func, array_in=array_in, + wcs_in=wcs_in, + wcs_out_sub=wcs_out_sub, shape_out=shape_out_sub, + return_footprint=return_footprint, + j_range=(jmin, jmax), i_range=(imin, imax)) + + output_array[imin:imax, jmin:jmax] = completed_block['res_arr'][:] + if return_footprint: + output_footprint[imin:imax, jmin:jmax] = completed_block['res_fp'][:] + + sequential_blocks_done += 1 + else: + # if parallel just submit all work items and move on to waiting for them to be done + future = proc_pool.submit(_block, reproject_func=reproject_func, array_in=array_in, + wcs_in=wcs_in, wcs_out_sub=wcs_out_sub, + shape_out=shape_out_sub, + return_footprint=return_footprint, j_range=(jmin, jmax), + i_range=(imin, imax)) + blocks_futures.append(future) + + # If a parallel implementation is being used that means the + # blocks have not been reassembled yet and must be done now + if proc_pool is not None: + completed_future_count = 0 + for completed_future in futures.as_completed(blocks_futures): + completed_block = completed_future.result() + i_range = completed_block['i'] + j_range = completed_block['j'] + output_array[i_range[0]:i_range[1], j_range[0]:j_range[1]] \ + = completed_block['res_arr'][:] + + if return_footprint: + footprint_block = completed_block['res_fp'][:] + output_footprint[i_range[0]:i_range[1], j_range[0]:j_range[1]] = footprint_block + + completed_future_count += 1 + idx = blocks_futures.index(completed_future) + # ensure memory used by returned data is freed + completed_future._result = None + del blocks_futures[idx], completed_future + proc_pool.shutdown() + del blocks_futures + + if return_footprint: + return output_array, output_footprint + else: + return output_array From a959111eda6e1a695e9e3f8cbfcd3f5557c04a66 Mon Sep 17 00:00:00 2001 From: Alistair Symonds Date: Sat, 27 Aug 2022 13:15:21 +1000 Subject: [PATCH 017/366] Work around warnings causing pytest fails with oldestdeps --- reproject/interpolation/tests/test_core.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/reproject/interpolation/tests/test_core.py b/reproject/interpolation/tests/test_core.py index 5223800df..84231f875 100644 --- a/reproject/interpolation/tests/test_core.py +++ b/reproject/interpolation/tests/test_core.py @@ -586,6 +586,25 @@ def test_identity_with_offset(roundtrip_coords): @pytest.mark.parametrize('parallel', [True, 0, 2, False]) @pytest.mark.parametrize('block_size', [[10, 10], [500, 500], [500, 100], None]) def test_blocked_against_single(parallel, block_size): + # the warning import and ignore is needed to keep pytest happy when running with + # older versions of astropy which don't have this fix: + # https://github.com/astropy/astropy/pull/12844 + # All the warning code should be removed when old version no longer being used + import warnings + warnings.simplefilter('ignore', category=FITSFixedWarning) + if (block_size == [10, 10]): + # this one is needed to avoid the following warning from when the np.as_strided() is + # called in wcs_utils.unbroadcast(), only shows up with py3.8, numpy1.17, astropy 4.0.*: + # DeprecationWarning: Numpy has detected that you (may be) writing to an array with + # overlapping memory from np.broadcast_arrays. If this is intentional + # set the WRITEABLE flag True or make a copy immediately before writing. + # We do call as_strided with writeable=True as it recommends and only shows up with the 10px + # testcase so assuming a numpy bug in the detection code which was fixed in later version. + # The pixel values all still match in the end, only shows up due to pytest clearing + # the standard python warning filters by default and failing as the warnings are now + # treated as the exceptions they're implemented on + warnings.simplefilter('ignore', category=DeprecationWarning) + hdu1 = fits.open(get_pkg_data_filename('galactic_center/gc_2mass_k.fits'))[0] hdu2 = fits.open(get_pkg_data_filename('galactic_center/gc_msx_e.fits'))[0] @@ -609,6 +628,9 @@ def test_blocked_corner_cases(): machines where num_cores > x or y dim of output image. So make sure it correctly functions when 0 block size goes in """ + # same reason as test above for FITSFixedWarning + import warnings + warnings.simplefilter('ignore', category=FITSFixedWarning) # Read in the input cube hdu_in = fits.open( From 368bfc9b2846ac344bd91dfcd15be276252150e6 Mon Sep 17 00:00:00 2001 From: Alistair Symonds Date: Sat, 27 Aug 2022 15:48:14 +1000 Subject: [PATCH 018/366] Use catch_warnings() manager for blocked workaround --- reproject/interpolation/tests/test_core.py | 39 ++++++++++++++-------- 1 file changed, 26 insertions(+), 13 deletions(-) diff --git a/reproject/interpolation/tests/test_core.py b/reproject/interpolation/tests/test_core.py index 84231f875..c2beea905 100644 --- a/reproject/interpolation/tests/test_core.py +++ b/reproject/interpolation/tests/test_core.py @@ -586,13 +586,24 @@ def test_identity_with_offset(roundtrip_coords): @pytest.mark.parametrize('parallel', [True, 0, 2, False]) @pytest.mark.parametrize('block_size', [[10, 10], [500, 500], [500, 100], None]) def test_blocked_against_single(parallel, block_size): + + # Ensure when we break a reprojection down into multiple discrete blocks + # it has the same result as if all pixels where reprejcted at once + + hdu1 = fits.open(get_pkg_data_filename('galactic_center/gc_2mass_k.fits'))[0] + hdu2 = fits.open(get_pkg_data_filename('galactic_center/gc_msx_e.fits'))[0] + array_test = None + footprint_test = None + # the warning import and ignore is needed to keep pytest happy when running with # older versions of astropy which don't have this fix: # https://github.com/astropy/astropy/pull/12844 # All the warning code should be removed when old version no longer being used + # Using context manager ensure only blocked function has them ignored import warnings - warnings.simplefilter('ignore', category=FITSFixedWarning) - if (block_size == [10, 10]): + with warnings.catch_warnings(): + warnings.simplefilter('ignore', category=FITSFixedWarning) + # this one is needed to avoid the following warning from when the np.as_strided() is # called in wcs_utils.unbroadcast(), only shows up with py3.8, numpy1.17, astropy 4.0.*: # DeprecationWarning: Numpy has detected that you (may be) writing to an array with @@ -603,17 +614,15 @@ def test_blocked_against_single(parallel, block_size): # The pixel values all still match in the end, only shows up due to pytest clearing # the standard python warning filters by default and failing as the warnings are now # treated as the exceptions they're implemented on - warnings.simplefilter('ignore', category=DeprecationWarning) + if (block_size == [10, 10]): + warnings.simplefilter('ignore', category=DeprecationWarning) - hdu1 = fits.open(get_pkg_data_filename('galactic_center/gc_2mass_k.fits'))[0] - hdu2 = fits.open(get_pkg_data_filename('galactic_center/gc_msx_e.fits'))[0] + array_test, footprint_test = reproject_interp(hdu2, hdu1.header, + parallel=parallel, block_size=block_size) array_reference, footprint_reference = reproject_interp(hdu2, hdu1.header, parallel=False, block_size=None) - array_test, footprint_test = reproject_interp(hdu2, hdu1.header, - parallel=parallel, block_size=block_size) - np.testing.assert_allclose(array_test, array_reference, equal_nan=True) np.testing.assert_allclose(footprint_test, footprint_reference, equal_nan=True) @@ -628,9 +637,6 @@ def test_blocked_corner_cases(): machines where num_cores > x or y dim of output image. So make sure it correctly functions when 0 block size goes in """ - # same reason as test above for FITSFixedWarning - import warnings - warnings.simplefilter('ignore', category=FITSFixedWarning) # Read in the input cube hdu_in = fits.open( @@ -650,7 +656,14 @@ def test_blocked_corner_cases(): array_reference = reproject_interp(hdu_in, header_out, return_footprint=False) - array_test = reproject_interp(hdu_in, header_out, parallel=True, - block_size=[0, 4], return_footprint=False) + array_test = None + + # same reason as test above for FITSFixedWarning + import warnings + with warnings.catch_warnings(): + warnings.simplefilter('ignore', category=FITSFixedWarning) + + array_test = reproject_interp(hdu_in, header_out, parallel=True, + block_size=[0, 4], return_footprint=False) np.testing.assert_allclose(array_test, array_reference, equal_nan=True, verbose=True) From 85c9ffb12b4012fdd7a0b52106dc55c12e8584d0 Mon Sep 17 00:00:00 2001 From: Alistair Symonds Date: Fri, 2 Sep 2022 23:14:58 +1000 Subject: [PATCH 019/366] Add docstrings to parallel and error checking when parallel= 0 --- reproject/interpolation/high_level.py | 6 ++ reproject/interpolation/tests/test_core.py | 2 +- reproject/utils.py | 92 +++++++++++++++++++--- 3 files changed, 88 insertions(+), 12 deletions(-) diff --git a/reproject/interpolation/high_level.py b/reproject/interpolation/high_level.py index b494d660e..a8b681a15 100644 --- a/reproject/interpolation/high_level.py +++ b/reproject/interpolation/high_level.py @@ -1,5 +1,6 @@ # Licensed under a 3-clause BSD style license - see LICENSE.rst import os + from astropy.utils import deprecated_renamed_argument from ..utils import parse_input_data, parse_output_projection, reproject_blocked @@ -70,6 +71,11 @@ def reproject_interp(input_data, output_projection, shape_out=None, hdu_in=0, reprojected to one block at a time, this is useful for memory limited scenarios such as dealing with very large arrays or high resolution output spaces. parallel : bool or int + Flag for parallel implementation. If ``True``, a parallel implementation + is chosen, the number of processes selected automatically to be equal to + the number of logical CPUs detected on the machine. If ``False``, a + serial implementation is chosen. If the flag is a positive integer ``n`` + greater than one, a parallel implementation using ``n`` processes is chosen. roundtrip_coords : bool Whether to verify that coordinate transformations are defined in both directions. diff --git a/reproject/interpolation/tests/test_core.py b/reproject/interpolation/tests/test_core.py index c2beea905..4bf9bfa34 100644 --- a/reproject/interpolation/tests/test_core.py +++ b/reproject/interpolation/tests/test_core.py @@ -583,7 +583,7 @@ def test_identity_with_offset(roundtrip_coords): assert_allclose(expected, array_out, atol=1e-10) -@pytest.mark.parametrize('parallel', [True, 0, 2, False]) +@pytest.mark.parametrize('parallel', [True, 2, False]) @pytest.mark.parametrize('block_size', [[10, 10], [500, 500], [500, 100], None]) def test_blocked_against_single(parallel, block_size): diff --git a/reproject/utils.py b/reproject/utils.py index dce3e6cc6..f8c302a6e 100644 --- a/reproject/utils.py +++ b/reproject/utils.py @@ -1,12 +1,11 @@ from concurrent import futures -import numpy as np + import astropy.nddata import numpy as np from astropy.io import fits from astropy.io.fits import CompImageHDU, HDUList, Header, ImageHDU, PrimaryHDU from astropy.wcs import WCS -from astropy.wcs.wcsapi import BaseHighLevelWCS -from astropy.wcs.wcsapi import SlicedLowLevelWCS +from astropy.wcs.wcsapi import BaseHighLevelWCS, SlicedLowLevelWCS from astropy.wcs.wcsapi.high_level_wcs_wrapper import HighLevelWCSWrapper __all__ = ['parse_input_data', 'parse_input_shape', 'parse_input_weights', @@ -141,7 +140,31 @@ def parse_output_projection(output_projection, shape_out=None, output_array=None def _block(reproject_func, array_in, wcs_in, wcs_out_sub, shape_out, i_range, j_range, return_footprint): - # i and j range must be passed through for multiprocessing to know where to reinsert patches + """ + Implementation function that handles reprojecting subsets blocks of pixels + from an input image and holds metadata about where to reinsert when done. + + Parameters + ---------- + reproject_func + One the existing reproject functions implementing a reprojection algorithm + that that will be used be used to perform reprojection + array_in + Data following the same format as expected by underlying reproject_func, + expected to `~numpy.ndarray` when used from reproject_blocked() + wcs_in: `~astropy.wcs.WCS` + WCS object corresponding to array_in + wcs_out_sub: + Output WCS image will be projected to. Normally will correspond to subset of + total output image when used by repoject_blocked() + shape_out: + Passed to reproject_func() alongside WCS out to determine image size + i_range: + Passed through unmodified, used to determine where to reinsert block + j_range: + Passed through unmodified, used to determine where to reinsert block + """ + result = reproject_func(array_in, wcs_in, wcs_out_sub, shape_out=shape_out, return_footprint=return_footprint) @@ -159,6 +182,47 @@ def _block(reproject_func, array_in, wcs_in, wcs_out_sub, shape_out, i_range, j_ def reproject_blocked(reproject_func, array_in, wcs_in, shape_out, wcs_out, block_size, output_array=None, return_footprint=True, output_footprint=None, parallel=True): + """ + Implementation function that handles reprojecting subsets blocks of pixels + from an input image and holds metadata about where to reinsert when done. + + Parameters + ---------- + reproject_func + One the existing reproject functions implementing a reprojection algorithm + that that will be used be used to perform reprojection + array_in + Data following the same format as expected by underlying reproject_func, + expected to `~numpy.ndarray` when used from reproject_blocked() + wcs_in: `~astropy.wcs.WCS` + WCS object corresponding to array_in + shape_out: tuple + Passed to reproject_func() alongside WCS out to determine image size + wcs_out: `~astropy.wcs.WCS` + Output WCS image will be projected to. Normally will correspond to subset of + total output image when used by repoject_blocked() + block_size: tuple + The size of blocks in terms of output array pixels that each block will handle + reprojecting. Extending out from (0,0) coords positively, block sizes + are clamped to output space edges when a block would extend past edge + output_array : None or `~numpy.ndarray` + An array in which to store the reprojected data. This can be any numpy + array including a memory map, which may be helpful when dealing with + extremely large files. + return_footprint : bool + Whether to return the footprint in addition to the output array. + output_footprint : None or `~numpy.ndarray` + An array in which to store the footprint of reprojected data. This can be + any numpy array including a memory map, which may be helpful when dealing with + extremely large files. + parallel : bool or int + Flag for parallel implementation. If ``True``, a parallel implementation + is chosen, the number of processes selected automatically to be equal to + the number of logical CPUs detected on the machine. If ``False``, a + serial implementation is chosen. If the flag is a positive integer ``n`` + greater than one, a parallel implementation using ``n`` processes is chosen. + """ + if output_array is None: output_array = np.zeros(shape_out, dtype=float) if output_footprint is None and return_footprint: @@ -169,12 +233,18 @@ def reproject_blocked(reproject_func, array_in, wcs_in, shape_out, wcs_out, bloc blocks_futures = [] if parallel or type(parallel) is int: - if type(parallel) is int and parallel > 0: - proc_pool = futures.ProcessPoolExecutor(max_workers=parallel) + if type(parallel) is int: + if parallel <= 0: + raise ValueError("The number of processors to use must be strictly positive") + else: + proc_pool = futures.ProcessPoolExecutor(max_workers=parallel) else: proc_pool = futures.ProcessPoolExecutor() - sequential_blocks_done = 0 + # This will iterate over the output space, generating slices of that + # WCS and either processing and reinserting them immediately, + # or when doing parallel impl submit them to workers then wait and reinsert as + # the workers complete each block for imin in range(0, output_array.shape[0], block_size[0]): imax = min(imin + block_size[0], output_array.shape[0]) for jmin in range(0, output_array.shape[1], block_size[1]): @@ -200,7 +270,6 @@ def reproject_blocked(reproject_func, array_in, wcs_in, shape_out, wcs_out, bloc if return_footprint: output_footprint[imin:imax, jmin:jmax] = completed_block['res_fp'][:] - sequential_blocks_done += 1 else: # if parallel just submit all work items and move on to waiting for them to be done future = proc_pool.submit(_block, reproject_func=reproject_func, array_in=array_in, @@ -211,15 +280,16 @@ def reproject_blocked(reproject_func, array_in, wcs_in, shape_out, wcs_out, bloc blocks_futures.append(future) # If a parallel implementation is being used that means the - # blocks have not been reassembled yet and must be done now + # blocks have not been reassembled yet and must be done now as their + # block call completes in the worker processes if proc_pool is not None: completed_future_count = 0 for completed_future in futures.as_completed(blocks_futures): completed_block = completed_future.result() i_range = completed_block['i'] j_range = completed_block['j'] - output_array[i_range[0]:i_range[1], j_range[0]:j_range[1]] \ - = completed_block['res_arr'][:] + output_array[i_range[0]:i_range[1], j_range[0]:j_range[1]] = ( + completed_block['res_arr'][:]) if return_footprint: footprint_block = completed_block['res_fp'][:] From bffb4f18fa2591bab78482f7adfc8fe56b97f27e Mon Sep 17 00:00:00 2001 From: Sam Van Kooten Date: Thu, 20 Jan 2022 14:38:25 -0700 Subject: [PATCH 020/366] Make Gaussian kernel default for adaptive resampling While this kernel introduces some slight blurring of the output image, it significantly enhances the anti-aliasing properties that this algorithm touts. --- docs/celestial.rst | 16 ++++++++-------- reproject/adaptive/core.py | 5 +++-- reproject/adaptive/deforest.pyx | 2 +- reproject/adaptive/high_level.py | 10 +--------- reproject/adaptive/tests/test_core.py | 10 ++++++---- 5 files changed, 19 insertions(+), 24 deletions(-) diff --git a/docs/celestial.rst b/docs/celestial.rst index 403c42509..203dfbeac 100644 --- a/docs/celestial.rst +++ b/docs/celestial.rst @@ -205,14 +205,14 @@ kernel---see below.) The kernel used for interpolation and averaging can be controlled with a set of options. The ``kernel`` argument can be set to 'hann' or 'gaussian' to set the -function being used. The Hann window is the default, and the Gaussian window -improves anti-aliasing and photometric accuracy (or flux conservation, when the -flux-conserving mode is enabled) at the cost of blurring the output image by a -few pixels. The ``kernel_width`` argument sets the width of the Gaussian -kernel, in pixels, and is ignored for the Hann window. This width is measured -between the Gaussian's :math:`\pm 1 \sigma` points. The default value is 1.3 -for the Gaussian, chosen to minimize blurring without compromising accuracy. -Lower values may introduce photometric errors or leave input pixels +function being used. The Gaussian window is the default, as it provides better +anti-aliasing and photometric accuracy (or flux conservation, when the +flux-conserving mode is enabled), though at the cost of blurring the output +image by a few pixels. The ``kernel_width`` argument sets the width of the +Gaussian kernel, in pixels, and is ignored for the Hann window. This width is +measured between the Gaussian's :math:`\pm 1 \sigma` points. The default value +is 1.3 for the Gaussian, chosen to minimize blurring without compromising +accuracy. Lower values may introduce photometric errors or leave input pixels under-sampled, while larger values may improve anti-aliasing behavior but will increase blurring of the output image. Since the Gaussian function has infinite extent, it must be truncated. This is done by sampling within a region of diff --git a/reproject/adaptive/core.py b/reproject/adaptive/core.py index 04c7e9976..5d58112c3 100644 --- a/reproject/adaptive/core.py +++ b/reproject/adaptive/core.py @@ -30,7 +30,7 @@ def __call__(self, pixel_out): def _reproject_adaptive_2d(array, wcs_in, wcs_out, shape_out, return_footprint=True, center_jacobian=False, roundtrip_coords=True, conserve_flux=False, - kernel='Hann', kernel_width=1.3, + kernel='Gaussian', kernel_width=1.3, sample_region_width=4, boundary_mode='ignore', boundary_fill_value=0, boundary_ignore_threshold=0.5, @@ -112,7 +112,8 @@ def _reproject_adaptive_2d(array, wcs_in, wcs_out, shape_out, transformer = CoordinateTransformer(wcs_in, wcs_out, roundtrip_coords) map_coordinates(array_in, array_out, transformer, out_of_range_nan=True, - center_jacobian=center_jacobian, conserve_flux=conserve_flux, + center_jacobian=center_jacobian, + conserve_flux=conserve_flux, kernel=kernel, kernel_width=kernel_width, sample_region_width=sample_region_width, boundary_mode=boundary_mode, diff --git a/reproject/adaptive/deforest.pyx b/reproject/adaptive/deforest.pyx index 1185d52f0..957f5eff1 100644 --- a/reproject/adaptive/deforest.pyx +++ b/reproject/adaptive/deforest.pyx @@ -188,7 +188,7 @@ BOUNDARY_MODES['nearest'] = 6 def map_coordinates(double[:,:] source, double[:,:] target, Ci, int max_samples_width=-1, int conserve_flux=False, int progress=False, int singularities_nan=False, int x_cyclic=False, int y_cyclic=False, int out_of_range_nan=False, - bint center_jacobian=False, str kernel='Hann', double kernel_width=1.3, + bint center_jacobian=False, str kernel='gaussian', double kernel_width=1.3, double sample_region_width=4, str boundary_mode="ignore", double boundary_fill_value=0, double boundary_ignore_threshold=0.5): cdef int kernel_flag diff --git a/reproject/adaptive/high_level.py b/reproject/adaptive/high_level.py index 148453a27..c2c3119ef 100644 --- a/reproject/adaptive/high_level.py +++ b/reproject/adaptive/high_level.py @@ -14,7 +14,7 @@ def reproject_adaptive(input_data, output_projection, shape_out=None, hdu_in=0, order=None, return_footprint=True, center_jacobian=False, roundtrip_coords=True, conserve_flux=False, - kernel=None, kernel_width=1.3, + kernel='gaussian', kernel_width=1.3, sample_region_width=4, boundary_mode=None, boundary_fill_value=0, boundary_ignore_threshold=0.5, x_cyclic=False, @@ -148,14 +148,6 @@ def reproject_adaptive(input_data, output_projection, shape_out=None, hdu_in=0, indicate valid values. """ - if kernel is None: - kernel = 'hann' - warnings.warn( - "The default kernel will change from 'Hann' to " - " 'Gaussian' in a future release. To suppress this warning, " - "explicitly select a kernel with the 'kernel' argument.", - FutureWarning, stacklevel=3) - if boundary_mode is None: boundary_mode = 'ignore' warnings.warn( diff --git a/reproject/adaptive/tests/test_core.py b/reproject/adaptive/tests/test_core.py index 0018b43be..a3d2812bf 100644 --- a/reproject/adaptive/tests/test_core.py +++ b/reproject/adaptive/tests/test_core.py @@ -52,7 +52,8 @@ def test_reproject_adaptive_2d(wcsapi, center_jacobian, roundtrip_coords): array_out, footprint_out = reproject_adaptive( (data_in, wcs_in), wcs_out, shape_out=(60, 60), center_jacobian=center_jacobian, - roundtrip_coords=roundtrip_coords) + roundtrip_coords=roundtrip_coords, + kernel='hann') # Check that surface brightness is conserved in the unrotated case assert_allclose(np.nansum(data_in), np.nansum(array_out) * (256 / 60) ** 2, rtol=0.1) @@ -94,7 +95,8 @@ def test_reproject_adaptive_2d_rotated(center_jacobian, roundtrip_coords): array_out, footprint_out = reproject_adaptive( (data_in, wcs_in), wcs_out, shape_out=(60, 60), center_jacobian=center_jacobian, - roundtrip_coords=roundtrip_coords) + roundtrip_coords=roundtrip_coords, + kernel='hann') # ASTROPY_LT_40: astropy v4.0 introduced new default header keywords, # once we support only astropy 4.0 and later we can update the reference @@ -603,7 +605,7 @@ def test_reproject_adaptive_roundtrip(file_format): data, wcs, target_wcs = prepare_test_data(file_format) output, footprint = reproject_adaptive((data, wcs), target_wcs, (128, 128), - center_jacobian=True) + center_jacobian=True, kernel='hann') header_out = target_wcs.to_header() @@ -630,7 +632,7 @@ def test_reproject_adaptive_uncentered_jacobian(): data, wcs, target_wcs = prepare_test_data('fits') output, footprint = reproject_adaptive((data, wcs), target_wcs, (128, 128), - center_jacobian=False) + center_jacobian=False, kernel='hann') header_out = target_wcs.to_header() From 1039a2b179f25a736f296463c2539ae7ba238825 Mon Sep 17 00:00:00 2001 From: Sam Van Kooten Date: Thu, 3 Feb 2022 14:44:32 -0700 Subject: [PATCH 021/366] Set default boundary mode to 'strict' for adaptive algo --- reproject/adaptive/core.py | 2 +- reproject/adaptive/deforest.pyx | 2 +- reproject/adaptive/high_level.py | 11 +---------- reproject/adaptive/tests/test_core.py | 11 +++++++---- reproject/tests/test_high_level.py | 12 ++++++++---- 5 files changed, 18 insertions(+), 20 deletions(-) diff --git a/reproject/adaptive/core.py b/reproject/adaptive/core.py index 5d58112c3..d14d5c36e 100644 --- a/reproject/adaptive/core.py +++ b/reproject/adaptive/core.py @@ -32,7 +32,7 @@ def _reproject_adaptive_2d(array, wcs_in, wcs_out, shape_out, roundtrip_coords=True, conserve_flux=False, kernel='Gaussian', kernel_width=1.3, sample_region_width=4, - boundary_mode='ignore', boundary_fill_value=0, + boundary_mode='strict', boundary_fill_value=0, boundary_ignore_threshold=0.5, x_cyclic=False, y_cyclic=False): """ diff --git a/reproject/adaptive/deforest.pyx b/reproject/adaptive/deforest.pyx index 957f5eff1..38599070a 100644 --- a/reproject/adaptive/deforest.pyx +++ b/reproject/adaptive/deforest.pyx @@ -189,7 +189,7 @@ def map_coordinates(double[:,:] source, double[:,:] target, Ci, int max_samples_ int conserve_flux=False, int progress=False, int singularities_nan=False, int x_cyclic=False, int y_cyclic=False, int out_of_range_nan=False, bint center_jacobian=False, str kernel='gaussian', double kernel_width=1.3, - double sample_region_width=4, str boundary_mode="ignore", + double sample_region_width=4, str boundary_mode="strict", double boundary_fill_value=0, double boundary_ignore_threshold=0.5): cdef int kernel_flag try: diff --git a/reproject/adaptive/high_level.py b/reproject/adaptive/high_level.py index c2c3119ef..08cf588a6 100644 --- a/reproject/adaptive/high_level.py +++ b/reproject/adaptive/high_level.py @@ -1,5 +1,4 @@ # Licensed under a 3-clause BSD style license - see LICENSE.rst -import warnings import astropy.utils @@ -16,7 +15,7 @@ def reproject_adaptive(input_data, output_projection, shape_out=None, hdu_in=0, roundtrip_coords=True, conserve_flux=False, kernel='gaussian', kernel_width=1.3, sample_region_width=4, - boundary_mode=None, boundary_fill_value=0, + boundary_mode='strict', boundary_fill_value=0, boundary_ignore_threshold=0.5, x_cyclic=False, y_cyclic=False): """ @@ -148,14 +147,6 @@ def reproject_adaptive(input_data, output_projection, shape_out=None, hdu_in=0, indicate valid values. """ - if boundary_mode is None: - boundary_mode = 'ignore' - warnings.warn( - "The default boundary mode will change from 'ignore' to " - " 'strict' in a future release. To suppress this warning, " - "explicitly select a mode with the 'boundary_mode' argument.", - FutureWarning, stacklevel=3) - # TODO: add support for output_array array_in, wcs_in = parse_input_data(input_data, hdu_in=hdu_in) diff --git a/reproject/adaptive/tests/test_core.py b/reproject/adaptive/tests/test_core.py index a3d2812bf..7ba7fa06c 100644 --- a/reproject/adaptive/tests/test_core.py +++ b/reproject/adaptive/tests/test_core.py @@ -53,7 +53,7 @@ def test_reproject_adaptive_2d(wcsapi, center_jacobian, roundtrip_coords): (data_in, wcs_in), wcs_out, shape_out=(60, 60), center_jacobian=center_jacobian, roundtrip_coords=roundtrip_coords, - kernel='hann') + kernel='hann', boundary_mode='ignore') # Check that surface brightness is conserved in the unrotated case assert_allclose(np.nansum(data_in), np.nansum(array_out) * (256 / 60) ** 2, rtol=0.1) @@ -96,7 +96,7 @@ def test_reproject_adaptive_2d_rotated(center_jacobian, roundtrip_coords): (data_in, wcs_in), wcs_out, shape_out=(60, 60), center_jacobian=center_jacobian, roundtrip_coords=roundtrip_coords, - kernel='hann') + kernel='hann', boundary_mode='ignore') # ASTROPY_LT_40: astropy v4.0 introduced new default header keywords, # once we support only astropy 4.0 and later we can update the reference @@ -294,7 +294,9 @@ def test_reproject_adaptive_flux_conservation(): # The Gaussian kernel does a better job at flux conservation, so # choosing it here allows a tighter tolerance. Increasing the sample # region width also allows a tighter tolerance---less room for bugs to - # hide! + # hide! Setting the boundary mode to 'ignore' avoids excessive clipping + # of the edges of our small output image, letting us use a smaller + # image and do more tests in the same time. array_out = reproject_adaptive((data_in, wcs_in), wcs_out, shape_out=(30, 30), return_footprint=False, @@ -302,7 +304,8 @@ def test_reproject_adaptive_flux_conservation(): center_jacobian=False, kernel='gaussian', sample_region_width=5, - conserve_flux=True) + conserve_flux=True, + boundary_mode='ignore') # The degree of flux-conservation we end up seeing isn't necessarily # something that we can constrain a priori, so here we test for diff --git a/reproject/tests/test_high_level.py b/reproject/tests/test_high_level.py index ddbc7bc48..c7c5f1230 100644 --- a/reproject/tests/test_high_level.py +++ b/reproject/tests/test_high_level.py @@ -132,8 +132,10 @@ def test_surface_brightness(projection_type, dtype): if projection_type == 'flux-conserving': data_out, footprint = reproject_exact((data_in, header_in), header_out) elif projection_type.startswith('adaptive'): - data_out, footprint = reproject_adaptive((data_in, header_in), header_out, - kernel=projection_type.split('-', 1)[1]) + data_out, footprint = reproject_adaptive( + (data_in, header_in), header_out, + kernel=projection_type.split('-', 1)[1], + boundary_mode='ignore') else: data_out, footprint = reproject_interp((data_in, header_in), header_out, order=projection_type) @@ -174,8 +176,10 @@ def test_identity_projection(projection_type): if projection_type == 'flux-conserving': data_out, footprint = reproject_exact((data_in, header_in), header_in) elif projection_type.startswith('adaptive'): - data_out, footprint = reproject_adaptive((data_in, header_in), header_in, - kernel=projection_type.split('-', 1)[1]) + data_out, footprint = reproject_adaptive( + (data_in, header_in), header_in, + kernel=projection_type.split('-', 1)[1], + boundary_mode='ignore') else: data_out, footprint = reproject_interp((data_in, header_in), header_in, order=projection_type) From 85585da0c66cfd535a4719fa1288cd7eef49ce01 Mon Sep 17 00:00:00 2001 From: Sam Van Kooten Date: Tue, 23 Aug 2022 11:09:52 -0600 Subject: [PATCH 022/366] Remove deprecated 'order' arg from reproject_adaptive --- reproject/adaptive/high_level.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/reproject/adaptive/high_level.py b/reproject/adaptive/high_level.py index 08cf588a6..ee8dee2f4 100644 --- a/reproject/adaptive/high_level.py +++ b/reproject/adaptive/high_level.py @@ -1,16 +1,12 @@ # Licensed under a 3-clause BSD style license - see LICENSE.rst -import astropy.utils - from ..utils import parse_input_data, parse_output_projection from .core import _reproject_adaptive_2d __all__ = ['reproject_adaptive'] -@astropy.utils.deprecated_renamed_argument('order', None, since=0.9) def reproject_adaptive(input_data, output_projection, shape_out=None, hdu_in=0, - order=None, return_footprint=True, center_jacobian=False, roundtrip_coords=True, conserve_flux=False, kernel='gaussian', kernel_width=1.3, @@ -50,9 +46,6 @@ def reproject_adaptive(input_data, output_projection, shape_out=None, hdu_in=0, hdu_in : int or str, optional If ``input_data`` is a FITS file or an `~astropy.io.fits.HDUList` instance, specifies the HDU to use. - order : str - Deprecated, and no longer has any effect. Will be removed in a future - release. return_footprint : bool Whether to return the footprint in addition to the output array. center_jacobian : bool From 418c879341a76b14d842fad3212482a17c9ad919 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Thu, 8 Sep 2022 10:46:48 +0100 Subject: [PATCH 023/366] Skip wheel tests on manylinux_aarch64 and explicitly skip tests on macosx_arm64 (these were already being skipped but this will avoid a warning) --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index c31477676..c2b13a1b6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -12,6 +12,7 @@ write_to = "reproject/version.py" [tool.cibuildwheel] skip = "cp36-* pp* *-musllinux* cp310-win32" +test-skip = "*-macosx_arm64 *-manylinux_aarch64" [tool.isort] extend_skip_glob = [ From 282ac37d28ed69dc1cb9d516b60fc55bb68f0cf7 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 12 Sep 2022 16:29:10 +0000 Subject: [PATCH 024/366] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/pre-commit/pre-commit-hooks: v4.2.0 → v4.3.0](https://github.com/pre-commit/pre-commit-hooks/compare/v4.2.0...v4.3.0) - [github.com/asottile/pyupgrade: v2.34.0 → v2.37.3](https://github.com/asottile/pyupgrade/compare/v2.34.0...v2.37.3) - https://gitlab.com/pycqa/flake8 → https://github.com/PyCQA/flake8 - [github.com/PyCQA/flake8: 3.9.2 → 5.0.4](https://github.com/PyCQA/flake8/compare/3.9.2...5.0.4) --- .pre-commit-config.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 38f15b5e4..90e7f725b 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.2.0 + rev: v4.3.0 hooks: - id: check-added-large-files - id: check-case-conflict @@ -18,7 +18,7 @@ repos: name: isort (python) - repo: https://github.com/asottile/pyupgrade - rev: v2.34.0 + rev: v2.37.3 hooks: - id: pyupgrade args: ["--py38-plus"] @@ -52,8 +52,8 @@ repos: # E999: SyntaxError -- failed to compile a file into an Abstract Syntax Tree # F822: undefined name in __all__ # F823: local variable name referenced before assignment - - repo: https://gitlab.com/pycqa/flake8 - rev: 3.9.2 + - repo: https://github.com/PyCQA/flake8 + rev: 5.0.4 hooks: - id: flake8 args: From da46501f99b3bbc79c7242cbc08336c7f83e2751 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 26 Sep 2022 16:30:50 +0000 Subject: [PATCH 025/366] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/asottile/pyupgrade: v2.37.3 → v2.38.2](https://github.com/asottile/pyupgrade/compare/v2.37.3...v2.38.2) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 90e7f725b..f47d43dc1 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -18,7 +18,7 @@ repos: name: isort (python) - repo: https://github.com/asottile/pyupgrade - rev: v2.37.3 + rev: v2.38.2 hooks: - id: pyupgrade args: ["--py38-plus"] From 7c7730abc9244769c8f60048e28b654ead6e4e57 Mon Sep 17 00:00:00 2001 From: Stuart Mumford Date: Fri, 9 Sep 2022 12:46:39 +0100 Subject: [PATCH 026/366] Reformat all Python code using Black --- .pre-commit-config.yaml | 7 + docs/conf.py | 74 ++- pyproject.toml | 7 +- reproject/adaptive/core.py | 60 +- reproject/adaptive/high_level.py | 57 +- reproject/adaptive/tests/test_core.py | 407 ++++++++------ reproject/array_utils.py | 10 +- reproject/conftest.py | 18 +- reproject/healpix/core.py | 28 +- reproject/healpix/high_level.py | 48 +- reproject/healpix/tests/test_healpix.py | 73 +-- reproject/healpix/tests/test_utils.py | 23 +- reproject/healpix/utils.py | 13 +- reproject/interpolation/core.py | 92 ++-- reproject/interpolation/high_level.py | 68 ++- reproject/interpolation/tests/test_core.py | 512 ++++++++++-------- reproject/mosaicking/background.py | 16 +- reproject/mosaicking/coadd.py | 77 +-- reproject/mosaicking/subset_array.py | 39 +- reproject/mosaicking/tests/test_background.py | 8 +- reproject/mosaicking/tests/test_coadd.py | 136 +++-- .../mosaicking/tests/test_subset_array.py | 30 +- .../mosaicking/tests/test_wcs_helpers.py | 73 +-- reproject/mosaicking/wcs_helpers.py | 22 +- reproject/spherical_intersect/core.py | 57 +- reproject/spherical_intersect/high_level.py | 23 +- reproject/spherical_intersect/overlap.py | 2 +- .../spherical_intersect/setup_package.py | 9 +- .../tests/test_high_level.py | 29 +- .../spherical_intersect/tests/test_overlap.py | 9 +- .../tests/test_reproject.py | 51 +- reproject/tests/helpers.py | 2 +- reproject/tests/test_high_level.py | 136 ++--- reproject/tests/test_utils.py | 51 +- reproject/utils.py | 143 +++-- reproject/wcs_utils.py | 4 +- 36 files changed, 1394 insertions(+), 1020 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index f47d43dc1..d71c1fe1d 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,6 +16,8 @@ repos: hooks: - id: isort name: isort (python) + args: + - "--filter-files" - repo: https://github.com/asottile/pyupgrade rev: v2.38.2 @@ -64,5 +66,10 @@ repos: ] exclude: ".*(data.*|extern.*|cextern)$" + - repo: https://github.com/psf/black + rev: 22.6.0 + hooks: + - id: black + ci: autofix_prs: false diff --git a/docs/conf.py b/docs/conf.py index 25ee35a03..f2cbca48d 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -32,27 +32,28 @@ try: from sphinx_astropy.conf.v1 import * # noqa except ImportError: - print('ERROR: the documentation requires the sphinx-astropy package to be installed') + print("ERROR: the documentation requires the sphinx-astropy package to be installed") sys.exit(1) # Get configuration information from setup.cfg from configparser import ConfigParser + conf = ConfigParser() -conf.read([os.path.join(os.path.dirname(__file__), '..', 'setup.cfg')]) -setup_cfg = dict(conf.items('metadata')) +conf.read([os.path.join(os.path.dirname(__file__), "..", "setup.cfg")]) +setup_cfg = dict(conf.items("metadata")) # -- General configuration ---------------------------------------------------- # By default, highlight as Python 3. -highlight_language = 'python3' +highlight_language = "python3" # If your documentation needs a minimal Sphinx version, state it here. -#needs_sphinx = '1.1' +# needs_sphinx = '1.1' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. -exclude_patterns.append('_templates') +exclude_patterns.append("_templates") # This is added to the end of RST files - a good place to put substitutions to # be used globally. @@ -62,20 +63,19 @@ # -- Project information ------------------------------------------------------ # This does not *have* to match the package name, but typically does -project = setup_cfg['name'] -author = setup_cfg['author'] -copyright = '{}, {}'.format( - datetime.datetime.now().year, setup_cfg['author']) +project = setup_cfg["name"] +author = setup_cfg["author"] +copyright = "{}, {}".format(datetime.datetime.now().year, setup_cfg["author"]) # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. -import_module(setup_cfg['name']) -package = sys.modules[setup_cfg['name']] +import_module(setup_cfg["name"]) +package = sys.modules[setup_cfg["name"]] # The short X.Y version. -version = package.__version__.split('-', 1)[0] +version = package.__version__.split("-", 1)[0] # The full version, including alpha/beta/rc tags. release = package.__version__ @@ -90,63 +90,61 @@ # global configuration are listed below, commented out. html_theme_options = { - 'logotext1': 're', # white, semi-bold - 'logotext2': 'project', # orange, light - 'logotext3': ':docs' # white, light - } + "logotext1": "re", # white, semi-bold + "logotext2": "project", # orange, light + "logotext3": ":docs", # white, light +} # Add any paths that contain custom themes here, relative to this directory. # To use a different custom theme, add the directory containing the theme. -#html_theme_path = [] +# html_theme_path = [] # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. To override the custom theme, set this to the # name of a builtin theme or the name of a custom theme in html_theme_path. -#html_theme = None +# html_theme = None # Custom sidebar templates, maps document names to template names. -#html_sidebars = {} +# html_sidebars = {} # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. -#html_favicon = '' +# html_favicon = '' # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. -#html_last_updated_fmt = '' +# html_last_updated_fmt = '' # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". -html_title = f'{project} v{release}' +html_title = f"{project} v{release}" # Output file base name for HTML help builder. -htmlhelp_basename = project + 'doc' +htmlhelp_basename = project + "doc" # -- Options for LaTeX output -------------------------------------------------- # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass [howto/manual]). -latex_documents = [('index', project + '.tex', project + ' Documentation', - author, 'manual')] +latex_documents = [("index", project + ".tex", project + " Documentation", author, "manual")] # -- Options for manual page output -------------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). -man_pages = [('index', project.lower(), project + ' Documentation', - [author], 1)] +man_pages = [("index", project.lower(), project + " Documentation", [author], 1)] ## -- Options for the edit_on_github extension ---------------------------------------- -if eval(setup_cfg.get('edit_on_github')): - extensions += ['astropy_helpers.sphinx.ext.edit_on_github'] +if eval(setup_cfg.get("edit_on_github")): + extensions += ["astropy_helpers.sphinx.ext.edit_on_github"] - versionmod = __import__(setup_cfg['package_name'] + '.version') - edit_on_github_project = setup_cfg['github_project'] + versionmod = __import__(setup_cfg["package_name"] + ".version") + edit_on_github_project = setup_cfg["github_project"] if versionmod.version.release: edit_on_github_branch = "v" + versionmod.version.version else: @@ -158,12 +156,12 @@ nitpicky = True plot_rcparams = {} -plot_rcparams['figure.figsize'] = (8, 6) -plot_rcparams['savefig.facecolor'] = 'none' -plot_rcparams['savefig.bbox'] = 'tight' -plot_rcparams['axes.labelsize'] = 'large' -plot_rcparams['figure.subplot.hspace'] = 0.5 +plot_rcparams["figure.figsize"] = (8, 6) +plot_rcparams["savefig.facecolor"] = "none" +plot_rcparams["savefig.bbox"] = "tight" +plot_rcparams["axes.labelsize"] = "large" +plot_rcparams["figure.subplot.hspace"] = 0.5 plot_apply_rcparams = True plot_html_show_source_link = False -plot_formats = ['png', 'svg', 'pdf'] +plot_formats = ["png", "svg", "pdf"] diff --git a/pyproject.toml b/pyproject.toml index c2b13a1b6..e61bbbda2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -15,13 +15,18 @@ skip = "cp36-* pp* *-musllinux* cp310-win32" test-skip = "*-macosx_arm64 *-manylinux_aarch64" [tool.isort] +profile = "black" +multi_line_output = 3 extend_skip_glob = [ "docs/*", "setup.py"] line_length = 100 known_third_party = ["astropy"] known_first_party = ["reproject"] -multi_line_output = 4 group_by_package = true indented_import_headings = false length_sort_sections = ["future", "stdlib"] + +[tool.black] +line-length = 100 +target-version = ['py38'] diff --git a/reproject/adaptive/core.py b/reproject/adaptive/core.py index d14d5c36e..2694ff070 100644 --- a/reproject/adaptive/core.py +++ b/reproject/adaptive/core.py @@ -5,11 +5,10 @@ from ..wcs_utils import efficient_pixel_to_pixel, efficient_pixel_to_pixel_with_roundtrip from .deforest import map_coordinates -__all__ = ['_reproject_adaptive_2d'] +__all__ = ["_reproject_adaptive_2d"] class CoordinateTransformer: - def __init__(self, wcs_in, wcs_out, roundtrip_coords): self.wcs_in = wcs_in self.wcs_out = wcs_out @@ -19,22 +18,32 @@ def __call__(self, pixel_out): pixel_out = pixel_out[:, :, 0], pixel_out[:, :, 1] if self.roundtrip_coords: pixel_in = efficient_pixel_to_pixel_with_roundtrip( - self.wcs_out, self.wcs_in, *pixel_out) + self.wcs_out, self.wcs_in, *pixel_out + ) else: - pixel_in = efficient_pixel_to_pixel( - self.wcs_out, self.wcs_in, *pixel_out) + pixel_in = efficient_pixel_to_pixel(self.wcs_out, self.wcs_in, *pixel_out) pixel_in = np.array(pixel_in).transpose().swapaxes(0, 1) return pixel_in -def _reproject_adaptive_2d(array, wcs_in, wcs_out, shape_out, - return_footprint=True, center_jacobian=False, - roundtrip_coords=True, conserve_flux=False, - kernel='Gaussian', kernel_width=1.3, - sample_region_width=4, - boundary_mode='strict', boundary_fill_value=0, - boundary_ignore_threshold=0.5, - x_cyclic=False, y_cyclic=False): +def _reproject_adaptive_2d( + array, + wcs_in, + wcs_out, + shape_out, + return_footprint=True, + center_jacobian=False, + roundtrip_coords=True, + conserve_flux=False, + kernel="Gaussian", + kernel_width=1.3, + sample_region_width=4, + boundary_mode="strict", + boundary_fill_value=0, + boundary_ignore_threshold=0.5, + x_cyclic=False, + y_cyclic=False, +): """ Reproject celestial slices from an n-d array from one WCS to another using the DeForest (2004) algorithm [1]_, and assuming all other dimensions @@ -111,15 +120,22 @@ def _reproject_adaptive_2d(array, wcs_in, wcs_out, shape_out, array_out = np.zeros(shape_out) transformer = CoordinateTransformer(wcs_in, wcs_out, roundtrip_coords) - map_coordinates(array_in, array_out, transformer, out_of_range_nan=True, - center_jacobian=center_jacobian, - conserve_flux=conserve_flux, - kernel=kernel, kernel_width=kernel_width, - sample_region_width=sample_region_width, - boundary_mode=boundary_mode, - boundary_fill_value=boundary_fill_value, - boundary_ignore_threshold=boundary_ignore_threshold, - x_cyclic=x_cyclic, y_cyclic=y_cyclic) + map_coordinates( + array_in, + array_out, + transformer, + out_of_range_nan=True, + center_jacobian=center_jacobian, + conserve_flux=conserve_flux, + kernel=kernel, + kernel_width=kernel_width, + sample_region_width=sample_region_width, + boundary_mode=boundary_mode, + boundary_fill_value=boundary_fill_value, + boundary_ignore_threshold=boundary_ignore_threshold, + x_cyclic=x_cyclic, + y_cyclic=y_cyclic, + ) if return_footprint: return array_out, (~np.isnan(array_out)).astype(float) diff --git a/reproject/adaptive/high_level.py b/reproject/adaptive/high_level.py index ee8dee2f4..58f75c68c 100644 --- a/reproject/adaptive/high_level.py +++ b/reproject/adaptive/high_level.py @@ -3,17 +3,27 @@ from ..utils import parse_input_data, parse_output_projection from .core import _reproject_adaptive_2d -__all__ = ['reproject_adaptive'] +__all__ = ["reproject_adaptive"] -def reproject_adaptive(input_data, output_projection, shape_out=None, hdu_in=0, - return_footprint=True, center_jacobian=False, - roundtrip_coords=True, conserve_flux=False, - kernel='gaussian', kernel_width=1.3, - sample_region_width=4, - boundary_mode='strict', boundary_fill_value=0, - boundary_ignore_threshold=0.5, x_cyclic=False, - y_cyclic=False): +def reproject_adaptive( + input_data, + output_projection, + shape_out=None, + hdu_in=0, + return_footprint=True, + center_jacobian=False, + roundtrip_coords=True, + conserve_flux=False, + kernel="gaussian", + kernel_width=1.3, + sample_region_width=4, + boundary_mode="strict", + boundary_fill_value=0, + boundary_ignore_threshold=0.5, + x_cyclic=False, + y_cyclic=False, +): """ Reproject a 2D array from one WCS to another using the DeForest (2004) adaptive, anti-aliased resampling algorithm, with optional flux @@ -145,14 +155,21 @@ def reproject_adaptive(input_data, output_projection, shape_out=None, hdu_in=0, array_in, wcs_in = parse_input_data(input_data, hdu_in=hdu_in) wcs_out, shape_out = parse_output_projection(output_projection, shape_out=shape_out) - return _reproject_adaptive_2d(array_in, wcs_in, wcs_out, shape_out, - return_footprint=return_footprint, - center_jacobian=center_jacobian, - roundtrip_coords=roundtrip_coords, - conserve_flux=conserve_flux, - kernel=kernel, kernel_width=kernel_width, - sample_region_width=sample_region_width, - boundary_mode=boundary_mode, - boundary_fill_value=boundary_fill_value, - boundary_ignore_threshold=boundary_ignore_threshold, - x_cyclic=x_cyclic, y_cyclic=y_cyclic) + return _reproject_adaptive_2d( + array_in, + wcs_in, + wcs_out, + shape_out, + return_footprint=return_footprint, + center_jacobian=center_jacobian, + roundtrip_coords=roundtrip_coords, + conserve_flux=conserve_flux, + kernel=kernel, + kernel_width=kernel_width, + sample_region_width=sample_region_width, + boundary_mode=boundary_mode, + boundary_fill_value=boundary_fill_value, + boundary_ignore_threshold=boundary_ignore_threshold, + x_cyclic=x_cyclic, + y_cyclic=y_cyclic, + ) diff --git a/reproject/adaptive/tests/test_core.py b/reproject/adaptive/tests/test_core.py index 7ba7fa06c..7898d95db 100644 --- a/reproject/adaptive/tests/test_core.py +++ b/reproject/adaptive/tests/test_core.py @@ -13,18 +13,18 @@ from ...tests.helpers import array_footprint_to_hdulist from ..high_level import reproject_adaptive -DATA = os.path.join(os.path.dirname(__file__), '..', '..', 'tests', 'data') +DATA = os.path.join(os.path.dirname(__file__), "..", "..", "tests", "data") def as_high_level_wcs(wcs): return HighLevelWCSWrapper(SlicedLowLevelWCS(wcs, Ellipsis)) -@pytest.mark.filterwarnings('ignore::FutureWarning') +@pytest.mark.filterwarnings("ignore::FutureWarning") @pytest.mark.array_compare(single_reference=True) -@pytest.mark.parametrize('wcsapi', (False, True)) -@pytest.mark.parametrize('center_jacobian', (False, True)) -@pytest.mark.parametrize('roundtrip_coords', (False, True)) +@pytest.mark.parametrize("wcsapi", (False, True)) +@pytest.mark.parametrize("center_jacobian", (False, True)) +@pytest.mark.parametrize("roundtrip_coords", (False, True)) def test_reproject_adaptive_2d(wcsapi, center_jacobian, roundtrip_coords): # Set up initial array with pattern @@ -50,10 +50,14 @@ def test_reproject_adaptive_2d(wcsapi, center_jacobian, roundtrip_coords): wcs_out = as_high_level_wcs(wcs_out) array_out, footprint_out = reproject_adaptive( - (data_in, wcs_in), wcs_out, shape_out=(60, 60), - center_jacobian=center_jacobian, - roundtrip_coords=roundtrip_coords, - kernel='hann', boundary_mode='ignore') + (data_in, wcs_in), + wcs_out, + shape_out=(60, 60), + center_jacobian=center_jacobian, + roundtrip_coords=roundtrip_coords, + kernel="hann", + boundary_mode="ignore", + ) # Check that surface brightness is conserved in the unrotated case assert_allclose(np.nansum(data_in), np.nansum(array_out) * (256 / 60) ** 2, rtol=0.1) @@ -61,16 +65,16 @@ def test_reproject_adaptive_2d(wcsapi, center_jacobian, roundtrip_coords): # ASTROPY_LT_40: astropy v4.0 introduced new default header keywords, # once we support only astropy 4.0 and later we can update the reference # data files and remove this section. - for key in ('DATEREF', 'MJDREFF', 'MJDREFI', 'MJDREF', 'MJD-OBS'): + for key in ("DATEREF", "MJDREFF", "MJDREFI", "MJDREF", "MJD-OBS"): header_out.pop(key, None) return array_footprint_to_hdulist(array_out, footprint_out, header_out) -@pytest.mark.filterwarnings('ignore::FutureWarning') +@pytest.mark.filterwarnings("ignore::FutureWarning") @pytest.mark.array_compare(single_reference=True) -@pytest.mark.parametrize('center_jacobian', (False, True)) -@pytest.mark.parametrize('roundtrip_coords', (False, True)) +@pytest.mark.parametrize("center_jacobian", (False, True)) +@pytest.mark.parametrize("roundtrip_coords", (False, True)) def test_reproject_adaptive_2d_rotated(center_jacobian, roundtrip_coords): # Set up initial array with pattern @@ -93,28 +97,33 @@ def test_reproject_adaptive_2d_rotated(center_jacobian, roundtrip_coords): header_out = wcs_out.to_header() array_out, footprint_out = reproject_adaptive( - (data_in, wcs_in), wcs_out, shape_out=(60, 60), - center_jacobian=center_jacobian, - roundtrip_coords=roundtrip_coords, - kernel='hann', boundary_mode='ignore') + (data_in, wcs_in), + wcs_out, + shape_out=(60, 60), + center_jacobian=center_jacobian, + roundtrip_coords=roundtrip_coords, + kernel="hann", + boundary_mode="ignore", + ) # ASTROPY_LT_40: astropy v4.0 introduced new default header keywords, # once we support only astropy 4.0 and later we can update the reference # data files and remove this section. - for key in ('DATEREF', 'MJDREFF', 'MJDREFI', 'MJDREF', 'MJD-OBS'): + for key in ("DATEREF", "MJDREFF", "MJDREFI", "MJDREF", "MJD-OBS"): header_out.pop(key, None) return array_footprint_to_hdulist(array_out, footprint_out, header_out) -@pytest.mark.filterwarnings('ignore::FutureWarning') -@pytest.mark.parametrize('roundtrip_coords', (False, True)) -@pytest.mark.parametrize('center_jacobian', (False, True)) -@pytest.mark.parametrize('kernel', ('hann', 'gaussian')) +@pytest.mark.filterwarnings("ignore::FutureWarning") +@pytest.mark.parametrize("roundtrip_coords", (False, True)) +@pytest.mark.parametrize("center_jacobian", (False, True)) +@pytest.mark.parametrize("kernel", ("hann", "gaussian")) def test_reproject_adaptive_high_aliasing_potential_rotation( - roundtrip_coords, center_jacobian, kernel): + roundtrip_coords, center_jacobian, kernel +): # Generate sample data with vertical stripes alternating with every column - data_in = np.arange(40*40).reshape((40, 40)) + data_in = np.arange(40 * 40).reshape((40, 40)) data_in = (data_in) % 2 # Set up the input image coordinates, defining pixel coordinates as world @@ -130,12 +139,15 @@ def test_reproject_adaptive_high_aliasing_potential_rotation( wcs_out.wcs.crval = 0, 0 wcs_out.wcs.cdelt = 2, 1 - array_out = reproject_adaptive((data_in, wcs_in), - wcs_out, shape_out=(11, 6), - return_footprint=False, - roundtrip_coords=roundtrip_coords, - center_jacobian=center_jacobian, - kernel=kernel) + array_out = reproject_adaptive( + (data_in, wcs_in), + wcs_out, + shape_out=(11, 6), + return_footprint=False, + roundtrip_coords=roundtrip_coords, + center_jacobian=center_jacobian, + kernel=kernel, + ) # The CDELT1 value in wcs_out produces a down-sampling by a factor of two # along the output x axis. With the input image containing vertical lines @@ -151,14 +163,16 @@ def test_reproject_adaptive_high_aliasing_potential_rotation( # since we're rotating the already-downsampled image, no pixel values # should change angle = 90 * np.pi / 180 - wcs_out.wcs.pc = [[np.cos(angle), -np.sin(angle)], - [np.sin(angle), np.cos(angle)]] - array_out = reproject_adaptive((data_in, wcs_in), - wcs_out, shape_out=(11, 6), - return_footprint=False, - roundtrip_coords=roundtrip_coords, - center_jacobian=center_jacobian, - kernel=kernel) + wcs_out.wcs.pc = [[np.cos(angle), -np.sin(angle)], [np.sin(angle), np.cos(angle)]] + array_out = reproject_adaptive( + (data_in, wcs_in), + wcs_out, + shape_out=(11, 6), + return_footprint=False, + roundtrip_coords=roundtrip_coords, + center_jacobian=center_jacobian, + kernel=kernel, + ) np.testing.assert_allclose(array_out, 0.5, rtol=0.001) # But if we add a 90-degree rotation to the input coordinates, then when @@ -169,14 +183,16 @@ def test_reproject_adaptive_high_aliasing_potential_rotation( # For these last two cases, we only use a Hann kernel, since the blurring # inherent with the Gaussian kernel makes the comparison more difficult. angle = 90 * np.pi / 180 - wcs_in.wcs.pc = [[np.cos(angle), -np.sin(angle)], - [np.sin(angle), np.cos(angle)]] - array_out = reproject_adaptive((data_in, wcs_in), - wcs_out, shape_out=(11, 6), - return_footprint=False, - roundtrip_coords=roundtrip_coords, - center_jacobian=center_jacobian, - kernel='hann') + wcs_in.wcs.pc = [[np.cos(angle), -np.sin(angle)], [np.sin(angle), np.cos(angle)]] + array_out = reproject_adaptive( + (data_in, wcs_in), + wcs_out, + shape_out=(11, 6), + return_footprint=False, + roundtrip_coords=roundtrip_coords, + center_jacobian=center_jacobian, + kernel="hann", + ) # Generate the expected pattern of alternating stripes data_ref = np.arange(array_out.shape[1]) % 2 @@ -185,24 +201,26 @@ def test_reproject_adaptive_high_aliasing_potential_rotation( # Clear the rotation in the output coordinates wcs_out.wcs.pc = [[1, 0], [0, 1]] - array_out = reproject_adaptive((data_in, wcs_in), - wcs_out, shape_out=(11, 6), - return_footprint=False, - roundtrip_coords=roundtrip_coords, - center_jacobian=center_jacobian, - kernel='hann') + array_out = reproject_adaptive( + (data_in, wcs_in), + wcs_out, + shape_out=(11, 6), + return_footprint=False, + roundtrip_coords=roundtrip_coords, + center_jacobian=center_jacobian, + kernel="hann", + ) data_ref = np.arange(array_out.shape[0]) % 2 data_ref = np.vstack([data_ref] * array_out.shape[1]).T np.testing.assert_allclose(array_out, data_ref) -@pytest.mark.filterwarnings('ignore::FutureWarning') -@pytest.mark.parametrize('roundtrip_coords', (False, True)) -@pytest.mark.parametrize('center_jacobian', (False, True)) -def test_reproject_adaptive_high_aliasing_potential_shearing( - roundtrip_coords, center_jacobian): +@pytest.mark.filterwarnings("ignore::FutureWarning") +@pytest.mark.parametrize("roundtrip_coords", (False, True)) +@pytest.mark.parametrize("center_jacobian", (False, True)) +def test_reproject_adaptive_high_aliasing_potential_shearing(roundtrip_coords, center_jacobian): # Generate sample data with vertical stripes alternating with every column - data_in = np.arange(40*40).reshape((40, 40)) + data_in = np.arange(40 * 40).reshape((40, 40)) data_in = (data_in) % 2 # Set up the input image coordinates, defining pixel coordinates as world @@ -223,21 +241,22 @@ def test_reproject_adaptive_high_aliasing_potential_shearing( wcs_out.wcs.crpix = 3, 5 wcs_out.wcs.crval = 0, 0 wcs_out.wcs.cdelt = 1, 1 - wcs_out.wcs.pc = ( - np.array([[1, shear_x], [0, 1]]) - @ np.array([[1, 0], [shear_y, 1]])) + wcs_out.wcs.pc = np.array([[1, shear_x], [0, 1]]) @ np.array([[1, 0], [shear_y, 1]]) # n.b. The Gaussian kernel is much better-behaved in this # particular scenario, so using it allows a much smaller tolerance # in the comparison. Likewise, the kernel width is boosted to 1.5 # to achieve stronger anti-aliasing for this test. - array_out = reproject_adaptive((data_in, wcs_in), - wcs_out, shape_out=(11, 6), - return_footprint=False, - roundtrip_coords=False, - center_jacobian=center_jacobian, - kernel='gaussian', - kernel_width=1.5) + array_out = reproject_adaptive( + (data_in, wcs_in), + wcs_out, + shape_out=(11, 6), + return_footprint=False, + roundtrip_coords=False, + center_jacobian=center_jacobian, + kernel="gaussian", + kernel_width=1.5, + ) # We should get values close to 0.5 (an average of the 1s and 0s # in the input image). This is as opposed to values near 0 or 1, @@ -245,7 +264,7 @@ def test_reproject_adaptive_high_aliasing_potential_shearing( np.testing.assert_allclose(array_out, 0.5, atol=0.02, rtol=0) -@pytest.mark.filterwarnings('ignore::FutureWarning') +@pytest.mark.filterwarnings("ignore::FutureWarning") def test_reproject_adaptive_flux_conservation(): # This is more than just testing the `conserve_flux` flag---the expectation # that flux should be conserved gives a very easy way to quantify how @@ -273,20 +292,19 @@ def test_reproject_adaptive_flux_conservation(): # for each parameter, this is by far the longest-running test in this file, # so it's tough to justify a finer grid. rotations = [0, 45, 80, 90] - scales_x = np.logspace(-.2, .2, 3) - scales_y = np.logspace(-.3, .3, 3) + scales_x = np.logspace(-0.2, 0.2, 3) + scales_y = np.logspace(-0.3, 0.3, 3) translations_x = np.linspace(0, 8, 3) - translations_y = np.linspace(0, .42, 3) - shears_x = np.linspace(-.7, .7, 3) - shears_y = np.linspace(-.2, .2, 3) + translations_y = np.linspace(0, 0.42, 3) + shears_x = np.linspace(-0.7, 0.7, 3) + shears_y = np.linspace(-0.2, 0.2, 3) for rot, scale_x, scale_y, trans_x, trans_y, shear_x, shear_y in product( - rotations, scales_x, scales_y, translations_x, translations_y, - shears_x, shears_y): + rotations, scales_x, scales_y, translations_x, translations_y, shears_x, shears_y + ): wcs_out.wcs.cdelt = scale_x, scale_y - angle = rot * np.pi/180 - rot_matrix = np.array([[np.cos(angle), -np.sin(angle)], - [np.sin(angle), np.cos(angle)]]) + angle = rot * np.pi / 180 + rot_matrix = np.array([[np.cos(angle), -np.sin(angle)], [np.sin(angle), np.cos(angle)]]) shear_x_matrix = np.array([[1, shear_x], [0, 1]]) shear_y_matrix = np.array([[1, 0], [shear_y, 1]]) wcs_out.wcs.pc = rot_matrix @ shear_x_matrix @ shear_y_matrix @@ -297,15 +315,18 @@ def test_reproject_adaptive_flux_conservation(): # hide! Setting the boundary mode to 'ignore' avoids excessive clipping # of the edges of our small output image, letting us use a smaller # image and do more tests in the same time. - array_out = reproject_adaptive((data_in, wcs_in), - wcs_out, shape_out=(30, 30), - return_footprint=False, - roundtrip_coords=False, - center_jacobian=False, - kernel='gaussian', - sample_region_width=5, - conserve_flux=True, - boundary_mode='ignore') + array_out = reproject_adaptive( + (data_in, wcs_in), + wcs_out, + shape_out=(30, 30), + return_footprint=False, + roundtrip_coords=False, + center_jacobian=False, + kernel="gaussian", + sample_region_width=5, + conserve_flux=True, + boundary_mode="ignore", + ) # The degree of flux-conservation we end up seeing isn't necessarily # something that we can constrain a priori, so here we test for @@ -316,9 +337,9 @@ def test_reproject_adaptive_flux_conservation(): np.testing.assert_allclose(np.nansum(array_out), 1, rtol=0.004) -@pytest.mark.parametrize('x_cyclic', (False, True)) -@pytest.mark.parametrize('y_cyclic', (False, True)) -@pytest.mark.parametrize('rotated', (False, True)) +@pytest.mark.parametrize("x_cyclic", (False, True)) +@pytest.mark.parametrize("y_cyclic", (False, True)) +@pytest.mark.parametrize("rotated", (False, True)) def test_boundary_modes(x_cyclic, y_cyclic, rotated): # Vertical-stripe test data data_in = np.arange(30) % 2 @@ -339,9 +360,7 @@ def test_boundary_modes(x_cyclic, y_cyclic, rotated): if rotated: data_in = data_in.T angle = 90 * np.pi / 180 - wcs_out.wcs.pc = [ - [np.cos(angle), -np.sin(angle)], - [np.sin(angle), np.cos(angle)]] + wcs_out.wcs.pc = [[np.cos(angle), -np.sin(angle)], [np.sin(angle), np.cos(angle)]] y_appears_cyclic = x_cyclic x_appears_cyclic = y_cyclic else: @@ -361,10 +380,16 @@ def test_boundary_modes(x_cyclic, y_cyclic, rotated): # in the rotated case. data_out = reproject_adaptive( - (data_in, wcs_in), wcs_out, shape_out=(40, 40), - return_footprint=False, kernel='gaussian', - sample_region_width=4.00001, - boundary_mode='strict', x_cyclic=x_cyclic, y_cyclic=y_cyclic) + (data_in, wcs_in), + wcs_out, + shape_out=(40, 40), + return_footprint=False, + kernel="gaussian", + sample_region_width=4.00001, + boundary_mode="strict", + x_cyclic=x_cyclic, + y_cyclic=y_cyclic, + ) # We've placed a 30x30 input image at the center of a 40x40 output image. # With strict boundaries, we should get NaNs for the 5 rows and columns on # each side of the output image, being outside the bounds of the input @@ -389,20 +414,32 @@ def test_boundary_modes(x_cyclic, y_cyclic, rotated): # The 'ignore_threshold' mode should match the 'strict' mode when the # threshold is set to 0. data_out_ignore_threshold = reproject_adaptive( - (data_in, wcs_in), wcs_out, shape_out=(40, 40), - return_footprint=False, kernel='gaussian', - sample_region_width=4.00001, - boundary_mode='ignore_threshold', boundary_ignore_threshold=0, - x_cyclic=x_cyclic, y_cyclic=y_cyclic) + (data_in, wcs_in), + wcs_out, + shape_out=(40, 40), + return_footprint=False, + kernel="gaussian", + sample_region_width=4.00001, + boundary_mode="ignore_threshold", + boundary_ignore_threshold=0, + x_cyclic=x_cyclic, + y_cyclic=y_cyclic, + ) np.testing.assert_array_equal(data_out, data_out_ignore_threshold) data_out = reproject_adaptive( - (data_in, wcs_in), wcs_out, shape_out=(40, 40), - return_footprint=False, kernel='gaussian', - sample_region_width=4.00001, - boundary_mode='constant', boundary_fill_value=1000, - x_cyclic=x_cyclic, y_cyclic=y_cyclic) + (data_in, wcs_in), + wcs_out, + shape_out=(40, 40), + return_footprint=False, + kernel="gaussian", + sample_region_width=4.00001, + boundary_mode="constant", + boundary_fill_value=1000, + x_cyclic=x_cyclic, + y_cyclic=y_cyclic, + ) # With constant filling, we should get NaNs for the # outermost 3 rows/columns. The next four rows/columns should have very # large values due to the kernel width and our fill value. The remaining @@ -428,11 +465,17 @@ def test_boundary_modes(x_cyclic, y_cyclic, rotated): assert np.all(data_out[7:-7, 7:-7] <= 1) data_out = reproject_adaptive( - (data_in, wcs_in), wcs_out, shape_out=(40, 40), - return_footprint=False, kernel='gaussian', - sample_region_width=4.00001, - boundary_mode='grid-constant', boundary_fill_value=1000, - x_cyclic=x_cyclic, y_cyclic=y_cyclic) + (data_in, wcs_in), + wcs_out, + shape_out=(40, 40), + return_footprint=False, + kernel="gaussian", + sample_region_width=4.00001, + boundary_mode="grid-constant", + boundary_fill_value=1000, + x_cyclic=x_cyclic, + y_cyclic=y_cyclic, + ) # With grid-constant filling, we should get our fill value for the # outermost 3 rows/columns. The next four rows/columns should have very # large values due to the kernel width and our fill value. The remaining @@ -460,10 +503,16 @@ def test_boundary_modes(x_cyclic, y_cyclic, rotated): assert not np.any(np.isnan(data_out)) data_out = reproject_adaptive( - (data_in, wcs_in), wcs_out, shape_out=(40, 40), - return_footprint=False, kernel='gaussian', - sample_region_width=4.00001, - boundary_mode='ignore', x_cyclic=x_cyclic, y_cyclic=y_cyclic) + (data_in, wcs_in), + wcs_out, + shape_out=(40, 40), + return_footprint=False, + kernel="gaussian", + sample_region_width=4.00001, + boundary_mode="ignore", + x_cyclic=x_cyclic, + y_cyclic=y_cyclic, + ) # With missing samples ignored, we should get NaNs for three pixels on all # edges, and no NaNs elsewhere. The next four rows on top and bottom, where # the sampling region spans the boundary, should match the interior of the @@ -494,20 +543,32 @@ def test_boundary_modes(x_cyclic, y_cyclic, rotated): # The 'ignore_threshold' mode should match the 'ignore' mode when the # threshold is set to 1. data_out_ignore_threshold = reproject_adaptive( - (data_in, wcs_in), wcs_out, shape_out=(40, 40), - return_footprint=False, kernel='gaussian', - sample_region_width=4.00001, - boundary_mode='ignore_threshold', boundary_ignore_threshold=1, - x_cyclic=x_cyclic, y_cyclic=y_cyclic) + (data_in, wcs_in), + wcs_out, + shape_out=(40, 40), + return_footprint=False, + kernel="gaussian", + sample_region_width=4.00001, + boundary_mode="ignore_threshold", + boundary_ignore_threshold=1, + x_cyclic=x_cyclic, + y_cyclic=y_cyclic, + ) np.testing.assert_array_equal(data_out, data_out_ignore_threshold) data_out = reproject_adaptive( - (data_in, wcs_in), wcs_out, shape_out=(40, 40), - return_footprint=False, kernel='gaussian', - sample_region_width=4.00001, - boundary_mode='ignore_threshold', boundary_ignore_threshold=0.5, - x_cyclic=x_cyclic, y_cyclic=y_cyclic) + (data_in, wcs_in), + wcs_out, + shape_out=(40, 40), + return_footprint=False, + kernel="gaussian", + sample_region_width=4.00001, + boundary_mode="ignore_threshold", + boundary_ignore_threshold=0.5, + x_cyclic=x_cyclic, + y_cyclic=y_cyclic, + ) # With the threshold set to 0.5, 'ignore_threshold' should look like the # 'ignore' output, except that the band of NaNs along the outside is 5 # pixels thick instead of 3---an extra 2 rows and columns are set to NaN @@ -533,10 +594,16 @@ def test_boundary_modes(x_cyclic, y_cyclic, rotated): assert not np.any(np.isnan(data_out[5:-5, 5:-5])) data_out = reproject_adaptive( - (data_in, wcs_in), wcs_out, shape_out=(40, 40), - return_footprint=False, kernel='gaussian', - sample_region_width=4.00001, - boundary_mode='nearest', x_cyclic=x_cyclic, y_cyclic=y_cyclic) + (data_in, wcs_in), + wcs_out, + shape_out=(40, 40), + return_footprint=False, + kernel="gaussian", + sample_region_width=4.00001, + boundary_mode="nearest", + x_cyclic=x_cyclic, + y_cyclic=y_cyclic, + ) # With out-of-bounds samples replaced with the nearest valid sample, the # top and bottom rows should be the same as the central rows. There should # be three columns of 0 on the left and 1 on the right, and the next three @@ -562,33 +629,33 @@ def test_boundary_modes(x_cyclic, y_cyclic, rotated): def prepare_test_data(file_format): - pytest.importorskip('sunpy', minversion='2.1.0') + pytest.importorskip("sunpy", minversion="2.1.0") from sunpy.coordinates.ephemeris import get_body_heliographic_stonyhurst from sunpy.map import Map - if file_format == 'fits': - map_aia = Map(os.path.join(DATA, 'aia_171_level1.fits')) + if file_format == "fits": + map_aia = Map(os.path.join(DATA, "aia_171_level1.fits")) data = map_aia.data wcs = map_aia.wcs date = map_aia.date target_wcs = wcs.deepcopy() - elif file_format == 'asdf': - pytest.importorskip('astropy', minversion='4.0') - pytest.importorskip('gwcs', minversion='0.12') - asdf = pytest.importorskip('asdf') - aia = asdf.open(os.path.join(DATA, 'aia_171_level1.asdf')) - data = aia['data'][...] - wcs = aia['wcs'] + elif file_format == "asdf": + pytest.importorskip("astropy", minversion="4.0") + pytest.importorskip("gwcs", minversion="0.12") + asdf = pytest.importorskip("asdf") + aia = asdf.open(os.path.join(DATA, "aia_171_level1.asdf")) + data = aia["data"][...] + wcs = aia["wcs"] date = wcs.output_frame.reference_frame.obstime - target_wcs = Map(os.path.join(DATA, 'aia_171_level1.fits')).wcs.deepcopy() + target_wcs = Map(os.path.join(DATA, "aia_171_level1.fits")).wcs.deepcopy() else: - raise ValueError('file_format should be fits or asdf') + raise ValueError("file_format should be fits or asdf") # Reproject to an observer on Venus - target_wcs.wcs.cdelt = ([24, 24]*u.arcsec).to(u.deg) + target_wcs.wcs.cdelt = ([24, 24] * u.arcsec).to(u.deg) target_wcs.wcs.crpix = [64, 64] - venus = get_body_heliographic_stonyhurst('venus', date) + venus = get_body_heliographic_stonyhurst("venus", date) target_wcs.wcs.aux.hgln_obs = venus.lon.to_value(u.deg) target_wcs.wcs.aux.hglt_obs = venus.lat.to_value(u.deg) target_wcs.wcs.aux.dsun_obs = venus.radius.to_value(u.m) @@ -596,9 +663,9 @@ def prepare_test_data(file_format): return data, wcs, target_wcs -@pytest.mark.filterwarnings('ignore::FutureWarning') +@pytest.mark.filterwarnings("ignore::FutureWarning") @pytest.mark.array_compare(single_reference=True) -@pytest.mark.parametrize('file_format', ['fits', 'asdf']) +@pytest.mark.parametrize("file_format", ["fits", "asdf"]) def test_reproject_adaptive_roundtrip(file_format): # Test the reprojection with solar data, which ensures that the masking of @@ -607,23 +674,34 @@ def test_reproject_adaptive_roundtrip(file_format): data, wcs, target_wcs = prepare_test_data(file_format) - output, footprint = reproject_adaptive((data, wcs), target_wcs, (128, 128), - center_jacobian=True, kernel='hann') + output, footprint = reproject_adaptive( + (data, wcs), target_wcs, (128, 128), center_jacobian=True, kernel="hann" + ) header_out = target_wcs.to_header() # ASTROPY_LT_40: astropy v4.0 introduced new default header keywords, # once we support only astropy 4.0 and later we can update the reference # data files and remove this section. - for key in ('CRLN_OBS', 'CRLT_OBS', 'DSUN_OBS', 'HGLN_OBS', 'HGLT_OBS', - 'MJDREFF', 'MJDREFI', 'MJDREF', 'MJD-OBS', 'RSUN_REF'): + for key in ( + "CRLN_OBS", + "CRLT_OBS", + "DSUN_OBS", + "HGLN_OBS", + "HGLT_OBS", + "MJDREFF", + "MJDREFI", + "MJDREF", + "MJD-OBS", + "RSUN_REF", + ): header_out.pop(key, None) - header_out['DATE-OBS'] = header_out['DATE-OBS'].replace('T', ' ') + header_out["DATE-OBS"] = header_out["DATE-OBS"].replace("T", " ") return array_footprint_to_hdulist(output, footprint, header_out) -@pytest.mark.filterwarnings('ignore::FutureWarning') +@pytest.mark.filterwarnings("ignore::FutureWarning") @pytest.mark.array_compare() def test_reproject_adaptive_uncentered_jacobian(): @@ -632,19 +710,30 @@ def test_reproject_adaptive_uncentered_jacobian(): # (Though more nan pixels are present, as the uncentered calculation draws # in values from a bit further away.) - data, wcs, target_wcs = prepare_test_data('fits') + data, wcs, target_wcs = prepare_test_data("fits") - output, footprint = reproject_adaptive((data, wcs), target_wcs, (128, 128), - center_jacobian=False, kernel='hann') + output, footprint = reproject_adaptive( + (data, wcs), target_wcs, (128, 128), center_jacobian=False, kernel="hann" + ) header_out = target_wcs.to_header() # ASTROPY_LT_40: astropy v4.0 introduced new default header keywords, # once we support only astropy 4.0 and later we can update the reference # data files and remove this section. - for key in ('CRLN_OBS', 'CRLT_OBS', 'DSUN_OBS', 'HGLN_OBS', 'HGLT_OBS', - 'MJDREFF', 'MJDREFI', 'MJDREF', 'MJD-OBS', 'RSUN_REF'): + for key in ( + "CRLN_OBS", + "CRLT_OBS", + "DSUN_OBS", + "HGLN_OBS", + "HGLT_OBS", + "MJDREFF", + "MJDREFI", + "MJDREF", + "MJD-OBS", + "RSUN_REF", + ): header_out.pop(key, None) - header_out['DATE-OBS'] = header_out['DATE-OBS'].replace('T', ' ') + header_out["DATE-OBS"] = header_out["DATE-OBS"].replace("T", " ") return array_footprint_to_hdulist(output, footprint, header_out) diff --git a/reproject/array_utils.py b/reproject/array_utils.py index e55eda7e6..0712fab35 100644 --- a/reproject/array_utils.py +++ b/reproject/array_utils.py @@ -1,10 +1,10 @@ import numpy as np -__all__ = ['map_coordinates'] +__all__ = ["map_coordinates"] def pad_edge_1(array): - return np.pad(array, 1, mode='edge') + return np.pad(array, 1, mode="edge") def map_coordinates(image, coords, **kwargs): @@ -27,9 +27,9 @@ def map_coordinates(image, coords, **kwargs): reset = np.zeros(coords.shape[1], dtype=bool) for i in range(coords.shape[0]): - reset |= (coords[i] < -0.5) - reset |= (coords[i] > original_shape[i] - 0.5) + reset |= coords[i] < -0.5 + reset |= coords[i] > original_shape[i] - 0.5 - values[reset] = kwargs.get('cval', 0.) + values[reset] = kwargs.get("cval", 0.0) return values diff --git a/reproject/conftest.py b/reproject/conftest.py index 611fd953c..65904c37e 100644 --- a/reproject/conftest.py +++ b/reproject/conftest.py @@ -7,11 +7,12 @@ try: from pytest_astropy_header.display import PYTEST_HEADER_MODULES, TESTED_VERSIONS + ASTROPY_HEADER = True except ImportError: ASTROPY_HEADER = False -os.environ['MPLBACKEND'] = 'Agg' +os.environ["MPLBACKEND"] = "Agg" def pytest_configure(config): @@ -20,12 +21,13 @@ def pytest_configure(config): config.option.astropy_header = True - PYTEST_HEADER_MODULES.pop('Pandas', None) - PYTEST_HEADER_MODULES.pop('h5py', None) - PYTEST_HEADER_MODULES.pop('Matplotlib', None) - PYTEST_HEADER_MODULES['Astropy'] = 'astropy' - PYTEST_HEADER_MODULES['astropy-healpix'] = 'astropy_healpix' - PYTEST_HEADER_MODULES['Cython'] = 'cython' + PYTEST_HEADER_MODULES.pop("Pandas", None) + PYTEST_HEADER_MODULES.pop("h5py", None) + PYTEST_HEADER_MODULES.pop("Matplotlib", None) + PYTEST_HEADER_MODULES["Astropy"] = "astropy" + PYTEST_HEADER_MODULES["astropy-healpix"] = "astropy_healpix" + PYTEST_HEADER_MODULES["Cython"] = "cython" from reproject import __version__ - TESTED_VERSIONS['reproject'] = __version__ + + TESTED_VERSIONS["reproject"] = __version__ diff --git a/reproject/healpix/core.py b/reproject/healpix/core.py index e45aa1735..345868caf 100644 --- a/reproject/healpix/core.py +++ b/reproject/healpix/core.py @@ -2,17 +2,18 @@ from astropy.coordinates import SkyCoord from astropy_healpix import HEALPix, npix_to_nside -__all__ = ['healpix_to_image', 'image_to_healpix'] +__all__ = ["healpix_to_image", "image_to_healpix"] ORDER = {} -ORDER['nearest-neighbor'] = 0 -ORDER['bilinear'] = 1 -ORDER['biquadratic'] = 2 -ORDER['bicubic'] = 3 +ORDER["nearest-neighbor"] = 0 +ORDER["bilinear"] = 1 +ORDER["biquadratic"] = 2 +ORDER["bicubic"] = 3 -def healpix_to_image(healpix_data, coord_system_in, wcs_out, shape_out, - order='bilinear', nested=False): +def healpix_to_image( + healpix_data, coord_system_in, wcs_out, shape_out, order="bilinear", nested=False +): """ Convert image in HEALPIX format to a normal FITS projection image (e.g. CAR or AIT). @@ -57,7 +58,7 @@ def healpix_to_image(healpix_data, coord_system_in, wcs_out, shape_out, # Look up lon, lat of pixels in reference system and convert celestial coordinates yinds, xinds = np.indices(shape_out) world_in = wcs_out.pixel_to_world(xinds, yinds).transform_to(coord_system_in) - world_in_unitsph = world_in.represent_as('unitspherical') + world_in_unitsph = world_in.represent_as("unitspherical") lon_in, lat_in = world_in_unitsph.lon, world_in_unitsph.lat if isinstance(order, str): @@ -65,7 +66,7 @@ def healpix_to_image(healpix_data, coord_system_in, wcs_out, shape_out, nside = npix_to_nside(len(healpix_data)) - hp = HEALPix(nside=nside, order='nested' if nested else 'ring') + hp = HEALPix(nside=nside, order="nested" if nested else "ring") if order == 1: data = hp.interpolate_bilinear_lonlat(lon_in, lat_in, healpix_data) @@ -80,8 +81,7 @@ def healpix_to_image(healpix_data, coord_system_in, wcs_out, shape_out, return data, footprint -def image_to_healpix(data, wcs_in, coord_system_out, - nside, order='bilinear', nested=False): +def image_to_healpix(data, wcs_in, coord_system_out, nside, order="bilinear", nested=False): """ Convert image in a normal WCS projection to HEALPIX format. @@ -121,7 +121,7 @@ def image_to_healpix(data, wcs_in, coord_system_out, """ from scipy.ndimage import map_coordinates - hp = HEALPix(nside=nside, order='nested' if nested else 'ring') + hp = HEALPix(nside=nside, order="nested" if nested else "ring") npix = hp.npix @@ -139,8 +139,6 @@ def image_to_healpix(data, wcs_in, coord_system_out, if isinstance(order, str): order = ORDER[order] - healpix_data = map_coordinates(data, [xinds, yinds], - order=order, - mode='constant', cval=np.nan) + healpix_data = map_coordinates(data, [xinds, yinds], order=order, mode="constant", cval=np.nan) return healpix_data, (~np.isnan(healpix_data)).astype(float) diff --git a/reproject/healpix/high_level.py b/reproject/healpix/high_level.py index 4dd8523a9..0d245ab1d 100644 --- a/reproject/healpix/high_level.py +++ b/reproject/healpix/high_level.py @@ -3,12 +3,12 @@ from .core import healpix_to_image, image_to_healpix from .utils import parse_coord_system, parse_input_healpix_data -__all__ = ['reproject_from_healpix', 'reproject_to_healpix'] +__all__ = ["reproject_from_healpix", "reproject_to_healpix"] -def reproject_from_healpix(input_data, output_projection, shape_out=None, - hdu_in=1, order='bilinear', nested=None, - field=0): +def reproject_from_healpix( + input_data, output_projection, shape_out=None, hdu_in=1, order="bilinear", nested=None, field=0 +): """ Reproject data from a HEALPIX projection to a standard projection. @@ -61,21 +61,26 @@ def reproject_from_healpix(input_data, output_projection, shape_out=None, indicate valid values. """ - array_in, coord_system_in, nested = parse_input_healpix_data(input_data, hdu_in=hdu_in, - field=field, nested=nested) + array_in, coord_system_in, nested = parse_input_healpix_data( + input_data, hdu_in=hdu_in, field=field, nested=nested + ) wcs_out, shape_out = parse_output_projection(output_projection, shape_out=shape_out) if nested is None: - raise ValueError("Could not determine whether the data follows the " - "'ring' or 'nested' ordering, so you should set " - "nested=True or nested=False explicitly.") + raise ValueError( + "Could not determine whether the data follows the " + "'ring' or 'nested' ordering, so you should set " + "nested=True or nested=False explicitly." + ) - return healpix_to_image(array_in, coord_system_in, wcs_out, shape_out, - order=order, nested=nested) + return healpix_to_image( + array_in, coord_system_in, wcs_out, shape_out, order=order, nested=nested + ) -def reproject_to_healpix(input_data, coord_system_out, hdu_in=0, - order='bilinear', nested=False, nside=128): +def reproject_to_healpix( + input_data, coord_system_out, hdu_in=0, order="bilinear", nested=False, nside=128 +): """ Reproject data from a standard projection to a HEALPIX projection. @@ -126,10 +131,15 @@ def reproject_to_healpix(input_data, coord_system_out, hdu_in=0, array_in, wcs_in = parse_input_data(input_data, hdu_in=hdu_in) coord_system_out = parse_coord_system(coord_system_out) - if (has_celestial(wcs_in) and wcs_in.low_level_wcs.pixel_n_dim == 2 and - wcs_in.low_level_wcs.world_n_dim == 2): - return image_to_healpix(array_in, wcs_in, coord_system_out, - nside=nside, order=order, nested=nested) + if ( + has_celestial(wcs_in) + and wcs_in.low_level_wcs.pixel_n_dim == 2 + and wcs_in.low_level_wcs.world_n_dim == 2 + ): + return image_to_healpix( + array_in, wcs_in, coord_system_out, nside=nside, order=order, nested=nested + ) else: - raise NotImplementedError("Only data with a 2-d celestial WCS can be " - "reprojected to a HEALPIX projection") + raise NotImplementedError( + "Only data with a 2-d celestial WCS can be " "reprojected to a HEALPIX projection" + ) diff --git a/reproject/healpix/tests/test_healpix.py b/reproject/healpix/tests/test_healpix.py index 6020045e3..7c7388fdb 100644 --- a/reproject/healpix/tests/test_healpix.py +++ b/reproject/healpix/tests/test_healpix.py @@ -13,35 +13,40 @@ from ...tests.test_high_level import ALL_DTYPES from ..high_level import reproject_from_healpix, reproject_to_healpix -DATA = os.path.join(os.path.dirname(__file__), 'data') +DATA = os.path.join(os.path.dirname(__file__), "data") def get_reference_header(oversample=2, nside=1): reference_header = fits.Header() - reference_header.update({ - 'CDELT1': -180.0 / (oversample * 4 * nside), - 'CDELT2': 180.0 / (oversample * 4 * nside), - 'CRPIX1': oversample * 4 * nside, - 'CRPIX2': oversample * 2 * nside, - 'CRVAL1': 180.0, - 'CRVAL2': 0.0, - 'CTYPE1': 'RA---CAR', - 'CTYPE2': 'DEC--CAR', - 'CUNIT1': 'deg', - 'CUNIT2': 'deg', - 'NAXIS': 2, - 'NAXIS1': oversample * 8 * nside, - 'NAXIS2': oversample * 4 * nside}) + reference_header.update( + { + "CDELT1": -180.0 / (oversample * 4 * nside), + "CDELT2": 180.0 / (oversample * 4 * nside), + "CRPIX1": oversample * 4 * nside, + "CRPIX2": oversample * 2 * nside, + "CRVAL1": 180.0, + "CRVAL2": 0.0, + "CTYPE1": "RA---CAR", + "CTYPE2": "DEC--CAR", + "CUNIT1": "deg", + "CUNIT2": "deg", + "NAXIS": 2, + "NAXIS1": oversample * 8 * nside, + "NAXIS2": oversample * 4 * nside, + } + ) return reference_header -@pytest.mark.parametrize("wcsapi,nside,nested,healpix_system,image_system,dtype", - itertools.product([True, False], [1, 2, 4, 8, 16, 32, 64], - [True, False], 'C', 'C', ALL_DTYPES)) -def test_reproject_healpix_to_image_round_trip(wcsapi, nside, nested, - healpix_system, image_system, dtype): +@pytest.mark.parametrize( + "wcsapi,nside,nested,healpix_system,image_system,dtype", + itertools.product([True, False], [1, 2, 4, 8, 16, 32, 64], [True, False], "C", "C", ALL_DTYPES), +) +def test_reproject_healpix_to_image_round_trip( + wcsapi, nside, nested, healpix_system, image_system, dtype +): """Test round-trip HEALPix->WCS->HEALPix conversion for a random map, with a WCS projection large enough to store each HEALPix pixel""" @@ -51,33 +56,39 @@ def test_reproject_healpix_to_image_round_trip(wcsapi, nside, nested, reference_header = get_reference_header(oversample=2, nside=nside) wcs_out = WCS(reference_header) - shape_out = reference_header['NAXIS2'], reference_header['NAXIS1'] + shape_out = reference_header["NAXIS2"], reference_header["NAXIS1"] if wcsapi: wcs_out = as_high_level_wcs(wcs_out) image_data, footprint = reproject_from_healpix( - (healpix_data, healpix_system), wcs_out, shape_out=shape_out, - order='nearest-neighbor', nested=nested) + (healpix_data, healpix_system), + wcs_out, + shape_out=shape_out, + order="nearest-neighbor", + nested=nested, + ) healpix_data_2, footprint = reproject_to_healpix( - (image_data, wcs_out), healpix_system, - nside=nside, order='nearest-neighbor', nested=nested) + (image_data, wcs_out), healpix_system, nside=nside, order="nearest-neighbor", nested=nested + ) np.testing.assert_array_equal(healpix_data, healpix_data_2) def test_reproject_file(): reference_header = get_reference_header(oversample=2, nside=8) - data, footprint = reproject_from_healpix(os.path.join(DATA, 'bayestar.fits.gz'), - reference_header) - reference_result = fits.getdata(os.path.join(DATA, 'reference_result.fits')) - np.testing.assert_allclose(data, reference_result, rtol=1.e-5) + data, footprint = reproject_from_healpix( + os.path.join(DATA, "bayestar.fits.gz"), reference_header + ) + reference_result = fits.getdata(os.path.join(DATA, "reference_result.fits")) + np.testing.assert_allclose(data, reference_result, rtol=1.0e-5) def test_reproject_invalid_order(): reference_header = get_reference_header(oversample=2, nside=8) with pytest.raises(ValueError) as exc: - reproject_from_healpix(os.path.join(DATA, 'bayestar.fits.gz'), - reference_header, order='bicubic') + reproject_from_healpix( + os.path.join(DATA, "bayestar.fits.gz"), reference_header, order="bicubic" + ) assert exc.value.args[0] == "Only nearest-neighbor and bilinear interpolation are supported" diff --git a/reproject/healpix/tests/test_utils.py b/reproject/healpix/tests/test_utils.py index 6ebb0cbc5..49fb2c9f4 100644 --- a/reproject/healpix/tests/test_utils.py +++ b/reproject/healpix/tests/test_utils.py @@ -11,37 +11,37 @@ def test_parse_coord_system(): frame = parse_coord_system(Galactic()) assert isinstance(frame, Galactic) - frame = parse_coord_system('fk5') + frame = parse_coord_system("fk5") assert isinstance(frame, FK5) with pytest.raises(ValueError) as exc: - frame = parse_coord_system('e') + frame = parse_coord_system("e") assert exc.value.args[0] == "Ecliptic coordinate frame not yet supported" - frame = parse_coord_system('g') + frame = parse_coord_system("g") assert isinstance(frame, Galactic) with pytest.raises(ValueError) as exc: - frame = parse_coord_system('spam') + frame = parse_coord_system("spam") assert exc.value.args[0] == "Could not determine frame for system=spam" -@pytest.mark.filterwarnings('ignore:unclosed file:ResourceWarning') +@pytest.mark.filterwarnings("ignore:unclosed file:ResourceWarning") def test_parse_input_healpix_data(tmpdir): data = np.arange(3072) - col = fits.Column(array=data, name='flux', format="E") + col = fits.Column(array=data, name="flux", format="E") hdu = fits.BinTableHDU.from_columns([col]) - hdu.header['NSIDE'] = 512 - hdu.header['COORDSYS'] = "G" + 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 + filename = tmpdir.join("test.fits").strpath hdu.writeto(filename) array, coordinate_system, nested = parse_input_healpix_data(filename) np.testing.assert_allclose(array, data) @@ -53,5 +53,6 @@ def test_parse_input_healpix_data(tmpdir): # 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)") + assert exc.value.args[0] == ( + "input_data should either be an HDU object or " "a tuple of (array, frame)" + ) diff --git a/reproject/healpix/utils.py b/reproject/healpix/utils.py index 8c35110bc..1fd962e08 100644 --- a/reproject/healpix/utils.py +++ b/reproject/healpix/utils.py @@ -3,10 +3,7 @@ from astropy.io import fits from astropy.io.fits import BinTableHDU, TableHDU -FRAMES = { - 'g': Galactic(), - 'c': ICRS() -} +FRAMES = {"g": Galactic(), "c": ICRS()} def parse_coord_system(system): @@ -14,7 +11,7 @@ def parse_coord_system(system): return system elif isinstance(system, str): system = system.lower() - if system == 'e': + if system == "e": raise ValueError("Ecliptic coordinate frame not yet supported") elif system in FRAMES: return FRAMES[system] @@ -34,10 +31,10 @@ def parse_input_healpix_data(input_data, field=0, hdu_in=None, nested=None): if isinstance(input_data, (TableHDU, BinTableHDU)): data = input_data.data header = input_data.header - coordinate_system_in = parse_coord_system(header['COORDSYS']) + 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' + if "ORDERING" in header: + nested = header["ORDERING"].lower() == "nested" elif isinstance(input_data, str): # NOTE: hdu is not closed here. hdu = fits.open(input_data)[hdu_in or 1] diff --git a/reproject/interpolation/core.py b/reproject/interpolation/core.py index d87f1a7b1..1ab4fd4fd 100644 --- a/reproject/interpolation/core.py +++ b/reproject/interpolation/core.py @@ -5,41 +5,39 @@ from ..array_utils import map_coordinates from ..wcs_utils import ( - efficient_pixel_to_pixel, efficient_pixel_to_pixel_with_roundtrip, has_celestial) + efficient_pixel_to_pixel, + efficient_pixel_to_pixel_with_roundtrip, + has_celestial, +) def _validate_wcs(wcs_in, wcs_out, shape_out): if wcs_in.low_level_wcs.pixel_n_dim != wcs_out.low_level_wcs.pixel_n_dim: - raise ValueError( - "Number of dimensions between input and output WCS should match") + raise ValueError("Number of dimensions between input and output WCS should match") elif len(shape_out) != wcs_out.low_level_wcs.pixel_n_dim: - raise ValueError( - "Length of shape_out should match number of dimensions in wcs_out") + raise ValueError("Length of shape_out should match number of dimensions in wcs_out") if has_celestial(wcs_in) and not has_celestial(wcs_out): - raise ValueError( - "Input WCS has celestial components but output WCS does not") + raise ValueError("Input WCS has celestial components but output WCS does not") elif has_celestial(wcs_out) and not has_celestial(wcs_in): - raise ValueError( - "Output WCS has celestial components but input WCS does not") + raise ValueError("Output WCS has celestial components but input WCS does not") if isinstance(wcs_in, WCS) and isinstance(wcs_out, WCS): # Check whether a spectral component is present, and if so, check that # the CTYPEs match. if wcs_in.wcs.spec >= 0 and wcs_out.wcs.spec >= 0: - if (wcs_in.wcs.ctype[wcs_in.wcs.spec] != - wcs_out.wcs.ctype[wcs_out.wcs.spec]): - raise ValueError("The input ({}) and output ({}) spectral " - "coordinate types are not equivalent." - .format(wcs_in.wcs.ctype[wcs_in.wcs.spec], - wcs_out.wcs.ctype[wcs_out.wcs.spec])) + if wcs_in.wcs.ctype[wcs_in.wcs.spec] != wcs_out.wcs.ctype[wcs_out.wcs.spec]: + raise ValueError( + "The input ({}) and output ({}) spectral " + "coordinate types are not equivalent.".format( + wcs_in.wcs.ctype[wcs_in.wcs.spec], wcs_out.wcs.ctype[wcs_out.wcs.spec] + ) + ) elif wcs_in.wcs.spec >= 0: - raise ValueError( - "Input WCS has a spectral component but output WCS does not") + raise ValueError("Input WCS has a spectral component but output WCS does not") elif wcs_out.wcs.spec >= 0: - raise ValueError( - "Output WCS has a spectral component but input WCS does not") + raise ValueError("Output WCS has a spectral component but input WCS does not") def _validate_array_out(array_out, array, shape_out): @@ -47,17 +45,29 @@ def _validate_array_out(array_out, array, shape_out): return if array_out.shape != tuple(shape_out): - raise ValueError("Array sizes don't match. Output array shape " - "should be {}".format(str(tuple(shape_out)))) + raise ValueError( + "Array sizes don't match. Output array shape " + "should be {}".format(str(tuple(shape_out))) + ) elif array_out.dtype != array.dtype: - raise ValueError("An output array of a different type than the " - "input array was specified, which will create an " - "undesired duplicate copy of the input array " - "in memory.") - - -def _reproject_full(array, wcs_in, wcs_out, shape_out, order=1, array_out=None, - return_footprint=True, roundtrip_coords=True): + raise ValueError( + "An output array of a different type than the " + "input array was specified, which will create an " + "undesired duplicate copy of the input array " + "in memory." + ) + + +def _reproject_full( + array, + wcs_in, + wcs_out, + shape_out, + order=1, + array_out=None, + return_footprint=True, + roundtrip_coords=True, +): """ Reproject n-dimensional data to a new projection using interpolation. @@ -77,16 +87,18 @@ def _reproject_full(array, wcs_in, wcs_out, shape_out, order=1, array_out=None, _validate_array_out(array_out, array, shape_out) - pixel_out = np.meshgrid(*[np.arange(size, dtype=float) for size in shape_out], - indexing='ij', sparse=False, copy=False) + pixel_out = np.meshgrid( + *[np.arange(size, dtype=float) for size in shape_out], + indexing="ij", + sparse=False, + copy=False, + ) pixel_out = [p.ravel() for p in pixel_out] # For each pixel in the ouput array, get the pixel value in the input WCS if roundtrip_coords: - pixel_in = efficient_pixel_to_pixel_with_roundtrip( - wcs_out, wcs_in, *pixel_out[::-1])[::-1] + pixel_in = efficient_pixel_to_pixel_with_roundtrip(wcs_out, wcs_in, *pixel_out[::-1])[::-1] else: - pixel_in = efficient_pixel_to_pixel( - wcs_out, wcs_in, *pixel_out[::-1])[::-1] + pixel_in = efficient_pixel_to_pixel(wcs_out, wcs_in, *pixel_out[::-1])[::-1] pixel_in = np.array(pixel_in) if array_out is not None: @@ -95,8 +107,14 @@ def _reproject_full(array, wcs_in, wcs_out, shape_out, order=1, array_out=None, array_out = np.empty(shape_out).ravel() # Interpolate array on to the pixels coordinates in pixel_in - map_coordinates(array, pixel_in, order=order, cval=np.nan, - mode='constant', output=array_out,).reshape(shape_out) + map_coordinates( + array, + pixel_in, + order=order, + cval=np.nan, + mode="constant", + output=array_out, + ).reshape(shape_out) array_out.shape = shape_out diff --git a/reproject/interpolation/high_level.py b/reproject/interpolation/high_level.py index a8b681a15..22494022b 100644 --- a/reproject/interpolation/high_level.py +++ b/reproject/interpolation/high_level.py @@ -6,20 +6,30 @@ from ..utils import parse_input_data, parse_output_projection, reproject_blocked from .core import _reproject_full -__all__ = ['reproject_interp'] +__all__ = ["reproject_interp"] ORDER = {} -ORDER['nearest-neighbor'] = 0 -ORDER['bilinear'] = 1 -ORDER['biquadratic'] = 2 -ORDER['bicubic'] = 3 - - -@deprecated_renamed_argument('independent_celestial_slices', None, since='0.6') -def reproject_interp(input_data, output_projection, shape_out=None, hdu_in=0, - order='bilinear', independent_celestial_slices=False, - output_array=None, return_footprint=True, output_footprint=None, - block_size=None, parallel=False, roundtrip_coords=True): +ORDER["nearest-neighbor"] = 0 +ORDER["bilinear"] = 1 +ORDER["biquadratic"] = 2 +ORDER["bicubic"] = 3 + + +@deprecated_renamed_argument("independent_celestial_slices", None, since="0.6") +def reproject_interp( + input_data, + output_projection, + shape_out=None, + hdu_in=0, + order="bilinear", + independent_celestial_slices=False, + output_array=None, + return_footprint=True, + output_footprint=None, + block_size=None, + parallel=False, + roundtrip_coords=True, +): """ Reproject data to a new projection using interpolation (this is typically the fastest way to reproject an image). @@ -91,8 +101,9 @@ def reproject_interp(input_data, output_projection, shape_out=None, hdu_in=0, """ array_in, wcs_in = parse_input_data(input_data, hdu_in=hdu_in) - wcs_out, shape_out = parse_output_projection(output_projection, shape_out=shape_out, - output_array=output_array) + wcs_out, shape_out = parse_output_projection( + output_projection, shape_out=shape_out, output_array=output_array + ) if isinstance(order, str): order = ORDER[order] @@ -112,11 +123,26 @@ def reproject_interp(input_data, output_projection, shape_out=None, hdu_in=0, if block_size[dim_idx] == 0: block_size[dim_idx] = shape_out[dim_idx] - return reproject_blocked(_reproject_full, array_in=array_in, wcs_in=wcs_in, wcs_out=wcs_out, - shape_out=shape_out, output_array=output_array, parallel=parallel, - block_size=block_size, return_footprint=return_footprint, - output_footprint=output_footprint) + return reproject_blocked( + _reproject_full, + array_in=array_in, + wcs_in=wcs_in, + wcs_out=wcs_out, + shape_out=shape_out, + output_array=output_array, + parallel=parallel, + block_size=block_size, + return_footprint=return_footprint, + output_footprint=output_footprint, + ) else: - return _reproject_full(array_in, wcs_in, wcs_out, shape_out=shape_out, - order=order, array_out=output_array, - return_footprint=return_footprint, roundtrip_coords=roundtrip_coords) + return _reproject_full( + array_in, + wcs_in, + wcs_out, + shape_out=shape_out, + order=order, + array_out=output_array, + return_footprint=return_footprint, + roundtrip_coords=roundtrip_coords, + ) diff --git a/reproject/interpolation/tests/test_core.py b/reproject/interpolation/tests/test_core.py index 4bf9bfa34..1fe4a0590 100644 --- a/reproject/interpolation/tests/test_core.py +++ b/reproject/interpolation/tests/test_core.py @@ -23,31 +23,32 @@ def as_high_level_wcs(wcs): @pytest.mark.array_compare(single_reference=True) -@pytest.mark.parametrize('wcsapi', (False, True)) -@pytest.mark.parametrize('roundtrip_coords', (False, True)) +@pytest.mark.parametrize("wcsapi", (False, True)) +@pytest.mark.parametrize("roundtrip_coords", (False, True)) def test_reproject_celestial_2d_gal2equ(wcsapi, roundtrip_coords): """ Test reprojection of a 2D celestial image, which includes a coordinate system conversion. """ - with fits.open(get_pkg_data_filename('data/galactic_2d.fits', package='reproject.tests')) as pf: + with fits.open(get_pkg_data_filename("data/galactic_2d.fits", package="reproject.tests")) as pf: hdu_in = pf[0] header_out = hdu_in.header.copy() - header_out['CTYPE1'] = 'RA---TAN' - header_out['CTYPE2'] = 'DEC--TAN' - header_out['CRVAL1'] = 266.39311 - header_out['CRVAL2'] = -28.939779 + header_out["CTYPE1"] = "RA---TAN" + header_out["CTYPE2"] = "DEC--TAN" + header_out["CRVAL1"] = 266.39311 + header_out["CRVAL2"] = -28.939779 if wcsapi: # Enforce a pure wcsapi API wcs_in, data_in = as_high_level_wcs(WCS(hdu_in.header)), hdu_in.data wcs_out = as_high_level_wcs(WCS(header_out)) - shape_out = header_out['NAXIS2'], header_out['NAXIS1'] + shape_out = header_out["NAXIS2"], header_out["NAXIS1"] array_out, footprint_out = reproject_interp( - (data_in, wcs_in), wcs_out, shape_out=shape_out, - roundtrip_coords=roundtrip_coords) + (data_in, wcs_in), wcs_out, shape_out=shape_out, roundtrip_coords=roundtrip_coords + ) else: array_out, footprint_out = reproject_interp( - hdu_in, header_out, roundtrip_coords=roundtrip_coords) + hdu_in, header_out, roundtrip_coords=roundtrip_coords + ) return array_footprint_to_hdulist(array_out, footprint_out, header_out) @@ -62,8 +63,8 @@ def test_reproject_celestial_2d_gal2equ(wcsapi, roundtrip_coords): @pytest.mark.array_compare(single_reference=True) -@pytest.mark.parametrize(('wcsapi', 'axis_order'), tuple(COMBINATIONS)) -@pytest.mark.parametrize('roundtrip_coords', (False, True)) +@pytest.mark.parametrize(("wcsapi", "axis_order"), tuple(COMBINATIONS)) +@pytest.mark.parametrize("roundtrip_coords", (False, True)) def test_reproject_celestial_3d_equ2gal(wcsapi, axis_order, roundtrip_coords): """ Test reprojection of a 3D cube with celestial components, which includes a @@ -76,20 +77,21 @@ def test_reproject_celestial_3d_equ2gal(wcsapi, axis_order, roundtrip_coords): # Read in the input cube with fits.open( - get_pkg_data_filename('data/equatorial_3d.fits', package='reproject.tests')) as pf: + get_pkg_data_filename("data/equatorial_3d.fits", package="reproject.tests") + ) as pf: hdu_in = pf[0] # Define the output header - this should be the same for all versions of # this test to make sure we can use a single reference file. header_out = hdu_in.header.copy() - header_out['NAXIS1'] = 10 - header_out['NAXIS2'] = 9 - header_out['CTYPE1'] = 'GLON-SIN' - header_out['CTYPE2'] = 'GLAT-SIN' - header_out['CRVAL1'] = 163.16724 - header_out['CRVAL2'] = -15.777405 - header_out['CRPIX1'] = 6 - header_out['CRPIX2'] = 5 + header_out["NAXIS1"] = 10 + header_out["NAXIS2"] = 9 + header_out["CTYPE1"] = "GLON-SIN" + header_out["CTYPE2"] = "GLAT-SIN" + header_out["CRVAL1"] = 163.16724 + header_out["CRVAL2"] = -15.777405 + header_out["CRPIX1"] = 6 + header_out["CRPIX2"] = 5 # We now scramble the input axes if axis_order != (0, 1, 2): @@ -101,52 +103,54 @@ def test_reproject_celestial_3d_equ2gal(wcsapi, axis_order, roundtrip_coords): if wcsapi: # Enforce a pure wcsapi API wcs_in, data_in = as_high_level_wcs(WCS(hdu_in.header)), hdu_in.data wcs_out = as_high_level_wcs(WCS(header_out)) - shape_out = header_out['NAXIS3'], header_out['NAXIS2'], header_out['NAXIS1'] + shape_out = header_out["NAXIS3"], header_out["NAXIS2"], header_out["NAXIS1"] array_out, footprint_out = reproject_interp( - (data_in, wcs_in), wcs_out, shape_out=shape_out, - roundtrip_coords=roundtrip_coords) + (data_in, wcs_in), wcs_out, shape_out=shape_out, roundtrip_coords=roundtrip_coords + ) else: array_out, footprint_out = reproject_interp( - hdu_in, header_out, roundtrip_coords=roundtrip_coords) + hdu_in, header_out, roundtrip_coords=roundtrip_coords + ) return array_footprint_to_hdulist(array_out, footprint_out, header_out) @pytest.mark.array_compare(single_reference=True) -@pytest.mark.parametrize('wcsapi', (False, True)) -@pytest.mark.parametrize('roundtrip_coords', (False, True)) +@pytest.mark.parametrize("wcsapi", (False, True)) +@pytest.mark.parametrize("roundtrip_coords", (False, True)) def test_small_cutout(wcsapi, roundtrip_coords): """ Test reprojection of a cutout from a larger image (makes sure that the pre-reprojection cropping works) """ - with fits.open(get_pkg_data_filename('data/galactic_2d.fits', package='reproject.tests')) as pf: + with fits.open(get_pkg_data_filename("data/galactic_2d.fits", package="reproject.tests")) as pf: hdu_in = pf[0] header_out = hdu_in.header.copy() - header_out['NAXIS1'] = 10 - header_out['NAXIS2'] = 9 - header_out['CTYPE1'] = 'RA---TAN' - header_out['CTYPE2'] = 'DEC--TAN' - header_out['CRVAL1'] = 266.39311 - header_out['CRVAL2'] = -28.939779 - header_out['CRPIX1'] = 5.1 - header_out['CRPIX2'] = 4.7 + header_out["NAXIS1"] = 10 + header_out["NAXIS2"] = 9 + header_out["CTYPE1"] = "RA---TAN" + header_out["CTYPE2"] = "DEC--TAN" + header_out["CRVAL1"] = 266.39311 + header_out["CRVAL2"] = -28.939779 + header_out["CRPIX1"] = 5.1 + header_out["CRPIX2"] = 4.7 if wcsapi: # Enforce a pure wcsapi API wcs_in, data_in = as_high_level_wcs(WCS(hdu_in.header)), hdu_in.data wcs_out = as_high_level_wcs(WCS(header_out)) - shape_out = header_out['NAXIS2'], header_out['NAXIS1'] + shape_out = header_out["NAXIS2"], header_out["NAXIS1"] array_out, footprint_out = reproject_interp( - (data_in, wcs_in), wcs_out, shape_out=shape_out, - roundtrip_coords=roundtrip_coords) + (data_in, wcs_in), wcs_out, shape_out=shape_out, roundtrip_coords=roundtrip_coords + ) else: array_out, footprint_out = reproject_interp( - hdu_in, header_out, roundtrip_coords=roundtrip_coords) + hdu_in, header_out, roundtrip_coords=roundtrip_coords + ) return array_footprint_to_hdulist(array_out, footprint_out, header_out) -@pytest.mark.parametrize('roundtrip_coords', (False, True)) +@pytest.mark.parametrize("roundtrip_coords", (False, True)) def test_mwpan_car_to_mol(roundtrip_coords): """ Test reprojection of the Mellinger Milky Way Panorama from CAR to MOL, @@ -154,78 +158,82 @@ def test_mwpan_car_to_mol(roundtrip_coords): reproject 0.3 (https://github.com/astrofrog/reproject/pull/124). """ hdu_in = fits.Header.fromtextfile( - get_pkg_data_filename('data/mwpan2_RGB_3600.hdr', package='reproject.tests')) + get_pkg_data_filename("data/mwpan2_RGB_3600.hdr", package="reproject.tests") + ) with pytest.warns(FITSFixedWarning): wcs_in = WCS(hdu_in, naxis=2) - data_in = np.ones((hdu_in['NAXIS2'], hdu_in['NAXIS1']), dtype=float) + data_in = np.ones((hdu_in["NAXIS2"], hdu_in["NAXIS1"]), dtype=float) header_out = fits.Header() - header_out['NAXIS'] = 2 - header_out['NAXIS1'] = 360 - header_out['NAXIS2'] = 180 - header_out['CRPIX1'] = 180 - header_out['CRPIX2'] = 90 - header_out['CRVAL1'] = 0 - header_out['CRVAL2'] = 0 - header_out['CDELT1'] = -2 * np.sqrt(2) / np.pi - header_out['CDELT2'] = 2 * np.sqrt(2) / np.pi - header_out['CTYPE1'] = 'GLON-MOL' - header_out['CTYPE2'] = 'GLAT-MOL' - header_out['RADESYS'] = 'ICRS' + header_out["NAXIS"] = 2 + header_out["NAXIS1"] = 360 + header_out["NAXIS2"] = 180 + header_out["CRPIX1"] = 180 + header_out["CRPIX2"] = 90 + header_out["CRVAL1"] = 0 + header_out["CRVAL2"] = 0 + header_out["CDELT1"] = -2 * np.sqrt(2) / np.pi + header_out["CDELT2"] = 2 * np.sqrt(2) / np.pi + header_out["CTYPE1"] = "GLON-MOL" + header_out["CTYPE2"] = "GLAT-MOL" + header_out["RADESYS"] = "ICRS" array_out, footprint_out = reproject_interp( - (data_in, wcs_in), header_out, roundtrip_coords=roundtrip_coords) + (data_in, wcs_in), header_out, roundtrip_coords=roundtrip_coords + ) assert np.isfinite(array_out).any() -@pytest.mark.parametrize('roundtrip_coords', (False, True)) +@pytest.mark.parametrize("roundtrip_coords", (False, True)) def test_small_cutout_outside(roundtrip_coords): """ Test reprojection of a cutout from a larger image - in this case the cutout is completely outside the region of the input image so we should take a shortcut that returns arrays of NaNs. """ - with fits.open(get_pkg_data_filename('data/galactic_2d.fits', package='reproject.tests')) as pf: + with fits.open(get_pkg_data_filename("data/galactic_2d.fits", package="reproject.tests")) as pf: hdu_in = pf[0] header_out = hdu_in.header.copy() - header_out['NAXIS1'] = 10 - header_out['NAXIS2'] = 9 - header_out['CTYPE1'] = 'RA---TAN' - header_out['CTYPE2'] = 'DEC--TAN' - header_out['CRVAL1'] = 216.39311 - header_out['CRVAL2'] = -21.939779 - header_out['CRPIX1'] = 5.1 - header_out['CRPIX2'] = 4.7 + header_out["NAXIS1"] = 10 + header_out["NAXIS2"] = 9 + header_out["CTYPE1"] = "RA---TAN" + header_out["CTYPE2"] = "DEC--TAN" + header_out["CRVAL1"] = 216.39311 + header_out["CRVAL2"] = -21.939779 + header_out["CRPIX1"] = 5.1 + header_out["CRPIX2"] = 4.7 array_out, footprint_out = reproject_interp( - hdu_in, header_out, roundtrip_coords=roundtrip_coords) + hdu_in, header_out, roundtrip_coords=roundtrip_coords + ) assert np.all(np.isnan(array_out)) assert np.all(footprint_out == 0) -@pytest.mark.parametrize('roundtrip_coords', (False, True)) +@pytest.mark.parametrize("roundtrip_coords", (False, True)) def test_celestial_mismatch_2d(roundtrip_coords): """ Make sure an error is raised if the input image has celestial WCS information and the output does not (and vice-versa). This example will use the _reproject_celestial route. """ - with fits.open(get_pkg_data_filename('data/galactic_2d.fits', package='reproject.tests')) as pf: + with fits.open(get_pkg_data_filename("data/galactic_2d.fits", package="reproject.tests")) as pf: hdu_in = pf[0] header_out = hdu_in.header.copy() - header_out['CTYPE1'] = 'APPLES' - header_out['CTYPE2'] = 'ORANGES' + header_out["CTYPE1"] = "APPLES" + header_out["CTYPE2"] = "ORANGES" data = hdu_in.data wcs1 = WCS(hdu_in.header) wcs2 = WCS(header_out) - with pytest.raises(ValueError, match="Input WCS has celestial components but output WCS " - "does not"): + with pytest.raises( + ValueError, match="Input WCS has celestial components but output WCS " "does not" + ): array_out, footprint_out = reproject_interp( - (data, wcs1), wcs2, shape_out=(2, 2), - roundtrip_coords=roundtrip_coords) + (data, wcs1), wcs2, shape_out=(2, 2), roundtrip_coords=roundtrip_coords + ) -@pytest.mark.parametrize('roundtrip_coords', (False, True)) +@pytest.mark.parametrize("roundtrip_coords", (False, True)) def test_celestial_mismatch_3d(roundtrip_coords): """ Make sure an error is raised if the input image has celestial WCS @@ -233,72 +241,81 @@ def test_celestial_mismatch_3d(roundtrip_coords): use the _reproject_full route. """ with fits.open( - get_pkg_data_filename('data/equatorial_3d.fits', package='reproject.tests')) as pf: + get_pkg_data_filename("data/equatorial_3d.fits", package="reproject.tests") + ) as pf: hdu_in = pf[0] header_out = hdu_in.header.copy() - header_out['CTYPE1'] = 'APPLES' - header_out['CTYPE2'] = 'ORANGES' - header_out['CTYPE3'] = 'BANANAS' + header_out["CTYPE1"] = "APPLES" + header_out["CTYPE2"] = "ORANGES" + header_out["CTYPE3"] = "BANANAS" data = hdu_in.data wcs1 = WCS(hdu_in.header) wcs2 = WCS(header_out) - with pytest.raises(ValueError, match="Input WCS has celestial components but output WCS " - "does not"): + with pytest.raises( + ValueError, match="Input WCS has celestial components but output WCS " "does not" + ): array_out, footprint_out = reproject_interp( - (data, wcs1), wcs2, shape_out=(1, 2, 3), - roundtrip_coords=roundtrip_coords) + (data, wcs1), wcs2, shape_out=(1, 2, 3), roundtrip_coords=roundtrip_coords + ) - with pytest.raises(ValueError, match="Output WCS has celestial components but input WCS " - "does not"): + with pytest.raises( + ValueError, match="Output WCS has celestial components but input WCS " "does not" + ): array_out, footprint_out = reproject_interp( - (data, wcs2), wcs1, shape_out=(1, 2, 3), - roundtrip_coords=roundtrip_coords) + (data, wcs2), wcs1, shape_out=(1, 2, 3), roundtrip_coords=roundtrip_coords + ) -@pytest.mark.parametrize('roundtrip_coords', (False, True)) +@pytest.mark.parametrize("roundtrip_coords", (False, True)) def test_spectral_mismatch_3d(roundtrip_coords): """ Make sure an error is raised if there are mismatches between the presence or type of spectral axis. """ with fits.open( - get_pkg_data_filename('data/equatorial_3d.fits', package='reproject.tests')) as pf: + get_pkg_data_filename("data/equatorial_3d.fits", package="reproject.tests") + ) as pf: hdu_in = pf[0] header_out = hdu_in.header.copy() - header_out['CTYPE3'] = 'FREQ' - header_out['CUNIT3'] = 'Hz' + header_out["CTYPE3"] = "FREQ" + header_out["CUNIT3"] = "Hz" data = hdu_in.data wcs1 = WCS(hdu_in.header) wcs2 = WCS(header_out) - with pytest.raises(ValueError, match=r"The input \(VOPT\) and output \(FREQ\) spectral " - r"coordinate types are not equivalent\."): + with pytest.raises( + ValueError, + match=r"The input \(VOPT\) and output \(FREQ\) spectral " + r"coordinate types are not equivalent\.", + ): array_out, footprint_out = reproject_interp( - (data, wcs1), wcs2, shape_out=(1, 2, 3), - roundtrip_coords=roundtrip_coords) + (data, wcs1), wcs2, shape_out=(1, 2, 3), roundtrip_coords=roundtrip_coords + ) - header_out['CTYPE3'] = 'BANANAS' + header_out["CTYPE3"] = "BANANAS" wcs2 = WCS(header_out) - with pytest.raises(ValueError, match="Input WCS has a spectral component but output WCS " - "does not"): + with pytest.raises( + ValueError, match="Input WCS has a spectral component but output WCS " "does not" + ): array_out, footprint_out = reproject_interp( - (data, wcs1), wcs2, shape_out=(1, 2, 3), - roundtrip_coords=roundtrip_coords) + (data, wcs1), wcs2, shape_out=(1, 2, 3), roundtrip_coords=roundtrip_coords + ) - with pytest.raises(ValueError, match="Output WCS has a spectral component but input WCS " - "does not"): + with pytest.raises( + ValueError, match="Output WCS has a spectral component but input WCS " "does not" + ): array_out, footprint_out = reproject_interp( - (data, wcs2), wcs1, shape_out=(1, 2, 3), - roundtrip_coords=roundtrip_coords) + (data, wcs2), wcs1, shape_out=(1, 2, 3), roundtrip_coords=roundtrip_coords + ) -@pytest.mark.parametrize('roundtrip_coords', (False, True)) +@pytest.mark.parametrize("roundtrip_coords", (False, True)) def test_naxis_mismatch(roundtrip_coords): """ Make sure an error is raised if the input and output WCS have a different @@ -308,37 +325,39 @@ def test_naxis_mismatch(roundtrip_coords): wcs_in = WCS(naxis=3) wcs_out = WCS(naxis=2) - with pytest.raises(ValueError, match="Number of dimensions between input and output WCS " - "should match"): + with pytest.raises( + ValueError, match="Number of dimensions between input and output WCS " "should match" + ): array_out, footprint_out = reproject_interp( - (data, wcs_in), wcs_out, shape_out=(1, 2), - roundtrip_coords=roundtrip_coords) + (data, wcs_in), wcs_out, shape_out=(1, 2), roundtrip_coords=roundtrip_coords + ) -@pytest.mark.parametrize('roundtrip_coords', (False, True)) +@pytest.mark.parametrize("roundtrip_coords", (False, True)) def test_slice_reprojection(roundtrip_coords): """ Test case where only the slices change and the celestial projection doesn't """ - inp_cube = np.arange(3, dtype='float').repeat(4 * 5).reshape(3, 4, 5) + inp_cube = np.arange(3, dtype="float").repeat(4 * 5).reshape(3, 4, 5) header_in = fits.Header.fromtextfile( - get_pkg_data_filename('data/cube.hdr', package='reproject.tests')) + get_pkg_data_filename("data/cube.hdr", package="reproject.tests") + ) - header_in['NAXIS1'] = 5 - header_in['NAXIS2'] = 4 - header_in['NAXIS3'] = 3 + header_in["NAXIS1"] = 5 + header_in["NAXIS2"] = 4 + header_in["NAXIS3"] = 3 header_out = header_in.copy() - header_out['NAXIS3'] = 2 - header_out['CRPIX3'] -= 0.5 + header_out["NAXIS3"] = 2 + header_out["CRPIX3"] -= 0.5 wcs_in = WCS(header_in) wcs_out = WCS(header_out) out_cube, out_cube_valid = reproject_interp( - (inp_cube, wcs_in), wcs_out, shape_out=(2, 4, 5), - roundtrip_coords=roundtrip_coords) + (inp_cube, wcs_in), wcs_out, shape_out=(2, 4, 5), roundtrip_coords=roundtrip_coords + ) # we expect to be projecting from # inp_cube = np.arange(3, dtype='float').repeat(4*5).reshape(3,4,5) @@ -350,24 +369,27 @@ def test_slice_reprojection(roundtrip_coords): # new_coords, order=1, cval=np.nan, mode='constant')) assert out_cube.shape == (2, 4, 5) - assert out_cube_valid.sum() == 40. + assert out_cube_valid.sum() == 40.0 # We only check that the *valid* pixels are equal # but it's still nice to check that the "valid" array works as a mask - np.testing.assert_allclose(out_cube[out_cube_valid.astype('bool')], - ((inp_cube[:-1] + inp_cube[1:]) / 2.)[out_cube_valid.astype('bool')]) + np.testing.assert_allclose( + out_cube[out_cube_valid.astype("bool")], + ((inp_cube[:-1] + inp_cube[1:]) / 2.0)[out_cube_valid.astype("bool")], + ) # Actually, I fixed it, so now we can test all - np.testing.assert_allclose(out_cube, ((inp_cube[:-1] + inp_cube[1:]) / 2.)) + np.testing.assert_allclose(out_cube, ((inp_cube[:-1] + inp_cube[1:]) / 2.0)) -@pytest.mark.parametrize('roundtrip_coords', (False, True)) +@pytest.mark.parametrize("roundtrip_coords", (False, True)) def test_4d_fails(roundtrip_coords): header_in = fits.Header.fromtextfile( - get_pkg_data_filename('data/cube.hdr', package='reproject.tests')) + get_pkg_data_filename("data/cube.hdr", package="reproject.tests") + ) - header_in['NAXIS'] = 4 + header_in["NAXIS"] = 4 header_out = header_in.copy() w_in = WCS(header_in) @@ -375,99 +397,108 @@ def test_4d_fails(roundtrip_coords): array_in = np.zeros((2, 3, 4, 5)) - with pytest.raises(ValueError, match="Length of shape_out should match number of dimensions " - "in wcs_out"): + with pytest.raises( + ValueError, match="Length of shape_out should match number of dimensions " "in wcs_out" + ): x_out, y_out, z_out = reproject_interp( - (array_in, w_in), w_out, shape_out=[2, 4, 5, 6], - roundtrip_coords=roundtrip_coords) + (array_in, w_in), w_out, shape_out=[2, 4, 5, 6], roundtrip_coords=roundtrip_coords + ) -@pytest.mark.parametrize('roundtrip_coords', (False, True)) +@pytest.mark.parametrize("roundtrip_coords", (False, True)) def test_inequal_wcs_dims(roundtrip_coords): - inp_cube = np.arange(3, dtype='float').repeat(4 * 5).reshape(3, 4, 5) + inp_cube = np.arange(3, dtype="float").repeat(4 * 5).reshape(3, 4, 5) header_in = fits.Header.fromtextfile( - get_pkg_data_filename('data/cube.hdr', package='reproject.tests')) + get_pkg_data_filename("data/cube.hdr", package="reproject.tests") + ) header_out = header_in.copy() - header_out['CTYPE3'] = 'VRAD' - header_out['CUNIT3'] = 'm/s' - header_in['CTYPE3'] = 'STOKES' - header_in['CUNIT3'] = '' + header_out["CTYPE3"] = "VRAD" + header_out["CUNIT3"] = "m/s" + header_in["CTYPE3"] = "STOKES" + header_in["CUNIT3"] = "" wcs_out = WCS(header_out) - with pytest.raises(ValueError, match="Output WCS has a spectral component but input WCS " - "does not"): + with pytest.raises( + ValueError, match="Output WCS has a spectral component but input WCS " "does not" + ): out_cube, out_cube_valid = reproject_interp( - (inp_cube, header_in), wcs_out, shape_out=(2, 4, 5), - roundtrip_coords=roundtrip_coords) + (inp_cube, header_in), wcs_out, shape_out=(2, 4, 5), roundtrip_coords=roundtrip_coords + ) -@pytest.mark.parametrize('roundtrip_coords', (False, True)) +@pytest.mark.parametrize("roundtrip_coords", (False, True)) def test_different_wcs_types(roundtrip_coords): - inp_cube = np.arange(3, dtype='float').repeat(4 * 5).reshape(3, 4, 5) + inp_cube = np.arange(3, dtype="float").repeat(4 * 5).reshape(3, 4, 5) header_in = fits.Header.fromtextfile( - get_pkg_data_filename('data/cube.hdr', package='reproject.tests')) + get_pkg_data_filename("data/cube.hdr", package="reproject.tests") + ) header_out = header_in.copy() - header_out['CTYPE3'] = 'VRAD' - header_out['CUNIT3'] = 'm/s' - header_in['CTYPE3'] = 'VELO' - header_in['CUNIT3'] = 'm/s' + header_out["CTYPE3"] = "VRAD" + header_out["CUNIT3"] = "m/s" + header_in["CTYPE3"] = "VELO" + header_in["CUNIT3"] = "m/s" wcs_out = WCS(header_out) - with pytest.raises(ValueError, match=r"The input \(VELO\) and output \(VRAD\) spectral " - r"coordinate types are not equivalent\."): + with pytest.raises( + ValueError, + match=r"The input \(VELO\) and output \(VRAD\) spectral " + r"coordinate types are not equivalent\.", + ): out_cube, out_cube_valid = reproject_interp( - (inp_cube, header_in), wcs_out, shape_out=(2, 4, 5), - roundtrip_coords=roundtrip_coords) + (inp_cube, header_in), wcs_out, shape_out=(2, 4, 5), roundtrip_coords=roundtrip_coords + ) + # TODO: add a test to check the units are the same. -@pytest.mark.parametrize('roundtrip_coords', (False, True)) +@pytest.mark.parametrize("roundtrip_coords", (False, True)) def test_reproject_3d_celestial_correctness_ra2gal(roundtrip_coords): - inp_cube = np.arange(3, dtype='float').repeat(7 * 8).reshape(3, 7, 8) + inp_cube = np.arange(3, dtype="float").repeat(7 * 8).reshape(3, 7, 8) header_in = fits.Header.fromtextfile( - get_pkg_data_filename('data/cube.hdr', package='reproject.tests')) + get_pkg_data_filename("data/cube.hdr", package="reproject.tests") + ) - header_in['NAXIS1'] = 8 - header_in['NAXIS2'] = 7 - header_in['NAXIS3'] = 3 + header_in["NAXIS1"] = 8 + header_in["NAXIS2"] = 7 + header_in["NAXIS3"] = 3 header_out = header_in.copy() - header_out['CTYPE1'] = 'GLON-TAN' - header_out['CTYPE2'] = 'GLAT-TAN' - header_out['CRVAL1'] = 158.5644791 - header_out['CRVAL2'] = -21.59589875 + header_out["CTYPE1"] = "GLON-TAN" + header_out["CTYPE2"] = "GLAT-TAN" + header_out["CRVAL1"] = 158.5644791 + header_out["CRVAL2"] = -21.59589875 # make the cube a cutout approximately in the center of the other one, but smaller - header_out['NAXIS1'] = 4 - header_out['CRPIX1'] = 2 - header_out['NAXIS2'] = 3 - header_out['CRPIX2'] = 1.5 + header_out["NAXIS1"] = 4 + header_out["CRPIX1"] = 2 + header_out["NAXIS2"] = 3 + header_out["CRPIX2"] = 1.5 - header_out['NAXIS3'] = 2 - header_out['CRPIX3'] -= 0.5 + header_out["NAXIS3"] = 2 + header_out["CRPIX3"] -= 0.5 wcs_in = WCS(header_in) wcs_out = WCS(header_out) out_cube, out_cube_valid = reproject_interp( - (inp_cube, wcs_in), wcs_out, shape_out=(2, 3, 4), - roundtrip_coords=roundtrip_coords) + (inp_cube, wcs_in), wcs_out, shape_out=(2, 3, 4), roundtrip_coords=roundtrip_coords + ) assert out_cube.shape == (2, 3, 4) assert out_cube_valid.sum() == out_cube.size # only compare the spectral axis - np.testing.assert_allclose(out_cube[:, 0, 0], ((inp_cube[:-1] + inp_cube[1:]) / 2.)[:, 0, 0]) + np.testing.assert_allclose(out_cube[:, 0, 0], ((inp_cube[:-1] + inp_cube[1:]) / 2.0)[:, 0, 0]) -@pytest.mark.parametrize('roundtrip_coords', (False, True)) +@pytest.mark.parametrize("roundtrip_coords", (False, True)) def test_reproject_with_output_array(roundtrip_coords): """ Test both full_reproject and slicewise reprojection. We use a case where the @@ -475,7 +506,8 @@ def test_reproject_with_output_array(roundtrip_coords): work. """ header_in = fits.Header.fromtextfile( - get_pkg_data_filename('data/cube.hdr', package='reproject.tests')) + get_pkg_data_filename("data/cube.hdr", package="reproject.tests") + ) array_in = np.ones((3, 200, 180)) shape_out = (3, 160, 170) @@ -483,21 +515,24 @@ def test_reproject_with_output_array(roundtrip_coords): wcs_in = WCS(header_in) wcs_out = wcs_in.deepcopy() - wcs_out.wcs.ctype = ['GLON-SIN', 'GLAT-SIN', wcs_in.wcs.ctype[2]] + wcs_out.wcs.ctype = ["GLON-SIN", "GLAT-SIN", wcs_in.wcs.ctype[2]] wcs_out.wcs.crval = [158.0501, -21.530282, wcs_in.wcs.crval[2]] - wcs_out.wcs.crpix = [50., 50., wcs_in.wcs.crpix[2] + 0.4] + wcs_out.wcs.crpix = [50.0, 50.0, wcs_in.wcs.crpix[2] + 0.4] # TODO when someone learns how to do it: make sure the memory isn't duplicated... - returned_array = reproject_interp((array_in, wcs_in), wcs_out, - output_array=out_full, - return_footprint=False, - roundtrip_coords=roundtrip_coords) + returned_array = reproject_interp( + (array_in, wcs_in), + wcs_out, + output_array=out_full, + return_footprint=False, + roundtrip_coords=roundtrip_coords, + ) assert out_full is returned_array @pytest.mark.array_compare(single_reference=True) -@pytest.mark.parametrize('file_format', ['fits', 'asdf']) +@pytest.mark.parametrize("file_format", ["fits", "asdf"]) def test_reproject_roundtrip(file_format): # Test the reprojection with solar data, which ensures that the masking of @@ -505,36 +540,37 @@ def test_reproject_roundtrip(file_format): # about testing a different format but making sure that GWCS works. # The observer handling changed in 2.1. - pytest.importorskip('sunpy', minversion='2.1.0') + pytest.importorskip("sunpy", minversion="2.1.0") from sunpy.coordinates.ephemeris import get_body_heliographic_stonyhurst from sunpy.map import Map - if file_format == 'fits': - map_aia = Map(get_pkg_data_filename('data/aia_171_level1.fits', package='reproject.tests')) + if file_format == "fits": + map_aia = Map(get_pkg_data_filename("data/aia_171_level1.fits", package="reproject.tests")) data = map_aia.data wcs = map_aia.wcs date = map_aia.date target_wcs = wcs.deepcopy() - elif file_format == 'asdf': - pytest.importorskip('astropy', minversion='4.0') - pytest.importorskip('gwcs', minversion='0.12') - asdf = pytest.importorskip('asdf') + elif file_format == "asdf": + pytest.importorskip("astropy", minversion="4.0") + pytest.importorskip("gwcs", minversion="0.12") + asdf = pytest.importorskip("asdf") aia = asdf.open( - get_pkg_data_filename('data/aia_171_level1.asdf', package='reproject.tests')) - data = aia['data'][...] - wcs = aia['wcs'] + get_pkg_data_filename("data/aia_171_level1.asdf", package="reproject.tests") + ) + data = aia["data"][...] + wcs = aia["wcs"] date = wcs.output_frame.reference_frame.obstime target_wcs = Map( - get_pkg_data_filename('data/aia_171_level1.fits', - package='reproject.tests')).wcs.deepcopy() + get_pkg_data_filename("data/aia_171_level1.fits", package="reproject.tests") + ).wcs.deepcopy() else: - raise ValueError('file_format should be fits or asdf') + raise ValueError("file_format should be fits or asdf") # Reproject to an observer on Venus - target_wcs.wcs.cdelt = ([24, 24]*u.arcsec).to(u.deg) + target_wcs.wcs.cdelt = ([24, 24] * u.arcsec).to(u.deg) target_wcs.wcs.crpix = [64, 64] - venus = get_body_heliographic_stonyhurst('venus', date) + venus = get_body_heliographic_stonyhurst("venus", date) target_wcs.wcs.aux.hgln_obs = venus.lon.to_value(u.deg) target_wcs.wcs.aux.hglt_obs = venus.lat.to_value(u.deg) target_wcs.wcs.aux.dsun_obs = venus.radius.to_value(u.m) @@ -546,15 +582,25 @@ def test_reproject_roundtrip(file_format): # ASTROPY_LT_40: astropy v4.0 introduced new default header keywords, # once we support only astropy 4.0 and later we can update the reference # data files and remove this section. - for key in ('CRLN_OBS', 'CRLT_OBS', 'DSUN_OBS', 'HGLN_OBS', 'HGLT_OBS', - 'MJDREFF', 'MJDREFI', 'MJDREF', 'MJD-OBS', 'RSUN_REF'): + for key in ( + "CRLN_OBS", + "CRLT_OBS", + "DSUN_OBS", + "HGLN_OBS", + "HGLT_OBS", + "MJDREFF", + "MJDREFI", + "MJDREF", + "MJD-OBS", + "RSUN_REF", + ): header_out.pop(key, None) - header_out['DATE-OBS'] = header_out['DATE-OBS'].replace('T', ' ') + header_out["DATE-OBS"] = header_out["DATE-OBS"].replace("T", " ") return array_footprint_to_hdulist(output, footprint, header_out) -@pytest.mark.parametrize('roundtrip_coords', (False, True)) +@pytest.mark.parametrize("roundtrip_coords", (False, True)) def test_identity_with_offset(roundtrip_coords): # Reproject an array and WCS to itself but with a margin, which should @@ -562,11 +608,11 @@ def test_identity_with_offset(roundtrip_coords): # values to extend beyond the original footprint. wcs = WCS(naxis=2) - wcs.wcs.ctype = 'RA---TAN', 'DEC--TAN' + wcs.wcs.ctype = "RA---TAN", "DEC--TAN" wcs.wcs.crpix = 322, 151 wcs.wcs.crval = 43, 23 wcs.wcs.cdelt = -0.1, 0.1 - wcs.wcs.equinox = 2000. + wcs.wcs.equinox = 2000.0 array_in = np.random.random((233, 123)) @@ -575,23 +621,23 @@ def test_identity_with_offset(roundtrip_coords): shape_out = (array_in.shape[0] + 2, array_in.shape[1] + 2) array_out, footprint = reproject_interp( - (array_in, wcs), wcs_out, shape_out=shape_out, - roundtrip_coords=roundtrip_coords) + (array_in, wcs), wcs_out, shape_out=shape_out, roundtrip_coords=roundtrip_coords + ) - expected = np.pad(array_in, 1, 'constant', constant_values=np.nan) + expected = np.pad(array_in, 1, "constant", constant_values=np.nan) assert_allclose(expected, array_out, atol=1e-10) -@pytest.mark.parametrize('parallel', [True, 2, False]) -@pytest.mark.parametrize('block_size', [[10, 10], [500, 500], [500, 100], None]) +@pytest.mark.parametrize("parallel", [True, 2, False]) +@pytest.mark.parametrize("block_size", [[10, 10], [500, 500], [500, 100], None]) def test_blocked_against_single(parallel, block_size): # Ensure when we break a reprojection down into multiple discrete blocks # it has the same result as if all pixels where reprejcted at once - hdu1 = fits.open(get_pkg_data_filename('galactic_center/gc_2mass_k.fits'))[0] - hdu2 = fits.open(get_pkg_data_filename('galactic_center/gc_msx_e.fits'))[0] + hdu1 = fits.open(get_pkg_data_filename("galactic_center/gc_2mass_k.fits"))[0] + hdu2 = fits.open(get_pkg_data_filename("galactic_center/gc_msx_e.fits"))[0] array_test = None footprint_test = None @@ -601,8 +647,9 @@ def test_blocked_against_single(parallel, block_size): # All the warning code should be removed when old version no longer being used # Using context manager ensure only blocked function has them ignored import warnings + with warnings.catch_warnings(): - warnings.simplefilter('ignore', category=FITSFixedWarning) + warnings.simplefilter("ignore", category=FITSFixedWarning) # this one is needed to avoid the following warning from when the np.as_strided() is # called in wcs_utils.unbroadcast(), only shows up with py3.8, numpy1.17, astropy 4.0.*: @@ -614,14 +661,16 @@ def test_blocked_against_single(parallel, block_size): # The pixel values all still match in the end, only shows up due to pytest clearing # the standard python warning filters by default and failing as the warnings are now # treated as the exceptions they're implemented on - if (block_size == [10, 10]): - warnings.simplefilter('ignore', category=DeprecationWarning) + if block_size == [10, 10]: + warnings.simplefilter("ignore", category=DeprecationWarning) - array_test, footprint_test = reproject_interp(hdu2, hdu1.header, - parallel=parallel, block_size=block_size) + array_test, footprint_test = reproject_interp( + hdu2, hdu1.header, parallel=parallel, block_size=block_size + ) - array_reference, footprint_reference = reproject_interp(hdu2, hdu1.header, - parallel=False, block_size=None) + array_reference, footprint_reference = reproject_interp( + hdu2, hdu1.header, parallel=False, block_size=None + ) np.testing.assert_allclose(array_test, array_reference, equal_nan=True) np.testing.assert_allclose(footprint_test, footprint_reference, equal_nan=True) @@ -639,20 +688,21 @@ def test_blocked_corner_cases(): """ # Read in the input cube - hdu_in = fits.open( - get_pkg_data_filename('data/equatorial_3d.fits', package='reproject.tests'))[0] + hdu_in = fits.open(get_pkg_data_filename("data/equatorial_3d.fits", package="reproject.tests"))[ + 0 + ] # Define the output header - this should be the same for all versions of # this test to make sure we can use a single reference file. header_out = hdu_in.header.copy() - header_out['NAXIS1'] = 10 - header_out['NAXIS2'] = 9 - header_out['CTYPE1'] = 'GLON-SIN' - header_out['CTYPE2'] = 'GLAT-SIN' - header_out['CRVAL1'] = 163.16724 - header_out['CRVAL2'] = -15.777405 - header_out['CRPIX1'] = 6 - header_out['CRPIX2'] = 5 + header_out["NAXIS1"] = 10 + header_out["NAXIS2"] = 9 + header_out["CTYPE1"] = "GLON-SIN" + header_out["CTYPE2"] = "GLAT-SIN" + header_out["CRVAL1"] = 163.16724 + header_out["CRVAL2"] = -15.777405 + header_out["CRPIX1"] = 6 + header_out["CRPIX2"] = 5 array_reference = reproject_interp(hdu_in, header_out, return_footprint=False) @@ -660,10 +710,12 @@ def test_blocked_corner_cases(): # same reason as test above for FITSFixedWarning import warnings + with warnings.catch_warnings(): - warnings.simplefilter('ignore', category=FITSFixedWarning) + warnings.simplefilter("ignore", category=FITSFixedWarning) - array_test = reproject_interp(hdu_in, header_out, parallel=True, - block_size=[0, 4], return_footprint=False) + array_test = reproject_interp( + hdu_in, header_out, parallel=True, block_size=[0, 4], return_footprint=False + ) np.testing.assert_allclose(array_test, array_reference, equal_nan=True, verbose=True) diff --git a/reproject/mosaicking/background.py b/reproject/mosaicking/background.py index 336c71b52..9a57e39a7 100644 --- a/reproject/mosaicking/background.py +++ b/reproject/mosaicking/background.py @@ -4,7 +4,7 @@ import numpy as np -__all__ = ['solve_corrections_sgd', 'determine_offset_matrix'] +__all__ = ["solve_corrections_sgd", "determine_offset_matrix"] def determine_offset_matrix(arrays): @@ -33,8 +33,7 @@ def determine_offset_matrix(arrays): return offset_matrix -def solve_corrections_sgd(offset_matrix, eta_initial=1, eta_half_life=100, - rtol=1e-10, atol=0): +def solve_corrections_sgd(offset_matrix, eta_initial=1, eta_half_life=100, rtol=1e-10, atol=0): r""" Given a matrix of offsets from each image to each other image, find the optimal offsets to use using Stochastic Gradient Descent. @@ -74,8 +73,7 @@ def solve_corrections_sgd(offset_matrix, eta_initial=1, eta_half_life=100, converged. """ - if (offset_matrix.ndim != 2 or - offset_matrix.shape[0] != offset_matrix.shape[1]): + if offset_matrix.ndim != 2 or offset_matrix.shape[0] != offset_matrix.shape[1]: raise ValueError("offset_matrix should be a square NxN matrix") N = offset_matrix.shape[0] @@ -93,7 +91,7 @@ def solve_corrections_sgd(offset_matrix, eta_initial=1, eta_half_life=100, np.random.shuffle(indices) # Update learning rate - eta = eta_initial * exp(-iteration/eta_half_life) + eta = eta_initial * exp(-iteration / eta_half_life) # Loop over each index and update the offset. What we call rows and # columns is arbitrary, but for the purpose of the comments below, we @@ -114,16 +112,14 @@ def solve_corrections_sgd(offset_matrix, eta_initial=1, eta_half_life=100, # The difference between the actual row in the matrix and this # fitted row gives us a measure of the gradient, so we then # adjust the solution in that direction. - corrections[i] += eta * np.mean(offset_matrix[i, keep] - - fitted_offset_matrix_row) + corrections[i] += eta * np.mean(offset_matrix[i, keep] - fitted_offset_matrix_row) # Subtract the mean offset from the offsets to make sure that the # corrections stay centered around zero corrections -= np.nanmean(corrections) if previous_corrections is not None: - if np.allclose(corrections, previous_corrections, - rtol=rtol, atol=atol): + if np.allclose(corrections, previous_corrections, rtol=rtol, atol=atol): break # the algorithm has converged previous_corrections = corrections.copy() diff --git a/reproject/mosaicking/coadd.py b/reproject/mosaicking/coadd.py index 5a4a15778..f5084ff44 100644 --- a/reproject/mosaicking/coadd.py +++ b/reproject/mosaicking/coadd.py @@ -6,13 +6,22 @@ from .background import determine_offset_matrix, solve_corrections_sgd from .subset_array import ReprojectedArraySubset -__all__ = ['reproject_and_coadd'] - - -def reproject_and_coadd(input_data, output_projection, shape_out=None, - input_weights=None, hdu_in=None, reproject_function=None, - hdu_weights=None, combine_function='mean', match_background=False, - background_reference=None, **kwargs): +__all__ = ["reproject_and_coadd"] + + +def reproject_and_coadd( + input_data, + output_projection, + shape_out=None, + input_weights=None, + hdu_in=None, + reproject_function=None, + hdu_weights=None, + combine_function="mean", + match_background=False, + background_reference=None, + **kwargs, +): """ Given a set of input images, reproject and co-add these to a single final image. @@ -83,17 +92,17 @@ def reproject_and_coadd(input_data, output_projection, shape_out=None, # Validate inputs - if combine_function not in ('mean', 'sum', 'median'): + if combine_function not in ("mean", "sum", "median"): raise ValueError("combine_function should be one of mean/sum/median") if reproject_function is None: - raise ValueError("reprojection function should be specified with " - "the reproject_function argument") + raise ValueError( + "reprojection function should be specified with " "the reproject_function argument" + ) # Parse the output projection to avoid having to do it for each - wcs_out, shape_out = parse_output_projection(output_projection, - shape_out=shape_out) + wcs_out, shape_out = parse_output_projection(output_projection, shape_out=shape_out) # Start off by reprojecting individual images to the final projection @@ -153,32 +162,35 @@ def reproject_and_coadd(input_data, output_projection, shape_out=None, # able to handle weights, and make the footprint become the combined # footprint + weight map - array, footprint = reproject_function((array_in, wcs_in), - output_projection=wcs_out_indiv, - shape_out=shape_out_indiv, - hdu_in=hdu_in, - **kwargs) + array, footprint = reproject_function( + (array_in, wcs_in), + output_projection=wcs_out_indiv, + shape_out=shape_out_indiv, + hdu_in=hdu_in, + **kwargs, + ) if weights_in is not None: - weights, _ = reproject_function((weights_in, wcs_in), - output_projection=wcs_out_indiv, - shape_out=shape_out_indiv, - hdu_in=hdu_in, - **kwargs) + weights, _ = reproject_function( + (weights_in, wcs_in), + output_projection=wcs_out_indiv, + shape_out=shape_out_indiv, + hdu_in=hdu_in, + **kwargs, + ) # For the purposes of mosaicking, we mask out NaN values from the array # and set the footprint to 0 at these locations. reset = np.isnan(array) - array[reset] = 0. - footprint[reset] = 0. + array[reset] = 0.0 + footprint[reset] = 0.0 # Combine weights and footprint if weights_in is not None: - weights[reset] = 0. + weights[reset] = 0.0 footprint *= weights - array = ReprojectedArraySubset(array, footprint, - imin, imax, jmin, jmax) + array = ReprojectedArraySubset(array, footprint, imin, imax, jmin, jmax) # TODO: make sure we gracefully handle the case where the # output image is empty (due e.g. to no overlap). @@ -201,7 +213,7 @@ def reproject_and_coadd(input_data, output_projection, shape_out=None, final_array = np.zeros(shape_out) final_footprint = np.zeros(shape_out) - if combine_function in ('mean', 'sum'): + if combine_function in ("mean", "sum"): for array in arrays: @@ -213,16 +225,15 @@ def reproject_and_coadd(input_data, output_projection, shape_out=None, final_array[array.view_in_original_array] += array.array * array.footprint final_footprint[array.view_in_original_array] += array.footprint - if combine_function == 'mean': - with np.errstate(invalid='ignore'): + if combine_function == "mean": + with np.errstate(invalid="ignore"): final_array /= final_footprint - elif combine_function == 'median': + elif combine_function == "median": # Here we need to operate in chunks since we could otherwise run # into memory issues - raise NotImplementedError("combine_function='median' is " - "not yet implemented") + raise NotImplementedError("combine_function='median' is " "not yet implemented") return final_array, final_footprint diff --git a/reproject/mosaicking/subset_array.py b/reproject/mosaicking/subset_array.py index c5553559a..ac86412a7 100644 --- a/reproject/mosaicking/subset_array.py +++ b/reproject/mosaicking/subset_array.py @@ -2,7 +2,7 @@ import operator -__all__ = ['ReprojectedArraySubset'] +__all__ = ["ReprojectedArraySubset"] class ReprojectedArraySubset: @@ -26,8 +26,7 @@ def __init__(self, array, footprint, imin, imax, jmin, jmax): self.jmax = jmax def __repr__(self): - return ('' - .format(self.jmin, self.jmax, self.imin, self.imax)) + return f"" @property def view_in_original_array(self): @@ -41,8 +40,12 @@ def overlaps(self, other): # Note that the use of <= or >= instead of < and > is due to # the fact that the max values are exclusive (so +1 above the # last value). - return not (self.imax <= other.imin or other.imax <= self.imin or - self.jmax <= other.jmin or other.jmax <= self.jmin) + return not ( + self.imax <= other.imin + or other.imax <= self.imin + or self.jmax <= other.jmin + or other.jmax <= self.jmin + ) def __add__(self, other): return self._operation(other, operator.add) @@ -73,15 +76,23 @@ def _operation(self, other, op): # Extract cutout from each - self_array = self.array[jmin - self.jmin:jmax - self.jmin, - imin - self.imin:imax - self.imin] - self_footprint = self.footprint[jmin - self.jmin:jmax - self.jmin, - imin - self.imin:imax - self.imin] - - other_array = other.array[jmin - other.jmin:jmax - other.jmin, - imin - other.imin:imax - other.imin] - other_footprint = other.footprint[jmin - other.jmin:jmax - other.jmin, - imin - other.imin:imax - other.imin] + self_array = self.array[ + jmin - self.jmin : jmax - self.jmin, + imin - self.imin : imax - self.imin, + ] + self_footprint = self.footprint[ + jmin - self.jmin : jmax - self.jmin, + imin - self.imin : imax - self.imin, + ] + + other_array = other.array[ + jmin - other.jmin : jmax - other.jmin, + imin - other.imin : imax - other.imin, + ] + other_footprint = other.footprint[ + jmin - other.jmin : jmax - other.jmin, + imin - other.imin : imax - other.imin, + ] # Carry out operator and store result in ReprojectedArraySubset diff --git a/reproject/mosaicking/tests/test_background.py b/reproject/mosaicking/tests/test_background.py index 8503ba7a4..e9e78e741 100644 --- a/reproject/mosaicking/tests/test_background.py +++ b/reproject/mosaicking/tests/test_background.py @@ -7,14 +7,10 @@ from ..background import solve_corrections_sgd # Try and cover a range of matrix sizes and absolute scales of corrections -CASES = [(4, 1.), - (33, 1e30), - (44, 1e-50), - (132, 1e10), - (1441, 1e-5)] +CASES = [(4, 1.0), (33, 1e30), (44, 1e-50), (132, 1e10), (1441, 1e-5)] -@pytest.mark.parametrize(('N', 'scale'), CASES) +@pytest.mark.parametrize(("N", "scale"), CASES) def test_solve_corrections_sgd(N, scale): # Generate random corrections diff --git a/reproject/mosaicking/tests/test_coadd.py b/reproject/mosaicking/tests/test_coadd.py index eb536f240..95e552fc6 100644 --- a/reproject/mosaicking/tests/test_coadd.py +++ b/reproject/mosaicking/tests/test_coadd.py @@ -14,27 +14,25 @@ from reproject.mosaicking.coadd import reproject_and_coadd from reproject.tests.helpers import array_footprint_to_hdulist -ATOL = 1.e-9 +ATOL = 1.0e-9 -DATA = os.path.join(os.path.dirname(__file__), '..', '..', 'tests', 'data') +DATA = os.path.join(os.path.dirname(__file__), "..", "..", "tests", "data") -@pytest.fixture(params=[reproject_interp, reproject_exact], - ids=["interp", "exact"]) +@pytest.fixture(params=[reproject_interp, reproject_exact], ids=["interp", "exact"]) def reproject_function(request): return request.param -class TestReprojectAndCoAdd(): - +class TestReprojectAndCoAdd: def setup_method(self, method): self.wcs = WCS(naxis=2) - self.wcs.wcs.ctype = 'RA---TAN', 'DEC--TAN' + self.wcs.wcs.ctype = "RA---TAN", "DEC--TAN" self.wcs.wcs.crpix = 322, 151 self.wcs.wcs.crval = 43, 23 self.wcs.wcs.cdelt = -0.1, 0.1 - self.wcs.wcs.equinox = 2000. + self.wcs.wcs.equinox = 2000.0 self.array = np.random.random((399, 334)) @@ -63,7 +61,7 @@ def _nonoverlapping_views(self): views = [] for i in range(4): for j in range(5): - views.append((je[j], je[j+1], ie[i], ie[i+1])) + views.append((je[j], je[j + 1], ie[i], ie[i + 1])) return views @@ -76,11 +74,11 @@ def _overlapping_views(self): views = [] for i in range(4): for j in range(5): - views.append((je[j], je[j+1] + 10, ie[i], ie[i+1] + 10)) + views.append((je[j], je[j + 1] + 10, ie[i], ie[i + 1] + 10)) return views - @pytest.mark.parametrize('combine_function', ['mean', 'sum']) + @pytest.mark.parametrize("combine_function", ["mean", "sum"]) def test_coadd_no_overlap(self, combine_function, reproject_function): # Make sure that if all tiles are exactly non-overlapping, and @@ -89,10 +87,13 @@ def test_coadd_no_overlap(self, combine_function, reproject_function): input_data = self._get_tiles(self._nonoverlapping_views) input_data = [(self.array, self.wcs)] - array, footprint = reproject_and_coadd(input_data, self.wcs, - shape_out=self.array.shape, - combine_function=combine_function, - reproject_function=reproject_function) + array, footprint = reproject_and_coadd( + input_data, + self.wcs, + shape_out=self.array.shape, + combine_function=combine_function, + reproject_function=reproject_function, + ) assert_allclose(array, self.array, atol=ATOL) assert_allclose(footprint, 1, atol=ATOL) @@ -104,10 +105,13 @@ def test_coadd_with_overlap(self, reproject_function): input_data = self._get_tiles(self._overlapping_views) - array, footprint = reproject_and_coadd(input_data, self.wcs, - shape_out=self.array.shape, - combine_function='mean', - reproject_function=reproject_function) + array, footprint = reproject_and_coadd( + input_data, + self.wcs, + shape_out=self.array.shape, + combine_function="mean", + reproject_function=reproject_function, + ) assert_allclose(array, self.array, atol=ATOL) @@ -122,26 +126,31 @@ def test_coadd_background_matching(self, reproject_function): # First check that without background matching the arrays don't match - array, footprint = reproject_and_coadd(input_data, self.wcs, - shape_out=self.array.shape, - combine_function='mean', - reproject_function=reproject_function) + array, footprint = reproject_and_coadd( + input_data, + self.wcs, + shape_out=self.array.shape, + combine_function="mean", + reproject_function=reproject_function, + ) assert not np.allclose(array, self.array, atol=ATOL) # Now check that once the backgrounds are matched the values agree - array, footprint = reproject_and_coadd(input_data, self.wcs, - shape_out=self.array.shape, - combine_function='mean', - reproject_function=reproject_function, - match_background=True) + array, footprint = reproject_and_coadd( + input_data, + self.wcs, + shape_out=self.array.shape, + combine_function="mean", + reproject_function=reproject_function, + match_background=True, + ) # The absolute values of the two arrays will be offset since any # solution that reproduces the offsets between images is valid - assert_allclose(array - np.mean(array), - self.array - np.mean(self.array), atol=ATOL) + assert_allclose(array - np.mean(array), self.array - np.mean(self.array), atol=ATOL) def test_coadd_background_matching_with_nan(self, reproject_function): @@ -160,20 +169,22 @@ def test_coadd_background_matching_with_nan(self, reproject_function): input_data = [(array1, self.wcs), (array2, self.wcs), (array3, self.wcs)] - array, footprint = reproject_and_coadd(input_data, self.wcs, - shape_out=self.array.shape, - combine_function='mean', - reproject_function=reproject_function, - match_background=True) + array, footprint = reproject_and_coadd( + input_data, + self.wcs, + shape_out=self.array.shape, + combine_function="mean", + reproject_function=reproject_function, + match_background=True, + ) # The absolute values of the two arrays will be offset since any # solution that reproduces the offsets between images is valid - assert_allclose(array - np.mean(array), - self.array - np.mean(self.array), atol=ATOL) + assert_allclose(array - np.mean(array), self.array - np.mean(self.array), atol=ATOL) - @pytest.mark.filterwarnings('ignore:unclosed file:ResourceWarning') - @pytest.mark.parametrize('mode', ['arrays', 'filenames', 'hdus']) + @pytest.mark.filterwarnings("ignore:unclosed file:ResourceWarning") + @pytest.mark.parametrize("mode", ["arrays", "filenames", "hdus"]) def test_coadd_with_weights(self, tmpdir, reproject_function, mode): # Make sure that things work properly when specifying weights @@ -186,25 +197,28 @@ def test_coadd_with_weights(self, tmpdir, reproject_function, mode): input_data = [(array1, self.wcs), (array2, self.wcs)] - if mode == 'arrays': + if mode == "arrays": input_weights = [weight1, weight2] - elif mode == 'filenames': - filename1 = tmpdir.join('weight1.fits').strpath - filename2 = tmpdir.join('weight2.fits').strpath + elif mode == "filenames": + filename1 = tmpdir.join("weight1.fits").strpath + filename2 = tmpdir.join("weight2.fits").strpath fits.writeto(filename1, weight1) fits.writeto(filename2, weight2) input_weights = [filename1, filename2] - elif mode == 'hdus': + elif mode == "hdus": hdu1 = fits.ImageHDU(weight1) hdu2 = fits.ImageHDU(weight2) input_weights = [hdu1, hdu2] - array, footprint = reproject_and_coadd(input_data, self.wcs, - shape_out=self.array.shape, - combine_function='mean', - input_weights=input_weights, - reproject_function=reproject_function, - match_background=False) + array, footprint = reproject_and_coadd( + input_data, + self.wcs, + shape_out=self.array.shape, + combine_function="mean", + input_weights=input_weights, + reproject_function=reproject_function, + match_background=False, + ) expected = self.array + (2 * (weight1 / weight1.max()) - 1) @@ -242,11 +256,11 @@ def test_coadd_solar_map(): # The reference image was generated for sunpy 3.0.1 - it will not work with # previous versions due to the bug that https://github.com/sunpy/sunpy/pull/5381 # fixes. - pytest.importorskip('sunpy', minversion='3.0.1') + pytest.importorskip("sunpy", minversion="3.0.1") from sunpy.map import Map, all_coordinates_from_map # Load in three images from different viewpoints around the Sun - filenames = ['secchi_l0_a.fits', 'aia_171_level1.fits', 'secchi_l0_b.fits'] + filenames = ["secchi_l0_a.fits", "aia_171_level1.fits", "secchi_l0_b.fits"] maps = [Map(os.path.join(DATA, f)) for f in filenames] # Produce weight maps that are centered on the solar disk and go to zero at the edges @@ -255,22 +269,26 @@ def test_coadd_solar_map(): input_weights = [(w / np.nanmax(w)) ** 4 for w in input_weights] shape_out = [90, 180] - wcs_out = WCS(Header.fromstring(HEADER_SOLAR_OUT, sep='\n')) - scales = [1/6, 1, 1/6] + wcs_out = WCS(Header.fromstring(HEADER_SOLAR_OUT, sep="\n")) + scales = [1 / 6, 1, 1 / 6] input_data = tuple((a.data * scale, a.wcs) for (a, scale) in zip(maps, scales)) - array, footprint = reproject_and_coadd(input_data, wcs_out, shape_out, - input_weights=input_weights, - reproject_function=reproject_interp, - match_background=True) + array, footprint = reproject_and_coadd( + input_data, + wcs_out, + shape_out, + input_weights=input_weights, + reproject_function=reproject_interp, + match_background=True, + ) header_out = wcs_out.to_header() # ASTROPY_LT_40: astropy v4.0 introduced new default header keywords, # once we support only astropy 4.0 and later we can update the reference # data files and remove this section. - for key in ('MJDREFF', 'MJDREFI', 'MJDREF', 'MJD-OBS'): + for key in ("MJDREFF", "MJDREFI", "MJDREF", "MJD-OBS"): header_out.pop(key, None) return array_footprint_to_hdulist(array, footprint, header_out) diff --git a/reproject/mosaicking/tests/test_subset_array.py b/reproject/mosaicking/tests/test_subset_array.py index abe922501..b351e3072 100644 --- a/reproject/mosaicking/tests/test_subset_array.py +++ b/reproject/mosaicking/tests/test_subset_array.py @@ -10,7 +10,6 @@ class TestReprojectedArraySubset: - def setup_method(self, method): self.array1 = np.random.random((123, 87)) @@ -21,26 +20,24 @@ def setup_method(self, method): self.footprint2 = (self.array2 > 0.5).astype(int) self.footprint3 = (self.array3 > 0.5).astype(int) - self.subset1 = ReprojectedArraySubset(self.array1[20:88, 34:40], - self.footprint1[20:88, 34:40], - 34, 40, 20, 88) + self.subset1 = ReprojectedArraySubset( + self.array1[20:88, 34:40], self.footprint1[20:88, 34:40], 34, 40, 20, 88 + ) - self.subset2 = ReprojectedArraySubset(self.array2[50:123, 37:42], - self.footprint2[50:123, 37:42], - 37, 42, 50, 123) + self.subset2 = ReprojectedArraySubset( + self.array2[50:123, 37:42], self.footprint2[50:123, 37:42], 37, 42, 50, 123 + ) - self.subset3 = ReprojectedArraySubset(self.array3[40:50, 11:19], - self.footprint3[40:50, 11:19], - 11, 19, 40, 50) + self.subset3 = ReprojectedArraySubset( + self.array3[40:50, 11:19], self.footprint3[40:50, 11:19], 11, 19, 40, 50 + ) def test_repr(self): - assert repr(self.subset1) == '' + assert repr(self.subset1) == "" def test_view_in_original_array(self): - assert_equal(self.array1[self.subset1.view_in_original_array], - self.subset1.array) - assert_equal(self.footprint1[self.subset1.view_in_original_array], - self.subset1.footprint) + assert_equal(self.array1[self.subset1.view_in_original_array], self.subset1.array) + assert_equal(self.footprint1[self.subset1.view_in_original_array], self.subset1.footprint) def test_shape(self): assert self.subset1.shape == (68, 6) @@ -56,8 +53,7 @@ def test_overlaps(self): assert not self.subset3.overlaps(self.subset2) assert self.subset3.overlaps(self.subset3) - @pytest.mark.parametrize('op', [operator.add, operator.sub, - operator.mul, operator.truediv]) + @pytest.mark.parametrize("op", [operator.add, operator.sub, operator.mul, operator.truediv]) def test_arithmetic(self, op): subset = op(self.subset1, self.subset2) assert subset.imin == 37 diff --git a/reproject/mosaicking/tests/test_wcs_helpers.py b/reproject/mosaicking/tests/test_wcs_helpers.py index 04bb5753d..dc0a2a7f0 100644 --- a/reproject/mosaicking/tests/test_wcs_helpers.py +++ b/reproject/mosaicking/tests/test_wcs_helpers.py @@ -20,16 +20,15 @@ SHAPELY_INSTALLED = True -class TestOptimalWCS(): - +class TestOptimalWCS: def setup_method(self, method): self.wcs = WCS(naxis=2) - self.wcs.wcs.ctype = 'RA---TAN', 'DEC--TAN' + self.wcs.wcs.ctype = "RA---TAN", "DEC--TAN" self.wcs.wcs.crpix = 10, 15 self.wcs.wcs.crval = 43, 23 self.wcs.wcs.cdelt = -0.1, 0.1 - self.wcs.wcs.equinox = 2000. + self.wcs.wcs.equinox = 2000.0 self.array = np.ones((30, 40)) @@ -37,11 +36,11 @@ def test_identity(self): wcs, shape = find_optimal_celestial_wcs([(self.array, self.wcs)], frame=FK5()) - assert tuple(wcs.wcs.ctype) == ('RA---TAN', 'DEC--TAN') + assert tuple(wcs.wcs.ctype) == ("RA---TAN", "DEC--TAN") assert_allclose(wcs.wcs.crval, (43, 23)) assert_allclose(wcs.wcs.cdelt, (-0.1, 0.1)) assert wcs.wcs.equinox == 2000 - assert wcs.wcs.radesys == 'FK5' + assert wcs.wcs.radesys == "FK5" assert_allclose(wcs.wcs.crpix, (10, 15)) assert shape == (30, 40) @@ -50,34 +49,36 @@ def test_args_tuple_wcs(self): wcs, shape = find_optimal_celestial_wcs([(self.array.shape, self.wcs)], frame=FK5()) def test_args_tuple_header(self): - wcs, shape = find_optimal_celestial_wcs([(self.array.shape, self.wcs.to_header())], - frame=FK5()) + wcs, shape = find_optimal_celestial_wcs( + [(self.array.shape, self.wcs.to_header())], frame=FK5() + ) def test_frame_projection(self): - wcs, shape = find_optimal_celestial_wcs([(self.array, self.wcs)], frame=Galactic(), - projection='CAR') + wcs, shape = find_optimal_celestial_wcs( + [(self.array, self.wcs)], frame=Galactic(), projection="CAR" + ) - assert tuple(wcs.wcs.ctype) == ('GLON-CAR', 'GLAT-CAR') - c = SkyCoord(43, 23, unit=('deg', 'deg'), frame='fk5').galactic + assert tuple(wcs.wcs.ctype) == ("GLON-CAR", "GLAT-CAR") + c = SkyCoord(43, 23, unit=("deg", "deg"), frame="fk5").galactic assert_allclose(wcs.wcs.crval, (c.l.degree, c.b.degree)) assert_allclose(wcs.wcs.cdelt, (-0.1, 0.1)) assert np.isnan(wcs.wcs.equinox) - assert wcs.wcs.radesys == '' + assert wcs.wcs.radesys == "" # The following values are empirical and just to make sure there are no regressions assert_allclose(wcs.wcs.crpix, (16.21218937, 28.86119519)) assert shape == (47, 50) def test_frame_str(self): - wcs, shape = find_optimal_celestial_wcs([(self.array, self.wcs)], frame='galactic') - assert tuple(wcs.wcs.ctype) == ('GLON-TAN', 'GLAT-TAN') + wcs, shape = find_optimal_celestial_wcs([(self.array, self.wcs)], frame="galactic") + assert tuple(wcs.wcs.ctype) == ("GLON-TAN", "GLAT-TAN") def test_resolution(self): wcs, shape = find_optimal_celestial_wcs([(self.array, self.wcs)], resolution=3 * u.arcmin) assert_allclose(wcs.wcs.cdelt, (-0.05, 0.05)) - @pytest.mark.skipif('not SHAPELY_INSTALLED') + @pytest.mark.skipif("not SHAPELY_INSTALLED") def test_auto_rotate(self): # To test auto_rotate, we set the frame to Galactic and the final image @@ -85,21 +86,22 @@ def test_auto_rotate(self): # actually gets rotated 90 degrees, so the values aren't quite the same # as the input, but they are round values. - wcs, shape = find_optimal_celestial_wcs([(self.array, self.wcs)], - frame=Galactic(), auto_rotate=True) + wcs, shape = find_optimal_celestial_wcs( + [(self.array, self.wcs)], frame=Galactic(), auto_rotate=True + ) - assert tuple(wcs.wcs.ctype) == ('GLON-TAN', 'GLAT-TAN') - c = SkyCoord(43, 23, unit=('deg', 'deg'), frame='fk5').galactic + assert tuple(wcs.wcs.ctype) == ("GLON-TAN", "GLAT-TAN") + c = SkyCoord(43, 23, unit=("deg", "deg"), frame="fk5").galactic assert_allclose(wcs.wcs.crval, (c.l.degree, c.b.degree)) assert_allclose(wcs.wcs.cdelt, (-0.1, 0.1)) assert np.isnan(wcs.wcs.equinox) - assert wcs.wcs.radesys == '' + assert wcs.wcs.radesys == "" assert_allclose(wcs.wcs.crpix, (10, 15)) assert shape == (30, 40) - @pytest.mark.skipif('not SHAPELY_INSTALLED') - @pytest.mark.parametrize('angle', np.linspace(0, 360, 13)) + @pytest.mark.skipif("not SHAPELY_INSTALLED") + @pytest.mark.parametrize("angle", np.linspace(0, 360, 13)) def test_auto_rotate_systematic(self, angle): # This is a test to make sure for a number of angles that the corners @@ -107,8 +109,7 @@ def test_auto_rotate_systematic(self, angle): # not. We test the full 360 range of angles. angle = np.radians(angle) - pc = np.array([[np.cos(angle), -np.sin(angle)], - [np.sin(angle), np.cos(angle)]]) + pc = np.array([[np.cos(angle), -np.sin(angle)], [np.sin(angle), np.cos(angle)]]) self.wcs.wcs.pc = pc wcs, shape = find_optimal_celestial_wcs([(self.array, self.wcs)], auto_rotate=True) @@ -123,8 +124,12 @@ def test_auto_rotate_systematic(self, angle): ny_final, nx_final = shape - inside = ((xp_final >= -0.5) & (xp_final <= nx_final - 0.5) & - (yp_final >= -0.5) & (yp_final <= ny_final - 0.5)) + inside = ( + (xp_final >= -0.5) + & (xp_final <= nx_final - 0.5) + & (yp_final >= -0.5) + & (yp_final <= ny_final - 0.5) + ) assert_equal(inside, [1, 1, 1, 1, 0, 0, 0, 0]) @@ -142,11 +147,11 @@ def test_multiple_size(self): wcs, shape = find_optimal_celestial_wcs(input_data, frame=FK5()) - assert tuple(wcs.wcs.ctype) == ('RA---TAN', 'DEC--TAN') + assert tuple(wcs.wcs.ctype) == ("RA---TAN", "DEC--TAN") assert_allclose(wcs.wcs.crval, (43, 23)) assert_allclose(wcs.wcs.cdelt, (-0.1, 0.1)) assert wcs.wcs.equinox == 2000 - assert wcs.wcs.radesys == 'FK5' + assert wcs.wcs.radesys == "FK5" assert_allclose(wcs.wcs.crpix, (20, 15)) assert shape == (35, 50) @@ -172,22 +177,22 @@ def test_invalid_array_shape(self): with pytest.raises(ValueError) as exc: wcs, shape = find_optimal_celestial_wcs([(array, self.wcs)]) - assert exc.value.args[0] == 'Input data is not 2-dimensional (got shape (30, 20, 10))' + assert exc.value.args[0] == "Input data is not 2-dimensional (got shape (30, 20, 10))" def test_invalid_wcs_shape(self): wcs = WCS(naxis=3) - wcs.wcs.ctype = 'RA---TAN', 'DEC--TAN', 'VELO-LSR' + wcs.wcs.ctype = "RA---TAN", "DEC--TAN", "VELO-LSR" wcs.wcs.set() with pytest.raises(ValueError) as exc: wcs, shape = find_optimal_celestial_wcs([(self.array, wcs)]) - assert exc.value.args[0] == 'Input WCS is not 2-dimensional' + assert exc.value.args[0] == "Input WCS is not 2-dimensional" def test_invalid_not_celestial(self): - self.wcs.wcs.ctype = 'OFFSETX', 'OFFSETY' + self.wcs.wcs.ctype = "OFFSETX", "OFFSETY" with pytest.raises(TypeError) as exc: wcs, shape = find_optimal_celestial_wcs([(self.array, self.wcs)]) - assert exc.value.args[0] == 'WCS does not have celestial components' + assert exc.value.args[0] == "WCS does not have celestial components" diff --git a/reproject/mosaicking/wcs_helpers.py b/reproject/mosaicking/wcs_helpers.py index 399411ee0..1769a50ce 100644 --- a/reproject/mosaicking/wcs_helpers.py +++ b/reproject/mosaicking/wcs_helpers.py @@ -4,17 +4,21 @@ from astropy import units as u from astropy.coordinates import SkyCoord, frame_transform_graph from astropy.wcs.utils import ( - celestial_frame_to_wcs, pixel_to_skycoord, proj_plane_pixel_scales, skycoord_to_pixel, - wcs_to_celestial_frame) + celestial_frame_to_wcs, + pixel_to_skycoord, + proj_plane_pixel_scales, + skycoord_to_pixel, + wcs_to_celestial_frame, +) from ..utils import parse_input_shape -__all__ = ['find_optimal_celestial_wcs'] +__all__ = ["find_optimal_celestial_wcs"] -def find_optimal_celestial_wcs(input_data, frame=None, auto_rotate=False, - projection='TAN', resolution=None, - reference=None): +def find_optimal_celestial_wcs( + input_data, frame=None, auto_rotate=False, projection="TAN", resolution=None, reference=None +): """ Given one or more images, return an optimal WCS projection object and shape. @@ -146,7 +150,7 @@ def find_optimal_celestial_wcs(input_data, frame=None, auto_rotate=False, # Construct WCS object centered on position wcs_final = celestial_frame_to_wcs(frame, projection=projection) - rep = reference.represent_as('unitspherical') + rep = reference.represent_as("unitspherical") wcs_final.wcs.crval = rep.lon.degree, rep.lat.degree wcs_final.wcs.cdelt = -cdelt, cdelt @@ -163,6 +167,7 @@ def find_optimal_celestial_wcs(input_data, frame=None, auto_rotate=False, # Use shapely to represent the points and find the minimum rotated # rectangle from shapely.geometry import MultiPoint + mp = MultiPoint(list(zip(xp, yp))) # The following returns a list of rectangle vertices - in fact there @@ -190,8 +195,7 @@ def find_optimal_celestial_wcs(input_data, frame=None, auto_rotate=False, # Set rotation matrix (use PC instead of CROTA2 since PC is the # recommended approach) - pc = np.array([[np.cos(angle), -np.sin(angle)], - [np.sin(angle), np.cos(angle)]]) + pc = np.array([[np.cos(angle), -np.sin(angle)], [np.sin(angle), np.cos(angle)]]) wcs_final.wcs.pc = pc # Recompute pixel coordinates (more accurate than simply rotating xp, yp) diff --git a/reproject/spherical_intersect/core.py b/reproject/spherical_intersect/core.py index 3d2b20899..d8accc9fc 100644 --- a/reproject/spherical_intersect/core.py +++ b/reproject/spherical_intersect/core.py @@ -18,11 +18,11 @@ def _init_worker(): def _reproject_slice(args): from ._overlap import _reproject_slice_cython + return _reproject_slice_cython(*args) -def _reproject_celestial(array, wcs_in, wcs_out, shape_out, parallel=True, - return_footprint=True): +def _reproject_celestial(array, wcs_in, wcs_out, shape_out, parallel=True, return_footprint=True): # Check the parallel flag. if type(parallel) != bool and type(parallel) != int: @@ -42,11 +42,15 @@ def _reproject_celestial(array, wcs_in, wcs_out, shape_out, parallel=True, # emit a warning if this is the case. For more details, see: # https://github.com/astropy/reproject/issues/199 area_threshold = (0.05 / 3600) ** 2 - if ((isinstance(wcs_in, WCS) and proj_plane_pixel_area(wcs_in) < area_threshold) - or (isinstance(wcs_out, WCS) and proj_plane_pixel_area(wcs_out) < area_threshold)): - warnings.warn("The reproject_exact function currently has precision " - "issues with images that have resolutions below ~0.05 " - "arcsec, so the results may not be accurate.", UserWarning) + if (isinstance(wcs_in, WCS) and proj_plane_pixel_area(wcs_in) < area_threshold) or ( + isinstance(wcs_out, WCS) and proj_plane_pixel_area(wcs_out) < area_threshold + ): + warnings.warn( + "The reproject_exact function currently has precision " + "issues with images that have resolutions below ~0.05 " + "arcsec, so the results may not be accurate.", + UserWarning, + ) # Convert input array to float values. If this comes from a FITS, it might have # float32 as value type and that can break things in Cython @@ -64,10 +68,10 @@ def _reproject_celestial(array, wcs_in, wcs_out, shape_out, parallel=True, ny_in, nx_in = array.shape - x = np.arange(nx_in + 1.) - 0.5 - y = np.arange(ny_in + 1.) - 0.5 + x = np.arange(nx_in + 1.0) - 0.5 + y = np.arange(ny_in + 1.0) - 0.5 - xp_in, yp_in = np.meshgrid(x, y, indexing='xy', sparse=False, copy=False) + xp_in, yp_in = np.meshgrid(x, y, indexing="xy", sparse=False, copy=False) world_in = wcs_in.pixel_to_world(xp_in, yp_in) @@ -75,10 +79,10 @@ def _reproject_celestial(array, wcs_in, wcs_out, shape_out, parallel=True, ny_out, nx_out = shape_out - x = np.arange(nx_out + 1.) - 0.5 - y = np.arange(ny_out + 1.) - 0.5 + x = np.arange(nx_out + 1.0) - 0.5 + y = np.arange(ny_out + 1.0) - 0.5 - xp_out, yp_out = np.meshgrid(x, y, indexing='xy', sparse=False, copy=False) + xp_out, yp_out = np.meshgrid(x, y, indexing="xy", sparse=False, copy=False) world_out = wcs_out.pixel_to_world(xp_out, yp_out) @@ -92,10 +96,10 @@ def _reproject_celestial(array, wcs_in, wcs_out, shape_out, parallel=True, xp_inout, yp_inout = wcs_out.world_to_pixel(world_in) - world_in_unitsph = world_in.represent_as('unitspherical') + world_in_unitsph = world_in.represent_as("unitspherical") xw_in, yw_in = world_in_unitsph.lon.to_value(u.deg), world_in_unitsph.lat.to_value(u.deg) - world_out_unitsph = world_out.represent_as('unitspherical') + world_out_unitsph = world_out.represent_as("unitspherical") xw_out, yw_out = world_out_unitsph.lon.to_value(u.deg), world_out_unitsph.lat.to_value(u.deg) # Put together the parameters common both to the serial and parallel implementations. The aca @@ -103,15 +107,26 @@ def _reproject_celestial(array, wcs_in, wcs_out, shape_out, parallel=True, # raw C function, otherwise Cython might complain. aca = np.ascontiguousarray - common_func_par = [0, ny_in, nx_out, ny_out, aca(xp_inout), aca(yp_inout), - aca(xw_in), aca(yw_in), aca(xw_out), aca(yw_out), aca(array), - shape_out] + common_func_par = [ + 0, + ny_in, + nx_out, + ny_out, + aca(xp_inout), + aca(yp_inout), + aca(xw_in), + aca(yw_in), + aca(xw_out), + aca(yw_out), + aca(array), + shape_out, + ] if nproc == 1: array_new, weights = _reproject_slice([0, nx_in] + common_func_par) - with np.errstate(invalid='ignore'): + with np.errstate(invalid="ignore"): array_new /= weights if return_footprint: @@ -119,7 +134,7 @@ def _reproject_celestial(array, wcs_in, wcs_out, shape_out, parallel=True, else: return array_new - elif (nproc is None or nproc > 1): + elif nproc is None or nproc > 1: from multiprocessing import Pool, cpu_count @@ -146,7 +161,7 @@ def _reproject_celestial(array, wcs_in, wcs_out, shape_out, parallel=True, array_new = sum(array_new) weights = sum(weights) - with np.errstate(invalid='ignore'): + with np.errstate(invalid="ignore"): array_new /= weights if return_footprint: diff --git a/reproject/spherical_intersect/high_level.py b/reproject/spherical_intersect/high_level.py index e2bf97404..0b1fbb650 100644 --- a/reproject/spherical_intersect/high_level.py +++ b/reproject/spherical_intersect/high_level.py @@ -4,11 +4,12 @@ from ..wcs_utils import has_celestial from .core import _reproject_celestial -__all__ = ['reproject_exact'] +__all__ = ["reproject_exact"] -def reproject_exact(input_data, output_projection, shape_out=None, hdu_in=0, - parallel=False, return_footprint=True): +def reproject_exact( + input_data, output_projection, shape_out=None, hdu_in=0, parallel=False, return_footprint=True +): """ Reproject data to a new projection using flux-conserving spherical polygon intersection (this is the slowest algorithm). @@ -61,8 +62,16 @@ def reproject_exact(input_data, output_projection, shape_out=None, hdu_in=0, wcs_out, shape_out = parse_output_projection(output_projection, shape_out=shape_out) if has_celestial(wcs_in) and wcs_in.pixel_n_dim == 2 and wcs_in.world_n_dim == 2: - return _reproject_celestial(array_in, wcs_in, wcs_out, shape_out=shape_out, - parallel=parallel, return_footprint=return_footprint) + return _reproject_celestial( + array_in, + wcs_in, + wcs_out, + shape_out=shape_out, + parallel=parallel, + return_footprint=return_footprint, + ) else: - raise NotImplementedError("Currently only data with a 2-d celestial " - "WCS can be reprojected using flux-conserving algorithm") + raise NotImplementedError( + "Currently only data with a 2-d celestial " + "WCS can be reprojected using flux-conserving algorithm" + ) diff --git a/reproject/spherical_intersect/overlap.py b/reproject/spherical_intersect/overlap.py index f16273b81..ddb0eec7a 100644 --- a/reproject/spherical_intersect/overlap.py +++ b/reproject/spherical_intersect/overlap.py @@ -2,7 +2,7 @@ from ._overlap import _compute_overlap -__all__ = ['compute_overlap'] +__all__ = ["compute_overlap"] def compute_overlap(ilon, ilat, olon, olat): diff --git a/reproject/spherical_intersect/setup_package.py b/reproject/spherical_intersect/setup_package.py index eab4fd250..6eb9c8bae 100644 --- a/reproject/spherical_intersect/setup_package.py +++ b/reproject/spherical_intersect/setup_package.py @@ -21,8 +21,8 @@ def get_extensions(): # Note that to set the DEBUG variable in the overlapArea.c code, which # results in debugging information being printed out, you can set # DEBUG_OVERLAP_AREA=1 at build-time. - if int(os.environ.get('DEBUG_OVERLAP_AREA', 0)): - define_macros = [('DEBUG_OVERLAP_AREA', 1)] + if int(os.environ.get("DEBUG_OVERLAP_AREA", 0)): + define_macros = [("DEBUG_OVERLAP_AREA", 1)] else: define_macros = None @@ -32,7 +32,8 @@ def get_extensions(): include_dirs=include_dirs, libraries=libraries, language="c", - extra_compile_args=['-O2'], - define_macros=define_macros) + extra_compile_args=["-O2"], + define_macros=define_macros, + ) return [extension] diff --git a/reproject/spherical_intersect/tests/test_high_level.py b/reproject/spherical_intersect/tests/test_high_level.py index 0ae8b0fb0..be54d8298 100644 --- a/reproject/spherical_intersect/tests/test_high_level.py +++ b/reproject/spherical_intersect/tests/test_high_level.py @@ -13,17 +13,16 @@ class TestReprojectExact: - def setup_class(self): - header_gal = get_pkg_data_filename('../../tests/data/gc_ga.hdr') - header_equ = get_pkg_data_filename('../../tests/data/gc_eq.hdr') + header_gal = get_pkg_data_filename("../../tests/data/gc_ga.hdr") + header_equ = get_pkg_data_filename("../../tests/data/gc_eq.hdr") self.header_in = fits.Header.fromtextfile(header_gal) self.header_out = fits.Header.fromtextfile(header_equ) - self.header_out['NAXIS'] = 2 - self.header_out['NAXIS1'] = 600 - self.header_out['NAXIS2'] = 550 + self.header_out["NAXIS"] = 2 + self.header_out["NAXIS1"] = 600 + self.header_out["NAXIS2"] = 550 self.array_in = np.ones((100, 100)) @@ -50,17 +49,16 @@ def test_identity(): # Reproject an array and WCS to itself wcs = WCS(naxis=2) - wcs.wcs.ctype = 'RA---TAN', 'DEC--TAN' + wcs.wcs.ctype = "RA---TAN", "DEC--TAN" wcs.wcs.crpix = 322, 151 wcs.wcs.crval = 43, 23 wcs.wcs.cdelt = -0.1, 0.1 - wcs.wcs.equinox = 2000. + wcs.wcs.equinox = 2000.0 np.random.seed(1249) array_in = np.random.random((423, 344)) - array_out, footprint = reproject_exact((array_in, wcs), wcs, - shape_out=array_in.shape) + array_out, footprint = reproject_exact((array_in, wcs), wcs, shape_out=array_in.shape) assert_allclose(array_out, array_in, atol=1e-10) @@ -70,13 +68,13 @@ def test_reproject_precision_warning(): for res in [0.1 / 3600, 0.01 / 3600]: wcs1 = WCS() - wcs1.wcs.ctype = 'RA---TAN', 'DEC--TAN' + wcs1.wcs.ctype = "RA---TAN", "DEC--TAN" wcs1.wcs.crval = 13, 80 - wcs1.wcs.crpix = 10., 10. + wcs1.wcs.crpix = 10.0, 10.0 wcs1.wcs.cdelt = res, res wcs2 = WCS() - wcs2.wcs.ctype = 'RA---TAN', 'DEC--TAN' + wcs2.wcs.ctype = "RA---TAN", "DEC--TAN" wcs2.wcs.crval = 13, 80 wcs2.wcs.crpix = 3, 3 wcs2.wcs.cdelt = 3 * res, 3 * res @@ -85,8 +83,9 @@ def test_reproject_precision_warning(): array[9, 9] = 1 if res < 0.05 / 3600: - with pytest.warns(UserWarning, match='The reproject_exact function ' - 'currently has precision'): + with pytest.warns( + UserWarning, match="The reproject_exact function " "currently has precision" + ): reproject_exact((array, wcs1), wcs2, shape_out=(5, 5)) else: with warnings.catch_warnings(record=True) as w: diff --git a/reproject/spherical_intersect/tests/test_overlap.py b/reproject/spherical_intersect/tests/test_overlap.py index a37f6982a..d96b0a6e8 100644 --- a/reproject/spherical_intersect/tests/test_overlap.py +++ b/reproject/spherical_intersect/tests/test_overlap.py @@ -10,7 +10,7 @@ def test_full_overlap(): EPS = np.radians(1e-2) lon, lat = np.array([[0, EPS, EPS, 0]]), np.array([[0, 0, EPS, EPS]]) overlap, area_ratio = compute_overlap(lon, lat, lon, lat) - np.testing.assert_allclose(overlap, EPS ** 2, rtol=1e-6) + np.testing.assert_allclose(overlap, EPS**2, rtol=1e-6) np.testing.assert_allclose(area_ratio, 1, rtol=1e-6) @@ -22,12 +22,11 @@ def test_partial_overlap(): olat = np.array([[0, 0, EPS, EPS]]) overlap, area_ratio = compute_overlap(ilon, ilat, olon, olat) - np.testing.assert_allclose(overlap, 0.5 * EPS ** 2, rtol=1e-6) + np.testing.assert_allclose(overlap, 0.5 * EPS**2, rtol=1e-6) np.testing.assert_allclose(area_ratio, 1, rtol=1e-6) -@pytest.mark.parametrize(('clockwise1', 'clockwise2'), - product([False, True], [False, True])) +@pytest.mark.parametrize(("clockwise1", "clockwise2"), product([False, True], [False, True])) def test_overlap_direction(clockwise1, clockwise2): # Regression test for a bug that caused the calculation to fail if one or @@ -46,5 +45,5 @@ def test_overlap_direction(clockwise1, clockwise2): olon, olat = olon[:, ::-1], olat[:, ::-1] overlap, area_ratio = compute_overlap(ilon, ilat, olon, olat) - np.testing.assert_allclose(overlap, 0.5 * EPS ** 2, rtol=1e-6) + np.testing.assert_allclose(overlap, 0.5 * EPS**2, rtol=1e-6) np.testing.assert_allclose(area_ratio, 1, rtol=1e-6) diff --git a/reproject/spherical_intersect/tests/test_reproject.py b/reproject/spherical_intersect/tests/test_reproject.py index e82884cb3..4fc2ca0a6 100644 --- a/reproject/spherical_intersect/tests/test_reproject.py +++ b/reproject/spherical_intersect/tests/test_reproject.py @@ -12,8 +12,8 @@ def test_reproject_celestial_slices_2d(): - header_in = fits.Header.fromtextfile(get_pkg_data_filename('../../tests/data/gc_ga.hdr')) - header_out = fits.Header.fromtextfile(get_pkg_data_filename('../../tests/data/gc_eq.hdr')) + header_in = fits.Header.fromtextfile(get_pkg_data_filename("../../tests/data/gc_ga.hdr")) + header_out = fits.Header.fromtextfile(get_pkg_data_filename("../../tests/data/gc_eq.hdr")) array_in = np.ones((100, 100)) @@ -58,34 +58,38 @@ def test_reproject_celestial_slices_2d(): EQUINOX = 2000.0 / [yr] Equinox of equatorial coordinates """ -MONTAGE_REF = np.array([[np.nan, 2., 2., np.nan], - [1., 1.6768244, 3.35364754, 4.], - [1., 1.6461656, 3.32308315, 4.], - [np.nan, 3., 3., np.nan]]) +MONTAGE_REF = np.array( + [ + [np.nan, 2.0, 2.0, np.nan], + [1.0, 1.6768244, 3.35364754, 4.0], + [1.0, 1.6461656, 3.32308315, 4.0], + [np.nan, 3.0, 3.0, np.nan], + ] +) def test_reproject_celestial_consistency(): # Consistency between the different modes - wcs_in = WCS(fits.Header.fromstring(INPUT_HDR, sep='\n')) - wcs_out = WCS(fits.Header.fromstring(OUTPUT_HDR, sep='\n')) + wcs_in = WCS(fits.Header.fromstring(INPUT_HDR, sep="\n")) + wcs_out = WCS(fits.Header.fromstring(OUTPUT_HDR, sep="\n")) array1, footprint1 = _reproject_celestial(DATA, wcs_in, wcs_out, (4, 4), parallel=False) array2, footprint2 = _reproject_celestial(DATA, wcs_in, wcs_out, (4, 4), parallel=True) - np.testing.assert_allclose(array1, array2, rtol=1.e-5) + np.testing.assert_allclose(array1, array2, rtol=1.0e-5) - np.testing.assert_allclose(footprint1, footprint2, rtol=3.e-5) + np.testing.assert_allclose(footprint1, footprint2, rtol=3.0e-5) -@pytest.mark.parametrize('wcsapi', (False, True)) +@pytest.mark.parametrize("wcsapi", (False, True)) def test_reproject_celestial_montage(wcsapi): # Accuracy compared to Montage - wcs_in = WCS(fits.Header.fromstring(INPUT_HDR, sep='\n')) - wcs_out = WCS(fits.Header.fromstring(OUTPUT_HDR, sep='\n')) + wcs_in = WCS(fits.Header.fromstring(INPUT_HDR, sep="\n")) + wcs_out = WCS(fits.Header.fromstring(OUTPUT_HDR, sep="\n")) if wcsapi: # Enforce a pure wcsapi API wcs_in, wcs_out = as_high_level_wcs(wcs_in), as_high_level_wcs(wcs_out) @@ -101,26 +105,27 @@ def test_reproject_flipping(): # Regression test for a bug that caused issues when the WCS was oriented # in a way that meant polygon vertices were clockwise. - wcs_in = WCS(fits.Header.fromstring(INPUT_HDR, sep='\n')) - wcs_out = WCS(fits.Header.fromstring(OUTPUT_HDR, sep='\n')) + wcs_in = WCS(fits.Header.fromstring(INPUT_HDR, sep="\n")) + wcs_out = WCS(fits.Header.fromstring(OUTPUT_HDR, sep="\n")) array1, footprint1 = _reproject_celestial(DATA, wcs_in, wcs_out, (4, 4), parallel=False) # Repeat with an input that is flipped horizontally with the equivalent WCS - wcs_in_flipped = WCS(fits.Header.fromstring(INPUT_HDR, sep='\n')) + wcs_in_flipped = WCS(fits.Header.fromstring(INPUT_HDR, sep="\n")) wcs_in_flipped.wcs.cdelt[0] = -wcs_in_flipped.wcs.cdelt[0] wcs_in_flipped.wcs.crpix[0] = 3 - wcs_in_flipped.wcs.crpix[0] - array2, footprint2 = _reproject_celestial(DATA[:, ::-1], wcs_in_flipped, - wcs_out, (4, 4), parallel=False) + array2, footprint2 = _reproject_celestial( + DATA[:, ::-1], wcs_in_flipped, wcs_out, (4, 4), parallel=False + ) # Repeat with an output that is flipped horizontally with the equivalent WCS - wcs_out_flipped = WCS(fits.Header.fromstring(OUTPUT_HDR, sep='\n')) + wcs_out_flipped = WCS(fits.Header.fromstring(OUTPUT_HDR, sep="\n")) wcs_out_flipped.wcs.cdelt[0] = -wcs_out_flipped.wcs.cdelt[0] wcs_out_flipped.wcs.crpix[0] = 5 - wcs_out_flipped.wcs.crpix[0] array3, footprint3 = _reproject_celestial(DATA, wcs_in, wcs_out_flipped, (4, 4), parallel=False) array3, footprint3 = array3[:, ::-1], footprint3[:, ::-1] - np.testing.assert_allclose(array1, array2, rtol=1.e-5) - np.testing.assert_allclose(array1, array3, rtol=1.e-5) + np.testing.assert_allclose(array1, array2, rtol=1.0e-5) + np.testing.assert_allclose(array1, array3, rtol=1.0e-5) - np.testing.assert_allclose(footprint1, footprint2, rtol=3.e-5) - np.testing.assert_allclose(footprint1, footprint3, rtol=3.e-5) + np.testing.assert_allclose(footprint1, footprint2, rtol=3.0e-5) + np.testing.assert_allclose(footprint1, footprint3, rtol=3.0e-5) diff --git a/reproject/tests/helpers.py b/reproject/tests/helpers.py index 2eae1148c..19f9cce73 100644 --- a/reproject/tests/helpers.py +++ b/reproject/tests/helpers.py @@ -4,5 +4,5 @@ def array_footprint_to_hdulist(array, footprint, header): hdulist = fits.HDUList() hdulist.append(fits.PrimaryHDU(array, header)) - hdulist.append(fits.ImageHDU(footprint, header, name='footprint')) + hdulist.append(fits.ImageHDU(footprint, header, name="footprint")) return hdulist diff --git a/reproject/tests/test_high_level.py b/reproject/tests/test_high_level.py index c7c5f1230..2f2b9f384 100644 --- a/reproject/tests/test_high_level.py +++ b/reproject/tests/test_high_level.py @@ -12,40 +12,43 @@ # TODO: add reference comparisons -ALL_MODES = ('nearest-neighbor', - 'bilinear', - 'biquadratic', - 'bicubic', - 'flux-conserving', - 'adaptive-hann', - 'adaptive-gaussian') +ALL_MODES = ( + "nearest-neighbor", + "bilinear", + "biquadratic", + "bicubic", + "flux-conserving", + "adaptive-hann", + "adaptive-gaussian", +) ALL_DTYPES = [] -for endian in ('<', '>'): - for kind in ('u', 'i', 'f'): - for size in ('1', '2', '4', '8'): - if kind == 'f' and size == '1': +for endian in ("<", ">"): + for kind in ("u", "i", "f"): + for size in ("1", "2", "4", "8"): + if kind == "f" and size == "1": continue ALL_DTYPES.append(np.dtype(endian + kind + size)) -@pytest.fixture(params=[reproject_interp, reproject_adaptive, reproject_exact], - ids=["interp", "adaptive", "exact"]) +@pytest.fixture( + params=[reproject_interp, reproject_adaptive, reproject_exact], + ids=["interp", "adaptive", "exact"], +) def reproject_function(request): return request.param class TestReproject: - def setup_method(self, method): - self.header_in = fits.Header.fromtextfile(get_pkg_data_filename('data/gc_ga.hdr')) - self.header_out = fits.Header.fromtextfile(get_pkg_data_filename('data/gc_eq.hdr')) + self.header_in = fits.Header.fromtextfile(get_pkg_data_filename("data/gc_ga.hdr")) + self.header_out = fits.Header.fromtextfile(get_pkg_data_filename("data/gc_eq.hdr")) self.header_out_size = self.header_out.copy() - self.header_out_size['NAXIS'] = 2 - self.header_out_size['NAXIS1'] = 600 - self.header_out_size['NAXIS2'] = 550 + self.header_out_size["NAXIS"] = 2 + self.header_out_size["NAXIS1"] = 600 + self.header_out_size["NAXIS2"] = 550 self.array_in = np.ones((700, 690)) @@ -55,42 +58,47 @@ def setup_method(self, method): self.wcs_out = WCS(self.header_out) self.shape_out = (600, 550) - @pytest.mark.filterwarnings('ignore::FutureWarning') + @pytest.mark.filterwarnings("ignore::FutureWarning") def test_hdu_header(self, reproject_function): with pytest.raises(ValueError) as exc: reproject_function(self.hdu_in, self.header_out) - assert exc.value.args[0] == ("Need to specify shape since output header " - "does not contain complete shape information") + assert exc.value.args[0] == ( + "Need to specify shape since output header " + "does not contain complete shape information" + ) reproject_interp(self.hdu_in, self.header_out_size) - @pytest.mark.filterwarnings('ignore::FutureWarning') + @pytest.mark.filterwarnings("ignore::FutureWarning") def test_hdu_wcs(self, reproject_function): reproject_function(self.hdu_in, self.wcs_out, shape_out=self.shape_out) - @pytest.mark.filterwarnings('ignore::FutureWarning') + @pytest.mark.filterwarnings("ignore::FutureWarning") def test_array_wcs_header(self, reproject_function): with pytest.raises(ValueError) as exc: reproject_function((self.array_in, self.wcs_in), self.header_out) - assert exc.value.args[0] == ("Need to specify shape since output header " - "does not contain complete shape information") + assert exc.value.args[0] == ( + "Need to specify shape since output header " + "does not contain complete shape information" + ) reproject_function((self.array_in, self.wcs_in), self.header_out_size) - @pytest.mark.filterwarnings('ignore::FutureWarning') + @pytest.mark.filterwarnings("ignore::FutureWarning") def test_array_wcs_wcs(self, reproject_function): reproject_function((self.array_in, self.wcs_in), self.wcs_out, shape_out=self.shape_out) - @pytest.mark.filterwarnings('ignore::FutureWarning') + @pytest.mark.filterwarnings("ignore::FutureWarning") def test_array_header_header(self, reproject_function): reproject_function((self.array_in, self.header_in), self.header_out_size) - @pytest.mark.filterwarnings('ignore::FutureWarning') + @pytest.mark.filterwarnings("ignore::FutureWarning") def test_return_footprint(self, reproject_function): - array = reproject_function(self.hdu_in, self.wcs_out, - shape_out=self.shape_out, return_footprint=False) + array = reproject_function( + self.hdu_in, self.wcs_out, shape_out=self.shape_out, return_footprint=False + ) assert isinstance(array, np.ndarray) @@ -111,34 +119,37 @@ def test_return_footprint(self, reproject_function): """ -@pytest.mark.filterwarnings('ignore::FutureWarning') -@pytest.mark.parametrize('projection_type, dtype', itertools.product(ALL_MODES, ALL_DTYPES)) +@pytest.mark.filterwarnings("ignore::FutureWarning") +@pytest.mark.parametrize("projection_type, dtype", itertools.product(ALL_MODES, ALL_DTYPES)) def test_surface_brightness(projection_type, dtype): - header_in = fits.Header.fromstring(INPUT_HDR, sep='\n') - header_in['NAXIS'] = 2 - header_in['NAXIS1'] = 10 - header_in['NAXIS2'] = 10 + header_in = fits.Header.fromstring(INPUT_HDR, sep="\n") + header_in["NAXIS"] = 2 + header_in["NAXIS1"] = 10 + header_in["NAXIS2"] = 10 header_out = header_in.copy() - header_out['CDELT1'] /= 2 - header_out['CDELT2'] /= 2 - header_out['NAXIS1'] *= 2 - header_out['NAXIS2'] *= 2 + header_out["CDELT1"] /= 2 + header_out["CDELT2"] /= 2 + header_out["NAXIS1"] *= 2 + header_out["NAXIS2"] *= 2 data_in = np.ones((10, 10), dtype=dtype) - if projection_type == 'flux-conserving': + if projection_type == "flux-conserving": data_out, footprint = reproject_exact((data_in, header_in), header_out) - elif projection_type.startswith('adaptive'): + elif projection_type.startswith("adaptive"): data_out, footprint = reproject_adaptive( - (data_in, header_in), header_out, - kernel=projection_type.split('-', 1)[1], - boundary_mode='ignore') + (data_in, header_in), + header_out, + kernel=projection_type.split("-", 1)[1], + boundary_mode="ignore", + ) else: - data_out, footprint = reproject_interp((data_in, header_in), header_out, - order=projection_type) + data_out, footprint = reproject_interp( + (data_in, header_in), header_out, order=projection_type + ) assert data_out.shape == (20, 20) @@ -167,28 +178,31 @@ def test_surface_brightness(projection_type, dtype): """ -@pytest.mark.filterwarnings('ignore::FutureWarning') -@pytest.mark.parametrize('projection_type', ALL_MODES) +@pytest.mark.filterwarnings("ignore::FutureWarning") +@pytest.mark.parametrize("projection_type", ALL_MODES) def test_identity_projection(projection_type): """Sanity check: identical input & output headers should preserve image.""" - header_in = fits.Header.fromstring(IDENTITY_TEST_HDR, sep='\n') - data_in = np.random.rand(header_in['NAXIS2'], header_in['NAXIS1']) - if projection_type == 'flux-conserving': + header_in = fits.Header.fromstring(IDENTITY_TEST_HDR, sep="\n") + data_in = np.random.rand(header_in["NAXIS2"], header_in["NAXIS1"]) + if projection_type == "flux-conserving": data_out, footprint = reproject_exact((data_in, header_in), header_in) - elif projection_type.startswith('adaptive'): + elif projection_type.startswith("adaptive"): data_out, footprint = reproject_adaptive( - (data_in, header_in), header_in, - kernel=projection_type.split('-', 1)[1], - boundary_mode='ignore') + (data_in, header_in), + header_in, + kernel=projection_type.split("-", 1)[1], + boundary_mode="ignore", + ) else: - data_out, footprint = reproject_interp((data_in, header_in), header_in, - order=projection_type) + data_out, footprint = reproject_interp( + (data_in, header_in), header_in, order=projection_type + ) # When reprojecting with an identical input and output header, # we may expect the input and output data to be similar, # and the footprint values to be ~ones. - expected_footprint = np.ones((header_in['NAXIS2'], header_in['NAXIS1'])) + expected_footprint = np.ones((header_in["NAXIS2"], header_in["NAXIS1"])) np.testing.assert_allclose(footprint, expected_footprint) - if projection_type != 'adaptive-gaussian': + if projection_type != "adaptive-gaussian": np.testing.assert_allclose(data_in, data_out, rtol=1e-6) else: # The Gaussian kernel in the adaptive algorithm blurs the image diff --git a/reproject/tests/test_utils.py b/reproject/tests/test_utils.py index 1e025b3c2..51d9dfc53 100644 --- a/reproject/tests/test_utils.py +++ b/reproject/tests/test_utils.py @@ -8,10 +8,10 @@ from reproject.utils import parse_input_data, parse_input_shape, parse_output_projection -@pytest.mark.filterwarnings('ignore:unclosed file:ResourceWarning') +@pytest.mark.filterwarnings("ignore:unclosed file:ResourceWarning") def test_parse_input_data(tmpdir): - header = fits.Header.fromtextfile(get_pkg_data_filename('data/gc_ga.hdr')) + header = fits.Header.fromtextfile(get_pkg_data_filename("data/gc_ga.hdr")) data = np.arange(200).reshape((10, 20)) @@ -22,13 +22,14 @@ def test_parse_input_data(tmpdir): np.testing.assert_allclose(array, data) # As filename - filename = tmpdir.join('test.fits').strpath + filename = tmpdir.join("test.fits").strpath hdu.writeto(filename) with pytest.raises(ValueError) as exc: array, coordinate_system = parse_input_data(filename) - assert exc.value.args[0] == ("More than one HDU is present, please specify " - "HDU to use with ``hdu_in=`` option") + assert exc.value.args[0] == ( + "More than one HDU is present, please specify " "HDU to use with ``hdu_in=`` option" + ) array, coordinate_system = parse_input_data(filename, hdu_in=1) np.testing.assert_allclose(array, data) @@ -50,17 +51,18 @@ def test_parse_input_data(tmpdir): # Invalid with pytest.raises(TypeError) as exc: parse_input_data(data) - assert exc.value.args[0] == ("input_data should either be an HDU object or " - "a tuple of (array, WCS) or (array, Header)") + assert exc.value.args[0] == ( + "input_data should either be an HDU object or " "a tuple of (array, WCS) or (array, Header)" + ) -@pytest.mark.filterwarnings('ignore:unclosed file:ResourceWarning') +@pytest.mark.filterwarnings("ignore:unclosed file:ResourceWarning") def test_parse_input_shape(tmpdir): """ This should support everything that parse_input_data does, *plus* an "array-like" argument that is just a shape rather than a populated array. """ - header = fits.Header.fromtextfile(get_pkg_data_filename('data/gc_ga.hdr')) + header = fits.Header.fromtextfile(get_pkg_data_filename("data/gc_ga.hdr")) in_shape = (10, 20) data = np.arange(200).reshape(in_shape) hdu = fits.ImageHDU(data) @@ -70,13 +72,14 @@ def test_parse_input_shape(tmpdir): assert shape == in_shape # As filename - filename = tmpdir.join('test.fits').strpath + filename = tmpdir.join("test.fits").strpath hdu.writeto(filename) with pytest.raises(ValueError) as exc: shape, coordinate_system = parse_input_shape(filename) - assert exc.value.args[0] == ("More than one HDU is present, please specify " - "HDU to use with ``hdu_in=`` option") + assert exc.value.args[0] == ( + "More than one HDU is present, please specify " "HDU to use with ``hdu_in=`` option" + ) shape, coordinate_system = parse_input_shape(filename, hdu_in=1) assert shape == in_shape @@ -106,27 +109,30 @@ def test_parse_input_shape(tmpdir): # Invalid with pytest.raises(TypeError) as exc: parse_input_shape(data) - assert exc.value.args[0] == ("input_shape should either be an HDU object or a tuple " - "of (array-or-shape, WCS) or (array-or-shape, Header)") + assert exc.value.args[0] == ( + "input_shape should either be an HDU object or a tuple " + "of (array-or-shape, WCS) or (array-or-shape, Header)" + ) def test_parse_output_projection(tmpdir): - header = fits.Header.fromtextfile(get_pkg_data_filename('data/gc_ga.hdr')) + header = fits.Header.fromtextfile(get_pkg_data_filename("data/gc_ga.hdr")) wcs = WCS(header) # As header with pytest.raises(ValueError) as exc: parse_output_projection(header) - assert exc.value.args[0] == ("Need to specify shape since output header " - "does not contain complete shape information") + assert exc.value.args[0] == ( + "Need to specify shape since output header " "does not contain complete shape information" + ) parse_output_projection(header, shape_out=(200, 200)) - header['NAXIS'] = 2 - header['NAXIS1'] = 200 - header['NAXIS2'] = 300 + header["NAXIS"] = 2 + header["NAXIS1"] = 200 + header["NAXIS2"] = 300 parse_output_projection(header) @@ -134,7 +140,8 @@ def test_parse_output_projection(tmpdir): with pytest.raises(ValueError) as exc: parse_output_projection(wcs) - assert exc.value.args[0] == ("Need to specify shape_out when specifying " - "output_projection as WCS object") + assert exc.value.args[0] == ( + "Need to specify shape_out when specifying " "output_projection as WCS object" + ) parse_output_projection(wcs, shape_out=(200, 200)) diff --git a/reproject/utils.py b/reproject/utils.py index f8c302a6e..d16619207 100644 --- a/reproject/utils.py +++ b/reproject/utils.py @@ -8,8 +8,12 @@ from astropy.wcs.wcsapi import BaseHighLevelWCS, SlicedLowLevelWCS from astropy.wcs.wcsapi.high_level_wcs_wrapper import HighLevelWCSWrapper -__all__ = ['parse_input_data', 'parse_input_shape', 'parse_input_weights', - 'parse_output_projection'] +__all__ = [ + "parse_input_data", + "parse_input_shape", + "parse_input_weights", + "parse_output_projection", +] def parse_input_data(input_data, hdu_in=None): @@ -23,8 +27,10 @@ def parse_input_data(input_data, hdu_in=None): elif isinstance(input_data, HDUList): if hdu_in is None: if len(input_data) > 1: - raise ValueError("More than one HDU is present, please specify " - "HDU to use with ``hdu_in=`` option") + raise ValueError( + "More than one HDU is present, please specify " + "HDU to use with ``hdu_in=`` option" + ) else: hdu_in = 0 return parse_input_data(input_data[hdu_in]) @@ -38,8 +44,10 @@ def parse_input_data(input_data, hdu_in=None): elif isinstance(input_data, astropy.nddata.NDDataBase): return input_data.data, input_data.wcs else: - raise TypeError("input_data should either be an HDU object or a tuple " - "of (array, WCS) or (array, Header)") + raise TypeError( + "input_data should either be an HDU object or a tuple " + "of (array, WCS) or (array, Header)" + ) def parse_input_shape(input_shape, hdu_in=None): @@ -52,8 +60,10 @@ def parse_input_shape(input_shape, hdu_in=None): elif isinstance(input_shape, HDUList): if hdu_in is None: if len(input_shape) > 1: - raise ValueError("More than one HDU is present, please specify " - "HDU to use with ``hdu_in=`` option") + raise ValueError( + "More than one HDU is present, please specify " + "HDU to use with ``hdu_in=`` option" + ) else: hdu_in = 0 return parse_input_shape(input_shape[hdu_in]) @@ -72,8 +82,10 @@ def parse_input_shape(input_shape, hdu_in=None): elif isinstance(input_shape, astropy.nddata.NDDataBase): return input_shape.data.shape, input_shape.wcs else: - raise TypeError("input_shape should either be an HDU object or a tuple " - "of (array-or-shape, WCS) or (array-or-shape, Header)") + raise TypeError( + "input_shape should either be an HDU object or a tuple " + "of (array-or-shape, WCS) or (array-or-shape, Header)" + ) def parse_input_weights(input_weights, hdu_weights=None): @@ -86,8 +98,10 @@ def parse_input_weights(input_weights, hdu_weights=None): elif isinstance(input_weights, HDUList): if hdu_weights is None: if len(input_weights) > 1: - raise ValueError("More than one HDU is present, please specify " - "HDU to use with ``hdu_weights=`` option") + raise ValueError( + "More than one HDU is present, please specify " + "HDU to use with ``hdu_weights=`` option" + ) else: hdu_weights = 0 return parse_input_data(input_weights[hdu_weights])[0] @@ -111,17 +125,21 @@ def parse_output_projection(output_projection, shape_out=None, output_array=None if isinstance(output_projection, Header): wcs_out = WCS(output_projection) try: - shape_out = [output_projection[f'NAXIS{i + 1}'] - for i in range(output_projection['NAXIS'])][::-1] + shape_out = [ + output_projection[f"NAXIS{i + 1}"] for i in range(output_projection["NAXIS"]) + ][::-1] except KeyError: if shape_out is None: - raise ValueError("Need to specify shape since output header " - "does not contain complete shape information") + raise ValueError( + "Need to specify shape since output header " + "does not contain complete shape information" + ) elif isinstance(output_projection, BaseHighLevelWCS): wcs_out = output_projection if shape_out is None: - raise ValueError("Need to specify shape_out when specifying " - "output_projection as WCS object") + raise ValueError( + "Need to specify shape_out when specifying " "output_projection as WCS object" + ) elif isinstance(output_projection, str): hdu_list = fits.open(output_projection) shape_out = hdu_list[0].data.shape @@ -129,17 +147,18 @@ def parse_output_projection(output_projection, shape_out=None, output_array=None wcs_out = WCS(header) hdu_list.close() else: - raise TypeError('output_projection should either be a Header, a WCS ' - 'object, or a filename') + raise TypeError( + "output_projection should either be a Header, a WCS " "object, or a filename" + ) if len(shape_out) == 0: - raise ValueError("The shape of the output image should not be an " - "empty tuple") + raise ValueError("The shape of the output image should not be an " "empty tuple") return wcs_out, shape_out -def _block(reproject_func, array_in, wcs_in, wcs_out_sub, shape_out, i_range, j_range, - return_footprint): +def _block( + reproject_func, array_in, wcs_in, wcs_out_sub, shape_out, i_range, j_range, return_footprint +): """ Implementation function that handles reprojecting subsets blocks of pixels from an input image and holds metadata about where to reinsert when done. @@ -165,8 +184,9 @@ def _block(reproject_func, array_in, wcs_in, wcs_out_sub, shape_out, i_range, j_ Passed through unmodified, used to determine where to reinsert block """ - result = reproject_func(array_in, wcs_in, wcs_out_sub, - shape_out=shape_out, return_footprint=return_footprint) + result = reproject_func( + array_in, wcs_in, wcs_out_sub, shape_out=shape_out, return_footprint=return_footprint + ) res_arr = None res_fp = None @@ -176,12 +196,21 @@ def _block(reproject_func, array_in, wcs_in, wcs_out_sub, shape_out, i_range, j_ else: res_arr = result - return {'i': i_range, 'j': j_range, 'res_arr': res_arr, 'res_fp': res_fp} - - -def reproject_blocked(reproject_func, array_in, wcs_in, shape_out, wcs_out, block_size, - output_array=None, - return_footprint=True, output_footprint=None, parallel=True): + return {"i": i_range, "j": j_range, "res_arr": res_arr, "res_fp": res_fp} + + +def reproject_blocked( + reproject_func, + array_in, + wcs_in, + shape_out, + wcs_out, + block_size, + output_array=None, + return_footprint=True, + output_footprint=None, + parallel=True, +): """ Implementation function that handles reprojecting subsets blocks of pixels from an input image and holds metadata about where to reinsert when done. @@ -260,23 +289,34 @@ def reproject_blocked(reproject_func, array_in, wcs_in, shape_out, wcs_out, bloc if proc_pool is None: # if sequential input data and reinsert block into main array immediately - completed_block = _block(reproject_func=reproject_func, array_in=array_in, - wcs_in=wcs_in, - wcs_out_sub=wcs_out_sub, shape_out=shape_out_sub, - return_footprint=return_footprint, - j_range=(jmin, jmax), i_range=(imin, imax)) - - output_array[imin:imax, jmin:jmax] = completed_block['res_arr'][:] + completed_block = _block( + reproject_func=reproject_func, + array_in=array_in, + wcs_in=wcs_in, + wcs_out_sub=wcs_out_sub, + shape_out=shape_out_sub, + return_footprint=return_footprint, + j_range=(jmin, jmax), + i_range=(imin, imax), + ) + + output_array[imin:imax, jmin:jmax] = completed_block["res_arr"][:] if return_footprint: - output_footprint[imin:imax, jmin:jmax] = completed_block['res_fp'][:] + output_footprint[imin:imax, jmin:jmax] = completed_block["res_fp"][:] else: # if parallel just submit all work items and move on to waiting for them to be done - future = proc_pool.submit(_block, reproject_func=reproject_func, array_in=array_in, - wcs_in=wcs_in, wcs_out_sub=wcs_out_sub, - shape_out=shape_out_sub, - return_footprint=return_footprint, j_range=(jmin, jmax), - i_range=(imin, imax)) + future = proc_pool.submit( + _block, + reproject_func=reproject_func, + array_in=array_in, + wcs_in=wcs_in, + wcs_out_sub=wcs_out_sub, + shape_out=shape_out_sub, + return_footprint=return_footprint, + j_range=(jmin, jmax), + i_range=(imin, imax), + ) blocks_futures.append(future) # If a parallel implementation is being used that means the @@ -286,14 +326,15 @@ def reproject_blocked(reproject_func, array_in, wcs_in, shape_out, wcs_out, bloc completed_future_count = 0 for completed_future in futures.as_completed(blocks_futures): completed_block = completed_future.result() - i_range = completed_block['i'] - j_range = completed_block['j'] - output_array[i_range[0]:i_range[1], j_range[0]:j_range[1]] = ( - completed_block['res_arr'][:]) + i_range = completed_block["i"] + j_range = completed_block["j"] + output_array[i_range[0] : i_range[1], j_range[0] : j_range[1]] = completed_block[ + "res_arr" + ][:] if return_footprint: - footprint_block = completed_block['res_fp'][:] - output_footprint[i_range[0]:i_range[1], j_range[0]:j_range[1]] = footprint_block + footprint_block = completed_block["res_fp"][:] + output_footprint[i_range[0] : i_range[1], j_range[0] : j_range[1]] = footprint_block completed_future_count += 1 idx = blocks_futures.index(completed_future) diff --git a/reproject/wcs_utils.py b/reproject/wcs_utils.py index fcb6b1834..d8ab5583e 100644 --- a/reproject/wcs_utils.py +++ b/reproject/wcs_utils.py @@ -9,7 +9,7 @@ from astropy.wcs import WCS from numpy.lib.stride_tricks import as_strided -__all__ = ['efficient_pixel_to_pixel', 'has_celestial'] +__all__ = ["efficient_pixel_to_pixel", "has_celestial"] def unbroadcast(array): @@ -235,7 +235,7 @@ def efficient_pixel_to_pixel_with_roundtrip(wcs1, wcs2, *inputs): inputs_check = efficient_pixel_to_pixel(wcs2, wcs1, *outputs) reset = np.zeros(inputs_check[0].shape, dtype=bool) for ipix in range(len(inputs_check)): - reset |= (np.abs(inputs_check[ipix] - inputs[ipix]) > 1) + reset |= np.abs(inputs_check[ipix] - inputs[ipix]) > 1 if np.any(reset): for ipix in range(len(inputs_check)): outputs[ipix] = outputs[ipix].copy() From 8996823b7e1e6fd9c492e64491834a8853281180 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Thu, 6 Oct 2022 23:29:13 +0100 Subject: [PATCH 027/366] Use pixel_to_pixel from astropy.wcs.utils --- reproject/adaptive/core.py | 8 +- reproject/interpolation/core.py | 8 +- reproject/wcs_utils.py | 212 +------------------------------- 3 files changed, 14 insertions(+), 214 deletions(-) diff --git a/reproject/adaptive/core.py b/reproject/adaptive/core.py index 2694ff070..9a25b5ea8 100644 --- a/reproject/adaptive/core.py +++ b/reproject/adaptive/core.py @@ -2,7 +2,9 @@ import numpy as np -from ..wcs_utils import efficient_pixel_to_pixel, efficient_pixel_to_pixel_with_roundtrip +from astropy.wcs.utils import pixel_to_pixel + +from ..wcs_utils import pixel_to_pixel_with_roundtrip from .deforest import map_coordinates __all__ = ["_reproject_adaptive_2d"] @@ -17,11 +19,11 @@ def __init__(self, wcs_in, wcs_out, roundtrip_coords): def __call__(self, pixel_out): pixel_out = pixel_out[:, :, 0], pixel_out[:, :, 1] if self.roundtrip_coords: - pixel_in = efficient_pixel_to_pixel_with_roundtrip( + pixel_in = pixel_to_pixel_with_roundtrip( self.wcs_out, self.wcs_in, *pixel_out ) else: - pixel_in = efficient_pixel_to_pixel(self.wcs_out, self.wcs_in, *pixel_out) + pixel_in = pixel_to_pixel(self.wcs_out, self.wcs_in, *pixel_out) pixel_in = np.array(pixel_in).transpose().swapaxes(0, 1) return pixel_in diff --git a/reproject/interpolation/core.py b/reproject/interpolation/core.py index 1ab4fd4fd..7c918f410 100644 --- a/reproject/interpolation/core.py +++ b/reproject/interpolation/core.py @@ -2,11 +2,11 @@ import numpy as np from astropy.wcs import WCS +from astropy.wcs.utils import pixel_to_pixel from ..array_utils import map_coordinates from ..wcs_utils import ( - efficient_pixel_to_pixel, - efficient_pixel_to_pixel_with_roundtrip, + pixel_to_pixel_with_roundtrip, has_celestial, ) @@ -96,9 +96,9 @@ def _reproject_full( pixel_out = [p.ravel() for p in pixel_out] # For each pixel in the ouput array, get the pixel value in the input WCS if roundtrip_coords: - pixel_in = efficient_pixel_to_pixel_with_roundtrip(wcs_out, wcs_in, *pixel_out[::-1])[::-1] + pixel_in = pixel_to_pixel_with_roundtrip(wcs_out, wcs_in, *pixel_out[::-1])[::-1] else: - pixel_in = efficient_pixel_to_pixel(wcs_out, wcs_in, *pixel_out[::-1])[::-1] + pixel_in = pixel_to_pixel(wcs_out, wcs_in, *pixel_out[::-1])[::-1] pixel_in = np.array(pixel_in) if array_out is not None: diff --git a/reproject/wcs_utils.py b/reproject/wcs_utils.py index d8ab5583e..47a24e2f6 100644 --- a/reproject/wcs_utils.py +++ b/reproject/wcs_utils.py @@ -7,211 +7,9 @@ import numpy as np from astropy.coordinates import SkyCoord from astropy.wcs import WCS -from numpy.lib.stride_tricks import as_strided +from astropy.wcs.utils import pixel_to_pixel -__all__ = ["efficient_pixel_to_pixel", "has_celestial"] - - -def unbroadcast(array): - """ - Given an array, return a new array that is the smallest subset of the - original array that can be re-broadcasted back to the original array. - - See https://stackoverflow.com/questions/40845769/un-broadcasting-numpy-arrays - for more details. - """ - - if array.ndim == 0: - return array - - new_shape = np.where(np.array(array.strides) == 0, 1, array.shape) - return as_strided(array, shape=new_shape) - - -def unique_with_order_preserved(items): - """ - Return a list of unique items in the list provided, preserving the order - in which they are found. - """ - new_items = [] - for item in items: - if item not in new_items: - new_items.append(item) - return new_items - - -def pixel_to_world_correlation_matrix(wcs): - """ - Return a correlation matrix between the pixel coordinates and the - high level world coordinates, along with the list of high level world - coordinate classes. - - The shape of the matrix is ``(n_world, n_pix)``, where ``n_world`` is the - number of high level world coordinates. - """ - - # We basically want to collapse the world dimensions together that are - # combined into the same high-level objects. - - # Get the following in advance as getting these properties can be expensive - all_components = wcs.low_level_wcs.world_axis_object_components - all_classes = wcs.low_level_wcs.world_axis_object_classes - axis_correlation_matrix = wcs.low_level_wcs.axis_correlation_matrix - - components = unique_with_order_preserved([c[0] for c in all_components]) - - matrix = np.zeros((len(components), wcs.pixel_n_dim), dtype=bool) - - for iworld in range(wcs.world_n_dim): - iworld_unique = components.index(all_components[iworld][0]) - matrix[iworld_unique] |= axis_correlation_matrix[iworld] - - classes = [all_classes[component][0] for component in components] - - return matrix, classes - - -def pixel_to_pixel_correlation_matrix(wcs1, wcs2): - """ - Correlation matrix between the input and output pixel coordinates for a - pixel -> world -> pixel transformation specified by two WCS instances. - - The first WCS specified is the one used for the pixel -> world - transformation and the second WCS specified is the one used for the world -> - pixel transformation. The shape of the matrix is - ``(n_pixel_out, n_pixel_in)``. - """ - - matrix1, classes1 = pixel_to_world_correlation_matrix(wcs1) - matrix2, classes2 = pixel_to_world_correlation_matrix(wcs2) - - if len(classes1) != len(classes2): - raise ValueError("The two WCS return a different number of world coordinates") - - # Check if classes match uniquely - unique_match = True - mapping = [] - for class1 in classes1: - matches = classes2.count(class1) - if matches == 0: - raise ValueError("The world coordinate types of the two WCS don't match") - elif matches > 1: - unique_match = False - break - else: - mapping.append(classes2.index(class1)) - - if unique_match: - - # Classes are unique, so we need to re-order matrix2 along the world - # axis using the mapping we found above. - matrix2 = matrix2[mapping] - - elif classes1 != classes2: - - raise ValueError("World coordinate order doesn't match and automatic matching is ambiguous") - - matrix = np.matmul(matrix2.T, matrix1) - - return matrix - - -def split_matrix(matrix): - """ - Given an axis correlation matrix from a WCS object, return information about - the individual WCS that can be split out. - - The output is a list of tuples, where each tuple contains a list of - pixel dimensions and a list of world dimensions that can be extracted to - form a new WCS. For example, in the case of a spectral cube with the first - two world coordinates being the celestial coordinates and the third - coordinate being an uncorrelated spectral axis, the matrix would look like:: - - array([[ True, True, False], - [ True, True, False], - [False, False, True]]) - - and this function will return ``[([0, 1], [0, 1]), ([2], [2])]``. - """ - - pixel_used = [] - - split_info = [] - - for ipix in range(matrix.shape[1]): - if ipix in pixel_used: - continue - pixel_include = np.zeros(matrix.shape[1], dtype=bool) - pixel_include[ipix] = True - n_pix_prev, n_pix = 0, 1 - while n_pix > n_pix_prev: - world_include = matrix[:, pixel_include].any(axis=1) - pixel_include = matrix[world_include, :].any(axis=0) - n_pix_prev, n_pix = n_pix, np.sum(pixel_include) - pixel_indices = list(np.nonzero(pixel_include)[0]) - world_indices = list(np.nonzero(world_include)[0]) - pixel_used.extend(pixel_indices) - split_info.append((pixel_indices, world_indices)) - - return split_info - - -def efficient_pixel_to_pixel(wcs1, wcs2, *inputs): - """ - Wrapper that performs a pixel -> world -> pixel transformation and - un-broadcasting arrays whenever possible for efficiency. - - Parameters - ---------- - wcs1 : `~astropy.wcs.WCS` - First WCS instance. - wcs2 : `~astropy.wcs.WCS` - Second WCS instance. - inputs : list[numpy.ndarray] - Pixels in the frame of ``wcs1``. - - Returns - ------- - outputs : list[numpy.ndarray] - Transformed pixels in the frame of ``wcs2``. - """ - - # Shortcut for scalars - if np.isscalar(inputs[0]): - world_outputs = wcs1.pixel_to_world(*inputs) - if not isinstance(world_outputs, (tuple, list)): - world_outputs = (world_outputs,) - return wcs2.world_to_pixel(*world_outputs) - - # Remember original shape - original_shape = inputs[0].shape - - matrix = pixel_to_pixel_correlation_matrix(wcs1, wcs2) - split_info = split_matrix(matrix) - - outputs = [None] * wcs2.pixel_n_dim - - for (pixel_in_indices, pixel_out_indices) in split_info: - - pixel_inputs = [] - for ipix in range(wcs1.pixel_n_dim): - if ipix in pixel_in_indices: - pixel_inputs.append(unbroadcast(inputs[ipix])) - else: - pixel_inputs.append(inputs[ipix].flat[0]) - - pixel_inputs = np.broadcast_arrays(*pixel_inputs) - - world_outputs = wcs1.pixel_to_world(*pixel_inputs) - if not isinstance(world_outputs, (tuple, list)): - world_outputs = (world_outputs,) - pixel_outputs = wcs2.world_to_pixel(*world_outputs) - - for ipix in range(wcs2.pixel_n_dim): - if ipix in pixel_out_indices: - outputs[ipix] = np.broadcast_to(pixel_outputs[ipix], original_shape) - - return outputs +__all__ = ["has_celestial", "pixel_to_pixel_with_roundtrip"] def has_celestial(wcs): @@ -227,12 +25,12 @@ def has_celestial(wcs): return False -def efficient_pixel_to_pixel_with_roundtrip(wcs1, wcs2, *inputs): +def pixel_to_pixel_with_roundtrip(wcs1, wcs2, *inputs): - outputs = efficient_pixel_to_pixel(wcs1, wcs2, *inputs) + outputs = pixel_to_pixel(wcs1, wcs2, *inputs) # Now convert back to check that coordinates round-trip, if not then set to NaN - inputs_check = efficient_pixel_to_pixel(wcs2, wcs1, *outputs) + inputs_check = pixel_to_pixel(wcs2, wcs1, *outputs) reset = np.zeros(inputs_check[0].shape, dtype=bool) for ipix in range(len(inputs_check)): reset |= np.abs(inputs_check[ipix] - inputs[ipix]) > 1 From 9c34df4605254eeae857b5e3ccadfd45146b3d4b Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Fri, 7 Oct 2022 00:04:09 +0100 Subject: [PATCH 028/366] Fix code style --- reproject/adaptive/core.py | 5 +---- reproject/interpolation/core.py | 5 +---- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/reproject/adaptive/core.py b/reproject/adaptive/core.py index 9a25b5ea8..57661748b 100644 --- a/reproject/adaptive/core.py +++ b/reproject/adaptive/core.py @@ -1,7 +1,6 @@ # Licensed under a 3-clause BSD style license - see LICENSE.rst import numpy as np - from astropy.wcs.utils import pixel_to_pixel from ..wcs_utils import pixel_to_pixel_with_roundtrip @@ -19,9 +18,7 @@ def __init__(self, wcs_in, wcs_out, roundtrip_coords): def __call__(self, pixel_out): pixel_out = pixel_out[:, :, 0], pixel_out[:, :, 1] if self.roundtrip_coords: - pixel_in = pixel_to_pixel_with_roundtrip( - self.wcs_out, self.wcs_in, *pixel_out - ) + pixel_in = pixel_to_pixel_with_roundtrip(self.wcs_out, self.wcs_in, *pixel_out) else: pixel_in = pixel_to_pixel(self.wcs_out, self.wcs_in, *pixel_out) pixel_in = np.array(pixel_in).transpose().swapaxes(0, 1) diff --git a/reproject/interpolation/core.py b/reproject/interpolation/core.py index 7c918f410..22bedc3f4 100644 --- a/reproject/interpolation/core.py +++ b/reproject/interpolation/core.py @@ -5,10 +5,7 @@ from astropy.wcs.utils import pixel_to_pixel from ..array_utils import map_coordinates -from ..wcs_utils import ( - pixel_to_pixel_with_roundtrip, - has_celestial, -) +from ..wcs_utils import has_celestial, pixel_to_pixel_with_roundtrip def _validate_wcs(wcs_in, wcs_out, shape_out): From 3407e094854ebd5fa9c8329dbfc99c232f00efea Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 10 Oct 2022 16:31:24 +0000 Subject: [PATCH 029/366] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/asottile/pyupgrade: v2.38.2 → v3.0.0](https://github.com/asottile/pyupgrade/compare/v2.38.2...v3.0.0) - [github.com/psf/black: 22.6.0 → 22.10.0](https://github.com/psf/black/compare/22.6.0...22.10.0) --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index d71c1fe1d..7f9ebd3b5 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -20,7 +20,7 @@ repos: - "--filter-files" - repo: https://github.com/asottile/pyupgrade - rev: v2.38.2 + rev: v3.0.0 hooks: - id: pyupgrade args: ["--py38-plus"] @@ -67,7 +67,7 @@ repos: exclude: ".*(data.*|extern.*|cextern)$" - repo: https://github.com/psf/black - rev: 22.6.0 + rev: 22.10.0 hooks: - id: black From ec042b98aa8d2ff4bdd617ad6474d2d0b77f2f39 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 17 Oct 2022 16:30:45 +0000 Subject: [PATCH 030/366] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/asottile/pyupgrade: v3.0.0 → v3.1.0](https://github.com/asottile/pyupgrade/compare/v3.0.0...v3.1.0) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 7f9ebd3b5..3f3b7eb43 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -20,7 +20,7 @@ repos: - "--filter-files" - repo: https://github.com/asottile/pyupgrade - rev: v3.0.0 + rev: v3.1.0 hooks: - id: pyupgrade args: ["--py38-plus"] From 6e693fa2631d129a6031e3cabb237043107f05f8 Mon Sep 17 00:00:00 2001 From: David Stansby Date: Mon, 11 Jul 2022 22:19:20 +0100 Subject: [PATCH 031/366] Test CI on Python 3.11 --- .github/workflows/ci_workflows.yml | 15 +++++---------- pyproject.toml | 2 +- tox.ini | 12 +++++------- 3 files changed, 11 insertions(+), 18 deletions(-) diff --git a/.github/workflows/ci_workflows.yml b/.github/workflows/ci_workflows.yml index adee4ca01..a4e848260 100644 --- a/.github/workflows/ci_workflows.yml +++ b/.github/workflows/ci_workflows.yml @@ -14,16 +14,11 @@ jobs: uses: OpenAstronomy/github-actions-workflows/.github/workflows/tox.yml@v1 with: envs: | - - macos: py38-test-oldestdeps - - macos: py39-test - - macos: py310-test - - linux: py38-test-oldestdeps - - linux: py38-test - - linux: py39-test - - linux: py310-test - - windows: py38-test-oldestdeps - - windows: py39-test - - windows: py310-test + - linux: py311-test-devdeps + python-version: '3.11.0-rc.2' + libraries: | + apt: + - libopenblas-dev coverage: 'codecov' publish: diff --git a/pyproject.toml b/pyproject.toml index e61bbbda2..0f2a9696e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ requires = ["setuptools", "wheel", "extension-helpers", "oldest-supported-numpy", - "cython==0.29.21"] + "cython==0.29.30"] build-backend = 'setuptools.build_meta' [tool.setuptools_scm] diff --git a/tox.ini b/tox.ini index cae65cd5a..34ac7749f 100644 --- a/tox.ini +++ b/tox.ini @@ -1,12 +1,9 @@ [tox] envlist = - py{37,38,39,310}-{test}{-oldestdeps,-numpy121} + py{37,38,39,310,311}-{test}{-oldestdeps,-numpy121} build_docs codestyle isolated_build = True -indexserver = - NIGHTLY = https://pypi.anaconda.org/scipy-wheels-nightly/simple - ASTROPY = https://pkgs.dev.azure.com/astropy-project/astropy/_packaging/nightly/pypi/simple/ [testenv] whitelist_externals = @@ -17,6 +14,7 @@ setenv = HOME = {envtmpdir} MPLBACKEND = Agg PYTEST_COMMAND = pytest --arraydiff --arraydiff-default-format=fits --pyargs reproject --cov reproject --cov-config={toxinidir}/setup.cfg {toxinidir}/docs + devdeps: PIP_EXTRA_INDEX_URL = https://pypi.anaconda.org/astropy/simple https://pypi.anaconda.org/scipy-wheels-nightly/simple changedir = .tmp/{envname} deps = @@ -27,9 +25,9 @@ deps = oldestdeps: astropy-healpix==0.6 oldestdeps: scipy==1.3.2 - devdeps: :NIGHTLY:numpy - devdeps: :NIGHTLY:scipy - devdeps: :ASTROPY:astropy + devdeps: numpy>=0.0.dev0 + devdeps: scipy>=0.0.dev0 + devdeps: astropy>=0.0.dev0 devdeps: git+https://github.com/asdf-format/asdf.git#egg=asdf devdeps: git+https://github.com/astropy/asdf-astropy.git devdeps: git+https://github.com/spacetelescope/gwcs.git#egg=gwcs From 79ffb2793925d55c7d9674994b00c675c1f31675 Mon Sep 17 00:00:00 2001 From: Stuart Mumford Date: Thu, 15 Sep 2022 12:02:38 +0100 Subject: [PATCH 032/366] cruft --- setup.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index 0d05072e8..c57e8fc6b 100644 --- a/setup.cfg +++ b/setup.cfg @@ -3,7 +3,7 @@ name = reproject author = Thomas Robitaille, Christoph Deil, Adam Ginsburg author_email = thomas.robitaille@gmail.com license = BSD 3-Clause -license_file = LICENSE +license_files = LICENSE url = https://reproject.readthedocs.io description = Reproject astronomical images long_description = file: README.rst From 3902ee3564895e529d765577f4c6785dd97e73da Mon Sep 17 00:00:00 2001 From: Stuart Mumford Date: Thu, 15 Sep 2022 12:40:53 +0100 Subject: [PATCH 033/366] HACKS --- tox.ini | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tox.ini b/tox.ini index 34ac7749f..03d021300 100644 --- a/tox.ini +++ b/tox.ini @@ -31,12 +31,13 @@ deps = devdeps: git+https://github.com/asdf-format/asdf.git#egg=asdf devdeps: git+https://github.com/astropy/asdf-astropy.git devdeps: git+https://github.com/spacetelescope/gwcs.git#egg=gwcs - devdeps: git+https://github.com/sunpy/sunpy.git#egg=sunpy + #devdeps: git+https://github.com/sunpy/sunpy.git#egg=sunpy extras = test # Don't run the more complex tests on oldestdeps because it pulls in a nest # web of dependencies much newer than our mins - !oldestdeps: testall + #!oldestdeps: testall + !devdeps: testall commands = pip freeze !oldestdeps: {env:PYTEST_COMMAND} {posargs} From 0ed1a1836ff78976e7f2c554bc2f503eb03bc743 Mon Sep 17 00:00:00 2001 From: Stuart Mumford Date: Wed, 26 Oct 2022 11:31:25 +0100 Subject: [PATCH 034/366] 3.11 final --- .github/workflows/ci_workflows.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/ci_workflows.yml b/.github/workflows/ci_workflows.yml index a4e848260..4600884d1 100644 --- a/.github/workflows/ci_workflows.yml +++ b/.github/workflows/ci_workflows.yml @@ -15,7 +15,6 @@ jobs: with: envs: | - linux: py311-test-devdeps - python-version: '3.11.0-rc.2' libraries: | apt: - libopenblas-dev From 3193ccf1b8d60135af3545a36fa10db2296ec3c6 Mon Sep 17 00:00:00 2001 From: Stuart Mumford Date: Wed, 26 Oct 2022 12:39:38 +0100 Subject: [PATCH 035/366] Add back old CI --- .github/workflows/ci_workflows.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.github/workflows/ci_workflows.yml b/.github/workflows/ci_workflows.yml index 4600884d1..3b72ce1ed 100644 --- a/.github/workflows/ci_workflows.yml +++ b/.github/workflows/ci_workflows.yml @@ -14,7 +14,17 @@ jobs: uses: OpenAstronomy/github-actions-workflows/.github/workflows/tox.yml@v1 with: envs: | + - macos: py38-test-oldestdeps + - macos: py39-test + - macos: py310-test + - linux: py38-test-oldestdeps + - linux: py38-test + - linux: py39-test + - linux: py310-test - linux: py311-test-devdeps + - windows: py38-test-oldestdeps + - windows: py39-test + - windows: py310-test libraries: | apt: - libopenblas-dev From f64d2060d67b0a74f4c842d675d7fd4b55268b83 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Fri, 7 Oct 2022 10:15:59 +0100 Subject: [PATCH 036/366] Update pinned version of Cython --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 0f2a9696e..ecc716bac 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ requires = ["setuptools", "wheel", "extension-helpers", "oldest-supported-numpy", - "cython==0.29.30"] + "cython==0.29.32"] build-backend = 'setuptools.build_meta' [tool.setuptools_scm] From 2cc4023d15e0b66a94e3faf0efdd5834986acbaa Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Tue, 18 Oct 2022 00:18:20 +0100 Subject: [PATCH 037/366] Use scipy==1.9.1 for win32 wheels as subsequent versions have no win32 wheels --- pyproject.toml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index ecc716bac..5bc2991de 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -14,6 +14,10 @@ write_to = "reproject/version.py" skip = "cp36-* pp* *-musllinux* cp310-win32" test-skip = "*-macosx_arm64 *-manylinux_aarch64" +[[tool.cibuildwheel.overrides]] +select = "*-win32" +test-requires = "scipy==1.9.1" + [tool.isort] profile = "black" multi_line_output = 3 From 3f58b47a58ec853e6402db30cef827fdeb8705fe Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Thu, 27 Oct 2022 09:13:16 +0100 Subject: [PATCH 038/366] Fix how scipy==1.9.1 is installed for cibuildwheel --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 5bc2991de..e574b1173 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -16,7 +16,7 @@ test-skip = "*-macosx_arm64 *-manylinux_aarch64" [[tool.cibuildwheel.overrides]] select = "*-win32" -test-requires = "scipy==1.9.1" +before-test = "pip install scipy==1.9.1" [tool.isort] profile = "black" From ca0d8b419a54776c688e39223284b8c7c51e005e Mon Sep 17 00:00:00 2001 From: Stuart Mumford Date: Thu, 27 Oct 2022 11:24:22 +0100 Subject: [PATCH 039/366] Fix oldestdeps --- tox.ini | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tox.ini b/tox.ini index 03d021300..e448c4d0a 100644 --- a/tox.ini +++ b/tox.ini @@ -36,8 +36,7 @@ extras = test # Don't run the more complex tests on oldestdeps because it pulls in a nest # web of dependencies much newer than our mins - #!oldestdeps: testall - !devdeps: testall + !oldestdeps: testall commands = pip freeze !oldestdeps: {env:PYTEST_COMMAND} {posargs} From da24ffb804e857e35de1c12a1649b2de39df9529 Mon Sep 17 00:00:00 2001 From: Stuart Mumford Date: Thu, 27 Oct 2022 15:22:15 +0100 Subject: [PATCH 040/366] Ignore python 3.11 deprecation warnings for now --- setup.cfg | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/setup.cfg b/setup.cfg index c57e8fc6b..bf5c97cae 100644 --- a/setup.cfg +++ b/setup.cfg @@ -63,6 +63,10 @@ filterwarnings = ignore:No observer defined on WCS:astropy.utils.exceptions.AstropyUserWarning ignore:unclosed file:ResourceWarning ignore:The conversion of these 2D helioprojective coordinates to 3D is all NaNs + # This is a sunpy < 4.1 issue with Python 3.11 + ignore:'xdrlib' is deprecated and slated for removal in Python 3.13 + # This is a pyvo issue with Python 3.11 + ignore:'cgi' is deprecated and slated for removal in Python 3.13 [coverage:run] omit = From d1b48e6ccd70bc67d07f47eb9a9536bb4a1af305 Mon Sep 17 00:00:00 2001 From: Stuart Mumford Date: Thu, 27 Oct 2022 16:25:17 +0100 Subject: [PATCH 041/366] No win32 on 3.11 --- .github/workflows/ci_workflows.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci_workflows.yml b/.github/workflows/ci_workflows.yml index 3b72ce1ed..9709fb241 100644 --- a/.github/workflows/ci_workflows.yml +++ b/.github/workflows/ci_workflows.yml @@ -41,6 +41,6 @@ jobs: - cp*-manylinux_aarch64 - cp*-macosx_x86_64 - cp*-macosx_arm64 - - cp*-win* + - cp{8,9,10}-win* secrets: pypi_token: ${{ secrets.pypi_token }} From 5c1c26a178d7016ed488f0f24efa4a2bb1aef019 Mon Sep 17 00:00:00 2001 From: Stuart Mumford Date: Thu, 27 Oct 2022 16:55:07 +0100 Subject: [PATCH 042/366] I give up with windows 32bit --- .github/workflows/ci_workflows.yml | 2 +- pyproject.toml | 4 ---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/.github/workflows/ci_workflows.yml b/.github/workflows/ci_workflows.yml index 9709fb241..27a1f0e46 100644 --- a/.github/workflows/ci_workflows.yml +++ b/.github/workflows/ci_workflows.yml @@ -41,6 +41,6 @@ jobs: - cp*-manylinux_aarch64 - cp*-macosx_x86_64 - cp*-macosx_arm64 - - cp{8,9,10}-win* + - cp*-win_amd64 secrets: pypi_token: ${{ secrets.pypi_token }} diff --git a/pyproject.toml b/pyproject.toml index e574b1173..ecc716bac 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -14,10 +14,6 @@ write_to = "reproject/version.py" skip = "cp36-* pp* *-musllinux* cp310-win32" test-skip = "*-macosx_arm64 *-manylinux_aarch64" -[[tool.cibuildwheel.overrides]] -select = "*-win32" -before-test = "pip install scipy==1.9.1" - [tool.isort] profile = "black" multi_line_output = 3 From 5800d358d1049e64f1775bca7569c9fba494d1af Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Thu, 27 Oct 2022 17:13:15 +0100 Subject: [PATCH 043/366] Speed up test_blocked_against_single by increasing smallest block size tested --- reproject/interpolation/tests/test_core.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reproject/interpolation/tests/test_core.py b/reproject/interpolation/tests/test_core.py index 1fe4a0590..810809074 100644 --- a/reproject/interpolation/tests/test_core.py +++ b/reproject/interpolation/tests/test_core.py @@ -630,7 +630,7 @@ def test_identity_with_offset(roundtrip_coords): @pytest.mark.parametrize("parallel", [True, 2, False]) -@pytest.mark.parametrize("block_size", [[10, 10], [500, 500], [500, 100], None]) +@pytest.mark.parametrize("block_size", [[40, 40], [500, 500], [500, 100], None]) def test_blocked_against_single(parallel, block_size): # Ensure when we break a reprojection down into multiple discrete blocks From 75d1a00acd56acbfa9e06e26e35eec34809a5391 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 31 Oct 2022 16:31:56 +0000 Subject: [PATCH 044/366] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/asottile/pyupgrade: v3.1.0 → v3.2.0](https://github.com/asottile/pyupgrade/compare/v3.1.0...v3.2.0) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 3f3b7eb43..41d768bbe 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -20,7 +20,7 @@ repos: - "--filter-files" - repo: https://github.com/asottile/pyupgrade - rev: v3.1.0 + rev: v3.2.0 hooks: - id: pyupgrade args: ["--py38-plus"] From 8964a62cf69af690ca95343a4d2b7ee28e5d97fa Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 14 Nov 2022 16:32:11 +0000 Subject: [PATCH 045/366] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/asottile/pyupgrade: v3.2.0 → v3.2.2](https://github.com/asottile/pyupgrade/compare/v3.2.0...v3.2.2) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 41d768bbe..1e722c15e 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -20,7 +20,7 @@ repos: - "--filter-files" - repo: https://github.com/asottile/pyupgrade - rev: v3.2.0 + rev: v3.2.2 hooks: - id: pyupgrade args: ["--py38-plus"] From acf134e95d898cebb0541dbf6ca13b0f714470a8 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 28 Nov 2022 16:32:50 +0000 Subject: [PATCH 046/366] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/pre-commit/pre-commit-hooks: v4.3.0 → v4.4.0](https://github.com/pre-commit/pre-commit-hooks/compare/v4.3.0...v4.4.0) - [github.com/PyCQA/flake8: 5.0.4 → 6.0.0](https://github.com/PyCQA/flake8/compare/5.0.4...6.0.0) --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 1e722c15e..734475cae 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.3.0 + rev: v4.4.0 hooks: - id: check-added-large-files - id: check-case-conflict @@ -55,7 +55,7 @@ repos: # F822: undefined name in __all__ # F823: local variable name referenced before assignment - repo: https://github.com/PyCQA/flake8 - rev: 5.0.4 + rev: 6.0.0 hooks: - id: flake8 args: From 6184071306cf234b7e75930dba214158bf5cc1cc Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 5 Dec 2022 16:32:33 +0000 Subject: [PATCH 047/366] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/asottile/pyupgrade: v3.2.2 → v3.3.0](https://github.com/asottile/pyupgrade/compare/v3.2.2...v3.3.0) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 734475cae..b93b09786 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -20,7 +20,7 @@ repos: - "--filter-files" - repo: https://github.com/asottile/pyupgrade - rev: v3.2.2 + rev: v3.3.0 hooks: - id: pyupgrade args: ["--py38-plus"] From 9c8f946407f061a8df443a35693f96f5e07779bd Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 19 Dec 2022 16:31:59 +0000 Subject: [PATCH 048/366] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/pycqa/isort: 5.10.1 → v5.11.3](https://github.com/pycqa/isort/compare/5.10.1...v5.11.3) - [github.com/asottile/pyupgrade: v3.3.0 → v3.3.1](https://github.com/asottile/pyupgrade/compare/v3.3.0...v3.3.1) - [github.com/psf/black: 22.10.0 → 22.12.0](https://github.com/psf/black/compare/22.10.0...22.12.0) --- .pre-commit-config.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index b93b09786..fb0d8f9c4 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -12,7 +12,7 @@ repos: exclude: ".*(data.*|extern.*|licenses.*|.*.fits)$" - repo: https://github.com/pycqa/isort - rev: 5.10.1 + rev: v5.11.3 hooks: - id: isort name: isort (python) @@ -20,7 +20,7 @@ repos: - "--filter-files" - repo: https://github.com/asottile/pyupgrade - rev: v3.3.0 + rev: v3.3.1 hooks: - id: pyupgrade args: ["--py38-plus"] @@ -67,7 +67,7 @@ repos: exclude: ".*(data.*|extern.*|cextern)$" - repo: https://github.com/psf/black - rev: 22.10.0 + rev: 22.12.0 hooks: - id: black From 6987dc03f4ffc54512ec4fb40bed32e6a2d3c83b Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 26 Dec 2022 16:33:08 +0000 Subject: [PATCH 049/366] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/pycqa/isort: v5.11.3 → 5.11.4](https://github.com/pycqa/isort/compare/v5.11.3...5.11.4) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index fb0d8f9c4..277b3ee42 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -12,7 +12,7 @@ repos: exclude: ".*(data.*|extern.*|licenses.*|.*.fits)$" - repo: https://github.com/pycqa/isort - rev: v5.11.3 + rev: 5.11.4 hooks: - id: isort name: isort (python) From 18ac2ecd862c211872fc6c21d3be934e9b7cbb40 Mon Sep 17 00:00:00 2001 From: Sam Van Kooten Date: Tue, 27 Dec 2022 17:26:30 -0700 Subject: [PATCH 050/366] Ensure FITS files are closed after reading --- reproject/utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/reproject/utils.py b/reproject/utils.py index d16619207..72ac23eb6 100644 --- a/reproject/utils.py +++ b/reproject/utils.py @@ -22,8 +22,8 @@ def parse_input_data(input_data, hdu_in=None): """ if isinstance(input_data, str): - # NOTE: File handler is not closed here. - return parse_input_data(fits.open(input_data), hdu_in=hdu_in) + with fits.open(input_data) as hdul: + return parse_input_data(hdul, hdu_in=hdu_in) elif isinstance(input_data, HDUList): if hdu_in is None: if len(input_data) > 1: From 6fbceb0063c9463e6e07ad1537d79463e4d1ed35 Mon Sep 17 00:00:00 2001 From: Sam Van Kooten Date: Tue, 27 Dec 2022 17:27:10 -0700 Subject: [PATCH 051/366] Expand testing of parse_input_data to cover returned WCS --- reproject/tests/test_utils.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/reproject/tests/test_utils.py b/reproject/tests/test_utils.py index 51d9dfc53..730128958 100644 --- a/reproject/tests/test_utils.py +++ b/reproject/tests/test_utils.py @@ -15,11 +15,18 @@ def test_parse_input_data(tmpdir): data = np.arange(200).reshape((10, 20)) - hdu = fits.ImageHDU(data) + hdu = fits.ImageHDU(data, header) + + # We want to test that the WCS is being parsed and output correctly in each + # of these cases. WCS doesn't seem to implement __eq__, so we convert the + # output WCS to a Header and compare that. Here we convert the original + # Header to a WCS and back to ensure an apples-to-apples comparision. + ref_coord_system = WCS(header).to_header() # As HDU array, coordinate_system = parse_input_data(hdu) np.testing.assert_allclose(array, data) + assert coordinate_system.to_header() == ref_coord_system # As filename filename = tmpdir.join("test.fits").strpath @@ -33,15 +40,18 @@ def test_parse_input_data(tmpdir): array, coordinate_system = parse_input_data(filename, hdu_in=1) np.testing.assert_allclose(array, data) + assert coordinate_system.to_header() == ref_coord_system # As array, header array, coordinate_system = parse_input_data((data, header)) np.testing.assert_allclose(array, data) + assert coordinate_system.to_header() == ref_coord_system # As array, WCS wcs = WCS(hdu.header) array, coordinate_system = parse_input_data((data, wcs)) np.testing.assert_allclose(array, data) + assert coordinate_system is wcs ndd = NDData(data, wcs=wcs) array, coordinate_system = parse_input_data(ndd) From 5f47be580098484db88faa071e32a9938200eba5 Mon Sep 17 00:00:00 2001 From: Sam Van Kooten Date: Fri, 30 Dec 2022 10:33:41 -0700 Subject: [PATCH 052/366] Fix weird quotation marks from Black auto-formatting --- reproject/healpix/high_level.py | 2 +- reproject/healpix/tests/test_utils.py | 2 +- reproject/interpolation/tests/test_core.py | 16 ++++++++-------- reproject/mosaicking/coadd.py | 4 ++-- .../spherical_intersect/tests/test_high_level.py | 2 +- reproject/tests/test_utils.py | 10 +++++----- reproject/utils.py | 8 +++----- 7 files changed, 21 insertions(+), 23 deletions(-) diff --git a/reproject/healpix/high_level.py b/reproject/healpix/high_level.py index 0d245ab1d..9f20a6590 100644 --- a/reproject/healpix/high_level.py +++ b/reproject/healpix/high_level.py @@ -141,5 +141,5 @@ def reproject_to_healpix( ) else: raise NotImplementedError( - "Only data with a 2-d celestial WCS can be " "reprojected to a HEALPIX projection" + "Only data with a 2-d celestial WCS can be reprojected to a HEALPIX projection" ) diff --git a/reproject/healpix/tests/test_utils.py b/reproject/healpix/tests/test_utils.py index 49fb2c9f4..fd53e9ddc 100644 --- a/reproject/healpix/tests/test_utils.py +++ b/reproject/healpix/tests/test_utils.py @@ -54,5 +54,5 @@ def test_parse_input_healpix_data(tmpdir): 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)" + "input_data should either be an HDU object or a tuple of (array, frame)" ) diff --git a/reproject/interpolation/tests/test_core.py b/reproject/interpolation/tests/test_core.py index 810809074..2e3fdafad 100644 --- a/reproject/interpolation/tests/test_core.py +++ b/reproject/interpolation/tests/test_core.py @@ -226,7 +226,7 @@ def test_celestial_mismatch_2d(roundtrip_coords): wcs2 = WCS(header_out) with pytest.raises( - ValueError, match="Input WCS has celestial components but output WCS " "does not" + ValueError, match="Input WCS has celestial components but output WCS does not" ): array_out, footprint_out = reproject_interp( (data, wcs1), wcs2, shape_out=(2, 2), roundtrip_coords=roundtrip_coords @@ -255,14 +255,14 @@ def test_celestial_mismatch_3d(roundtrip_coords): wcs2 = WCS(header_out) with pytest.raises( - ValueError, match="Input WCS has celestial components but output WCS " "does not" + ValueError, match="Input WCS has celestial components but output WCS does not" ): array_out, footprint_out = reproject_interp( (data, wcs1), wcs2, shape_out=(1, 2, 3), roundtrip_coords=roundtrip_coords ) with pytest.raises( - ValueError, match="Output WCS has celestial components but input WCS " "does not" + ValueError, match="Output WCS has celestial components but input WCS does not" ): array_out, footprint_out = reproject_interp( (data, wcs2), wcs1, shape_out=(1, 2, 3), roundtrip_coords=roundtrip_coords @@ -301,14 +301,14 @@ def test_spectral_mismatch_3d(roundtrip_coords): wcs2 = WCS(header_out) with pytest.raises( - ValueError, match="Input WCS has a spectral component but output WCS " "does not" + ValueError, match="Input WCS has a spectral component but output WCS does not" ): array_out, footprint_out = reproject_interp( (data, wcs1), wcs2, shape_out=(1, 2, 3), roundtrip_coords=roundtrip_coords ) with pytest.raises( - ValueError, match="Output WCS has a spectral component but input WCS " "does not" + ValueError, match="Output WCS has a spectral component but input WCS does not" ): array_out, footprint_out = reproject_interp( (data, wcs2), wcs1, shape_out=(1, 2, 3), roundtrip_coords=roundtrip_coords @@ -326,7 +326,7 @@ def test_naxis_mismatch(roundtrip_coords): wcs_out = WCS(naxis=2) with pytest.raises( - ValueError, match="Number of dimensions between input and output WCS " "should match" + ValueError, match="Number of dimensions between input and output WCS should match" ): array_out, footprint_out = reproject_interp( (data, wcs_in), wcs_out, shape_out=(1, 2), roundtrip_coords=roundtrip_coords @@ -398,7 +398,7 @@ def test_4d_fails(roundtrip_coords): array_in = np.zeros((2, 3, 4, 5)) with pytest.raises( - ValueError, match="Length of shape_out should match number of dimensions " "in wcs_out" + ValueError, match="Length of shape_out should match number of dimensions in wcs_out" ): x_out, y_out, z_out = reproject_interp( (array_in, w_in), w_out, shape_out=[2, 4, 5, 6], roundtrip_coords=roundtrip_coords @@ -421,7 +421,7 @@ def test_inequal_wcs_dims(roundtrip_coords): wcs_out = WCS(header_out) with pytest.raises( - ValueError, match="Output WCS has a spectral component but input WCS " "does not" + ValueError, match="Output WCS has a spectral component but input WCS does not" ): out_cube, out_cube_valid = reproject_interp( (inp_cube, header_in), wcs_out, shape_out=(2, 4, 5), roundtrip_coords=roundtrip_coords diff --git a/reproject/mosaicking/coadd.py b/reproject/mosaicking/coadd.py index f5084ff44..be6573c5f 100644 --- a/reproject/mosaicking/coadd.py +++ b/reproject/mosaicking/coadd.py @@ -97,7 +97,7 @@ def reproject_and_coadd( if reproject_function is None: raise ValueError( - "reprojection function should be specified with " "the reproject_function argument" + "reprojection function should be specified with the reproject_function argument" ) # Parse the output projection to avoid having to do it for each @@ -234,6 +234,6 @@ def reproject_and_coadd( # Here we need to operate in chunks since we could otherwise run # into memory issues - raise NotImplementedError("combine_function='median' is " "not yet implemented") + raise NotImplementedError("combine_function='median' is not yet implemented") return final_array, final_footprint diff --git a/reproject/spherical_intersect/tests/test_high_level.py b/reproject/spherical_intersect/tests/test_high_level.py index be54d8298..ad92a47d4 100644 --- a/reproject/spherical_intersect/tests/test_high_level.py +++ b/reproject/spherical_intersect/tests/test_high_level.py @@ -84,7 +84,7 @@ def test_reproject_precision_warning(): if res < 0.05 / 3600: with pytest.warns( - UserWarning, match="The reproject_exact function " "currently has precision" + UserWarning, match="The reproject_exact function currently has precision" ): reproject_exact((array, wcs1), wcs2, shape_out=(5, 5)) else: diff --git a/reproject/tests/test_utils.py b/reproject/tests/test_utils.py index 51d9dfc53..12c522c95 100644 --- a/reproject/tests/test_utils.py +++ b/reproject/tests/test_utils.py @@ -28,7 +28,7 @@ def test_parse_input_data(tmpdir): with pytest.raises(ValueError) as exc: array, coordinate_system = parse_input_data(filename) assert exc.value.args[0] == ( - "More than one HDU is present, please specify " "HDU to use with ``hdu_in=`` option" + "More than one HDU is present, please specify HDU to use with ``hdu_in=`` option" ) array, coordinate_system = parse_input_data(filename, hdu_in=1) @@ -52,7 +52,7 @@ def test_parse_input_data(tmpdir): with pytest.raises(TypeError) as exc: parse_input_data(data) assert exc.value.args[0] == ( - "input_data should either be an HDU object or " "a tuple of (array, WCS) or (array, Header)" + "input_data should either be an HDU object or a tuple of (array, WCS) or (array, Header)" ) @@ -78,7 +78,7 @@ def test_parse_input_shape(tmpdir): with pytest.raises(ValueError) as exc: shape, coordinate_system = parse_input_shape(filename) assert exc.value.args[0] == ( - "More than one HDU is present, please specify " "HDU to use with ``hdu_in=`` option" + "More than one HDU is present, please specify HDU to use with ``hdu_in=`` option" ) shape, coordinate_system = parse_input_shape(filename, hdu_in=1) @@ -125,7 +125,7 @@ def test_parse_output_projection(tmpdir): with pytest.raises(ValueError) as exc: parse_output_projection(header) assert exc.value.args[0] == ( - "Need to specify shape since output header " "does not contain complete shape information" + "Need to specify shape since output header does not contain complete shape information" ) parse_output_projection(header, shape_out=(200, 200)) @@ -141,7 +141,7 @@ def test_parse_output_projection(tmpdir): with pytest.raises(ValueError) as exc: parse_output_projection(wcs) assert exc.value.args[0] == ( - "Need to specify shape_out when specifying " "output_projection as WCS object" + "Need to specify shape_out when specifying output_projection as WCS object" ) parse_output_projection(wcs, shape_out=(200, 200)) diff --git a/reproject/utils.py b/reproject/utils.py index d16619207..bf427df01 100644 --- a/reproject/utils.py +++ b/reproject/utils.py @@ -138,7 +138,7 @@ def parse_output_projection(output_projection, shape_out=None, output_array=None wcs_out = output_projection if shape_out is None: raise ValueError( - "Need to specify shape_out when specifying " "output_projection as WCS object" + "Need to specify shape_out when specifying output_projection as WCS object" ) elif isinstance(output_projection, str): hdu_list = fits.open(output_projection) @@ -147,12 +147,10 @@ def parse_output_projection(output_projection, shape_out=None, output_array=None wcs_out = WCS(header) hdu_list.close() else: - raise TypeError( - "output_projection should either be a Header, a WCS " "object, or a filename" - ) + raise TypeError("output_projection should either be a Header, a WCS object, or a filename") if len(shape_out) == 0: - raise ValueError("The shape of the output image should not be an " "empty tuple") + raise ValueError("The shape of the output image should not be an empty tuple") return wcs_out, shape_out From 68241db36a72c2c5f6b744c7bbbfec021c71feee Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Mon, 9 Jan 2023 11:11:38 +0000 Subject: [PATCH 053/366] Fixed doctest --- docs/mosaicking.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/mosaicking.rst b/docs/mosaicking.rst index c1b9e284c..184e5c0f5 100644 --- a/docs/mosaicking.rst +++ b/docs/mosaicking.rst @@ -162,7 +162,7 @@ Note that this requires `Shapely >> shape_out - (1800, 2201) + (1800, 2202) As expected, the optimal shape is smaller than was returned previously. From 1fb7350853974d2cb2e045a5fac471f71b69d647 Mon Sep 17 00:00:00 2001 From: Sam Van Kooten Date: Fri, 30 Dec 2022 16:57:09 -0700 Subject: [PATCH 054/366] Support broadcasting transformations across extra dims for interp algo --- reproject/interpolation/core.py | 93 ++++++++++++++++------ reproject/interpolation/high_level.py | 6 ++ reproject/interpolation/tests/test_core.py | 88 +++++++++++++++++--- 3 files changed, 152 insertions(+), 35 deletions(-) diff --git a/reproject/interpolation/core.py b/reproject/interpolation/core.py index 22bedc3f4..61fc9d8bb 100644 --- a/reproject/interpolation/core.py +++ b/reproject/interpolation/core.py @@ -8,11 +8,27 @@ from ..wcs_utils import has_celestial, pixel_to_pixel_with_roundtrip -def _validate_wcs(wcs_in, wcs_out, shape_out): +def _validate_wcs(wcs_in, wcs_out, shape_in, shape_out): if wcs_in.low_level_wcs.pixel_n_dim != wcs_out.low_level_wcs.pixel_n_dim: - raise ValueError("Number of dimensions between input and output WCS should match") - elif len(shape_out) != wcs_out.low_level_wcs.pixel_n_dim: - raise ValueError("Length of shape_out should match number of dimensions in wcs_out") + raise ValueError("Number of dimensions in input and output WCS should match") + elif len(shape_out) < wcs_out.low_level_wcs.pixel_n_dim: + raise ValueError("Too few dimensions in shape_out") + elif len(shape_in) < wcs_in.low_level_wcs.pixel_n_dim: + raise ValueError("Too few dimensions in input data") + + if len(shape_out) < len(shape_in) and len(shape_out) == wcs_out.low_level_wcs.pixel_n_dim: + # Add the broadcast dimensions to the output shape, which does not + # currently have any broadcast dims + shape_out = (*shape_in[:-len(shape_out)], *shape_out) + if len(shape_in) != len(shape_out): + raise ValueError("Number of dimensions in input and output data should match") + + # Separate the "extra" dimensions that don't correspond to a WCS axis and + # which we'll be looping over + extra_dimens_in = shape_in[: -wcs_in.low_level_wcs.pixel_n_dim] + extra_dimens_out = shape_out[: -wcs_out.low_level_wcs.pixel_n_dim] + if extra_dimens_in != extra_dimens_out: + raise ValueError("Dimensions to be looped over must match exactly") if has_celestial(wcs_in) and not has_celestial(wcs_out): raise ValueError("Input WCS has celestial components but output WCS does not") @@ -20,7 +36,6 @@ def _validate_wcs(wcs_in, wcs_out, shape_out): raise ValueError("Output WCS has celestial components but input WCS does not") if isinstance(wcs_in, WCS) and isinstance(wcs_out, WCS): - # Check whether a spectral component is present, and if so, check that # the CTYPEs match. if wcs_in.wcs.spec >= 0 and wcs_out.wcs.spec >= 0: @@ -35,6 +50,7 @@ def _validate_wcs(wcs_in, wcs_out, shape_out): raise ValueError("Input WCS has a spectral component but output WCS does not") elif wcs_out.wcs.spec >= 0: raise ValueError("Output WCS has a spectral component but input WCS does not") + return shape_out def _validate_array_out(array_out, array, shape_out): @@ -74,18 +90,44 @@ def _reproject_full( - The output shape should match the dimensionality of the WCS - The input and output WCS should have matching physical types, although the order can be different as long as the physical types are unique. - """ - _validate_wcs(wcs_in, wcs_out, shape_out) + If the input array contains extra dimensions beyond what the input WCS has, + the extra leading dimensions are assumed to represent multiple images with + the same coordinate information. The transformation is computed once and + "broadcast" across those images. + """ # Make sure image is floating point array = np.asarray(array, dtype=float) - # shape_out must be exact a tuple type + # shape_out must be exactly a tuple type shape_out = tuple(shape_out) - + # shape_out may be updated by _validate_wcs if the transformation is being + # broadcast + shape_out = _validate_wcs(wcs_in, wcs_out, array.shape, shape_out) _validate_array_out(array_out, array, shape_out) + if array_out is None: + array_out = np.empty(shape_out) + + array_out_loopable = array_out + if len(array.shape) == wcs_in.low_level_wcs.pixel_n_dim: + # We don't need to broadcast the transformation over any extra + # axes---add an extra axis of length one just so we have something + # to loop over in all cases. + array = array.reshape((1, *array.shape)) + array_out_loopable = array_out.reshape((1, *array_out.shape)) + elif len(array.shape) > wcs_in.low_level_wcs.pixel_n_dim: + # We're broadcasting. Flatten the extra dimensions so there's just one + # to loop over + array = array.reshape((-1, *array.shape[-wcs_in.low_level_wcs.pixel_n_dim :])) + array_out_loopable = array_out.reshape( + (-1, *array_out.shape[-wcs_out.low_level_wcs.pixel_n_dim :]) + ) + else: + raise ValueError("Too few dimensions for input array") + + wcs_dims = shape_out[-wcs_in.low_level_wcs.pixel_n_dim :] pixel_out = np.meshgrid( - *[np.arange(size, dtype=float) for size in shape_out], + *[np.arange(size, dtype=float) for size in wcs_dims], indexing="ij", sparse=False, copy=False, @@ -98,22 +140,21 @@ def _reproject_full( pixel_in = pixel_to_pixel(wcs_out, wcs_in, *pixel_out[::-1])[::-1] pixel_in = np.array(pixel_in) - if array_out is not None: - array_out.shape = (array_out.size,) - else: - array_out = np.empty(shape_out).ravel() - - # Interpolate array on to the pixels coordinates in pixel_in - map_coordinates( - array, - pixel_in, - order=order, - cval=np.nan, - mode="constant", - output=array_out, - ).reshape(shape_out) - - array_out.shape = shape_out + # Loop over the broadcasted dimensions in our array, re-using the same + # computed transformation each time + for i in range(len(array)): + # Interpolate array on to the pixels coordinates in pixel_in + map_coordinates( + array[i], + pixel_in, + order=order, + cval=np.nan, + mode="constant", + output=array_out_loopable[i].ravel(), + ) + + # n.b. We write the reprojected data into array_out_loopable, but array_out + # also contains this data and has the user's desired output shape. if return_footprint: return array_out, (~np.isnan(array_out)).astype(float) diff --git a/reproject/interpolation/high_level.py b/reproject/interpolation/high_level.py index 22494022b..1b303cc80 100644 --- a/reproject/interpolation/high_level.py +++ b/reproject/interpolation/high_level.py @@ -50,6 +50,12 @@ def reproject_interp( * An `~astropy.nddata.NDData` object from which the ``.data`` and ``.wcs`` attributes will be used as the input data. + If the data array contains more dimensions than are described by the + input header or WCS, the extra dimensions (assumed to be the first + dimensions) are taken to represent multiple images with the same + coordinate information. The coordinate transformation will be computed + once and then each image will be reprojected, offering a speedup over + reprojecting each image individually. output_projection : `~astropy.wcs.WCS` or `~astropy.io.fits.Header` The output projection, which can be either a `~astropy.wcs.WCS` or a `~astropy.io.fits.Header` instance. diff --git a/reproject/interpolation/tests/test_core.py b/reproject/interpolation/tests/test_core.py index 2e3fdafad..e9ff0e704 100644 --- a/reproject/interpolation/tests/test_core.py +++ b/reproject/interpolation/tests/test_core.py @@ -326,7 +326,7 @@ def test_naxis_mismatch(roundtrip_coords): wcs_out = WCS(naxis=2) with pytest.raises( - ValueError, match="Number of dimensions between input and output WCS should match" + ValueError, match="Number of dimensions in input and output WCS should match" ): array_out, footprint_out = reproject_interp( (data, wcs_in), wcs_out, shape_out=(1, 2), roundtrip_coords=roundtrip_coords @@ -382,14 +382,12 @@ def test_slice_reprojection(roundtrip_coords): np.testing.assert_allclose(out_cube, ((inp_cube[:-1] + inp_cube[1:]) / 2.0)) -@pytest.mark.parametrize("roundtrip_coords", (False, True)) -def test_4d_fails(roundtrip_coords): - +def test_too_few_dimens(): header_in = fits.Header.fromtextfile( get_pkg_data_filename("data/cube.hdr", package="reproject.tests") ) - header_in["NAXIS"] = 4 + header_in["NAXIS"] = 3 header_out = header_in.copy() w_in = WCS(header_in) @@ -397,12 +395,25 @@ def test_4d_fails(roundtrip_coords): array_in = np.zeros((2, 3, 4, 5)) + # Create mis-matches between the input and output shapes + with pytest.raises( - ValueError, match="Length of shape_out should match number of dimensions in wcs_out" + ValueError, match="Number of dimensions in input and output data should match" ): - x_out, y_out, z_out = reproject_interp( - (array_in, w_in), w_out, shape_out=[2, 4, 5, 6], roundtrip_coords=roundtrip_coords - ) + array_out, footprint = reproject_interp((array_in, w_in), w_out, shape_out=[2, 3, 4, 5, 6]) + + with pytest.raises( + ValueError, match="Number of dimensions in input and output data should match" + ): + array_out, footprint = reproject_interp((array_in[0], w_in), w_out, shape_out=[3, 4, 5, 6]) + + # Give fewer array dimensions than WCS dimensions + + with pytest.raises(ValueError, match="Too few dimensions in input data"): + array_out, footprint = reproject_interp((array_in[0, 0], w_in), w_out, shape_out=[4, 5, 6]) + + with pytest.raises(ValueError, match="Too few dimensions in shape_out"): + array_out, footprint = reproject_interp((array_in[0], w_in), w_out, shape_out=[5, 6]) @pytest.mark.parametrize("roundtrip_coords", (False, True)) @@ -629,6 +640,65 @@ def test_identity_with_offset(roundtrip_coords): assert_allclose(expected, array_out, atol=1e-10) +@pytest.mark.parametrize("input_extra_dims", (1, 2)) +@pytest.mark.parametrize("output_shape", (None, 'single', 'full')) +@pytest.mark.parametrize("input_as_wcs", (True, False)) +@pytest.mark.parametrize("output_as_wcs", (True, False)) +def test_broadcast_reprojection(input_extra_dims, output_shape, input_as_wcs, output_as_wcs): + with fits.open(get_pkg_data_filename("data/galactic_2d.fits", package="reproject.tests")) as pf: + hdu_in = pf[0] + header_in = hdu_in.header.copy() + header_out = hdu_in.header.copy() + header_out["CTYPE1"] = "RA---TAN" + header_out["CTYPE2"] = "DEC--TAN" + header_out["CRVAL1"] = 266.39311 + header_out["CRVAL2"] = -28.939779 + + data = hdu_in.data + + image_stack = np.stack((data, data.T, data[::-1], data[:, ::-1])) + + # Build the reference array through un-broadcast reprojections + array_indiv = np.empty_like(image_stack) + footprint_indiv = np.empty_like(image_stack) + for i in range(len(image_stack)): + array_out, footprint_out = reproject_interp( + (image_stack[i], header_in), header_out + ) + array_indiv[i] = array_out + footprint_indiv[i] = footprint_out + + # Test both single and multiple dimensions being broadcast + if input_extra_dims == 2: + image_stack = image_stack.reshape((2, 2, *data.shape)) + array_indiv.shape = image_stack.shape + footprint_indiv.shape = image_stack.shape + + # Test different ways of providing the output shape + if output_shape == "single": + # Have the broadcast dimensions be auto-added to the output shape + output_shape = image_stack.shape[-2:] + elif output_shape == "full": + # Provide the broadcast dimensions as part of the output shape + output_shape = image_stack.shape + + # Ensure logic works with WCS inputs as well as Header inputs + if input_as_wcs: + header_in = WCS(header_in) + if output_as_wcs: + header_out = WCS(header_out) + if output_shape is None: + # Shape must be provided in this case + output_shape = data.shape + + array_broadcast, footprint_broadcast = reproject_interp( + (image_stack, header_in), header_out, output_shape + ) + + np.testing.assert_array_equal(footprint_broadcast, footprint_indiv) + np.testing.assert_allclose(array_broadcast, array_indiv) + + @pytest.mark.parametrize("parallel", [True, 2, False]) @pytest.mark.parametrize("block_size", [[40, 40], [500, 500], [500, 100], None]) def test_blocked_against_single(parallel, block_size): From 18fee7884c33f9c9556b08a344b8725cbad803bb Mon Sep 17 00:00:00 2001 From: Sam Van Kooten Date: Wed, 4 Jan 2023 17:02:30 -0700 Subject: [PATCH 055/366] Support broadcasting with blocked reprojection As part of this, move the logic for updating shape_out to utils.py --- reproject/interpolation/core.py | 12 +--- reproject/interpolation/high_level.py | 6 +- reproject/interpolation/tests/test_core.py | 72 ++++++++++++++++------ reproject/utils.py | 37 +++++++---- 4 files changed, 81 insertions(+), 46 deletions(-) diff --git a/reproject/interpolation/core.py b/reproject/interpolation/core.py index 61fc9d8bb..63e2decd0 100644 --- a/reproject/interpolation/core.py +++ b/reproject/interpolation/core.py @@ -15,12 +15,7 @@ def _validate_wcs(wcs_in, wcs_out, shape_in, shape_out): raise ValueError("Too few dimensions in shape_out") elif len(shape_in) < wcs_in.low_level_wcs.pixel_n_dim: raise ValueError("Too few dimensions in input data") - - if len(shape_out) < len(shape_in) and len(shape_out) == wcs_out.low_level_wcs.pixel_n_dim: - # Add the broadcast dimensions to the output shape, which does not - # currently have any broadcast dims - shape_out = (*shape_in[:-len(shape_out)], *shape_out) - if len(shape_in) != len(shape_out): + elif len(shape_in) != len(shape_out): raise ValueError("Number of dimensions in input and output data should match") # Separate the "extra" dimensions that don't correspond to a WCS axis and @@ -50,7 +45,6 @@ def _validate_wcs(wcs_in, wcs_out, shape_in, shape_out): raise ValueError("Input WCS has a spectral component but output WCS does not") elif wcs_out.wcs.spec >= 0: raise ValueError("Output WCS has a spectral component but input WCS does not") - return shape_out def _validate_array_out(array_out, array, shape_out): @@ -100,9 +94,7 @@ def _reproject_full( array = np.asarray(array, dtype=float) # shape_out must be exactly a tuple type shape_out = tuple(shape_out) - # shape_out may be updated by _validate_wcs if the transformation is being - # broadcast - shape_out = _validate_wcs(wcs_in, wcs_out, array.shape, shape_out) + _validate_wcs(wcs_in, wcs_out, array.shape, shape_out) _validate_array_out(array_out, array, shape_out) if array_out is None: diff --git a/reproject/interpolation/high_level.py b/reproject/interpolation/high_level.py index 1b303cc80..a00daa01c 100644 --- a/reproject/interpolation/high_level.py +++ b/reproject/interpolation/high_level.py @@ -108,7 +108,7 @@ def reproject_interp( array_in, wcs_in = parse_input_data(input_data, hdu_in=hdu_in) wcs_out, shape_out = parse_output_projection( - output_projection, shape_out=shape_out, output_array=output_array + output_projection, shape_in=array_in.shape, shape_out=shape_out, output_array=output_array ) if isinstance(order, str): @@ -119,9 +119,9 @@ def reproject_interp( # if parallel is set but block size isn't, we'll choose # block size so each thread gets one block each if parallel is not False and block_size is None: - block_size = shape_out.copy() + block_size = list(shape_out) # each thread gets an equal sized strip of output area to process - block_size[0] = shape_out[0] // os.cpu_count() + block_size[-2] = shape_out[-2] // os.cpu_count() # given we have cases where modern system have many cpu cores some sanity clamping is # to avoid 0 length block sizes when num_cpu_cores is greater than the side of the image diff --git a/reproject/interpolation/tests/test_core.py b/reproject/interpolation/tests/test_core.py index e9ff0e704..705e1fd37 100644 --- a/reproject/interpolation/tests/test_core.py +++ b/reproject/interpolation/tests/test_core.py @@ -640,11 +640,7 @@ def test_identity_with_offset(roundtrip_coords): assert_allclose(expected, array_out, atol=1e-10) -@pytest.mark.parametrize("input_extra_dims", (1, 2)) -@pytest.mark.parametrize("output_shape", (None, 'single', 'full')) -@pytest.mark.parametrize("input_as_wcs", (True, False)) -@pytest.mark.parametrize("output_as_wcs", (True, False)) -def test_broadcast_reprojection(input_extra_dims, output_shape, input_as_wcs, output_as_wcs): +def _setup_for_broadcast_test(): with fits.open(get_pkg_data_filename("data/galactic_2d.fits", package="reproject.tests")) as pf: hdu_in = pf[0] header_in = hdu_in.header.copy() @@ -659,20 +655,27 @@ def test_broadcast_reprojection(input_extra_dims, output_shape, input_as_wcs, ou image_stack = np.stack((data, data.T, data[::-1], data[:, ::-1])) # Build the reference array through un-broadcast reprojections - array_indiv = np.empty_like(image_stack) - footprint_indiv = np.empty_like(image_stack) + array_ref = np.empty_like(image_stack) + footprint_ref = np.empty_like(image_stack) for i in range(len(image_stack)): - array_out, footprint_out = reproject_interp( - (image_stack[i], header_in), header_out - ) - array_indiv[i] = array_out - footprint_indiv[i] = footprint_out + array_out, footprint_out = reproject_interp((image_stack[i], header_in), header_out) + array_ref[i] = array_out + footprint_ref[i] = footprint_out + + return image_stack, array_ref, footprint_ref, header_in, header_out + +@pytest.mark.parametrize("input_extra_dims", (1, 2)) +@pytest.mark.parametrize("output_shape", (None, "single", "full")) +@pytest.mark.parametrize("input_as_wcs", (True, False)) +@pytest.mark.parametrize("output_as_wcs", (True, False)) +def test_broadcast_reprojection(input_extra_dims, output_shape, input_as_wcs, output_as_wcs): + image_stack, array_ref, footprint_ref, header_in, header_out = _setup_for_broadcast_test() # Test both single and multiple dimensions being broadcast if input_extra_dims == 2: - image_stack = image_stack.reshape((2, 2, *data.shape)) - array_indiv.shape = image_stack.shape - footprint_indiv.shape = image_stack.shape + image_stack = image_stack.reshape((2, 2, *image_stack.shape[-2:])) + array_ref.shape = image_stack.shape + footprint_ref.shape = image_stack.shape # Test different ways of providing the output shape if output_shape == "single": @@ -688,15 +691,44 @@ def test_broadcast_reprojection(input_extra_dims, output_shape, input_as_wcs, ou if output_as_wcs: header_out = WCS(header_out) if output_shape is None: - # Shape must be provided in this case - output_shape = data.shape + # This combination of parameter values is not valid + return + + array_broadcast, footprint_broadcast = reproject_interp( + (image_stack, header_in), + header_out, + output_shape, + ) + + np.testing.assert_array_equal(footprint_broadcast, footprint_ref) + np.testing.assert_allclose(array_broadcast, array_ref) + + +@pytest.mark.parametrize("input_extra_dims", (1, 2)) +@pytest.mark.parametrize("output_shape", (None, "single", "full")) +@pytest.mark.parametrize("parallel", [True, False]) +def test_blocked_broadcast_reprojection(input_extra_dims, output_shape, parallel): + image_stack, array_ref, footprint_ref, header_in, header_out = _setup_for_broadcast_test() + # Test both single and multiple dimensions being broadcast + if input_extra_dims == 2: + image_stack = image_stack.reshape((2, 2, *image_stack.shape[-2:])) + array_ref.shape = image_stack.shape + footprint_ref.shape = image_stack.shape + + # Test different ways of providing the output shape + if output_shape == "single": + # Have the broadcast dimensions be auto-added to the output shape + output_shape = image_stack.shape[-2:] + elif output_shape == "full": + # Provide the broadcast dimensions as part of the output shape + output_shape = image_stack.shape array_broadcast, footprint_broadcast = reproject_interp( - (image_stack, header_in), header_out, output_shape + (image_stack, header_in), header_out, output_shape, parallel=parallel, block_size=[5, 5] ) - np.testing.assert_array_equal(footprint_broadcast, footprint_indiv) - np.testing.assert_allclose(array_broadcast, array_indiv) + np.testing.assert_array_equal(footprint_broadcast, footprint_ref) + np.testing.assert_allclose(array_broadcast, array_ref) @pytest.mark.parametrize("parallel", [True, 2, False]) diff --git a/reproject/utils.py b/reproject/utils.py index a5af74ec7..044058a44 100644 --- a/reproject/utils.py +++ b/reproject/utils.py @@ -113,7 +113,7 @@ def parse_input_weights(input_weights, hdu_weights=None): raise TypeError("input_weights should either be an HDU object or a Numpy array") -def parse_output_projection(output_projection, shape_out=None, output_array=None): +def parse_output_projection(output_projection, shape_in=None, shape_out=None, output_array=None): if shape_out is None: if output_array is not None: @@ -151,6 +151,15 @@ def parse_output_projection(output_projection, shape_out=None, output_array=None if len(shape_out) == 0: raise ValueError("The shape of the output image should not be an empty tuple") + + if ( + shape_in is not None + and len(shape_out) < len(shape_in) + and len(shape_out) == wcs_out.low_level_wcs.pixel_n_dim + ): + # Add the broadcast dimensions to the output shape, which does not + # currently have any broadcast dims + shape_out = (*shape_in[: -len(shape_out)], *shape_out) return wcs_out, shape_out @@ -272,17 +281,17 @@ def reproject_blocked( # WCS and either processing and reinserting them immediately, # or when doing parallel impl submit them to workers then wait and reinsert as # the workers complete each block - for imin in range(0, output_array.shape[0], block_size[0]): - imax = min(imin + block_size[0], output_array.shape[0]) - for jmin in range(0, output_array.shape[1], block_size[1]): - jmax = min(jmin + block_size[1], output_array.shape[1]) + for imin in range(0, output_array.shape[-2], block_size[0]): + imax = min(imin + block_size[0], output_array.shape[-2]) + for jmin in range(0, output_array.shape[-1], block_size[1]): + jmax = min(jmin + block_size[1], output_array.shape[-1]) shape_out_sub = (imax - imin, jmax - jmin) - # if the output has more than two dims, just append them on the end of the - # shape to it still matches the base WCS - for dim in range(2, len(output_array.shape)): - shape_out_sub = shape_out_sub + (output_array.shape[dim],) + # if the output has more than two dims, apply our blocking to only the last two + shape_out_sub = output_array.shape[:-2] + shape_out_sub slices = [slice(imin, imax), slice(jmin, jmax)] + if wcs_out.low_level_wcs.pixel_n_dim > 2: + slices = [Ellipsis] + slices wcs_out_sub = HighLevelWCSWrapper(SlicedLowLevelWCS(wcs_out, slices=slices)) if proc_pool is None: @@ -298,9 +307,9 @@ def reproject_blocked( i_range=(imin, imax), ) - output_array[imin:imax, jmin:jmax] = completed_block["res_arr"][:] + output_array[..., imin:imax, jmin:jmax] = completed_block["res_arr"][:] if return_footprint: - output_footprint[imin:imax, jmin:jmax] = completed_block["res_fp"][:] + output_footprint[..., imin:imax, jmin:jmax] = completed_block["res_fp"][:] else: # if parallel just submit all work items and move on to waiting for them to be done @@ -326,13 +335,15 @@ def reproject_blocked( completed_block = completed_future.result() i_range = completed_block["i"] j_range = completed_block["j"] - output_array[i_range[0] : i_range[1], j_range[0] : j_range[1]] = completed_block[ + output_array[..., i_range[0] : i_range[1], j_range[0] : j_range[1]] = completed_block[ "res_arr" ][:] if return_footprint: footprint_block = completed_block["res_fp"][:] - output_footprint[i_range[0] : i_range[1], j_range[0] : j_range[1]] = footprint_block + output_footprint[ + ..., i_range[0] : i_range[1], j_range[0] : j_range[1] + ] = footprint_block completed_future_count += 1 idx = blocks_futures.index(completed_future) From 6910db21b264bbc1fa1b6324f7f512a69a6066e8 Mon Sep 17 00:00:00 2001 From: Sam Van Kooten Date: Tue, 3 Jan 2023 17:39:10 -0700 Subject: [PATCH 056/366] Support broadcasting in spherical_intersect algo --- reproject/spherical_intersect/core.py | 123 +++++++++++++----- reproject/spherical_intersect/high_level.py | 10 +- .../tests/test_high_level.py | 91 +++++++++++++ 3 files changed, 189 insertions(+), 35 deletions(-) diff --git a/reproject/spherical_intersect/core.py b/reproject/spherical_intersect/core.py index d8accc9fc..6068599ae 100644 --- a/reproject/spherical_intersect/core.py +++ b/reproject/spherical_intersect/core.py @@ -55,10 +55,24 @@ def _reproject_celestial(array, wcs_in, wcs_out, shape_out, parallel=True, retur # Convert input array to float values. If this comes from a FITS, it might have # float32 as value type and that can break things in Cython array = np.asarray(array, dtype=float) + shape_out = tuple(shape_out) - # TODO: make this work for n-dimensional arrays if wcs_in.pixel_n_dim != 2: + # TODO: make this work for n-dimensional arrays raise NotImplementedError("Only 2-dimensional arrays can be reprojected at this time") + elif len(shape_out) < wcs_out.low_level_wcs.pixel_n_dim: + raise ValueError("Too few dimensions in shape_out") + elif len(array.shape) < wcs_in.low_level_wcs.pixel_n_dim: + raise ValueError("Too few dimensions in input data") + elif len(array.shape) != len(shape_out): + raise ValueError("Number of dimensions in input and output data should match") + + # Separate the "extra" dimensions that don't correspond to a WCS axis and + # which we'll be looping over + extra_dimens_in = array.shape[: -wcs_in.low_level_wcs.pixel_n_dim] + extra_dimens_out = shape_out[: -wcs_out.low_level_wcs.pixel_n_dim] + if extra_dimens_in != extra_dimens_out: + raise ValueError("Dimensions to be looped over must match exactly") # TODO: at the moment, we compute the coordinates of all of the corners, # but we might want to do it in steps for large images. @@ -66,7 +80,7 @@ def _reproject_celestial(array, wcs_in, wcs_out, shape_out, parallel=True, retur # Start off by finding the world position of all the corners of the input # image in world coordinates - ny_in, nx_in = array.shape + ny_in, nx_in = array.shape[-2:] x = np.arange(nx_in + 1.0) - 0.5 y = np.arange(ny_in + 1.0) - 0.5 @@ -77,7 +91,7 @@ def _reproject_celestial(array, wcs_in, wcs_out, shape_out, parallel=True, retur # Now compute the world positions of all the corners in the output header - ny_out, nx_out = shape_out + ny_out, nx_out = shape_out[-2:] x = np.arange(nx_out + 1.0) - 0.5 y = np.arange(ny_out + 1.0) - 0.5 @@ -102,6 +116,22 @@ def _reproject_celestial(array, wcs_in, wcs_out, shape_out, parallel=True, retur world_out_unitsph = world_out.represent_as("unitspherical") xw_out, yw_out = world_out_unitsph.lon.to_value(u.deg), world_out_unitsph.lat.to_value(u.deg) + # If the input array contains extra dimensions beyond what the input WCS + # has, the extra leading dimensions are assumed to represent multiple + # images with the same coordinate information. The transformation is + # computed once and "broadcast" across those images. + if len(array.shape) == wcs_in.low_level_wcs.pixel_n_dim: + # We don't need to broadcast the transformation over any extra + # axes---add an extra axis of length one just so we have something + # to loop over in all cases. + array = array.reshape((1, *array.shape)) + elif len(array.shape) > wcs_in.low_level_wcs.pixel_n_dim: + # We're broadcasting. Flatten the extra dimensions so there's just one + # to loop over + array = array.reshape((-1, *array.shape[-wcs_in.low_level_wcs.pixel_n_dim :])) + else: + raise ValueError("Too few dimensions for input array") + # Put together the parameters common both to the serial and parallel implementations. The aca # function is needed to enforce that the array will be contiguous when passed to the low-level # raw C function, otherwise Cython might complain. @@ -118,24 +148,13 @@ def _reproject_celestial(array, wcs_in, wcs_out, shape_out, parallel=True, retur aca(yw_in), aca(xw_out), aca(yw_out), - aca(array), - shape_out, + None, # input data + shape_out[-2:], ] + array = aca(array) - if nproc == 1: - - array_new, weights = _reproject_slice([0, nx_in] + common_func_par) - - with np.errstate(invalid="ignore"): - array_new /= weights - - if return_footprint: - return array_new, weights - else: - return array_new - - elif nproc is None or nproc > 1: - + if nproc is None or nproc > 1: + # Spin up our process pool outside the loop over broadcast dimensions from multiprocessing import Pool, cpu_count # If needed, establish the number of processors to use. @@ -146,25 +165,61 @@ def _reproject_celestial(array, wcs_in, wcs_out, shape_out, parallel=True, retur # the ctrl+c signal in the child process. pool = Pool(nproc, _init_worker) - inputs = [] - for i in range(nproc): - start = int(nx_in) // nproc * i - end = int(nx_in) if i == nproc - 1 else int(nx_in) // nproc * (i + 1) - inputs.append([start, end] + common_func_par) + outputs = [] + output_weights = [] + for i in range(len(array)): + common_func_par[-2] = array[i] - results = pool.map(_reproject_slice, inputs) + if nproc == 1: - pool.close() + array_new, weights = _reproject_slice([0, nx_in] + common_func_par) + + with np.errstate(invalid="ignore"): + array_new /= weights + + outputs.append(array_new) + if return_footprint: + output_weights.append(weights) - array_new, weights = zip(*results) + elif nproc > 1: - array_new = sum(array_new) - weights = sum(weights) + inputs = [] + for i in range(nproc): + start = int(nx_in) // nproc * i + end = int(nx_in) if i == nproc - 1 else int(nx_in) // nproc * (i + 1) + inputs.append([start, end] + common_func_par) - with np.errstate(invalid="ignore"): - array_new /= weights + results = pool.map(_reproject_slice, inputs) + array_new, weights = zip(*results) + + array_new = sum(array_new) + weights = sum(weights) + + with np.errstate(invalid="ignore"): + array_new /= weights + + outputs.append(array_new) + if return_footprint: + output_weights.append(weights) + + if nproc > 1: + pool.close() + + if len(shape_out) == wcs_out.low_level_wcs.pixel_n_dim: + # We weren't broadcasting, so don't return any extra dimensions + outputs = outputs[0] + if return_footprint: + output_weights = output_weights[0] + else: + outputs = np.stack(outputs) + # If we're broadcasting over multiple dimensions, impose them here + outputs.shape = shape_out if return_footprint: - return array_new, weights - else: - return array_new + output_weights = np.stack(output_weights) + output_weights.shape = shape_out + + if return_footprint: + return outputs, output_weights + else: + return outputs diff --git a/reproject/spherical_intersect/high_level.py b/reproject/spherical_intersect/high_level.py index 0b1fbb650..17e09798e 100644 --- a/reproject/spherical_intersect/high_level.py +++ b/reproject/spherical_intersect/high_level.py @@ -30,6 +30,12 @@ def reproject_exact( * An `~astropy.nddata.NDData` object from which the ``.data`` and ``.wcs`` attributes will be used as the input data. + If the data array contains more dimensions than are described by the + input header or WCS, the extra dimensions (assumed to be the first + dimensions) are taken to represent multiple images with the same + coordinate information. The coordinate transformation will be computed + once and then each image will be reprojected, offering a speedup over + reprojecting each image individually. output_projection : `~astropy.wcs.WCS` or `~astropy.io.fits.Header` The output projection, which can be either a `~astropy.wcs.WCS` or a `~astropy.io.fits.Header` instance. @@ -59,7 +65,9 @@ def reproject_exact( """ array_in, wcs_in = parse_input_data(input_data, hdu_in=hdu_in) - wcs_out, shape_out = parse_output_projection(output_projection, shape_out=shape_out) + wcs_out, shape_out = parse_output_projection( + output_projection, shape_in=array_in.shape, shape_out=shape_out + ) if has_celestial(wcs_in) and wcs_in.pixel_n_dim == 2 and wcs_in.world_n_dim == 2: return _reproject_celestial( diff --git a/reproject/spherical_intersect/tests/test_high_level.py b/reproject/spherical_intersect/tests/test_high_level.py index ad92a47d4..db6d9b153 100644 --- a/reproject/spherical_intersect/tests/test_high_level.py +++ b/reproject/spherical_intersect/tests/test_high_level.py @@ -91,3 +91,94 @@ def test_reproject_precision_warning(): with warnings.catch_warnings(record=True) as w: reproject_exact((array, wcs1), wcs2, shape_out=(5, 5)) assert len(w) == 0 + + +def _setup_for_broadcast_test(): + with fits.open(get_pkg_data_filename("data/galactic_2d.fits", package="reproject.tests")) as pf: + hdu_in = pf[0] + header_in = hdu_in.header.copy() + header_out = hdu_in.header.copy() + header_out["CTYPE1"] = "RA---TAN" + header_out["CTYPE2"] = "DEC--TAN" + header_out["CRVAL1"] = 266.39311 + header_out["CRVAL2"] = -28.939779 + + data = hdu_in.data + + image_stack = np.stack((data, data.T, data[::-1], data[:, ::-1])) + + # Build the reference array through un-broadcast reprojections + array_ref = [] + footprint_ref = [] + for i in range(len(image_stack)): + array_out, footprint_out = reproject_exact((image_stack[i], header_in), header_out) + array_ref.append(array_out) + footprint_ref.append(footprint_out) + array_ref = np.stack(array_ref) + footprint_ref = np.stack(footprint_ref) + + return image_stack, array_ref, footprint_ref, header_in, header_out + + +@pytest.mark.parametrize("input_extra_dims", (1, 2)) +@pytest.mark.parametrize("output_shape", (None, "single", "full")) +@pytest.mark.parametrize("input_as_wcs", (True, False)) +@pytest.mark.parametrize("output_as_wcs", (True, False)) +def test_broadcast_reprojection(input_extra_dims, output_shape, input_as_wcs, output_as_wcs): + image_stack, array_ref, footprint_ref, header_in, header_out = _setup_for_broadcast_test() + # Test both single and multiple dimensions being broadcast + if input_extra_dims == 2: + image_stack = image_stack.reshape((2, 2, *image_stack.shape[-2:])) + array_ref.shape = image_stack.shape + footprint_ref.shape = image_stack.shape + + # Test different ways of providing the output shape + if output_shape == "single": + # Have the broadcast dimensions be auto-added to the output shape + output_shape = image_stack.shape[-2:] + elif output_shape == "full": + # Provide the broadcast dimensions as part of the output shape + output_shape = image_stack.shape + + # Ensure logic works with WCS inputs as well as Header inputs + if input_as_wcs: + header_in = WCS(header_in) + if output_as_wcs: + header_out = WCS(header_out) + if output_shape is None: + # This combination of parameter values is not valid + return + + array_broadcast, footprint_broadcast = reproject_exact( + (image_stack, header_in), header_out, output_shape + ) + + np.testing.assert_allclose(footprint_broadcast, footprint_ref) + np.testing.assert_allclose(array_broadcast, array_ref) + + +@pytest.mark.parametrize("input_extra_dims", (1, 2)) +@pytest.mark.parametrize("output_shape", (None, "single", "full")) +@pytest.mark.parametrize("parallel", (2, False)) +def test_broadcast_parallel_reprojection(input_extra_dims, output_shape, parallel): + image_stack, array_ref, footprint_ref, header_in, header_out = _setup_for_broadcast_test() + # Test both single and multiple dimensions being broadcast + if input_extra_dims == 2: + image_stack = image_stack.reshape((2, 2, *image_stack.shape[-2:])) + array_ref.shape = image_stack.shape + footprint_ref.shape = image_stack.shape + + # Test different ways of providing the output shape + if output_shape == "single": + # Have the broadcast dimensions be auto-added to the output shape + output_shape = image_stack.shape[-2:] + elif output_shape == "full": + # Provide the broadcast dimensions as part of the output shape + output_shape = image_stack.shape + + array_broadcast, footprint_broadcast = reproject_exact( + (image_stack, header_in), header_out, output_shape, parallel=parallel + ) + + np.testing.assert_allclose(footprint_broadcast, footprint_ref) + np.testing.assert_allclose(array_broadcast, array_ref) From 958b00bf15ca5d05bf19dfc56294a187af880c20 Mon Sep 17 00:00:00 2001 From: Sam Van Kooten Date: Wed, 4 Jan 2023 15:56:41 -0700 Subject: [PATCH 057/366] Support broadcasting in adaptive algo --- reproject/adaptive/core.py | 37 +++++++- reproject/adaptive/deforest.pyx | 127 ++++++++++++++------------ reproject/adaptive/high_level.py | 10 +- reproject/adaptive/tests/test_core.py | 125 +++++++++++++++++++++++++ 4 files changed, 240 insertions(+), 59 deletions(-) diff --git a/reproject/adaptive/core.py b/reproject/adaptive/core.py index 57661748b..3566d07a1 100644 --- a/reproject/adaptive/core.py +++ b/reproject/adaptive/core.py @@ -104,20 +104,51 @@ def _reproject_adaptive_2d( to numerical issues, causing a band of NaN values around the edge of the final image (when reprojecting from helioprojective to heliographic). See https://github.com/astropy/reproject/issues/195 for more information. + + Note + ---- + If the input array contains extra dimensions beyond what the input WCS has, + the extra leading dimensions are assumed to represent multiple images with + the same coordinate information. The transformation is computed once and + "broadcast" across those images. """ # Make sure image is floating point array_in = np.asarray(array, dtype=float) + shape_out = tuple(shape_out) # Check dimensionality of WCS and shape_out if wcs_in.low_level_wcs.pixel_n_dim != wcs_out.low_level_wcs.pixel_n_dim: raise ValueError("Number of dimensions between input and output WCS should match") - elif len(shape_out) != wcs_out.low_level_wcs.pixel_n_dim: - raise ValueError("Length of shape_out should match number of dimensions in wcs_out") + elif len(shape_out) < wcs_out.low_level_wcs.pixel_n_dim: + raise ValueError("Length of shape_out too small for number of dimensions in wcs_out") + elif len(array_in.shape) < wcs_in.low_level_wcs.pixel_n_dim: + raise ValueError("Too few input-data dimensions for number of dimensions in wcs_out") + elif len(array_in.shape) != len(shape_out): + raise ValueError("Number of dimensions in input and output data should match") + + # Separate the "extra" dimensions that don't correspond to a WCS axis and + # which we'll be looping over + extra_dimens_in = array_in.shape[: -wcs_in.low_level_wcs.pixel_n_dim] + extra_dimens_out = shape_out[: -wcs_out.low_level_wcs.pixel_n_dim] + if extra_dimens_in != extra_dimens_out: + raise ValueError("Dimensions to be looped over must match exactly") # Create output array array_out = np.zeros(shape_out) + if len(array_in.shape) == wcs_in.low_level_wcs.pixel_n_dim: + # We don't need to broadcast the transformation over any extra + # axes---add an extra axis of length one just so we have something + # to loop over in all cases. + array_in = array_in.reshape((1, *array_in.shape)) + array_out = array_out.reshape((1, *array_out.shape)) + elif len(array_in.shape) > wcs_in.low_level_wcs.pixel_n_dim: + # We're broadcasting. Flatten the extra dimensions so there's just one + # to loop over + array_in = array_in.reshape((-1, *array_in.shape[-wcs_in.low_level_wcs.pixel_n_dim :])) + array_out = array_out.reshape((-1, *array_out.shape[-wcs_out.low_level_wcs.pixel_n_dim :])) + transformer = CoordinateTransformer(wcs_in, wcs_out, roundtrip_coords) map_coordinates( array_in, @@ -136,6 +167,8 @@ def _reproject_adaptive_2d( y_cyclic=y_cyclic, ) + array_out.shape = shape_out + if return_footprint: return array_out, (~np.isnan(array_out)).astype(float) else: diff --git a/reproject/adaptive/deforest.pyx b/reproject/adaptive/deforest.pyx index 38599070a..076a57acd 100644 --- a/reproject/adaptive/deforest.pyx +++ b/reproject/adaptive/deforest.pyx @@ -156,15 +156,19 @@ cdef double clip(double x, double vmin, double vmax, int cyclic, @cython.wraparound(False) @cython.nonecheck(False) @cython.cdivision(True) -cdef (double, bint) sample_array(double[:,:] source, double x, double y, - int x_cyclic, int y_cyclic, bint out_of_range_nearest) nogil: - x = clip(x, 0, source.shape[1] - 1, x_cyclic, out_of_range_nearest) - y = clip(y, 0, source.shape[0] - 1, y_cyclic, out_of_range_nearest) +cdef bint sample_array(double[:,:,:] source, double[:] dest, + double x, double y, int x_cyclic, int y_cyclic, + bint out_of_range_nearest) nogil: + x = clip(x, 0, source.shape[2] - 1, x_cyclic, out_of_range_nearest) + y = clip(y, 0, source.shape[1] - 1, y_cyclic, out_of_range_nearest) if isnan(x) or isnan(y): - return nan, False + return False - return source[ y, x], True + # Cython doesn't like a return type of (double[:], bint), so we put the + # input data into the provided output array + dest[:] = source[:, y, x] + return True KERNELS = {} @@ -185,12 +189,18 @@ BOUNDARY_MODES['nearest'] = 6 @cython.wraparound(False) @cython.nonecheck(False) @cython.cdivision(True) -def map_coordinates(double[:,:] source, double[:,:] target, Ci, int max_samples_width=-1, +def map_coordinates(double[:,:,:] source, double[:,:,:] target, Ci, int max_samples_width=-1, int conserve_flux=False, int progress=False, int singularities_nan=False, int x_cyclic=False, int y_cyclic=False, int out_of_range_nan=False, bint center_jacobian=False, str kernel='gaussian', double kernel_width=1.3, double sample_region_width=4, str boundary_mode="strict", double boundary_fill_value=0, double boundary_ignore_threshold=0.5): + # n.b. the source and target arrays are expected to contain three + # dimensions---the last two are the image dimensions, while the first + # indexes multiple images with the same coordinates. The transformation is + # computed once, and then each image is reprojected using that + # transformation. For the single-image case, the first dimension is still + # required and will have size 1. cdef int kernel_flag try: kernel_flag = KERNELS[kernel.lower()] @@ -207,12 +217,12 @@ def map_coordinates(double[:,:] source, double[:,:] target, Ci, int max_samples_ cdef np.ndarray[np.float64_t, ndim=3] pixel_target cdef int delta if center_jacobian: - pixel_target = np.zeros((target.shape[0], target.shape[1], 2)) + pixel_target = np.zeros((target.shape[1], target.shape[2], 2)) delta = 0 else: # Pad by one on all four sides of the array, so we can interpolate # Jacobian values from both directions at all points. - pixel_target = np.zeros((target.shape[0]+2, target.shape[1]+2, 2)) + pixel_target = np.zeros((target.shape[1]+2, target.shape[2]+2, 2)) # With this delta set, the value of pixel_target at (0,0) will really # be representing (-1,-1) in the output image. delta = -1 @@ -229,19 +239,19 @@ def map_coordinates(double[:,:] source, double[:,:] target, Ci, int max_samples_ # Prepare arrays marking coordinates offset by a half pixel, to allow # for calculating centered Jacobians for each output pixel by using the # corresponding input coordinate at locations offset by +/- 0.5 pixels. - offset_target_x = np.zeros((target.shape[0], target.shape[1]+1, 2)) - offset_target_y = np.zeros((target.shape[0]+1, target.shape[1], 2)) - for yi in range(target.shape[0]): - for xi in range(target.shape[1]): + offset_target_x = np.zeros((target.shape[1], target.shape[2]+1, 2)) + offset_target_y = np.zeros((target.shape[1]+1, target.shape[2], 2)) + for yi in range(target.shape[1]): + for xi in range(target.shape[2]): offset_target_x[yi,xi,0] = xi - 0.5 offset_target_x[yi,xi,1] = yi offset_target_y[yi,xi,0] = xi offset_target_y[yi,xi,1] = yi - 0.5 - offset_target_x[yi,target.shape[1],0] = target.shape[1]-1 + 0.5 - offset_target_x[yi,target.shape[1],1] = yi - for xi in range(target.shape[1]): - offset_target_y[target.shape[0],xi,0] = xi - offset_target_y[target.shape[0],xi,1] = target.shape[0]-1 + 0.5 + offset_target_x[yi,target.shape[2],0] = target.shape[2]-1 + 0.5 + offset_target_x[yi,target.shape[2],1] = yi + for xi in range(target.shape[2]): + offset_target_y[target.shape[1],xi,0] = xi + offset_target_y[target.shape[1],xi,1] = target.shape[1]-1 + 0.5 # These source arrays store a corresponding input-image coordinate for each # pixel in the output image. @@ -261,19 +271,19 @@ def map_coordinates(double[:,:] source, double[:,:] target, Ci, int max_samples_ # d(input coordinate)/d(output x) at (x=-.5, y=0) in the output image, # and Jy at [0, 0, :] representing d(input coordinate)/d(output y) at # (x=0,y=-.5). - Jx = np.empty((target.shape[0], target.shape[1] + 1, 2)) - Jy = np.empty((target.shape[0] + 1, target.shape[1], 2)) - for yi in range(target.shape[0]): - for xi in range(target.shape[1]): + Jx = np.empty((target.shape[1], target.shape[2] + 1, 2)) + Jy = np.empty((target.shape[1] + 1, target.shape[2], 2)) + for yi in range(target.shape[1]): + for xi in range(target.shape[2]): Jx[yi, xi, 0] = -pixel_source[yi+1, xi, 0] + pixel_source[yi+1, xi+1, 0] Jx[yi, xi, 1] = -pixel_source[yi+1, xi, 1] + pixel_source[yi+1, xi+1, 1] Jy[yi, xi, 0] = -pixel_source[yi, xi+1, 0] + pixel_source[yi+1, xi+1, 0] Jy[yi, xi, 1] = -pixel_source[yi, xi+1, 1] + pixel_source[yi+1, xi+1, 1] - xi = target.shape[1] + xi = target.shape[2] Jx[yi, xi, 0] = -pixel_source[yi+1, xi, 0] + pixel_source[yi+1, xi+1, 0] Jx[yi, xi, 1] = -pixel_source[yi+1, xi, 1] + pixel_source[yi+1, xi+1, 1] - yi = target.shape[0] - for xi in range(target.shape[1]): + yi = target.shape[1] + for xi in range(target.shape[2]): Jy[yi, xi, 0] = -pixel_source[yi, xi+1, 0] + pixel_source[yi+1, xi+1, 0] Jy[yi, xi, 1] = -pixel_source[yi, xi+1, 1] + pixel_source[yi+1, xi+1, 1] @@ -296,18 +306,19 @@ def map_coordinates(double[:,:] source, double[:,:] target, Ci, int max_samples_ cdef double weight_sum cdef double ignored_weight_sum cdef double weight - cdef double value + cdef double[:] value = np.empty(source.shape[0]) cdef double[:] P1 = np.empty((2,)) cdef double[:] P2 = np.empty((2,)) cdef double[:] P3 = np.empty((2,)) cdef double[:] P4 = np.empty((2,)) cdef double top, bottom, left, right + cdef double determinant cdef bint has_sampled_this_row cdef bint is_good_sample with nogil: # Iterate through each pixel in the output image. - for yi in range(target.shape[0]): - for xi in range(target.shape[1]): + for yi in range(target.shape[1]): + for xi in range(target.shape[2]): if center_jacobian: # Compute the Jacobian for the transformation applied to # this pixel, as finite differences. @@ -324,7 +335,7 @@ def map_coordinates(double[:,:] source, double[:,:] target, Ci, int max_samples_ Ji[0,1] = (Jy[yi, xi, 0] + Jy[yi+1, xi, 0]) / 2 Ji[1,1] = (Jy[yi, xi, 1] + Jy[yi+1, xi, 1]) / 2 if isnan(Ji[0,0]) or isnan(Ji[0,1]) or isnan(Ji[1,0]) or isnan(Ji[1,1]) or isnan(pixel_source[yi,xi,0]) or isnan(pixel_source[yi,xi,1]): - target[yi,xi] = nan + target[:,yi,xi] = nan continue # Find and pad the singular values of the Jacobian. @@ -395,18 +406,18 @@ def map_coordinates(double[:,:] source, double[:,:] target, Ci, int max_samples_ if max_samples_width > 0 and max(right-left, top-bottom) > max_samples_width: if singularities_nan: - target[yi,xi] = nan + target[:,yi,xi] = nan else: - value, is_good_sample = sample_array( - source, current_pixel_source[0], + is_good_sample = sample_array( + source, value, current_pixel_source[0], current_pixel_source[1], x_cyclic, y_cyclic, out_of_range_nearest=boundary_flag == 6) if is_good_sample: - target[yi,xi] = value + target[:,yi,xi] = value elif boundary_flag == 2 or boundary_flag == 3: - target[yi,xi] = boundary_fill_value + target[:,yi,xi] = boundary_fill_value else: - target[yi,xi] = nan + target[:,yi,xi] = nan continue top += pixel_source[yi,xi,1] @@ -427,28 +438,28 @@ def map_coordinates(double[:,:] source, double[:,:] target, Ci, int max_samples_ # all other boundary modes, we still need to calculate weights # for each out-of-bounds sample, so we do nothing here. if not x_cyclic: - if right > source.shape[1] - 1: + if right > source.shape[2] - 1: if boundary_flag == 1: - target[yi,xi] = nan + target[:,yi,xi] = nan continue if boundary_flag == 4: - right = source.shape[1] - 1 + right = source.shape[2] - 1 if left < 0: if boundary_flag == 1: - target[yi,xi] = nan + target[:,yi,xi] = nan continue if boundary_flag == 4: left = 0 if not y_cyclic: - if top > source.shape[0] - 1: + if top > source.shape[1] - 1: if boundary_flag == 1: - target[yi,xi] = nan + target[:,yi,xi] = nan continue if boundary_flag == 4: - top = source.shape[0] - 1 + top = source.shape[1] - 1 if bottom < 0: if boundary_flag == 1: - target[yi,xi] = nan + target[:,yi,xi] = nan continue if boundary_flag == 4: bottom = 0 @@ -460,16 +471,16 @@ def map_coordinates(double[:,:] source, double[:,:] target, Ci, int max_samples_ # sampling region can become very large when well outside the # input image, and so this detection becomes an important # optimization. - if (not x_cyclic and (right < 0 or left > source.shape[1] - 1) + if (not x_cyclic and (right < 0 or left > source.shape[2] - 1) or not y_cyclic - and (top < 0 or bottom > source.shape[0] - 1)): + and (top < 0 or bottom > source.shape[1] - 1)): if boundary_flag == 3: - target[yi,xi] = boundary_fill_value + target[:,yi,xi] = boundary_fill_value continue if (boundary_flag == 2 or boundary_flag == 4 or boundary_flag == 5): - target[yi,xi] = nan + target[:,yi,xi] = nan continue if boundary_flag == 6: # Just sample one row or column so that we get all of @@ -481,7 +492,7 @@ def map_coordinates(double[:,:] source, double[:,:] target, Ci, int max_samples_ if top < bottom: top = bottom - target[yi,xi] = 0 + target[:,yi,xi] = 0 weight_sum = 0 ignored_weight_sum = 0 @@ -529,18 +540,19 @@ def map_coordinates(double[:,:] source, double[:,:] target, Ci, int max_samples_ continue has_sampled_this_row = True - value, is_good_sample = sample_array( - source, current_pixel_source[0], + is_good_sample = sample_array( + source, value, current_pixel_source[0], current_pixel_source[1], x_cyclic, y_cyclic, out_of_range_nearest=(boundary_flag == 6)) if ((boundary_flag == 2 or boundary_flag == 3) and not is_good_sample): - value = boundary_fill_value + value[:] = boundary_fill_value is_good_sample = True if is_good_sample: - target[yi,xi] += weight * value + for i in range(target.shape[0]): + target[i,yi,xi] += weight * value[i] weight_sum += weight else: if boundary_flag == 5: @@ -549,14 +561,17 @@ def map_coordinates(double[:,:] source, double[:,:] target, Ci, int max_samples_ if (boundary_flag == 5 and ignored_weight_sum / (ignored_weight_sum + weight_sum) > boundary_ignore_threshold): - target[yi,xi] = nan + target[:,yi,xi] = nan else: - target[yi,xi] /= weight_sum if conserve_flux: - target[yi,xi] *= fabs(det2x2(Ji)) + determinant = fabs(det2x2(Ji)) + for i in range(target.shape[0]): + target[i,yi,xi] /= weight_sum + if conserve_flux: + target[i,yi,xi] *= determinant if progress: with gil: - sys.stdout.write("\r%d/%d done" % (yi+1, target.shape[0])) + sys.stdout.write("\r%d/%d done" % (yi+1, target.shape[1])) sys.stdout.flush() if progress: sys.stdout.write("\n") diff --git a/reproject/adaptive/high_level.py b/reproject/adaptive/high_level.py index 58f75c68c..7470805ec 100644 --- a/reproject/adaptive/high_level.py +++ b/reproject/adaptive/high_level.py @@ -47,6 +47,12 @@ def reproject_adaptive( * An `~astropy.nddata.NDData` object from which the ``.data`` and ``.wcs`` attributes will be used as the input data. + If the data array contains more dimensions than are described by the + input header or WCS, the extra dimensions (assumed to be the first + dimensions) are taken to represent multiple images with the same + coordinate information. The coordinate transformation will be computed + once and then each image will be reprojected, offering a speedup over + reprojecting each image individually. output_projection : `~astropy.wcs.WCS` or `~astropy.io.fits.Header` The output projection, which can be either a `~astropy.wcs.WCS` or a `~astropy.io.fits.Header` instance. @@ -153,7 +159,9 @@ def reproject_adaptive( # TODO: add support for output_array array_in, wcs_in = parse_input_data(input_data, hdu_in=hdu_in) - wcs_out, shape_out = parse_output_projection(output_projection, shape_out=shape_out) + wcs_out, shape_out = parse_output_projection( + output_projection, shape_in=array_in.shape, shape_out=shape_out + ) return _reproject_adaptive_2d( array_in, diff --git a/reproject/adaptive/tests/test_core.py b/reproject/adaptive/tests/test_core.py index 7898d95db..06de9ac8f 100644 --- a/reproject/adaptive/tests/test_core.py +++ b/reproject/adaptive/tests/test_core.py @@ -6,6 +6,8 @@ import numpy as np import pytest from astropy import units as u +from astropy.io import fits +from astropy.utils.data import get_pkg_data_filename from astropy.wcs import WCS from astropy.wcs.wcsapi import HighLevelWCSWrapper, SlicedLowLevelWCS from numpy.testing import assert_allclose @@ -737,3 +739,126 @@ def test_reproject_adaptive_uncentered_jacobian(): header_out["DATE-OBS"] = header_out["DATE-OBS"].replace("T", " ") return array_footprint_to_hdulist(output, footprint, header_out) + + +def _setup_for_broadcast_test( + conserve_flux=False, boundary_mode="strict", kernel="gaussian", center_jacobian=False +): + with fits.open(get_pkg_data_filename("data/galactic_2d.fits", package="reproject.tests")) as pf: + hdu_in = pf[0] + header_in = hdu_in.header.copy() + header_out = hdu_in.header.copy() + header_out["CTYPE1"] = "RA---TAN" + header_out["CTYPE2"] = "DEC--TAN" + header_out["CRVAL1"] = 266.39311 + header_out["CRVAL2"] = -28.939779 + + data = hdu_in.data + + image_stack = np.stack((data, data.T, data[::-1], data[:, ::-1])) + + # Build the reference array through un-broadcast reprojections + array_ref = np.empty_like(image_stack) + footprint_ref = np.empty_like(image_stack) + for i in range(len(image_stack)): + array_out, footprint_out = reproject_adaptive( + (image_stack[i], header_in), + header_out, + conserve_flux=conserve_flux, + boundary_mode=boundary_mode, + kernel=kernel, + center_jacobian=center_jacobian, + ) + array_ref[i] = array_out + footprint_ref[i] = footprint_out + + return image_stack, array_ref, footprint_ref, header_in, header_out + + +@pytest.mark.parametrize("input_extra_dims", (1, 2)) +@pytest.mark.parametrize("output_shape", (None, "single", "full")) +@pytest.mark.parametrize("input_as_wcs", (True, False)) +@pytest.mark.parametrize("output_as_wcs", (True, False)) +def test_broadcast_reprojection(input_extra_dims, output_shape, input_as_wcs, output_as_wcs): + image_stack, array_ref, footprint_ref, header_in, header_out = _setup_for_broadcast_test() + + # Test both single and multiple dimensions being broadcast + if input_extra_dims == 2: + image_stack = image_stack.reshape((2, 2, *image_stack.shape[-2:])) + array_ref.shape = image_stack.shape + footprint_ref.shape = image_stack.shape + + # Test different ways of providing the output shape + if output_shape == "single": + # Have the broadcast dimensions be auto-added to the output shape + output_shape = image_stack.shape[-2:] + elif output_shape == "full": + # Provide the broadcast dimensions as part of the output shape + output_shape = image_stack.shape + + # Ensure logic works with WCS inputs as well as Header inputs + if input_as_wcs: + header_in = WCS(header_in) + if output_as_wcs: + header_out = WCS(header_out) + if output_shape is None: + # This combination of parameter values is not valid + return + + array_broadcast, footprint_broadcast = reproject_adaptive( + (image_stack, header_in), + header_out, + output_shape, + conserve_flux=False, + boundary_mode="strict", + ) + + np.testing.assert_array_equal(footprint_broadcast, footprint_ref) + np.testing.assert_allclose(array_broadcast, array_ref) + + +def _test_broadcast_reprojection_algo_specific_options( + conserve_flux=False, boundary_mode="strict", kernel="gaussian", center_jacobian=False +): + image_stack, array_ref, footprint_ref, header_in, header_out = _setup_for_broadcast_test( + conserve_flux=conserve_flux, + boundary_mode=boundary_mode, + kernel=kernel, + center_jacobian=center_jacobian, + ) + + array_broadcast, footprint_broadcast = reproject_adaptive( + (image_stack, header_in), + header_out, + image_stack.shape, + conserve_flux=conserve_flux, + boundary_mode=boundary_mode, + kernel=kernel, + center_jacobian=center_jacobian, + ) + + np.testing.assert_array_equal(footprint_broadcast, footprint_ref) + np.testing.assert_allclose(array_broadcast, array_ref) + + +@pytest.mark.parametrize("kernel", ("gaussian", "hann")) +def test_broadcast_reprojection_kernel(kernel): + _test_broadcast_reprojection_algo_specific_options(kernel=kernel) + + +@pytest.mark.parametrize( + "boundary_mode", + ("strict", "constant", "grid-constant", "ignore", "ignore_threshold", "nearest"), +) +def test_broadcast_reprojection_boundary_mode(boundary_mode): + _test_broadcast_reprojection_algo_specific_options(boundary_mode=boundary_mode) + + +@pytest.mark.parametrize("conserve_flux", (True, False)) +def test_broadcast_reprojection_conserve_flux(conserve_flux): + _test_broadcast_reprojection_algo_specific_options(conserve_flux=conserve_flux) + + +@pytest.mark.parametrize("center_jacobian", (True, False)) +def test_broadcast_reprojection_center_jacobian(center_jacobian): + _test_broadcast_reprojection_algo_specific_options(center_jacobian=center_jacobian) From 6416e7210bc69bf4c50f164a3c29a4a9305bd1e0 Mon Sep 17 00:00:00 2001 From: Sam Van Kooten Date: Wed, 4 Jan 2023 18:51:16 -0700 Subject: [PATCH 058/366] Address warning in oldestdeps tests & increase coverage --- reproject/adaptive/core.py | 4 +- reproject/interpolation/tests/test_core.py | 49 ++++++--------------- reproject/tests/test_high_level.py | 50 ++++++++++++++++++++++ 3 files changed, 64 insertions(+), 39 deletions(-) diff --git a/reproject/adaptive/core.py b/reproject/adaptive/core.py index 3566d07a1..8f4edaa9a 100644 --- a/reproject/adaptive/core.py +++ b/reproject/adaptive/core.py @@ -121,9 +121,9 @@ def _reproject_adaptive_2d( if wcs_in.low_level_wcs.pixel_n_dim != wcs_out.low_level_wcs.pixel_n_dim: raise ValueError("Number of dimensions between input and output WCS should match") elif len(shape_out) < wcs_out.low_level_wcs.pixel_n_dim: - raise ValueError("Length of shape_out too small for number of dimensions in wcs_out") + raise ValueError("Too few dimensions in shape_out") elif len(array_in.shape) < wcs_in.low_level_wcs.pixel_n_dim: - raise ValueError("Too few input-data dimensions for number of dimensions in wcs_out") + raise ValueError("Too few dimensions in input data") elif len(array_in.shape) != len(shape_out): raise ValueError("Number of dimensions in input and output data should match") diff --git a/reproject/interpolation/tests/test_core.py b/reproject/interpolation/tests/test_core.py index 705e1fd37..f59028b59 100644 --- a/reproject/interpolation/tests/test_core.py +++ b/reproject/interpolation/tests/test_core.py @@ -382,40 +382,6 @@ def test_slice_reprojection(roundtrip_coords): np.testing.assert_allclose(out_cube, ((inp_cube[:-1] + inp_cube[1:]) / 2.0)) -def test_too_few_dimens(): - header_in = fits.Header.fromtextfile( - get_pkg_data_filename("data/cube.hdr", package="reproject.tests") - ) - - header_in["NAXIS"] = 3 - - header_out = header_in.copy() - w_in = WCS(header_in) - w_out = WCS(header_out) - - array_in = np.zeros((2, 3, 4, 5)) - - # Create mis-matches between the input and output shapes - - with pytest.raises( - ValueError, match="Number of dimensions in input and output data should match" - ): - array_out, footprint = reproject_interp((array_in, w_in), w_out, shape_out=[2, 3, 4, 5, 6]) - - with pytest.raises( - ValueError, match="Number of dimensions in input and output data should match" - ): - array_out, footprint = reproject_interp((array_in[0], w_in), w_out, shape_out=[3, 4, 5, 6]) - - # Give fewer array dimensions than WCS dimensions - - with pytest.raises(ValueError, match="Too few dimensions in input data"): - array_out, footprint = reproject_interp((array_in[0, 0], w_in), w_out, shape_out=[4, 5, 6]) - - with pytest.raises(ValueError, match="Too few dimensions in shape_out"): - array_out, footprint = reproject_interp((array_in[0], w_in), w_out, shape_out=[5, 6]) - - @pytest.mark.parametrize("roundtrip_coords", (False, True)) def test_inequal_wcs_dims(roundtrip_coords): inp_cube = np.arange(3, dtype="float").repeat(4 * 5).reshape(3, 4, 5) @@ -723,9 +689,18 @@ def test_blocked_broadcast_reprojection(input_extra_dims, output_shape, parallel # Provide the broadcast dimensions as part of the output shape output_shape = image_stack.shape - array_broadcast, footprint_broadcast = reproject_interp( - (image_stack, header_in), header_out, output_shape, parallel=parallel, block_size=[5, 5] - ) + # the warning import and ignore is needed to keep pytest happy when running with + # older versions of astropy which don't have this fix: + # https://github.com/astropy/astropy/pull/12844 + # All the warning code should be removed when old version no longer being used + # Using context manager ensure only blocked function has them ignored + import warnings + + with warnings.catch_warnings(): + warnings.simplefilter("ignore", category=FITSFixedWarning) + array_broadcast, footprint_broadcast = reproject_interp( + (image_stack, header_in), header_out, output_shape, parallel=parallel, block_size=[5, 5] + ) np.testing.assert_array_equal(footprint_broadcast, footprint_ref) np.testing.assert_allclose(array_broadcast, array_ref) diff --git a/reproject/tests/test_high_level.py b/reproject/tests/test_high_level.py index 2f2b9f384..6d2df9353 100644 --- a/reproject/tests/test_high_level.py +++ b/reproject/tests/test_high_level.py @@ -207,3 +207,53 @@ def test_identity_projection(projection_type): else: # The Gaussian kernel in the adaptive algorithm blurs the image assert np.all(data_in != data_out) + + +@pytest.mark.parametrize( + "reproject_function", [reproject_interp, reproject_adaptive, reproject_exact] +) +def test_dimensions_checks(reproject_function): + header_in = fits.Header.fromtextfile( + get_pkg_data_filename("data/gc_eq.hdr", package="reproject.tests") + ) + + header_out = header_in.copy() + w_in = WCS(header_in) + w_out = WCS(header_out) + + array_in = np.zeros((2, 3, 4, 5)) + + # Create mis-matches between the input and output shapes + + with pytest.raises( + ValueError, match="Number of dimensions in input and output data should match" + ): + array_out, footprint = reproject_function( + (array_in, w_in), w_out, shape_out=[2, 3, 4, 5, 6] + ) + + with pytest.raises( + ValueError, match="Number of dimensions in input and output data should match" + ): + array_out, footprint = reproject_function( + (array_in[0], w_in), w_out, shape_out=[3, 4, 5, 6] + ) + + # Give fewer array dimensions than WCS dimensions + + with pytest.raises(ValueError, match="Too few dimensions in input data"): + array_out, footprint = reproject_function( + (array_in[0, 0, 0], w_in), w_out, shape_out=[4, 5, 6] + ) + + with pytest.raises(ValueError, match="Too few dimensions in shape_out"): + array_out, footprint = reproject_function( + (array_in[0], w_in), + w_out, + shape_out=[ + 5, + ], + ) + + with pytest.raises(ValueError, match="Dimensions to be looped over must match exactly"): + array_out, footprint = reproject_function((array_in, w_in), w_out, shape_out=[3, 3, 4, 5]) From 1650c89fc06c8b74d5cff3140d0988cdd5c72c49 Mon Sep 17 00:00:00 2001 From: Sam Van Kooten Date: Mon, 9 Jan 2023 16:54:15 -0700 Subject: [PATCH 059/366] Document multi-image reprojection --- docs/celestial.rst | 48 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/docs/celestial.rst b/docs/celestial.rst index 203dfbeac..ca6766a27 100644 --- a/docs/celestial.rst +++ b/docs/celestial.rst @@ -419,3 +419,51 @@ Or if you're dealing with FITS files, you can skip the numpy memmap step and use ... return_footprint=False, ... independent_celestial_slices=True) >>> outhdu.flush() + +Multiple images with the same coordinates +========================================= + +If you have multiple images with the exact same coordinate system (e.g. a raw +image and a corresponding processed image) and want to reproject both to the +same output frame, it is faster to compute the coordinate mapping between input +and output pixels only once and re-use this mapping for each reprojection. This +is supported by passing multiple input images as an additional dimension in the +input data array. When the input array contains more dimensions than the input +WCS describes, the extra leading dimensions are taken to represent separate +images with the same coordinates, and the reprojection loops over those +dimensions after computing the pixel mapping. For example: + +.. doctest-skip:: + >>> raw_image, header_in = fits.getdata('raw_image.fits', header=True) + >>> bg_subtracted_image = fits.getdata('background_subtracted_image.fits') + >>> header_out = # Prepare your desired output projection here + >>> # Combine the two images into one array + >>> image_stack = np.stack((raw_image, bg_subtracted_image)) + >>> # We provide a header that describes 2 WCS dimensions, but our input + >>> # array shape is (2, ny, nx)---the 'extra' first dimension represents + >>> # separate images sharing the same coordinates. + >>> reprojected, footprint = reproject.reproject_adaptive( + ... (image_stack, header_in), header_out) + >>> # The shape of `reprojected` is (2, ny', nx') + >>> reprojected_raw, reprojected_bg_subtracted = reprojected[0], reprojected[1] + +For :func:`~reproject.reproject_interp` and +:func:`~reproject.reproject_adaptive`, this is approximately twice as fast as +reprojecting the two images separately. For :func:`~reproject.reproject_exact` +the savings are much less significant, as producing the coordinate mapping is a +much smaller portion of the total runtime for this algorithm. + +When the output coordinates are provided as a WCS and a ``shape_out`` tuple, +``shape_out`` may describe the output shape of a single image, in which case +the extra leading dimensions are prepended automatically, or it may include the +extra dimensions, in which case the size of the extra dimensions must match +those of the input data exactly. + +While the reproject functions can accept the name of a FITS file as input, from +which the input data and coordinates are loaded automatically, this multi-image +reprojection feature does not support loading multiple images automatically +from multiple HDUs within one FITS file, as it would be difficult to verify +automatically that the HDUs contain the same exact coordinates. If multiple +HDUs do share coordinates and are to be reprojected together, they must be +separately loaded and combined into a single input array (e.g. using +``np.stack`` as in the above example). From 653a27e21c727270c6d4e2920e9bcf05b1b77aa7 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Wed, 25 Jan 2023 15:13:26 +0000 Subject: [PATCH 060/366] Add support for APE 14 WCSes in find_optimal_celestial_wcs --- .../mosaicking/tests/test_wcs_helpers.py | 123 ++++++++++++------ reproject/mosaicking/wcs_helpers.py | 71 +++++++--- 2 files changed, 133 insertions(+), 61 deletions(-) diff --git a/reproject/mosaicking/tests/test_wcs_helpers.py b/reproject/mosaicking/tests/test_wcs_helpers.py index dc0a2a7f0..87059f9a7 100644 --- a/reproject/mosaicking/tests/test_wcs_helpers.py +++ b/reproject/mosaicking/tests/test_wcs_helpers.py @@ -4,10 +4,15 @@ import numpy as np import pytest +from astropy import coordinates as coord from astropy import units as u from astropy.coordinates import FK5, Galactic, SkyCoord +from astropy.modeling import models from astropy.wcs import WCS from astropy.wcs.utils import pixel_to_skycoord, skycoord_to_pixel +from astropy.wcs.wcsapi import HighLevelWCSWrapper, SlicedLowLevelWCS +from gwcs import coordinate_frames as cf +from gwcs.wcs import WCS as GWCS from numpy.testing import assert_allclose, assert_equal from ..wcs_helpers import find_optimal_celestial_wcs @@ -20,16 +25,9 @@ SHAPELY_INSTALLED = True -class TestOptimalWCS: +class BaseTestOptimalWCS: def setup_method(self, method): - - self.wcs = WCS(naxis=2) - self.wcs.wcs.ctype = "RA---TAN", "DEC--TAN" - self.wcs.wcs.crpix = 10, 15 - self.wcs.wcs.crval = 43, 23 - self.wcs.wcs.cdelt = -0.1, 0.1 - self.wcs.wcs.equinox = 2000.0 - + self.wcs = self.generate_wcs() self.array = np.ones((30, 40)) def test_identity(self): @@ -37,12 +35,12 @@ def test_identity(self): wcs, shape = find_optimal_celestial_wcs([(self.array, self.wcs)], frame=FK5()) assert tuple(wcs.wcs.ctype) == ("RA---TAN", "DEC--TAN") - assert_allclose(wcs.wcs.crval, (43, 23)) - assert_allclose(wcs.wcs.cdelt, (-0.1, 0.1)) + assert_allclose(wcs.wcs.crval, (43, 23), atol=self.crval_atol) + assert_allclose(wcs.wcs.cdelt, (-0.1, 0.1), rtol=self.cdelt_rtol) assert wcs.wcs.equinox == 2000 assert wcs.wcs.radesys == "FK5" - assert_allclose(wcs.wcs.crpix, (10, 15)) + assert_allclose(wcs.wcs.crpix, self.identity_expected_crpix) assert shape == (30, 40) def test_args_tuple_wcs(self): @@ -61,14 +59,13 @@ def test_frame_projection(self): assert tuple(wcs.wcs.ctype) == ("GLON-CAR", "GLAT-CAR") c = SkyCoord(43, 23, unit=("deg", "deg"), frame="fk5").galactic - assert_allclose(wcs.wcs.crval, (c.l.degree, c.b.degree)) - assert_allclose(wcs.wcs.cdelt, (-0.1, 0.1)) + assert_allclose(wcs.wcs.crval, (c.l.degree, c.b.degree), atol=self.crval_atol) + assert_allclose(wcs.wcs.cdelt, (-0.1, 0.1), rtol=self.cdelt_rtol) assert np.isnan(wcs.wcs.equinox) assert wcs.wcs.radesys == "" - # The following values are empirical and just to make sure there are no regressions - assert_allclose(wcs.wcs.crpix, (16.21218937, 28.86119519)) - assert shape == (47, 50) + assert_allclose(wcs.wcs.crpix, self.frame_projection_expected_crpix) + assert shape == self.frame_projection_expected_shape def test_frame_str(self): wcs, shape = find_optimal_celestial_wcs([(self.array, self.wcs)], frame="galactic") @@ -92,12 +89,12 @@ def test_auto_rotate(self): assert tuple(wcs.wcs.ctype) == ("GLON-TAN", "GLAT-TAN") c = SkyCoord(43, 23, unit=("deg", "deg"), frame="fk5").galactic - assert_allclose(wcs.wcs.crval, (c.l.degree, c.b.degree)) - assert_allclose(wcs.wcs.cdelt, (-0.1, 0.1)) + assert_allclose(wcs.wcs.crval, (c.l.degree, c.b.degree), atol=self.crval_atol) + assert_allclose(wcs.wcs.cdelt, (-0.1, 0.1), rtol=self.cdelt_rtol) assert np.isnan(wcs.wcs.equinox) assert wcs.wcs.radesys == "" - assert_allclose(wcs.wcs.crpix, (10, 15)) + assert_allclose(wcs.wcs.crpix, self.auto_rotate_expected_crpix) assert shape == (30, 40) @pytest.mark.skipif("not SHAPELY_INSTALLED") @@ -110,7 +107,7 @@ def test_auto_rotate_systematic(self, angle): angle = np.radians(angle) pc = np.array([[np.cos(angle), -np.sin(angle)], [np.sin(angle), np.cos(angle)]]) - self.wcs.wcs.pc = pc + self.generate_wcs(pc=pc) wcs, shape = find_optimal_celestial_wcs([(self.array, self.wcs)], auto_rotate=True) @@ -119,8 +116,8 @@ def test_auto_rotate_systematic(self, angle): xp = np.array([0, 0, nx - 1, nx - 1, -1, -1, nx, nx]) yp = np.array([0, ny - 1, ny - 1, 0, -1, ny, ny, -1]) - c = pixel_to_skycoord(xp, yp, self.wcs, origin=0) - xp_final, yp_final = skycoord_to_pixel(c, wcs, origin=0) + c = self.wcs.pixel_to_world(xp, yp) + xp_final, yp_final = wcs.world_to_pixel(c) ny_final, nx_final = shape @@ -136,40 +133,32 @@ def test_auto_rotate_systematic(self, angle): def test_multiple_size(self): wcs1 = self.wcs - - wcs2 = deepcopy(self.wcs) - wcs2.wcs.crpix[0] += 10 - - wcs3 = deepcopy(self.wcs) - wcs3.wcs.crpix[1] -= 5 + wcs2 = self.generate_wcs(crpix=(20, 15)) + wcs3 = self.generate_wcs(crpix=(10, 10)) input_data = [(self.array, wcs1), (self.array, wcs2), (self.array, wcs3)] wcs, shape = find_optimal_celestial_wcs(input_data, frame=FK5()) assert tuple(wcs.wcs.ctype) == ("RA---TAN", "DEC--TAN") - assert_allclose(wcs.wcs.crval, (43, 23)) - assert_allclose(wcs.wcs.cdelt, (-0.1, 0.1)) + assert_allclose(wcs.wcs.crval, (43, 23), atol=self.crval_atol) + assert_allclose(wcs.wcs.cdelt, (-0.1, 0.1), rtol=self.cdelt_rtol) assert wcs.wcs.equinox == 2000 assert wcs.wcs.radesys == "FK5" - assert_allclose(wcs.wcs.crpix, (20, 15)) + assert_allclose(wcs.wcs.crpix, self.multiple_size_expected_crpix) assert shape == (35, 50) def test_multiple_resolution(self): wcs1 = self.wcs - - wcs2 = deepcopy(self.wcs) - wcs2.wcs.cdelt = -0.01, 0.02 - - wcs3 = deepcopy(self.wcs) - wcs3.wcs.crpix = -0.2, 0.3 + wcs2 = self.generate_wcs(cdelt=(-0.01, 0.02)) + wcs3 = self.generate_wcs(cdelt=(-0.2, 0.3)) input_data = [(self.array, wcs1), (self.array, wcs2), (self.array, wcs3)] wcs, shape = find_optimal_celestial_wcs(input_data) - assert_allclose(wcs.wcs.cdelt, (-0.01, 0.01)) + assert_allclose(wcs.wcs.cdelt, (-0.01, 0.01), rtol=self.cdelt_rtol) def test_invalid_array_shape(self): @@ -191,8 +180,62 @@ def test_invalid_wcs_shape(self): def test_invalid_not_celestial(self): - self.wcs.wcs.ctype = "OFFSETX", "OFFSETY" + self.wcs = self.generate_wcs(celestial=False) with pytest.raises(TypeError) as exc: wcs, shape = find_optimal_celestial_wcs([(self.array, self.wcs)]) assert exc.value.args[0] == "WCS does not have celestial components" + + +class TestOptimalFITSWCS(BaseTestOptimalWCS): + def generate_wcs( + self, crpix=(10, 15), crval=(43, 23), cdelt=(-0.1, 0.1), pc=None, celestial=True + ): + wcs = WCS(naxis=2) + if celestial: + wcs.wcs.ctype = "RA---TAN", "DEC--TAN" + else: + wcs.wcs.ctype = "OFFSETX", "OFFSETY" + wcs.wcs.crpix = crpix + wcs.wcs.crval = crval + wcs.wcs.cdelt = cdelt + wcs.wcs.equinox = 2000.0 + if pc is not None: + wcs.wcs.pc = pc + return wcs + + crval_atol = 1e-8 + crpix_atol = 1e-6 + cdelt_rtol = 1e-8 + + identity_expected_crpix = 10, 15 + auto_rotate_expected_crpix = 10, 15 + multiple_size_expected_crpix = 20, 15 + + # The following values are empirical and just to make sure there are no regressions + frame_projection_expected_crpix = 16.212189, 28.861195 + frame_projection_expected_shape = 47, 50 + + +class TestOptimalGWCS(TestOptimalFITSWCS): + def generate_wcs( + self, crpix=(10, 15), crval=(43, 23), cdelt=(-0.1, 0.1), pc=None, celestial=True + ): + wcs = super().generate_wcs( + crpix=crpix, crval=crval, cdelt=cdelt, pc=pc, celestial=celestial + ) + return HighLevelWCSWrapper(wcs) + + def test_args_tuple_header(self): + pytest.skip() + + crval_atol = 1.5 + crpix_atol = 1e-6 + cdelt_rtol = 1.0e-3 + + # The following values are empirical and just to make sure there are no regressions + identity_expected_crpix = 20.630112, 15.649142 + frame_projection_expected_crpix = 25.381691, 23.668728 + frame_projection_expected_shape = 46, 50 + auto_rotate_expected_crpix = 20.520875, 15.503349 + multiple_size_expected_crpix = 27.279739, 17.29016 diff --git a/reproject/mosaicking/wcs_helpers.py b/reproject/mosaicking/wcs_helpers.py index 1769a50ce..a3cbcf33f 100644 --- a/reproject/mosaicking/wcs_helpers.py +++ b/reproject/mosaicking/wcs_helpers.py @@ -3,6 +3,7 @@ import numpy as np from astropy import units as u from astropy.coordinates import SkyCoord, frame_transform_graph +from astropy.wcs import WCS from astropy.wcs.utils import ( celestial_frame_to_wcs, pixel_to_skycoord, @@ -89,15 +90,29 @@ def find_optimal_celestial_wcs( if len(shape) != 2: raise ValueError(f"Input data is not 2-dimensional (got shape {shape!r})") - if wcs.naxis != 2: + if wcs.pixel_n_dim != 2 or wcs.world_n_dim != 2: raise ValueError("Input WCS is not 2-dimensional") - if not wcs.has_celestial: - raise TypeError("WCS does not have celestial components") + if isinstance(wcs, WCS): - # Determine frame if it wasn't specified - if frame is None: - frame = wcs_to_celestial_frame(wcs) + if not wcs.has_celestial: + raise TypeError("WCS does not have celestial components") + + # Determine frame if it wasn't specified + if frame is None: + frame = wcs_to_celestial_frame(wcs) + + else: + + # Convert a single position to determine type of output and make + # sure there is only a single SkyCoord returned. + coord = wcs.pixel_to_world(0, 0) + + if not isinstance(coord, SkyCoord): + raise TypeError("WCS does not have celestial components") + + if frame is None: + frame = coord.frame.replicate_without_data() # Find pixel coordinates of corners. In future if we are worried about # significant distortions of the edges in the reprojection process we @@ -108,21 +123,35 @@ def find_optimal_celestial_wcs( # We have to do .frame here to make sure that we get an ICRS object # without any 'hidden' attributes, otherwise the stacking below won't - # work. TODO: check if we need to enable distortions here. - corners.append(pixel_to_skycoord(xc, yc, wcs, origin=0).icrs.frame) - - # We now figure out the reference coordinate for the image in ICRS. The - # easiest way to do this is actually to use pixel_to_skycoord with the - # reference position in pixel coordinates. We have to set origin=1 - # because crpix values are 1-based. - xp, yp = wcs.wcs.crpix - references.append(pixel_to_skycoord(xp, yp, wcs, origin=1).icrs.frame) - - # Find the pixel scale at the reference position - we take the minimum - # since we are going to set up a header with 'square' pixels with the - # smallest resolution specified. - scales = proj_plane_pixel_scales(wcs) - resolutions.append(np.min(np.abs(scales))) + # work. + corners.append(wcs.pixel_to_world(xc, yc).icrs.frame) + + if isinstance(wcs, WCS): + + # We now figure out the reference coordinate for the image in ICRS. The + # easiest way to do this is actually to use pixel_to_skycoord with the + # reference position in pixel coordinates. We have to set origin=1 + # because crpix values are 1-based. + xp, yp = wcs.wcs.crpix + references.append(pixel_to_skycoord(xp, yp, wcs, origin=1).icrs.frame) + + # Find the pixel scale at the reference position - we take the minimum + # since we are going to set up a header with 'square' pixels with the + # smallest resolution specified. + scales = proj_plane_pixel_scales(wcs) + resolutions.append(np.min(np.abs(scales))) + + else: + + xp, yp = (nx - 1) / 2, (ny - 1) / 2 + references.append(wcs.pixel_to_world(xp, yp).icrs.frame) + + xs = np.array([xp, xp, xp + 1]) + ys = np.array([yp, yp + 1, yp]) + cs = wcs.pixel_to_world(xs, ys) + dx = abs(cs[0].separation(cs[2]).deg) + dy = abs(cs[0].separation(cs[1]).deg) + resolutions.append(min(dx, dy)) # We now stack the coordinates - however the ICRS class can't do this # so we have to use the high-level SkyCoord class. From 55254948e34cd033a4fe73282236b868547fe72f Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Thu, 26 Jan 2023 15:31:56 +0000 Subject: [PATCH 061/366] Remove unusued imports --- reproject/mosaicking/tests/test_wcs_helpers.py | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/reproject/mosaicking/tests/test_wcs_helpers.py b/reproject/mosaicking/tests/test_wcs_helpers.py index 87059f9a7..5357f2d98 100644 --- a/reproject/mosaicking/tests/test_wcs_helpers.py +++ b/reproject/mosaicking/tests/test_wcs_helpers.py @@ -1,18 +1,11 @@ # Licensed under a 3-clause BSD style license - see LICENSE.rst -from copy import deepcopy - import numpy as np import pytest -from astropy import coordinates as coord from astropy import units as u from astropy.coordinates import FK5, Galactic, SkyCoord -from astropy.modeling import models from astropy.wcs import WCS -from astropy.wcs.utils import pixel_to_skycoord, skycoord_to_pixel -from astropy.wcs.wcsapi import HighLevelWCSWrapper, SlicedLowLevelWCS -from gwcs import coordinate_frames as cf -from gwcs.wcs import WCS as GWCS +from astropy.wcs.wcsapi import HighLevelWCSWrapper from numpy.testing import assert_allclose, assert_equal from ..wcs_helpers import find_optimal_celestial_wcs @@ -217,7 +210,7 @@ def generate_wcs( frame_projection_expected_shape = 47, 50 -class TestOptimalGWCS(TestOptimalFITSWCS): +class TestOptimalAPE14WCS(TestOptimalFITSWCS): def generate_wcs( self, crpix=(10, 15), crval=(43, 23), cdelt=(-0.1, 0.1), pc=None, celestial=True ): From c3908a19ff52cd9c24b521068976982ed7767291 Mon Sep 17 00:00:00 2001 From: astrofrog Date: Mon, 30 Jan 2023 10:57:29 +0000 Subject: [PATCH 062/366] Update CHANGELOG --- CHANGES.md | 249 ++++++++++++++++++++++++++++++----------------------- 1 file changed, 139 insertions(+), 110 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 6371fa759..cc6987f48 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,160 +1,189 @@ -0.9 (2022-09-02) ----------------- +## v0.10.0 - 2023-01-30 + + +### What's Changed + +#### Bug Fixes + +- Close FITS files after loading by @svank in https://github.com/astropy/reproject/pull/330 + +#### New Features + +- Add support for blocked and parallel reprojection in `reproject_interp` by @AlistairSymonds in https://github.com/astropy/reproject/pull/214 +- Add support for efficiently reprojecting multiple images with the same wcs by @svank in https://github.com/astropy/reproject/pull/332 +- Add support for APE 14 WCSes in find_optimal_celestial_wcs by @astrofrog in https://github.com/astropy/reproject/pull/334 + +#### Other Changes + +- Update package infrastructure by @Cadair in https://github.com/astropy/reproject/pull/304 +- Changed default filter kernel and boundary mode in `reproject_adaptive`, and removed `order` argument. by @svank in https://github.com/astropy/reproject/pull/291 +- Skip wheel tests on manylinux_aarch64 by @astrofrog in https://github.com/astropy/reproject/pull/307 +- Reformat all Python code using Black by @Cadair in https://github.com/astropy/reproject/pull/308 +- Use pixel_to_pixel from astropy.wcs.utils by @astrofrog in https://github.com/astropy/reproject/pull/315 +- Test CI on Python 3.11 beta by @dstansby in https://github.com/astropy/reproject/pull/298 +- Update pinned version of Cython by @astrofrog in https://github.com/astropy/reproject/pull/316 +- Speed up test_blocked_against_single by increasing smallest block size by @astrofrog in https://github.com/astropy/reproject/pull/319 +- Fix weird quotation marks from Black auto-formatting by @svank in https://github.com/astropy/reproject/pull/331 +- Fix CI by @astrofrog in https://github.com/astropy/reproject/pull/333 + +### New Contributors + +- @AlistairSymonds made their first contribution in https://github.com/astropy/reproject/pull/214 + +**Full Changelog**: https://github.com/astropy/reproject/compare/v0.9...v0.10.0 + +## 0.9 (2022-09-02) - Drop support for Python 3.7. - Infrastructure and packaging updates. - Made many improvements, bug fixes, and significant speed-ups for the adaptive - resampling algorithm, ``reproject_adaptive``. These bug fixes may cause - changes to the reprojected images, which are typically negligible. - Improvements include the addition of a flux-conserving mode, support for a - Gaussian filter kernel, a menu of boundary-handling modes, and a - ``center_jacobian`` flag to trade speed for accuracy with rapidly-varying - transformations. -- Added a ``roundtrip_coords`` argument to ``reproject_adaptive`` and - ``reproject_interp``. By default, all coordinate transformations are run in - both directions to handle some situations where they are ambiguous. This can - be disabled by setting ``roundtrip_coords=False`` which may offer a - significant speed increase. - -0.8 (2021-08-11) ----------------- - -- Improve ``find_optimal_celestial_wcs`` to accept input data descriptions as - just array shapes, not necessarily fully populated arrays. This makes it - possible to solve for the optimal WCS for a set of images that couldn't fit - into memory all at once, since the actual data aren't needed for optimal WCS - determination. [#242] - -- Fix implementation of ``hdu_weights`` in ``reproject_and_coadd``. [#249] - -0.7.1 (2020-05-29) ------------------- +- resampling algorithm, `reproject_adaptive`. These bug fixes may cause +- changes to the reprojected images, which are typically negligible. +- Improvements include the addition of a flux-conserving mode, support for a +- Gaussian filter kernel, a menu of boundary-handling modes, and a +- `center_jacobian` flag to trade speed for accuracy with rapidly-varying +- transformations. +- Added a `roundtrip_coords` argument to `reproject_adaptive` and +- `reproject_interp`. By default, all coordinate transformations are run in +- both directions to handle some situations where they are ambiguous. This can +- be disabled by setting `roundtrip_coords=False` which may offer a +- significant speed increase. + +## 0.8 (2021-08-11) + +- Improve `find_optimal_celestial_wcs` to accept input data descriptions as +- just array shapes, not necessarily fully populated arrays. This makes it +- possible to solve for the optimal WCS for a set of images that couldn't fit +- into memory all at once, since the actual data aren't needed for optimal WCS +- determination. [#242] +- +- Fix implementation of `hdu_weights` in `reproject_and_coadd`. [#249] +- + +## 0.7.1 (2020-05-29) - Fixed compatibility with Astropy 4.1. [#234] - +- - Updated minimum requirement for SciPy. [#236] +- -0.7 (2020-04-02) ----------------- +## 0.7 (2020-04-02) - Made C extension in overlapArea.c thread-safe by removing global - variables. [#211] - +- variables. [#211] +- - Made it possible to control whether to output debugging information - from overlapArea.c by setting DEBUG_OVERLAP_AREA=1 at build-time. [#211] - +- from overlapArea.c by setting DEBUG_OVERLAP_AREA=1 at build-time. [#211] +- - Fix compatibility with astropy v4.0.1. [#227] - -- Disable parallelization by default in ``reproject_exact`` - this can be - enabled with ``parallel=True``. [#227] - -- Fixed a bug with ``reproject_exact`` with ``parallel=False`` and - ``return_footprint=False``, which caused the footprint to be returned - anyway. [#227] - +- +- Disable parallelization by default in `reproject_exact` - this can be +- enabled with `parallel=True`. [#227] +- +- Fixed a bug with `reproject_exact` with `parallel=False` and +- `return_footprint=False`, which caused the footprint to be returned +- anyway. [#227] +- - The infrastructure of the package has been updated in line with the - APE 17 roadmap (https://github.com/astropy/astropy-APEs/blob/main/APE17.rst). - The main changes are that the ``python setup.py test`` and - ``python setup.py build_docs`` commands will no longer work. The - easiest way to replicate these commands is to install the tox - (https://tox.readthedocs.io) package and run ``tox -e test`` and - ``tox -e build_docs``. It is also possible to run pytest and sphinx - directly. [#228] +- APE 17 roadmap (https://github.com/astropy/astropy-APEs/blob/main/APE17.rst). +- The main changes are that the `python setup.py test` and +- `python setup.py build_docs` commands will no longer work. The +- easiest way to replicate these commands is to install the tox +- (https://tox.readthedocs.io) package and run `tox -e test` and +- `tox -e build_docs`. It is also possible to run pytest and sphinx +- directly. [#228] +- -0.6 (2019-11-01) ----------------- +## 0.6 (2019-11-01) - Added support for using any WCS that conforms to the WCS API described - in the Astropy Proposal for Enhancements 14 (APE 14). The - ``independent_celestial_slices=`` argument to ``reproject_interp`` has - been deprecated since it is no longer needed, as transformations are - automatically done in the most efficient way possible. [#166] - -- Include a warning for high resolution images with ``reproject_exact``, - since if the pixels are <0.05", precision issues can occur. [#200] - -- Added a new ``reproject_and_coadd`` function for doing mosaicking of - individual images, and added section in documentation about mosaicking. - [#186] - +- in the Astropy Proposal for Enhancements 14 (APE 14). The +- `independent_celestial_slices=` argument to `reproject_interp` has +- been deprecated since it is no longer needed, as transformations are +- automatically done in the most efficient way possible. [#166] +- +- Include a warning for high resolution images with `reproject_exact`, +- since if the pixels are <0.05", precision issues can occur. [#200] +- +- Added a new `reproject_and_coadd` function for doing mosaicking of +- individual images, and added section in documentation about mosaicking. +- [#186] +- - Added a new reproject.adaptive sub-package that implements the DeForest - (2004) algorithm for reprojection. [#52] - +- (2004) algorithm for reprojection. [#52] +- - Fixed a bug that caused 'exact' reprojection results to have numerical - issues when doing identity transformations. [#190] +- issues when doing identity transformations. [#190] +- -0.5.1 (2019-09-01) ------------------- +## 0.5.1 (2019-09-01) - Fixed a bug that caused 'exact' reprojection to fail if one or more of - the WCSes was oriented such that E and W were flipped. [#188] +- the WCSes was oriented such that E and W were flipped. [#188] -0.5 (2019-06-13) ----------------- +## 0.5 (2019-06-13) - Improve parse_output_projection to make it so that the output projection - can be specified as a filename. [#150] - +- can be specified as a filename. [#150] +- - Fixed a bug that caused HEALPix maps in RING order to not be correctly - interpreted. [#163] - +- interpreted. [#163] +- - Make it possible to specify the output array for reprojection using the - ``output_array=`` keyword argument. [#115] +- `output_array=` keyword argument. [#115] +- -0.4 (2018-01-29) ----------------- +## 0.4 (2018-01-29) - Refactored HEALPix reprojection code to use the astropy-healpix package - instead of healpy. [#139] - -- Added the ability to specify an output array in ``reproject_interp``, which - permits the use of memory-mapped arrays and therefore provides the capability - to handle data cubes much larger than memory [#115] - +- instead of healpy. [#139] +- +- Added the ability to specify an output array in `reproject_interp`, which +- permits the use of memory-mapped arrays and therefore provides the capability +- to handle data cubes much larger than memory [#115] +- - Fix test 32-bit test failures. [#146] - +- - Fix an issue with reprojecting images where there are two solutions along - the line of sight by forcing round-tripping of coordinate conversions [#129] - +- the line of sight by forcing round-tripping of coordinate conversions [#129] +- - Explicitly define default HDU as 0 for normal reprojection and 1 for - HEALPix reprojection. [#119] - +- HEALPix reprojection. [#119] +- - Added a function to find the optimal WCS for a set of images. [#136, #137] +- -0.3.2 (2017-10-22) ------------------- +## 0.3.2 (2017-10-22) - Fix a regression that caused certain all-sky images (e.g. the Mellinger Milky - Way Panorama, http://www.milkywaysky.com) to be reprojected to all NaNs when - the output WCS was in Mollweide coordinates. [#124] +- Way Panorama, http://www.milkywaysky.com) to be reprojected to all NaNs when +- the output WCS was in Mollweide coordinates. [#124] -0.3.1 (2016-07-07) ------------------- +## 0.3.1 (2016-07-07) - Include missing license file in tarball. - +- - Updated documentation to remove warnings about early versions. +- -0.3 (2016-07-06) ----------------- - -- Allow users to pass a ``field=`` option to ``reproject_from_healpix`` - to access different fields in a HEALPIX file. [#86] +## 0.3 (2016-07-06) +- Allow users to pass a `field=` option to `reproject_from_healpix` +- to access different fields in a HEALPIX file. [#86] +- - Significant improvements to performance when the input data is a large - memory-mapped array. [#105] - +- memory-mapped array. [#105] +- - Significant refactoring of interpolating reprojection to improve support for - n-dimensional arrays, optionally including two celestial axes (in which - case the coordinate transformation is taken into account). [#96, #102] +- n-dimensional arrays, optionally including two celestial axes (in which +- case the coordinate transformation is taken into account). [#96, #102] +- -0.2 (2015-10-29) ----------------- +## 0.2 (2015-10-29) - Fixed a bug that caused reprojection by interpolation to be truncated for - rectangular output images. +- rectangular output images. -0.1 (2015-05-08) ----------------- +## 0.1 (2015-05-08) - Initial Release. From 185dae10b968c2dc85898164e10e01ca7e7bcda1 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Mon, 30 Jan 2023 11:03:41 +0000 Subject: [PATCH 063/366] Fix changelog formatting --- CHANGES.md | 189 +++++++++++++++++++++++++++-------------------------- 1 file changed, 96 insertions(+), 93 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index cc6987f48..cc64982fa 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -32,158 +32,161 @@ **Full Changelog**: https://github.com/astropy/reproject/compare/v0.9...v0.10.0 -## 0.9 (2022-09-02) +## 0.9 - 2022-09-02 - Drop support for Python 3.7. + - Infrastructure and packaging updates. + - Made many improvements, bug fixes, and significant speed-ups for the adaptive -- resampling algorithm, `reproject_adaptive`. These bug fixes may cause -- changes to the reprojected images, which are typically negligible. -- Improvements include the addition of a flux-conserving mode, support for a -- Gaussian filter kernel, a menu of boundary-handling modes, and a -- `center_jacobian` flag to trade speed for accuracy with rapidly-varying -- transformations. + resampling algorithm, `reproject_adaptive`. These bug fixes may cause + changes to the reprojected images, which are typically negligible. + Improvements include the addition of a flux-conserving mode, support for a + Gaussian filter kernel, a menu of boundary-handling modes, and a + `center_jacobian` flag to trade speed for accuracy with rapidly-varying + transformations. + - Added a `roundtrip_coords` argument to `reproject_adaptive` and -- `reproject_interp`. By default, all coordinate transformations are run in -- both directions to handle some situations where they are ambiguous. This can -- be disabled by setting `roundtrip_coords=False` which may offer a -- significant speed increase. + `reproject_interp`. By default, all coordinate transformations are run in + both directions to handle some situations where they are ambiguous. This can + be disabled by setting `roundtrip_coords=False` which may offer a + significant speed increase. -## 0.8 (2021-08-11) +## 0.8 - 2021-08-11 - Improve `find_optimal_celestial_wcs` to accept input data descriptions as -- just array shapes, not necessarily fully populated arrays. This makes it -- possible to solve for the optimal WCS for a set of images that couldn't fit -- into memory all at once, since the actual data aren't needed for optimal WCS -- determination. [#242] -- + just array shapes, not necessarily fully populated arrays. This makes it + possible to solve for the optimal WCS for a set of images that couldn't fit + into memory all at once, since the actual data aren't needed for optimal WCS + determination. [#242] + - Fix implementation of `hdu_weights` in `reproject_and_coadd`. [#249] -- -## 0.7.1 (2020-05-29) + +## 0.7.1 - 2020-05-29 - Fixed compatibility with Astropy 4.1. [#234] -- + - Updated minimum requirement for SciPy. [#236] -- -## 0.7 (2020-04-02) + +## 0.7 - 2020-04-02 - Made C extension in overlapArea.c thread-safe by removing global -- variables. [#211] -- + variables. [#211] + - Made it possible to control whether to output debugging information -- from overlapArea.c by setting DEBUG_OVERLAP_AREA=1 at build-time. [#211] -- + from overlapArea.c by setting DEBUG_OVERLAP_AREA=1 at build-time. [#211] + - Fix compatibility with astropy v4.0.1. [#227] -- + - Disable parallelization by default in `reproject_exact` - this can be -- enabled with `parallel=True`. [#227] -- + enabled with `parallel=True`. [#227] + - Fixed a bug with `reproject_exact` with `parallel=False` and -- `return_footprint=False`, which caused the footprint to be returned -- anyway. [#227] -- + `return_footprint=False`, which caused the footprint to be returned + anyway. [#227] + - The infrastructure of the package has been updated in line with the -- APE 17 roadmap (https://github.com/astropy/astropy-APEs/blob/main/APE17.rst). -- The main changes are that the `python setup.py test` and -- `python setup.py build_docs` commands will no longer work. The -- easiest way to replicate these commands is to install the tox -- (https://tox.readthedocs.io) package and run `tox -e test` and -- `tox -e build_docs`. It is also possible to run pytest and sphinx -- directly. [#228] -- + APE 17 roadmap (https://github.com/astropy/astropy-APEs/blob/main/APE17.rst). + The main changes are that the `python setup.py test` and + `python setup.py build_docs` commands will no longer work. The + easiest way to replicate these commands is to install the tox + (https://tox.readthedocs.io) package and run `tox -e test` and + `tox -e build_docs`. It is also possible to run pytest and sphinx + directly. [#228] -## 0.6 (2019-11-01) + +## 0.6 - 2019-11-01 - Added support for using any WCS that conforms to the WCS API described -- in the Astropy Proposal for Enhancements 14 (APE 14). The -- `independent_celestial_slices=` argument to `reproject_interp` has -- been deprecated since it is no longer needed, as transformations are -- automatically done in the most efficient way possible. [#166] -- + in the Astropy Proposal for Enhancements 14 (APE 14). The + `independent_celestial_slices=` argument to `reproject_interp` has + been deprecated since it is no longer needed, as transformations are + automatically done in the most efficient way possible. [#166] + - Include a warning for high resolution images with `reproject_exact`, -- since if the pixels are <0.05", precision issues can occur. [#200] -- + since if the pixels are <0.05", precision issues can occur. [#200] + - Added a new `reproject_and_coadd` function for doing mosaicking of -- individual images, and added section in documentation about mosaicking. -- [#186] -- + individual images, and added section in documentation about mosaicking. + [#186] + - Added a new reproject.adaptive sub-package that implements the DeForest -- (2004) algorithm for reprojection. [#52] -- + (2004) algorithm for reprojection. [#52] + - Fixed a bug that caused 'exact' reprojection results to have numerical -- issues when doing identity transformations. [#190] -- + issues when doing identity transformations. [#190] + -## 0.5.1 (2019-09-01) +## 0.5.1 - 2019-09-01 - Fixed a bug that caused 'exact' reprojection to fail if one or more of -- the WCSes was oriented such that E and W were flipped. [#188] + the WCSes was oriented such that E and W were flipped. [#188] -## 0.5 (2019-06-13) +## 0.5 - 2019-06-13 - Improve parse_output_projection to make it so that the output projection -- can be specified as a filename. [#150] -- + can be specified as a filename. [#150] + - Fixed a bug that caused HEALPix maps in RING order to not be correctly -- interpreted. [#163] -- + interpreted. [#163] + - Make it possible to specify the output array for reprojection using the -- `output_array=` keyword argument. [#115] -- + `output_array=` keyword argument. [#115] -## 0.4 (2018-01-29) + +## 0.4 - 2018-01-29 - Refactored HEALPix reprojection code to use the astropy-healpix package -- instead of healpy. [#139] -- + instead of healpy. [#139] + - Added the ability to specify an output array in `reproject_interp`, which -- permits the use of memory-mapped arrays and therefore provides the capability -- to handle data cubes much larger than memory [#115] -- + permits the use of memory-mapped arrays and therefore provides the capability + to handle data cubes much larger than memory [#115] + - Fix test 32-bit test failures. [#146] -- + - Fix an issue with reprojecting images where there are two solutions along -- the line of sight by forcing round-tripping of coordinate conversions [#129] -- + the line of sight by forcing round-tripping of coordinate conversions [#129] + - Explicitly define default HDU as 0 for normal reprojection and 1 for -- HEALPix reprojection. [#119] -- + HEALPix reprojection. [#119] + - Added a function to find the optimal WCS for a set of images. [#136, #137] -- -## 0.3.2 (2017-10-22) + +## 0.3.2 - 2017-10-22 - Fix a regression that caused certain all-sky images (e.g. the Mellinger Milky -- Way Panorama, http://www.milkywaysky.com) to be reprojected to all NaNs when -- the output WCS was in Mollweide coordinates. [#124] + Way Panorama, http://www.milkywaysky.com) to be reprojected to all NaNs when + the output WCS was in Mollweide coordinates. [#124] -## 0.3.1 (2016-07-07) +## 0.3.1 - 2016-07-07 - Include missing license file in tarball. -- + - Updated documentation to remove warnings about early versions. -- -## 0.3 (2016-07-06) + +## 0.3 - 2016-07-06 - Allow users to pass a `field=` option to `reproject_from_healpix` -- to access different fields in a HEALPIX file. [#86] -- + to access different fields in a HEALPIX file. [#86] + - Significant improvements to performance when the input data is a large -- memory-mapped array. [#105] -- + memory-mapped array. [#105] + - Significant refactoring of interpolating reprojection to improve support for -- n-dimensional arrays, optionally including two celestial axes (in which -- case the coordinate transformation is taken into account). [#96, #102] -- + n-dimensional arrays, optionally including two celestial axes (in which + case the coordinate transformation is taken into account). [#96, #102] + -## 0.2 (2015-10-29) +## 0.2 - 2015-10-29 - Fixed a bug that caused reprojection by interpolation to be truncated for -- rectangular output images. + rectangular output images. -## 0.1 (2015-05-08) +## 0.1 - 2015-05-08 - Initial Release. From 760d72a02c664e89f9797b3ec8211d287efff40b Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 6 Feb 2023 16:40:00 +0000 Subject: [PATCH 064/366] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/pycqa/isort: 5.11.4 → 5.12.0](https://github.com/pycqa/isort/compare/5.11.4...5.12.0) - [github.com/psf/black: 22.12.0 → 23.1.0](https://github.com/psf/black/compare/22.12.0...23.1.0) --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 277b3ee42..90f8f5398 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -12,7 +12,7 @@ repos: exclude: ".*(data.*|extern.*|licenses.*|.*.fits)$" - repo: https://github.com/pycqa/isort - rev: 5.11.4 + rev: 5.12.0 hooks: - id: isort name: isort (python) @@ -67,7 +67,7 @@ repos: exclude: ".*(data.*|extern.*|cextern)$" - repo: https://github.com/psf/black - rev: 22.12.0 + rev: 23.1.0 hooks: - id: black From 4e619cccafa2d7f8d719f63e7bd006b43115067c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Maret?= Date: Fri, 24 Feb 2023 11:06:50 +0100 Subject: [PATCH 065/366] Mark tests that use remote data Fixes #338. --- reproject/interpolation/tests/test_core.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/reproject/interpolation/tests/test_core.py b/reproject/interpolation/tests/test_core.py index f59028b59..bb7004f1a 100644 --- a/reproject/interpolation/tests/test_core.py +++ b/reproject/interpolation/tests/test_core.py @@ -25,6 +25,7 @@ def as_high_level_wcs(wcs): @pytest.mark.array_compare(single_reference=True) @pytest.mark.parametrize("wcsapi", (False, True)) @pytest.mark.parametrize("roundtrip_coords", (False, True)) +@pytest.mark.remote_data def test_reproject_celestial_2d_gal2equ(wcsapi, roundtrip_coords): """ Test reprojection of a 2D celestial image, which includes a coordinate @@ -65,6 +66,7 @@ def test_reproject_celestial_2d_gal2equ(wcsapi, roundtrip_coords): @pytest.mark.array_compare(single_reference=True) @pytest.mark.parametrize(("wcsapi", "axis_order"), tuple(COMBINATIONS)) @pytest.mark.parametrize("roundtrip_coords", (False, True)) +@pytest.mark.remote_data def test_reproject_celestial_3d_equ2gal(wcsapi, axis_order, roundtrip_coords): """ Test reprojection of a 3D cube with celestial components, which includes a @@ -118,6 +120,7 @@ def test_reproject_celestial_3d_equ2gal(wcsapi, axis_order, roundtrip_coords): @pytest.mark.array_compare(single_reference=True) @pytest.mark.parametrize("wcsapi", (False, True)) @pytest.mark.parametrize("roundtrip_coords", (False, True)) +@pytest.mark.remote_data def test_small_cutout(wcsapi, roundtrip_coords): """ Test reprojection of a cutout from a larger image (makes sure that the @@ -151,6 +154,7 @@ def test_small_cutout(wcsapi, roundtrip_coords): @pytest.mark.parametrize("roundtrip_coords", (False, True)) +@pytest.mark.remote_data def test_mwpan_car_to_mol(roundtrip_coords): """ Test reprojection of the Mellinger Milky Way Panorama from CAR to MOL, @@ -183,6 +187,7 @@ def test_mwpan_car_to_mol(roundtrip_coords): @pytest.mark.parametrize("roundtrip_coords", (False, True)) +@pytest.mark.remote_data def test_small_cutout_outside(roundtrip_coords): """ Test reprojection of a cutout from a larger image - in this case the @@ -208,6 +213,7 @@ def test_small_cutout_outside(roundtrip_coords): @pytest.mark.parametrize("roundtrip_coords", (False, True)) +@pytest.mark.remote_data def test_celestial_mismatch_2d(roundtrip_coords): """ Make sure an error is raised if the input image has celestial WCS @@ -234,6 +240,7 @@ def test_celestial_mismatch_2d(roundtrip_coords): @pytest.mark.parametrize("roundtrip_coords", (False, True)) +@pytest.mark.remote_data def test_celestial_mismatch_3d(roundtrip_coords): """ Make sure an error is raised if the input image has celestial WCS @@ -270,6 +277,7 @@ def test_celestial_mismatch_3d(roundtrip_coords): @pytest.mark.parametrize("roundtrip_coords", (False, True)) +@pytest.mark.remote_data def test_spectral_mismatch_3d(roundtrip_coords): """ Make sure an error is raised if there are mismatches between the presence @@ -334,6 +342,7 @@ def test_naxis_mismatch(roundtrip_coords): @pytest.mark.parametrize("roundtrip_coords", (False, True)) +@pytest.mark.remote_data def test_slice_reprojection(roundtrip_coords): """ Test case where only the slices change and the celestial projection doesn't @@ -383,6 +392,7 @@ def test_slice_reprojection(roundtrip_coords): @pytest.mark.parametrize("roundtrip_coords", (False, True)) +@pytest.mark.remote_data def test_inequal_wcs_dims(roundtrip_coords): inp_cube = np.arange(3, dtype="float").repeat(4 * 5).reshape(3, 4, 5) header_in = fits.Header.fromtextfile( @@ -406,6 +416,7 @@ def test_inequal_wcs_dims(roundtrip_coords): @pytest.mark.parametrize("roundtrip_coords", (False, True)) +@pytest.mark.remote_data def test_different_wcs_types(roundtrip_coords): inp_cube = np.arange(3, dtype="float").repeat(4 * 5).reshape(3, 4, 5) @@ -435,6 +446,7 @@ def test_different_wcs_types(roundtrip_coords): @pytest.mark.parametrize("roundtrip_coords", (False, True)) +@pytest.mark.remote_data def test_reproject_3d_celestial_correctness_ra2gal(roundtrip_coords): inp_cube = np.arange(3, dtype="float").repeat(7 * 8).reshape(3, 7, 8) @@ -476,6 +488,7 @@ def test_reproject_3d_celestial_correctness_ra2gal(roundtrip_coords): @pytest.mark.parametrize("roundtrip_coords", (False, True)) +@pytest.mark.remote_data def test_reproject_with_output_array(roundtrip_coords): """ Test both full_reproject and slicewise reprojection. We use a case where the @@ -510,6 +523,7 @@ def test_reproject_with_output_array(roundtrip_coords): @pytest.mark.array_compare(single_reference=True) @pytest.mark.parametrize("file_format", ["fits", "asdf"]) +@pytest.mark.remote_data def test_reproject_roundtrip(file_format): # Test the reprojection with solar data, which ensures that the masking of @@ -578,6 +592,7 @@ def test_reproject_roundtrip(file_format): @pytest.mark.parametrize("roundtrip_coords", (False, True)) +@pytest.mark.remote_data def test_identity_with_offset(roundtrip_coords): # Reproject an array and WCS to itself but with a margin, which should @@ -708,6 +723,7 @@ def test_blocked_broadcast_reprojection(input_extra_dims, output_shape, parallel @pytest.mark.parametrize("parallel", [True, 2, False]) @pytest.mark.parametrize("block_size", [[40, 40], [500, 500], [500, 100], None]) +@pytest.mark.remote_data def test_blocked_against_single(parallel, block_size): # Ensure when we break a reprojection down into multiple discrete blocks @@ -753,6 +769,7 @@ def test_blocked_against_single(parallel, block_size): np.testing.assert_allclose(footprint_test, footprint_reference, equal_nan=True) +@pytest.mark.remote_data def test_blocked_corner_cases(): """ From aee57c86ea4bd23507a0d8432911d825f9086b30 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Fri, 24 Feb 2023 21:27:28 +0000 Subject: [PATCH 066/366] Fix code style --- reproject/adaptive/tests/test_core.py | 4 ---- reproject/array_utils.py | 1 - reproject/conftest.py | 2 -- reproject/healpix/tests/test_healpix.py | 1 - reproject/healpix/tests/test_utils.py | 2 -- reproject/interpolation/tests/test_core.py | 6 ------ reproject/mosaicking/background.py | 2 -- reproject/mosaicking/coadd.py | 4 ---- reproject/mosaicking/subset_array.py | 1 - reproject/mosaicking/tests/test_background.py | 1 - reproject/mosaicking/tests/test_coadd.py | 12 +----------- reproject/mosaicking/tests/test_subset_array.py | 1 - reproject/mosaicking/tests/test_wcs_helpers.py | 9 --------- reproject/mosaicking/wcs_helpers.py | 6 ------ reproject/spherical_intersect/core.py | 3 --- reproject/spherical_intersect/setup_package.py | 1 - .../spherical_intersect/tests/test_high_level.py | 5 ----- reproject/spherical_intersect/tests/test_overlap.py | 1 - .../spherical_intersect/tests/test_reproject.py | 4 ---- reproject/tests/test_high_level.py | 4 ---- reproject/tests/test_utils.py | 2 -- reproject/utils.py | 1 - reproject/wcs_utils.py | 1 - 23 files changed, 1 insertion(+), 73 deletions(-) diff --git a/reproject/adaptive/tests/test_core.py b/reproject/adaptive/tests/test_core.py index 06de9ac8f..c2275f910 100644 --- a/reproject/adaptive/tests/test_core.py +++ b/reproject/adaptive/tests/test_core.py @@ -28,7 +28,6 @@ def as_high_level_wcs(wcs): @pytest.mark.parametrize("center_jacobian", (False, True)) @pytest.mark.parametrize("roundtrip_coords", (False, True)) def test_reproject_adaptive_2d(wcsapi, center_jacobian, roundtrip_coords): - # Set up initial array with pattern data_in = np.zeros((256, 256)) data_in[::20, :] = 1 @@ -78,7 +77,6 @@ def test_reproject_adaptive_2d(wcsapi, center_jacobian, roundtrip_coords): @pytest.mark.parametrize("center_jacobian", (False, True)) @pytest.mark.parametrize("roundtrip_coords", (False, True)) def test_reproject_adaptive_2d_rotated(center_jacobian, roundtrip_coords): - # Set up initial array with pattern data_in = np.zeros((256, 256)) data_in[::20, :] = 1 @@ -669,7 +667,6 @@ def prepare_test_data(file_format): @pytest.mark.array_compare(single_reference=True) @pytest.mark.parametrize("file_format", ["fits", "asdf"]) def test_reproject_adaptive_roundtrip(file_format): - # Test the reprojection with solar data, which ensures that the masking of # pixels based on round-tripping works correctly. Using asdf is not just # about testing a different format but making sure that GWCS works. @@ -706,7 +703,6 @@ def test_reproject_adaptive_roundtrip(file_format): @pytest.mark.filterwarnings("ignore::FutureWarning") @pytest.mark.array_compare() def test_reproject_adaptive_uncentered_jacobian(): - # Explicitly test the uncentered-Jacobian path for a non-affine transform. # For this case, output pixels change by 6% at most, and usually much less. # (Though more nan pixels are present, as the uncentered calculation draws diff --git a/reproject/array_utils.py b/reproject/array_utils.py index 0712fab35..15825e544 100644 --- a/reproject/array_utils.py +++ b/reproject/array_utils.py @@ -8,7 +8,6 @@ def pad_edge_1(array): def map_coordinates(image, coords, **kwargs): - # In the built-in scipy map_coordinates, the values are defined at the # center of the pixels. This means that map_coordinates does not # correctly treat pixels that are in the outer half of the outer pixels. diff --git a/reproject/conftest.py b/reproject/conftest.py index 65904c37e..9ac7aaf70 100644 --- a/reproject/conftest.py +++ b/reproject/conftest.py @@ -16,9 +16,7 @@ def pytest_configure(config): - if ASTROPY_HEADER: - config.option.astropy_header = True PYTEST_HEADER_MODULES.pop("Pandas", None) diff --git a/reproject/healpix/tests/test_healpix.py b/reproject/healpix/tests/test_healpix.py index 7c7388fdb..7833d1ec3 100644 --- a/reproject/healpix/tests/test_healpix.py +++ b/reproject/healpix/tests/test_healpix.py @@ -17,7 +17,6 @@ def get_reference_header(oversample=2, nside=1): - reference_header = fits.Header() reference_header.update( { diff --git a/reproject/healpix/tests/test_utils.py b/reproject/healpix/tests/test_utils.py index fd53e9ddc..3c3d24551 100644 --- a/reproject/healpix/tests/test_utils.py +++ b/reproject/healpix/tests/test_utils.py @@ -7,7 +7,6 @@ def test_parse_coord_system(): - frame = parse_coord_system(Galactic()) assert isinstance(frame, Galactic) @@ -28,7 +27,6 @@ def test_parse_coord_system(): @pytest.mark.filterwarnings("ignore:unclosed file:ResourceWarning") def test_parse_input_healpix_data(tmpdir): - data = np.arange(3072) col = fits.Column(array=data, name="flux", format="E") diff --git a/reproject/interpolation/tests/test_core.py b/reproject/interpolation/tests/test_core.py index bb7004f1a..82be075c1 100644 --- a/reproject/interpolation/tests/test_core.py +++ b/reproject/interpolation/tests/test_core.py @@ -418,7 +418,6 @@ def test_inequal_wcs_dims(roundtrip_coords): @pytest.mark.parametrize("roundtrip_coords", (False, True)) @pytest.mark.remote_data def test_different_wcs_types(roundtrip_coords): - inp_cube = np.arange(3, dtype="float").repeat(4 * 5).reshape(3, 4, 5) header_in = fits.Header.fromtextfile( get_pkg_data_filename("data/cube.hdr", package="reproject.tests") @@ -448,7 +447,6 @@ def test_different_wcs_types(roundtrip_coords): @pytest.mark.parametrize("roundtrip_coords", (False, True)) @pytest.mark.remote_data def test_reproject_3d_celestial_correctness_ra2gal(roundtrip_coords): - inp_cube = np.arange(3, dtype="float").repeat(7 * 8).reshape(3, 7, 8) header_in = fits.Header.fromtextfile( @@ -525,7 +523,6 @@ def test_reproject_with_output_array(roundtrip_coords): @pytest.mark.parametrize("file_format", ["fits", "asdf"]) @pytest.mark.remote_data def test_reproject_roundtrip(file_format): - # Test the reprojection with solar data, which ensures that the masking of # pixels based on round-tripping works correctly. Using asdf is not just # about testing a different format but making sure that GWCS works. @@ -594,7 +591,6 @@ def test_reproject_roundtrip(file_format): @pytest.mark.parametrize("roundtrip_coords", (False, True)) @pytest.mark.remote_data def test_identity_with_offset(roundtrip_coords): - # Reproject an array and WCS to itself but with a margin, which should # end up empty. This is a regression test for a bug that caused some # values to extend beyond the original footprint. @@ -725,7 +721,6 @@ def test_blocked_broadcast_reprojection(input_extra_dims, output_shape, parallel @pytest.mark.parametrize("block_size", [[40, 40], [500, 500], [500, 100], None]) @pytest.mark.remote_data def test_blocked_against_single(parallel, block_size): - # Ensure when we break a reprojection down into multiple discrete blocks # it has the same result as if all pixels where reprejcted at once @@ -771,7 +766,6 @@ def test_blocked_against_single(parallel, block_size): @pytest.mark.remote_data def test_blocked_corner_cases(): - """ When doing blocked there are a few checks designed to sanity clamp/preserve values. Even though the blocking process only tiles in a 2d manner 3d information diff --git a/reproject/mosaicking/background.py b/reproject/mosaicking/background.py index 9a57e39a7..26fd9c571 100644 --- a/reproject/mosaicking/background.py +++ b/reproject/mosaicking/background.py @@ -86,7 +86,6 @@ def solve_corrections_sgd(offset_matrix, eta_initial=1, eta_half_life=100, rtol= previous_corrections = None for iteration in range(int(eta_half_life * 10)): - # Shuffle the indices to avoid cyclical behavior np.random.shuffle(indices) @@ -97,7 +96,6 @@ def solve_corrections_sgd(offset_matrix, eta_initial=1, eta_half_life=100, rtol= # columns is arbitrary, but for the purpose of the comments below, we # treat this as iterating over rows of the matrix. for i in indices: - if np.isnan(corrections[i]): continue diff --git a/reproject/mosaicking/coadd.py b/reproject/mosaicking/coadd.py index be6573c5f..2f380885e 100644 --- a/reproject/mosaicking/coadd.py +++ b/reproject/mosaicking/coadd.py @@ -109,7 +109,6 @@ def reproject_and_coadd( arrays = [] for idata in range(len(input_data)): - # We need to pre-parse the data here since we need to figure out how to # optimize/minimize the size of each output tile (see below). array_in, wcs_in = parse_input_data(input_data[idata], hdu_in=hdu_in) @@ -214,9 +213,7 @@ def reproject_and_coadd( final_footprint = np.zeros(shape_out) if combine_function in ("mean", "sum"): - for array in arrays: - # By default, values outside of the footprint are set to NaN # but we set these to 0 here to avoid getting NaNs in the # means/sums. @@ -230,7 +227,6 @@ def reproject_and_coadd( final_array /= final_footprint elif combine_function == "median": - # Here we need to operate in chunks since we could otherwise run # into memory issues diff --git a/reproject/mosaicking/subset_array.py b/reproject/mosaicking/subset_array.py index ac86412a7..69731b248 100644 --- a/reproject/mosaicking/subset_array.py +++ b/reproject/mosaicking/subset_array.py @@ -60,7 +60,6 @@ def __truediv__(self, other): return self._operation(other, operator.truediv) def _operation(self, other, op): - # Determine cutout parameters for overlap region imin = max(self.imin, other.imin) diff --git a/reproject/mosaicking/tests/test_background.py b/reproject/mosaicking/tests/test_background.py index e9e78e741..4ca335016 100644 --- a/reproject/mosaicking/tests/test_background.py +++ b/reproject/mosaicking/tests/test_background.py @@ -12,7 +12,6 @@ @pytest.mark.parametrize(("N", "scale"), CASES) def test_solve_corrections_sgd(N, scale): - # Generate random corrections expected = np.random.uniform(-scale, scale, N) diff --git a/reproject/mosaicking/tests/test_coadd.py b/reproject/mosaicking/tests/test_coadd.py index 95e552fc6..ec82af7a3 100644 --- a/reproject/mosaicking/tests/test_coadd.py +++ b/reproject/mosaicking/tests/test_coadd.py @@ -26,7 +26,6 @@ def reproject_function(request): class TestReprojectAndCoAdd: def setup_method(self, method): - self.wcs = WCS(naxis=2) self.wcs.wcs.ctype = "RA---TAN", "DEC--TAN" self.wcs.wcs.crpix = 322, 151 @@ -37,13 +36,12 @@ def setup_method(self, method): self.array = np.random.random((399, 334)) def _get_tiles(self, views): - # Given a list of views as (imin, imax, jmin, jmax), construct # tiles that can be passed into the co-adding code input_data = [] - for (jmin, jmax, imin, imax) in views: + for jmin, jmax, imin, imax in views: array = self.array[jmin:jmax, imin:imax].copy() wcs = self.wcs.deepcopy() wcs.wcs.crpix[0] -= imin @@ -54,7 +52,6 @@ def _get_tiles(self, views): @property def _nonoverlapping_views(self): - ie = (0, 122, 233, 245, 334) je = (0, 44, 45, 333, 335, 399) @@ -67,7 +64,6 @@ def _nonoverlapping_views(self): @property def _overlapping_views(self): - ie = (0, 122, 233, 245, 334) je = (0, 44, 45, 333, 335, 399) @@ -80,7 +76,6 @@ def _overlapping_views(self): @pytest.mark.parametrize("combine_function", ["mean", "sum"]) def test_coadd_no_overlap(self, combine_function, reproject_function): - # Make sure that if all tiles are exactly non-overlapping, and # we use 'sum' or 'mean', we get the exact input array back. @@ -99,7 +94,6 @@ def test_coadd_no_overlap(self, combine_function, reproject_function): assert_allclose(footprint, 1, atol=ATOL) def test_coadd_with_overlap(self, reproject_function): - # Here we make the input tiles overlapping. We can only check the # mean, not the sum. @@ -116,7 +110,6 @@ def test_coadd_with_overlap(self, reproject_function): assert_allclose(array, self.array, atol=ATOL) def test_coadd_background_matching(self, reproject_function): - # Test out the background matching input_data = self._get_tiles(self._overlapping_views) @@ -153,7 +146,6 @@ def test_coadd_background_matching(self, reproject_function): assert_allclose(array - np.mean(array), self.array - np.mean(self.array), atol=ATOL) def test_coadd_background_matching_with_nan(self, reproject_function): - # Test out the background matching when NaN values are present. We do # this by using three arrays with the same footprint but with different # parts masked. @@ -186,7 +178,6 @@ def test_coadd_background_matching_with_nan(self, reproject_function): @pytest.mark.filterwarnings("ignore:unclosed file:ResourceWarning") @pytest.mark.parametrize("mode", ["arrays", "filenames", "hdus"]) def test_coadd_with_weights(self, tmpdir, reproject_function, mode): - # Make sure that things work properly when specifying weights array1 = self.array + 1 @@ -247,7 +238,6 @@ def test_coadd_with_weights(self, tmpdir, reproject_function, mode): @pytest.mark.array_compare() def test_coadd_solar_map(): - # This is a test that exercises a lot of different parts of the mosaicking # code. The idea is to take three solar images from different viewpoints # and combine them into a single one. This uses weight maps that are not diff --git a/reproject/mosaicking/tests/test_subset_array.py b/reproject/mosaicking/tests/test_subset_array.py index b351e3072..3ecde69f9 100644 --- a/reproject/mosaicking/tests/test_subset_array.py +++ b/reproject/mosaicking/tests/test_subset_array.py @@ -11,7 +11,6 @@ class TestReprojectedArraySubset: def setup_method(self, method): - self.array1 = np.random.random((123, 87)) self.array2 = np.random.random((123, 87)) self.array3 = np.random.random((123, 87)) diff --git a/reproject/mosaicking/tests/test_wcs_helpers.py b/reproject/mosaicking/tests/test_wcs_helpers.py index 5357f2d98..b279b6bef 100644 --- a/reproject/mosaicking/tests/test_wcs_helpers.py +++ b/reproject/mosaicking/tests/test_wcs_helpers.py @@ -24,7 +24,6 @@ def setup_method(self, method): self.array = np.ones((30, 40)) def test_identity(self): - wcs, shape = find_optimal_celestial_wcs([(self.array, self.wcs)], frame=FK5()) assert tuple(wcs.wcs.ctype) == ("RA---TAN", "DEC--TAN") @@ -45,7 +44,6 @@ def test_args_tuple_header(self): ) def test_frame_projection(self): - wcs, shape = find_optimal_celestial_wcs( [(self.array, self.wcs)], frame=Galactic(), projection="CAR" ) @@ -70,7 +68,6 @@ def test_resolution(self): @pytest.mark.skipif("not SHAPELY_INSTALLED") def test_auto_rotate(self): - # To test auto_rotate, we set the frame to Galactic and the final image # should have the same size as the input image. In this case, the image # actually gets rotated 90 degrees, so the values aren't quite the same @@ -93,7 +90,6 @@ def test_auto_rotate(self): @pytest.mark.skipif("not SHAPELY_INSTALLED") @pytest.mark.parametrize("angle", np.linspace(0, 360, 13)) def test_auto_rotate_systematic(self, angle): - # This is a test to make sure for a number of angles that the corners # of the image are inside the final WCS but the next pixels outwards are # not. We test the full 360 range of angles. @@ -124,7 +120,6 @@ def test_auto_rotate_systematic(self, angle): assert_equal(inside, [1, 1, 1, 1, 0, 0, 0, 0]) def test_multiple_size(self): - wcs1 = self.wcs wcs2 = self.generate_wcs(crpix=(20, 15)) wcs3 = self.generate_wcs(crpix=(10, 10)) @@ -143,7 +138,6 @@ def test_multiple_size(self): assert shape == (35, 50) def test_multiple_resolution(self): - wcs1 = self.wcs wcs2 = self.generate_wcs(cdelt=(-0.01, 0.02)) wcs3 = self.generate_wcs(cdelt=(-0.2, 0.3)) @@ -154,7 +148,6 @@ def test_multiple_resolution(self): assert_allclose(wcs.wcs.cdelt, (-0.01, 0.01), rtol=self.cdelt_rtol) def test_invalid_array_shape(self): - array = np.ones((30, 20, 10)) with pytest.raises(ValueError) as exc: @@ -162,7 +155,6 @@ def test_invalid_array_shape(self): assert exc.value.args[0] == "Input data is not 2-dimensional (got shape (30, 20, 10))" def test_invalid_wcs_shape(self): - wcs = WCS(naxis=3) wcs.wcs.ctype = "RA---TAN", "DEC--TAN", "VELO-LSR" wcs.wcs.set() @@ -172,7 +164,6 @@ def test_invalid_wcs_shape(self): assert exc.value.args[0] == "Input WCS is not 2-dimensional" def test_invalid_not_celestial(self): - self.wcs = self.generate_wcs(celestial=False) with pytest.raises(TypeError) as exc: diff --git a/reproject/mosaicking/wcs_helpers.py b/reproject/mosaicking/wcs_helpers.py index a3cbcf33f..824dc342c 100644 --- a/reproject/mosaicking/wcs_helpers.py +++ b/reproject/mosaicking/wcs_helpers.py @@ -86,7 +86,6 @@ def find_optimal_celestial_wcs( resolutions = [] for shape, wcs in input_shapes: - if len(shape) != 2: raise ValueError(f"Input data is not 2-dimensional (got shape {shape!r})") @@ -94,7 +93,6 @@ def find_optimal_celestial_wcs( raise ValueError("Input WCS is not 2-dimensional") if isinstance(wcs, WCS): - if not wcs.has_celestial: raise TypeError("WCS does not have celestial components") @@ -103,7 +101,6 @@ def find_optimal_celestial_wcs( frame = wcs_to_celestial_frame(wcs) else: - # Convert a single position to determine type of output and make # sure there is only a single SkyCoord returned. coord = wcs.pixel_to_world(0, 0) @@ -127,7 +124,6 @@ def find_optimal_celestial_wcs( corners.append(wcs.pixel_to_world(xc, yc).icrs.frame) if isinstance(wcs, WCS): - # We now figure out the reference coordinate for the image in ICRS. The # easiest way to do this is actually to use pixel_to_skycoord with the # reference position in pixel coordinates. We have to set origin=1 @@ -142,7 +138,6 @@ def find_optimal_celestial_wcs( resolutions.append(np.min(np.abs(scales))) else: - xp, yp = (nx - 1) / 2, (ny - 1) / 2 references.append(wcs.pixel_to_world(xp, yp).icrs.frame) @@ -192,7 +187,6 @@ def find_optimal_celestial_wcs( xp, yp = skycoord_to_pixel(corners, wcs_final, origin=1) if auto_rotate: - # Use shapely to represent the points and find the minimum rotated # rectangle from shapely.geometry import MultiPoint diff --git a/reproject/spherical_intersect/core.py b/reproject/spherical_intersect/core.py index 6068599ae..694f0770c 100644 --- a/reproject/spherical_intersect/core.py +++ b/reproject/spherical_intersect/core.py @@ -23,7 +23,6 @@ def _reproject_slice(args): def _reproject_celestial(array, wcs_in, wcs_out, shape_out, parallel=True, return_footprint=True): - # Check the parallel flag. if type(parallel) != bool and type(parallel) != int: raise TypeError("The 'parallel' flag must be a boolean or integral value") @@ -171,7 +170,6 @@ def _reproject_celestial(array, wcs_in, wcs_out, shape_out, parallel=True, retur common_func_par[-2] = array[i] if nproc == 1: - array_new, weights = _reproject_slice([0, nx_in] + common_func_par) with np.errstate(invalid="ignore"): @@ -182,7 +180,6 @@ def _reproject_celestial(array, wcs_in, wcs_out, shape_out, parallel=True, retur output_weights.append(weights) elif nproc > 1: - inputs = [] for i in range(nproc): start = int(nx_in) // nproc * i diff --git a/reproject/spherical_intersect/setup_package.py b/reproject/spherical_intersect/setup_package.py index 6eb9c8bae..0fa4d6b53 100644 --- a/reproject/spherical_intersect/setup_package.py +++ b/reproject/spherical_intersect/setup_package.py @@ -7,7 +7,6 @@ def get_extensions(): - libraries = [] sources = [] diff --git a/reproject/spherical_intersect/tests/test_high_level.py b/reproject/spherical_intersect/tests/test_high_level.py index db6d9b153..9e925ddbd 100644 --- a/reproject/spherical_intersect/tests/test_high_level.py +++ b/reproject/spherical_intersect/tests/test_high_level.py @@ -14,7 +14,6 @@ class TestReprojectExact: def setup_class(self): - header_gal = get_pkg_data_filename("../../tests/data/gc_ga.hdr") header_equ = get_pkg_data_filename("../../tests/data/gc_eq.hdr") self.header_in = fits.Header.fromtextfile(header_gal) @@ -36,7 +35,6 @@ def test_array_header(self): reproject_exact((self.array_in, self.header_in), self.header_out) def test_parallel_option(self): - reproject_exact((self.array_in, self.header_in), self.header_out, parallel=1) with pytest.raises(ValueError) as exc: @@ -45,7 +43,6 @@ def test_parallel_option(self): def test_identity(): - # Reproject an array and WCS to itself wcs = WCS(naxis=2) @@ -64,9 +61,7 @@ def test_identity(): def test_reproject_precision_warning(): - for res in [0.1 / 3600, 0.01 / 3600]: - wcs1 = WCS() wcs1.wcs.ctype = "RA---TAN", "DEC--TAN" wcs1.wcs.crval = 13, 80 diff --git a/reproject/spherical_intersect/tests/test_overlap.py b/reproject/spherical_intersect/tests/test_overlap.py index d96b0a6e8..984b37009 100644 --- a/reproject/spherical_intersect/tests/test_overlap.py +++ b/reproject/spherical_intersect/tests/test_overlap.py @@ -28,7 +28,6 @@ def test_partial_overlap(): @pytest.mark.parametrize(("clockwise1", "clockwise2"), product([False, True], [False, True])) def test_overlap_direction(clockwise1, clockwise2): - # Regression test for a bug that caused the calculation to fail if one or # both of the polygons were clockwise diff --git a/reproject/spherical_intersect/tests/test_reproject.py b/reproject/spherical_intersect/tests/test_reproject.py index 4fc2ca0a6..2cb90897b 100644 --- a/reproject/spherical_intersect/tests/test_reproject.py +++ b/reproject/spherical_intersect/tests/test_reproject.py @@ -11,7 +11,6 @@ def test_reproject_celestial_slices_2d(): - header_in = fits.Header.fromtextfile(get_pkg_data_filename("../../tests/data/gc_ga.hdr")) header_out = fits.Header.fromtextfile(get_pkg_data_filename("../../tests/data/gc_eq.hdr")) @@ -69,7 +68,6 @@ def test_reproject_celestial_slices_2d(): def test_reproject_celestial_consistency(): - # Consistency between the different modes wcs_in = WCS(fits.Header.fromstring(INPUT_HDR, sep="\n")) @@ -85,7 +83,6 @@ def test_reproject_celestial_consistency(): @pytest.mark.parametrize("wcsapi", (False, True)) def test_reproject_celestial_montage(wcsapi): - # Accuracy compared to Montage wcs_in = WCS(fits.Header.fromstring(INPUT_HDR, sep="\n")) @@ -101,7 +98,6 @@ def test_reproject_celestial_montage(wcsapi): def test_reproject_flipping(): - # Regression test for a bug that caused issues when the WCS was oriented # in a way that meant polygon vertices were clockwise. diff --git a/reproject/tests/test_high_level.py b/reproject/tests/test_high_level.py index 6d2df9353..d90b7aa77 100644 --- a/reproject/tests/test_high_level.py +++ b/reproject/tests/test_high_level.py @@ -41,7 +41,6 @@ def reproject_function(request): class TestReproject: def setup_method(self, method): - self.header_in = fits.Header.fromtextfile(get_pkg_data_filename("data/gc_ga.hdr")) self.header_out = fits.Header.fromtextfile(get_pkg_data_filename("data/gc_eq.hdr")) @@ -60,7 +59,6 @@ def setup_method(self, method): @pytest.mark.filterwarnings("ignore::FutureWarning") def test_hdu_header(self, reproject_function): - with pytest.raises(ValueError) as exc: reproject_function(self.hdu_in, self.header_out) assert exc.value.args[0] == ( @@ -76,7 +74,6 @@ def test_hdu_wcs(self, reproject_function): @pytest.mark.filterwarnings("ignore::FutureWarning") def test_array_wcs_header(self, reproject_function): - with pytest.raises(ValueError) as exc: reproject_function((self.array_in, self.wcs_in), self.header_out) assert exc.value.args[0] == ( @@ -122,7 +119,6 @@ def test_return_footprint(self, reproject_function): @pytest.mark.filterwarnings("ignore::FutureWarning") @pytest.mark.parametrize("projection_type, dtype", itertools.product(ALL_MODES, ALL_DTYPES)) def test_surface_brightness(projection_type, dtype): - header_in = fits.Header.fromstring(INPUT_HDR, sep="\n") header_in["NAXIS"] = 2 header_in["NAXIS1"] = 10 diff --git a/reproject/tests/test_utils.py b/reproject/tests/test_utils.py index 90182e7e9..5908f231b 100644 --- a/reproject/tests/test_utils.py +++ b/reproject/tests/test_utils.py @@ -10,7 +10,6 @@ @pytest.mark.filterwarnings("ignore:unclosed file:ResourceWarning") def test_parse_input_data(tmpdir): - header = fits.Header.fromtextfile(get_pkg_data_filename("data/gc_ga.hdr")) data = np.arange(200).reshape((10, 20)) @@ -126,7 +125,6 @@ def test_parse_input_shape(tmpdir): def test_parse_output_projection(tmpdir): - header = fits.Header.fromtextfile(get_pkg_data_filename("data/gc_ga.hdr")) wcs = WCS(header) diff --git a/reproject/utils.py b/reproject/utils.py index 044058a44..7c536e93d 100644 --- a/reproject/utils.py +++ b/reproject/utils.py @@ -114,7 +114,6 @@ def parse_input_weights(input_weights, hdu_weights=None): def parse_output_projection(output_projection, shape_in=None, shape_out=None, output_array=None): - if shape_out is None: if output_array is not None: shape_out = output_array.shape diff --git a/reproject/wcs_utils.py b/reproject/wcs_utils.py index 47a24e2f6..1a6baf76c 100644 --- a/reproject/wcs_utils.py +++ b/reproject/wcs_utils.py @@ -26,7 +26,6 @@ def has_celestial(wcs): def pixel_to_pixel_with_roundtrip(wcs1, wcs2, *inputs): - outputs = pixel_to_pixel(wcs1, wcs2, *inputs) # Now convert back to check that coordinates round-trip, if not then set to NaN From 7e2faee5a7573b11cb52465f1570c82712c2f303 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Tue, 6 Sep 2022 17:15:13 +0100 Subject: [PATCH 067/366] Started refactoring reproject_blocked to use dask --- reproject/utils.py | 148 ++++++--------------------------------------- 1 file changed, 18 insertions(+), 130 deletions(-) diff --git a/reproject/utils.py b/reproject/utils.py index 7c536e93d..0b25e2633 100644 --- a/reproject/utils.py +++ b/reproject/utils.py @@ -1,6 +1,7 @@ from concurrent import futures import astropy.nddata +import dask.array as da import numpy as np from astropy.io import fits from astropy.io.fits import CompImageHDU, HDUList, Header, ImageHDU, PrimaryHDU @@ -162,49 +163,6 @@ def parse_output_projection(output_projection, shape_in=None, shape_out=None, ou return wcs_out, shape_out -def _block( - reproject_func, array_in, wcs_in, wcs_out_sub, shape_out, i_range, j_range, return_footprint -): - """ - Implementation function that handles reprojecting subsets blocks of pixels - from an input image and holds metadata about where to reinsert when done. - - Parameters - ---------- - reproject_func - One the existing reproject functions implementing a reprojection algorithm - that that will be used be used to perform reprojection - array_in - Data following the same format as expected by underlying reproject_func, - expected to `~numpy.ndarray` when used from reproject_blocked() - wcs_in: `~astropy.wcs.WCS` - WCS object corresponding to array_in - wcs_out_sub: - Output WCS image will be projected to. Normally will correspond to subset of - total output image when used by repoject_blocked() - shape_out: - Passed to reproject_func() alongside WCS out to determine image size - i_range: - Passed through unmodified, used to determine where to reinsert block - j_range: - Passed through unmodified, used to determine where to reinsert block - """ - - result = reproject_func( - array_in, wcs_in, wcs_out_sub, shape_out=shape_out, return_footprint=return_footprint - ) - - res_arr = None - res_fp = None - - if return_footprint: - res_arr, res_fp = result - else: - res_arr = result - - return {"i": i_range, "j": j_range, "res_arr": res_arr, "res_fp": res_fp} - - def reproject_blocked( reproject_func, array_in, @@ -218,7 +176,7 @@ def reproject_blocked( parallel=True, ): """ - Implementation function that handles reprojecting subsets blocks of pixels + Implementaton function that handles reprojecting subsets blocks of pixels from an input image and holds metadata about where to reinsert when done. Parameters @@ -258,99 +216,29 @@ def reproject_blocked( greater than one, a parallel implementation using ``n`` processes is chosen. """ + # TODO: use block_size + if output_array is None: output_array = np.zeros(shape_out, dtype=float) if output_footprint is None and return_footprint: output_footprint = np.zeros(shape_out, dtype=float) - # setup variables needed for multiprocessing if required - proc_pool = None - blocks_futures = [] - - if parallel or type(parallel) is int: - if type(parallel) is int: - if parallel <= 0: - raise ValueError("The number of processors to use must be strictly positive") - else: - proc_pool = futures.ProcessPoolExecutor(max_workers=parallel) - else: - proc_pool = futures.ProcessPoolExecutor() - - # This will iterate over the output space, generating slices of that - # WCS and either processing and reinserting them immediately, - # or when doing parallel impl submit them to workers then wait and reinsert as - # the workers complete each block - for imin in range(0, output_array.shape[-2], block_size[0]): - imax = min(imin + block_size[0], output_array.shape[-2]) - for jmin in range(0, output_array.shape[-1], block_size[1]): - jmax = min(jmin + block_size[1], output_array.shape[-1]) - shape_out_sub = (imax - imin, jmax - jmin) - # if the output has more than two dims, apply our blocking to only the last two - shape_out_sub = output_array.shape[:-2] + shape_out_sub - - slices = [slice(imin, imax), slice(jmin, jmax)] - if wcs_out.low_level_wcs.pixel_n_dim > 2: - slices = [Ellipsis] + slices - wcs_out_sub = HighLevelWCSWrapper(SlicedLowLevelWCS(wcs_out, slices=slices)) - - if proc_pool is None: - # if sequential input data and reinsert block into main array immediately - completed_block = _block( - reproject_func=reproject_func, - array_in=array_in, - wcs_in=wcs_in, - wcs_out_sub=wcs_out_sub, - shape_out=shape_out_sub, - return_footprint=return_footprint, - j_range=(jmin, jmax), - i_range=(imin, imax), - ) - - output_array[..., imin:imax, jmin:jmax] = completed_block["res_arr"][:] - if return_footprint: - output_footprint[..., imin:imax, jmin:jmax] = completed_block["res_fp"][:] - - else: - # if parallel just submit all work items and move on to waiting for them to be done - future = proc_pool.submit( - _block, - reproject_func=reproject_func, - array_in=array_in, - wcs_in=wcs_in, - wcs_out_sub=wcs_out_sub, - shape_out=shape_out_sub, - return_footprint=return_footprint, - j_range=(jmin, jmax), - i_range=(imin, imax), - ) - blocks_futures.append(future) - - # If a parallel implementation is being used that means the - # blocks have not been reassembled yet and must be done now as their - # block call completes in the worker processes - if proc_pool is not None: - completed_future_count = 0 - for completed_future in futures.as_completed(blocks_futures): - completed_block = completed_future.result() - i_range = completed_block["i"] - j_range = completed_block["j"] - output_array[..., i_range[0] : i_range[1], j_range[0] : j_range[1]] = completed_block[ - "res_arr" - ][:] + def reproject_single_block(a, block_info=None): + if a.ndim == 0: + return a + slices = [slice(*x) for x in block_info[None]["array-location"]] + wcs_out_sub = HighLevelWCSWrapper(SlicedLowLevelWCS(wcs_out, slices=slices)) # + array, footprint = reproject_func( + array_in, wcs_in, wcs_out_sub, block_info[None]["chunk-shape"] + ) + return np.array([array, footprint]) - if return_footprint: - footprint_block = completed_block["res_fp"][:] - output_footprint[ - ..., i_range[0] : i_range[1], j_range[0] : j_range[1] - ] = footprint_block + output_array_dask = da.map_blocks(reproject_single_block, output_array, dtype=float) - completed_future_count += 1 - idx = blocks_futures.index(completed_future) - # ensure memory used by returned data is freed - completed_future._result = None - del blocks_futures[idx], completed_future - proc_pool.shutdown() - del blocks_futures + # TODO: set the relevant scheduler + da.store( + [output_array_dask[0], output_array_dask[0]], [output_array, output_footprint], compute=True + ) if return_footprint: return output_array, output_footprint From 37ca966f54c48784c156bafbdf0242c0f40b8908 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Wed, 7 Sep 2022 22:39:33 +0100 Subject: [PATCH 068/366] Tweaks --- reproject/utils.py | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/reproject/utils.py b/reproject/utils.py index 0b25e2633..63b7dbcd6 100644 --- a/reproject/utils.py +++ b/reproject/utils.py @@ -216,31 +216,38 @@ def reproject_blocked( greater than one, a parallel implementation using ``n`` processes is chosen. """ - # TODO: use block_size - if output_array is None: output_array = np.zeros(shape_out, dtype=float) if output_footprint is None and return_footprint: output_footprint = np.zeros(shape_out, dtype=float) + scheduler = "processes" if parallel else "synchronous" + def reproject_single_block(a, block_info=None): if a.ndim == 0: return a - slices = [slice(*x) for x in block_info[None]["array-location"]] + slices = [slice(*x) for x in block_info[None]["array-location"][1:]] wcs_out_sub = HighLevelWCSWrapper(SlicedLowLevelWCS(wcs_out, slices=slices)) # array, footprint = reproject_func( - array_in, wcs_in, wcs_out_sub, block_info[None]["chunk-shape"] + array_in, wcs_in, wcs_out_sub, block_info[None]["chunk-shape"][1:] ) return np.array([array, footprint]) - output_array_dask = da.map_blocks(reproject_single_block, output_array, dtype=float) - - # TODO: set the relevant scheduler - da.store( - [output_array_dask[0], output_array_dask[0]], [output_array, output_footprint], compute=True + output_array_dask = da.map_blocks( + reproject_single_block, + da.from_array(output_array, chunks=block_size), + dtype=float, + new_axis=0, ) if return_footprint: + da.store( + [output_array_dask[0], output_array_dask[1]], + [output_array, output_footprint], + compute=True, + scheduler=scheduler, + ) return output_array, output_footprint else: + da.store(output_array_dask[0], output_array, compute=True, scheduler=scheduler) return output_array From ad1ee0d21be9a5f832a66c34c8d07cba799d9414 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Wed, 5 Oct 2022 22:24:10 +0100 Subject: [PATCH 069/366] Added dask[array] as a reproject dependency --- setup.cfg | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.cfg b/setup.cfg index bf5c97cae..bd27151a6 100644 --- a/setup.cfg +++ b/setup.cfg @@ -21,6 +21,7 @@ install_requires = astropy>=4.0 astropy-healpix>=0.6 scipy>=1.3 + dask[array] [options.extras_require] test = From 1465af424436decfead64d3785cc998d004d8670 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Thu, 6 Oct 2022 14:34:52 +0100 Subject: [PATCH 070/366] Increase small block size to speed up test, and remove test which is no longer needed --- reproject/interpolation/tests/test_core.py | 45 ---------------------- 1 file changed, 45 deletions(-) diff --git a/reproject/interpolation/tests/test_core.py b/reproject/interpolation/tests/test_core.py index 82be075c1..1cc6dc03e 100644 --- a/reproject/interpolation/tests/test_core.py +++ b/reproject/interpolation/tests/test_core.py @@ -762,48 +762,3 @@ def test_blocked_against_single(parallel, block_size): np.testing.assert_allclose(array_test, array_reference, equal_nan=True) np.testing.assert_allclose(footprint_test, footprint_reference, equal_nan=True) - - -@pytest.mark.remote_data -def test_blocked_corner_cases(): - """ - When doing blocked there are a few checks designed to sanity clamp/preserve - values. Even though the blocking process only tiles in a 2d manner 3d information - about the image needs to be preserved and transformed correctly. Additonally - when automatically determining block size based on CPU cores zeros can appear on - machines where num_cores > x or y dim of output image. So make sure it correctly - functions when 0 block size goes in - """ - - # Read in the input cube - hdu_in = fits.open(get_pkg_data_filename("data/equatorial_3d.fits", package="reproject.tests"))[ - 0 - ] - - # Define the output header - this should be the same for all versions of - # this test to make sure we can use a single reference file. - header_out = hdu_in.header.copy() - header_out["NAXIS1"] = 10 - header_out["NAXIS2"] = 9 - header_out["CTYPE1"] = "GLON-SIN" - header_out["CTYPE2"] = "GLAT-SIN" - header_out["CRVAL1"] = 163.16724 - header_out["CRVAL2"] = -15.777405 - header_out["CRPIX1"] = 6 - header_out["CRPIX2"] = 5 - - array_reference = reproject_interp(hdu_in, header_out, return_footprint=False) - - array_test = None - - # same reason as test above for FITSFixedWarning - import warnings - - with warnings.catch_warnings(): - warnings.simplefilter("ignore", category=FITSFixedWarning) - - array_test = reproject_interp( - hdu_in, header_out, parallel=True, block_size=[0, 4], return_footprint=False - ) - - np.testing.assert_allclose(array_test, array_reference, equal_nan=True, verbose=True) From bc7dda7c24c8ae643dfc1cdbb2600fc63ef76755 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Thu, 6 Oct 2022 14:37:42 +0100 Subject: [PATCH 071/366] Fix tests --- reproject/utils.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/reproject/utils.py b/reproject/utils.py index 63b7dbcd6..83dc154dc 100644 --- a/reproject/utils.py +++ b/reproject/utils.py @@ -224,8 +224,8 @@ def reproject_blocked( scheduler = "processes" if parallel else "synchronous" def reproject_single_block(a, block_info=None): - if a.ndim == 0: - return a + if a.ndim == 0 or block_info is None or block_info == []: + return np.array([a, a]) slices = [slice(*x) for x in block_info[None]["array-location"][1:]] wcs_out_sub = HighLevelWCSWrapper(SlicedLowLevelWCS(wcs_out, slices=slices)) # array, footprint = reproject_func( @@ -238,8 +238,11 @@ def reproject_single_block(a, block_info=None): da.from_array(output_array, chunks=block_size), dtype=float, new_axis=0, + chunks=(2,) + tuple(block_size), ) + output_array_dask = output_array_dask[:, : output_array.shape[0], : output_array.shape[1]] + if return_footprint: da.store( [output_array_dask[0], output_array_dask[1]], @@ -249,5 +252,10 @@ def reproject_single_block(a, block_info=None): ) return output_array, output_footprint else: - da.store(output_array_dask[0], output_array, compute=True, scheduler=scheduler) + da.store( + output_array_dask[0], + output_array, + compute=True, + scheduler=scheduler, + ) return output_array From 6cb31fd862952aac5ab7901b73d03492aed4b727 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Thu, 6 Oct 2022 14:58:18 +0100 Subject: [PATCH 072/366] Remove code to determine chunk size, instead leave this up to dask --- reproject/interpolation/high_level.py | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/reproject/interpolation/high_level.py b/reproject/interpolation/high_level.py index a00daa01c..15e290bec 100644 --- a/reproject/interpolation/high_level.py +++ b/reproject/interpolation/high_level.py @@ -116,19 +116,6 @@ def reproject_interp( # if either of these are not default, it means a blocked method must be used if block_size is not None or parallel is not False: - # if parallel is set but block size isn't, we'll choose - # block size so each thread gets one block each - if parallel is not False and block_size is None: - block_size = list(shape_out) - # each thread gets an equal sized strip of output area to process - block_size[-2] = shape_out[-2] // os.cpu_count() - - # given we have cases where modern system have many cpu cores some sanity clamping is - # to avoid 0 length block sizes when num_cpu_cores is greater than the side of the image - for dim_idx in range(min(len(shape_out), 2)): - if block_size[dim_idx] == 0: - block_size[dim_idx] = shape_out[dim_idx] - return reproject_blocked( _reproject_full, array_in=array_in, From a7b20676833b0064037684b4d9ffeb5510e6e924 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Thu, 6 Oct 2022 14:58:51 +0100 Subject: [PATCH 073/366] Fix more issues --- reproject/utils.py | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/reproject/utils.py b/reproject/utils.py index 83dc154dc..a88ceea96 100644 --- a/reproject/utils.py +++ b/reproject/utils.py @@ -8,6 +8,7 @@ from astropy.wcs import WCS from astropy.wcs.wcsapi import BaseHighLevelWCS, SlicedLowLevelWCS from astropy.wcs.wcsapi.high_level_wcs_wrapper import HighLevelWCSWrapper +from dask.utils import SerializableLock __all__ = [ "parse_input_data", @@ -233,29 +234,36 @@ def reproject_single_block(a, block_info=None): ) return np.array([array, footprint]) - output_array_dask = da.map_blocks( + if block_size is None: + output_array_dask = da.from_array(output_array, chunks="auto") + else: + output_array_dask = da.from_array(output_array, chunks=block_size) + + result = da.map_blocks( reproject_single_block, - da.from_array(output_array, chunks=block_size), + output_array_dask, dtype=float, new_axis=0, - chunks=(2,) + tuple(block_size), + chunks=(2,) + output_array_dask.chunksize, ) - output_array_dask = output_array_dask[:, : output_array.shape[0], : output_array.shape[1]] + result = result[:, : output_array.shape[0], : output_array.shape[1]] if return_footprint: da.store( - [output_array_dask[0], output_array_dask[1]], + [result[0], result[1]], [output_array, output_footprint], compute=True, scheduler=scheduler, + lock=SerializableLock(), ) return output_array, output_footprint else: da.store( - output_array_dask[0], + result[0], output_array, compute=True, scheduler=scheduler, + lock=SerializableLock(), ) return output_array From bdb7d0b3f9a5baf14765fb15dc85f062eb1efe57 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Thu, 6 Oct 2022 15:20:40 +0100 Subject: [PATCH 074/366] Work around issue with da.store() for now --- reproject/utils.py | 35 +++++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/reproject/utils.py b/reproject/utils.py index a88ceea96..481c9590d 100644 --- a/reproject/utils.py +++ b/reproject/utils.py @@ -249,21 +249,28 @@ def reproject_single_block(a, block_info=None): result = result[:, : output_array.shape[0], : output_array.shape[1]] + # FIXME: for some reason, the store() calls below do not work correctly + # and result in output_array and output_footprint being unmodified compared + # to the versions passed in. + if return_footprint: - da.store( - [result[0], result[1]], - [output_array, output_footprint], - compute=True, - scheduler=scheduler, - lock=SerializableLock(), - ) + output_array[:] = result[0].compute(scheduler=scheduler) + output_footprint[:] = result[1].compute(scheduler=scheduler) + # da.store( + # [result[0], result[1]], + # [output_array, output_footprint], + # compute=True, + # scheduler=scheduler, + # lock=SerializableLock(), + # ) return output_array, output_footprint else: - da.store( - result[0], - output_array, - compute=True, - scheduler=scheduler, - lock=SerializableLock(), - ) + output_array[:] = result[0].compute(scheduler=scheduler) + # da.store( + # result[0], + # output_array, + # compute=True, + # scheduler=scheduler, + # lock=SerializableLock(), + # ) return output_array From 60fbe20999f37678d28b874ac9de4f24df1c9558 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Thu, 6 Oct 2022 15:28:38 +0100 Subject: [PATCH 075/366] Added note --- reproject/utils.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/reproject/utils.py b/reproject/utils.py index 481c9590d..07da8773c 100644 --- a/reproject/utils.py +++ b/reproject/utils.py @@ -234,6 +234,8 @@ def reproject_single_block(a, block_info=None): ) return np.array([array, footprint]) + # NOTE: the following array is just used to set up the iteration in map_blocks + # but isn't actually used otherwise - this is deliberate. if block_size is None: output_array_dask = da.from_array(output_array, chunks="auto") else: From f5a884df3d2735850ce11c7b5ada5b3b1207b2b3 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Thu, 6 Oct 2022 15:31:34 +0100 Subject: [PATCH 076/366] Simplify logic Co-authored-by: Stuart Mumford --- reproject/utils.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/reproject/utils.py b/reproject/utils.py index 07da8773c..8fbc0c260 100644 --- a/reproject/utils.py +++ b/reproject/utils.py @@ -236,10 +236,7 @@ def reproject_single_block(a, block_info=None): # NOTE: the following array is just used to set up the iteration in map_blocks # but isn't actually used otherwise - this is deliberate. - if block_size is None: - output_array_dask = da.from_array(output_array, chunks="auto") - else: - output_array_dask = da.from_array(output_array, chunks=block_size) + output_array_dask = da.from_array(output_array, chunks=block_size or "auto") result = da.map_blocks( reproject_single_block, From 07d980699a291cc8bd3a1b595bb51ae844b2198d Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Fri, 28 Oct 2022 09:05:36 +0100 Subject: [PATCH 077/366] Bump minimum required version of numpy to follow NEP 29, bump minimum required scipy, and set minimum required dask version --- setup.cfg | 6 +++--- tox.ini | 5 +++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/setup.cfg b/setup.cfg index bd27151a6..0083e5e29 100644 --- a/setup.cfg +++ b/setup.cfg @@ -17,11 +17,11 @@ packages = find: python_requires = >=3.8 setup_requires = setuptools_scm install_requires = - numpy>=1.17 + numpy>=1.20 astropy>=4.0 astropy-healpix>=0.6 - scipy>=1.3 - dask[array] + scipy>=1.5 + dask[array]>=2020.* [options.extras_require] test = diff --git a/tox.ini b/tox.ini index e448c4d0a..8ed4136d2 100644 --- a/tox.ini +++ b/tox.ini @@ -20,10 +20,11 @@ changedir = deps = numpy121: numpy==1.21.* - oldestdeps: numpy==1.17.3 + oldestdeps: numpy==1.20.* oldestdeps: astropy==4.0.* oldestdeps: astropy-healpix==0.6 - oldestdeps: scipy==1.3.2 + oldestdeps: scipy==1.5.* + oldestdeps: dask==2020.12.* devdeps: numpy>=0.0.dev0 devdeps: scipy>=0.0.dev0 From 7d3492b98a4e11704398032f29431372c709a771 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Fri, 28 Oct 2022 09:15:38 +0100 Subject: [PATCH 078/366] Added cloudpickle as a dependency --- setup.cfg | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.cfg b/setup.cfg index 0083e5e29..f6b2ca89d 100644 --- a/setup.cfg +++ b/setup.cfg @@ -22,6 +22,7 @@ install_requires = astropy-healpix>=0.6 scipy>=1.5 dask[array]>=2020.* + cloudpickle [options.extras_require] test = From 1f5efbadd0a297a74f2da0f9ebdb2eb158828f6b Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Mon, 27 Feb 2023 14:53:45 +0000 Subject: [PATCH 079/366] Fixed syntax in setup.cfg --- setup.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index f6b2ca89d..afb9c08c7 100644 --- a/setup.cfg +++ b/setup.cfg @@ -21,7 +21,7 @@ install_requires = astropy>=4.0 astropy-healpix>=0.6 scipy>=1.5 - dask[array]>=2020.* + dask[array]>=2020 cloudpickle [options.extras_require] From de9474224015efbeeab2c936ad39a55518a36dfe Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Mon, 27 Feb 2023 23:58:29 +0000 Subject: [PATCH 080/366] Fix blocked reprojection with extra broadcast dimensions --- reproject/utils.py | 66 ++++++++++++++++++++++------------------------ 1 file changed, 31 insertions(+), 35 deletions(-) diff --git a/reproject/utils.py b/reproject/utils.py index 8fbc0c260..daa750619 100644 --- a/reproject/utils.py +++ b/reproject/utils.py @@ -1,6 +1,8 @@ +import tempfile from concurrent import futures import astropy.nddata +import dask import dask.array as da import numpy as np from astropy.io import fits @@ -194,7 +196,7 @@ def reproject_blocked( Passed to reproject_func() alongside WCS out to determine image size wcs_out: `~astropy.wcs.WCS` Output WCS image will be projected to. Normally will correspond to subset of - total output image when used by repoject_blocked() + total output image when used by reproject_blocked() block_size: tuple The size of blocks in terms of output array pixels that each block will handle reprojecting. Extending out from (0,0) coords positively, block sizes @@ -217,17 +219,14 @@ def reproject_blocked( greater than one, a parallel implementation using ``n`` processes is chosen. """ - if output_array is None: - output_array = np.zeros(shape_out, dtype=float) - if output_footprint is None and return_footprint: - output_footprint = np.zeros(shape_out, dtype=float) - - scheduler = "processes" if parallel else "synchronous" + if len(block_size) < len(shape_out): + block_size = [-1] * (len(shape_out) - len(block_size)) + list(block_size) def reproject_single_block(a, block_info=None): if a.ndim == 0 or block_info is None or block_info == []: return np.array([a, a]) - slices = [slice(*x) for x in block_info[None]["array-location"][1:]] + print(block_info[None]["array-location"][-wcs_out.pixel_n_dim :]) + slices = [slice(*x) for x in block_info[None]["array-location"][-wcs_out.pixel_n_dim :]] wcs_out_sub = HighLevelWCSWrapper(SlicedLowLevelWCS(wcs_out, slices=slices)) # array, footprint = reproject_func( array_in, wcs_in, wcs_out_sub, block_info[None]["chunk-shape"][1:] @@ -236,7 +235,7 @@ def reproject_single_block(a, block_info=None): # NOTE: the following array is just used to set up the iteration in map_blocks # but isn't actually used otherwise - this is deliberate. - output_array_dask = da.from_array(output_array, chunks=block_size or "auto") + output_array_dask = da.empty(shape_out, chunks=block_size or "auto") result = da.map_blocks( reproject_single_block, @@ -246,30 +245,27 @@ def reproject_single_block(a, block_info=None): chunks=(2,) + output_array_dask.chunksize, ) - result = result[:, : output_array.shape[0], : output_array.shape[1]] - - # FIXME: for some reason, the store() calls below do not work correctly - # and result in output_array and output_footprint being unmodified compared - # to the versions passed in. + # Truncate extra elements + result = result[tuple([slice(None)] + [slice(s) for s in shape_out])] - if return_footprint: - output_array[:] = result[0].compute(scheduler=scheduler) - output_footprint[:] = result[1].compute(scheduler=scheduler) - # da.store( - # [result[0], result[1]], - # [output_array, output_footprint], - # compute=True, - # scheduler=scheduler, - # lock=SerializableLock(), - # ) - return output_array, output_footprint - else: - output_array[:] = result[0].compute(scheduler=scheduler) - # da.store( - # result[0], - # output_array, - # compute=True, - # scheduler=scheduler, - # lock=SerializableLock(), - # ) - return output_array + with dask.config.set(scheduler="processes" if parallel else "synchronous"): + if output_array is None: + output_array = result[0].compute() + else: + filename = tempfile.mktemp() + result[0].to_zarr(filename) + print(filename) + # TODO: load back into array - for now computing into memory + output_array = result[0].compute() + if return_footprint: + if output_footprint is None: + output_footprint = result[1].compute() + else: + filename = tempfile.mktemp() + result[1].to_zarr(filename) + print(filename) + # TODO: load back into array - for now computing into memory + output_footprint = result[1].compute() + return output_array, output_footprint + else: + return output_array From 9513323af36df990c889be51c730d1e36e60f4c8 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Tue, 28 Feb 2023 14:07:05 +0000 Subject: [PATCH 081/366] Expand test suite and fix all tests --- reproject/interpolation/core.py | 7 ++- reproject/interpolation/high_level.py | 1 + reproject/interpolation/tests/test_core.py | 71 +++++++++++++++------- reproject/utils.py | 60 +++++++++++------- 4 files changed, 94 insertions(+), 45 deletions(-) diff --git a/reproject/interpolation/core.py b/reproject/interpolation/core.py index 63e2decd0..7fe3a0a9a 100644 --- a/reproject/interpolation/core.py +++ b/reproject/interpolation/core.py @@ -74,6 +74,7 @@ def _reproject_full( array_out=None, return_footprint=True, roundtrip_coords=True, + output_footprint=None, ): """ Reproject n-dimensional data to a new projection using interpolation. @@ -100,6 +101,9 @@ def _reproject_full( if array_out is None: array_out = np.empty(shape_out) + if output_footprint is None: + output_footprint = np.empty(shape_out) + array_out_loopable = array_out if len(array.shape) == wcs_in.low_level_wcs.pixel_n_dim: # We don't need to broadcast the transformation over any extra @@ -149,6 +153,7 @@ def _reproject_full( # also contains this data and has the user's desired output shape. if return_footprint: - return array_out, (~np.isnan(array_out)).astype(float) + output_footprint[:] = (~np.isnan(array_out)).astype(float) + return array_out, output_footprint else: return array_out diff --git a/reproject/interpolation/high_level.py b/reproject/interpolation/high_level.py index 15e290bec..e3bcebae3 100644 --- a/reproject/interpolation/high_level.py +++ b/reproject/interpolation/high_level.py @@ -138,4 +138,5 @@ def reproject_interp( array_out=output_array, return_footprint=return_footprint, roundtrip_coords=roundtrip_coords, + output_footprint=output_footprint, ) diff --git a/reproject/interpolation/tests/test_core.py b/reproject/interpolation/tests/test_core.py index 1cc6dc03e..d1b471d25 100644 --- a/reproject/interpolation/tests/test_core.py +++ b/reproject/interpolation/tests/test_core.py @@ -718,9 +718,11 @@ def test_blocked_broadcast_reprojection(input_extra_dims, output_shape, parallel @pytest.mark.parametrize("parallel", [True, 2, False]) -@pytest.mark.parametrize("block_size", [[40, 40], [500, 500], [500, 100], None]) +@pytest.mark.parametrize("block_size", [[500, 500], [500, 100], None]) +@pytest.mark.parametrize("return_footprint", [False, True]) +@pytest.mark.parametrize("existing_outputs", [False, True]) @pytest.mark.remote_data -def test_blocked_against_single(parallel, block_size): +def test_blocked_against_single(tmp_path, parallel, block_size, return_footprint, existing_outputs): # Ensure when we break a reprojection down into multiple discrete blocks # it has the same result as if all pixels where reprejcted at once @@ -729,6 +731,19 @@ def test_blocked_against_single(parallel, block_size): array_test = None footprint_test = None + shape_out = (720, 721) + + if existing_outputs: + output_array_test = np.zeros(shape_out) + output_footprint_test = np.zeros(shape_out) + output_array_reference = np.zeros(shape_out) + output_footprint_reference = np.zeros(shape_out) + else: + output_array_test = None + output_footprint_test = None + output_array_reference = None + output_footprint_reference = None + # the warning import and ignore is needed to keep pytest happy when running with # older versions of astropy which don't have this fix: # https://github.com/astropy/astropy/pull/12844 @@ -738,27 +753,41 @@ def test_blocked_against_single(parallel, block_size): with warnings.catch_warnings(): warnings.simplefilter("ignore", category=FITSFixedWarning) - - # this one is needed to avoid the following warning from when the np.as_strided() is - # called in wcs_utils.unbroadcast(), only shows up with py3.8, numpy1.17, astropy 4.0.*: - # DeprecationWarning: Numpy has detected that you (may be) writing to an array with - # overlapping memory from np.broadcast_arrays. If this is intentional - # set the WRITEABLE flag True or make a copy immediately before writing. - # We do call as_strided with writeable=True as it recommends and only shows up with the 10px - # testcase so assuming a numpy bug in the detection code which was fixed in later version. - # The pixel values all still match in the end, only shows up due to pytest clearing - # the standard python warning filters by default and failing as the warnings are now - # treated as the exceptions they're implemented on - if block_size == [10, 10]: - warnings.simplefilter("ignore", category=DeprecationWarning) - - array_test, footprint_test = reproject_interp( - hdu2, hdu1.header, parallel=parallel, block_size=block_size + result_test = reproject_interp( + hdu2, + hdu1.header, + parallel=parallel, + block_size=block_size, + return_footprint=return_footprint, + output_array=output_array_test, + output_footprint=output_footprint_test, ) - array_reference, footprint_reference = reproject_interp( - hdu2, hdu1.header, parallel=False, block_size=None + result_reference = reproject_interp( + hdu2, + hdu1.header, + parallel=False, + block_size=None, + return_footprint=return_footprint, + output_array=output_array_reference, + output_footprint=output_footprint_reference, ) + if return_footprint: + array_test, footprint_test = result_test + array_reference, footprint_reference = result_reference + else: + array_test = result_test + array_reference = result_reference + + if existing_outputs: + assert array_test is output_array_test + assert array_reference is output_array_reference + if return_footprint: + assert footprint_test is output_footprint_test + assert footprint_reference is output_footprint_reference + + np.testing.assert_allclose(array_test, array_reference, equal_nan=True) - np.testing.assert_allclose(footprint_test, footprint_reference, equal_nan=True) + if return_footprint: + np.testing.assert_allclose(footprint_test, footprint_reference, equal_nan=True) diff --git a/reproject/utils.py b/reproject/utils.py index daa750619..581e4a63c 100644 --- a/reproject/utils.py +++ b/reproject/utils.py @@ -219,13 +219,17 @@ def reproject_blocked( greater than one, a parallel implementation using ``n`` processes is chosen. """ - if len(block_size) < len(shape_out): + if output_array is None: + output_array = np.zeros(shape_out, dtype=float) + if output_footprint is None and return_footprint: + output_footprint = np.zeros(shape_out, dtype=float) + + if block_size is not None and len(block_size) < len(shape_out): block_size = [-1] * (len(shape_out) - len(block_size)) + list(block_size) def reproject_single_block(a, block_info=None): if a.ndim == 0 or block_info is None or block_info == []: return np.array([a, a]) - print(block_info[None]["array-location"][-wcs_out.pixel_n_dim :]) slices = [slice(*x) for x in block_info[None]["array-location"][-wcs_out.pixel_n_dim :]] wcs_out_sub = HighLevelWCSWrapper(SlicedLowLevelWCS(wcs_out, slices=slices)) # array, footprint = reproject_func( @@ -248,24 +252,34 @@ def reproject_single_block(a, block_info=None): # Truncate extra elements result = result[tuple([slice(None)] + [slice(s) for s in shape_out])] - with dask.config.set(scheduler="processes" if parallel else "synchronous"): - if output_array is None: - output_array = result[0].compute() - else: - filename = tempfile.mktemp() - result[0].to_zarr(filename) - print(filename) - # TODO: load back into array - for now computing into memory - output_array = result[0].compute() - if return_footprint: - if output_footprint is None: - output_footprint = result[1].compute() - else: - filename = tempfile.mktemp() - result[1].to_zarr(filename) - print(filename) - # TODO: load back into array - for now computing into memory - output_footprint = result[1].compute() - return output_array, output_footprint - else: - return output_array + scheduler = "processes" if parallel else "synchronous" + + if scheduler == 'processes': + # As discussed in https://github.com/dask/dask/issues/9556, da.store + # will not work well in multiprocessing mode when the destination is a + # Numpy array. Instead, in this case we save the dask array to a zarr + # array on disk which can be done in parallel, and re-load it as a dask + # array. We can then use da.store in the next step using the + # 'synchronous' scheduler since that is I/O limited so does not need + # to be done in parallel. + filename = tempfile.mktemp() + with dask.config.set(scheduler="processes"): + result.to_zarr(filename) + result = da.from_zarr(filename) + + if return_footprint: + da.store( + [result[0], result[1]], + [output_array, output_footprint], + compute=True, + scheduler='synchronous', + ) + return output_array, output_footprint + else: + da.store( + result[0], + output_array, + compute=True, + scheduler='synchronous', + ) + return output_array From d40768c9b776073bb7911496e45d065614b03239 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Tue, 28 Feb 2023 14:11:33 +0000 Subject: [PATCH 082/366] Recognize number of workers --- reproject/utils.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/reproject/utils.py b/reproject/utils.py index 581e4a63c..4749f618b 100644 --- a/reproject/utils.py +++ b/reproject/utils.py @@ -252,9 +252,7 @@ def reproject_single_block(a, block_info=None): # Truncate extra elements result = result[tuple([slice(None)] + [slice(s) for s in shape_out])] - scheduler = "processes" if parallel else "synchronous" - - if scheduler == 'processes': + if parallel: # As discussed in https://github.com/dask/dask/issues/9556, da.store # will not work well in multiprocessing mode when the destination is a # Numpy array. Instead, in this case we save the dask array to a zarr @@ -263,7 +261,11 @@ def reproject_single_block(a, block_info=None): # 'synchronous' scheduler since that is I/O limited so does not need # to be done in parallel. filename = tempfile.mktemp() - with dask.config.set(scheduler="processes"): + if isinstance(parallel, int): + workers = {'num_workers': parallel} + else: + workers = {} + with dask.config.set(scheduler="processes", **workers): result.to_zarr(filename) result = da.from_zarr(filename) From 669741e50127ff7a92412ac964c79b786199b0e3 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Tue, 28 Feb 2023 15:11:39 +0000 Subject: [PATCH 083/366] Pass input array to processes via memmap --- reproject/utils.py | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/reproject/utils.py b/reproject/utils.py index 4749f618b..d4df4a9f8 100644 --- a/reproject/utils.py +++ b/reproject/utils.py @@ -227,11 +227,22 @@ def reproject_blocked( if block_size is not None and len(block_size) < len(shape_out): block_size = [-1] * (len(shape_out) - len(block_size)) + list(block_size) + shape_in = array_in.shape + + # When in parallel mode, we want to make sure we avoid having to copy the + # input array to all processes for each chunk, so instead we write out + # the input array to a Numpy memory map and load it in inside each process + # as a memory-mapped array. + array_in_file = tempfile.mktemp() + array_in_memmapped = np.memmap(array_in_file, dtype=float, shape=array_in.shape, mode="w+") + array_in_memmapped[:] = array_in[:] + def reproject_single_block(a, block_info=None): if a.ndim == 0 or block_info is None or block_info == []: return np.array([a, a]) slices = [slice(*x) for x in block_info[None]["array-location"][-wcs_out.pixel_n_dim :]] - wcs_out_sub = HighLevelWCSWrapper(SlicedLowLevelWCS(wcs_out, slices=slices)) # + wcs_out_sub = HighLevelWCSWrapper(SlicedLowLevelWCS(wcs_out, slices=slices)) + array_in = np.memmap(array_in_file, dtype=float, shape=shape_in) array, footprint = reproject_func( array_in, wcs_in, wcs_out_sub, block_info[None]["chunk-shape"][1:] ) @@ -262,7 +273,7 @@ def reproject_single_block(a, block_info=None): # to be done in parallel. filename = tempfile.mktemp() if isinstance(parallel, int): - workers = {'num_workers': parallel} + workers = {"num_workers": parallel} else: workers = {} with dask.config.set(scheduler="processes", **workers): @@ -274,7 +285,7 @@ def reproject_single_block(a, block_info=None): [result[0], result[1]], [output_array, output_footprint], compute=True, - scheduler='synchronous', + scheduler="synchronous", ) return output_array, output_footprint else: @@ -282,6 +293,6 @@ def reproject_single_block(a, block_info=None): result[0], output_array, compute=True, - scheduler='synchronous', + scheduler="synchronous", ) return output_array From 76f4d7d144e575496ed26a04f2d3ff25ff23b6e4 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Tue, 28 Feb 2023 15:21:18 +0000 Subject: [PATCH 084/366] Clean up code --- reproject/utils.py | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/reproject/utils.py b/reproject/utils.py index d4df4a9f8..cadfd6ccc 100644 --- a/reproject/utils.py +++ b/reproject/utils.py @@ -232,17 +232,27 @@ def reproject_blocked( # When in parallel mode, we want to make sure we avoid having to copy the # input array to all processes for each chunk, so instead we write out # the input array to a Numpy memory map and load it in inside each process - # as a memory-mapped array. - array_in_file = tempfile.mktemp() - array_in_memmapped = np.memmap(array_in_file, dtype=float, shape=array_in.shape, mode="w+") - array_in_memmapped[:] = array_in[:] + # as a memory-mapped array. We need to be careful how this gets passed to + # reproject_single_block so we pass a variable that can be either a string + # or the array itself (for synchronous mode). + if parallel: + array_in_or_path = tempfile.mktemp() + array_in_memmapped = np.memmap( + array_in_or_path, dtype=float, shape=array_in.shape, mode="w+" + ) + array_in_memmapped[:] = array_in[:] + else: + array_in_or_path = array_in def reproject_single_block(a, block_info=None): if a.ndim == 0 or block_info is None or block_info == []: return np.array([a, a]) slices = [slice(*x) for x in block_info[None]["array-location"][-wcs_out.pixel_n_dim :]] wcs_out_sub = HighLevelWCSWrapper(SlicedLowLevelWCS(wcs_out, slices=slices)) - array_in = np.memmap(array_in_file, dtype=float, shape=shape_in) + if isinstance(array_in_or_path, str): + array_in = np.memmap(array_in_or_path, dtype=float, shape=shape_in) + else: + array_in = array_in_or_path array, footprint = reproject_func( array_in, wcs_in, wcs_out_sub, block_info[None]["chunk-shape"][1:] ) From 8704d7704159afd377f1577b720c58071d2ad46b Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Tue, 28 Feb 2023 15:36:29 +0000 Subject: [PATCH 085/366] Add zarr and fsspec to dependencies --- setup.cfg | 2 ++ 1 file changed, 2 insertions(+) diff --git a/setup.cfg b/setup.cfg index afb9c08c7..62f11003d 100644 --- a/setup.cfg +++ b/setup.cfg @@ -23,6 +23,8 @@ install_requires = scipy>=1.5 dask[array]>=2020 cloudpickle + zarr + fsspec [options.extras_require] test = From 13cc3d20ba86a011a6e2b156fe4a177fd1c4fd20 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Tue, 28 Feb 2023 15:36:52 +0000 Subject: [PATCH 086/366] Make reproject_blocked private --- reproject/interpolation/high_level.py | 4 ++-- reproject/utils.py | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/reproject/interpolation/high_level.py b/reproject/interpolation/high_level.py index e3bcebae3..dfd91bddb 100644 --- a/reproject/interpolation/high_level.py +++ b/reproject/interpolation/high_level.py @@ -3,7 +3,7 @@ from astropy.utils import deprecated_renamed_argument -from ..utils import parse_input_data, parse_output_projection, reproject_blocked +from ..utils import _reproject_blocked, parse_input_data, parse_output_projection from .core import _reproject_full __all__ = ["reproject_interp"] @@ -116,7 +116,7 @@ def reproject_interp( # if either of these are not default, it means a blocked method must be used if block_size is not None or parallel is not False: - return reproject_blocked( + return _reproject_blocked( _reproject_full, array_in=array_in, wcs_in=wcs_in, diff --git a/reproject/utils.py b/reproject/utils.py index cadfd6ccc..7e4d4b74b 100644 --- a/reproject/utils.py +++ b/reproject/utils.py @@ -166,7 +166,7 @@ def parse_output_projection(output_projection, shape_in=None, shape_out=None, ou return wcs_out, shape_out -def reproject_blocked( +def _reproject_blocked( reproject_func, array_in, wcs_in, @@ -189,14 +189,14 @@ def reproject_blocked( that that will be used be used to perform reprojection array_in Data following the same format as expected by underlying reproject_func, - expected to `~numpy.ndarray` when used from reproject_blocked() + expected to `~numpy.ndarray` when used from _reproject_blocked() wcs_in: `~astropy.wcs.WCS` WCS object corresponding to array_in shape_out: tuple Passed to reproject_func() alongside WCS out to determine image size wcs_out: `~astropy.wcs.WCS` Output WCS image will be projected to. Normally will correspond to subset of - total output image when used by reproject_blocked() + total output image when used by _reproject_blocked() block_size: tuple The size of blocks in terms of output array pixels that each block will handle reprojecting. Extending out from (0,0) coords positively, block sizes From 8615e483ddb032820354fe5742fcb61424df84ee Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Tue, 28 Feb 2023 15:37:49 +0000 Subject: [PATCH 087/366] Fix codestyle --- reproject/interpolation/tests/test_core.py | 1 - 1 file changed, 1 deletion(-) diff --git a/reproject/interpolation/tests/test_core.py b/reproject/interpolation/tests/test_core.py index d1b471d25..d4b3726b6 100644 --- a/reproject/interpolation/tests/test_core.py +++ b/reproject/interpolation/tests/test_core.py @@ -787,7 +787,6 @@ def test_blocked_against_single(tmp_path, parallel, block_size, return_footprint assert footprint_test is output_footprint_test assert footprint_reference is output_footprint_reference - np.testing.assert_allclose(array_test, array_reference, equal_nan=True) if return_footprint: np.testing.assert_allclose(footprint_test, footprint_reference, equal_nan=True) From ad5c9329631e439a0d574fdf30c55693f2dc2261 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Tue, 28 Feb 2023 16:51:50 +0000 Subject: [PATCH 088/366] Fix typo and remove unused fixture --- reproject/interpolation/tests/test_core.py | 2 +- reproject/utils.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/reproject/interpolation/tests/test_core.py b/reproject/interpolation/tests/test_core.py index d4b3726b6..69db845db 100644 --- a/reproject/interpolation/tests/test_core.py +++ b/reproject/interpolation/tests/test_core.py @@ -722,7 +722,7 @@ def test_blocked_broadcast_reprojection(input_extra_dims, output_shape, parallel @pytest.mark.parametrize("return_footprint", [False, True]) @pytest.mark.parametrize("existing_outputs", [False, True]) @pytest.mark.remote_data -def test_blocked_against_single(tmp_path, parallel, block_size, return_footprint, existing_outputs): +def test_blocked_against_single(parallel, block_size, return_footprint, existing_outputs): # Ensure when we break a reprojection down into multiple discrete blocks # it has the same result as if all pixels where reprejcted at once diff --git a/reproject/utils.py b/reproject/utils.py index 7e4d4b74b..eaeba857e 100644 --- a/reproject/utils.py +++ b/reproject/utils.py @@ -179,7 +179,7 @@ def _reproject_blocked( parallel=True, ): """ - Implementaton function that handles reprojecting subsets blocks of pixels + Implementation function that handles reprojecting subsets blocks of pixels from an input image and holds metadata about where to reinsert when done. Parameters From ece1b055866e58983976429791f8fa0d2a859fc2 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Thu, 2 Mar 2023 10:34:16 +0000 Subject: [PATCH 089/366] Improve test coverage for input types for find_optimal_celestial_wcs and add ability to pass in hdu_in for FITS files and HDULists --- reproject/adaptive/high_level.py | 3 + reproject/interpolation/high_level.py | 3 + .../mosaicking/tests/test_wcs_helpers.py | 66 +++++++++++++++++++ reproject/mosaicking/wcs_helpers.py | 38 ++++++++++- reproject/spherical_intersect/high_level.py | 3 + reproject/utils.py | 3 +- 6 files changed, 113 insertions(+), 3 deletions(-) diff --git a/reproject/adaptive/high_level.py b/reproject/adaptive/high_level.py index 7470805ec..cca0fd050 100644 --- a/reproject/adaptive/high_level.py +++ b/reproject/adaptive/high_level.py @@ -41,6 +41,9 @@ def reproject_adaptive( * An image HDU object such as a `~astropy.io.fits.PrimaryHDU`, `~astropy.io.fits.ImageHDU`, or `~astropy.io.fits.CompImageHDU` instance + * A tuple where the first element is an Numpy array shape tuple + the second element is either a `~astropy.wcs.WCS` or a + `~astropy.io.fits.Header` object * A tuple where the first element is a `~numpy.ndarray` and the second element is either a `~astropy.wcs.WCS` or a `~astropy.io.fits.Header` object diff --git a/reproject/interpolation/high_level.py b/reproject/interpolation/high_level.py index dfd91bddb..cd665f994 100644 --- a/reproject/interpolation/high_level.py +++ b/reproject/interpolation/high_level.py @@ -44,6 +44,9 @@ def reproject_interp( * An image HDU object such as a `~astropy.io.fits.PrimaryHDU`, `~astropy.io.fits.ImageHDU`, or `~astropy.io.fits.CompImageHDU` instance + * A tuple where the first element is an Numpy array shape tuple + the second element is either a `~astropy.wcs.WCS` or a + `~astropy.io.fits.Header` object * A tuple where the first element is a `~numpy.ndarray` and the second element is either a `~astropy.wcs.WCS` or a `~astropy.io.fits.Header` object diff --git a/reproject/mosaicking/tests/test_wcs_helpers.py b/reproject/mosaicking/tests/test_wcs_helpers.py index b279b6bef..bcab67a01 100644 --- a/reproject/mosaicking/tests/test_wcs_helpers.py +++ b/reproject/mosaicking/tests/test_wcs_helpers.py @@ -4,6 +4,8 @@ import pytest from astropy import units as u from astropy.coordinates import FK5, Galactic, SkyCoord +from astropy.io import fits +from astropy.nddata import NDData from astropy.wcs import WCS from astropy.wcs.wcsapi import HighLevelWCSWrapper from numpy.testing import assert_allclose, assert_equal @@ -170,6 +172,67 @@ def test_invalid_not_celestial(self): wcs, shape = find_optimal_celestial_wcs([(self.array, self.wcs)]) assert exc.value.args[0] == "WCS does not have celestial components" + @pytest.mark.parametrize("iterable", [False, True]) + @pytest.mark.parametrize( + "input_type", + [ + "filename", + "path", + "hdulist", + "primary_hdu", + "image_hdu", + "comp_image_hdu", + "shape_wcs_tuple", + "data_wcs_tuple", + "nddata", + ], + ) + def test_input_types(self, tmp_path, iterable, input_type): + # Test different kinds of inputs and check the result is always the same + + # Reference + wcs_ref, shape_ref = find_optimal_celestial_wcs([(self.array, self.wcs)], frame=FK5()) + + # Test + + hdulist = fits.HDUList( + [ + fits.PrimaryHDU(self.array, self.wcs.to_header()), + fits.ImageHDU(self.array, self.wcs.to_header()), + fits.CompImageHDU(self.array, self.wcs.to_header()), + ] + ) + + hdu_in = None + + if input_type in ["filename", "path"]: + input_value = tmp_path / "test.fits" + if input_type == "filename": + input_value = str(input_value) + hdulist.writeto(input_value) + hdu_in = 0 + elif input_type == "hdulist": + input_value = hdulist + hdu_in = 1 + elif input_type == "primary_hdu": + input_value = hdulist[0] + elif input_type == "image_hdu": + input_value = hdulist[1] + elif input_type == "comp_image_hdu": + input_value = hdulist[2] + elif input_type == "shape_wcs_tuple": + input_value = (self.array.shape, self.wcs) + elif input_type == "data_wcs_tuple": + input_value = (self.array, self.wcs) + elif input_type == "nddata": + input_value = NDData(data=self.array, wcs=self.wcs) + + if iterable: + input_value = [input_value] + + wcs_test, shape_test = find_optimal_celestial_wcs(input_value, frame=FK5(), hdu_in=hdu_in) + assert wcs_test.to_header() == wcs_ref.to_header() and shape_test == shape_ref + class TestOptimalFITSWCS(BaseTestOptimalWCS): def generate_wcs( @@ -213,6 +276,9 @@ def generate_wcs( def test_args_tuple_header(self): pytest.skip() + def test_input_types(self): + pytest.skip() + crval_atol = 1.5 crpix_atol = 1e-6 cdelt_rtol = 1.0e-3 diff --git a/reproject/mosaicking/wcs_helpers.py b/reproject/mosaicking/wcs_helpers.py index 824dc342c..951acb065 100644 --- a/reproject/mosaicking/wcs_helpers.py +++ b/reproject/mosaicking/wcs_helpers.py @@ -3,6 +3,8 @@ import numpy as np from astropy import units as u from astropy.coordinates import SkyCoord, frame_transform_graph +from astropy.io.fits import Header +from astropy.utils import isiterable from astropy.wcs import WCS from astropy.wcs.utils import ( celestial_frame_to_wcs, @@ -18,7 +20,13 @@ def find_optimal_celestial_wcs( - input_data, frame=None, auto_rotate=False, projection="TAN", resolution=None, reference=None + input_data, + hdu_in=None, + frame=None, + auto_rotate=False, + projection="TAN", + resolution=None, + reference=None, ): """ Given one or more images, return an optimal WCS projection object and @@ -44,7 +52,15 @@ def find_optimal_celestial_wcs( * A tuple where the first element is a `~numpy.ndarray` and the second element is either a `~astropy.wcs.WCS` or a `~astropy.io.fits.Header` object + * An `~astropy.nddata.NDData` object from which the ``.data`` and + ``.wcs`` attributes will be used as the input data. + If only one input data needs to be provided, it is also possible to + pass it in without including it in an iterable. + + hdu_in : int or str, optional + If ``input_data`` is a FITS file or an `~astropy.io.fits.HDUList` + instance, specifies the HDU to use. frame : str or `~astropy.coordinates.BaseCoordinateFrame` The coordinate system for the final image (defaults to the frame of the first image specified) @@ -75,7 +91,25 @@ def find_optimal_celestial_wcs( if isinstance(frame, str): frame = frame_transform_graph.lookup_name(frame)() - input_shapes = [parse_input_shape(shape) for shape in input_data] + # Determine whether an iterable of input values was given or a single + # input data. + + if isinstance(input_data, str): + # Handle this explicitly as isiterable(str) is True + iterable = False + elif isiterable(input_data): + if len(input_data) == 2 and isinstance(input_data[1], (WCS, Header)): + # Since 2-element tuples are valid single inputs we need to check for this + iterable = False + else: + iterable = True + else: + iterable = False + + if iterable: + input_shapes = [parse_input_shape(shape, hdu_in=hdu_in) for shape in input_data] + else: + input_shapes = [parse_input_shape(input_data, hdu_in=hdu_in)] # We start off by looping over images, checking that they are indeed # celestial images, and building up a list of all corners and all reference diff --git a/reproject/spherical_intersect/high_level.py b/reproject/spherical_intersect/high_level.py index 17e09798e..4837d3993 100644 --- a/reproject/spherical_intersect/high_level.py +++ b/reproject/spherical_intersect/high_level.py @@ -24,6 +24,9 @@ def reproject_exact( * An image HDU object such as a `~astropy.io.fits.PrimaryHDU`, `~astropy.io.fits.ImageHDU`, or `~astropy.io.fits.CompImageHDU` instance + * A tuple where the first element is an Numpy array shape tuple + the second element is either a `~astropy.wcs.WCS` or a + `~astropy.io.fits.Header` object * A tuple where the first element is a `~numpy.ndarray` and the second element is either a `~astropy.wcs.WCS` or a `~astropy.io.fits.Header` object diff --git a/reproject/utils.py b/reproject/utils.py index eaeba857e..cf6ae233d 100644 --- a/reproject/utils.py +++ b/reproject/utils.py @@ -1,4 +1,5 @@ import tempfile +from pathlib import Path from concurrent import futures import astropy.nddata @@ -59,7 +60,7 @@ def parse_input_shape(input_shape, hdu_in=None): Parse input shape information to return an array shape tuple and WCS object. """ - if isinstance(input_shape, str): + if isinstance(input_shape, (str, Path)): return parse_input_shape(fits.open(input_shape), hdu_in=hdu_in) elif isinstance(input_shape, HDUList): if hdu_in is None: From 1b6216265bc2d55225ae12bcf7df6e831cc2963f Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Thu, 2 Mar 2023 11:17:47 +0000 Subject: [PATCH 090/366] Updated minimum version of astropy --- docs/installation.rst | 13 ++++--------- setup.cfg | 2 +- tox.ini | 2 +- 3 files changed, 6 insertions(+), 11 deletions(-) diff --git a/docs/installation.rst b/docs/installation.rst index fefcb77ef..317c564a5 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -9,13 +9,13 @@ Requirements This package has the following hard run time dependencies: -* `Python `__ 3.7 or later +* `Python `__ 3.8 or later -* `Numpy `__ 1.14 or later +* `Numpy `__ 1.20 or later -* `Astropy `__ 3.2 or later +* `Astropy `__ 5.0 or later -* `Scipy `__ 1.1 or later +* `Scipy `__ 1.5 or later * `astropy-healpix `_ 0.6 or later for HEALPIX image reprojection @@ -23,11 +23,6 @@ and the following optional dependencies: * `shapely `_ 1.6 or later for some of the mosaicking functionality -If you build the package from the source, the following additional packages -are required: - -* `Cython `__ - and to run the tests, you will also need: * `Matplotlib `__ diff --git a/setup.cfg b/setup.cfg index 62f11003d..2421b615f 100644 --- a/setup.cfg +++ b/setup.cfg @@ -18,7 +18,7 @@ python_requires = >=3.8 setup_requires = setuptools_scm install_requires = numpy>=1.20 - astropy>=4.0 + astropy>=5.0 astropy-healpix>=0.6 scipy>=1.5 dask[array]>=2020 diff --git a/tox.ini b/tox.ini index 8ed4136d2..11cb9536f 100644 --- a/tox.ini +++ b/tox.ini @@ -21,7 +21,7 @@ deps = numpy121: numpy==1.21.* oldestdeps: numpy==1.20.* - oldestdeps: astropy==4.0.* + oldestdeps: astropy==5.0.* oldestdeps: astropy-healpix==0.6 oldestdeps: scipy==1.5.* oldestdeps: dask==2020.12.* From ebe141aea0cc317a3a4ca25bd9dbfbde55d19701 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Thu, 2 Mar 2023 13:11:40 +0000 Subject: [PATCH 091/366] Don't do exact comparison for header --- reproject/mosaicking/tests/test_wcs_helpers.py | 7 ++++++- reproject/tests/helpers.py | 11 +++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/reproject/mosaicking/tests/test_wcs_helpers.py b/reproject/mosaicking/tests/test_wcs_helpers.py index bcab67a01..a25aa6040 100644 --- a/reproject/mosaicking/tests/test_wcs_helpers.py +++ b/reproject/mosaicking/tests/test_wcs_helpers.py @@ -10,6 +10,8 @@ from astropy.wcs.wcsapi import HighLevelWCSWrapper from numpy.testing import assert_allclose, assert_equal +from reproject.tests.helpers import assert_header_allclose + from ..wcs_helpers import find_optimal_celestial_wcs try: @@ -231,7 +233,10 @@ def test_input_types(self, tmp_path, iterable, input_type): input_value = [input_value] wcs_test, shape_test = find_optimal_celestial_wcs(input_value, frame=FK5(), hdu_in=hdu_in) - assert wcs_test.to_header() == wcs_ref.to_header() and shape_test == shape_ref + + assert_header_allclose(wcs_test.to_header(), wcs_ref.to_header()) + + assert shape_test == shape_ref class TestOptimalFITSWCS(BaseTestOptimalWCS): diff --git a/reproject/tests/helpers.py b/reproject/tests/helpers.py index 19f9cce73..ae128cc8c 100644 --- a/reproject/tests/helpers.py +++ b/reproject/tests/helpers.py @@ -1,4 +1,5 @@ from astropy.io import fits +from numpy.testing import assert_allclose def array_footprint_to_hdulist(array, footprint, header): @@ -6,3 +7,13 @@ def array_footprint_to_hdulist(array, footprint, header): hdulist.append(fits.PrimaryHDU(array, header)) hdulist.append(fits.ImageHDU(footprint, header, name="footprint")) return hdulist + + +def assert_header_allclose(header1, header2, **kwargs): + assert sorted(header1) == sorted(header2) + + for key1, value1 in header1.items(): + if isinstance(value1, str): + assert value1 == header2[key1] + else: + assert_allclose(value1, header2[key1], **kwargs) From f91a6c6fcbf985bc042e3ebb26c0ddcd243de17e Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Fri, 3 Mar 2023 10:53:26 +0000 Subject: [PATCH 092/366] Added support for APE 14 WCSes with array_shape defined --- reproject/mosaicking/tests/test_wcs_helpers.py | 13 +++++++++++++ reproject/utils.py | 6 +++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/reproject/mosaicking/tests/test_wcs_helpers.py b/reproject/mosaicking/tests/test_wcs_helpers.py index a25aa6040..e18291b09 100644 --- a/reproject/mosaicking/tests/test_wcs_helpers.py +++ b/reproject/mosaicking/tests/test_wcs_helpers.py @@ -181,9 +181,11 @@ def test_invalid_not_celestial(self): "filename", "path", "hdulist", + "hdulist_all", "primary_hdu", "image_hdu", "comp_image_hdu", + "ape14_wcs", "shape_wcs_tuple", "data_wcs_tuple", "nddata", @@ -216,18 +218,29 @@ def test_input_types(self, tmp_path, iterable, input_type): elif input_type == "hdulist": input_value = hdulist hdu_in = 1 + elif input_type == "hdulist_all": + # If a single HDUList is passed, the function will iterate over + # all HDUs. This test is only relevant in the non-iterable case + if iterable: + pytest.skip() + input_value = hdulist elif input_type == "primary_hdu": input_value = hdulist[0] elif input_type == "image_hdu": input_value = hdulist[1] elif input_type == "comp_image_hdu": input_value = hdulist[2] + elif input_type == "ape14_wcs": + input_value = self.wcs + input_value._naxis = list(shape_ref[::-1]) elif input_type == "shape_wcs_tuple": input_value = (self.array.shape, self.wcs) elif input_type == "data_wcs_tuple": input_value = (self.array, self.wcs) elif input_type == "nddata": input_value = NDData(data=self.array, wcs=self.wcs) + else: + raise ValueError(f"Unknown mode: {input_type}") if iterable: input_value = [input_value] diff --git a/reproject/utils.py b/reproject/utils.py index cf6ae233d..d7ba76c9c 100644 --- a/reproject/utils.py +++ b/reproject/utils.py @@ -9,7 +9,7 @@ from astropy.io import fits from astropy.io.fits import CompImageHDU, HDUList, Header, ImageHDU, PrimaryHDU from astropy.wcs import WCS -from astropy.wcs.wcsapi import BaseHighLevelWCS, SlicedLowLevelWCS +from astropy.wcs.wcsapi import BaseLowLevelWCS, BaseHighLevelWCS, SlicedLowLevelWCS from astropy.wcs.wcsapi.high_level_wcs_wrapper import HighLevelWCSWrapper from dask.utils import SerializableLock @@ -46,6 +46,8 @@ def parse_input_data(input_data, hdu_in=None): return input_data[0], WCS(input_data[1]) else: return input_data + elif isinstance(input_data, BaseLowLevelWCS) and input_data.array_shape is not None: + return input_data.array_shape, input_data elif isinstance(input_data, astropy.nddata.NDDataBase): return input_data.data, input_data.wcs else: @@ -84,6 +86,8 @@ def parse_input_shape(input_shape, hdu_in=None): return input_shape[0], WCS(input_shape[1]) else: return input_shape + elif isinstance(input_shape, BaseLowLevelWCS) and input_shape.array_shape is not None: + return input_shape.array_shape, input_shape elif isinstance(input_shape, astropy.nddata.NDDataBase): return input_shape.data.shape, input_shape.wcs else: From ac23464f322fa2755aa797699e504f1192ad24c4 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Fri, 3 Mar 2023 11:09:01 +0000 Subject: [PATCH 093/366] Refactor valid celestial inputs into a pytest fixture --- reproject/conftest.py | 74 +++++++++++++ .../mosaicking/tests/test_wcs_helpers.py | 102 ++++-------------- 2 files changed, 96 insertions(+), 80 deletions(-) diff --git a/reproject/conftest.py b/reproject/conftest.py index 9ac7aaf70..bb3685062 100644 --- a/reproject/conftest.py +++ b/reproject/conftest.py @@ -5,6 +5,12 @@ import os +import pytest +import numpy as np +from astropy.wcs import WCS +from astropy.io import fits +from astropy.nddata import NDData + try: from pytest_astropy_header.display import PYTEST_HEADER_MODULES, TESTED_VERSIONS @@ -29,3 +35,71 @@ def pytest_configure(config): from reproject import __version__ TESTED_VERSIONS["reproject"] = __version__ + + +@pytest.fixture(params= [ + "filename", + "path", + "hdulist", + "hdulist_all", + "primary_hdu", + "image_hdu", + "comp_image_hdu", + "ape14_wcs", + "shape_wcs_tuple", + "data_wcs_tuple", + "nddata", + ]) +def valid_celestial_input(tmp_path, request): + + request.param + + array = np.ones((30, 40)) + + wcs = WCS(naxis=2) + wcs.wcs.ctype = "RA---TAN", "DEC--TAN" + wcs.wcs.crpix = (1, 2) + wcs.wcs.crval = (30, 40) + wcs.wcs.cdelt = (-0.05, 0.04) + wcs.wcs.equinox = 2000.0 + + hdulist = fits.HDUList( + [ + fits.PrimaryHDU(array, wcs.to_header()), + fits.ImageHDU(array, wcs.to_header()), + fits.CompImageHDU(array, wcs.to_header()), + ] + ) + + kwargs = {} + + if request.param in ["filename", "path"]: + input_value = tmp_path / "test.fits" + if request.param == "filename": + input_value = str(input_value) + hdulist.writeto(input_value) + kwargs['hdu_in'] = 0 + elif request.param == "hdulist": + input_value = hdulist + kwargs['hdu_in'] = 1 + elif request.param == "hdulist_all": + input_value = hdulist + elif request.param == "primary_hdu": + input_value = hdulist[0] + elif request.param == "image_hdu": + input_value = hdulist[1] + elif request.param == "comp_image_hdu": + input_value = hdulist[2] + elif request.param == "ape14_wcs": + input_value = wcs + input_value._naxis = list(array.shape[::-1]) + elif request.param == "shape_wcs_tuple": + input_value = (array.shape, wcs) + elif request.param == "data_wcs_tuple": + input_value = (array, wcs) + elif request.param == "nddata": + input_value = NDData(data=array, wcs=wcs) + else: + raise ValueError(f"Unknown mode: {request.param}") + + return array, wcs, input_value, kwargs diff --git a/reproject/mosaicking/tests/test_wcs_helpers.py b/reproject/mosaicking/tests/test_wcs_helpers.py index e18291b09..7aea8fbc8 100644 --- a/reproject/mosaicking/tests/test_wcs_helpers.py +++ b/reproject/mosaicking/tests/test_wcs_helpers.py @@ -174,83 +174,6 @@ def test_invalid_not_celestial(self): wcs, shape = find_optimal_celestial_wcs([(self.array, self.wcs)]) assert exc.value.args[0] == "WCS does not have celestial components" - @pytest.mark.parametrize("iterable", [False, True]) - @pytest.mark.parametrize( - "input_type", - [ - "filename", - "path", - "hdulist", - "hdulist_all", - "primary_hdu", - "image_hdu", - "comp_image_hdu", - "ape14_wcs", - "shape_wcs_tuple", - "data_wcs_tuple", - "nddata", - ], - ) - def test_input_types(self, tmp_path, iterable, input_type): - # Test different kinds of inputs and check the result is always the same - - # Reference - wcs_ref, shape_ref = find_optimal_celestial_wcs([(self.array, self.wcs)], frame=FK5()) - - # Test - - hdulist = fits.HDUList( - [ - fits.PrimaryHDU(self.array, self.wcs.to_header()), - fits.ImageHDU(self.array, self.wcs.to_header()), - fits.CompImageHDU(self.array, self.wcs.to_header()), - ] - ) - - hdu_in = None - - if input_type in ["filename", "path"]: - input_value = tmp_path / "test.fits" - if input_type == "filename": - input_value = str(input_value) - hdulist.writeto(input_value) - hdu_in = 0 - elif input_type == "hdulist": - input_value = hdulist - hdu_in = 1 - elif input_type == "hdulist_all": - # If a single HDUList is passed, the function will iterate over - # all HDUs. This test is only relevant in the non-iterable case - if iterable: - pytest.skip() - input_value = hdulist - elif input_type == "primary_hdu": - input_value = hdulist[0] - elif input_type == "image_hdu": - input_value = hdulist[1] - elif input_type == "comp_image_hdu": - input_value = hdulist[2] - elif input_type == "ape14_wcs": - input_value = self.wcs - input_value._naxis = list(shape_ref[::-1]) - elif input_type == "shape_wcs_tuple": - input_value = (self.array.shape, self.wcs) - elif input_type == "data_wcs_tuple": - input_value = (self.array, self.wcs) - elif input_type == "nddata": - input_value = NDData(data=self.array, wcs=self.wcs) - else: - raise ValueError(f"Unknown mode: {input_type}") - - if iterable: - input_value = [input_value] - - wcs_test, shape_test = find_optimal_celestial_wcs(input_value, frame=FK5(), hdu_in=hdu_in) - - assert_header_allclose(wcs_test.to_header(), wcs_ref.to_header()) - - assert shape_test == shape_ref - class TestOptimalFITSWCS(BaseTestOptimalWCS): def generate_wcs( @@ -294,9 +217,6 @@ def generate_wcs( def test_args_tuple_header(self): pytest.skip() - def test_input_types(self): - pytest.skip() - crval_atol = 1.5 crpix_atol = 1e-6 cdelt_rtol = 1.0e-3 @@ -307,3 +227,25 @@ def test_input_types(self): frame_projection_expected_shape = 46, 50 auto_rotate_expected_crpix = 20.520875, 15.503349 multiple_size_expected_crpix = 27.279739, 17.29016 + + +@pytest.mark.parametrize("iterable", [False, True]) +def test_input_types(valid_celestial_input, iterable): + + # Test different kinds of inputs and check the result is always the same + + array, wcs, input_value, kwargs = valid_celestial_input + + wcs_ref, shape_ref = find_optimal_celestial_wcs([(array, wcs)], frame=FK5()) + + if isinstance(input_value, fits.HDUList) and iterable and kwargs == {}: + pytest.skip() + + if iterable: + input_value = [input_value] + + wcs_test, shape_test = find_optimal_celestial_wcs(input_value, frame=FK5(), **kwargs) + + assert_header_allclose(wcs_test.to_header(), wcs_ref.to_header()) + + assert shape_test == shape_ref From cb2729f8706294ecad2137b914d3fdee221eb555 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Fri, 3 Mar 2023 11:33:00 +0000 Subject: [PATCH 094/366] More test simplification with fixtures --- reproject/conftest.py | 66 ++++++---- .../mosaicking/tests/test_wcs_helpers.py | 17 +-- reproject/tests/test_utils.py | 120 +++++------------- reproject/utils.py | 4 +- 4 files changed, 89 insertions(+), 118 deletions(-) diff --git a/reproject/conftest.py b/reproject/conftest.py index bb3685062..cc4a7ab91 100644 --- a/reproject/conftest.py +++ b/reproject/conftest.py @@ -5,11 +5,11 @@ import os -import pytest import numpy as np -from astropy.wcs import WCS +import pytest from astropy.io import fits from astropy.nddata import NDData +from astropy.wcs import WCS try: from pytest_astropy_header.display import PYTEST_HEADER_MODULES, TESTED_VERSIONS @@ -37,23 +37,7 @@ def pytest_configure(config): TESTED_VERSIONS["reproject"] = __version__ -@pytest.fixture(params= [ - "filename", - "path", - "hdulist", - "hdulist_all", - "primary_hdu", - "image_hdu", - "comp_image_hdu", - "ape14_wcs", - "shape_wcs_tuple", - "data_wcs_tuple", - "nddata", - ]) def valid_celestial_input(tmp_path, request): - - request.param - array = np.ones((30, 40)) wcs = WCS(naxis=2) @@ -78,12 +62,10 @@ def valid_celestial_input(tmp_path, request): if request.param == "filename": input_value = str(input_value) hdulist.writeto(input_value) - kwargs['hdu_in'] = 0 + kwargs["hdu_in"] = 0 elif request.param == "hdulist": input_value = hdulist - kwargs['hdu_in'] = 1 - elif request.param == "hdulist_all": - input_value = hdulist + kwargs["hdu_in"] = 1 elif request.param == "primary_hdu": input_value = hdulist[0] elif request.param == "image_hdu": @@ -99,7 +81,47 @@ def valid_celestial_input(tmp_path, request): input_value = (array, wcs) elif request.param == "nddata": input_value = NDData(data=array, wcs=wcs) + elif request.param == "ape14_wcs": + input_value = wcs + input_value._naxis = list(array.shape[::-1]) + elif request.param == "shape_wcs_tuple": + input_value = (array.shape, wcs) + else: raise ValueError(f"Unknown mode: {request.param}") return array, wcs, input_value, kwargs + + +@pytest.fixture( + params=[ + "filename", + "path", + "hdulist", + "primary_hdu", + "image_hdu", + "comp_image_hdu", + "data_wcs_tuple", + "nddata", + ] +) +def valid_celestial_input_data(tmp_path, request): + return valid_celestial_input(tmp_path, request) + + +@pytest.fixture( + params=[ + "filename", + "path", + "hdulist", + "primary_hdu", + "image_hdu", + "comp_image_hdu", + "data_wcs_tuple", + "nddata", + "ape14_wcs", + "shape_wcs_tuple", + ] +) +def valid_celestial_input_shapes(tmp_path, request): + return valid_celestial_input(tmp_path, request) diff --git a/reproject/mosaicking/tests/test_wcs_helpers.py b/reproject/mosaicking/tests/test_wcs_helpers.py index 7aea8fbc8..87132f0ef 100644 --- a/reproject/mosaicking/tests/test_wcs_helpers.py +++ b/reproject/mosaicking/tests/test_wcs_helpers.py @@ -230,22 +230,23 @@ def test_args_tuple_header(self): @pytest.mark.parametrize("iterable", [False, True]) -def test_input_types(valid_celestial_input, iterable): - +def test_input_types(valid_celestial_input_shapes, iterable): # Test different kinds of inputs and check the result is always the same - array, wcs, input_value, kwargs = valid_celestial_input + array, wcs, input_value, kwargs = valid_celestial_input_shapes wcs_ref, shape_ref = find_optimal_celestial_wcs([(array, wcs)], frame=FK5()) - if isinstance(input_value, fits.HDUList) and iterable and kwargs == {}: - pytest.skip() - if iterable: input_value = [input_value] wcs_test, shape_test = find_optimal_celestial_wcs(input_value, frame=FK5(), **kwargs) - assert_header_allclose(wcs_test.to_header(), wcs_ref.to_header()) - assert shape_test == shape_ref + + if isinstance(input_value, fits.HDUList) and not iterable: + # Also check case of not passing hdu_in and having all HDUs being included + + wcs_test, shape_test = find_optimal_celestial_wcs(input_value, frame=FK5()) + assert_header_allclose(wcs_test.to_header(), wcs_ref.to_header()) + assert shape_test == shape_ref diff --git a/reproject/tests/test_utils.py b/reproject/tests/test_utils.py index 5908f231b..43d4f3f69 100644 --- a/reproject/tests/test_utils.py +++ b/reproject/tests/test_utils.py @@ -5,115 +5,51 @@ from astropy.utils.data import get_pkg_data_filename from astropy.wcs import WCS +from reproject.tests.helpers import assert_header_allclose from reproject.utils import parse_input_data, parse_input_shape, parse_output_projection @pytest.mark.filterwarnings("ignore:unclosed file:ResourceWarning") -def test_parse_input_data(tmpdir): - header = fits.Header.fromtextfile(get_pkg_data_filename("data/gc_ga.hdr")) +def test_parse_input_data(tmpdir, valid_celestial_input_data, request): + array_ref, wcs_ref, input_value, kwargs = valid_celestial_input_data - data = np.arange(200).reshape((10, 20)) + data, wcs = parse_input_data(input_value, **kwargs) + np.testing.assert_allclose(data, array_ref) + assert_header_allclose(wcs.to_header(), wcs_ref.to_header()) - hdu = fits.ImageHDU(data, header) - # We want to test that the WCS is being parsed and output correctly in each - # of these cases. WCS doesn't seem to implement __eq__, so we convert the - # output WCS to a Header and compare that. Here we convert the original - # Header to a WCS and back to ensure an apples-to-apples comparision. - ref_coord_system = WCS(header).to_header() +def test_parse_input_data_invalid(): + data = np.ones((30, 40)) - # As HDU - array, coordinate_system = parse_input_data(hdu) - np.testing.assert_allclose(array, data) - assert coordinate_system.to_header() == ref_coord_system + with pytest.raises(TypeError, match="input_data should either be an HDU object"): + parse_input_data(data) - # As filename - filename = tmpdir.join("test.fits").strpath - hdu.writeto(filename) - with pytest.raises(ValueError) as exc: - array, coordinate_system = parse_input_data(filename) - assert exc.value.args[0] == ( - "More than one HDU is present, please specify HDU to use with ``hdu_in=`` option" +def test_parse_input_shape_missing_hdu_in(): + hdulist = fits.HDUList( + [fits.PrimaryHDU(data=np.ones((30, 40))), fits.ImageHDU(data=np.ones((20, 30)))] ) - array, coordinate_system = parse_input_data(filename, hdu_in=1) - np.testing.assert_allclose(array, data) - assert coordinate_system.to_header() == ref_coord_system - - # As array, header - array, coordinate_system = parse_input_data((data, header)) - np.testing.assert_allclose(array, data) - assert coordinate_system.to_header() == ref_coord_system - - # As array, WCS - wcs = WCS(hdu.header) - array, coordinate_system = parse_input_data((data, wcs)) - np.testing.assert_allclose(array, data) - assert coordinate_system is wcs - - ndd = NDData(data, wcs=wcs) - array, coordinate_system = parse_input_data(ndd) - np.testing.assert_allclose(array, data) - assert coordinate_system is wcs - - # Invalid - with pytest.raises(TypeError) as exc: - parse_input_data(data) - assert exc.value.args[0] == ( - "input_data should either be an HDU object or a tuple of (array, WCS) or (array, Header)" - ) + with pytest.raises(TypeError, match="More than one HDU"): + parse_input_data(hdulist) @pytest.mark.filterwarnings("ignore:unclosed file:ResourceWarning") -def test_parse_input_shape(tmpdir): +def test_parse_input_shape(tmpdir, valid_celestial_input_shapes): """ This should support everything that parse_input_data does, *plus* an "array-like" argument that is just a shape rather than a populated array. """ - header = fits.Header.fromtextfile(get_pkg_data_filename("data/gc_ga.hdr")) - in_shape = (10, 20) - data = np.arange(200).reshape(in_shape) - hdu = fits.ImageHDU(data) - - # As HDU - shape, coordinate_system = parse_input_shape(hdu) - assert shape == in_shape - # As filename - filename = tmpdir.join("test.fits").strpath - hdu.writeto(filename) + array_ref, wcs_ref, input_value, kwargs = valid_celestial_input_shapes - with pytest.raises(ValueError) as exc: - shape, coordinate_system = parse_input_shape(filename) - assert exc.value.args[0] == ( - "More than one HDU is present, please specify HDU to use with ``hdu_in=`` option" - ) - - shape, coordinate_system = parse_input_shape(filename, hdu_in=1) - assert shape == in_shape - - # As array, header - shape, coordinate_system = parse_input_shape((data, header)) - assert shape == in_shape - - # As array, WCS - wcs = WCS(hdu.header) - shape, coordinate_system = parse_input_shape((data, wcs)) - assert shape == in_shape - - ndd = NDData(data, wcs=wcs) - shape, coordinate_system = parse_input_shape(ndd) - assert shape == in_shape - assert coordinate_system is wcs + shape, wcs = parse_input_shape(input_value, **kwargs) + assert shape == array_ref.shape + assert_header_allclose(wcs.to_header(), wcs_ref.to_header()) - # As shape, header - shape, coordinate_system = parse_input_shape((data.shape, header)) - assert shape == in_shape - # As shape, WCS - shape, coordinate_system = parse_input_shape((data.shape, wcs)) - assert shape == in_shape +def test_parse_input_shape_invalid(): + data = np.ones((30, 40)) # Invalid with pytest.raises(TypeError) as exc: @@ -124,6 +60,18 @@ def test_parse_input_shape(tmpdir): ) +def test_parse_input_shape_missing_hdu_in(): + hdulist = fits.HDUList( + [fits.PrimaryHDU(data=np.ones((30, 40))), fits.ImageHDU(data=np.ones((20, 30)))] + ) + + with pytest.raises(ValueError) as exc: + shape, coordinate_system = parse_input_shape(hdulist) + assert exc.value.args[0] == ( + "More than one HDU is present, please specify HDU to use with ``hdu_in=`` option" + ) + + def test_parse_output_projection(tmpdir): header = fits.Header.fromtextfile(get_pkg_data_filename("data/gc_ga.hdr")) wcs = WCS(header) diff --git a/reproject/utils.py b/reproject/utils.py index d7ba76c9c..8407a4a30 100644 --- a/reproject/utils.py +++ b/reproject/utils.py @@ -9,7 +9,7 @@ from astropy.io import fits from astropy.io.fits import CompImageHDU, HDUList, Header, ImageHDU, PrimaryHDU from astropy.wcs import WCS -from astropy.wcs.wcsapi import BaseLowLevelWCS, BaseHighLevelWCS, SlicedLowLevelWCS +from astropy.wcs.wcsapi import BaseHighLevelWCS, BaseLowLevelWCS, SlicedLowLevelWCS from astropy.wcs.wcsapi.high_level_wcs_wrapper import HighLevelWCSWrapper from dask.utils import SerializableLock @@ -26,7 +26,7 @@ def parse_input_data(input_data, hdu_in=None): Parse input data to return a Numpy array and WCS object. """ - if isinstance(input_data, str): + if isinstance(input_data, (str, Path)): with fits.open(input_data) as hdul: return parse_input_data(hdul, hdu_in=hdu_in) elif isinstance(input_data, HDUList): From de5f9faddd2861e3dcce240b503ba9cd7099e1ac Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Fri, 3 Mar 2023 21:47:11 +0000 Subject: [PATCH 095/366] Remove whitespace Co-authored-by: Stuart Mumford --- reproject/conftest.py | 1 - 1 file changed, 1 deletion(-) diff --git a/reproject/conftest.py b/reproject/conftest.py index cc4a7ab91..5255e3452 100644 --- a/reproject/conftest.py +++ b/reproject/conftest.py @@ -86,7 +86,6 @@ def valid_celestial_input(tmp_path, request): input_value._naxis = list(array.shape[::-1]) elif request.param == "shape_wcs_tuple": input_value = (array.shape, wcs) - else: raise ValueError(f"Unknown mode: {request.param}") From 702e76a9ba6e0eb3b6240293b37421d2aa4f5b80 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Fri, 3 Mar 2023 22:02:06 +0000 Subject: [PATCH 096/366] Removed extraneous elif and tidied params --- reproject/conftest.py | 39 +++++++++++++++------------------------ 1 file changed, 15 insertions(+), 24 deletions(-) diff --git a/reproject/conftest.py b/reproject/conftest.py index 5255e3452..a7d197c8f 100644 --- a/reproject/conftest.py +++ b/reproject/conftest.py @@ -72,9 +72,6 @@ def valid_celestial_input(tmp_path, request): input_value = hdulist[1] elif request.param == "comp_image_hdu": input_value = hdulist[2] - elif request.param == "ape14_wcs": - input_value = wcs - input_value._naxis = list(array.shape[::-1]) elif request.param == "shape_wcs_tuple": input_value = (array.shape, wcs) elif request.param == "data_wcs_tuple": @@ -92,32 +89,26 @@ def valid_celestial_input(tmp_path, request): return array, wcs, input_value, kwargs -@pytest.fixture( - params=[ - "filename", - "path", - "hdulist", - "primary_hdu", - "image_hdu", - "comp_image_hdu", - "data_wcs_tuple", - "nddata", - ] -) +COMMON_PARAMS = [ + "filename", + "path", + "hdulist", + "primary_hdu", + "image_hdu", + "comp_image_hdu", + "data_wcs_tuple", + "nddata", +] + + +@pytest.fixture(params=COMMON_PARAMS) def valid_celestial_input_data(tmp_path, request): return valid_celestial_input(tmp_path, request) @pytest.fixture( - params=[ - "filename", - "path", - "hdulist", - "primary_hdu", - "image_hdu", - "comp_image_hdu", - "data_wcs_tuple", - "nddata", + params=COMMON_PARAMS + + [ "ape14_wcs", "shape_wcs_tuple", ] From 8e2c39b74f08bb44182dc9c2d2ca0afb436a3fc9 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Fri, 3 Mar 2023 23:56:33 +0000 Subject: [PATCH 097/366] Remove code that was required for astropy<4 and updated two reference datasets --- .../reference/test_reproject_adaptive_2d.fits | Bin 63360 -> 63360 bytes .../test_reproject_adaptive_2d_rotated.fits | Bin 63360 -> 63360 bytes reproject/adaptive/tests/test_core.py | 44 ------------ reproject/interpolation/tests/test_core.py | 64 +++++------------- reproject/mosaicking/tests/test_coadd.py | 6 -- 5 files changed, 18 insertions(+), 96 deletions(-) diff --git a/reproject/adaptive/tests/reference/test_reproject_adaptive_2d.fits b/reproject/adaptive/tests/reference/test_reproject_adaptive_2d.fits index 604af3e902c74bd6414ebe235858d890980cca83..331005aed373f61440a8e17ca04af9e50b909415 100644 GIT binary patch delta 1628 zcmbtVeN0nV6!%bO-cw_EB0j2-mN9J$2rUB(2;4e9sEUL60VpD^e6^^wAPSC|mMoEO zZbLa_beoB6!O0Rwk++OQ7a?wr;=lsRB-ylPo1=44NDM(YTzAnY|Lo7*KhE!+ch5cN zymNl%UHiDiJ}&7n1SW0Wynfq;jdFRa{69+_qn5|Y^NRB2)JSeAk(Zc@4i}n@2jo@e zgT<%nc%jFxn~V0qEY!`fN85Y`J@X$!r{G@T1=u6BEu>H-ID#?*qzUBv8v~|K{2Fre z+ShlC4{Aw&$E2q9>LgAq;vo*1V8XpB+Qg9?W9KhBA1%P#;B@d|OR!BEFYc`tF`f)f zuNcYxF$5DA6IM&y$&Cd?P0P@}xQlBg-JY;c7`;r+pBUTO)XAehq!*TB(2^;58yS9z z-AmrcBa1fi9ms@w;TZZt{jdgo(t400$MHh_(iRDkpwFwbHArr$5( zQz2*@4VEQLY@Y&3Ki^Ip#G@(OGE?d7aWD(+98Uo3N9S&u89MVU>AfAyj_11>LCELJA@G^zTBb%~WYLk&!!DsE{~XNltiT2D;^Tr+ zw7onJ&4zaA8Ic7Bq9R#OZEGxK-&jf=%&Ef(sZ`ixTpd7LWG~F&bfV$j7l^(>85of% zYJhBX7Ac?*d#E&`uc#oYGws^+KO-l7dq_p*7wey#t(qe)al8{|2`&yQG5^^&3Enic z7t2_IY`Z_)q`EzVZB+Y8BoD#ooG6xQ9*|sa@o3cP3$rwM-t0rO z@vPIQV?N9T5`4#!kaKYWbtOD2A>|n=mq%TOh7uVYPV$d`+1_;Dgbu1*7Yk8=9n^9c zu{?U%@`TgiV2Mog7cpX=DWmZta$<8*<($Xaq+i$at@Cc{Y&1)IUyrNNMUtZ)=4b!V zAjnJW0TQsyG|ooMPQ*+ryUa?;;Jao8rY}=qOj$To3Rz_`fPBHapOVCkSqHsfLUnmF ztQOqm1prS_Y4w5^nF>EN3ruAf{Rd;uAqDJ4-ys|96--sss#S0wrgPOUIF8-|IzTTn TG1W3J{lCk=N-u-Xdb{;s0QiJK delta 1536 zcmX|BeN0nV6zA}v@2PQpfEKpdhf~%TP+Am=h}`1X=%yR^1x$u5f>52(q9PF7S`ssw z;FNLLY}7@zsIWwZa1|C^l(;zxgB8IK+@dwx9MxqO;|!YNx(gKEALr!WoOeEczu&o5 zPk_}E&|(OrErGSuurDwJwKJ=s4IMMRK&fMv3m}g+&1SBQ+9c`z;Y-Mm1HHzhUNyPi z-F7$c?@H3s@>G4|>OVLfEQKY=g%Bam7qf&644uAYe=-}hL(<>@HiTF(EmXoUCD%qH zZ)DyK#e`78FC&h`I(qqQFOkzVLmTT`!%#P;6J#ivI|5b6Nk@1+ zS(`(G-iN56m*AI<4ZyWJ@m3Y03uN#D}qn4R^|d5n#B>=kH!cuv{B=H zHp*?(EwDfrIu;bcLR2jr0XZrnJ%WiiCB^f$YqCjaW#N%#sH2M4oxqEvwQgnQmBzg& z2vRClr~q0~rxXSCWRK)P*u}Y{nvbzuDZ&A?D+$Xxs5F5Sby1cvIHL$OAQw#-eiL)a zNMMyYr~A-TB0JtYFEZf_8lpwkhc#LXb!d-HhYEB@Yj}=a$@kX;==)I-qY+LJp(Hpe zi8PYK|4ys#N!p9%80q|qTtzmH|l%XOvjahT76!xMmwv*Wmm7LjfRT;d3!>Tku zOi+vbd>_JJK1{_ou$Kd^qDwbXj00X);Lo8!of{SHoS?+}pY)KrO z&XIVF&nJbS8D^6?k7?5i_Rl%Atvn8YQbn>9;1ARiIgTVtSoU(_gq^64;oagE`ndMcJ&BMN81_~D{p+)3p=Rsy~6=Q5b2#^Gk@3Po%B?)jul%=hu4n) zDA2vp0&!@}G>xH~=>ao!WI6p=^Y%?%@I!l!i3xv#d~r-kDE}aXf6WJG+4zknRf5&& zT7H1EFABfEf!i>4%ll0f>-wZYQOkaeS%^Z}X(a;?J)ucj*l4QKFtSBX0hLWnluL{k%VN{o=@rMGm=4S~h zzQD~s&q&P|8MR!&A;?C1fgHAA2eW!~7wDJM`Z38C zLILMcC;6t7V>J5?q$6pDN# z$^GQa+4_ejw6Rma4+thy7MVv(PaZWq7nWgPp{RaA^03RertV8p^Um^Je$TQ&FR| zZ&#pa6-TqShXHJ&<`0>)jF_?01%;?8J`C~HQLG0TMTOY~caSUb!cofYY6r-{j@@!* q-McMdpvFBc>J)X9vJE^>Z56kGt=eQ?#!4=Tc38FJFMVsZ)%-u>ifeEH diff --git a/reproject/adaptive/tests/reference/test_reproject_adaptive_2d_rotated.fits b/reproject/adaptive/tests/reference/test_reproject_adaptive_2d_rotated.fits index 863ee07a90ac6b68dd8ccfdd4afa9d9c5c23eaa5..cd8b761bd07dae43376f8570d76834ae341aff76 100644 GIT binary patch delta 4266 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 delta 4149 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 xnU- Date: Fri, 3 Mar 2023 23:41:20 +0000 Subject: [PATCH 098/366] Add support for specifying output projection as APE 14 WCS with array_shape defined, and refactor output_projection tests to use a fixture --- reproject/conftest.py | 46 +++++++++++++++++++++++++++++------ reproject/tests/test_utils.py | 37 +++++++++++----------------- reproject/utils.py | 10 +++++--- 3 files changed, 59 insertions(+), 34 deletions(-) diff --git a/reproject/conftest.py b/reproject/conftest.py index a7d197c8f..be7f202a0 100644 --- a/reproject/conftest.py +++ b/reproject/conftest.py @@ -37,15 +37,20 @@ def pytest_configure(config): TESTED_VERSIONS["reproject"] = __version__ -def valid_celestial_input(tmp_path, request): - array = np.ones((30, 40)) - +@pytest.fixture +def simple_celestial_wcs(): wcs = WCS(naxis=2) wcs.wcs.ctype = "RA---TAN", "DEC--TAN" wcs.wcs.crpix = (1, 2) wcs.wcs.crval = (30, 40) wcs.wcs.cdelt = (-0.05, 0.04) wcs.wcs.equinox = 2000.0 + return wcs + + +def valid_celestial_input(tmp_path, request, wcs): + + array = np.ones((30, 40)) hdulist = fits.HDUList( [ @@ -102,8 +107,8 @@ def valid_celestial_input(tmp_path, request): @pytest.fixture(params=COMMON_PARAMS) -def valid_celestial_input_data(tmp_path, request): - return valid_celestial_input(tmp_path, request) +def valid_celestial_input_data(tmp_path, request, simple_celestial_wcs): + return valid_celestial_input(tmp_path, request, simple_celestial_wcs) @pytest.fixture( @@ -113,5 +118,32 @@ def valid_celestial_input_data(tmp_path, request): "shape_wcs_tuple", ] ) -def valid_celestial_input_shapes(tmp_path, request): - return valid_celestial_input(tmp_path, request) +def valid_celestial_input_shapes(tmp_path, request, simple_celestial_wcs): + return valid_celestial_input(tmp_path, request, simple_celestial_wcs) + + +@pytest.fixture(params=["wcs_shape", "header", "header_shape", "ape14_wcs"]) +def valid_celestial_output_projections(request, simple_celestial_wcs): + + shape = (30, 40) + wcs = simple_celestial_wcs + + kwargs = {} + + if request.param == "wcs_shape": + output_value = wcs + kwargs["shape_out"] = shape + elif request.param == "header": + header = wcs.to_header() + header["NAXIS"] = 2 + header["NAXIS1"] = 40 + header["NAXIS2"] = 30 + output_value = header + elif request.param == "header_shape": + output_value = wcs.to_header() + kwargs["shape_out"] = shape + elif request.param == "ape14_wcs": + output_value = wcs + output_value._naxis = (40, 30) + + return wcs, shape, output_value, kwargs diff --git a/reproject/tests/test_utils.py b/reproject/tests/test_utils.py index 43d4f3f69..bbc3328ed 100644 --- a/reproject/tests/test_utils.py +++ b/reproject/tests/test_utils.py @@ -25,12 +25,12 @@ def test_parse_input_data_invalid(): parse_input_data(data) -def test_parse_input_shape_missing_hdu_in(): +def test_parse_input_data_missing_hdu_in(): hdulist = fits.HDUList( [fits.PrimaryHDU(data=np.ones((30, 40))), fits.ImageHDU(data=np.ones((20, 30)))] ) - with pytest.raises(TypeError, match="More than one HDU"): + with pytest.raises(ValueError, match="More than one HDU"): parse_input_data(hdulist) @@ -72,32 +72,23 @@ def test_parse_input_shape_missing_hdu_in(): ) -def test_parse_output_projection(tmpdir): - header = fits.Header.fromtextfile(get_pkg_data_filename("data/gc_ga.hdr")) - wcs = WCS(header) +def test_parse_output_projection(valid_celestial_output_projections): - # As header + wcs_ref, shape_ref, output_value, kwargs = valid_celestial_output_projections - with pytest.raises(ValueError) as exc: - parse_output_projection(header) - assert exc.value.args[0] == ( - "Need to specify shape since output header does not contain complete shape information" - ) + wcs, shape = parse_output_projection(output_value, **kwargs) - parse_output_projection(header, shape_out=(200, 200)) + assert shape == shape_ref + assert_header_allclose(wcs.to_header(), wcs_ref.to_header()) - header["NAXIS"] = 2 - header["NAXIS1"] = 200 - header["NAXIS2"] = 300 - parse_output_projection(header) +def test_parse_output_projection_invalid_header(simple_celestial_wcs): - # As WCS + with pytest.raises(ValueError, match="Need to specify shape"): + parse_output_projection(simple_celestial_wcs.to_header()) - with pytest.raises(ValueError) as exc: - parse_output_projection(wcs) - assert exc.value.args[0] == ( - "Need to specify shape_out when specifying output_projection as WCS object" - ) - parse_output_projection(wcs, shape_out=(200, 200)) +def test_parse_output_projection_invalid_wcs(simple_celestial_wcs): + + with pytest.raises(ValueError, match="Need to specify shape"): + parse_output_projection(simple_celestial_wcs) diff --git a/reproject/utils.py b/reproject/utils.py index 8407a4a30..22120f635 100644 --- a/reproject/utils.py +++ b/reproject/utils.py @@ -9,7 +9,7 @@ from astropy.io import fits from astropy.io.fits import CompImageHDU, HDUList, Header, ImageHDU, PrimaryHDU from astropy.wcs import WCS -from astropy.wcs.wcsapi import BaseHighLevelWCS, BaseLowLevelWCS, SlicedLowLevelWCS +from astropy.wcs.wcsapi import BaseLowLevelWCS, SlicedLowLevelWCS from astropy.wcs.wcsapi.high_level_wcs_wrapper import HighLevelWCSWrapper from dask.utils import SerializableLock @@ -142,9 +142,11 @@ def parse_output_projection(output_projection, shape_in=None, shape_out=None, ou "Need to specify shape since output header " "does not contain complete shape information" ) - elif isinstance(output_projection, BaseHighLevelWCS): + elif isinstance(output_projection, BaseLowLevelWCS): wcs_out = output_projection - if shape_out is None: + if wcs_out.array_shape is not None: + shape_out = wcs_out.array_shape + elif shape_out is None: raise ValueError( "Need to specify shape_out when specifying output_projection as WCS object" ) @@ -168,7 +170,7 @@ def parse_output_projection(output_projection, shape_in=None, shape_out=None, ou # Add the broadcast dimensions to the output shape, which does not # currently have any broadcast dims shape_out = (*shape_in[: -len(shape_out)], *shape_out) - return wcs_out, shape_out + return wcs_out, tuple(shape_out) def _reproject_blocked( From 1121bea520f851dbd11241ff04feca28b704a941 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Fri, 3 Mar 2023 23:50:44 +0000 Subject: [PATCH 099/366] Add back support for passing high level WCS --- reproject/utils.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/reproject/utils.py b/reproject/utils.py index 22120f635..a253663d4 100644 --- a/reproject/utils.py +++ b/reproject/utils.py @@ -9,7 +9,7 @@ from astropy.io import fits from astropy.io.fits import CompImageHDU, HDUList, Header, ImageHDU, PrimaryHDU from astropy.wcs import WCS -from astropy.wcs.wcsapi import BaseLowLevelWCS, SlicedLowLevelWCS +from astropy.wcs.wcsapi import BaseLowLevelWCS, BaseHighLevelWCS, SlicedLowLevelWCS from astropy.wcs.wcsapi.high_level_wcs_wrapper import HighLevelWCSWrapper from dask.utils import SerializableLock @@ -142,9 +142,9 @@ def parse_output_projection(output_projection, shape_in=None, shape_out=None, ou "Need to specify shape since output header " "does not contain complete shape information" ) - elif isinstance(output_projection, BaseLowLevelWCS): + elif isinstance(output_projection, (BaseLowLevelWCS, BaseHighLevelWCS)): wcs_out = output_projection - if wcs_out.array_shape is not None: + if getattr(wcs_out, 'array_shape') is not None: shape_out = wcs_out.array_shape elif shape_out is None: raise ValueError( From 0a64a7ac1bbf895b5ead98350456c1a0bbcbfe5d Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Fri, 3 Mar 2023 23:58:16 +0000 Subject: [PATCH 100/366] Fix codestyle --- reproject/conftest.py | 2 -- reproject/tests/test_utils.py | 3 --- reproject/utils.py | 4 ++-- 3 files changed, 2 insertions(+), 7 deletions(-) diff --git a/reproject/conftest.py b/reproject/conftest.py index be7f202a0..9c27f24af 100644 --- a/reproject/conftest.py +++ b/reproject/conftest.py @@ -49,7 +49,6 @@ def simple_celestial_wcs(): def valid_celestial_input(tmp_path, request, wcs): - array = np.ones((30, 40)) hdulist = fits.HDUList( @@ -124,7 +123,6 @@ def valid_celestial_input_shapes(tmp_path, request, simple_celestial_wcs): @pytest.fixture(params=["wcs_shape", "header", "header_shape", "ape14_wcs"]) def valid_celestial_output_projections(request, simple_celestial_wcs): - shape = (30, 40) wcs = simple_celestial_wcs diff --git a/reproject/tests/test_utils.py b/reproject/tests/test_utils.py index bbc3328ed..0404d7036 100644 --- a/reproject/tests/test_utils.py +++ b/reproject/tests/test_utils.py @@ -73,7 +73,6 @@ def test_parse_input_shape_missing_hdu_in(): def test_parse_output_projection(valid_celestial_output_projections): - wcs_ref, shape_ref, output_value, kwargs = valid_celestial_output_projections wcs, shape = parse_output_projection(output_value, **kwargs) @@ -83,12 +82,10 @@ def test_parse_output_projection(valid_celestial_output_projections): def test_parse_output_projection_invalid_header(simple_celestial_wcs): - with pytest.raises(ValueError, match="Need to specify shape"): parse_output_projection(simple_celestial_wcs.to_header()) def test_parse_output_projection_invalid_wcs(simple_celestial_wcs): - with pytest.raises(ValueError, match="Need to specify shape"): parse_output_projection(simple_celestial_wcs) diff --git a/reproject/utils.py b/reproject/utils.py index a253663d4..05c7b4c78 100644 --- a/reproject/utils.py +++ b/reproject/utils.py @@ -9,7 +9,7 @@ from astropy.io import fits from astropy.io.fits import CompImageHDU, HDUList, Header, ImageHDU, PrimaryHDU from astropy.wcs import WCS -from astropy.wcs.wcsapi import BaseLowLevelWCS, BaseHighLevelWCS, SlicedLowLevelWCS +from astropy.wcs.wcsapi import BaseHighLevelWCS, BaseLowLevelWCS, SlicedLowLevelWCS from astropy.wcs.wcsapi.high_level_wcs_wrapper import HighLevelWCSWrapper from dask.utils import SerializableLock @@ -144,7 +144,7 @@ def parse_output_projection(output_projection, shape_in=None, shape_out=None, ou ) elif isinstance(output_projection, (BaseLowLevelWCS, BaseHighLevelWCS)): wcs_out = output_projection - if getattr(wcs_out, 'array_shape') is not None: + if getattr(wcs_out, "array_shape") is not None: shape_out = wcs_out.array_shape elif shape_out is None: raise ValueError( From c83897d9a6d399f4aa4c762ed21ae15e22773516 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Mon, 6 Mar 2023 10:38:55 +0000 Subject: [PATCH 101/366] Added reprojection tests for different inputs/outputs --- reproject/adaptive/tests/test_core.py | 25 +++++++++++++++++++ reproject/conftest.py | 19 +++++++++++--- reproject/interpolation/tests/test_core.py | 23 +++++++++++++++++ .../tests/test_high_level.py | 23 +++++++++++++++++ 4 files changed, 86 insertions(+), 4 deletions(-) diff --git a/reproject/adaptive/tests/test_core.py b/reproject/adaptive/tests/test_core.py index c2275f910..225db294e 100644 --- a/reproject/adaptive/tests/test_core.py +++ b/reproject/adaptive/tests/test_core.py @@ -858,3 +858,28 @@ def test_broadcast_reprojection_conserve_flux(conserve_flux): @pytest.mark.parametrize("center_jacobian", (True, False)) def test_broadcast_reprojection_center_jacobian(center_jacobian): _test_broadcast_reprojection_algo_specific_options(center_jacobian=center_jacobian) + + +def test_adaptive_input_output_types( + valid_celestial_input_data, valid_celestial_output_projections +): + # Check that all valid input/output types work properly + + array_ref, wcs_in_ref, input_value, kwargs_in = valid_celestial_input_data + + wcs_out_ref, shape_ref, output_value, kwargs_out = valid_celestial_output_projections + + # Compute reference + + output_ref, footprint_ref = reproject_adaptive( + (array_ref, wcs_in_ref), wcs_out_ref, shape_out=shape_ref + ) + + # Compute test + + output_test, footprint_test = reproject_adaptive( + input_value, output_value, **(kwargs_in | kwargs_out) + ) + + assert_allclose(output_ref, output_test) + assert_allclose(footprint_ref, footprint_test) diff --git a/reproject/conftest.py b/reproject/conftest.py index 9c27f24af..683227931 100644 --- a/reproject/conftest.py +++ b/reproject/conftest.py @@ -82,7 +82,7 @@ def valid_celestial_input(tmp_path, request, wcs): input_value = (array, wcs) elif request.param == "nddata": input_value = NDData(data=array, wcs=wcs) - elif request.param == "ape14_wcs": + elif request.param == "ape14_highlevel_wcs": input_value = wcs input_value._naxis = list(array.shape[::-1]) elif request.param == "shape_wcs_tuple": @@ -113,7 +113,7 @@ def valid_celestial_input_data(tmp_path, request, simple_celestial_wcs): @pytest.fixture( params=COMMON_PARAMS + [ - "ape14_wcs", + "ape14_highlevel_wcs", "shape_wcs_tuple", ] ) @@ -121,11 +121,22 @@ def valid_celestial_input_shapes(tmp_path, request, simple_celestial_wcs): return valid_celestial_input(tmp_path, request, simple_celestial_wcs) -@pytest.fixture(params=["wcs_shape", "header", "header_shape", "ape14_wcs"]) +@pytest.fixture( + params=[ + "wcs_shape", + "header", + "header_shape", + "ape14_highlevel_wcs", + ] +) def valid_celestial_output_projections(request, simple_celestial_wcs): shape = (30, 40) wcs = simple_celestial_wcs + # Rotate the WCS in case this is used for actual reprojection tests + + wcs.wcs.pc = np.array([[np.cos(0.4), -np.sin(0.4)], [np.sin(0.4), np.cos(0.4)]]) + kwargs = {} if request.param == "wcs_shape": @@ -140,7 +151,7 @@ def valid_celestial_output_projections(request, simple_celestial_wcs): elif request.param == "header_shape": output_value = wcs.to_header() kwargs["shape_out"] = shape - elif request.param == "ape14_wcs": + elif request.param == "ape14_highlevel_wcs": output_value = wcs output_value._naxis = (40, 30) diff --git a/reproject/interpolation/tests/test_core.py b/reproject/interpolation/tests/test_core.py index 69db845db..5e08e2258 100644 --- a/reproject/interpolation/tests/test_core.py +++ b/reproject/interpolation/tests/test_core.py @@ -790,3 +790,26 @@ def test_blocked_against_single(parallel, block_size, return_footprint, existing np.testing.assert_allclose(array_test, array_reference, equal_nan=True) if return_footprint: np.testing.assert_allclose(footprint_test, footprint_reference, equal_nan=True) + + +def test_interp_input_output_types(valid_celestial_input_data, valid_celestial_output_projections): + # Check that all valid input/output types work properly + + array_ref, wcs_in_ref, input_value, kwargs_in = valid_celestial_input_data + + wcs_out_ref, shape_ref, output_value, kwargs_out = valid_celestial_output_projections + + # Compute reference + + output_ref, footprint_ref = reproject_interp( + (array_ref, wcs_in_ref), wcs_out_ref, shape_out=shape_ref + ) + + # Compute test + + output_test, footprint_test = reproject_interp( + input_value, output_value, **(kwargs_in | kwargs_out) + ) + + assert_allclose(output_ref, output_test) + assert_allclose(footprint_ref, footprint_test) diff --git a/reproject/spherical_intersect/tests/test_high_level.py b/reproject/spherical_intersect/tests/test_high_level.py index 9e925ddbd..290b81d40 100644 --- a/reproject/spherical_intersect/tests/test_high_level.py +++ b/reproject/spherical_intersect/tests/test_high_level.py @@ -177,3 +177,26 @@ def test_broadcast_parallel_reprojection(input_extra_dims, output_shape, paralle np.testing.assert_allclose(footprint_broadcast, footprint_ref) np.testing.assert_allclose(array_broadcast, array_ref) + + +def test_exact_input_output_types(valid_celestial_input_data, valid_celestial_output_projections): + # Check that all valid input/output types work properly + + array_ref, wcs_in_ref, input_value, kwargs_in = valid_celestial_input_data + + wcs_out_ref, shape_ref, output_value, kwargs_out = valid_celestial_output_projections + + # Compute reference + + output_ref, footprint_ref = reproject_exact( + (array_ref, wcs_in_ref), wcs_out_ref, shape_out=shape_ref + ) + + # Compute test + + output_test, footprint_test = reproject_exact( + input_value, output_value, **(kwargs_in | kwargs_out) + ) + + assert_allclose(output_ref, output_test) + assert_allclose(footprint_ref, footprint_test) From f292c5c0b54f6d324b2ce83446a73a67793f05d5 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Mon, 6 Mar 2023 10:41:24 +0000 Subject: [PATCH 102/366] Remove mentions of the low level WCS API for now --- reproject/utils.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/reproject/utils.py b/reproject/utils.py index 05c7b4c78..8af00f8f0 100644 --- a/reproject/utils.py +++ b/reproject/utils.py @@ -9,7 +9,7 @@ from astropy.io import fits from astropy.io.fits import CompImageHDU, HDUList, Header, ImageHDU, PrimaryHDU from astropy.wcs import WCS -from astropy.wcs.wcsapi import BaseHighLevelWCS, BaseLowLevelWCS, SlicedLowLevelWCS +from astropy.wcs.wcsapi import BaseHighLevelWCS, SlicedLowLevelWCS from astropy.wcs.wcsapi.high_level_wcs_wrapper import HighLevelWCSWrapper from dask.utils import SerializableLock @@ -46,7 +46,10 @@ def parse_input_data(input_data, hdu_in=None): return input_data[0], WCS(input_data[1]) else: return input_data - elif isinstance(input_data, BaseLowLevelWCS) and input_data.array_shape is not None: + elif ( + isinstance(input_data, BaseHighLevelWCS) + and input_data.low_level_wcs.array_shape is not None + ): return input_data.array_shape, input_data elif isinstance(input_data, astropy.nddata.NDDataBase): return input_data.data, input_data.wcs @@ -86,7 +89,10 @@ def parse_input_shape(input_shape, hdu_in=None): return input_shape[0], WCS(input_shape[1]) else: return input_shape - elif isinstance(input_shape, BaseLowLevelWCS) and input_shape.array_shape is not None: + elif ( + isinstance(input_shape, BaseHighLevelWCS) + and input_shape.low_level_wcs.array_shape is not None + ): return input_shape.array_shape, input_shape elif isinstance(input_shape, astropy.nddata.NDDataBase): return input_shape.data.shape, input_shape.wcs @@ -142,7 +148,7 @@ def parse_output_projection(output_projection, shape_in=None, shape_out=None, ou "Need to specify shape since output header " "does not contain complete shape information" ) - elif isinstance(output_projection, (BaseLowLevelWCS, BaseHighLevelWCS)): + elif isinstance(output_projection, BaseHighLevelWCS): wcs_out = output_projection if getattr(wcs_out, "array_shape") is not None: shape_out = wcs_out.array_shape From c2b09db577857037d2d4eb95511b160903465ff0 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Mon, 6 Mar 2023 11:30:44 +0000 Subject: [PATCH 103/366] Properly test and support pure low- and high-level APE14 WCSes --- reproject/conftest.py | 57 +++++++++++++++++-- .../mosaicking/tests/test_wcs_helpers.py | 40 +++++++++++-- reproject/tests/helpers.py | 27 ++++++++- reproject/tests/test_utils.py | 8 +-- reproject/utils.py | 19 +++++-- 5 files changed, 131 insertions(+), 20 deletions(-) diff --git a/reproject/conftest.py b/reproject/conftest.py index 683227931..ad3461666 100644 --- a/reproject/conftest.py +++ b/reproject/conftest.py @@ -10,6 +10,7 @@ from astropy.io import fits from astropy.nddata import NDData from astropy.wcs import WCS +from astropy.wcs.wcsapi import HighLevelWCSMixin, SlicedLowLevelWCS try: from pytest_astropy_header.display import PYTEST_HEADER_MODULES, TESTED_VERSIONS @@ -82,9 +83,15 @@ def valid_celestial_input(tmp_path, request, wcs): input_value = (array, wcs) elif request.param == "nddata": input_value = NDData(data=array, wcs=wcs) - elif request.param == "ape14_highlevel_wcs": + elif request.param == "fits_wcs": + wcs._naxis = list(array.shape[::-1]) input_value = wcs - input_value._naxis = list(array.shape[::-1]) + elif request.param == "ape14_lowlevel_wcs": + wcs._naxis = list(array.shape[::-1]) + input_value = TestLowLevelWCS(wcs) + elif request.param == "ape14_highlevel_wcs": + wcs._naxis = list(array.shape[::-1]) + input_value = TestHighLevelWCS(wcs) elif request.param == "shape_wcs_tuple": input_value = (array.shape, wcs) else: @@ -113,6 +120,8 @@ def valid_celestial_input_data(tmp_path, request, simple_celestial_wcs): @pytest.fixture( params=COMMON_PARAMS + [ + "fits_wcs", + "ape14_lowlevel_wcs", "ape14_highlevel_wcs", "shape_wcs_tuple", ] @@ -121,11 +130,41 @@ def valid_celestial_input_shapes(tmp_path, request, simple_celestial_wcs): return valid_celestial_input(tmp_path, request, simple_celestial_wcs) +class TestLowLevelWCS(SlicedLowLevelWCS): + # The simplest way to get a 'pure' low level WCS is to call SlicedLowLevelWCS + # with an ellipsis slice! + + def __init__(self, low_level_wcs): + self._low_level_wcs = low_level_wcs + super().__init__(low_level_wcs, Ellipsis) + + +class TestHighLevelWCS(HighLevelWCSMixin): + def __init__(self, low_level_wcs): + self._low_level_wcs = low_level_wcs + + @property + def low_level_wcs(self): + return self._low_level_wcs + + # FIXME: due to a bug in astropy we need world_n_dim to be defined here + + @property + def world_n_dim(self): + return self.low_level_wcs.world_n_dim + + @property + def pixel_n_dim(self): + return self.low_level_wcs.pixel_n_dim + + @pytest.fixture( params=[ "wcs_shape", "header", "header_shape", + "fits_wcs", + "ape14_lowlevel_wcs", "ape14_highlevel_wcs", ] ) @@ -135,7 +174,7 @@ def valid_celestial_output_projections(request, simple_celestial_wcs): # Rotate the WCS in case this is used for actual reprojection tests - wcs.wcs.pc = np.array([[np.cos(0.4), -np.sin(0.4)], [np.sin(0.4), np.cos(0.4)]]) + # wcs.wcs.pc = np.array([[np.cos(0.4), -np.sin(0.4)], [np.sin(0.4), np.cos(0.4)]]) kwargs = {} @@ -151,8 +190,16 @@ def valid_celestial_output_projections(request, simple_celestial_wcs): elif request.param == "header_shape": output_value = wcs.to_header() kwargs["shape_out"] = shape - elif request.param == "ape14_highlevel_wcs": + elif request.param == "fits_wcs": + wcs._naxis = (40, 30) output_value = wcs - output_value._naxis = (40, 30) + elif request.param == "ape14_lowlevel_wcs": + wcs._naxis = (40, 30) + # Enforce only the low level API + output_value = TestLowLevelWCS(wcs) + elif request.param == "ape14_highlevel_wcs": + wcs._naxis = (40, 30) + # Enforce only the high level API + output_value = TestHighLevelWCS(wcs) return wcs, shape, output_value, kwargs diff --git a/reproject/mosaicking/tests/test_wcs_helpers.py b/reproject/mosaicking/tests/test_wcs_helpers.py index 87132f0ef..5293eb231 100644 --- a/reproject/mosaicking/tests/test_wcs_helpers.py +++ b/reproject/mosaicking/tests/test_wcs_helpers.py @@ -7,10 +7,10 @@ from astropy.io import fits from astropy.nddata import NDData from astropy.wcs import WCS -from astropy.wcs.wcsapi import HighLevelWCSWrapper +from astropy.wcs.wcsapi import BaseHighLevelWCS, BaseLowLevelWCS, HighLevelWCSWrapper from numpy.testing import assert_allclose, assert_equal -from reproject.tests.helpers import assert_header_allclose +from reproject.tests.helpers import assert_wcs_allclose from ..wcs_helpers import find_optimal_celestial_wcs @@ -229,6 +229,31 @@ def test_args_tuple_header(self): multiple_size_expected_crpix = 27.279739, 17.29016 +# In cases where the input WCS is wrapped in a pure APE-14 WCS, the results +# are a little different - as find_optimal_celestial_wcs takes shortcuts if +# FITS WCSes are passed in. + +APE14_HEADER_REF = """ +WCSAXES = 2 / Number of coordinate axes +CRPIX1 = 25.72083769123 / Pixel coordinate of reference point +CRPIX2 = 15.85922213012 / Pixel coordinate of reference point +CDELT1 = -0.039990388998799 / [deg] Coordinate increment at reference point +CDELT2 = 0.039990388998799 / [deg] Coordinate increment at reference point +CUNIT1 = 'deg' / Units of coordinate increment and value +CUNIT2 = 'deg' / Units of coordinate increment and value +CTYPE1 = 'RA---TAN' / Right ascension, gnomonic projection +CTYPE2 = 'DEC--TAN' / Declination, gnomonic projection +CRVAL1 = 28.717296496293 / [deg] Coordinate value at reference point +CRVAL2 = 40.532891284598 / [deg] Coordinate value at reference point +LONPOLE = 180.0 / [deg] Native longitude of celestial pole +LATPOLE = 40.532891284598 / [deg] Native latitude of celestial pole +MJDREF = 0.0 / [d] MJD of fiducial time +RADESYS = 'FK5' / Equatorial coordinate system +EQUINOX = 2000.0 / [yr] Equinox of equatorial coordinates +END +""".strip() + + @pytest.mark.parametrize("iterable", [False, True]) def test_input_types(valid_celestial_input_shapes, iterable): # Test different kinds of inputs and check the result is always the same @@ -237,16 +262,23 @@ def test_input_types(valid_celestial_input_shapes, iterable): wcs_ref, shape_ref = find_optimal_celestial_wcs([(array, wcs)], frame=FK5()) + if not isinstance(input_value, WCS) and isinstance( + input_value, (BaseLowLevelWCS, BaseHighLevelWCS) + ): + wcs_ref = WCS(fits.Header.fromstring(APE14_HEADER_REF, sep="\n")) + shape_ref = (31, 50) + if iterable: input_value = [input_value] wcs_test, shape_test = find_optimal_celestial_wcs(input_value, frame=FK5(), **kwargs) - assert_header_allclose(wcs_test.to_header(), wcs_ref.to_header()) + assert_wcs_allclose(wcs_test, wcs_ref) assert shape_test == shape_ref if isinstance(input_value, fits.HDUList) and not iterable: # Also check case of not passing hdu_in and having all HDUs being included wcs_test, shape_test = find_optimal_celestial_wcs(input_value, frame=FK5()) - assert_header_allclose(wcs_test.to_header(), wcs_ref.to_header()) + + assert_wcs_allclose(wcs_test, wcs_ref) assert shape_test == shape_ref diff --git a/reproject/tests/helpers.py b/reproject/tests/helpers.py index ae128cc8c..70058e974 100644 --- a/reproject/tests/helpers.py +++ b/reproject/tests/helpers.py @@ -1,6 +1,9 @@ from astropy.io import fits +from astropy.wcs import WCS from numpy.testing import assert_allclose +from reproject.conftest import TestLowLevelWCS + def array_footprint_to_hdulist(array, footprint, header): hdulist = fits.HDUList() @@ -9,7 +12,29 @@ def array_footprint_to_hdulist(array, footprint, header): return hdulist -def assert_header_allclose(header1, header2, **kwargs): +def _underlying_wcs(wcs): + # For testing purposes, try and return an underlying WCS object if equivalent + if hasattr(wcs, "low_level_wcs"): + if isinstance(wcs.low_level_wcs, WCS): + return wcs.low_level_wcs + elif isinstance(wcs.low_level_wcs, TestLowLevelWCS): + return wcs.low_level_wcs._low_level_wcs + return wcs + + +def assert_wcs_allclose(wcs1, wcs2, **kwargs): + # First check whether the WCSes are actually the same, either directly + # or through layers + + if wcs1 is wcs2: + return True + + if _underlying_wcs(wcs1) is _underlying_wcs(wcs2): + return True + + header1 = wcs1.to_header() + header2 = wcs2.to_header() + assert sorted(header1) == sorted(header2) for key1, value1 in header1.items(): diff --git a/reproject/tests/test_utils.py b/reproject/tests/test_utils.py index 0404d7036..a62ed1350 100644 --- a/reproject/tests/test_utils.py +++ b/reproject/tests/test_utils.py @@ -5,7 +5,7 @@ from astropy.utils.data import get_pkg_data_filename from astropy.wcs import WCS -from reproject.tests.helpers import assert_header_allclose +from reproject.tests.helpers import assert_wcs_allclose from reproject.utils import parse_input_data, parse_input_shape, parse_output_projection @@ -15,7 +15,7 @@ def test_parse_input_data(tmpdir, valid_celestial_input_data, request): data, wcs = parse_input_data(input_value, **kwargs) np.testing.assert_allclose(data, array_ref) - assert_header_allclose(wcs.to_header(), wcs_ref.to_header()) + assert_wcs_allclose(wcs, wcs_ref) def test_parse_input_data_invalid(): @@ -45,7 +45,7 @@ def test_parse_input_shape(tmpdir, valid_celestial_input_shapes): shape, wcs = parse_input_shape(input_value, **kwargs) assert shape == array_ref.shape - assert_header_allclose(wcs.to_header(), wcs_ref.to_header()) + assert_wcs_allclose(wcs, wcs_ref) def test_parse_input_shape_invalid(): @@ -78,7 +78,7 @@ def test_parse_output_projection(valid_celestial_output_projections): wcs, shape = parse_output_projection(output_value, **kwargs) assert shape == shape_ref - assert_header_allclose(wcs.to_header(), wcs_ref.to_header()) + assert_wcs_allclose(wcs, wcs_ref) def test_parse_output_projection_invalid_header(simple_celestial_wcs): diff --git a/reproject/utils.py b/reproject/utils.py index 8af00f8f0..11052117e 100644 --- a/reproject/utils.py +++ b/reproject/utils.py @@ -9,7 +9,7 @@ from astropy.io import fits from astropy.io.fits import CompImageHDU, HDUList, Header, ImageHDU, PrimaryHDU from astropy.wcs import WCS -from astropy.wcs.wcsapi import BaseHighLevelWCS, SlicedLowLevelWCS +from astropy.wcs.wcsapi import BaseHighLevelWCS, BaseLowLevelWCS, SlicedLowLevelWCS from astropy.wcs.wcsapi.high_level_wcs_wrapper import HighLevelWCSWrapper from dask.utils import SerializableLock @@ -51,6 +51,8 @@ def parse_input_data(input_data, hdu_in=None): and input_data.low_level_wcs.array_shape is not None ): return input_data.array_shape, input_data + elif isinstance(input_data, BaseLowLevelWCS) and input_data.array_shape is not None: + return input_data.array_shape, HighLevelWCSWrapper(input_data) elif isinstance(input_data, astropy.nddata.NDDataBase): return input_data.data, input_data.wcs else: @@ -93,7 +95,9 @@ def parse_input_shape(input_shape, hdu_in=None): isinstance(input_shape, BaseHighLevelWCS) and input_shape.low_level_wcs.array_shape is not None ): - return input_shape.array_shape, input_shape + return input_shape.low_level_wcs.array_shape, input_shape + elif isinstance(input_shape, BaseLowLevelWCS) and input_shape.array_shape is not None: + return input_shape.array_shape, HighLevelWCSWrapper(input_shape) elif isinstance(input_shape, astropy.nddata.NDDataBase): return input_shape.data.shape, input_shape.wcs else: @@ -148,10 +152,13 @@ def parse_output_projection(output_projection, shape_in=None, shape_out=None, ou "Need to specify shape since output header " "does not contain complete shape information" ) - elif isinstance(output_projection, BaseHighLevelWCS): - wcs_out = output_projection - if getattr(wcs_out, "array_shape") is not None: - shape_out = wcs_out.array_shape + elif isinstance(output_projection, (BaseLowLevelWCS, BaseHighLevelWCS)): + if isinstance(output_projection, BaseLowLevelWCS): + wcs_out = HighLevelWCSWrapper(output_projection) + else: + wcs_out = output_projection + if wcs_out.low_level_wcs.array_shape is not None: + shape_out = wcs_out.low_level_wcs.array_shape elif shape_out is None: raise ValueError( "Need to specify shape_out when specifying output_projection as WCS object" From 6bf7e574552c07e2532c7e1972f1e515788130fb Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Mon, 6 Mar 2023 11:42:45 +0000 Subject: [PATCH 104/366] Ensure reproject_and_coadd works properly with High Level APE14 WCS objects --- reproject/mosaicking/coadd.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/reproject/mosaicking/coadd.py b/reproject/mosaicking/coadd.py index 2f380885e..efe2089c4 100644 --- a/reproject/mosaicking/coadd.py +++ b/reproject/mosaicking/coadd.py @@ -1,6 +1,8 @@ # Licensed under a 3-clause BSD style license - see LICENSE.rst import numpy as np +from astropy.wcs import WCS +from astropy.wcs.wcsapi import SlicedLowLevelWCS from ..utils import parse_input_data, parse_input_weights, parse_output_projection from .background import determine_offset_matrix, solve_corrections_sgd @@ -154,7 +156,13 @@ def reproject_and_coadd( if imax < imin or jmax < jmin: continue - wcs_out_indiv = wcs_out[jmin:jmax, imin:imax] + if isinstance(wcs_out, WCS): + wcs_out_indiv = wcs_out[jmin:jmax, imin:imax] + else: + wcs_out_indiv = SlicedLowLevelWCS( + wcs_out.low_level_wcs, (slice(jmin, jmax), slice(imin, imax)) + ) + shape_out_indiv = (jmax - jmin, imax - imin) # TODO: optimize handling of weights by making reprojection functions From d5daac0e951929c3b9b7a00dce8c00d6a8853355 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Mon, 6 Mar 2023 12:13:54 +0000 Subject: [PATCH 105/366] More tidying up and expand combinations in fixtures --- reproject/adaptive/high_level.py | 9 +- reproject/conftest.py | 131 ++++++++++-------- reproject/interpolation/high_level.py | 9 +- .../mosaicking/tests/test_wcs_helpers.py | 9 +- reproject/mosaicking/wcs_helpers.py | 20 ++- reproject/spherical_intersect/high_level.py | 9 +- reproject/tests/helpers.py | 4 + reproject/tests/test_utils.py | 8 +- reproject/utils.py | 15 +- 9 files changed, 124 insertions(+), 90 deletions(-) diff --git a/reproject/adaptive/high_level.py b/reproject/adaptive/high_level.py index cca0fd050..5f4972f27 100644 --- a/reproject/adaptive/high_level.py +++ b/reproject/adaptive/high_level.py @@ -36,16 +36,15 @@ def reproject_adaptive( input_data The input data to reproject. This can be: - * The name of a FITS file + * The name of a FITS file as a `str` or a `pathlib.Path` object * An `~astropy.io.fits.HDUList` object * An image HDU object such as a `~astropy.io.fits.PrimaryHDU`, `~astropy.io.fits.ImageHDU`, or `~astropy.io.fits.CompImageHDU` instance - * A tuple where the first element is an Numpy array shape tuple - the second element is either a `~astropy.wcs.WCS` or a - `~astropy.io.fits.Header` object * A tuple where the first element is a `~numpy.ndarray` and the - second element is either a `~astropy.wcs.WCS` or a + second element is either a + `~astropy.wcs.wcsapi.BaseLowLevelWCS`, + `~astropy.wcs.wcsapi.BaseHighLevelWCS`, or a `~astropy.io.fits.Header` object * An `~astropy.nddata.NDData` object from which the ``.data`` and ``.wcs`` attributes will be used as the input data. diff --git a/reproject/conftest.py b/reproject/conftest.py index ad3461666..725388728 100644 --- a/reproject/conftest.py +++ b/reproject/conftest.py @@ -38,8 +38,36 @@ def pytest_configure(config): TESTED_VERSIONS["reproject"] = __version__ +class TestLowLevelWCS(SlicedLowLevelWCS): + # The simplest way to get a 'pure' low level WCS is to call SlicedLowLevelWCS + # with an ellipsis slice! + + def __init__(self, low_level_wcs): + self._low_level_wcs = low_level_wcs + super().__init__(low_level_wcs, Ellipsis) + + +class TestHighLevelWCS(HighLevelWCSMixin): + def __init__(self, low_level_wcs): + self._low_level_wcs = low_level_wcs + + @property + def low_level_wcs(self): + return self._low_level_wcs + + # FIXME: due to a bug in astropy we need world_n_dim to be defined here + + @property + def world_n_dim(self): + return self.low_level_wcs.world_n_dim + + @property + def pixel_n_dim(self): + return self.low_level_wcs.pixel_n_dim + + @pytest.fixture -def simple_celestial_wcs(): +def simple_celestial_fits_wcs(): wcs = WCS(naxis=2) wcs.wcs.ctype = "RA---TAN", "DEC--TAN" wcs.wcs.crpix = (1, 2) @@ -49,19 +77,42 @@ def simple_celestial_wcs(): return wcs +@pytest.fixture(params=["fits_wcs", "ape14_low_level_wcs", "ape14_high_level_wcs"]) +def simple_celestial_wcs(request, simple_celestial_fits_wcs): + if request.param == "fits_wcs": + return simple_celestial_fits_wcs + elif request.param == "ape14_low_level_wcs": + return TestLowLevelWCS(simple_celestial_fits_wcs) + elif request.param == "ape14_high_level_wcs": + return TestHighLevelWCS(simple_celestial_fits_wcs) + + +def set_wcs_array_shape(wcs, shape): + if isinstance(wcs, WCS): + wcs._naxis = list(shape[::-1]) + elif isinstance(wcs, TestLowLevelWCS): + wcs._low_level_wcs._naxis = list(shape[::-1]) + elif isinstance(wcs, TestHighLevelWCS): + wcs.low_level_wcs._naxis = list(shape[::-1]) + + def valid_celestial_input(tmp_path, request, wcs): array = np.ones((30, 40)) - hdulist = fits.HDUList( - [ - fits.PrimaryHDU(array, wcs.to_header()), - fits.ImageHDU(array, wcs.to_header()), - fits.CompImageHDU(array, wcs.to_header()), - ] - ) - kwargs = {} + if "hdu" in request.param or request.param in ["filename", "path"]: + if not isinstance(wcs, WCS): + pytest.skip() + + hdulist = fits.HDUList( + [ + fits.PrimaryHDU(array, wcs.to_header()), + fits.ImageHDU(array, wcs.to_header()), + fits.CompImageHDU(array, wcs.to_header()), + ] + ) + if request.param in ["filename", "path"]: input_value = tmp_path / "test.fits" if request.param == "filename": @@ -83,15 +134,9 @@ def valid_celestial_input(tmp_path, request, wcs): input_value = (array, wcs) elif request.param == "nddata": input_value = NDData(data=array, wcs=wcs) - elif request.param == "fits_wcs": - wcs._naxis = list(array.shape[::-1]) + elif request.param == "wcs": + set_wcs_array_shape(wcs, array.shape) input_value = wcs - elif request.param == "ape14_lowlevel_wcs": - wcs._naxis = list(array.shape[::-1]) - input_value = TestLowLevelWCS(wcs) - elif request.param == "ape14_highlevel_wcs": - wcs._naxis = list(array.shape[::-1]) - input_value = TestHighLevelWCS(wcs) elif request.param == "shape_wcs_tuple": input_value = (array.shape, wcs) else: @@ -120,9 +165,7 @@ def valid_celestial_input_data(tmp_path, request, simple_celestial_wcs): @pytest.fixture( params=COMMON_PARAMS + [ - "fits_wcs", - "ape14_lowlevel_wcs", - "ape14_highlevel_wcs", + "wcs", "shape_wcs_tuple", ] ) @@ -130,42 +173,12 @@ def valid_celestial_input_shapes(tmp_path, request, simple_celestial_wcs): return valid_celestial_input(tmp_path, request, simple_celestial_wcs) -class TestLowLevelWCS(SlicedLowLevelWCS): - # The simplest way to get a 'pure' low level WCS is to call SlicedLowLevelWCS - # with an ellipsis slice! - - def __init__(self, low_level_wcs): - self._low_level_wcs = low_level_wcs - super().__init__(low_level_wcs, Ellipsis) - - -class TestHighLevelWCS(HighLevelWCSMixin): - def __init__(self, low_level_wcs): - self._low_level_wcs = low_level_wcs - - @property - def low_level_wcs(self): - return self._low_level_wcs - - # FIXME: due to a bug in astropy we need world_n_dim to be defined here - - @property - def world_n_dim(self): - return self.low_level_wcs.world_n_dim - - @property - def pixel_n_dim(self): - return self.low_level_wcs.pixel_n_dim - - @pytest.fixture( params=[ "wcs_shape", "header", "header_shape", - "fits_wcs", - "ape14_lowlevel_wcs", - "ape14_highlevel_wcs", + "wcs", ] ) def valid_celestial_output_projections(request, simple_celestial_wcs): @@ -182,24 +195,20 @@ def valid_celestial_output_projections(request, simple_celestial_wcs): output_value = wcs kwargs["shape_out"] = shape elif request.param == "header": + if not isinstance(wcs, WCS): + pytest.skip() header = wcs.to_header() header["NAXIS"] = 2 header["NAXIS1"] = 40 header["NAXIS2"] = 30 output_value = header elif request.param == "header_shape": + if not isinstance(wcs, WCS): + pytest.skip() output_value = wcs.to_header() kwargs["shape_out"] = shape - elif request.param == "fits_wcs": - wcs._naxis = (40, 30) + elif request.param == "wcs": + set_wcs_array_shape(wcs, (30, 40)) output_value = wcs - elif request.param == "ape14_lowlevel_wcs": - wcs._naxis = (40, 30) - # Enforce only the low level API - output_value = TestLowLevelWCS(wcs) - elif request.param == "ape14_highlevel_wcs": - wcs._naxis = (40, 30) - # Enforce only the high level API - output_value = TestHighLevelWCS(wcs) return wcs, shape, output_value, kwargs diff --git a/reproject/interpolation/high_level.py b/reproject/interpolation/high_level.py index cd665f994..98b126eca 100644 --- a/reproject/interpolation/high_level.py +++ b/reproject/interpolation/high_level.py @@ -39,16 +39,15 @@ def reproject_interp( input_data The input data to reproject. This can be: - * The name of a FITS file + * The name of a FITS file as a `str` or a `pathlib.Path` object * An `~astropy.io.fits.HDUList` object * An image HDU object such as a `~astropy.io.fits.PrimaryHDU`, `~astropy.io.fits.ImageHDU`, or `~astropy.io.fits.CompImageHDU` instance - * A tuple where the first element is an Numpy array shape tuple - the second element is either a `~astropy.wcs.WCS` or a - `~astropy.io.fits.Header` object * A tuple where the first element is a `~numpy.ndarray` and the - second element is either a `~astropy.wcs.WCS` or a + second element is either a + `~astropy.wcs.wcsapi.BaseLowLevelWCS`, + `~astropy.wcs.wcsapi.BaseHighLevelWCS`, or a `~astropy.io.fits.Header` object * An `~astropy.nddata.NDData` object from which the ``.data`` and ``.wcs`` attributes will be used as the input data. diff --git a/reproject/mosaicking/tests/test_wcs_helpers.py b/reproject/mosaicking/tests/test_wcs_helpers.py index 5293eb231..de5ae13e6 100644 --- a/reproject/mosaicking/tests/test_wcs_helpers.py +++ b/reproject/mosaicking/tests/test_wcs_helpers.py @@ -262,8 +262,13 @@ def test_input_types(valid_celestial_input_shapes, iterable): wcs_ref, shape_ref = find_optimal_celestial_wcs([(array, wcs)], frame=FK5()) - if not isinstance(input_value, WCS) and isinstance( - input_value, (BaseLowLevelWCS, BaseHighLevelWCS) + if ( + not isinstance(input_value, WCS) + and isinstance(input_value, (BaseLowLevelWCS, BaseHighLevelWCS)) + ) or ( + isinstance(input_value, tuple) + and not isinstance(input_value[1], WCS) + and isinstance(input_value[1], (BaseLowLevelWCS, BaseHighLevelWCS)) ): wcs_ref = WCS(fits.Header.fromstring(APE14_HEADER_REF, sep="\n")) shape_ref = (31, 50) diff --git a/reproject/mosaicking/wcs_helpers.py b/reproject/mosaicking/wcs_helpers.py index 951acb065..327e81477 100644 --- a/reproject/mosaicking/wcs_helpers.py +++ b/reproject/mosaicking/wcs_helpers.py @@ -13,6 +13,7 @@ skycoord_to_pixel, wcs_to_celestial_frame, ) +from astropy.wcs.wcsapi import BaseHighLevelWCS, BaseLowLevelWCS from ..utils import parse_input_shape @@ -41,19 +42,26 @@ def find_optimal_celestial_wcs( the final WCS. This should be an iterable containing one entry for each specification, where a single data specification is one of: - * The name of a FITS file + * The name of a FITS file as a `str` or a `pathlib.Path` object * An `~astropy.io.fits.HDUList` object * An image HDU object such as a `~astropy.io.fits.PrimaryHDU`, `~astropy.io.fits.ImageHDU`, or `~astropy.io.fits.CompImageHDU` instance - * A tuple where the first element is an Numpy array shape tuple - the second element is either a `~astropy.wcs.WCS` or a + * A tuple where the first element is an Numpy array shape tuple and + the second element is either a + `~astropy.wcs.wcsapi.BaseLowLevelWCS`, + `~astropy.wcs.wcsapi.BaseHighLevelWCS`, or a `~astropy.io.fits.Header` object * A tuple where the first element is a `~numpy.ndarray` and the - second element is either a `~astropy.wcs.WCS` or a + second element is either a + `~astropy.wcs.wcsapi.BaseLowLevelWCS`, + `~astropy.wcs.wcsapi.BaseHighLevelWCS`, or a `~astropy.io.fits.Header` object * An `~astropy.nddata.NDData` object from which the ``.data`` and ``.wcs`` attributes will be used as the input data. + * A `~astropy.wcs.wcsapi.BaseLowLevelWCS` object with ``array_shape`` set + or a `~astropy.wcs.wcsapi.BaseHighLevelWCS` object whose + underlying low level WCS object has ``array_shape`` set. If only one input data needs to be provided, it is also possible to pass it in without including it in an iterable. @@ -98,7 +106,9 @@ def find_optimal_celestial_wcs( # Handle this explicitly as isiterable(str) is True iterable = False elif isiterable(input_data): - if len(input_data) == 2 and isinstance(input_data[1], (WCS, Header)): + if len(input_data) == 2 and isinstance( + input_data[1], (BaseLowLevelWCS, BaseHighLevelWCS, Header) + ): # Since 2-element tuples are valid single inputs we need to check for this iterable = False else: diff --git a/reproject/spherical_intersect/high_level.py b/reproject/spherical_intersect/high_level.py index 4837d3993..babc27d86 100644 --- a/reproject/spherical_intersect/high_level.py +++ b/reproject/spherical_intersect/high_level.py @@ -19,16 +19,15 @@ def reproject_exact( input_data The input data to reproject. This can be: - * The name of a FITS file + * The name of a FITS file as a `str` or a `pathlib.Path` object * An `~astropy.io.fits.HDUList` object * An image HDU object such as a `~astropy.io.fits.PrimaryHDU`, `~astropy.io.fits.ImageHDU`, or `~astropy.io.fits.CompImageHDU` instance - * A tuple where the first element is an Numpy array shape tuple - the second element is either a `~astropy.wcs.WCS` or a - `~astropy.io.fits.Header` object * A tuple where the first element is a `~numpy.ndarray` and the - second element is either a `~astropy.wcs.WCS` or a + second element is either a + `~astropy.wcs.wcsapi.BaseLowLevelWCS`, + `~astropy.wcs.wcsapi.BaseHighLevelWCS`, or a `~astropy.io.fits.Header` object * An `~astropy.nddata.NDData` object from which the ``.data`` and ``.wcs`` attributes will be used as the input data. diff --git a/reproject/tests/helpers.py b/reproject/tests/helpers.py index 70058e974..d37827e33 100644 --- a/reproject/tests/helpers.py +++ b/reproject/tests/helpers.py @@ -14,11 +14,15 @@ def array_footprint_to_hdulist(array, footprint, header): def _underlying_wcs(wcs): # For testing purposes, try and return an underlying WCS object if equivalent + if hasattr(wcs, "low_level_wcs"): if isinstance(wcs.low_level_wcs, WCS): return wcs.low_level_wcs elif isinstance(wcs.low_level_wcs, TestLowLevelWCS): return wcs.low_level_wcs._low_level_wcs + elif isinstance(wcs, TestLowLevelWCS): + return wcs._low_level_wcs + return wcs diff --git a/reproject/tests/test_utils.py b/reproject/tests/test_utils.py index a62ed1350..88c2510d1 100644 --- a/reproject/tests/test_utils.py +++ b/reproject/tests/test_utils.py @@ -81,11 +81,11 @@ def test_parse_output_projection(valid_celestial_output_projections): assert_wcs_allclose(wcs, wcs_ref) -def test_parse_output_projection_invalid_header(simple_celestial_wcs): +def test_parse_output_projection_invalid_header(simple_celestial_fits_wcs): with pytest.raises(ValueError, match="Need to specify shape"): - parse_output_projection(simple_celestial_wcs.to_header()) + parse_output_projection(simple_celestial_fits_wcs.to_header()) -def test_parse_output_projection_invalid_wcs(simple_celestial_wcs): +def test_parse_output_projection_invalid_wcs(simple_celestial_fits_wcs): with pytest.raises(ValueError, match="Need to specify shape"): - parse_output_projection(simple_celestial_wcs) + parse_output_projection(simple_celestial_fits_wcs) diff --git a/reproject/utils.py b/reproject/utils.py index 11052117e..d79b13b14 100644 --- a/reproject/utils.py +++ b/reproject/utils.py @@ -45,7 +45,10 @@ def parse_input_data(input_data, hdu_in=None): if isinstance(input_data[1], Header): return input_data[0], WCS(input_data[1]) else: - return input_data + if isinstance(input_data[1], BaseHighLevelWCS): + return input_data + else: + return input_data[0], HighLevelWCSWrapper(input_data[1]) elif ( isinstance(input_data, BaseHighLevelWCS) and input_data.low_level_wcs.array_shape is not None @@ -85,12 +88,18 @@ def parse_input_shape(input_shape, hdu_in=None): if isinstance(input_shape[1], Header): return input_shape[0].shape, WCS(input_shape[1]) else: - return input_shape[0].shape, input_shape[1] + if isinstance(input_shape[1], BaseHighLevelWCS): + return input_shape[0].shape, input_shape[1] + else: + return input_shape[0].shape, HighLevelWCSWrapper(input_shape[1]) elif isinstance(input_shape, tuple) and isinstance(input_shape[0], tuple): if isinstance(input_shape[1], Header): return input_shape[0], WCS(input_shape[1]) else: - return input_shape + if isinstance(input_shape[1], BaseHighLevelWCS): + return input_shape + else: + return input_shape[0], HighLevelWCSWrapper(input_shape[1]) elif ( isinstance(input_shape, BaseHighLevelWCS) and input_shape.low_level_wcs.array_shape is not None From 9a54ddfbca2c3e50714c33409d3f6ab2e5261dc8 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Mon, 6 Mar 2023 12:47:25 +0000 Subject: [PATCH 106/366] Updated more reference files --- .../reference/test_reproject_adaptive_2d.fits | Bin 63360 -> 63360 bytes .../test_reproject_adaptive_2d_rotated.fits | Bin 63360 -> 63360 bytes .../test_reproject_adaptive_roundtrip.fits | Bin 270720 -> 270720 bytes ...eproject_adaptive_uncentered_jacobian.fits | Bin 270720 -> 270720 bytes .../tests/reference/test_coadd_solar_map.fits | Bin 264960 -> 264960 bytes 5 files changed, 0 insertions(+), 0 deletions(-) diff --git a/reproject/adaptive/tests/reference/test_reproject_adaptive_2d.fits b/reproject/adaptive/tests/reference/test_reproject_adaptive_2d.fits index 331005aed373f61440a8e17ca04af9e50b909415..d76d1189cb32021669f06eaf8471c93bbaff4b16 100644 GIT binary patch delta 1510 zcmX|BeN0nV6z71<_tdyPKnvUC;gt22qO>R$5xL;l=%yR^1x$u*1)(~nMMWUE0m(9( z;M8&0Y}7@zsIWwZa1|C^)VMhcgB8IK+@dwx9MxqO;|!YNx{JK>{x~Q1=DhRq`~A+X z@dnj+gIYVMLOVLA`hZgBG&g{mHcw}+oZ1zcfe{PHj|07yLp~k3+TDKJ{C5@UX??6a zdift54pG7akyjA1y^Jn44x+^>W0=fw1uN#Mkhp~V&(|cBBvbTjbvpW33*F-8o6*c z>_Sht57N*Ru^V3$qUgSt96=C`Qe-iFiu)toU`Lxc0w1F#$_MS#GMkNZ6Ag2!p$naJ zieWBl=Z=61rD(5UB`!tDtj+pd(pgn>um$R=^qLEJk+jt>t-92-69qv@m68^q4Gn5h z(D{2Ld&1An9Myk>6>1UophHbq-a)k$Txf`?9)mN6KqGP*!tfiPE0RpAwdHm1drTsa z^v;S-Jc%Za$oi2C zxQmTCE9^i;ydIp?5YOQKjYg3MrYQv*=_-tkxwEU(?`dvY zOCZcZF5L&glzW3EyMmrK-Taja2qwK#?6&WEe11ba?d$fP|j4)ew)CZ0i`h z*0k*|WF=aHEq9Di7*d4n9)Gwon@*}C_zCSOER&C#a(Mu4kmFt$J)J~0+! z^?DA<&|+$d_{y&kq9$3-s+W|J4MWMb5KxP@{BUY8>jR+>YYK9NG&yj+>=~&ABBPco z+y}YnC{)1)yvVE(J%z@_G^Km=ziazFW>T8@P5RSgu}?3k!$8$CS#i_BmWdfS?ibG9E-i$6>ryA~C(aui& zG7wCtEYeJ@j~+BW6&7J%k*Iq{%-CgFQU4{WdwcP=fG0VmC$;f=$DQL7)vTwwF1FOS zlO)Z9{Oq4Tr%}bb0TQv<;+@!*ae_NGNoY*qaA;E$)8{zJtDh69+AN`W8Ao$AhXbsm zwhx%JESR;`4MnIeIRFXNSz-hjMakxdTga9A;1K1ubpYhy#qBC)J=?3nL@hg5)N$%8 VV;gve+AD7WTQz@Qma6bw{{z@uVKV># delta 1484 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 b(z|}l*1rR=qzA*J$xAqBDbHEby5#=>gxpji delta 4125 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 diff --git a/reproject/adaptive/tests/reference/test_reproject_adaptive_roundtrip.fits b/reproject/adaptive/tests/reference/test_reproject_adaptive_roundtrip.fits index 2115ba981b131cc6395afde7a529c91ffe4e8d44..0f08aaecfe814a5871dbf6679b60f4f7863cade0 100644 GIT binary patch delta 997 zcmZoTEYNURV1og>rLUJukgJ=5f~^7uFwiql&{v2~iB$k9QpitJNXtwqP0mcrQ7FmG zO`X^%KG}dhz#J&2>+ci{Gr-i;%)ngF00fLJjZIMv$WJOxEhht`8kP23Pp)2nWe=O8|CX=K$?ISLNys0SQwZaTbfvyn;Gbt z8H4NtYI4aeE=kNwPE|-N%Fk6uPR%PRN(K2KxHM0pBwqpIj?^NMmp$Blkj*nQ)w3`( zHn1=>HMKM|u>`w6H67@TlKi~NjMAd|;u3|7)SS%x^rFOqjLc+(oV;|9$p|BX28JLU zsB5HWW@>3{Vq|1yY-wn21UJwF#Smbal$54`-H0#=5+kmDE=WnsMo7jR)%)8TW0z z@vxnpk!y3p%YJXhzuP~UFkV;SJmb(S!oMyOji!vjpE4ps g{2YBI#x@zIHW}t^GAs?O0N$%2(EtDd delta 157 zcmZoTEYNURV1ohsrLUJukgJ=5f~^7uFwiql&{v2~iB$k9QpitJNXtwqP0mcrQ7FmG zO`X^%KG}dhz#J&2>+ci{Gr-i;%)ngF00fLJjZIMv$WJOxEhht`8kP23Pp)2nWe=O8|CX=K$?ISLNys0SQwZaTbfvyn;Gbt z8H4NtYI4aeE=kNwPE|-N%Fk6uPR%PRN(K2KxHM0pBwqpIj?^NMmp$Blkj*nQ)w3`( zHn1=>HMKM|u>`w6H67@TlKi~NjMAd|;u3|7)SS%x^rFOqjLc+(oV;|9$p|BX28JLU zsB5HWW@>3{Vq|1yY-wn21UJwF#Smbal$54`-H0#=5+kmDE=W*D_HA50j5!jrduuwXpS#+bByhdtx-K*qV-7ffX2XJ?$beZvgK z``nDvwoh2XD9^%JzkS0B#`!FaYqtliW&Fp?b&T1e`KgxKVa}B85gQr5Gc$H=57^51 qA!#&G4gN$G5#s0Q>k7=Z>Tdr2+viCzH8HlyFty1rZMA zn-nvFnwU}5KcCS>bbTdyDsk7 z{=tM1C_H8R2MfmIY>bK9ci1yN4`iIPeZfRVes;zg+c(T$ywA-zdHaMVjPfjub=x6OTu1LXG(XicJItBBJz^u{cV@=M?EzaEKO})1yuCn*X#&EAOA1eN~wg3vb0G`v03Q zqIa^iC=pRfh0yl9Kfmkx{jTeKednKJ_H&;z=XF0{kLU9imNq*qZFb49q@lUl4(pw! z>gp!y|JO}dM^}A~`XNteb@C$hKyP(#iYLc|;^wE$rucgu51*GZENQusJhyuPHk<$N z12$~X*WIY2t82JXfBgo%_5bGw1iG`lm_crAN?^e8@CD_rGsImbQXnfOI^YFn>3hdZ zM)Sd};XcUjng@EL=c)KtilBF3u=S;BvX;a2BkAS@{$>xmD7qHy}3x$$eU`Kr5 zWM!p+otr+aFtG&edwh{q<9)C{go;kAng0tMsg%O@k_vD%i~M7sS%9}lFBB)DO7&A)l3JR$Lue33qx=|h5cTz=_i6@*ScRxb41We!8g6N90cvL zecOMu06{**FCwkn5fml*+;zwXK{-`-RLJ{+s$KRwT+T(%E0F{4g&#-ISWdwzFDY>6 zY|xx^{RytN^p~6)Yrx$x_ePl2Zg8Ef>eTMYfy=5Go=iyqH-2AUj=e3o=l*uM6$F4= z`gEG*rwiadEZg1tYd*NIN_GFvP6hYNy&hKeGjRX*Nv}HY1Kupfdo>eT;Hg^IUA4^v zPqgZc#_E^v!882ENnafRo~iZyIGf+#S=EH+97qSxX;avYsYc*=-5b04F%i4~7mjPX z4tPP+-UiE$fhTz6)qGA0yohsQx{vRH7n9ugCGrM%2^onFKjgqmzMr;zW;A%m=h}yj zlkJI^J)+Hbz)M*sIl569BHjsE_5GeJ!3&AcjNR5tzQ|_0s8kg^YwF3vfn+3)`)2vX z{Q$RilVXJ^4&3r{{mzryz`Z*6NAlVA;O3u_T5Z)0?s=`f7a!JwoB7z5+B_ZHQ=#*I z85@C{y6vZ{-g|IQs8lbL9s&3G^nyFi8_8Dw&{pe5BJi}kow_IE!BZRX=@gJXoa-E= z5*Z4f6gGNVTZ22E(tT^!Rd5F#AKrJ$Cx`Zw!sVfUaGT7YFKyt0TgjbNDCdA%q)w#- zc#$30`NQ_-cW~nd?K(Gn1D7{zce+L?xL#%afz{*ST6bq@UQ`Bm%L&&_OGQ)3xo!;E zE;IpmUP4LWK|OHA+uOhX$3)PVl2;S6_aLZaeqGx-5rXRGT%RZ?Mo{6|aoK8d1f|wU z_pdmNAYn+1=w&m4yju2O7Lxaw-(!^Dor0hZj+=VUl_F^2Nt4(GTM;xhc<-#{Ti^^% z56_8u0#1v$nNXDI3r?A-^DozZ;ABodFer-#Cp@s*u}A_O??Mm1f@W~`-mq^zm}*oO0Lcexsay{Mx@6rWoUw)i7K+2)^M_5biz^wRgmj- z;b3H>luO<@0EQs--m?Ev!EoK;))rGiDqN1=rRh7sSk<&lZ<#(Aij7N`jhKNUcFCgS zzZTHHaGGXZy$SkjqX`qkanPSkb#4tIUvc@?n)D8`9Y68=iy7$4>()>9hr&C0g_yzp5_mgl z{Ne}w2XCDXdtN5bhSzVW)Dx@4;3ax+%<0qXXm}+ys2skL2QQ0J4@&Mzc+HfP?V8gI z&j#^BNA^sIr=ZU8Dl-V4I>zo7P8-AHmCL!qg{km}R1%BpHG{{B{R=mC4Z^))%wkc- zXSlonoOoGb3J)p9Aty0gc>3l48Vq2W|!Gp$G8WE3GZr)zlZc_D%wT4u?YUO}*VhY#;r7J_$e-6m`AfMB1H&Jl;_ z2#%gC(R{*#P-g00F`pUaGb~fy&5=ndk(UzPa|GLW<&J^5L15LMSL)yaEcF`Gte$UR zMrBSvDfI!2;jOckR+6*ndiR{uA3xCB7uI=glLOtxE#RcZGz1o^xpfz>M?l5?+p@dp z2$C}CS#A6VoKD9-kHpD!Qh!`}e*AiHO14Z7so4xpmd%|TkB)+qRQptRuZ!a)BBdmMVQJ0LOUOgzj(|IGbk(%!9?i+2TF5^71ING8-BiTGM?>)-7Wpobg?iQ2eeXGE(EUlMX z<^lGp^fvd-6tJ0DJ8Y_k!8Y$}pZ1zu@mi^?4#>^|Te2Z}r8XO^_vfE9Dc%OFX1m7Z zm)BsOt4aUO`~z0}SaW1r0$3rU2|w=19-dhQ3i#*q3cJ`C(*Q|EW6lA7flrLUv-59}2;o;Xpo!1@-|wqTkWSmmP* zvKMv94*!cg|9mZ2CKm>uJF0>Cd*IN`bN9f!-KtZOngJ%8uFpTZ7|d0VpMKt_1QDYt zk1BNc17qvrgovp`MBPwJSa8+|^u^7)vl6%nRBzd%6f+9{@jVThy-fHu#VM=QuL3Pg zA#Cn~HK6*3$T@JIg0gA*@|_{!+!uagafcWdp6aUF6&oxfCdy6+x51JkP%{)>ml zmaXPj>BexM<5-_q=Olvr)HlB@;uYW?CT!}Dv4s0r#L~qZFT=y@=NC(#UU)qHx@(*t z4NpD(wjFk2@JzaP!mD%zJbNx%*vq=WYn>>^O@nNO=Fv}og}|#;k7Jlq39o->2jzQpQ+uf!0M-X^_H@`XG5rO@-Wy_Dg16`bA zA02*!%o3O07?{O_ttdax(%J>~R!Ix_M{B^Q)@`kHb|R;8d$hrzIoOvIygasjd5C~@ zznS7OOazD-INVJug#Y~n&K?;b_(yJSUvgXl{>C?duG}sLzps|tlOtN;cP2VL`iUz1 zcD%}S)hkFDR?-o*MInUB&ZT_QLCBTL$_kmC2pQ2~jyyOCzR7!zd)rs=uNuer2mOR# z)14aCfdUAg`8U(A1w%;hTzQ--iBM&s*(84pLZ7zfS5+N>DB0(^u_Fz?{6Y}+uP;4D(DEG75PKd8#xDIZTnZKJCz_T?XTdL{U3x`Ij)q? zXhoQ$uEW{1HxPMVnVV~U9ii83h9fDLAskkF&9V6c;r2!G#ll_)Ro3lk$rp#<`#N8G zk`DwAmtV7U`~tz*dB$-jyCGoA4xBj5g7iL_xk~k zV}22Mn!j!~%$*9JueRr@pp)R0?nt+0O+~P5N^QCELInHStht!LLGX)Bcgvq|M94w8 zrb_hzgj^G)CXIJq0AE`v|C;?e@Go;8?{!FpU`-rV%Ci>yfB$Lg|NacVVq8Rj{ageq z9CmMN9|CWk)&7FlMEv1{rYePspv^OUb$h}|{cGI88nAi{thqXr@O9Rv8Ph?LRYeFBKE5CUMEnaRbReYRF_8VLy8@ z?=Dy=Bkg{vGr+p8@gQwsG+1x=*(-_AU@N7hYNh@l1$RjD_xv|tmx^ap+L$TP)|@rpIK=p+u++ZPxLhK4t%tyWb+3i;pO8U z^;GXOJW^F2_xkOF+x6MbGG~W`ERP_QH3ZvB6}16DajN>+N3XfEp&*cJ{d`sBPs&(bptE z9qhNSG`|R%)QOeN8C{^Qm>8ubeg@6z&?VLUGoXbIPd{q43AB>W9mUW5KM{72J1U!SYEBE$#l3w$d+QBca;C+{Et@gQ!-~+RDZ)TC# zSMQEgP}z=<)j$6B*wi8<$8xDC@2(yA3)8o%ne7F?!%4%mP#=Q-p5|AEqMA@C}iGIHc#nM>v#q3I@yI&gI^)&;-@oppD!aQ#i>Q?G^v9pctvx6$ss7|;=ugx#t0IL z7KP0|o`@iiS<8y_83@wRK4vTqBKPnEsXcg!KZD08i3zcK~f zH6}Wbe)fSoYTZ4SuL<7$_}kt+0|?H3Tt{K*AtbtI*+3NpPtj;e^98)w=;x5RF==Lsb35JlJ~p}wKw23Z~L$SLkNmwY|;#t0ULiVoYXuD z=F}O#eiiNqBZixo#@GzzrrKM7x@^GwdMaN`<0V+`^yU)=|wjs zcay@%`86JPOd0HjJ(C@Idx;?2yzq$%={;*tY*eUD1S?7F;MqLFq-<;}eJ7g1S{df0 zu%ZAgnW__!o$6rr@HSj;_yFde-Tz+M27;Mbx05R|1I?^_jz*yz_9AS zWTmDC25q8a)jiQFFmjgOC{}g^qtsQlar$bhF;}53w zkQ~YjFnNrHZz*qIKbWlwsZYtpwEll@~T;PJlKtNt@S@3O~oyeMe8ug5T^~!P34I z(B7&}(z|j%yJx;!P0|sxYNJC3^Tt7YdUwZqxdWhm%e*{wng#rnOiziO? z81C@tf3{CG(f~ee3(fW4+u&0^ekQqWC4Bcj)lDHv)wkW@MN&g7C3zUM5rX4*`nVHO&td5wPXW zv7K)35D*lZZu>nF0k^d_XMKGAZ(K|y_P-nXb=6Em{d-&5a&v zIt9Y}C1-R%w&o5G!DTYulYurk4 z6NHkX+|`Zaq@wPbxu1U)0$QBjI`J)~PQNB|+Sr~_4E7cpV zBZN0bPyBVzX#)4IafBlrkT%qpISkW~Tyg;owCxib4=ecW^f!lwF`z)dv+#3VkQ_9K+m(=YXy0;13zD*$-?K0qb@@a;7`QW|x z$oZFi4#AJON4zb1MF@GiKK0Cx58xMd&Kr=jfgre8^L@1mgu9Iz7Viim?L&9v{Ljh= zoj82<+>GxC{ZjvU`14{2o7jz~>&`=vnz!uB3i1=2^Tf z8Qht5O9avu2wKFjU$*vtG;jM`T^{NEO$Q8Z3`Lj0a{G9FO4UkI;l@plxru}QXQ=Y+ zV!|0!_#{OUpi&E+hDv& z444)`c+T1ntzr2u!MxlysPujitVPnk7iNhQ=x}#c^?^rZfLB@gD#7e+x_%E6Rujt$zS2}a2ZQO>GEs$lFe6qPy5B-~J%x7V^1bdBR1+IZ#& z=pVT=Rlg7Z&UQyvuNQ#!Q1M2CWet&#v95CW_JWqZ@dAGu>0r&CChM#?1HXU#Q?6Gy z@Gt*cA46LWfBzcha_yt=Tm0$txoHNV&H6iYCFz8z21}HW&9DU}OEkTN{ohRZ{(RYW ztKtEC(O5~Vw1-a!cS#c?5#A9+!7q~v;T3+PZN!BoN-dp8o~PR{2Nl zzGCCzhT7HekXjD%hJ=PG{PrN`u7#1+3?-? zW2Rh$5-3`|GaD>|KwaF~p|~v(G!5nFE6%r&k@j!=)O!#9%lVY(3epvgWFB?%-6=qz z`QPTN>mDF*vDL-m9gYb2zWujDCxHt!l&IEhIRqTlI{SEKGy+^J&J6{%BVgl_IDfre z2vE=&xRFQ1`2=&h{|&N3A5E3ahgv{+t{K!=`@ik=X%+8HMWD2s6`#m{3Cf#W^4t1{ zLFwzwk-Fpu>Ws^>-#Z=t|I#d|vwgL>b0-9^(_2%!%ODt^uXcV&2%+N4uBqHg2-nqC zk6Kb8w2g_Mda{X>cUUTjQ!2w}P; zU0+g;Y|S&P)SAf?7yO<`nnQRX#%gI~1%%T$jR$V|LQt~s>-lL`5bRm%8GI%N{O4=! zZ6zYWpQbug-kFOK`BSCLN&$jZ?-^;Xj08`ow?}@;YH)Yx$G<8hMo71Lzy07b@an?g zfAbQ8uMOJ|i8iDlq(t-jzRwFFaFEPrTg*Uc+rOF0lvfbl)+te6V2rSlKe5`or4fGG zbhEa?ZiLl2&P|nwgs3olQn<_w!mB-uXGVlqyf~TUsGbHsvslAVgF&dl0`JVf{osqu zE=jm~7a<>xj>@)sA>>J_%z3jn2swjYA`AH&2(hI9`+Cw0!2?_O)e)W%{L}lRgVuF~ z$o#IVsUe*CZPEj2+ZY7%O!I6%n1T0dXUEUE1T!pA-P$ow4(|Og`~9BG12=Tpj`#b9 zz@4G?W)?FHL8h`dGhI5tS%0p*VTj;{1y-}FZxhV=;JupC_W#+6Jkcd-)dYeC%DV)s z$e}S@b*gD$4l%)!(%L(SS0oqnkG5w8IL%G1()X!^0_YtWh&~2Z)x}wxol3!)zo#Uo zJs8Z~tAU$$X@hxg@CiG%RQDlB&!;(VGb#D^|JB|v8mE7go;P6L6y1(ZyWvl__@f<73W}H7zwXmSU|72^ zsR=ob0G(Iv_a|Bqcr4_s=n1Lq!H=&NjT3~vyiIl0k9!CxbN@uo-VcA(mp_~ioI*g} z(MMJm#-NuCU${M40p>iT1=5!5!4y$XINX~-2XpVTM~T}@h`4<5FY(bRn9NB-!!5)j z9v5r;^?M#z)aw$v*1rdT(2%6`qfa z+#>>g`{k^l@nA4y|JC`QT#bN0@h#6Dyalx(h9>0h`-kzc#(=HxJ{AW6m!w;Bjwlm^pR5g8u+ z^}rKbLO*w0O|M@;Ky#3k@4N_66eu1diFgY$_zd;F{bQ~K-)Tk_F;9uQl`=GD_jbUi zZ=yLv$`d|C<@(-=Qt+MmSh0*8I^U-s=LjRHpj^uJQwX08YGx0eKKB@CxvZjNy5Hds zGbcOt4+L~_mKV~OgRb!I!W`8m&@+beMAfA4m|~HCPc(ZA7-qrY^mQ)efe!}x7m`8u z)@vO(rjEcH+jG4_%gB$Zadbr-9sVy44(88(2mhHi0TwI?_&KY6h%#>lt$xkK&=*h8 zO#bcDuX_M$)4Ul^lo7$S?gYa?fJ9zihEr}W}Qa(`Rj{GvNU5D0kdcbb!$f9GYS zh87{Se_PLADh`Km&&D6g3p5}M@~NEpcmTq{s>%$*pAdRIy0TA!)OH6smU-|9gr+n3 zegZiN9!p5w-pYVLRjj{zz#065Ln;=5n-C%p{g6<-L*%qz&x_H4nc#8%`?)KHnB78! zM-y4&1Qg^{n#kpXyO;8bqI?#-W71tiEk8)5JdnR8ll1qe7xs7@m_g+9(52f2T!e2) zz8K;D4-vKZj)}gGA%gLrQ%x9Q+&#tn=_-d2de`gou{VPR^{n)LO5F~=sD+=g`8Z)e zPx{d?>I(kH%b7myZQ!?G|8L@TF!<3St0F7O9&5hqJYP=sxOVN0Sv}d{)3$v2Z`m05 zqbGt7oghTmnKSygYZ!vYjv|}7KjetS<`yJM5f(1%T(LC|p?1!f%s*U1=-%+!BV+3z zY|dWvu>Ukf!o-6QnzlEBpP!C=c{gAffKNZw%P0^OmiX0Ttlx9=M{1D50Z)&T;` znc-DmU99`TI2LTNwTb+ik4B`Qn7oUg})DcL5Kqk1YjQ*9g|+&!xQl>tMZm2$yp% zgy`12kaBSZ(}4D5N$?yn81+m=yU(BxxbJ?c`V{ouW_gW47ts4c!hR&ZBGy^+f^*UT z;Q!_PlA+~8@H?vg?_h=*{9aP$$PDVje_2MEP<#sf6&@aT$`}2G-zS^F&+KdPuXUbl zcvAp+q@>aMWRk*B&Decbi$g|ym;1c*AecON_e{|ZFeA017=999sdYB;(!0RATvHo! zt{tp)-svC3%LpxewsR5ZA#wAKpD7+!BG#E|woK(IqOLb7L>>+!GX3>_{nLaH%Q@5< zi=K9q5kAdLpPU8u;`%dD#ayt4mOGq&WegT&yIGZxeBsBwtNjy1j>}nOZ*(9d72I+A zr}KrVT)nQ*v@}rdvjd-*Y=N(%lA@Y)B7Co<`KazW4&Nu9<+eH9@Ev}ane(v(l)E*v z6m$p?K0gt(H`5KYQ?#cdSz7`Mey8lEmr_BSGM`GV7XxMN&0|MpWB9(Ey=8=R9Deb7 z8VRFwK;QZ~=i%2$aw-zP{3ekO`Veze^Jq6{KOc1kFPI5$H=Q;qaSA+SK06+G9t`)} z;_Y8`D&fB4L#Vn#9^9uHOt>%~!t=rF+jdW5;q$CiWW4s;R?xI>yS@`lC+c-Wtb908 zv65ci2GkMI!m4*k{zpKoxbZ;tvM^A`oZn2}kO%6M&}Hr3L`KI3xqK(+g{l`7E+az< z*@xfNg{#a!$v5{=?wbt?`)jOSXg&PSuC;l8ge1GR^?hK>Z3kmQKCtUnC0I<+MjIoC zWYSvET^}9o0^|4F6T56lo}lB;CAAb^1fD&%{_Z#%{&x-!=`%^AVVHO;qTPrP-+<5c z=QBW6Kc_BydK#2HsUed^>*0Iio@PO(3w$Onp8s019zOiCVdwe1gp3>tSt@G-pPxp` zM-FX_jP&Q{1*r-uGW2jl$dL=Ri`y;L&zJB zNS!UFBTQea;%UihgiSMXURAgqqSCd?m;RC_On0u^vfB)V)<;p-U9Lo^yyi1c&Uy%~ z#Hwnawh;Ro@5&WB7hollTi5;m{=Dxs*5g1@uP z%;f0^&Mnzu87d@?_jyDt2}TI-!(s=K<7tEhef+|wzdljRUv{=xZ}i~&LtzEssq@W_3cJL>N82-$G} zhprVNc#hWjN|!f8^&d0~wgIREk^Zel_Ft?;e#w{=aqh{r!1*#SVIC!_JedUu<{VqxWTRS+9 z?mYhc?km_TSNzy>_L2NipUTmT+F?g(zYrzFm-(ax!rY@zd zuLj$rB%na+Kd@a_e~nC+2WwCy+O*V04J;hVSUpAq)2ZaemRn7M}CjtzqeNy;amG!)_SYsyF9SMc-qvE7S6Ejd43 z=wbruRM`;OoUQO*aQ}v4OBaDo&66qSGe9ptawEdu6*QUQt#1k^;PEMJ;nqnhh}>vQ z?f1)k;Wjk#CP}B5#3p)FyatNk!R7WFuet@VXuQ8MYJ=-|}rj@6AcjvA0J5J4q!O+A9h#5l&Bu+1C5#2N+tFS9d%M z0X_VHwaH!bMrN2|fAn?GqE0`TmZgIt)2%7pN}^t4`E%nm``~rhApAt|cX(~TzW;&U zJ$TtIn=zz+242a@JHwWAz-#a$Rg*3OZ?Cn-3lE4s!~0*usdWUe`#fnmeNiU^zV|=V zy~6(zAn{Od%YqWnWc?C8?<5du=f3Vik~#H{u)AV7{}zGBlS{0KB^}tiRlLsKlT6DW zO_;~0lB~|}qq?^(i4k#lz=-z+jB1wsvXg$K*_=F_usIn4m(NS>*jIo+g=MR+iE7AY zcgXYB+m79c&|*4FTV8_jSB|~o=M)hhCGRTn#|Gic)69CG<|3@Rf&27y7Q!s4hZsF` zAS&6vXo==qh<>md68HQgcAHhEmR}CSD90`uKf8&rf}7lV>gy2pMe;*O^IU{a%N?{P zzk#rawPG{R86qt5X{m^9z(ttJ6%*FAZiwDzOU!Y+g3x0t>@t5#K&Zx-Ze6|&{3O2d zJYh9L3VhCccgKRyPUu}$LXsXf>azvSTOr(eqoQ~1ObDmwuD!d8RL0wBi{$^>LJ&XY zZ}1i;@bwDs9}qSnxc***_q7&M$kY|L?YRu@r+kr{1UNJs zZ17gv3xP+2!Q%N0h^~qrUz0q6h!xW&&ZP_?l6IA+_)#54b|z=~n;u1&!|s4M!B2!t z{0vB^e1kxo)p=ZuL{N^OtM^%Q1fil*C2h%i2$d{C3v86ZPZSjE{6{M1FOiDtg*M`R zs+@Wdb#XQLYu{?E^AkbvYD$`loj5{2HV#F1tNeJ(=sCCtR-Sy(N8Gg_*$TVE zwqQTr==88IkmR(!sEegjYQcW!R_`;=05)H0qa&vU?Dcg;E#&fHTlts1{x=)U8Mkkz zN68W3z~;YrTn&0eYE$PrfKAQ96qy_X-C5v-qSdhhuB4G;zE=LThrOM|N+Dhcdjf!!f%Y9d$;X3(RB zZ}=BUjW#_z)<#UGCw(&)EAIxif*CZ=xEd7s1^tDJS)dG;KCAWA18pwDDsEOF{H{4o zU)tt`fE__63$E%T;M=C2K~XXS%kpOaN+X(4Q0`e5y9*4dL$*a%M~VNZSikUcR}UBw zeP<=!wt#hS%l8P2wcwmGv5O0x3HHgd6Zcd4z`D4$+^gLjOw%(QaeI3*nVkGK^C@xj ztKa5ao#Fzjiu?hk`egWiH#{+YbQ&m~F%E@i?|}Mp@yHx|3j7wv$$16m!+$Pq<)ecx z@DEB-kn0pF!Jj2he^y*X7|#xi+iUIM&xvE%GU^bp_P>mFZ4U(QNgmn3CS-J1pU?1y zMEHN|vtv>$;io<0Q@ABDsT5Qjb|;fMVcKn{Ubh2&uKC{fU)~_V(curhaTs*tob~N& zTT;OM#`kCvLXsGA!Qcy^MwDcH+Bh9PYW!0$ zH-!6hEsGB^yGQ`TF6y~10T#MW-`Ay(phWFk6<6hh@Ut~$FM1$D=7JWXKke9< zX5$?MPFL4IBTrw7P|J=T-36^Wb6w!`F6fm12gML#9x8Ih& z=&NHJfx{i|7borqHPk1iLb3ziz1vtG{O|DFe)oamMv_RopVl|`Z4;PPzqEhO93vgm zyvfiPw?SJXa8?%&h1X};#x$*F9?(;fpd<))sNfHZ_#NorN(VNeAg>QD*{zv6| zK&jjczWfrti+g|R$NC}OV%Cyf;>Qq|UX+zr!a{7T$NX>Yt%#knjNYno7BQ-4 zB(FUDf#_AsVjl;pA!K^!kh?PN49Yw>4v`~^bQj(0wQi~+> zd>w?_U5+YNy#{_AwBxEI`a?Ui|nperAm4qKCG?4Z0ySn4d$ zKWr@Qzw{Y_pFS~d7tSHd$lN#TpEq$t&eksXBwTRhwEcl+5(vo3p1Cc9aDnwFA96$u zSHPN0|GZkyhQMOG#*1_<0u;Ua%|l7=*D_^K*+&AGBR95&ChZ6NU8+xA-3zewLIjKH zFTf0Vwez{l_n7S8G>E(<=W`u9nIp>k;UYFQ=ZSegU0vi}CXVLAeVKuG!UXiGYh=r<#>U!0%zY z(-dM_Q@>r4YU-PSzoa;CDH$Mxu~AN0%#y5Y*ep6G7wrs||Ct3V`6MiIzvbVmcO;6D zV;3F-SZ_Lw6#|I9rlvi-}lXBXCCPf zL}%_dBrZi@J@bQhu^t$=zkL1WydA8AmlD0MDuMmJXv5Q&bg;{qY5#pJ1@pP+{>d33 z1ifzE?6_Y3F_;>Do0aBQfu$&Wq4kIon46jZTIQ1Ua=O=iN6!)XTMgP9W@^Frxr%Af z+hKSnaixoE9;Zp6LP3+YhT!M=QZu*+E=N`=VvW^d5j{tUPp401v|tl`|WFXhCi z92{EruUmP?1RM&*CY^%Paj>a0oRXc4LkF8HIKI>2^s@WY@!?f)vB-@MA}cOjr}Tg9 zNYjDqjT3IS41?gd*5GtE+Y@d_22cACcg?LhL+*#U6WprboQSK>f!pZysRs|4k({>v zDvLQwec{fp$zGKI5bm}56O?3EcucvWJ=-8b(ehj{AMMiQ&`d_8)zrRg2K zUWBc_BBl!O3)OWdUPN5pIkNEEk|hXUr{aG_ya4fftNaXSzC-*Tm!)~-vWRy|`>x^I zhj>>Bnxum};;s9w=M0kmKi+6xzGmZ8#4BxhCb3Q$aeWuxv?*jEuJC-KnfPhMY2VRG z4(A|dtiv(BISkRo-y?3L3Q-Of2KS#_z>x`S*`47gI5OkU{fpxV5V2UBafqRbaKp7p z7bQu2`NQnNSJj&lZgB6)PtihzN3U>vo#TM;i1JTPq75?<_RHXmi@`gD`Tsa8OQ^TV z+oIc_eGbB^(HY8z6iC{)!sIGjL?<`C zLU>|YMMvs-9I<%mpRxTuA|II_>Rj4@@NkzikE=ZiPT#mY_aXV>Z#Ea|%1R;HVaS{O zO-E>9;)PlD?;*5vvZB-{k~v_3g87_D2+UWhT%279LGehz{V-*OU0<=&aw9o1KkA$* zvRe>osMA#U;2|RKy%as|*V%+4ktsE1P5ThOppoY&(t&8@HSyV@b_ng*u=wuxrwHwB zFERHRg($VZK@c*VKq;kwW8dc^EcmvQD)pYf)z{Nn2ShG(DKTmJPG2SNs8{5H$Tck;F=?u-Gj>r$(*{=qWu~C zSSyNhW)lx*>N+z@mjwKuSZkl}Clykfbuoslq6^Tr92(Ll!GxW6ZmVc_!tZYE%Y)Yw z;IFblv{!Qm0*K|i;NU|9>TAatK9hm}kn$qy6hop7=>f$t1YCZzcymLBh*8m0jdC-E zrwDkgVc%V|3I59KT^{6W!2eiUSeil}sSCpaM;eG>SVT)5(dq#+@83tGH+Bf5iB9ew zQV0Du!*u>_2{4ufu41L7BH-n3%7H<3&@5Mr*_aN%cfpj%SQ8IW>XwdVsA_OTA*ZyxpS76Wt6zx%bz3&?7MNBX`uYRSqhyUp2eO-VFk ztA&ZnKG1p9%za~2Fm)fVaMdJ9?)M84WQkwMTX4a0+dOly#;!z#ZTWLe;3hopi# z*n9bIf>-{bC6*!$QYyXA7tQ`y16O6LJpX+qT-)pyJN5HO3GHgA7!|-JOTDx2dJG(g z`a6biM8JW`+wv?v9tQ)(6$?0P;Fvt`1M>t7P82 zA<%i_i)T?-LWP$x=V%zuqoqcLNd z#!HAjZg6+4PZeUn9j6-`+aT^xk7l-$4C3kyKP!@g5l8bpGg#Y+xM@>Yb!HtxY)0fh zH-8{@(Xf~5XDP%S`~2|X_9ci}8kR9U?Jc4!V?s+Bnh?EvIqTYv7l>-0W!dCNBXaCu zP5V#*BA1A^TcyPTM-F>e|0(Z8M0Mb)TFNg(=%2gQzaa_X)uHUEi+>@!Ozh^^OM8S@ z=uiFCww}muWzD$hq!0~@%hy;RMi^$j(bkWLX!k_bqma)C-M#d$+Q1eFYiZw>I1@s0 zDKPlk#&(h${G5L2Q!GN`YJ2~xia{jOiMPrbQ%7hr-+8+wNxglR9MdgW0O1RLL#J;9 z#q1S3tQ$`lPoeahTd(;9L|o(CIaz>kzme4f$#aM}-7ykX(1~!>O7#lmc?i99=tt=t z;`dGe_VAhknM2Csq)-020l}w!xp!^Z5LoV3{aaT7ft1_bnI)v63bY<(&e?kwqIX@Z zcH5E<-1YB$Z01fJsk|B`6CgljghoeF^$bML&aAG8`i+Q0hOztX1caYl0!_{fgsPX*Ib$SRIZLra>dGF31W^B2OTIu*x5o{Gs(LVG zo3?g6QbVBU4arT3e+f|Ra@g*bPe_Nh;jKbl_!*rke)Hru0-Nan-7%mOcinK^iRsOx zzP#q!{uuzn!FGdG-AvG{i~_8tiyndgYSVT7H~k1$TC3Icipb=Pa=$8XH-ehIv3G}L z+5grj%rDws2ERSu2JXLUhd)(gjPZ?BLfN>NKc7f|{)RICP)HWfmdyH<`o5T~FV-!a z=SWbZw<5vAMO9@IR^aG6KJ!r{%Ob-XkB77t6Hn-+c3Imq(DyvAPXFzI zfNWze(d3E~pc`D0T4WVM!sMm@*u?8)zx|it>X}MF!2!Y6RB4h_eA6b^!XS(P=GV?z z-3pG$q{P;m5d=L9y=JjFj;w1^u$!D_hv0?fhxcrXCr-!V2k+ySf~T*=o4t;#BMIh!mRGM<5*SdAc0a5Q`aa+513iR_*~{-2Yc(MM$ziH~)$%8xhu;2l>JIUW z-Cw+_&R7E8L2jE2Q;UREV~t6Nm**(5r{t>Gu%$$K0gYkxhIyYUxp@~SBo z=ZfGwA?vzwpchWB{mdTiR)W*t%kLGo0nTyjER*eBaQMpNQu{z#xVdI8vMnHDH{nzK z-IFD77l~awQY5tyZnmZGeplwfwS3<1mHA|KVxNSC-0r9FnKsn1{qqY@7b%SmoHd94 z{0)qV;a2$DT~F_RL7Ge#)2g$t!w4<^*0gmoS(%z_arJ_VIXHh<#n+E41}|fgfz7vj z2x%T(y->jbg3RM-r`MGsRBySo+*$`xfHIG%r>!YK7}Iq1mJljgg=bm0l*H)6Psat1 z93Ft+?gkgDWB(!cpwyHTrDaIIkn=jP%K}LnFFb6TWs$&cJKM5>sN^iu&JU%*h&j8q z|BaO%q5_p~j#{67vWYv=bUs=Lb%KHov&%` z2)}(k`atU*V&nZk6rFcG)&CdAGZGmYDMH97TSi00u~!+DkWwUjUG8wLYwr=FWGkd; zBqAfLWmHzu7m2LMj5Pet@2?);z9sj%pYuNN*Ll63r@Eg|rU_y2p6ddy6NKp$l}AXJ zK5d;EWQSm8 zR(cSGjOslMKaK-N=>zN3#g{;<6uXeHD+%TH`Y6ghl--XTzbPQ1c3j!#Awj#71wl$7 z?H{iGg@8AV`_9N#K!C;S`KW-S7#>Lp7OaK$3dWxd3$~!y3`fxp9|U*g8y0d<>PNd zfbzA2nb93%gnWHt-x6hjeB^zER?Zoq2);P@hHDe+!TrP6-e6kh^wQ@7Y+QWi8LR8x z-2#Hah}ikJ-!RQgui@IV3%vhuMeZ2J$jALiTAOq|9S$1L*beOf2j0(RXZ{?_0`KyK z(`zP%;8QIyosg&nJ|o*Jchul`zRB{4BxY~Dn+ehdA5l9VJJ9}U>JncW8p=uHYB69BwSNzMVIptfIv8Av7ns0#$a0gM54+@qmXV*k zalVl7z~a6j#t#4KtW!=20V%0W<;_<#E|I7QRg8x)%+J~wLFa|Jp88z+2Q2;Iqq?a_ z%~b~PjH{BlGPdCLc*VLSs~)^=PS%b&r{EyM?{KvvJ$TM&Ue_)}goN$8Fq5)j@EQGW z+Q5xLamM@Zn)~o@qPw;Fx)ELQ6CF;KH*|o=m>95kyFXGpI9WM|=8;3vIVpbbK?3;e zE5BDhhcW0F6WGqTuvUC8c~hYmYzEebGBuUp^2q0o z1D{Q>G&hQW`Mv;-bd3siyqCdofGIOqd>x$Pc6zKKr^I=$0o&i7XtP^p9eI7m1n+)- zhxTOP6x@1||D;Sm5c2-@+7New&#QrP4w@New!+VISfm95S)(&EUP~A#|1rMWaycHT zrZbl=xuOj>$#lC$4SKzj)+4_OqHWIbv3;Hoy5p*ib&W--7`U##l^t;tVoL-5lb3r2 z2`y=#txU2Z(ScLGI!hFiSpS*dy-^HFb)k=Wm}Vdurx#4A=iwUP?pYdh?+jd{{@wVL z#0A%)2FUFfR3J$&V^ZLW5X38K&3Eifhv=b{&Ua#&5Rq2I_p~boB7U4UvQtrmNbR4O z+_S$zWPDWd+nT=+p`mEAx%wHx(h{;bN^oj6>zCw#5vCC7v?GSPSYIk>auv1g*!@oG z$=-bov_F0{KlwMp5Rw*g;FKgzHr?$czivToSIwr7y?-Z!s8}UuKEfcOPHEJ66Vz;F zqZX<&ts$t{gW*KceF*ke?cbF*2{a!m_kknX5TYb+SFaHTAz``?wtEbq#!HRgfASLq zeYkNZKXVrZt}1DqWjqeSs#eN{d4C{8kCx-%POlEZ3P-ZHHvszazG?{hq(Go=$w!jL zRR~PA_?KwFjCq~kvA#Lk;GbT0@*nScpjtDr-Lyd>#|YgU)#Q^9uzO@m4?C!!Yp3!q zrd|YE&6gu542w{f-`dKMFm?ydw$IZ`P1pYlS3|rmm!S9Bh(oVp@wwu=1&@au;THAQ-zXu*o4s(UW zec;*tmDzR&1-#+t{4O3p@VSysT(mmxvuj-F|0U^9`~Jt-1!T5EiS?iOktR|;T0e*P_}>iQ zF7+m0&o-4->j>y(G5d2eK?M8+m445RL<2=a$|+S0{XVDmJ&NGMFv;NDiAEjtlAVtG zZS@vji2F**Azk@f++|78_8y5)m+ zUG?G#XS7P^4P4oA@jIGEYRtV^T2W9a5Z#mg2S~#u(H%W1E3uaM}6V(reWdT=ZIt8k=;$>0JSTWThlr(fUK1T@pD9mptEmySr8k z*8dce-k*p7>+c`Bg`S;(OFN&u9eZj5c3flB6TM`({FgmgY{>^MzizZW7~~9>osEsY z2RwqyrLb7geI4wAl}`FHCV{=jLuJnr1GsXIbLjOMOi=ERv<|qE3N8;15$YVdF)6;` zpxiGCUJq$KUSIdBgLi;({)f&bAOtO1jeMy9pLSWt&J~gRA%otmu zxj-Srnst;*Z^u9PL;J_X_Fjn6A{>r(N9ncryuEWRCxkcsSLqOq`hVCbLy53YiV)g$ z$=H_M1R+E5-^{{Cf%cbGKYhg;f?m&y8ALvTfRn%Ge@9}-#B_Lu&(#WH8de(R8DAji z#GP`=ogoP9dtI_#{Stx_n(tYrJ3#PV|M&xEmr;~njop`e6oNUE@_NNl>^|3X<>eze zEO<3_;^&xY5Rw>8r{ypS!C@y6DJKVkY0nwH(aAtSgwfzHniKdhe^2`<^&SG5e+}Bt zB6eh#;>!%pQt;b-vpO~mNoyuy1h4jRgd~|MF&^UrYOdl2W6>X=J|8j3d4)DS`68B= zs@uRn?K|;!wjEkL?$tgpMvu6D?dQP9G&F}S4YQo5&_l?6%Q>xw0T6m_&x8I1F$kTn zeYhL5pCLaN+vIhFA!Lf@X0`t^gbLe@ub(*tp%ophReO#=*p@w=Y6&!y?QgKmGmAr@ zh94N!$OcW@GKFaC|VM*n!q;t@V!OVy1;D>Z8AjE5%$PzJf zbz<{=#ibE&uiLS(w|*8yX{ooAWelI$S$8FAw1elGq0X6h7Q5?ip%qpLy4 z4G=!0Lp!wpBT%n%S*BrTGe9&pd}i$>j$FTmn(#XSbu~Mx>evD%mi|joe42$44T}e1 zJQ2HAwz{p@kdR0NSKbt%Qkq)qam8K(9nGRbk(aQZP{qEPNnpl%xW1F-y$^`oK9SOm zl0Xb(jN;au1mCw?9^77CL`9^McK3O;9(etrzK|TR!UB}3Wgvkb_SJ2|S#5-`vYB*wQ z)e+&C;SP>i+tptKzIVDE7n~S?bhT%j5_Y#Fp~p>=ZZbfm7p5;OMEp^m$g-XFG!QdR z_Rh7T!EjD#r*U8h_+BxO82pQ+`{4J^BaP^OJE7_{-Z%~3{r3(Z{-_Fs#He4-#`XY# zR#Eu6;xk$%e=lxJ{tq*o{GQwgDv(LB=lEHX92M~S7_KE@gBnor=t7Wu5WX`Pa__2W z1JQBvn9va-kbL;hs)`K)rQqDoe7$eTc949ACqMD@X0w3DauktX zZ5f;`4^P?B<%5HV0h6wE5;!2r&y^?+SAW*LuFdxWht{xhadQ>8@=I_!3Qs}%r(sU2 z@_KOj%h$Z}VOM@%%&eQth>Tq>!*z#W3JaS6d zx9lYHft%w+vHZt4m{>6`&KqY2QdFuAd%#@?XuACAM7KADV4P9+QyYY_e&V~}RsghD z7C(9qA!MR)i!$MP2KX^w6Y^!O2V%LjUMM9O2(40a>kz%|J`MOv*+7~YvnBP)s!nG@Oe z$HoLA67PHOUCe-RC!xK=w11)yChPxt*W-2wUAny}iIIvh(E;K6Z%`&beT^%YxeyhQ zZQ+g#*CBkX`H=;?``BJb*!}&Z1ffjHO5I5$pk2J3$+N-)!K3@C@{bQeu=R1_r~UR2 zsGs6GrW$}Sik|h`p(PN|WzF%;4<(&N(R&s@cR{d8`Vh^21^FDOn)DVbJx~d`>fmX0| z8L`F>U(zhIE&2pm-t!s{kb(LuPp>1x38-C@M2!u^e29xbs3{Qy{{#0$*X1gJI{Smx zI%MYn{yFUaC$sQS>Gj;%_QV7NR3oN4WB!2uYMGr(*ct?U5C8iVr`tgWU132nZV=pU zUKdBn$4_sdqTe;_|4*>*(OSI%v@;i1EzKKW|QM#0CUx z-BnrRt_J=FLfoaP_TV2z6Cj5i??9;qwuXOt0YU2GzfKOJCM2Wq{u}0hL)7kwzZ*fV zXY69VsEH#44scmUD59&qEljv_*acZwF3*NL&A~r}P?;L>6R7VFu%1rGenG%YYCZw4 zW+A+xZG^sWmlF^~J#v5n zGut6&H?<5Ff!t=b@B24-@GEINux8^3{$+!wFFnIAOx_*D!G@)Kv9%?q2#3ra=bx24 z!NG&dIrj2mB%KT|XSpZ6#gRCmqI@CF1Xm^BI1Fhk&3rHhZZV$eyA zEB8%4x@(%ohKeL`{_hvu`YCIH0;JJywu6mmjZ`Y}8SHEW-&vy7LdyT+HG85J1Cuec z$?>wIzW_BH`cduk=w1sCwounR0lsd_^b)8C5>JkE`V*f6*(g{bz|s-O;auXqHs^py zw)i=bPQiBm_l}=_{XjhbXzKkBcZ}&5%(%tUQW17_G@;?GArMUwh|z|Q$MtpP0utJ2 zDDG7^GHQS@zkeV`_C65nEMgKR&jE>p{Rc_-0Qky15X>^22aoR#&+hVE0iS>E#rJm# zfX`&%>;yR!yob;JvwH9beZY(J*E^-bw|$lM&7poE#fZ)kbkl(%(AHr})4-VQqd11$ z``Ce8S}=D>P!s&FY%x_tvI&VH`pt5ID3H|3dyb{<11dA!uuMb@kZac}9h_0y$&l0k zoN58Y&esCPqeb8&{#m7lwgjGKQ3?gqpTP~4A{lSYmPafo?lsduEQhS*JIZ-*V)hd6 z85;$sbeZ?G_~_^0_K5FRSwIYU&9J>sej5mcjImv-Pto7^j-p8)hNoE15i^zIX_zn(5l^-^28ev%XUB7Co>@C?0&mPFbW8f z7q88K!_eukX`uA>#yrJatJZusodp4*){(XVyfit^? zT`C|>>^jR!PZx;opqJD|NJT99Qn*6tX^52|KiOLB1+g3leBPco1+iyo)ZU+dTo9XC z+rKO6w@bFW*i&|6& zOHa3Hzs3lmg}&mW`rQ!vJT#bZ!49wSCeJihPe5oie>vx*AcWlUe!U)%4z%>A5r-$R z=S@5D8;wc$+U^oey>#y5Cn7;Ih|91bD#`%@cPk%L`uZO}HqIm6bSE$Yc`Il9 zkuuOU1dnOchXQTNTtj`f41xx2st4apK=44uN|%=^giyF*qNI!=WUyYa>Vh56-W-iH zs6nMfJ>O=>!;=u`eok?Z{Sx@EnMt$02lW5mzm$69&^GXEP}|UFe+2#{D&0FRFAS4! z=gHtfv>Z96S=q}I0)Dgpbd@ZCfZ{6sz+JjP87upDXcEIHr%Zld9b7>9?s5i0-eI5y za6Pqsq5*+w*9`6@n?hi6+Cto+|4>V|hLfQ74h*3?ZAO)XEx7o03`{%5{vV{SSJ z0fVy%{Q{9Fz1Y{BR%eEwz4NZK-7OG!zOjwP^BfKmr%U4Z)T329r2mH1BM3PE#Jg~- z9Qe1H^L-Tg4S|~Tu_xH8Ac%eGc8@q6o*^Oe1%DX8UyG-)tNWF1z>;k4Zc za-|V!ov@kbWe#M{6|T7OVIVS*p3XJg1fR#x-_X4?#sHFE&%�AW!d$eMQLuiep%@ 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⪚_s2a2}VC{dF`lmk$BjU78EW)4g-DDZPQmk#zLTNi}H-UM5VqXnBL zoQ6@ZzdXyOllib~9VKKrYUL6v253%$fj2xd{Vo2rsU05Lc?KFiF@maq|57dt|AoFC zvm8*8%RU$MfH&iAyBqj)hu0sv=k25ZMtp<7yNh~xWbZm<=C&D1cPY99fUc&mk%N z`;a$#s7=ggS87**Rmr)yzJI5o{!QZe3fu;HTMJ@-!u*lkvZL;H;B~_Pa#HRL6y5BQ z57jrx#LXC^B+{=?r6dS!QKKYjHlWrm*4e}kYP7O!%OV@0c2&2+mklG}v8L2_pScax zMXPoS6Dm-Ai=4R<^$TcG0NNFhtdp^}fC%nF{X*h08`BC2(|WA!7&NGLn{{H}KSa@O zc8G}&tgfxOHf^Z~`17GP%?ck_sN41Wq$V?POTTbD@mV>_Zj-)uQGJ_w=Y8!cxlcV~tJJ&~Uwf=+=oMXy|IXqh419>HwAF0H=6(&fmH? zKbo58B8pZax^xVwC@!Mx^9!|#DN`q-Z!rO9iDAX`vK;kFi2HV4R6;pjU=7LQC3ciuAx{(%!@HfMd)_tb5tEY%_9zLE2vT*1-1PA z_pixcpdoF9*2R+h=vzs7D+!t<*G`}LX^%OT>|yPn@FGB#u6N@dimFKFrC8K4RG{NS z(ylwo9+CwB)elKxU(|m@LUcvXM^vdh(~nXfQORHCfbvJA$al1^rU1`EMK#IfhDFtE zxxp!@Nozpy#)(j~B2Q~j$S|l`UOKCElo={&SkS%<4Z|LT^vn-ztDzF?h00nA+SSk) z*LVBh{`IJ?C5mF+_c0{{YW@{Zh;lp@J)zn)6vPj<=rb)eFk3#WFGKxPIy(EDtE50OZJ5axoUe=+wk?ghy zHH~DOzOPX06+K=;$t&W)^RibRIk*Dt-cX6Siq;zPwNX$%=7oLZssT`E`_GyzwK+VA zcskwis}dy|TC@OV8ulYqislp1c7BX5yv?~dXG@?7%9@DnYRqXOAwp2o#Lj2W{y@jK zREkot=q;J*6Y4jU*e}qxnf$N^the8IYjaW^tQe;|>EBF8)HKticHZt?hgr)o>OJ*3 zm!K)mA**4p11jEA^X3DuYPi?B!QY{=+)ziqX%)OIsHps5=>jium)4&QN`waG($4{1 zZ}Bbf2|j}uc;(pSojiUfGzP{U)HTe5ml@p@0qLz6_L2IOVbE|f{jIx~12mLxRJ`rg z4ljBudzs&M!JLnz#$8_XkpO0*(d84_D+VQ>*nTGC!9`dCW=}`MB zDTcKYFk4aCO5gVJZ5=PE%$a{<6D-yO{oUc!vb|FwD?HL{yW2icI8~b!WE#V{Eti{I zoR-3wc<0-Lefna{H?lw^>U}3O8T01fNp$TzXwJ<#WHCaCF>UnvFQ&DzZUPRP-0xN_ z%I^i5Y0kf=_x+4oKPZ9tqWA|*I>|@LzriL49k&I=c08d&X&=_fKJ?ZDx|)h zIX7}WMQ3gSGYhr4aH&iaD!R~=E~ueFFcaQn88i<+v>#qCTg)}Nwr)21=9+_=uAG(jY?$y})Yj|t9TaMj z^mJgQ#R0=hwF#11bkXKEb$Ew1>Gu>h+9Y&;6zb5BgPuC1&>mEDJh`GqhtQjWVgJ(8 zcU1h#-D9R;m@b#WoI&$$#OMJitgF_{bQ*-d-MD@Rr4aYEN1^-D2XHWcZgR%YJFv^d z93Rt-^I@r{s0(>`)W5tl)J2&NWF|kA>T&nRWx@MT2B*%RTn_J-?fYeMsTAJj z&bTAo`~aH2P74}p^$VH@KDK;p*%{uB`ZIm~=Nr&ufBNrr&pIi2p7yhj=5Ae(`*z+% zKz5`27uJ&$8;kOuByJBrpjU_eUZPeX(kuXd`*772_wE*Nbiv<4aAoe;0$!s})@BZ3j3Mz!#T-LIb%wM* zF~5n{{W)XiGK%^WvnEXI&mohKjsv)r269jr3_TY2whJ^EMcNMt3x+uI^AjS+NpS^FkTVugt=Keg=(Es0@Z5a;&A3<{R0v5>9@{!5<)XO>(Jw)T z2?;zLRVD<44D|+a{g|Idkb+!y@|umYYH0csN4I5zP%(&@ zzooNP3WXmSHkc62Moq_X89hx&pe4M*l#65p!{NpB!Otp2+Caltr#d}mFFKl$D0AsJ zm9y=@{rfT{O2KvE}G#L4?()upsHf(9V+R z*rUXf9P~EkSke>?YAm^OW+>(iB_*6uJ(O%nCW2vLPeQO0V9BlA-s_rah$MM_&a?A#?lA zt%;$baN&0KW2~-3p%t+W#}+FRVka-OCW_2!bR5Z938q0q^f$Y=K_@`H-Rx&TZ6!)Z z(&sjmk0iX0U{1%=6s)B|TM5s5E*?G9K_#`Jmq94BA+pP`#fE0m@#`qgjgAjr7^S!T zH-8(v_^a)jDyf8*%@an1?sP)&C<@p!($OTsKC~W91l&gu6!^?} z5cj1FWur-pQb-OL*KNJ{4_lN0}{I>@;d@>Dxurw<9t3qMaQ##6%Zf z9LU=K`=LEF+$+3j(%%>wxc7}yW<`VAwda;SV-0Hq`E^0Y)-fZ(d3~(go z4jALenXyf#nB&O#7%YM!(=S#_m*0lcvZ~(YG4W7-!#YH&fiIPc_ zxn7~mWRi6_N+y%!IVhtigDj|3%0DkIcnwc_oAqF;mP4H+{XkkoC2A%Ug;vy>LTjF) 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 Y$aSjW==iyq%s#`+xOSl=RH8*xR6-G@$kJ{} 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

<&pK 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

;v)0UifO_f9Zr@UerUT=thvA8VaCIOLJ|89h& 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 Ef0e&??f?J) From d3477f2eed992c40706b4b1c18f1d9cfae0ead2c Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Mon, 6 Mar 2023 13:21:09 +0000 Subject: [PATCH 107/366] Fix compatibility with Python 3.8 --- reproject/adaptive/tests/test_core.py | 2 +- reproject/interpolation/tests/test_core.py | 2 +- reproject/spherical_intersect/tests/test_high_level.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/reproject/adaptive/tests/test_core.py b/reproject/adaptive/tests/test_core.py index 225db294e..755f0e461 100644 --- a/reproject/adaptive/tests/test_core.py +++ b/reproject/adaptive/tests/test_core.py @@ -878,7 +878,7 @@ def test_adaptive_input_output_types( # Compute test output_test, footprint_test = reproject_adaptive( - input_value, output_value, **(kwargs_in | kwargs_out) + input_value, output_value, **kwargs_in, **kwargs_out ) assert_allclose(output_ref, output_test) diff --git a/reproject/interpolation/tests/test_core.py b/reproject/interpolation/tests/test_core.py index 5e08e2258..0b624e807 100644 --- a/reproject/interpolation/tests/test_core.py +++ b/reproject/interpolation/tests/test_core.py @@ -808,7 +808,7 @@ def test_interp_input_output_types(valid_celestial_input_data, valid_celestial_o # Compute test output_test, footprint_test = reproject_interp( - input_value, output_value, **(kwargs_in | kwargs_out) + input_value, output_value, **kwargs_in, **kwargs_out ) assert_allclose(output_ref, output_test) diff --git a/reproject/spherical_intersect/tests/test_high_level.py b/reproject/spherical_intersect/tests/test_high_level.py index 290b81d40..4bb93caea 100644 --- a/reproject/spherical_intersect/tests/test_high_level.py +++ b/reproject/spherical_intersect/tests/test_high_level.py @@ -195,7 +195,7 @@ def test_exact_input_output_types(valid_celestial_input_data, valid_celestial_ou # Compute test output_test, footprint_test = reproject_exact( - input_value, output_value, **(kwargs_in | kwargs_out) + input_value, output_value, **kwargs_in, **kwargs_out ) assert_allclose(output_ref, output_test) From 3fb80e67f1378b26eac992dab770c9696ef820c8 Mon Sep 17 00:00:00 2001 From: "Adam Ginsburg (keflavich)" Date: Fri, 10 Mar 2023 13:49:21 -0500 Subject: [PATCH 108/366] fix high level wcs wrapping bug and add a regression test fix typo fix bugs might've fixed it? try to add a test (it doesn't test what I want, but it adds _some_ coverage) and add a hack-fix _actually_ expand test coverage; forgot to pass args on previously black black --- reproject/interpolation/core.py | 1 + reproject/tests/test_high_level.py | 8 +++++--- reproject/tests/test_utils.py | 21 +++++++++++++++++++++ reproject/utils.py | 8 +++++++- reproject/wcs_utils.py | 1 + 5 files changed, 35 insertions(+), 4 deletions(-) diff --git a/reproject/interpolation/core.py b/reproject/interpolation/core.py index 7fe3a0a9a..bcdf67d16 100644 --- a/reproject/interpolation/core.py +++ b/reproject/interpolation/core.py @@ -95,6 +95,7 @@ def _reproject_full( array = np.asarray(array, dtype=float) # shape_out must be exactly a tuple type shape_out = tuple(shape_out) + _validate_wcs(wcs_in, wcs_out, array.shape, shape_out) _validate_array_out(array_out, array, shape_out) diff --git a/reproject/tests/test_high_level.py b/reproject/tests/test_high_level.py index d90b7aa77..06c1675b7 100644 --- a/reproject/tests/test_high_level.py +++ b/reproject/tests/test_high_level.py @@ -117,8 +117,10 @@ def test_return_footprint(self, reproject_function): @pytest.mark.filterwarnings("ignore::FutureWarning") -@pytest.mark.parametrize("projection_type, dtype", itertools.product(ALL_MODES, ALL_DTYPES)) -def test_surface_brightness(projection_type, dtype): +@pytest.mark.parametrize( + "projection_type, dtype, block_size", itertools.product(ALL_MODES, ALL_DTYPES, (None, [2, 2])) +) +def test_surface_brightness(projection_type, dtype, block_size): header_in = fits.Header.fromstring(INPUT_HDR, sep="\n") header_in["NAXIS"] = 2 header_in["NAXIS1"] = 10 @@ -144,7 +146,7 @@ def test_surface_brightness(projection_type, dtype): ) else: data_out, footprint = reproject_interp( - (data_in, header_in), header_out, order=projection_type + (data_in, header_in), header_out, order=projection_type, block_size=block_size ) assert data_out.shape == (20, 20) diff --git a/reproject/tests/test_utils.py b/reproject/tests/test_utils.py index 88c2510d1..17b20239a 100644 --- a/reproject/tests/test_utils.py +++ b/reproject/tests/test_utils.py @@ -1,5 +1,6 @@ import numpy as np import pytest +import warnings from astropy.io import fits from astropy.nddata import NDData from astropy.utils.data import get_pkg_data_filename @@ -7,6 +8,7 @@ from reproject.tests.helpers import assert_wcs_allclose from reproject.utils import parse_input_data, parse_input_shape, parse_output_projection +from reproject.wcs_utils import has_celestial @pytest.mark.filterwarnings("ignore:unclosed file:ResourceWarning") @@ -89,3 +91,22 @@ def test_parse_output_projection_invalid_header(simple_celestial_fits_wcs): def test_parse_output_projection_invalid_wcs(simple_celestial_fits_wcs): with pytest.raises(ValueError, match="Need to specify shape"): parse_output_projection(simple_celestial_fits_wcs) + + +def test_has_celestial(): + from .test_high_level import INPUT_HDR + + with warnings.catch_warnings(): + warnings.simplefilter("ignore") + hdr = fits.Header.fromstring(INPUT_HDR) + ww = WCS(hdr) + assert ww.has_celestial + assert has_celestial(ww) + + from astropy.wcs.wcsapi import HighLevelWCSWrapper, SlicedLowLevelWCS + + wwh = HighLevelWCSWrapper(SlicedLowLevelWCS(ww, Ellipsis)) + assert has_celestial(wwh) + + wwh2 = HighLevelWCSWrapper(SlicedLowLevelWCS(ww, [slice(0, 1), slice(0, 1)])) + assert has_celestial(wwh2) diff --git a/reproject/utils.py b/reproject/utils.py index d79b13b14..74e438cc2 100644 --- a/reproject/utils.py +++ b/reproject/utils.py @@ -277,7 +277,13 @@ def reproject_single_block(a, block_info=None): if a.ndim == 0 or block_info is None or block_info == []: return np.array([a, a]) slices = [slice(*x) for x in block_info[None]["array-location"][-wcs_out.pixel_n_dim :]] - wcs_out_sub = HighLevelWCSWrapper(SlicedLowLevelWCS(wcs_out, slices=slices)) + + # @astrofrog why is it possible for this to be a HighLevelWCSWrapper? + if isinstance(wcs_out, HighLevelWCSWrapper): + lowlevwcs = SlicedLowLevelWCS(wcs_out.low_level_wcs, slices=slices) + else: + lowlevwcs = SlicedLowLevelWCS(wcs_out, slices=slices) + wcs_out_sub = HighLevelWCSWrapper(lowlevwcs) if isinstance(array_in_or_path, str): array_in = np.memmap(array_in_or_path, dtype=float, shape=shape_in) else: diff --git a/reproject/wcs_utils.py b/reproject/wcs_utils.py index 1a6baf76c..6753689d0 100644 --- a/reproject/wcs_utils.py +++ b/reproject/wcs_utils.py @@ -7,6 +7,7 @@ import numpy as np from astropy.coordinates import SkyCoord from astropy.wcs import WCS +from astropy.wcs.wcsapi.high_level_api import BaseHighLevelWCS from astropy.wcs.utils import pixel_to_pixel __all__ = ["has_celestial", "pixel_to_pixel_with_roundtrip"] From 78d5bc26aa7605e22cecc52947a173fc7a5fc866 Mon Sep 17 00:00:00 2001 From: "Adam Ginsburg (keflavich)" Date: Fri, 10 Mar 2023 18:42:31 -0500 Subject: [PATCH 109/366] remove some unnecessary lines, then add an example of what looks weird in modding has_celestial implement Tom's suggestions fix fix^2 the recursive approach might allow for infinite loops fix the unbound local error remove unnecessary test --- reproject/interpolation/core.py | 1 - reproject/tests/test_high_level.py | 8 +++----- reproject/utils.py | 9 ++++----- reproject/wcs_utils.py | 2 ++ 4 files changed, 9 insertions(+), 11 deletions(-) diff --git a/reproject/interpolation/core.py b/reproject/interpolation/core.py index bcdf67d16..7fe3a0a9a 100644 --- a/reproject/interpolation/core.py +++ b/reproject/interpolation/core.py @@ -95,7 +95,6 @@ def _reproject_full( array = np.asarray(array, dtype=float) # shape_out must be exactly a tuple type shape_out = tuple(shape_out) - _validate_wcs(wcs_in, wcs_out, array.shape, shape_out) _validate_array_out(array_out, array, shape_out) diff --git a/reproject/tests/test_high_level.py b/reproject/tests/test_high_level.py index 06c1675b7..d90b7aa77 100644 --- a/reproject/tests/test_high_level.py +++ b/reproject/tests/test_high_level.py @@ -117,10 +117,8 @@ def test_return_footprint(self, reproject_function): @pytest.mark.filterwarnings("ignore::FutureWarning") -@pytest.mark.parametrize( - "projection_type, dtype, block_size", itertools.product(ALL_MODES, ALL_DTYPES, (None, [2, 2])) -) -def test_surface_brightness(projection_type, dtype, block_size): +@pytest.mark.parametrize("projection_type, dtype", itertools.product(ALL_MODES, ALL_DTYPES)) +def test_surface_brightness(projection_type, dtype): header_in = fits.Header.fromstring(INPUT_HDR, sep="\n") header_in["NAXIS"] = 2 header_in["NAXIS1"] = 10 @@ -146,7 +144,7 @@ def test_surface_brightness(projection_type, dtype, block_size): ) else: data_out, footprint = reproject_interp( - (data_in, header_in), header_out, order=projection_type, block_size=block_size + (data_in, header_in), header_out, order=projection_type ) assert data_out.shape == (20, 20) diff --git a/reproject/utils.py b/reproject/utils.py index 74e438cc2..a098d095c 100644 --- a/reproject/utils.py +++ b/reproject/utils.py @@ -278,12 +278,11 @@ def reproject_single_block(a, block_info=None): return np.array([a, a]) slices = [slice(*x) for x in block_info[None]["array-location"][-wcs_out.pixel_n_dim :]] - # @astrofrog why is it possible for this to be a HighLevelWCSWrapper? - if isinstance(wcs_out, HighLevelWCSWrapper): - lowlevwcs = SlicedLowLevelWCS(wcs_out.low_level_wcs, slices=slices) + if isinstance(wcs_out, BaseHighLevelWCS): + low_level_wcs = SlicedLowLevelWCS(wcs_out.low_level_wcs, slices=slices) else: - lowlevwcs = SlicedLowLevelWCS(wcs_out, slices=slices) - wcs_out_sub = HighLevelWCSWrapper(lowlevwcs) + low_level_wcs = SlicedLowLevelWCS(wcs_out, slices=slices) + wcs_out_sub = HighLevelWCSWrapper(low_level_wcs) if isinstance(array_in_or_path, str): array_in = np.memmap(array_in_or_path, dtype=float, shape=shape_in) else: diff --git a/reproject/wcs_utils.py b/reproject/wcs_utils.py index 6753689d0..8d2657c49 100644 --- a/reproject/wcs_utils.py +++ b/reproject/wcs_utils.py @@ -20,6 +20,8 @@ def has_celestial(wcs): if isinstance(wcs, WCS): return wcs.has_celestial else: + if isinstance(wcs.low_level_wcs, BaseHighLevelWCS): + wcs = wcs.low_level_wcs for world_axis_class in wcs.low_level_wcs.world_axis_object_classes.values(): if issubclass(world_axis_class[0], SkyCoord): return True From 60baa56c3a3bc287aa0c0664d132d14aaca85be9 Mon Sep 17 00:00:00 2001 From: "Adam Ginsburg (keflavich)" Date: Sat, 11 Mar 2023 06:05:44 -0500 Subject: [PATCH 110/366] add a test that fails before this change: If this test code is injected at the start of reproject_single_block, the error will occur print(has_celestial(HighLevelWCSWrapper(SlicedLowLevelWCS(wcs_out, slices=slices)))) style fixes style black --- reproject/interpolation/tests/test_core.py | 15 +++++++++++---- reproject/tests/test_utils.py | 9 ++++----- reproject/wcs_utils.py | 2 +- 3 files changed, 16 insertions(+), 10 deletions(-) diff --git a/reproject/interpolation/tests/test_core.py b/reproject/interpolation/tests/test_core.py index 2dac25c55..1941eefb0 100644 --- a/reproject/interpolation/tests/test_core.py +++ b/reproject/interpolation/tests/test_core.py @@ -672,8 +672,9 @@ def test_broadcast_reprojection(input_extra_dims, output_shape, input_as_wcs, ou @pytest.mark.parametrize("input_extra_dims", (1, 2)) @pytest.mark.parametrize("output_shape", (None, "single", "full")) @pytest.mark.parametrize("parallel", [True, False]) +@pytest.mark.parametrize("header_or_wcs", (lambda x: x, WCS)) @pytest.mark.filterwarnings("ignore::astropy.wcs.wcs.FITSFixedWarning") -def test_blocked_broadcast_reprojection(input_extra_dims, output_shape, parallel): +def test_blocked_broadcast_reprojection(input_extra_dims, output_shape, parallel, header_or_wcs): image_stack, array_ref, footprint_ref, header_in, header_out = _setup_for_broadcast_test() # Test both single and multiple dimensions being broadcast if input_extra_dims == 2: @@ -689,6 +690,9 @@ def test_blocked_broadcast_reprojection(input_extra_dims, output_shape, parallel # Provide the broadcast dimensions as part of the output shape output_shape = image_stack.shape + # test different behavior when the output projection is a WCS + header_out = header_or_wcs(header_out) + array_broadcast, footprint_broadcast = reproject_interp( (image_stack, header_in), header_out, output_shape, parallel=parallel, block_size=[5, 5] ) @@ -701,9 +705,12 @@ def test_blocked_broadcast_reprojection(input_extra_dims, output_shape, parallel @pytest.mark.parametrize("block_size", [[500, 500], [500, 100], None]) @pytest.mark.parametrize("return_footprint", [False, True]) @pytest.mark.parametrize("existing_outputs", [False, True]) +@pytest.mark.parametrize("header_or_wcs", (lambda x: x, WCS)) @pytest.mark.remote_data @pytest.mark.filterwarnings("ignore::astropy.wcs.wcs.FITSFixedWarning") -def test_blocked_against_single(parallel, block_size, return_footprint, existing_outputs): +def test_blocked_against_single( + parallel, block_size, return_footprint, existing_outputs, header_or_wcs +): # Ensure when we break a reprojection down into multiple discrete blocks # it has the same result as if all pixels where reprejcted at once @@ -727,7 +734,7 @@ def test_blocked_against_single(parallel, block_size, return_footprint, existing result_test = reproject_interp( hdu2, - hdu1.header, + header_or_wcs(hdu1.header), parallel=parallel, block_size=block_size, return_footprint=return_footprint, @@ -737,7 +744,7 @@ def test_blocked_against_single(parallel, block_size, return_footprint, existing result_reference = reproject_interp( hdu2, - hdu1.header, + header_or_wcs(hdu1.header), parallel=False, block_size=None, return_footprint=return_footprint, diff --git a/reproject/tests/test_utils.py b/reproject/tests/test_utils.py index 17b20239a..d842cb940 100644 --- a/reproject/tests/test_utils.py +++ b/reproject/tests/test_utils.py @@ -1,6 +1,5 @@ import numpy as np import pytest -import warnings from astropy.io import fits from astropy.nddata import NDData from astropy.utils.data import get_pkg_data_filename @@ -93,13 +92,13 @@ def test_parse_output_projection_invalid_wcs(simple_celestial_fits_wcs): parse_output_projection(simple_celestial_fits_wcs) +@pytest.mark.filterwarnings("ignore::astropy.utils.exceptions.AstropyUserWarning") +@pytest.mark.filterwarnings("ignore::astropy.wcs.wcs.FITSFixedWarning") def test_has_celestial(): from .test_high_level import INPUT_HDR - with warnings.catch_warnings(): - warnings.simplefilter("ignore") - hdr = fits.Header.fromstring(INPUT_HDR) - ww = WCS(hdr) + hdr = fits.Header.fromstring(INPUT_HDR) + ww = WCS(hdr) assert ww.has_celestial assert has_celestial(ww) diff --git a/reproject/wcs_utils.py b/reproject/wcs_utils.py index 8d2657c49..e5faa5e19 100644 --- a/reproject/wcs_utils.py +++ b/reproject/wcs_utils.py @@ -7,8 +7,8 @@ import numpy as np from astropy.coordinates import SkyCoord from astropy.wcs import WCS -from astropy.wcs.wcsapi.high_level_api import BaseHighLevelWCS from astropy.wcs.utils import pixel_to_pixel +from astropy.wcs.wcsapi.high_level_api import BaseHighLevelWCS __all__ = ["has_celestial", "pixel_to_pixel_with_roundtrip"] From fcd811ef2f69f63c8abc471b88004f0cc1219988 Mon Sep 17 00:00:00 2001 From: "Adam Ginsburg (keflavich)" Date: Sat, 11 Mar 2023 06:18:44 -0500 Subject: [PATCH 111/366] remove unnecessary check: HighLevelWCS.low_level_wcs can't be a HighLevelWCS --- reproject/wcs_utils.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/reproject/wcs_utils.py b/reproject/wcs_utils.py index e5faa5e19..9fe71862e 100644 --- a/reproject/wcs_utils.py +++ b/reproject/wcs_utils.py @@ -20,8 +20,6 @@ def has_celestial(wcs): if isinstance(wcs, WCS): return wcs.has_celestial else: - if isinstance(wcs.low_level_wcs, BaseHighLevelWCS): - wcs = wcs.low_level_wcs for world_axis_class in wcs.low_level_wcs.world_axis_object_classes.values(): if issubclass(world_axis_class[0], SkyCoord): return True From a85d5c36215e7a6c73b5169c8c6053e13e71dc6e Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 8 May 2023 16:38:42 +0000 Subject: [PATCH 112/366] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/asottile/pyupgrade: v3.3.1 → v3.4.0](https://github.com/asottile/pyupgrade/compare/v3.3.1...v3.4.0) - [github.com/psf/black: 23.1.0 → 23.3.0](https://github.com/psf/black/compare/23.1.0...23.3.0) --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 90f8f5398..c623db3f8 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -20,7 +20,7 @@ repos: - "--filter-files" - repo: https://github.com/asottile/pyupgrade - rev: v3.3.1 + rev: v3.4.0 hooks: - id: pyupgrade args: ["--py38-plus"] @@ -67,7 +67,7 @@ repos: exclude: ".*(data.*|extern.*|cextern)$" - repo: https://github.com/psf/black - rev: 23.1.0 + rev: 23.3.0 hooks: - id: black From 188b38bae45a2094455db0896ce5f93be3011b31 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Thu, 18 May 2023 12:18:42 +0100 Subject: [PATCH 113/366] If shape_out is specified, use this over the array_shape attribute of a WCS object --- reproject/utils.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/reproject/utils.py b/reproject/utils.py index a098d095c..1bd5d7d41 100644 --- a/reproject/utils.py +++ b/reproject/utils.py @@ -166,12 +166,13 @@ def parse_output_projection(output_projection, shape_in=None, shape_out=None, ou wcs_out = HighLevelWCSWrapper(output_projection) else: wcs_out = output_projection - if wcs_out.low_level_wcs.array_shape is not None: - shape_out = wcs_out.low_level_wcs.array_shape - elif shape_out is None: - raise ValueError( - "Need to specify shape_out when specifying output_projection as WCS object" - ) + if shape_out is None: + if wcs_out.low_level_wcs.array_shape is not None: + shape_out = wcs_out.low_level_wcs.array_shape + else: + raise ValueError( + "Need to specify shape_out when specifying output_projection as WCS object" + ) elif isinstance(output_projection, str): hdu_list = fits.open(output_projection) shape_out = hdu_list[0].data.shape From 8e2b2b9f0149a267cb280cc489ccfe53961cf604 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Thu, 18 May 2023 13:43:41 +0100 Subject: [PATCH 114/366] Added regression test --- reproject/tests/test_utils.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/reproject/tests/test_utils.py b/reproject/tests/test_utils.py index d842cb940..5e0c3fec0 100644 --- a/reproject/tests/test_utils.py +++ b/reproject/tests/test_utils.py @@ -5,6 +5,7 @@ from astropy.utils.data import get_pkg_data_filename from astropy.wcs import WCS +from reproject.conftest import set_wcs_array_shape from reproject.tests.helpers import assert_wcs_allclose from reproject.utils import parse_input_data, parse_input_shape, parse_output_projection from reproject.wcs_utils import has_celestial @@ -92,6 +93,20 @@ def test_parse_output_projection_invalid_wcs(simple_celestial_fits_wcs): parse_output_projection(simple_celestial_fits_wcs) +def test_parse_output_projection_override_shape_out(simple_celestial_wcs): + + wcs_ref = simple_celestial_wcs + + set_wcs_array_shape(wcs_ref, (10, 20)) + + assert wcs_ref.low_level_wcs.array_shape == (10, 20) + + wcs, shape = parse_output_projection(wcs_ref, shape_out=(30, 40)) + + assert shape == (30, 40) + assert_wcs_allclose(wcs, wcs_ref) + + @pytest.mark.filterwarnings("ignore::astropy.utils.exceptions.AstropyUserWarning") @pytest.mark.filterwarnings("ignore::astropy.wcs.wcs.FITSFixedWarning") def test_has_celestial(): From 692bd4124f53cbee635503ce516f2ee0a218f6f4 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Thu, 18 May 2023 13:45:06 +0100 Subject: [PATCH 115/366] Fix test --- reproject/tests/test_utils.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/reproject/tests/test_utils.py b/reproject/tests/test_utils.py index 5e0c3fec0..1ce7b2483 100644 --- a/reproject/tests/test_utils.py +++ b/reproject/tests/test_utils.py @@ -95,11 +95,18 @@ def test_parse_output_projection_invalid_wcs(simple_celestial_fits_wcs): def test_parse_output_projection_override_shape_out(simple_celestial_wcs): + # Regression test for a bug that caused shape_out to be ignored if the + # WCS object had array_shape set - but shape_out should override the WCS + # shape. + wcs_ref = simple_celestial_wcs set_wcs_array_shape(wcs_ref, (10, 20)) - assert wcs_ref.low_level_wcs.array_shape == (10, 20) + if hasattr(wcs_ref, 'low_level_wcs'): + assert wcs_ref.low_level_wcs.array_shape == (10, 20) + else: + assert wcs_ref.array_shape == (10, 20) wcs, shape = parse_output_projection(wcs_ref, shape_out=(30, 40)) From 71697b1912d646596e3a3b7b2be5d4f8a1b7b227 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Thu, 18 May 2023 13:45:53 +0100 Subject: [PATCH 116/366] Fix codestyle --- reproject/tests/test_utils.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/reproject/tests/test_utils.py b/reproject/tests/test_utils.py index 1ce7b2483..a0b8b4253 100644 --- a/reproject/tests/test_utils.py +++ b/reproject/tests/test_utils.py @@ -94,7 +94,6 @@ def test_parse_output_projection_invalid_wcs(simple_celestial_fits_wcs): def test_parse_output_projection_override_shape_out(simple_celestial_wcs): - # Regression test for a bug that caused shape_out to be ignored if the # WCS object had array_shape set - but shape_out should override the WCS # shape. @@ -103,7 +102,7 @@ def test_parse_output_projection_override_shape_out(simple_celestial_wcs): set_wcs_array_shape(wcs_ref, (10, 20)) - if hasattr(wcs_ref, 'low_level_wcs'): + if hasattr(wcs_ref, "low_level_wcs"): assert wcs_ref.low_level_wcs.array_shape == (10, 20) else: assert wcs_ref.array_shape == (10, 20) From c895e734de90cf869e3df731e80a39c15cd9142a Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Thu, 18 May 2023 12:18:01 +0100 Subject: [PATCH 117/366] Fix find_optimal_celestial_wcs to work with e.g. Solar frames, and make sure that we don't assume the output WCS is in degrees --- reproject/mosaicking/wcs_helpers.py | 34 ++++++++++++++++------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/reproject/mosaicking/wcs_helpers.py b/reproject/mosaicking/wcs_helpers.py index 327e81477..d0c8551ab 100644 --- a/reproject/mosaicking/wcs_helpers.py +++ b/reproject/mosaicking/wcs_helpers.py @@ -123,7 +123,7 @@ def find_optimal_celestial_wcs( # We start off by looping over images, checking that they are indeed # celestial images, and building up a list of all corners and all reference - # coordinates in celestial (ICRS) coordinates. + # coordinates in the frame of reference of the first image. corners = [] references = [] @@ -162,18 +162,19 @@ def find_optimal_celestial_wcs( xc = np.array([-0.5, nx - 0.5, nx - 0.5, -0.5]) yc = np.array([-0.5, -0.5, ny - 0.5, ny - 0.5]) - # We have to do .frame here to make sure that we get an ICRS object + # We have to do .frame here to make sure that we get a frame object # without any 'hidden' attributes, otherwise the stacking below won't # work. - corners.append(wcs.pixel_to_world(xc, yc).icrs.frame) + corners.append(wcs.pixel_to_world(xc, yc).transform_to(frame).frame) if isinstance(wcs, WCS): - # We now figure out the reference coordinate for the image in ICRS. The - # easiest way to do this is actually to use pixel_to_skycoord with the - # reference position in pixel coordinates. We have to set origin=1 - # because crpix values are 1-based. + # We now figure out the reference coordinate for the image in the + # frame of the first image. The easiest way to do this is actually + # to use pixel_to_skycoord with the reference position in pixel + # coordinates. We have to set origin=1 because crpix values are + # 1-based. xp, yp = wcs.wcs.crpix - references.append(pixel_to_skycoord(xp, yp, wcs, origin=1).icrs.frame) + references.append(pixel_to_skycoord(xp, yp, wcs, origin=1).transform_to(frame).frame) # Find the pixel scale at the reference position - we take the minimum # since we are going to set up a header with 'square' pixels with the @@ -183,7 +184,7 @@ def find_optimal_celestial_wcs( else: xp, yp = (nx - 1) / 2, (ny - 1) / 2 - references.append(wcs.pixel_to_world(xp, yp).icrs.frame) + references.append(wcs.pixel_to_world(xp, yp).transform_to(frame).frame) xs = np.array([xp, xp, xp + 1]) ys = np.array([yp, yp + 1, yp]) @@ -192,7 +193,7 @@ def find_optimal_celestial_wcs( dy = abs(cs[0].separation(cs[1]).deg) resolutions.append(min(dx, dy)) - # We now stack the coordinates - however the ICRS class can't do this + # We now stack the coordinates - however the frame classes can't do this # so we have to use the high-level SkyCoord class. corners = SkyCoord(corners) references = SkyCoord(references) @@ -212,15 +213,18 @@ def find_optimal_celestial_wcs( if resolution is None: resolution = np.min(resolutions) * u.deg - # Determine the resolution in degrees - cdelt = resolution.to(u.deg).value - # Construct WCS object centered on position wcs_final = celestial_frame_to_wcs(frame, projection=projection) rep = reference.represent_as("unitspherical") - wcs_final.wcs.crval = rep.lon.degree, rep.lat.degree - wcs_final.wcs.cdelt = -cdelt, cdelt + wcs_final.wcs.crval = ( + rep.lon.to_value(wcs_final.wcs.cunit[0]), + rep.lat.to_value(wcs_final.wcs.cunit[1]), + ) + wcs_final.wcs.cdelt = ( + -resolution.to_value(wcs_final.wcs.cunit[0]), + resolution.to_value(wcs_final.wcs.cunit[1]), + ) # For now, set crpix to (1, 1) and we'll then figure out where all the # images fall in this projection, then we'll adjust crpix. From f5069badbccb4874468539a0a5b852e1778bc5f7 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Thu, 18 May 2023 14:11:25 +0100 Subject: [PATCH 118/366] Added regression test for solar issues --- .../mosaicking/tests/test_wcs_helpers.py | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/reproject/mosaicking/tests/test_wcs_helpers.py b/reproject/mosaicking/tests/test_wcs_helpers.py index de5ae13e6..f74895231 100644 --- a/reproject/mosaicking/tests/test_wcs_helpers.py +++ b/reproject/mosaicking/tests/test_wcs_helpers.py @@ -287,3 +287,53 @@ def test_input_types(valid_celestial_input_shapes, iterable): assert_wcs_allclose(wcs_test, wcs_ref) assert shape_test == shape_ref + + +SOLAR_HEADER = """ +CRPIX1 = -1374.571094981584 / [pix] +CRPIX2 = 2081.629159922445 / [pix] +CRDATE1 = '2017-01-01T00:00:00.000' +CRDATE2 = '2017-01-01T00:00:00.000' +CRVAL1 = -619.0078311637853 +CRVAL2 = -407.000970936774 +CDELT1 = 0.01099999994039536 +CDELT2 = 0.01099999994039536 +CUNIT1 = 'arcsec ' +CUNIT2 = 'arcsec ' +CTYPE1 = 'HPLN-TAN' +CTYPE2 = 'HPLT-TAN' +PC1_1 = 0.966887196065055 +PC1_2 = -0.01087372434907635 +PC2_1 = 0.01173971407248916 +PC2_2 = 0.9871195868097251 +LONPOLE = 180.0 / [deg] +DATEREF = '2022-06-02T17:22:53.220' +OBSGEO-X= -5466045.256954942 / [m] +OBSGEO-Y= -2404388.737412784 / [m] +OBSGEO-Z= 2242133.887690042 / [m] +SPECSYS = 'TOPOCENT' +VELOSYS = 0.0 +""" + + +def test_solar_wcs(): + + # Regression test for issues that occurred when trying to find + # the optimal WCS for a set of solar WCSes + + import sunpy.coordinates + + wcs_ref = WCS(fits.Header.fromstring(SOLAR_HEADER, sep="\n"), fix=False) + + wcs1 = wcs_ref + wcs2 = wcs_ref.copy() + wcs2.wcs.crpix[0] -= 4096 + + wcs, shape = find_optimal_celestial_wcs([((4096, 4096), wcs1), ((4096, 4096), wcs2)]) + + wcs.wcs.set() + + assert wcs.wcs.ctype[0] == wcs_ref.wcs.ctype[0] + assert wcs.wcs.ctype[1] == wcs_ref.wcs.ctype[1] + assert wcs.wcs.cunit[0] == wcs_ref.wcs.cunit[0] + assert wcs.wcs.cunit[1] == wcs_ref.wcs.cunit[1] From 1e093debd1da79bc1a537ada975c09f8e9ce7c22 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Thu, 18 May 2023 14:22:12 +0100 Subject: [PATCH 119/366] Improve test --- reproject/mosaicking/tests/test_wcs_helpers.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/reproject/mosaicking/tests/test_wcs_helpers.py b/reproject/mosaicking/tests/test_wcs_helpers.py index f74895231..bd3ea7c66 100644 --- a/reproject/mosaicking/tests/test_wcs_helpers.py +++ b/reproject/mosaicking/tests/test_wcs_helpers.py @@ -316,17 +316,21 @@ def test_input_types(valid_celestial_input_shapes, iterable): """ +@pytest.mark.filterwarnings("ignore::astropy.wcs.wcs.FITSFixedWarning") def test_solar_wcs(): # Regression test for issues that occurred when trying to find # the optimal WCS for a set of solar WCSes + pytest.importorskip("sunpy", minversion="2.1.0") + + # Make sure the WCS <-> frame functions are registered import sunpy.coordinates - wcs_ref = WCS(fits.Header.fromstring(SOLAR_HEADER, sep="\n"), fix=False) + wcs_ref = WCS(fits.Header.fromstring(SOLAR_HEADER, sep="\n")) - wcs1 = wcs_ref - wcs2 = wcs_ref.copy() + wcs1 = wcs_ref.deepcopy() + wcs2 = wcs_ref.deepcopy() wcs2.wcs.crpix[0] -= 4096 wcs, shape = find_optimal_celestial_wcs([((4096, 4096), wcs1), ((4096, 4096), wcs2)]) @@ -337,3 +341,5 @@ def test_solar_wcs(): assert wcs.wcs.ctype[1] == wcs_ref.wcs.ctype[1] assert wcs.wcs.cunit[0] == wcs_ref.wcs.cunit[0] assert wcs.wcs.cunit[1] == wcs_ref.wcs.cunit[1] + + assert shape == (4281, 8237) From 6da7f6cf09e45118f855b82f6db4ab75c339416e Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Thu, 18 May 2023 14:25:57 +0100 Subject: [PATCH 120/366] Whitespace --- reproject/mosaicking/tests/test_wcs_helpers.py | 1 - 1 file changed, 1 deletion(-) diff --git a/reproject/mosaicking/tests/test_wcs_helpers.py b/reproject/mosaicking/tests/test_wcs_helpers.py index bd3ea7c66..c15b6d79d 100644 --- a/reproject/mosaicking/tests/test_wcs_helpers.py +++ b/reproject/mosaicking/tests/test_wcs_helpers.py @@ -318,7 +318,6 @@ def test_input_types(valid_celestial_input_shapes, iterable): @pytest.mark.filterwarnings("ignore::astropy.wcs.wcs.FITSFixedWarning") def test_solar_wcs(): - # Regression test for issues that occurred when trying to find # the optimal WCS for a set of solar WCSes From 5d1b68213939add5f5313cf72c464c6e10368ecd Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Thu, 18 May 2023 14:37:00 +0100 Subject: [PATCH 121/366] Fix issue with WCSes that have no default units --- reproject/mosaicking/wcs_helpers.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/reproject/mosaicking/wcs_helpers.py b/reproject/mosaicking/wcs_helpers.py index d0c8551ab..79f314461 100644 --- a/reproject/mosaicking/wcs_helpers.py +++ b/reproject/mosaicking/wcs_helpers.py @@ -216,6 +216,12 @@ def find_optimal_celestial_wcs( # Construct WCS object centered on position wcs_final = celestial_frame_to_wcs(frame, projection=projection) + if wcs_final.wcs.cunit[0] == "": + wcs_final.wcs.cunit[0] = "deg" + + if wcs_final.wcs.cunit[1] == "": + wcs_final.wcs.cunit[1] = "deg" + rep = reference.represent_as("unitspherical") wcs_final.wcs.crval = ( rep.lon.to_value(wcs_final.wcs.cunit[0]), From d597aa31dda8dceaa7d3a883e0d13b0be90e14dd Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Thu, 16 Mar 2023 21:41:50 +0000 Subject: [PATCH 122/366] Started adding support for allowing dask arrays as input --- reproject/conftest.py | 4 ++++ reproject/interpolation/high_level.py | 4 ++++ reproject/utils.py | 31 +++++++++++++++++++++++---- 3 files changed, 35 insertions(+), 4 deletions(-) diff --git a/reproject/conftest.py b/reproject/conftest.py index 725388728..9ec8eda32 100644 --- a/reproject/conftest.py +++ b/reproject/conftest.py @@ -7,6 +7,7 @@ import numpy as np import pytest +import dask.array as da from astropy.io import fits from astropy.nddata import NDData from astropy.wcs import WCS @@ -132,6 +133,8 @@ def valid_celestial_input(tmp_path, request, wcs): input_value = (array.shape, wcs) elif request.param == "data_wcs_tuple": input_value = (array, wcs) + elif request.param == "dask_wcs_tuple": + input_value = (da.from_array(array), wcs) elif request.param == "nddata": input_value = NDData(data=array, wcs=wcs) elif request.param == "wcs": @@ -153,6 +156,7 @@ def valid_celestial_input(tmp_path, request, wcs): "image_hdu", "comp_image_hdu", "data_wcs_tuple", + "dask_wcs_tuple", "nddata", ] diff --git a/reproject/interpolation/high_level.py b/reproject/interpolation/high_level.py index 98b126eca..32225b405 100644 --- a/reproject/interpolation/high_level.py +++ b/reproject/interpolation/high_level.py @@ -1,6 +1,7 @@ # Licensed under a 3-clause BSD style license - see LICENSE.rst import os +from dask import array as da from astropy.utils import deprecated_renamed_argument from ..utils import _reproject_blocked, parse_input_data, parse_output_projection @@ -116,6 +117,9 @@ def reproject_interp( if isinstance(order, str): order = ORDER[order] + if isinstance(array_in, da.core.Array) and block_size is None: + block_size = array_in.chunksize + # if either of these are not default, it means a blocked method must be used if block_size is not None or parallel is not False: return _reproject_blocked( diff --git a/reproject/utils.py b/reproject/utils.py index 1bd5d7d41..a60cf9f9b 100644 --- a/reproject/utils.py +++ b/reproject/utils.py @@ -41,7 +41,7 @@ def parse_input_data(input_data, hdu_in=None): return parse_input_data(input_data[hdu_in]) elif isinstance(input_data, (PrimaryHDU, ImageHDU, CompImageHDU)): return input_data.data, WCS(input_data.header) - elif isinstance(input_data, tuple) and isinstance(input_data[0], np.ndarray): + elif isinstance(input_data, tuple) and isinstance(input_data[0], (np.ndarray, da.core.Array)): if isinstance(input_data[1], Header): return input_data[0], WCS(input_data[1]) else: @@ -259,18 +259,41 @@ def _reproject_blocked( shape_in = array_in.shape + dask_input = isinstance(array_in, da.core.Array) + # When in parallel mode, we want to make sure we avoid having to copy the # input array to all processes for each chunk, so instead we write out # the input array to a Numpy memory map and load it in inside each process # as a memory-mapped array. We need to be careful how this gets passed to # reproject_single_block so we pass a variable that can be either a string - # or the array itself (for synchronous mode). - if parallel: + # or the array itself (for synchronous mode). We don't need to do this if + # the input dataset is a dask array as then dask knows how to pass it between + # processes. When the input array is a dask array, we also for now write + # this to a memmap array and pass that to the individual reproject functions. + # In future, it might be possible to make at least some of the reproject + # functions understand dask arrays directly. + if parallel or dask_input: array_in_or_path = tempfile.mktemp() array_in_memmapped = np.memmap( array_in_or_path, dtype=float, shape=array_in.shape, mode="w+" ) - array_in_memmapped[:] = array_in[:] + if dask_input: + # First compute and store the dask array to zarr using whatever + # the default scheduler is at this point + filename = tempfile.mktemp() + array_in.to_zarr(filename) + array_in = da.from_zarr(filename) + # Then store this in the Numpy memmap array (schedulers other than + # synchronous don't work well when writing to a Numpy array) + da.store( + array_in, + array_in_memmapped, + compute=True, + scheduler="synchronous", + ) + else: + array_in_memmapped[:] = array_in[:] + else: array_in_or_path = array_in From 1a0ef7b107bc31b3c7e2f52dd64d0424d06dcf71 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Thu, 16 Mar 2023 21:58:51 +0000 Subject: [PATCH 123/366] Fixed code style --- reproject/conftest.py | 2 +- reproject/interpolation/high_level.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/reproject/conftest.py b/reproject/conftest.py index 9ec8eda32..d75be112f 100644 --- a/reproject/conftest.py +++ b/reproject/conftest.py @@ -5,9 +5,9 @@ import os +import dask.array as da import numpy as np import pytest -import dask.array as da from astropy.io import fits from astropy.nddata import NDData from astropy.wcs import WCS diff --git a/reproject/interpolation/high_level.py b/reproject/interpolation/high_level.py index 32225b405..76d13f751 100644 --- a/reproject/interpolation/high_level.py +++ b/reproject/interpolation/high_level.py @@ -1,8 +1,8 @@ # Licensed under a 3-clause BSD style license - see LICENSE.rst import os -from dask import array as da from astropy.utils import deprecated_renamed_argument +from dask import array as da from ..utils import _reproject_blocked, parse_input_data, parse_output_projection from .core import _reproject_full From 1f27856d704aa1fa77ec17478b8087d5f8cbcb3b Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Thu, 18 May 2023 16:47:48 +0100 Subject: [PATCH 124/366] Avoid using np.pad in our map_coordinates wrapper --- reproject/array_utils.py | 13 ++++----- reproject/tests/test_array_utils.py | 42 +++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 6 deletions(-) create mode 100644 reproject/tests/test_array_utils.py diff --git a/reproject/array_utils.py b/reproject/array_utils.py index 15825e544..cec454133 100644 --- a/reproject/array_utils.py +++ b/reproject/array_utils.py @@ -3,10 +3,6 @@ __all__ = ["map_coordinates"] -def pad_edge_1(array): - return np.pad(array, 1, mode="edge") - - def map_coordinates(image, coords, **kwargs): # In the built-in scipy map_coordinates, the values are defined at the # center of the pixels. This means that map_coordinates does not @@ -19,9 +15,14 @@ def map_coordinates(image, coords, **kwargs): original_shape = image.shape - image = pad_edge_1(image) + coords = coords.copy() - values = scipy_map_coordinates(image, coords + 1, **kwargs) + for i in range(coords.shape[0]): + coords[i][(coords[i] < 0) & (coords[i] >= -0.5)] = 0 + coords[i][(coords[i] < original_shape[i] - 0.5) & (coords[i] >= original_shape[i] - 1)] = ( + original_shape[i] - 1 + ) + values = scipy_map_coordinates(image, coords, **kwargs) reset = np.zeros(coords.shape[1], dtype=bool) diff --git a/reproject/tests/test_array_utils.py b/reproject/tests/test_array_utils.py new file mode 100644 index 000000000..ac23d5bcd --- /dev/null +++ b/reproject/tests/test_array_utils.py @@ -0,0 +1,42 @@ +import numpy as np +from numpy.testing import assert_allclose +from scipy.ndimage import map_coordinates as scipy_map_coordinates + +from reproject.array_utils import map_coordinates + + +def test_custom_map_coordinates(): + np.random.seed(1249) + + data = np.random.random((3, 4)) + + coords = np.random.uniform(-2, 6, (2, 10000)) + + expected = scipy_map_coordinates( + np.pad(data, 1, mode="edge"), + coords + 1, + order=1, + cval=np.nan, + mode="constant", + ) + + reset = np.zeros(coords.shape[1], dtype=bool) + + for i in range(coords.shape[0]): + reset |= coords[i] < -0.5 + reset |= coords[i] > data.shape[i] - 0.5 + + expected[reset] = np.nan + + result = map_coordinates( + data, + coords, + order=1, + cval=np.nan, + mode="constant", + ) + + for i in range(coords.shape[1]): + print(coords[:, i], expected[i], result[i]) + + assert_allclose(result, expected) From 21b19e640826923ce943f5760dbbc2c5a3a40282 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Thu, 18 May 2023 16:50:21 +0100 Subject: [PATCH 125/366] Update comment --- reproject/array_utils.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/reproject/array_utils.py b/reproject/array_utils.py index cec454133..ec3a39a2e 100644 --- a/reproject/array_utils.py +++ b/reproject/array_utils.py @@ -7,21 +7,23 @@ def map_coordinates(image, coords, **kwargs): # In the built-in scipy map_coordinates, the values are defined at the # center of the pixels. This means that map_coordinates does not # correctly treat pixels that are in the outer half of the outer pixels. - # We solve this by extending the array, updating the pixel coordinates, - # then getting rid of values that were sampled in the range -1 to -0.5 - # and n to n - 0.5. + # We solve this by resetting any coordinates that are in the outer half of + # the border pixels to be at the center of the border pixels. We used to + # instead pad the array but this was not memory efficient as it ended up + # producing a copy of the output array. from scipy.ndimage import map_coordinates as scipy_map_coordinates original_shape = image.shape + # We copy the coordinates array as we then modify it in-place below coords = coords.copy() - for i in range(coords.shape[0]): coords[i][(coords[i] < 0) & (coords[i] >= -0.5)] = 0 coords[i][(coords[i] < original_shape[i] - 0.5) & (coords[i] >= original_shape[i] - 1)] = ( original_shape[i] - 1 ) + values = scipy_map_coordinates(image, coords, **kwargs) reset = np.zeros(coords.shape[1], dtype=bool) From 4750e5f4d73f4be58ef8497605e3e25682232bba Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Thu, 18 May 2023 16:58:05 +0100 Subject: [PATCH 126/366] Fix parse_input_shape for dask arrays --- reproject/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reproject/utils.py b/reproject/utils.py index a60cf9f9b..86401bbcf 100644 --- a/reproject/utils.py +++ b/reproject/utils.py @@ -84,7 +84,7 @@ def parse_input_shape(input_shape, hdu_in=None): return parse_input_shape(input_shape[hdu_in]) elif isinstance(input_shape, (PrimaryHDU, ImageHDU, CompImageHDU)): return input_shape.shape, WCS(input_shape.header) - elif isinstance(input_shape, tuple) and isinstance(input_shape[0], np.ndarray): + elif isinstance(input_shape, tuple) and isinstance(input_shape[0], (np.ndarray, da.core.Array)): if isinstance(input_shape[1], Header): return input_shape[0].shape, WCS(input_shape[1]) else: From a73946820be95e0c545ee43287971b90d54d05a1 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Fri, 19 May 2023 12:17:26 +0100 Subject: [PATCH 127/366] Move conversion of dask array to memory-mapped Numpy array to parse_input_data --- reproject/tests/test_utils.py | 1 + reproject/utils.py | 77 +++++++++++++++++++++++------------ 2 files changed, 52 insertions(+), 26 deletions(-) diff --git a/reproject/tests/test_utils.py b/reproject/tests/test_utils.py index a0b8b4253..ce90b2d4c 100644 --- a/reproject/tests/test_utils.py +++ b/reproject/tests/test_utils.py @@ -16,6 +16,7 @@ def test_parse_input_data(tmpdir, valid_celestial_input_data, request): array_ref, wcs_ref, input_value, kwargs = valid_celestial_input_data data, wcs = parse_input_data(input_value, **kwargs) + assert isinstance(data, np.ndarray) np.testing.assert_allclose(data, array_ref) assert_wcs_allclose(wcs, wcs_ref) diff --git a/reproject/utils.py b/reproject/utils.py index 86401bbcf..b65626445 100644 --- a/reproject/utils.py +++ b/reproject/utils.py @@ -21,6 +21,47 @@ ] +def _dask_to_numpy_memmap(dask_array): + """ + Given a dask array, write it out to disk and load it again as a Numpy + memmap array. + """ + + with tempfile.TemporaryDirectory() as zarr_tmp: + # First compute and store the dask array to zarr using whatever + # the default scheduler is at this point + dask_array.to_zarr(zarr_tmp) + + # Load the array back to dask + zarr_array = da.from_zarr(zarr_tmp) + + # Then store this in the Numpy memmap array (schedulers other than + # synchronous don't work well when writing to a Numpy array) + + memmap_path = tempfile.mktemp() + + memmapped_array = np.memmap( + memmap_path, + dtype=zarr_array.dtype, + shape=zarr_array.shape, + mode="w+", + ) + + da.store( + zarr_array, + memmapped_array, + compute=True, + scheduler="synchronous", + ) + + # Note that at this point the zarr directory should be removed, but + # the memmapped array will persist on disk. In future we may want to + # clean this up automatically once reproject has completed to avoid + # a build-up of temporary files + + return memmapped_array + + def parse_input_data(input_data, hdu_in=None): """ Parse input data to return a Numpy array and WCS object. @@ -42,13 +83,17 @@ def parse_input_data(input_data, hdu_in=None): elif isinstance(input_data, (PrimaryHDU, ImageHDU, CompImageHDU)): return input_data.data, WCS(input_data.header) elif isinstance(input_data, tuple) and isinstance(input_data[0], (np.ndarray, da.core.Array)): + if isinstance(input_data[0], da.core.Array): + data = _dask_to_numpy_memmap(input_data[0]) + else: + data = input_data[0] if isinstance(input_data[1], Header): - return input_data[0], WCS(input_data[1]) + return data, WCS(input_data[1]) else: if isinstance(input_data[1], BaseHighLevelWCS): - return input_data + return data, input_data[1] else: - return input_data[0], HighLevelWCSWrapper(input_data[1]) + return data, HighLevelWCSWrapper(input_data[1]) elif ( isinstance(input_data, BaseHighLevelWCS) and input_data.low_level_wcs.array_shape is not None @@ -266,33 +311,13 @@ def _reproject_blocked( # the input array to a Numpy memory map and load it in inside each process # as a memory-mapped array. We need to be careful how this gets passed to # reproject_single_block so we pass a variable that can be either a string - # or the array itself (for synchronous mode). We don't need to do this if - # the input dataset is a dask array as then dask knows how to pass it between - # processes. When the input array is a dask array, we also for now write - # this to a memmap array and pass that to the individual reproject functions. - # In future, it might be possible to make at least some of the reproject - # functions understand dask arrays directly. - if parallel or dask_input: + # or the array itself (for synchronous mode). + if parallel: array_in_or_path = tempfile.mktemp() array_in_memmapped = np.memmap( array_in_or_path, dtype=float, shape=array_in.shape, mode="w+" ) - if dask_input: - # First compute and store the dask array to zarr using whatever - # the default scheduler is at this point - filename = tempfile.mktemp() - array_in.to_zarr(filename) - array_in = da.from_zarr(filename) - # Then store this in the Numpy memmap array (schedulers other than - # synchronous don't work well when writing to a Numpy array) - da.store( - array_in, - array_in_memmapped, - compute=True, - scheduler="synchronous", - ) - else: - array_in_memmapped[:] = array_in[:] + array_in_memmapped[:] = array_in[:] else: array_in_or_path = array_in From aa240dabaee3e27a743749b11779ce9fea968f5f Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Fri, 19 May 2023 12:27:02 +0100 Subject: [PATCH 128/366] Don't use input dask array block size for output array chunking --- reproject/interpolation/high_level.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/reproject/interpolation/high_level.py b/reproject/interpolation/high_level.py index 76d13f751..b6f61b7cc 100644 --- a/reproject/interpolation/high_level.py +++ b/reproject/interpolation/high_level.py @@ -117,9 +117,6 @@ def reproject_interp( if isinstance(order, str): order = ORDER[order] - if isinstance(array_in, da.core.Array) and block_size is None: - block_size = array_in.chunksize - # if either of these are not default, it means a blocked method must be used if block_size is not None or parallel is not False: return _reproject_blocked( From 82e9bfb9f275fdb059c4bb3477defb8c59f6888b Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Fri, 19 May 2023 12:28:20 +0100 Subject: [PATCH 129/366] Fix codestyle --- reproject/interpolation/high_level.py | 1 - 1 file changed, 1 deletion(-) diff --git a/reproject/interpolation/high_level.py b/reproject/interpolation/high_level.py index b6f61b7cc..98b126eca 100644 --- a/reproject/interpolation/high_level.py +++ b/reproject/interpolation/high_level.py @@ -2,7 +2,6 @@ import os from astropy.utils import deprecated_renamed_argument -from dask import array as da from ..utils import _reproject_blocked, parse_input_data, parse_output_projection from .core import _reproject_full From 3aeb27a6e97d0d85c2af09f074814e2da6e40fcb Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Fri, 19 May 2023 12:29:22 +0100 Subject: [PATCH 130/366] Remove unused changes --- reproject/utils.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/reproject/utils.py b/reproject/utils.py index b65626445..a4a5682fc 100644 --- a/reproject/utils.py +++ b/reproject/utils.py @@ -304,8 +304,6 @@ def _reproject_blocked( shape_in = array_in.shape - dask_input = isinstance(array_in, da.core.Array) - # When in parallel mode, we want to make sure we avoid having to copy the # input array to all processes for each chunk, so instead we write out # the input array to a Numpy memory map and load it in inside each process @@ -318,7 +316,6 @@ def _reproject_blocked( array_in_or_path, dtype=float, shape=array_in.shape, mode="w+" ) array_in_memmapped[:] = array_in[:] - else: array_in_or_path = array_in From 16af29807f26e726d54ffd1bd76fe6d7002d86ec Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Fri, 19 May 2023 12:41:12 +0100 Subject: [PATCH 131/366] Add a new 'all' extras for shapely and remove version restrictions for installing that dependency --- setup.cfg | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index 2421b615f..9d00e30a9 100644 --- a/setup.cfg +++ b/setup.cfg @@ -27,10 +27,12 @@ install_requires = fsspec [options.extras_require] +all = + shapely test = pytest-astropy testall = - shapely;sys_platform!='win32' and python_version!='3.8' + shapely sunpy[map]>=2.1;platform_machine!='i686' asdf gwcs From 2b1bd5bc17df4aaffcc857c5e9436b524deac2fd Mon Sep 17 00:00:00 2001 From: astrofrog Date: Fri, 19 May 2023 12:25:49 +0000 Subject: [PATCH 132/366] Update CHANGELOG --- CHANGES.md | 198 +++++++++++++++++++++++++++++++---------------------- 1 file changed, 115 insertions(+), 83 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index cc64982fa..c8f68741e 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,34 @@ +## v0.11.0 - 2023-05-19 + + +### What's Changed + +#### Bug Fixes + +- Fix for HighLevelWCS attribute error by @keflavich in https://github.com/astropy/reproject/pull/349 +- Fixes for solar frames and non-degree units by @astrofrog in https://github.com/astropy/reproject/pull/360 +- If shape_out is specified, use this over the array_shape attribute of a WCS object by @astrofrog in https://github.com/astropy/reproject/pull/361 + +#### New Features + +- Allow single inputs to find_optimal_celestial_wcs and add ability to specify HDU by @astrofrog in https://github.com/astropy/reproject/pull/344 +- Add support for specifying output projection as APE 14 WCS with array_shape defined by @astrofrog in https://github.com/astropy/reproject/pull/345 +- Started adding support for allowing dask arrays as input by @astrofrog in https://github.com/astropy/reproject/pull/352 + +#### Other Changes + +- Mark tests that use remote data by @smaret in https://github.com/astropy/reproject/pull/339 +- Fix code style by @astrofrog in https://github.com/astropy/reproject/pull/340 +- Simplify blocked reprojection implementation by using dask and improve efficiency of parallel reprojection by @astrofrog in https://github.com/astropy/reproject/pull/314 +- Remove code that was required for astropy<4 by @astrofrog in https://github.com/astropy/reproject/pull/346 +- Add a new 'all' extras for shapely by @astrofrog in https://github.com/astropy/reproject/pull/363 + +### New Contributors + +- @smaret made their first contribution in https://github.com/astropy/reproject/pull/339 + +**Full Changelog**: https://github.com/astropy/reproject/compare/v0.10.0...v0.11.0 + ## v0.10.0 - 2023-01-30 @@ -35,157 +66,158 @@ ## 0.9 - 2022-09-02 - Drop support for Python 3.7. - +- - Infrastructure and packaging updates. - +- - Made many improvements, bug fixes, and significant speed-ups for the adaptive - resampling algorithm, `reproject_adaptive`. These bug fixes may cause - changes to the reprojected images, which are typically negligible. - Improvements include the addition of a flux-conserving mode, support for a - Gaussian filter kernel, a menu of boundary-handling modes, and a - `center_jacobian` flag to trade speed for accuracy with rapidly-varying - transformations. - +- resampling algorithm, `reproject_adaptive`. These bug fixes may cause +- changes to the reprojected images, which are typically negligible. +- Improvements include the addition of a flux-conserving mode, support for a +- Gaussian filter kernel, a menu of boundary-handling modes, and a +- `center_jacobian` flag to trade speed for accuracy with rapidly-varying +- transformations. +- - Added a `roundtrip_coords` argument to `reproject_adaptive` and - `reproject_interp`. By default, all coordinate transformations are run in - both directions to handle some situations where they are ambiguous. This can - be disabled by setting `roundtrip_coords=False` which may offer a - significant speed increase. +- `reproject_interp`. By default, all coordinate transformations are run in +- both directions to handle some situations where they are ambiguous. This can +- be disabled by setting `roundtrip_coords=False` which may offer a +- significant speed increase. +- ## 0.8 - 2021-08-11 - Improve `find_optimal_celestial_wcs` to accept input data descriptions as - just array shapes, not necessarily fully populated arrays. This makes it - possible to solve for the optimal WCS for a set of images that couldn't fit - into memory all at once, since the actual data aren't needed for optimal WCS - determination. [#242] - +- just array shapes, not necessarily fully populated arrays. This makes it +- possible to solve for the optimal WCS for a set of images that couldn't fit +- into memory all at once, since the actual data aren't needed for optimal WCS +- determination. [#242] +- - Fix implementation of `hdu_weights` in `reproject_and_coadd`. [#249] - +- ## 0.7.1 - 2020-05-29 - Fixed compatibility with Astropy 4.1. [#234] - +- - Updated minimum requirement for SciPy. [#236] - +- ## 0.7 - 2020-04-02 - Made C extension in overlapArea.c thread-safe by removing global - variables. [#211] - +- variables. [#211] +- - Made it possible to control whether to output debugging information - from overlapArea.c by setting DEBUG_OVERLAP_AREA=1 at build-time. [#211] - +- from overlapArea.c by setting DEBUG_OVERLAP_AREA=1 at build-time. [#211] +- - Fix compatibility with astropy v4.0.1. [#227] - +- - Disable parallelization by default in `reproject_exact` - this can be - enabled with `parallel=True`. [#227] - +- enabled with `parallel=True`. [#227] +- - Fixed a bug with `reproject_exact` with `parallel=False` and - `return_footprint=False`, which caused the footprint to be returned - anyway. [#227] - +- `return_footprint=False`, which caused the footprint to be returned +- anyway. [#227] +- - The infrastructure of the package has been updated in line with the - APE 17 roadmap (https://github.com/astropy/astropy-APEs/blob/main/APE17.rst). - The main changes are that the `python setup.py test` and - `python setup.py build_docs` commands will no longer work. The - easiest way to replicate these commands is to install the tox - (https://tox.readthedocs.io) package and run `tox -e test` and - `tox -e build_docs`. It is also possible to run pytest and sphinx - directly. [#228] - +- APE 17 roadmap (https://github.com/astropy/astropy-APEs/blob/main/APE17.rst). +- The main changes are that the `python setup.py test` and +- `python setup.py build_docs` commands will no longer work. The +- easiest way to replicate these commands is to install the tox +- (https://tox.readthedocs.io) package and run `tox -e test` and +- `tox -e build_docs`. It is also possible to run pytest and sphinx +- directly. [#228] +- ## 0.6 - 2019-11-01 - Added support for using any WCS that conforms to the WCS API described - in the Astropy Proposal for Enhancements 14 (APE 14). The - `independent_celestial_slices=` argument to `reproject_interp` has - been deprecated since it is no longer needed, as transformations are - automatically done in the most efficient way possible. [#166] - +- in the Astropy Proposal for Enhancements 14 (APE 14). The +- `independent_celestial_slices=` argument to `reproject_interp` has +- been deprecated since it is no longer needed, as transformations are +- automatically done in the most efficient way possible. [#166] +- - Include a warning for high resolution images with `reproject_exact`, - since if the pixels are <0.05", precision issues can occur. [#200] - +- since if the pixels are <0.05", precision issues can occur. [#200] +- - Added a new `reproject_and_coadd` function for doing mosaicking of - individual images, and added section in documentation about mosaicking. - [#186] - +- individual images, and added section in documentation about mosaicking. +- [#186] +- - Added a new reproject.adaptive sub-package that implements the DeForest - (2004) algorithm for reprojection. [#52] - +- (2004) algorithm for reprojection. [#52] +- - Fixed a bug that caused 'exact' reprojection results to have numerical - issues when doing identity transformations. [#190] - +- issues when doing identity transformations. [#190] +- ## 0.5.1 - 2019-09-01 - Fixed a bug that caused 'exact' reprojection to fail if one or more of - the WCSes was oriented such that E and W were flipped. [#188] +- the WCSes was oriented such that E and W were flipped. [#188] ## 0.5 - 2019-06-13 - Improve parse_output_projection to make it so that the output projection - can be specified as a filename. [#150] - +- can be specified as a filename. [#150] +- - Fixed a bug that caused HEALPix maps in RING order to not be correctly - interpreted. [#163] - +- interpreted. [#163] +- - Make it possible to specify the output array for reprojection using the - `output_array=` keyword argument. [#115] - +- `output_array=` keyword argument. [#115] +- ## 0.4 - 2018-01-29 - Refactored HEALPix reprojection code to use the astropy-healpix package - instead of healpy. [#139] - +- instead of healpy. [#139] +- - Added the ability to specify an output array in `reproject_interp`, which - permits the use of memory-mapped arrays and therefore provides the capability - to handle data cubes much larger than memory [#115] - +- permits the use of memory-mapped arrays and therefore provides the capability +- to handle data cubes much larger than memory [#115] +- - Fix test 32-bit test failures. [#146] - +- - Fix an issue with reprojecting images where there are two solutions along - the line of sight by forcing round-tripping of coordinate conversions [#129] - +- the line of sight by forcing round-tripping of coordinate conversions [#129] +- - Explicitly define default HDU as 0 for normal reprojection and 1 for - HEALPix reprojection. [#119] - +- HEALPix reprojection. [#119] +- - Added a function to find the optimal WCS for a set of images. [#136, #137] - +- ## 0.3.2 - 2017-10-22 - Fix a regression that caused certain all-sky images (e.g. the Mellinger Milky - Way Panorama, http://www.milkywaysky.com) to be reprojected to all NaNs when - the output WCS was in Mollweide coordinates. [#124] +- Way Panorama, http://www.milkywaysky.com) to be reprojected to all NaNs when +- the output WCS was in Mollweide coordinates. [#124] ## 0.3.1 - 2016-07-07 - Include missing license file in tarball. - +- - Updated documentation to remove warnings about early versions. - +- ## 0.3 - 2016-07-06 - Allow users to pass a `field=` option to `reproject_from_healpix` - to access different fields in a HEALPIX file. [#86] - +- to access different fields in a HEALPIX file. [#86] +- - Significant improvements to performance when the input data is a large - memory-mapped array. [#105] - +- memory-mapped array. [#105] +- - Significant refactoring of interpolating reprojection to improve support for - n-dimensional arrays, optionally including two celestial axes (in which - case the coordinate transformation is taken into account). [#96, #102] - +- n-dimensional arrays, optionally including two celestial axes (in which +- case the coordinate transformation is taken into account). [#96, #102] +- ## 0.2 - 2015-10-29 - Fixed a bug that caused reprojection by interpolation to be truncated for - rectangular output images. +- rectangular output images. ## 0.1 - 2015-05-08 From 1e2d9743ee3eb54570399bdaa4644a6b1eaf0cb4 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Fri, 19 May 2023 13:32:09 +0100 Subject: [PATCH 133/366] Slight increase to tolerance to accommodate new Numpy versions --- reproject/adaptive/tests/test_core.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/reproject/adaptive/tests/test_core.py b/reproject/adaptive/tests/test_core.py index 39f46085f..858a2530f 100644 --- a/reproject/adaptive/tests/test_core.py +++ b/reproject/adaptive/tests/test_core.py @@ -141,7 +141,7 @@ def test_reproject_adaptive_high_aliasing_potential_rotation( # along the output x axis. With the input image containing vertical lines # with values of zero or one, we should have uniform values of 0.5 # throughout our output array. - np.testing.assert_allclose(array_out, 0.5, rtol=0.001) + np.testing.assert_allclose(array_out, 0.5, rtol=0.002) # Within the transforms, the order of operations is: # input pixel coordinates -> input rotation -> input scaling @@ -161,7 +161,7 @@ def test_reproject_adaptive_high_aliasing_potential_rotation( center_jacobian=center_jacobian, kernel=kernel, ) - np.testing.assert_allclose(array_out, 0.5, rtol=0.001) + np.testing.assert_allclose(array_out, 0.5, rtol=0.002) # But if we add a 90-degree rotation to the input coordinates, then when # our stretched output pixels are projected onto the input data, they will From f65fc6f97c38b1ccb70ccee4274ac3a6975cfe0d Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 19 May 2023 12:41:16 +0000 Subject: [PATCH 134/366] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- CHANGES.md | 66 +++++++++++++++++++++++++++--------------------------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index c8f68741e..c553e4d29 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -66,9 +66,9 @@ ## 0.9 - 2022-09-02 - Drop support for Python 3.7. -- +- - Infrastructure and packaging updates. -- +- - Made many improvements, bug fixes, and significant speed-ups for the adaptive - resampling algorithm, `reproject_adaptive`. These bug fixes may cause - changes to the reprojected images, which are typically negligible. @@ -76,13 +76,13 @@ - Gaussian filter kernel, a menu of boundary-handling modes, and a - `center_jacobian` flag to trade speed for accuracy with rapidly-varying - transformations. -- +- - Added a `roundtrip_coords` argument to `reproject_adaptive` and - `reproject_interp`. By default, all coordinate transformations are run in - both directions to handle some situations where they are ambiguous. This can - be disabled by setting `roundtrip_coords=False` which may offer a - significant speed increase. -- +- ## 0.8 - 2021-08-11 @@ -91,34 +91,34 @@ - possible to solve for the optimal WCS for a set of images that couldn't fit - into memory all at once, since the actual data aren't needed for optimal WCS - determination. [#242] -- +- - Fix implementation of `hdu_weights` in `reproject_and_coadd`. [#249] -- +- ## 0.7.1 - 2020-05-29 - Fixed compatibility with Astropy 4.1. [#234] -- +- - Updated minimum requirement for SciPy. [#236] -- +- ## 0.7 - 2020-04-02 - Made C extension in overlapArea.c thread-safe by removing global - variables. [#211] -- +- - Made it possible to control whether to output debugging information - from overlapArea.c by setting DEBUG_OVERLAP_AREA=1 at build-time. [#211] -- +- - Fix compatibility with astropy v4.0.1. [#227] -- +- - Disable parallelization by default in `reproject_exact` - this can be - enabled with `parallel=True`. [#227] -- +- - Fixed a bug with `reproject_exact` with `parallel=False` and - `return_footprint=False`, which caused the footprint to be returned - anyway. [#227] -- +- - The infrastructure of the package has been updated in line with the - APE 17 roadmap (https://github.com/astropy/astropy-APEs/blob/main/APE17.rst). - The main changes are that the `python setup.py test` and @@ -127,7 +127,7 @@ - (https://tox.readthedocs.io) package and run `tox -e test` and - `tox -e build_docs`. It is also possible to run pytest and sphinx - directly. [#228] -- +- ## 0.6 - 2019-11-01 @@ -136,20 +136,20 @@ - `independent_celestial_slices=` argument to `reproject_interp` has - been deprecated since it is no longer needed, as transformations are - automatically done in the most efficient way possible. [#166] -- +- - Include a warning for high resolution images with `reproject_exact`, - since if the pixels are <0.05", precision issues can occur. [#200] -- +- - Added a new `reproject_and_coadd` function for doing mosaicking of - individual images, and added section in documentation about mosaicking. - [#186] -- +- - Added a new reproject.adaptive sub-package that implements the DeForest - (2004) algorithm for reprojection. [#52] -- +- - Fixed a bug that caused 'exact' reprojection results to have numerical - issues when doing identity transformations. [#190] -- +- ## 0.5.1 - 2019-09-01 @@ -160,33 +160,33 @@ - Improve parse_output_projection to make it so that the output projection - can be specified as a filename. [#150] -- +- - Fixed a bug that caused HEALPix maps in RING order to not be correctly - interpreted. [#163] -- +- - Make it possible to specify the output array for reprojection using the - `output_array=` keyword argument. [#115] -- +- ## 0.4 - 2018-01-29 - Refactored HEALPix reprojection code to use the astropy-healpix package - instead of healpy. [#139] -- +- - Added the ability to specify an output array in `reproject_interp`, which - permits the use of memory-mapped arrays and therefore provides the capability - to handle data cubes much larger than memory [#115] -- +- - Fix test 32-bit test failures. [#146] -- +- - Fix an issue with reprojecting images where there are two solutions along - the line of sight by forcing round-tripping of coordinate conversions [#129] -- +- - Explicitly define default HDU as 0 for normal reprojection and 1 for - HEALPix reprojection. [#119] -- +- - Added a function to find the optimal WCS for a set of images. [#136, #137] -- +- ## 0.3.2 - 2017-10-22 @@ -197,22 +197,22 @@ ## 0.3.1 - 2016-07-07 - Include missing license file in tarball. -- +- - Updated documentation to remove warnings about early versions. -- +- ## 0.3 - 2016-07-06 - Allow users to pass a `field=` option to `reproject_from_healpix` - to access different fields in a HEALPIX file. [#86] -- +- - Significant improvements to performance when the input data is a large - memory-mapped array. [#105] -- +- - Significant refactoring of interpolating reprojection to improve support for - n-dimensional arrays, optionally including two celestial axes (in which - case the coordinate transformation is taken into account). [#96, #102] -- +- ## 0.2 - 2015-10-29 From d0e6926583a9010290c99a038121fc085d95b9e1 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Mon, 22 May 2023 08:06:49 +0100 Subject: [PATCH 135/366] Fix support for NDData objects with dask .data attributes --- reproject/conftest.py | 3 +++ reproject/utils.py | 6 +++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/reproject/conftest.py b/reproject/conftest.py index d75be112f..ff60d6f47 100644 --- a/reproject/conftest.py +++ b/reproject/conftest.py @@ -137,6 +137,8 @@ def valid_celestial_input(tmp_path, request, wcs): input_value = (da.from_array(array), wcs) elif request.param == "nddata": input_value = NDData(data=array, wcs=wcs) + elif request.param == "nddata_dask": + input_value = NDData(data=da.from_array(array), wcs=wcs) elif request.param == "wcs": set_wcs_array_shape(wcs, array.shape) input_value = wcs @@ -158,6 +160,7 @@ def valid_celestial_input(tmp_path, request, wcs): "data_wcs_tuple", "dask_wcs_tuple", "nddata", + "nddata_dask", ] diff --git a/reproject/utils.py b/reproject/utils.py index a4a5682fc..fcc246aeb 100644 --- a/reproject/utils.py +++ b/reproject/utils.py @@ -102,7 +102,11 @@ def parse_input_data(input_data, hdu_in=None): elif isinstance(input_data, BaseLowLevelWCS) and input_data.array_shape is not None: return input_data.array_shape, HighLevelWCSWrapper(input_data) elif isinstance(input_data, astropy.nddata.NDDataBase): - return input_data.data, input_data.wcs + if isinstance(input_data.data, da.core.Array): + data = _dask_to_numpy_memmap(input_data.data) + else: + data = input_data.data + return data, input_data.wcs else: raise TypeError( "input_data should either be an HDU object or a tuple " From 4ab3b3a8a8f2d070506013dd2b69c74bff8623f3 Mon Sep 17 00:00:00 2001 From: Sam Van Kooten Date: Wed, 24 May 2023 16:13:43 -0600 Subject: [PATCH 136/366] Add despike_jacobian option for adaptive resampling --- docs/celestial.rst | 14 ++ reproject/adaptive/core.py | 11 +- reproject/adaptive/deforest.pyx | 204 +++++++++++++++++++++++--- reproject/adaptive/high_level.py | 20 ++- reproject/adaptive/tests/test_core.py | 63 ++++++++ 5 files changed, 284 insertions(+), 28 deletions(-) diff --git a/docs/celestial.rst b/docs/celestial.rst index ca6766a27..f6688f0cc 100644 --- a/docs/celestial.rst +++ b/docs/celestial.rst @@ -243,6 +243,20 @@ points. This is more efficient, and the loss of accuracy is extremely small for transformations that vary smoothly between pixels. The default (``False``) is to use the faster option. +In some situations (e.g. an all-sky map, with a wrap point in the longitude), +extremely large Jacobian values may be computed which are artifacts of the +coordinate system definition, rather than reflecting the actual nature of the +coordinate transformation. This may result in a band of ``nan`` pixels in the +output image. In these situations, if the actual transformation is +approximately constant in the region of these artifacts, the +``despike_jacobian`` option should be enabled. If enabled, the typical +magnitude (distance from the determinant) of the Jacobian matrix, ``Jmag2 = +sum_j sum_i (J_ij**2)``, is computed for each pixel and compared to the 25th +percentile of that value in the local 3x3 neighborhood (i.e. the third-lowest +value). If it exceeds that percentile value by more than 10 times, the Jacobian +matrix is deemed to be "spiking" and it is replaced by the average of the +non-spiking values in the 3x3 neighborhood. + When, for any one output pixel, the sampling region in the input image straddles the boundary of the input image or lies entirely outside the input image, a range of boundary modes can be applied, and this is set with the diff --git a/reproject/adaptive/core.py b/reproject/adaptive/core.py index 8f4edaa9a..59c0bf1de 100644 --- a/reproject/adaptive/core.py +++ b/reproject/adaptive/core.py @@ -32,6 +32,7 @@ def _reproject_adaptive_2d( shape_out, return_footprint=True, center_jacobian=False, + despike_jacobian=False, roundtrip_coords=True, conserve_flux=False, kernel="Gaussian", @@ -62,6 +63,8 @@ def _reproject_adaptive_2d( Whether to return the footprint in addition to the output array. center_jacobian : bool Whether to compute centered Jacobians + despike_jacobian : bool + Whether to despike the Jacobians roundtrip_coords : bool Whether to veryfiy that coordinate transformations are defined in both directions. @@ -98,13 +101,6 @@ def _reproject_adaptive_2d( Solar Physics volume 219, pages 3–23 (2004), https://doi.org/10.1023/B:SOLA.0000021743.24248.b0 - Warnings - -------- - Coordinates that lie exactly on the edge of an all-sky map may be subject - to numerical issues, causing a band of NaN values around the edge of the - final image (when reprojecting from helioprojective to heliographic). - See https://github.com/astropy/reproject/issues/195 for more information. - Note ---- If the input array contains extra dimensions beyond what the input WCS has, @@ -156,6 +152,7 @@ def _reproject_adaptive_2d( transformer, out_of_range_nan=True, center_jacobian=center_jacobian, + despiked_jacobian=despike_jacobian, conserve_flux=conserve_flux, kernel=kernel, kernel_width=kernel_width, diff --git a/reproject/adaptive/deforest.pyx b/reproject/adaptive/deforest.pyx index 076a57acd..c2dd15a39 100644 --- a/reproject/adaptive/deforest.pyx +++ b/reproject/adaptive/deforest.pyx @@ -33,6 +33,7 @@ import numpy as np cimport cython cimport numpy as np from libc.math cimport atan2, ceil, cos, exp, fabs, floor, round, sin, sqrt +from libc.stdlib cimport qsort import sys @@ -171,6 +172,164 @@ cdef bint sample_array(double[:,:,:] source, double[:] dest, return True +@cython.boundscheck(False) +@cython.wraparound(False) +@cython.nonecheck(False) +@cython.cdivision(True) +cdef void calculate_jacobian(double[:, :] Ji, int center_jacobian, + int yi, int xi, + double[:, :, :] offset_source_x, double[:, :, :] offset_source_y, + double[:, :, :] Jx, double[:, :, :] Jy) nogil: + """ Utility function to calculate the Jacobian at one (yi, xi) location""" + if center_jacobian: + # Compute the Jacobian for the transformation applied to + # this pixel, as finite differences. + Ji[0,0] = -offset_source_x[yi, xi, 0] + offset_source_x[yi, xi+1, 0] + Ji[1,0] = -offset_source_x[yi, xi, 1] + offset_source_x[yi, xi+1, 1] + Ji[0,1] = -offset_source_y[yi, xi, 0] + offset_source_y[yi+1, xi, 0] + Ji[1,1] = -offset_source_y[yi, xi, 1] + offset_source_y[yi+1, xi, 1] + else: + # Compute the Jacobian for the transformation applied to + # this pixel, as a mean of the Jacobian a half-pixel + # forwards and backwards. + Ji[0,0] = (Jx[yi, xi, 0] + Jx[yi, xi+1, 0]) / 2 + Ji[1,0] = (Jx[yi, xi, 1] + Jx[yi, xi+1, 1]) / 2 + Ji[0,1] = (Jy[yi, xi, 0] + Jy[yi+1, xi, 0]) / 2 + Ji[1,1] = (Jy[yi, xi, 1] + Jy[yi+1, xi, 1]) / 2 + + +@cython.boundscheck(False) +@cython.wraparound(False) +@cython.nonecheck(False) +@cython.cdivision(True) +cdef int cmp_func(const void* a, const void* b) nogil: + cdef double a_v = (a)[0] + cdef double b_v = (b)[0] + if a_v < b_v: + return -1 + elif a_v == b_v: + return 0 + else: + return 1 + + +@cython.boundscheck(False) +@cython.wraparound(False) +@cython.nonecheck(False) +@cython.cdivision(True) +cdef void sort(double[:] a) nogil: + qsort(&a[0], a.shape[0], a.strides[0], &cmp_func) + + +@cython.boundscheck(False) +@cython.wraparound(False) +@cython.nonecheck(False) +@cython.cdivision(True) +cdef void despike_jacobian(double[:, :, :, :] jacobian): + """Detects and fixes pixels where the Jacobian is extremely large + + This can occur, e.g., for an all-sky map at the point where the longitude + wraps around. In such cases, the large Jacobian is an artefact of the + coordinates and should be eliminated. + + The spike detection uses the typical magnitude (distance from determinant) + of the Jacobian matrix + Jmag2 = sum_j sum_i (J_ij**2). + The value of Jmag2 is calculated in the 3x3 neighborhood around each pixel, + and the 25th percentile value (the third lowest value) is kept. Anywhere + Jmag2 > Jmag2_25pct * threshold_factor + is marked as a spike. Threshold_factor is currently hardcoded to 10. The + Jacobian's components at spike locations are replaced with the mean of those + from nearby non-spike locations. + + The average-magnitude-of-Jacobian method works okay because the + typical use case is for pixel to pixel mapping (resampling data + sets), where the overall singular value ratio is not likely to be + large (less than, say, 30). + """ + # Compute the magnitude of the Jacobian + cdef double[:, :] Jmag2 = np.empty((jacobian.shape[0], jacobian.shape[1])) + cdef int xi, yi + for yi in range(jacobian.shape[0]): + for xi in range(jacobian.shape[1]): + Jmag2[yi, xi] = ( + jacobian[yi, xi, 0, 0]**2 + + jacobian[yi, xi, 0, 1]**2 + + jacobian[yi, xi, 1, 0]**2 + + jacobian[yi, xi, 1, 1]**2) + + # Cycle through and look for outliers + cdef double percentile, thresh + cdef int n_contributing, ymax, xmax + ymax = jacobian.shape[0] - 2 + xmax = jacobian.shape[1] - 2 + cdef double[:] neighborhood = np.empty(9) + with nogil: + for yi in range(1, ymax + 1): + for xi in range(1, xmax + 1): + neighborhood[0:3] = Jmag2[yi-1, xi-1:xi+2] + neighborhood[3:6] = Jmag2[yi, xi-1:xi+2] + neighborhood[6:] = Jmag2[yi+1, xi-1:xi+2] + # Computing the percentile through this C function is *much* + # faster than calling np.percentile. + sort(neighborhood) + percentile = neighborhood[2] + thresh = 10 * percentile + if Jmag2[yi, xi] > thresh: + # This pixel is an outlier. Replace it with an average of + # the neighboring, non-outlier pixels + fill_in_jacobian( + jacobian, xi, xi-1, xi+1, yi, yi-1, yi+1, + Jmag2, thresh) + + # Check edges + if yi == 1 and Jmag2[0, xi] > thresh: + fill_in_jacobian( + jacobian, xi, xi-1, xi+1, 0, 0, 1, + Jmag2, thresh) + + if yi == ymax and Jmag2[ymax+1, xi] > thresh: + fill_in_jacobian( + jacobian, xi, xi-1, xi+1, ymax+1, ymax, ymax+1, + Jmag2, thresh) + + if xi == 1 and Jmag2[yi, 0] > thresh: + fill_in_jacobian( + jacobian, 0, 0, 1, yi, yi-1, yi+1, Jmag2, thresh) + + if xi == xmax and Jmag2[yi, xmax+1] > thresh: + fill_in_jacobian( + jacobian, xmax+1, xmax, xmax+1, yi, yi-1, yi+1, + Jmag2, thresh) + + +@cython.boundscheck(False) +@cython.wraparound(False) +@cython.nonecheck(False) +@cython.cdivision(True) +cdef void fill_in_jacobian(double[:, :, :, :] jacobian, + int xi, int xfirst, int xlast, + int yi, int yfirst, int ylast, + double[:, :] Jmag2, double thresh) nogil: + """ Utility function that replaces a spiking Jacobian pixel """ + jacobian[yi, xi] = 0 + cdef int n_contributing = 0 + for i in range(yfirst, ylast+1): + for j in range(xfirst, xlast+1): + if i == yi and j == xi: + continue + if Jmag2[i, j] < thresh: + n_contributing += 1 + jacobian[yi, xi, 0, 0] += jacobian[i, j, 0, 0] + jacobian[yi, xi, 0, 1] += jacobian[i, j, 0, 1] + jacobian[yi, xi, 1, 0] += jacobian[i, j, 1, 0] + jacobian[yi, xi, 1, 1] += jacobian[i, j, 1, 1] + jacobian[yi, xi, 0, 0] /= n_contributing + jacobian[yi, xi, 0, 1] /= n_contributing + jacobian[yi, xi, 1, 0] /= n_contributing + jacobian[yi, xi, 1, 1] /= n_contributing + + KERNELS = {} KERNELS['hann'] = 0 KERNELS['hanning'] = KERNELS['hann'] @@ -192,9 +351,11 @@ BOUNDARY_MODES['nearest'] = 6 def map_coordinates(double[:,:,:] source, double[:,:,:] target, Ci, int max_samples_width=-1, int conserve_flux=False, int progress=False, int singularities_nan=False, int x_cyclic=False, int y_cyclic=False, int out_of_range_nan=False, - bint center_jacobian=False, str kernel='gaussian', double kernel_width=1.3, + bint center_jacobian=False, bint despiked_jacobian=False, + str kernel='gaussian', double kernel_width=1.3, double sample_region_width=4, str boundary_mode="strict", - double boundary_fill_value=0, double boundary_ignore_threshold=0.5): + double boundary_fill_value=0, double boundary_ignore_threshold=0.5, + ): # n.b. the source and target arrays are expected to contain three # dimensions---the last two are the image dimensions, while the first # indexes multiple images with the same coordinates. The transformation is @@ -256,10 +417,10 @@ def map_coordinates(double[:,:,:] source, double[:,:,:] target, Ci, int max_samp # These source arrays store a corresponding input-image coordinate for each # pixel in the output image. cdef np.ndarray[np.float64_t, ndim=3] pixel_source = Ci(pixel_target) - cdef np.ndarray[np.float64_t, ndim=3] offset_source_x = None - cdef np.ndarray[np.float64_t, ndim=3] offset_source_y = None - cdef np.ndarray[np.float64_t, ndim=3] Jx = None - cdef np.ndarray[np.float64_t, ndim=3] Jy = None + cdef double[:,:,:] offset_source_x = None + cdef double[:,:,:] offset_source_y = None + cdef double[:,:,:] Jx = None + cdef double[:,:,:] Jy = None if center_jacobian: offset_source_x = Ci(offset_target_x) @@ -294,6 +455,19 @@ def map_coordinates(double[:,:,:] source, double[:,:,:] target, Ci, int max_samp pixel_source = pixel_source[1:-1, 1:-1] cdef double[:,:] Ji = np.zeros((2, 2)) + cdef double[:, :, :, :] jacobian = None + if despiked_jacobian: + # To do despiking, we need to have all the final Jacobian values + # computed and ready. If we're not despiking, there's no need to hold + # all the values in memory at once. + jacobian = np.empty((target.shape[1], target.shape[2], 2, 2)) + for yi in range(target.shape[1]): + for xi in range(target.shape[2]): + calculate_jacobian(Ji, center_jacobian, yi, xi, + offset_source_x, offset_source_y, Jx, Jy) + jacobian[yi, xi] = Ji + despike_jacobian(jacobian) + cdef double[:,:] Ji_padded = np.zeros((2, 2)) cdef double[:,:] J = np.zeros((2, 2)) cdef double[:,:] U = np.zeros((2, 2)) @@ -319,21 +493,11 @@ def map_coordinates(double[:,:,:] source, double[:,:,:] target, Ci, int max_samp # Iterate through each pixel in the output image. for yi in range(target.shape[1]): for xi in range(target.shape[2]): - if center_jacobian: - # Compute the Jacobian for the transformation applied to - # this pixel, as finite differences. - Ji[0,0] = -offset_source_x[yi, xi, 0] + offset_source_x[yi, xi+1, 0] - Ji[1,0] = -offset_source_x[yi, xi, 1] + offset_source_x[yi, xi+1, 1] - Ji[0,1] = -offset_source_y[yi, xi, 0] + offset_source_y[yi+1, xi, 0] - Ji[1,1] = -offset_source_y[yi, xi, 1] + offset_source_y[yi+1, xi, 1] + if despiked_jacobian: + Ji = jacobian[yi, xi] else: - # Compute the Jacobian for the transformation applied to - # this pixel, as a mean of the Jacobian a half-pixel - # forwards and backwards. - Ji[0,0] = (Jx[yi, xi, 0] + Jx[yi, xi+1, 0]) / 2 - Ji[1,0] = (Jx[yi, xi, 1] + Jx[yi, xi+1, 1]) / 2 - Ji[0,1] = (Jy[yi, xi, 0] + Jy[yi+1, xi, 0]) / 2 - Ji[1,1] = (Jy[yi, xi, 1] + Jy[yi+1, xi, 1]) / 2 + calculate_jacobian(Ji, center_jacobian, yi, xi, + offset_source_x, offset_source_y, Jx, Jy) if isnan(Ji[0,0]) or isnan(Ji[0,1]) or isnan(Ji[1,0]) or isnan(Ji[1,1]) or isnan(pixel_source[yi,xi,0]) or isnan(pixel_source[yi,xi,1]): target[:,yi,xi] = nan continue diff --git a/reproject/adaptive/high_level.py b/reproject/adaptive/high_level.py index 5f4972f27..1dbd4e645 100644 --- a/reproject/adaptive/high_level.py +++ b/reproject/adaptive/high_level.py @@ -13,6 +13,7 @@ def reproject_adaptive( hdu_in=0, return_footprint=True, center_jacobian=False, + despike_jacobian=False, roundtrip_coords=True, conserve_flux=False, kernel="gaussian", @@ -82,6 +83,22 @@ def reproject_adaptive( efficient, and the loss of accuracy is extremely small for transformations that vary smoothly between pixels. Defaults to ``False``. + despike_jacobian : bool + Whether to despike the computed Jacobian values. In some situations + (e.g. an all-sky map, with a wrap point in the longitude), extremely + large Jacobian values may be computed which are artifacts of the + coordinate system definition, rather than reflecting the actual nature + of the coordinate transformation. This may result in a band of ``nan`` + pixels in the output image. In these situations, if the actual + transformation is approximately constant in the region of these + artifacts, this option should be enabled. If enabled, the typical + magnitude (distance from the determinant) of the Jacobian matrix, + ``Jmag2 = sum_j sum_i (J_ij**2)``, is computed for each pixel and + compared to the 25th percentile of that value in the local 3x3 + neighborhood (i.e. the third-lowest value). If it exceeds that + percentile value by more than 10 times, the Jacobian matrix is deemed + to be "spiking" and it is replaced by the average of the non-spiking + values in the 3x3 neighborhood. roundtrip_coords : bool Whether to verify that coordinate transformations are defined in both directions. @@ -135,7 +152,7 @@ def reproject_adaptive( ``boundary_ignore_threshold`` argument. In that case, acts as ``strict``. * ``nearest`` --- Samples outside the input image are replaced by - the nearst in-bounds input pixel. + the nearest in-bounds input pixel. boundary_fill_value : double The constant value used by the ``constant`` boundary mode. @@ -172,6 +189,7 @@ def reproject_adaptive( shape_out, return_footprint=return_footprint, center_jacobian=center_jacobian, + despike_jacobian=despike_jacobian, roundtrip_coords=roundtrip_coords, conserve_flux=conserve_flux, kernel=kernel, diff --git a/reproject/adaptive/tests/test_core.py b/reproject/adaptive/tests/test_core.py index 858a2530f..e4f578cea 100644 --- a/reproject/adaptive/tests/test_core.py +++ b/reproject/adaptive/tests/test_core.py @@ -66,6 +66,69 @@ def test_reproject_adaptive_2d(wcsapi, center_jacobian, roundtrip_coords): return array_footprint_to_hdulist(array_out, footprint_out, header_out) +@pytest.mark.parametrize("axis", ("x", "y")) +@pytest.mark.parametrize("center_jacobian", (True, False)) +def test_reproject_adaptive_despike_jacobian(axis, center_jacobian): + if axis == "x": + wcs_in = WCS(naxis=2) + wcs_in.wcs.ctype = "RA---CAR", "DEC--CAR" + wcs_in.wcs.crpix = 18.5, 9.5 + wcs_in.wcs.crval = 180, 0 + wcs_in.wcs.cdelt = 10, 10 + data_in = np.arange(18 * 36).reshape((18, 36)) + + wcs_out = WCS(naxis=2) + wcs_out.wcs.ctype = "RA---CAR", "DEC--CAR" + wcs_out.wcs.crpix = 20, 20 + wcs_out.wcs.crval = 10, 0 + wcs_out.wcs.cdelt = 5, 1 + else: + wcs_in = WCS(naxis=2) + wcs_in.wcs.ctype = "DEC--CAR", "RA---CAR" + wcs_in.wcs.crpix = 9.5, 18.5 + wcs_in.wcs.crval = 0, 180 + wcs_in.wcs.cdelt = 10, 10 + data_in = np.arange(18 * 36).reshape((36, 18)) + + wcs_out = WCS(naxis=2) + wcs_out.wcs.ctype = "DEC--CAR", "RA---CAR" + wcs_out.wcs.crpix = 20, 20 + wcs_out.wcs.crval = 0, 10 + wcs_out.wcs.cdelt = 1, 5 + + output = reproject_adaptive( + (data_in, wcs_in), + wcs_out, + (40, 40), + roundtrip_coords=False, + return_footprint=False, + despike_jacobian=False, + center_jacobian=center_jacobian, + x_cyclic=(axis == "x"), + y_cyclic=(axis == "y"), + ) + + output_despiked = reproject_adaptive( + (data_in, wcs_in), + wcs_out, + (40, 40), + roundtrip_coords=False, + return_footprint=False, + despike_jacobian=True, + center_jacobian=center_jacobian, + x_cyclic=(axis == "x"), + y_cyclic=(axis == "y"), + ) + + finite = np.isfinite(output) + # There should be a strip of nans in the un-despiked output + assert np.sum(finite) < output.size + # The outputs should match where spiking wasn't a concern + np.testing.assert_equal(output[finite], output_despiked[finite]) + # The despiked output should have no nans + assert np.all(np.isfinite(output_despiked)) + + @pytest.mark.filterwarnings("ignore::FutureWarning") @pytest.mark.array_compare(single_reference=True) @pytest.mark.parametrize("center_jacobian", (False, True)) From d37fc5f036fd690c84c32d9cbc1f7d43b8e5e329 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Fri, 26 May 2023 11:55:45 +0100 Subject: [PATCH 137/366] Add check for cases where compute() has to be called twice on dask array, and set a maximum block size for chunks if not specified --- reproject/utils.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/reproject/utils.py b/reproject/utils.py index a4a5682fc..e5f911de0 100644 --- a/reproject/utils.py +++ b/reproject/utils.py @@ -27,6 +27,11 @@ def _dask_to_numpy_memmap(dask_array): memmap array. """ + # Sometimes compute() has to be called twice to return a Numpy array, + # so we need to check here if this is the case and call the first compute() + if isinstance(dask_array.ravel()[0].compute(), da.Array): + dask_array = dask_array.compute() + with tempfile.TemporaryDirectory() as zarr_tmp: # First compute and store the dask array to zarr using whatever # the default scheduler is at this point @@ -340,7 +345,10 @@ def reproject_single_block(a, block_info=None): # NOTE: the following array is just used to set up the iteration in map_blocks # but isn't actually used otherwise - this is deliberate. - output_array_dask = da.empty(shape_out, chunks=block_size or "auto") + if block_size: + output_array_dask = da.empty(shape_out, chunks=block_size) + else: + output_array_dask = da.empty(shape_out).rechunk(block_size_limit=8 * 1024**2) result = da.map_blocks( reproject_single_block, From f605db572824663d9658cbd2c76d9506e20d2a00 Mon Sep 17 00:00:00 2001 From: "P. L. Lim" <2090236+pllim@users.noreply.github.com> Date: Thu, 1 Jun 2023 18:25:42 -0400 Subject: [PATCH 138/366] TST: Update URL for Scientific Python nightlies --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index 11cb9536f..2ab6e3f34 100644 --- a/tox.ini +++ b/tox.ini @@ -14,7 +14,7 @@ setenv = HOME = {envtmpdir} MPLBACKEND = Agg PYTEST_COMMAND = pytest --arraydiff --arraydiff-default-format=fits --pyargs reproject --cov reproject --cov-config={toxinidir}/setup.cfg {toxinidir}/docs - devdeps: PIP_EXTRA_INDEX_URL = https://pypi.anaconda.org/astropy/simple https://pypi.anaconda.org/scipy-wheels-nightly/simple + devdeps: PIP_EXTRA_INDEX_URL = https://pypi.anaconda.org/astropy/simple https://pypi.anaconda.org/scientific-python-nightly-wheels/simple changedir = .tmp/{envname} deps = From 66368020907ffc85e00891a1c15c5238f739d824 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 12 Jun 2023 16:39:25 +0000 Subject: [PATCH 139/366] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/asottile/pyupgrade: v3.4.0 → v3.6.0](https://github.com/asottile/pyupgrade/compare/v3.4.0...v3.6.0) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index c623db3f8..2ff999c20 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -20,7 +20,7 @@ repos: - "--filter-files" - repo: https://github.com/asottile/pyupgrade - rev: v3.4.0 + rev: v3.6.0 hooks: - id: pyupgrade args: ["--py38-plus"] From b4015f9b91d21b7a5ef8f6a60b25442b985171bb Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 19 Jun 2023 16:39:09 +0000 Subject: [PATCH 140/366] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/asottile/pyupgrade: v3.6.0 → v3.7.0](https://github.com/asottile/pyupgrade/compare/v3.6.0...v3.7.0) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 2ff999c20..2b1b1f00c 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -20,7 +20,7 @@ repos: - "--filter-files" - repo: https://github.com/asottile/pyupgrade - rev: v3.6.0 + rev: v3.7.0 hooks: - id: pyupgrade args: ["--py38-plus"] From 69f91213f66f514de01fa16c2a44ecaf9e091c38 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 3 Jul 2023 16:41:00 +0000 Subject: [PATCH 141/366] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/asottile/pyupgrade: v3.7.0 → v3.8.0](https://github.com/asottile/pyupgrade/compare/v3.7.0...v3.8.0) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 2b1b1f00c..a19bb6a51 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -20,7 +20,7 @@ repos: - "--filter-files" - repo: https://github.com/asottile/pyupgrade - rev: v3.7.0 + rev: v3.8.0 hooks: - id: pyupgrade args: ["--py38-plus"] From e1df24bb0227d27890981fd0028e973b46479b67 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 10 Jul 2023 16:40:15 +0000 Subject: [PATCH 142/366] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/asottile/pyupgrade: v3.8.0 → v3.9.0](https://github.com/asottile/pyupgrade/compare/v3.8.0...v3.9.0) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index a19bb6a51..359ae8947 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -20,7 +20,7 @@ repos: - "--filter-files" - repo: https://github.com/asottile/pyupgrade - rev: v3.8.0 + rev: v3.9.0 hooks: - id: pyupgrade args: ["--py38-plus"] From 1fc620b373b29beec19bef89a2a97d4da7dd536b Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Thu, 13 Jul 2023 15:56:57 +0100 Subject: [PATCH 143/366] Fix --remote-data tests --- docs/celestial.rst | 2 +- .../reference/test_reproject_roundtrip.fits | Bin 270720 -> 270720 bytes reproject/utils.py | 4 +++- tox.ini | 2 +- 4 files changed, 5 insertions(+), 3 deletions(-) diff --git a/docs/celestial.rst b/docs/celestial.rst index f6688f0cc..2a0f804a8 100644 --- a/docs/celestial.rst +++ b/docs/celestial.rst @@ -125,7 +125,7 @@ coordinate falls within the bounds of the input image.) As an example, we start off by opening a FITS file using Astropy:: >>> from astropy.io import fits - >>> hdu = fits.open('http://data.astropy.org/galactic_center/gc_msx_e.fits')[0] # doctest: +REMOTE_DATA + >>> hdu = fits.open('http://data.astropy.org/galactic_center/gc_msx_e.fits')[0] # doctest: +REMOTE_DATA +IGNORE_OUTPUT Downloading http://data.astropy.org/galactic_center/gc_msx_e.fits [Done] The image is currently using a Plate Carée projection:: diff --git a/reproject/interpolation/tests/reference/test_reproject_roundtrip.fits b/reproject/interpolation/tests/reference/test_reproject_roundtrip.fits index 71416c810d29a0094677831be85e2a163df80d58..c586395017fe031cdf63bcaf558037892d7aaf02 100644 GIT binary patch delta 56611 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 delta 55772 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 Date: Thu, 13 Jul 2023 11:23:24 +0100 Subject: [PATCH 144/366] Moved and renamed _reproject_blocked to reproject.common --- reproject/common.py | 159 ++++++++++++++++++++++++++ reproject/interpolation/high_level.py | 5 +- reproject/utils.py | 155 +------------------------ 3 files changed, 163 insertions(+), 156 deletions(-) create mode 100644 reproject/common.py diff --git a/reproject/common.py b/reproject/common.py new file mode 100644 index 000000000..dfb89a63b --- /dev/null +++ b/reproject/common.py @@ -0,0 +1,159 @@ +import tempfile + +import dask +import dask.array as da +import numpy as np +from astropy.wcs.wcsapi import BaseHighLevelWCS, SlicedLowLevelWCS +from astropy.wcs.wcsapi.high_level_wcs_wrapper import HighLevelWCSWrapper + +__all__ = ['_reproject_dispatcher'] + + +def _reproject_dispatcher( + reproject_func, + array_in, + wcs_in, + shape_out, + wcs_out, + block_size, + output_array=None, + return_footprint=True, + output_footprint=None, + parallel=True, +): + """ + Main function that handles either calling the core algorithms directly or + parallelizing or operating in chunks, using dask. + + Parameters + ---------- + reproject_func + One the existing reproject functions implementing a reprojection algorithm + that that will be used be used to perform reprojection + array_in + Data following the same format as expected by underlying reproject_func, + expected to `~numpy.ndarray` when used from _reproject_blocked() + wcs_in: `~astropy.wcs.WCS` + WCS object corresponding to array_in + shape_out: tuple + Passed to reproject_func() alongside WCS out to determine image size + wcs_out: `~astropy.wcs.WCS` + Output WCS image will be projected to. Normally will correspond to subset of + total output image when used by _reproject_blocked() + block_size: tuple + The size of blocks in terms of output array pixels that each block will handle + reprojecting. Extending out from (0,0) coords positively, block sizes + are clamped to output space edges when a block would extend past edge + output_array : None or `~numpy.ndarray` + An array in which to store the reprojected data. This can be any numpy + array including a memory map, which may be helpful when dealing with + extremely large files. + return_footprint : bool + Whether to return the footprint in addition to the output array. + output_footprint : None or `~numpy.ndarray` + An array in which to store the footprint of reprojected data. This can be + any numpy array including a memory map, which may be helpful when dealing with + extremely large files. + parallel : bool or int + Flag for parallel implementation. If ``True``, a parallel implementation + is chosen, the number of processes selected automatically to be equal to + the number of logical CPUs detected on the machine. If ``False``, a + serial implementation is chosen. If the flag is a positive integer ``n`` + greater than one, a parallel implementation using ``n`` processes is chosen. + """ + + if output_array is None: + output_array = np.zeros(shape_out, dtype=float) + if output_footprint is None and return_footprint: + output_footprint = np.zeros(shape_out, dtype=float) + + if block_size is not None and len(block_size) < len(shape_out): + block_size = [-1] * (len(shape_out) - len(block_size)) + list(block_size) + + shape_in = array_in.shape + + # When in parallel mode, we want to make sure we avoid having to copy the + # input array to all processes for each chunk, so instead we write out + # the input array to a Numpy memory map and load it in inside each process + # as a memory-mapped array. We need to be careful how this gets passed to + # reproject_single_block so we pass a variable that can be either a string + # or the array itself (for synchronous mode). + if parallel: + array_in_or_path = tempfile.mktemp() + array_in_memmapped = np.memmap( + array_in_or_path, dtype=float, shape=array_in.shape, mode="w+" + ) + array_in_memmapped[:] = array_in[:] + else: + array_in_or_path = array_in + + def reproject_single_block(a, block_info=None): + if a.ndim == 0 or block_info is None or block_info == []: + return np.array([a, a]) + slices = [slice(*x) for x in block_info[None]["array-location"][-wcs_out.pixel_n_dim :]] + + if isinstance(wcs_out, BaseHighLevelWCS): + low_level_wcs = SlicedLowLevelWCS(wcs_out.low_level_wcs, slices=slices) + else: + low_level_wcs = SlicedLowLevelWCS(wcs_out, slices=slices) + wcs_out_sub = HighLevelWCSWrapper(low_level_wcs) + if isinstance(array_in_or_path, str): + array_in = np.memmap(array_in_or_path, dtype=float, shape=shape_in) + else: + array_in = array_in_or_path + array, footprint = reproject_func( + array_in, wcs_in, wcs_out_sub, block_info[None]["chunk-shape"][1:] + ) + return np.array([array, footprint]) + + # NOTE: the following array is just used to set up the iteration in map_blocks + # but isn't actually used otherwise - this is deliberate. + if block_size: + output_array_dask = da.empty(shape_out, chunks=block_size) + else: + output_array_dask = da.empty(shape_out).rechunk(block_size_limit=8 * 1024**2) + + result = da.map_blocks( + reproject_single_block, + output_array_dask, + dtype=float, + new_axis=0, + chunks=(2,) + output_array_dask.chunksize, + ) + + # Truncate extra elements + result = result[tuple([slice(None)] + [slice(s) for s in shape_out])] + + if parallel: + # As discussed in https://github.com/dask/dask/issues/9556, da.store + # will not work well in multiprocessing mode when the destination is a + # Numpy array. Instead, in this case we save the dask array to a zarr + # array on disk which can be done in parallel, and re-load it as a dask + # array. We can then use da.store in the next step using the + # 'synchronous' scheduler since that is I/O limited so does not need + # to be done in parallel. + filename = tempfile.mktemp() + if isinstance(parallel, int): + workers = {"num_workers": parallel} + else: + workers = {} + with dask.config.set(scheduler="processes", **workers): + result.to_zarr(filename) + result = da.from_zarr(filename) + + if return_footprint: + da.store( + [result[0], result[1]], + [output_array, output_footprint], + compute=True, + scheduler="synchronous", + ) + return output_array, output_footprint + else: + da.store( + result[0], + output_array, + compute=True, + scheduler="synchronous", + ) + return output_array diff --git a/reproject/interpolation/high_level.py b/reproject/interpolation/high_level.py index 98b126eca..fe830ec7b 100644 --- a/reproject/interpolation/high_level.py +++ b/reproject/interpolation/high_level.py @@ -3,7 +3,8 @@ from astropy.utils import deprecated_renamed_argument -from ..utils import _reproject_blocked, parse_input_data, parse_output_projection +from ..utils import parse_input_data, parse_output_projection +from ..common import _reproject_dispatcher from .core import _reproject_full __all__ = ["reproject_interp"] @@ -118,7 +119,7 @@ def reproject_interp( # if either of these are not default, it means a blocked method must be used if block_size is not None or parallel is not False: - return _reproject_blocked( + return _reproject_dispatcher( _reproject_full, array_in=array_in, wcs_in=wcs_in, diff --git a/reproject/utils.py b/reproject/utils.py index e2ede9e35..bd18eaba2 100644 --- a/reproject/utils.py +++ b/reproject/utils.py @@ -1,17 +1,14 @@ import tempfile from pathlib import Path -from concurrent import futures import astropy.nddata -import dask import dask.array as da import numpy as np from astropy.io import fits from astropy.io.fits import CompImageHDU, HDUList, Header, ImageHDU, PrimaryHDU from astropy.wcs import WCS -from astropy.wcs.wcsapi import BaseHighLevelWCS, BaseLowLevelWCS, SlicedLowLevelWCS +from astropy.wcs.wcsapi import BaseHighLevelWCS, BaseLowLevelWCS from astropy.wcs.wcsapi.high_level_wcs_wrapper import HighLevelWCSWrapper -from dask.utils import SerializableLock __all__ = [ "parse_input_data", @@ -250,153 +247,3 @@ def parse_output_projection(output_projection, shape_in=None, shape_out=None, ou # currently have any broadcast dims shape_out = (*shape_in[: -len(shape_out)], *shape_out) return wcs_out, tuple(shape_out) - - -def _reproject_blocked( - reproject_func, - array_in, - wcs_in, - shape_out, - wcs_out, - block_size, - output_array=None, - return_footprint=True, - output_footprint=None, - parallel=True, -): - """ - Implementation function that handles reprojecting subsets blocks of pixels - from an input image and holds metadata about where to reinsert when done. - - Parameters - ---------- - reproject_func - One the existing reproject functions implementing a reprojection algorithm - that that will be used be used to perform reprojection - array_in - Data following the same format as expected by underlying reproject_func, - expected to `~numpy.ndarray` when used from _reproject_blocked() - wcs_in: `~astropy.wcs.WCS` - WCS object corresponding to array_in - shape_out: tuple - Passed to reproject_func() alongside WCS out to determine image size - wcs_out: `~astropy.wcs.WCS` - Output WCS image will be projected to. Normally will correspond to subset of - total output image when used by _reproject_blocked() - block_size: tuple - The size of blocks in terms of output array pixels that each block will handle - reprojecting. Extending out from (0,0) coords positively, block sizes - are clamped to output space edges when a block would extend past edge - output_array : None or `~numpy.ndarray` - An array in which to store the reprojected data. This can be any numpy - array including a memory map, which may be helpful when dealing with - extremely large files. - return_footprint : bool - Whether to return the footprint in addition to the output array. - output_footprint : None or `~numpy.ndarray` - An array in which to store the footprint of reprojected data. This can be - any numpy array including a memory map, which may be helpful when dealing with - extremely large files. - parallel : bool or int - Flag for parallel implementation. If ``True``, a parallel implementation - is chosen, the number of processes selected automatically to be equal to - the number of logical CPUs detected on the machine. If ``False``, a - serial implementation is chosen. If the flag is a positive integer ``n`` - greater than one, a parallel implementation using ``n`` processes is chosen. - """ - - if output_array is None: - output_array = np.zeros(shape_out, dtype=float) - if output_footprint is None and return_footprint: - output_footprint = np.zeros(shape_out, dtype=float) - - if block_size is not None and len(block_size) < len(shape_out): - block_size = [-1] * (len(shape_out) - len(block_size)) + list(block_size) - - shape_in = array_in.shape - - # When in parallel mode, we want to make sure we avoid having to copy the - # input array to all processes for each chunk, so instead we write out - # the input array to a Numpy memory map and load it in inside each process - # as a memory-mapped array. We need to be careful how this gets passed to - # reproject_single_block so we pass a variable that can be either a string - # or the array itself (for synchronous mode). - if parallel: - array_in_or_path = tempfile.mktemp() - array_in_memmapped = np.memmap( - array_in_or_path, dtype=float, shape=array_in.shape, mode="w+" - ) - array_in_memmapped[:] = array_in[:] - else: - array_in_or_path = array_in - - def reproject_single_block(a, block_info=None): - if a.ndim == 0 or block_info is None or block_info == []: - return np.array([a, a]) - slices = [slice(*x) for x in block_info[None]["array-location"][-wcs_out.pixel_n_dim :]] - - if isinstance(wcs_out, BaseHighLevelWCS): - low_level_wcs = SlicedLowLevelWCS(wcs_out.low_level_wcs, slices=slices) - else: - low_level_wcs = SlicedLowLevelWCS(wcs_out, slices=slices) - wcs_out_sub = HighLevelWCSWrapper(low_level_wcs) - if isinstance(array_in_or_path, str): - array_in = np.memmap(array_in_or_path, dtype=float, shape=shape_in) - else: - array_in = array_in_or_path - array, footprint = reproject_func( - array_in, wcs_in, wcs_out_sub, block_info[None]["chunk-shape"][1:] - ) - return np.array([array, footprint]) - - # NOTE: the following array is just used to set up the iteration in map_blocks - # but isn't actually used otherwise - this is deliberate. - if block_size: - output_array_dask = da.empty(shape_out, chunks=block_size) - else: - output_array_dask = da.empty(shape_out).rechunk(block_size_limit=8 * 1024**2) - - result = da.map_blocks( - reproject_single_block, - output_array_dask, - dtype=float, - new_axis=0, - chunks=(2,) + output_array_dask.chunksize, - ) - - # Truncate extra elements - result = result[tuple([slice(None)] + [slice(s) for s in shape_out])] - - if parallel: - # As discussed in https://github.com/dask/dask/issues/9556, da.store - # will not work well in multiprocessing mode when the destination is a - # Numpy array. Instead, in this case we save the dask array to a zarr - # array on disk which can be done in parallel, and re-load it as a dask - # array. We can then use da.store in the next step using the - # 'synchronous' scheduler since that is I/O limited so does not need - # to be done in parallel. - filename = tempfile.mktemp() - if isinstance(parallel, int): - workers = {"num_workers": parallel} - else: - workers = {} - with dask.config.set(scheduler="processes", **workers): - result.to_zarr(filename) - result = da.from_zarr(filename) - - if return_footprint: - da.store( - [result[0], result[1]], - [output_array, output_footprint], - compute=True, - scheduler="synchronous", - ) - return output_array, output_footprint - else: - da.store( - result[0], - output_array, - compute=True, - scheduler="synchronous", - ) - return output_array From cb285ab4986815ab9102cd21e60473bd2fb0a105 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Thu, 13 Jul 2023 13:33:59 +0100 Subject: [PATCH 145/366] Ensure that all temporary files are cleaned up, and add option to specify return_type='numpy' or 'dask' for reproject_interp --- reproject/common.py | 309 +++++++++++++++++--------- reproject/interpolation/core.py | 22 -- reproject/interpolation/high_level.py | 52 ++--- reproject/utils.py | 33 ++- 4 files changed, 242 insertions(+), 174 deletions(-) diff --git a/reproject/common.py b/reproject/common.py index dfb89a63b..3b282f825 100644 --- a/reproject/common.py +++ b/reproject/common.py @@ -1,3 +1,5 @@ +import os +import uuid import tempfile import dask @@ -5,21 +7,44 @@ import numpy as np from astropy.wcs.wcsapi import BaseHighLevelWCS, SlicedLowLevelWCS from astropy.wcs.wcsapi.high_level_wcs_wrapper import HighLevelWCSWrapper +from dask import delayed -__all__ = ['_reproject_dispatcher'] +from .utils import _dask_to_numpy_memmap + +__all__ = ["_reproject_dispatcher"] + + +@delayed(pure=True) +def as_delayed_memmap_path(array, tmp_dir): + if isinstance(array, da.core.Array): + array_path, _ = _dask_to_numpy_memmap(array, tmp_dir) + else: + array_path = tempfile.mktemp() + array_memmapped = np.memmap( + array_path, + dtype=float, + shape=array.shape, + mode="w+", + ) + array_memmapped[:] = array[:] + + return array_path def _reproject_dispatcher( reproject_func, + *, array_in, wcs_in, shape_out, wcs_out, - block_size, - output_array=None, + block_size=None, + array_out=None, return_footprint=True, output_footprint=None, parallel=True, + reproject_func_kwargs=None, + return_type=None, ): """ Main function that handles either calling the core algorithms directly or @@ -30,27 +55,25 @@ def _reproject_dispatcher( reproject_func One the existing reproject functions implementing a reprojection algorithm that that will be used be used to perform reprojection - array_in - Data following the same format as expected by underlying reproject_func, - expected to `~numpy.ndarray` when used from _reproject_blocked() - wcs_in: `~astropy.wcs.WCS` - WCS object corresponding to array_in + array_in : `numpy.ndarray` or `dask.array.Array` + Numpy or dask input array + wcs_in: `~astropy.wcs.wcsapi.BaseHighLevelWCS` + Input data WCS shape_out: tuple - Passed to reproject_func() alongside WCS out to determine image size + Target shape wcs_out: `~astropy.wcs.WCS` - Output WCS image will be projected to. Normally will correspond to subset of - total output image when used by _reproject_blocked() - block_size: tuple + Target WCS + block_size: tuple, optional The size of blocks in terms of output array pixels that each block will handle reprojecting. Extending out from (0,0) coords positively, block sizes are clamped to output space edges when a block would extend past edge - output_array : None or `~numpy.ndarray` + array_out : `~numpy.ndarray`, optional An array in which to store the reprojected data. This can be any numpy array including a memory map, which may be helpful when dealing with extremely large files. - return_footprint : bool + return_footprint : bool, optional Whether to return the footprint in addition to the output array. - output_footprint : None or `~numpy.ndarray` + output_footprint : `~numpy.ndarray`, optional An array in which to store the footprint of reprojected data. This can be any numpy array including a memory map, which may be helpful when dealing with extremely large files. @@ -60,100 +83,180 @@ def _reproject_dispatcher( the number of logical CPUs detected on the machine. If ``False``, a serial implementation is chosen. If the flag is a positive integer ``n`` greater than one, a parallel implementation using ``n`` processes is chosen. + reproject_func_kwargs : dict, optional + Keyword arguments to pass through to ``reproject_func`` + return_type : {'numpy', 'dask'}, optional + Whether to return numpy or dask arrays - defaults to 'numpy'. """ - if output_array is None: - output_array = np.zeros(shape_out, dtype=float) - if output_footprint is None and return_footprint: - output_footprint = np.zeros(shape_out, dtype=float) - - if block_size is not None and len(block_size) < len(shape_out): - block_size = [-1] * (len(shape_out) - len(block_size)) + list(block_size) - - shape_in = array_in.shape - - # When in parallel mode, we want to make sure we avoid having to copy the - # input array to all processes for each chunk, so instead we write out - # the input array to a Numpy memory map and load it in inside each process - # as a memory-mapped array. We need to be careful how this gets passed to - # reproject_single_block so we pass a variable that can be either a string - # or the array itself (for synchronous mode). - if parallel: - array_in_or_path = tempfile.mktemp() - array_in_memmapped = np.memmap( - array_in_or_path, dtype=float, shape=array_in.shape, mode="w+" - ) - array_in_memmapped[:] = array_in[:] - else: - array_in_or_path = array_in + if return_type is None: + return_type = "numpy" + elif return_type not in ("numpy", "dask"): + raise ValueError("return_type should be set to 'numpy' or 'dask'") + + if reproject_func_kwargs is None: + reproject_func_kwargs = {} + + # We set up a global temporary directory since this will be used e.g. to + # store memory mapped Numpy arrays and zarr arrays. - def reproject_single_block(a, block_info=None): - if a.ndim == 0 or block_info is None or block_info == []: - return np.array([a, a]) - slices = [slice(*x) for x in block_info[None]["array-location"][-wcs_out.pixel_n_dim :]] + with tempfile.TemporaryDirectory() as tmp_dir: + if array_out is None: + array_out = np.zeros(shape_out, dtype=float) + elif array_out.shape != tuple(shape_out): + raise ValueError( + f"Output array shape {array_out.shape} should match " f"shape_out={shape_out}" + ) + elif array_out.dtype != array_in.dtype: + raise ValueError( + f"Output array dtype {array_out.dtype} should match " + f"input array dtype ({array_in.dtype})" + ) - if isinstance(wcs_out, BaseHighLevelWCS): - low_level_wcs = SlicedLowLevelWCS(wcs_out.low_level_wcs, slices=slices) + # If neither parallel nor blocked reprojection are requested, we simply + # call the underlying core reproject function with the full arrays. + + if block_size is None and parallel is False: + # If a dask array was passed as input, we first convert this to a + # Numpy memory mapped array + + if return_type != "numpy": + raise ValueError( + "Output cannot be returned as dask arrays " + "when parallel=False and no block size has " + "been specified" + ) + + if isinstance(array_in.data, da.core.Array): + _, array_in = _dask_to_numpy_memmap(array_in, tmp_dir) + + return reproject_func( + array_in, + wcs_in, + wcs_out, + shape_out=shape_out, + array_out=array_out, + return_footprint=return_footprint, + output_footprint=output_footprint, + **reproject_func_kwargs, + ) + + if output_footprint is None and return_footprint: + output_footprint = np.zeros(shape_out, dtype=float) + + if block_size is not None and len(block_size) < len(shape_out): + block_size = [-1] * (len(shape_out) - len(block_size)) + list(block_size) + + shape_in = array_in.shape + + # When in parallel mode, we want to make sure we avoid having to copy the + # input array to all processes for each chunk, so instead we write out + # the input array to a Numpy memory map and load it in inside each process + # as a memory-mapped array. We need to be careful how this gets passed to + # reproject_single_block so we pass a variable that can be either a string + # or the array itself (for synchronous mode). + + if parallel: + array_in_or_path = as_delayed_memmap_path(array_in, tmp_dir) else: - low_level_wcs = SlicedLowLevelWCS(wcs_out, slices=slices) - wcs_out_sub = HighLevelWCSWrapper(low_level_wcs) - if isinstance(array_in_or_path, str): - array_in = np.memmap(array_in_or_path, dtype=float, shape=shape_in) + # Here we could set array_in_or_path to array_in_path if it + # has been set previously, but in synchronous mode it is better to + # simply pass a reference to the memmap array itself to avoid having + # to load the memmap inside each reproject_single_block call. + array_in_or_path = array_in + + def reproject_single_block(a, array_or_path, block_info=None): + if a.ndim == 0 or block_info is None or block_info == []: + return np.array([a, a]) + slices = [slice(*x) for x in block_info[None]["array-location"][-wcs_out.pixel_n_dim :]] + + if isinstance(wcs_out, BaseHighLevelWCS): + low_level_wcs = SlicedLowLevelWCS(wcs_out.low_level_wcs, slices=slices) + else: + low_level_wcs = SlicedLowLevelWCS(wcs_out, slices=slices) + + wcs_out_sub = HighLevelWCSWrapper(low_level_wcs) + + if isinstance(array_or_path, str): + array_in = np.memmap(array_or_path, dtype=float, shape=shape_in) + else: + array_in = array_or_path + + if array_or_path is None: + raise ValueError() + + array, footprint = reproject_func( + array_in, + wcs_in, + wcs_out_sub, + block_info[None]["chunk-shape"][1:], + array_out=np.zeros(block_info[None]["chunk-shape"][1:]), + **reproject_func_kwargs, + ) + + return np.array([array, footprint]) + + # NOTE: the following array is just used to set up the iteration in map_blocks + # but isn't actually used otherwise - this is deliberate. + + if block_size: + array_out_dask = da.empty(shape_out, chunks=block_size) else: - array_in = array_in_or_path - array, footprint = reproject_func( - array_in, wcs_in, wcs_out_sub, block_info[None]["chunk-shape"][1:] + array_out_dask = da.empty(shape_out).rechunk(block_size_limit=8 * 1024**2) + + result = da.map_blocks( + reproject_single_block, + array_out_dask, + array_in_or_path, + dtype=float, + new_axis=0, + chunks=(2,) + array_out_dask.chunksize, ) - return np.array([array, footprint]) - # NOTE: the following array is just used to set up the iteration in map_blocks - # but isn't actually used otherwise - this is deliberate. - if block_size: - output_array_dask = da.empty(shape_out, chunks=block_size) - else: - output_array_dask = da.empty(shape_out).rechunk(block_size_limit=8 * 1024**2) - - result = da.map_blocks( - reproject_single_block, - output_array_dask, - dtype=float, - new_axis=0, - chunks=(2,) + output_array_dask.chunksize, - ) - - # Truncate extra elements - result = result[tuple([slice(None)] + [slice(s) for s in shape_out])] - - if parallel: - # As discussed in https://github.com/dask/dask/issues/9556, da.store - # will not work well in multiprocessing mode when the destination is a - # Numpy array. Instead, in this case we save the dask array to a zarr - # array on disk which can be done in parallel, and re-load it as a dask - # array. We can then use da.store in the next step using the - # 'synchronous' scheduler since that is I/O limited so does not need - # to be done in parallel. - filename = tempfile.mktemp() - if isinstance(parallel, int): - workers = {"num_workers": parallel} + # Truncate extra elements + result = result[tuple([slice(None)] + [slice(s) for s in shape_out])] + + if return_type == "dask": + if return_footprint: + return result[0], result[1] + else: + return result[0] + + # We now convert the dask arrays back to Numpy arrays + + if parallel: + # As discussed in https://github.com/dask/dask/issues/9556, da.store + # will not work well in multiprocessing mode when the destination is a + # Numpy array. Instead, in this case we save the dask array to a zarr + # array on disk which can be done in parallel, and re-load it as a dask + # array. We can then use da.store in the next step using the + # 'synchronous' scheduler since that is I/O limited so does not need + # to be done in parallel. + + if isinstance(parallel, int): + workers = {"num_workers": parallel} + else: + workers = {} + + zarr_path = os.path.join(tmp_dir, f"{uuid.uuid4()}.zarr") + + with dask.config.set(scheduler="processes", **workers): + result.to_zarr(zarr_path) + result = da.from_zarr(zarr_path) + + if return_footprint: + da.store( + [result[0], result[1]], + [array_out, output_footprint], + compute=True, + scheduler="synchronous", + ) + return array_out, output_footprint else: - workers = {} - with dask.config.set(scheduler="processes", **workers): - result.to_zarr(filename) - result = da.from_zarr(filename) - - if return_footprint: - da.store( - [result[0], result[1]], - [output_array, output_footprint], - compute=True, - scheduler="synchronous", - ) - return output_array, output_footprint - else: - da.store( - result[0], - output_array, - compute=True, - scheduler="synchronous", - ) - return output_array + da.store( + result[0], + array_out, + compute=True, + scheduler="synchronous", + ) + return array_out diff --git a/reproject/interpolation/core.py b/reproject/interpolation/core.py index 7fe3a0a9a..f1a4cb345 100644 --- a/reproject/interpolation/core.py +++ b/reproject/interpolation/core.py @@ -47,24 +47,6 @@ def _validate_wcs(wcs_in, wcs_out, shape_in, shape_out): raise ValueError("Output WCS has a spectral component but input WCS does not") -def _validate_array_out(array_out, array, shape_out): - if array_out is None: - return - - if array_out.shape != tuple(shape_out): - raise ValueError( - "Array sizes don't match. Output array shape " - "should be {}".format(str(tuple(shape_out))) - ) - elif array_out.dtype != array.dtype: - raise ValueError( - "An output array of a different type than the " - "input array was specified, which will create an " - "undesired duplicate copy of the input array " - "in memory." - ) - - def _reproject_full( array, wcs_in, @@ -96,10 +78,6 @@ def _reproject_full( # shape_out must be exactly a tuple type shape_out = tuple(shape_out) _validate_wcs(wcs_in, wcs_out, array.shape, shape_out) - _validate_array_out(array_out, array, shape_out) - - if array_out is None: - array_out = np.empty(shape_out) if output_footprint is None: output_footprint = np.empty(shape_out) diff --git a/reproject/interpolation/high_level.py b/reproject/interpolation/high_level.py index fe830ec7b..7b6b33a13 100644 --- a/reproject/interpolation/high_level.py +++ b/reproject/interpolation/high_level.py @@ -3,8 +3,8 @@ from astropy.utils import deprecated_renamed_argument -from ..utils import parse_input_data, parse_output_projection from ..common import _reproject_dispatcher +from ..utils import parse_input_data, parse_output_projection from .core import _reproject_full __all__ = ["reproject_interp"] @@ -16,20 +16,19 @@ ORDER["bicubic"] = 3 -@deprecated_renamed_argument("independent_celestial_slices", None, since="0.6") def reproject_interp( input_data, output_projection, shape_out=None, hdu_in=0, order="bilinear", - independent_celestial_slices=False, output_array=None, return_footprint=True, output_footprint=None, block_size=None, parallel=False, roundtrip_coords=True, + return_type=None, ): """ Reproject data to a new projection using interpolation (this is typically @@ -98,6 +97,8 @@ def reproject_interp( roundtrip_coords : bool Whether to verify that coordinate transformations are defined in both directions. + return_type : {'numpy', 'dask'}, optional + Whether to return numpy or dask arrays - defaults to 'numpy'. Returns ------- @@ -117,29 +118,22 @@ def reproject_interp( if isinstance(order, str): order = ORDER[order] - # if either of these are not default, it means a blocked method must be used - if block_size is not None or parallel is not False: - return _reproject_dispatcher( - _reproject_full, - array_in=array_in, - wcs_in=wcs_in, - wcs_out=wcs_out, - shape_out=shape_out, - output_array=output_array, - parallel=parallel, - block_size=block_size, - return_footprint=return_footprint, - output_footprint=output_footprint, - ) - else: - return _reproject_full( - array_in, - wcs_in, - wcs_out, - shape_out=shape_out, - order=order, - array_out=output_array, - return_footprint=return_footprint, - roundtrip_coords=roundtrip_coords, - output_footprint=output_footprint, - ) + # TODO: add tests that actually ensure that order and roundtrip_coords work + + return _reproject_dispatcher( + _reproject_full, + array_in=array_in, + wcs_in=wcs_in, + wcs_out=wcs_out, + shape_out=shape_out, + array_out=output_array, + parallel=parallel, + block_size=block_size, + return_footprint=return_footprint, + output_footprint=output_footprint, + reproject_func_kwargs={ + "order": order, + "roundtrip_coords": roundtrip_coords, + }, + return_type=return_type, + ) diff --git a/reproject/utils.py b/reproject/utils.py index bd18eaba2..3b5d0d905 100644 --- a/reproject/utils.py +++ b/reproject/utils.py @@ -1,3 +1,5 @@ +import os +import uuid import tempfile from pathlib import Path @@ -18,7 +20,7 @@ ] -def _dask_to_numpy_memmap(dask_array): +def _dask_to_numpy_memmap(dask_array, tmp_dir): """ Given a dask array, write it out to disk and load it again as a Numpy memmap array. @@ -29,6 +31,10 @@ def _dask_to_numpy_memmap(dask_array): if isinstance(dask_array.ravel()[0].compute(), da.Array): dask_array = dask_array.compute() + # NOTE: here we use a new TemporaryDirectory context manager for the zarr + # array because we can remove the temporary directory straight away after + # converting the input to a Numpy memory mapped array. + with tempfile.TemporaryDirectory() as zarr_tmp: # First compute and store the dask array to zarr using whatever # the default scheduler is at this point @@ -40,7 +46,7 @@ def _dask_to_numpy_memmap(dask_array): # Then store this in the Numpy memmap array (schedulers other than # synchronous don't work well when writing to a Numpy array) - memmap_path = tempfile.mktemp() + memmap_path = os.path.join(tmp_dir, f"{uuid.uuid4()}.npy") memmapped_array = np.memmap( memmap_path, @@ -56,12 +62,7 @@ def _dask_to_numpy_memmap(dask_array): scheduler="synchronous", ) - # Note that at this point the zarr directory should be removed, but - # the memmapped array will persist on disk. In future we may want to - # clean this up automatically once reproject has completed to avoid - # a build-up of temporary files - - return memmapped_array + return memmap_path, memmapped_array def parse_input_data(input_data, hdu_in=None): @@ -85,17 +86,13 @@ def parse_input_data(input_data, hdu_in=None): elif isinstance(input_data, (PrimaryHDU, ImageHDU, CompImageHDU)): return input_data.data, WCS(input_data.header) elif isinstance(input_data, tuple) and isinstance(input_data[0], (np.ndarray, da.core.Array)): - if isinstance(input_data[0], da.core.Array): - data = _dask_to_numpy_memmap(input_data[0]) - else: - data = input_data[0] if isinstance(input_data[1], Header): - return data, WCS(input_data[1]) + return input_data[0], WCS(input_data[1]) else: if isinstance(input_data[1], BaseHighLevelWCS): - return data, input_data[1] + return input_data[0], input_data[1] else: - return data, HighLevelWCSWrapper(input_data[1]) + return input_data[0], HighLevelWCSWrapper(input_data[1]) elif ( isinstance(input_data, BaseHighLevelWCS) and input_data.low_level_wcs.array_shape is not None @@ -104,11 +101,7 @@ def parse_input_data(input_data, hdu_in=None): elif isinstance(input_data, BaseLowLevelWCS) and input_data.array_shape is not None: return input_data.array_shape, HighLevelWCSWrapper(input_data) elif isinstance(input_data, astropy.nddata.NDDataBase): - if isinstance(input_data.data, da.core.Array): - data = _dask_to_numpy_memmap(input_data.data) - else: - data = input_data.data - return data, input_data.wcs + return input_data.data, input_data.wcs else: raise TypeError( "input_data should either be an HDU object or a tuple " From 2504efa7d4c802f77d3cf77d50aa4e84ce707e8e Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Thu, 13 Jul 2023 13:58:49 +0100 Subject: [PATCH 146/366] Extend blocked/parallel reprojection to adaptive and spherical_intersect algorithms --- reproject/adaptive/core.py | 12 ++++-- reproject/adaptive/high_level.py | 48 +++++++++++++-------- reproject/interpolation/core.py | 3 ++ reproject/interpolation/high_level.py | 12 +++--- reproject/spherical_intersect/core.py | 26 +++++++++-- reproject/spherical_intersect/high_level.py | 25 ++++++++--- 6 files changed, 92 insertions(+), 34 deletions(-) diff --git a/reproject/adaptive/core.py b/reproject/adaptive/core.py index 59c0bf1de..195905b4d 100644 --- a/reproject/adaptive/core.py +++ b/reproject/adaptive/core.py @@ -30,6 +30,8 @@ def _reproject_adaptive_2d( wcs_in, wcs_out, shape_out, + array_out=None, + output_footprint=None, return_footprint=True, center_jacobian=False, despike_jacobian=False, @@ -130,8 +132,11 @@ def _reproject_adaptive_2d( if extra_dimens_in != extra_dimens_out: raise ValueError("Dimensions to be looped over must match exactly") - # Create output array - array_out = np.zeros(shape_out) + if array_out is None: + array_out = np.empty(shape_out) + + if output_footprint is None: + output_footprint = np.empty(shape_out) if len(array_in.shape) == wcs_in.low_level_wcs.pixel_n_dim: # We don't need to broadcast the transformation over any extra @@ -167,6 +172,7 @@ def _reproject_adaptive_2d( array_out.shape = shape_out if return_footprint: - return array_out, (~np.isnan(array_out)).astype(float) + output_footprint[:] = (~np.isnan(array_out)).astype(float) + return array_out, output_footprint else: return array_out diff --git a/reproject/adaptive/high_level.py b/reproject/adaptive/high_level.py index 1dbd4e645..2b588d364 100644 --- a/reproject/adaptive/high_level.py +++ b/reproject/adaptive/high_level.py @@ -1,5 +1,6 @@ # Licensed under a 3-clause BSD style license - see LICENSE.rst +from ..common import _reproject_dispatcher from ..utils import parse_input_data, parse_output_projection from .core import _reproject_adaptive_2d @@ -11,7 +12,12 @@ def reproject_adaptive( output_projection, shape_out=None, hdu_in=0, + output_array=None, return_footprint=True, + output_footprint=None, + block_size=None, + parallel=False, + return_type=None, center_jacobian=False, despike_jacobian=False, roundtrip_coords=True, @@ -182,22 +188,30 @@ def reproject_adaptive( output_projection, shape_in=array_in.shape, shape_out=shape_out ) - return _reproject_adaptive_2d( - array_in, - wcs_in, - wcs_out, - shape_out, + return _reproject_dispatcher( + _reproject_adaptive_2d, + array_in=array_in, + wcs_in=wcs_in, + wcs_out=wcs_out, + shape_out=shape_out, + array_out=output_array, + parallel=parallel, + block_size=block_size, return_footprint=return_footprint, - center_jacobian=center_jacobian, - despike_jacobian=despike_jacobian, - roundtrip_coords=roundtrip_coords, - conserve_flux=conserve_flux, - kernel=kernel, - kernel_width=kernel_width, - sample_region_width=sample_region_width, - boundary_mode=boundary_mode, - boundary_fill_value=boundary_fill_value, - boundary_ignore_threshold=boundary_ignore_threshold, - x_cyclic=x_cyclic, - y_cyclic=y_cyclic, + output_footprint=output_footprint, + reproject_func_kwargs=dict( + center_jacobian=center_jacobian, + despike_jacobian=despike_jacobian, + roundtrip_coords=roundtrip_coords, + conserve_flux=conserve_flux, + kernel=kernel, + kernel_width=kernel_width, + sample_region_width=sample_region_width, + boundary_mode=boundary_mode, + boundary_fill_value=boundary_fill_value, + boundary_ignore_threshold=boundary_ignore_threshold, + x_cyclic=x_cyclic, + y_cyclic=y_cyclic, + ), + return_type=return_type, ) diff --git a/reproject/interpolation/core.py b/reproject/interpolation/core.py index f1a4cb345..c4adec34b 100644 --- a/reproject/interpolation/core.py +++ b/reproject/interpolation/core.py @@ -79,6 +79,9 @@ def _reproject_full( shape_out = tuple(shape_out) _validate_wcs(wcs_in, wcs_out, array.shape, shape_out) + if array_out is None: + array_out = np.empty(shape_out) + if output_footprint is None: output_footprint = np.empty(shape_out) diff --git a/reproject/interpolation/high_level.py b/reproject/interpolation/high_level.py index 7b6b33a13..e4209c1aa 100644 --- a/reproject/interpolation/high_level.py +++ b/reproject/interpolation/high_level.py @@ -21,14 +21,14 @@ def reproject_interp( output_projection, shape_out=None, hdu_in=0, - order="bilinear", output_array=None, return_footprint=True, output_footprint=None, block_size=None, parallel=False, - roundtrip_coords=True, return_type=None, + order="bilinear", + roundtrip_coords=True, ): """ Reproject data to a new projection using interpolation (this is typically @@ -131,9 +131,9 @@ def reproject_interp( block_size=block_size, return_footprint=return_footprint, output_footprint=output_footprint, - reproject_func_kwargs={ - "order": order, - "roundtrip_coords": roundtrip_coords, - }, + reproject_func_kwargs=dict( + order=order, + roundtrip_coords=roundtrip_coords, + ), return_type=return_type, ) diff --git a/reproject/spherical_intersect/core.py b/reproject/spherical_intersect/core.py index 694f0770c..aad78fc55 100644 --- a/reproject/spherical_intersect/core.py +++ b/reproject/spherical_intersect/core.py @@ -22,7 +22,23 @@ def _reproject_slice(args): return _reproject_slice_cython(*args) -def _reproject_celestial(array, wcs_in, wcs_out, shape_out, parallel=True, return_footprint=True): +def _reproject_celestial( + array, + wcs_in, + wcs_out, + shape_out, + array_out=None, + output_footprint=None, + parallel=True, + return_footprint=True, +): + + if array_out is None: + array_out = np.empty(shape_out) + + if output_footprint is None: + output_footprint = np.empty(shape_out) + # Check the parallel flag. if type(parallel) != bool and type(parallel) != int: raise TypeError("The 'parallel' flag must be a boolean or integral value") @@ -216,7 +232,11 @@ def _reproject_celestial(array, wcs_in, wcs_out, shape_out, parallel=True, retur output_weights = np.stack(output_weights) output_weights.shape = shape_out + # TODO: we should be able to make use of the output arrays more efficiently + + array_out[:] = outputs if return_footprint: - return outputs, output_weights + output_footprint[:] = output_weights + return array_out, output_footprint else: - return outputs + return array_out diff --git a/reproject/spherical_intersect/high_level.py b/reproject/spherical_intersect/high_level.py index babc27d86..3aaca8fff 100644 --- a/reproject/spherical_intersect/high_level.py +++ b/reproject/spherical_intersect/high_level.py @@ -1,5 +1,6 @@ # Licensed under a 3-clause BSD style license - see LICENSE.rst +from ..common import _reproject_dispatcher from ..utils import parse_input_data, parse_output_projection from ..wcs_utils import has_celestial from .core import _reproject_celestial @@ -8,7 +9,16 @@ def reproject_exact( - input_data, output_projection, shape_out=None, hdu_in=0, parallel=False, return_footprint=True + input_data, + output_projection, + shape_out=None, + hdu_in=0, + output_array=None, + return_footprint=True, + output_footprint=None, + block_size=None, + parallel=False, + return_type=None, ): """ Reproject data to a new projection using flux-conserving spherical @@ -72,13 +82,18 @@ def reproject_exact( ) if has_celestial(wcs_in) and wcs_in.pixel_n_dim == 2 and wcs_in.world_n_dim == 2: - return _reproject_celestial( - array_in, - wcs_in, - wcs_out, + return _reproject_dispatcher( + _reproject_celestial, + array_in=array_in, + wcs_in=wcs_in, + wcs_out=wcs_out, shape_out=shape_out, + array_out=output_array, parallel=parallel, + block_size=block_size, return_footprint=return_footprint, + output_footprint=output_footprint, + return_type=return_type, ) else: raise NotImplementedError( From 80c74edca7cc64e0a920d1e65ad550e410bbd666 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Thu, 13 Jul 2023 14:07:47 +0100 Subject: [PATCH 147/366] Remove custom parallel implementation for reproject_exact --- reproject/spherical_intersect/core.py | 140 ++++++-------------------- 1 file changed, 29 insertions(+), 111 deletions(-) diff --git a/reproject/spherical_intersect/core.py b/reproject/spherical_intersect/core.py index aad78fc55..3ad7e6935 100644 --- a/reproject/spherical_intersect/core.py +++ b/reproject/spherical_intersect/core.py @@ -1,6 +1,5 @@ # Licensed under a 3-clause BSD style license - see LICENSE.rst -import signal import warnings import numpy as np @@ -8,18 +7,7 @@ from astropy.wcs import WCS from astropy.wcs.utils import proj_plane_pixel_area - -def _init_worker(): - """ - Function to disable ctrl+c in the worker processes. - """ - signal.signal(signal.SIGINT, signal.SIG_IGN) - - -def _reproject_slice(args): - from ._overlap import _reproject_slice_cython - - return _reproject_slice_cython(*args) +from ._overlap import _reproject_slice_cython def _reproject_celestial( @@ -29,7 +17,6 @@ def _reproject_celestial( shape_out, array_out=None, output_footprint=None, - parallel=True, return_footprint=True, ): @@ -39,20 +26,6 @@ def _reproject_celestial( if output_footprint is None: output_footprint = np.empty(shape_out) - # Check the parallel flag. - if type(parallel) != bool and type(parallel) != int: - raise TypeError("The 'parallel' flag must be a boolean or integral value") - - if type(parallel) == int: - # parallel is a number of processes. - if parallel <= 0: - raise ValueError("The number of processors to use must be strictly positive") - nproc = parallel - else: - # parallel is a boolean flag. nproc = None here means automatically selected - # number of processes. - nproc = None if parallel else 1 - # There are currently precision issues below certain resolutions, so we # emit a warning if this is the case. For more details, see: # https://github.com/astropy/reproject/issues/199 @@ -139,104 +112,49 @@ def _reproject_celestial( # We don't need to broadcast the transformation over any extra # axes---add an extra axis of length one just so we have something # to loop over in all cases. + broadcasting = False array = array.reshape((1, *array.shape)) elif len(array.shape) > wcs_in.low_level_wcs.pixel_n_dim: # We're broadcasting. Flatten the extra dimensions so there's just one # to loop over + broadcasting = True array = array.reshape((-1, *array.shape[-wcs_in.low_level_wcs.pixel_n_dim :])) else: raise ValueError("Too few dimensions for input array") - # Put together the parameters common both to the serial and parallel implementations. The aca - # function is needed to enforce that the array will be contiguous when passed to the low-level - # raw C function, otherwise Cython might complain. - - aca = np.ascontiguousarray - common_func_par = [ - 0, - ny_in, - nx_out, - ny_out, - aca(xp_inout), - aca(yp_inout), - aca(xw_in), - aca(yw_in), - aca(xw_out), - aca(yw_out), - None, # input data - shape_out[-2:], - ] - array = aca(array) - - if nproc is None or nproc > 1: - # Spin up our process pool outside the loop over broadcast dimensions - from multiprocessing import Pool, cpu_count - - # If needed, establish the number of processors to use. - if nproc is None: - nproc = cpu_count() - - # Prime each process in the pool with a small function that disables - # the ctrl+c signal in the child process. - pool = Pool(nproc, _init_worker) - - outputs = [] - output_weights = [] - for i in range(len(array)): - common_func_par[-2] = array[i] + array = np.ascontiguousarray(array) - if nproc == 1: - array_new, weights = _reproject_slice([0, nx_in] + common_func_par) + for i in range(len(array)): + array_new, weights = _reproject_slice_cython( + 0, + nx_in, + 0, + ny_in, + nx_out, + ny_out, + np.ascontiguousarray(xp_inout), + np.ascontiguousarray(yp_inout), + np.ascontiguousarray(xw_in), + np.ascontiguousarray(yw_in), + np.ascontiguousarray(xw_out), + np.ascontiguousarray(yw_out), + array[i], + shape_out[-2:], + ) - with np.errstate(invalid="ignore"): - array_new /= weights + with np.errstate(invalid="ignore"): + array_new /= weights - outputs.append(array_new) + if broadcasting: + array_out[i] = array_new if return_footprint: - output_weights.append(weights) - - elif nproc > 1: - inputs = [] - for i in range(nproc): - start = int(nx_in) // nproc * i - end = int(nx_in) if i == nproc - 1 else int(nx_in) // nproc * (i + 1) - inputs.append([start, end] + common_func_par) - - results = pool.map(_reproject_slice, inputs) - - array_new, weights = zip(*results) - - array_new = sum(array_new) - weights = sum(weights) - - with np.errstate(invalid="ignore"): - array_new /= weights - - outputs.append(array_new) + output_footprint[i] = weights + else: + array_out[:] = array_new if return_footprint: - output_weights.append(weights) - - if nproc > 1: - pool.close() - - if len(shape_out) == wcs_out.low_level_wcs.pixel_n_dim: - # We weren't broadcasting, so don't return any extra dimensions - outputs = outputs[0] - if return_footprint: - output_weights = output_weights[0] - else: - outputs = np.stack(outputs) - # If we're broadcasting over multiple dimensions, impose them here - outputs.shape = shape_out - if return_footprint: - output_weights = np.stack(output_weights) - output_weights.shape = shape_out - - # TODO: we should be able to make use of the output arrays more efficiently + output_footprint[:] = weights - array_out[:] = outputs if return_footprint: - output_footprint[:] = output_weights return array_out, output_footprint else: return array_out From c0cf7d122b4ebff709cef6c86379bc6ca58f28aa Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Thu, 13 Jul 2023 14:49:58 +0100 Subject: [PATCH 148/366] Fix tests --- reproject/common.py | 7 ++++-- reproject/spherical_intersect/core.py | 9 ++++++- .../tests/test_high_level.py | 14 +++++++++++ .../tests/test_reproject.py | 24 ++++--------------- reproject/tests/test_utils.py | 3 ++- 5 files changed, 33 insertions(+), 24 deletions(-) diff --git a/reproject/common.py b/reproject/common.py index 3b282f825..422f4e9aa 100644 --- a/reproject/common.py +++ b/reproject/common.py @@ -127,7 +127,7 @@ def _reproject_dispatcher( "been specified" ) - if isinstance(array_in.data, da.core.Array): + if isinstance(array_in, da.core.Array): _, array_in = _dask_to_numpy_memmap(array_in, tmp_dir) return reproject_func( @@ -234,7 +234,10 @@ def reproject_single_block(a, array_or_path, block_info=None): # to be done in parallel. if isinstance(parallel, int): - workers = {"num_workers": parallel} + if parallel > 0: + workers = {"num_workers": parallel} + else: + raise ValueError("The number of processors to use must be strictly positive") else: workers = {} diff --git a/reproject/spherical_intersect/core.py b/reproject/spherical_intersect/core.py index 3ad7e6935..f78c663c1 100644 --- a/reproject/spherical_intersect/core.py +++ b/reproject/spherical_intersect/core.py @@ -19,7 +19,6 @@ def _reproject_celestial( output_footprint=None, return_footprint=True, ): - if array_out is None: array_out = np.empty(shape_out) @@ -119,6 +118,8 @@ def _reproject_celestial( # to loop over broadcasting = True array = array.reshape((-1, *array.shape[-wcs_in.low_level_wcs.pixel_n_dim :])) + array_out = array_out.reshape((-1, *shape_out[-2:])) + output_footprint = output_footprint.reshape((-1, *shape_out[-2:])) else: raise ValueError("Too few dimensions for input array") @@ -146,6 +147,7 @@ def _reproject_celestial( array_new /= weights if broadcasting: + print(array_out.shape, array_new.shape) array_out[i] = array_new if return_footprint: output_footprint[i] = weights @@ -154,6 +156,11 @@ def _reproject_celestial( if return_footprint: output_footprint[:] = weights + if broadcasting: + array_out = array_out.reshape(shape_out) + if return_footprint: + output_footprint = output_footprint.reshape(shape_out) + if return_footprint: return array_out, output_footprint else: diff --git a/reproject/spherical_intersect/tests/test_high_level.py b/reproject/spherical_intersect/tests/test_high_level.py index 4bb93caea..dbffcaa04 100644 --- a/reproject/spherical_intersect/tests/test_high_level.py +++ b/reproject/spherical_intersect/tests/test_high_level.py @@ -41,6 +41,20 @@ def test_parallel_option(self): reproject_exact((self.array_in, self.header_in), self.header_out, parallel=-1) assert exc.value.args[0] == "The number of processors to use must be strictly positive" + def test_reproject_parallel_consistency(self): + reproject_exact((self.array_in, self.header_in), self.header_out, parallel=1) + + array1, footprint1 = reproject_exact( + (self.array_in, self.header_in), self.header_out, parallel=False + ) + array2, footprint2 = reproject_exact( + (self.array_in, self.header_in), self.header_out, parallel=4 + ) + + np.testing.assert_allclose(array1, array2, rtol=1.0e-5) + + np.testing.assert_allclose(footprint1, footprint2, rtol=3.0e-5) + def test_identity(): # Reproject an array and WCS to itself diff --git a/reproject/spherical_intersect/tests/test_reproject.py b/reproject/spherical_intersect/tests/test_reproject.py index 2cb90897b..eab2b5f40 100644 --- a/reproject/spherical_intersect/tests/test_reproject.py +++ b/reproject/spherical_intersect/tests/test_reproject.py @@ -67,20 +67,6 @@ def test_reproject_celestial_slices_2d(): ) -def test_reproject_celestial_consistency(): - # Consistency between the different modes - - wcs_in = WCS(fits.Header.fromstring(INPUT_HDR, sep="\n")) - wcs_out = WCS(fits.Header.fromstring(OUTPUT_HDR, sep="\n")) - - array1, footprint1 = _reproject_celestial(DATA, wcs_in, wcs_out, (4, 4), parallel=False) - array2, footprint2 = _reproject_celestial(DATA, wcs_in, wcs_out, (4, 4), parallel=True) - - np.testing.assert_allclose(array1, array2, rtol=1.0e-5) - - np.testing.assert_allclose(footprint1, footprint2, rtol=3.0e-5) - - @pytest.mark.parametrize("wcsapi", (False, True)) def test_reproject_celestial_montage(wcsapi): # Accuracy compared to Montage @@ -91,7 +77,7 @@ def test_reproject_celestial_montage(wcsapi): if wcsapi: # Enforce a pure wcsapi API wcs_in, wcs_out = as_high_level_wcs(wcs_in), as_high_level_wcs(wcs_out) - array, footprint = _reproject_celestial(DATA, wcs_in, wcs_out, (4, 4), parallel=False) + array, footprint = _reproject_celestial(DATA, wcs_in, wcs_out, (4, 4)) # TODO: improve agreement with Montage - at the moment agreement is ~10% np.testing.assert_allclose(array, MONTAGE_REF, rtol=0.09) @@ -103,21 +89,19 @@ def test_reproject_flipping(): wcs_in = WCS(fits.Header.fromstring(INPUT_HDR, sep="\n")) wcs_out = WCS(fits.Header.fromstring(OUTPUT_HDR, sep="\n")) - array1, footprint1 = _reproject_celestial(DATA, wcs_in, wcs_out, (4, 4), parallel=False) + array1, footprint1 = _reproject_celestial(DATA, wcs_in, wcs_out, (4, 4)) # Repeat with an input that is flipped horizontally with the equivalent WCS wcs_in_flipped = WCS(fits.Header.fromstring(INPUT_HDR, sep="\n")) wcs_in_flipped.wcs.cdelt[0] = -wcs_in_flipped.wcs.cdelt[0] wcs_in_flipped.wcs.crpix[0] = 3 - wcs_in_flipped.wcs.crpix[0] - array2, footprint2 = _reproject_celestial( - DATA[:, ::-1], wcs_in_flipped, wcs_out, (4, 4), parallel=False - ) + array2, footprint2 = _reproject_celestial(DATA[:, ::-1], wcs_in_flipped, wcs_out, (4, 4)) # Repeat with an output that is flipped horizontally with the equivalent WCS wcs_out_flipped = WCS(fits.Header.fromstring(OUTPUT_HDR, sep="\n")) wcs_out_flipped.wcs.cdelt[0] = -wcs_out_flipped.wcs.cdelt[0] wcs_out_flipped.wcs.crpix[0] = 5 - wcs_out_flipped.wcs.crpix[0] - array3, footprint3 = _reproject_celestial(DATA, wcs_in, wcs_out_flipped, (4, 4), parallel=False) + array3, footprint3 = _reproject_celestial(DATA, wcs_in, wcs_out_flipped, (4, 4)) array3, footprint3 = array3[:, ::-1], footprint3[:, ::-1] np.testing.assert_allclose(array1, array2, rtol=1.0e-5) diff --git a/reproject/tests/test_utils.py b/reproject/tests/test_utils.py index ce90b2d4c..c0f27126a 100644 --- a/reproject/tests/test_utils.py +++ b/reproject/tests/test_utils.py @@ -1,3 +1,4 @@ +import dask.array as da import numpy as np import pytest from astropy.io import fits @@ -16,7 +17,7 @@ def test_parse_input_data(tmpdir, valid_celestial_input_data, request): array_ref, wcs_ref, input_value, kwargs = valid_celestial_input_data data, wcs = parse_input_data(input_value, **kwargs) - assert isinstance(data, np.ndarray) + assert isinstance(data, (da.Array, np.ndarray)) np.testing.assert_allclose(data, array_ref) assert_wcs_allclose(wcs, wcs_ref) From e48626ecfcc807b13afbe7e9a0ee91a6a2c6891a Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Thu, 13 Jul 2023 16:51:07 +0100 Subject: [PATCH 149/366] Fix dtype check --- reproject/common.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/reproject/common.py b/reproject/common.py index 422f4e9aa..4ce2f9dda 100644 --- a/reproject/common.py +++ b/reproject/common.py @@ -107,7 +107,11 @@ def _reproject_dispatcher( raise ValueError( f"Output array shape {array_out.shape} should match " f"shape_out={shape_out}" ) - elif array_out.dtype != array_in.dtype: + elif (array_out.dtype.kind, array_out.dtype.itemsize) != ( + array_in.dtype.kind, + array_in.dtype.itemsize, + ): + # Note that here we don't care if the endians don't match raise ValueError( f"Output array dtype {array_out.dtype} should match " f"input array dtype ({array_in.dtype})" From 5e4a5dd07d0934294329d626641efd92b97cfb99 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Thu, 13 Jul 2023 20:44:05 +0100 Subject: [PATCH 150/366] Avoid keeping references to Numpy memmaps --- reproject/common.py | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/reproject/common.py b/reproject/common.py index 4ce2f9dda..8af8ef9c3 100644 --- a/reproject/common.py +++ b/reproject/common.py @@ -134,16 +134,20 @@ def _reproject_dispatcher( if isinstance(array_in, da.core.Array): _, array_in = _dask_to_numpy_memmap(array_in, tmp_dir) - return reproject_func( - array_in, - wcs_in, - wcs_out, - shape_out=shape_out, - array_out=array_out, - return_footprint=return_footprint, - output_footprint=output_footprint, - **reproject_func_kwargs, - ) + try: + return reproject_func( + array_in, + wcs_in, + wcs_out, + shape_out=shape_out, + array_out=array_out, + return_footprint=return_footprint, + output_footprint=output_footprint, + **reproject_func_kwargs, + ) + finally: + # Clean up reference to numpy memmap + array_in = None if output_footprint is None and return_footprint: output_footprint = np.zeros(shape_out, dtype=float) @@ -217,6 +221,10 @@ def reproject_single_block(a, array_or_path, block_info=None): chunks=(2,) + array_out_dask.chunksize, ) + # Ensure that there are no more references to Numpy memmaps + array_in = None + array_in_or_path = None + # Truncate extra elements result = result[tuple([slice(None)] + [slice(s) for s in shape_out])] From 50d29b66c2acea8c81532daf436322b02f84d417 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Fri, 14 Jul 2023 09:23:21 +0100 Subject: [PATCH 151/366] Bump minimum required dask version to 2021.8 to avoid bug with memory mapped arrays on Windows --- setup.cfg | 2 +- tox.ini | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.cfg b/setup.cfg index 9d00e30a9..aa3e9f760 100644 --- a/setup.cfg +++ b/setup.cfg @@ -21,7 +21,7 @@ install_requires = astropy>=5.0 astropy-healpix>=0.6 scipy>=1.5 - dask[array]>=2020 + dask[array]>=2021.8 cloudpickle zarr fsspec diff --git a/tox.ini b/tox.ini index 6f4bb0bd7..cc387ab3f 100644 --- a/tox.ini +++ b/tox.ini @@ -24,7 +24,7 @@ deps = oldestdeps: astropy==5.0.* oldestdeps: astropy-healpix==0.6 oldestdeps: scipy==1.5.* - oldestdeps: dask==2020.12.* + oldestdeps: dask==2021.8.* devdeps: numpy>=0.0.dev0 devdeps: scipy>=0.0.dev0 From 64d6909aae33f8051516fa70be4d0776d2cdf458 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Fri, 14 Jul 2023 10:37:25 +0100 Subject: [PATCH 152/366] Added documentation about dask and dask.distributed --- docs/celestial.rst | 9 ++-- docs/dask.rst | 108 +++++++++++++++++++++++++++++++++++++++++++++ docs/index.rst | 1 + 3 files changed, 112 insertions(+), 6 deletions(-) create mode 100644 docs/dask.rst diff --git a/docs/celestial.rst b/docs/celestial.rst index 2a0f804a8..3aceaa256 100644 --- a/docs/celestial.rst +++ b/docs/celestial.rst @@ -385,12 +385,9 @@ reprojection using the spherical polygon intersection of input and output pixels >>> from reproject import reproject_exact -In addition to the arguments described in :ref:`common`, an optional -``parallel=`` option can be used to control whether to parallelize the -reprojection, and if so how many cores to use (see -:func:`~reproject.reproject_exact` for more details). For this algorithm, the -footprint array returned gives the exact fractional overlap of new pixels with -the original image (see :doc:`footprints` for more details). +For this algorithm, the footprint array returned gives the exact fractional +overlap of new pixels with the original image (see :doc:`footprints` for more +details). .. warning:: The :func:`~reproject.reproject_exact` is currently known to have precision issues for images with resolutions <0.05". For diff --git a/docs/dask.rst b/docs/dask.rst new file mode 100644 index 000000000..e23ed67fb --- /dev/null +++ b/docs/dask.rst @@ -0,0 +1,108 @@ +Integration with dask and parallel processing +============================================= + +The following functions all integrate well with the `dask `_ library. + +* :func:`~reproject.reproject_interp` +* :func:`~reproject.reproject_adaptive` +* :func:`~reproject.reproject_exact` + +This integration has several aspects that we will discuss in the following sections. + +.. testsetup:: + + >>> import numpy as np + >>> import dask.array as da + >>> input_array = np.random.random((1024, 1024)) + >>> dask_array = da.from_array(input_array, chunks=(128, 128)) + >>> from astropy.wcs import WCS + >>> wcs_in = WCS(naxis=2) + >>> wcs_out = WCS(naxis=2) + +Input dask arrays +----------------- + +The three reprojection functions mentioned above can accept dask arrays as +inputs, e.g. assuming you have already constructed a dask array named +``dask_array``:: + + >>> dask_array + dask.array + +you can pass this in as part of the first argument to one of the reprojection +functions:: + + >>> from reproject import reproject_interp + >>> array, footprint = reproject_interp((dask_array, wcs_in), wcs_out, + ... shape_out=(2048, 2048)) + +In general however, we cannot benefit much from the chunking of the input arrays +because any input pixel might in principle contribute to any output pixel. +Therefore, for now, when a dask array is passed as input, it is computed using +the current default scheduler and converted to a Numpy memory-mapped array. This +is done efficiently in terms of memory and never results in the whole dataset +being loaded into memory at any given time. However, this does require +sufficient space on disk to store the array. + +Chunk by chunk reprojection and parallel processing +--------------------------------------------------- + +Regardless of whether a dask or Numpy array is passed in as input to the +reprojection functions, you can specify a block size to use for the +reprojection, and this is used to iterate over chunks in the output array in +chunks. For instance, if you pass in a (1024, 1024) array and specify that the +shape of the output should be a (2048, 2048) array (e.g., via ``shape_out``), +then if you set ``block_size=(256, 256)``:: + + >>> input_array.shape + (1024, 1024) + >>> array, footprint = reproject_interp((input_array, wcs_in), wcs_out, + ... shape_out=(2048, 2048), block_size=(256, 256)) + +the reprojection will be done in 64 separate output chunks. Note however that +this does not break up the input array into chunks since in the general case any +input pixel may contribute to any output pixel. + +By default, the iteration over the output chunks is done in a single +process/thread, but you may specify ``parallel=True`` to process these in +parallel. If you do this, reproject will use multiple processes (rather than +threads) to parallelize the computation (this is because the core reprojection +algorithms we use are not currently thread-safe). If you specify +``parallel=True``, then ``block_size`` will be automatically set to a sensible +default, but you can also set ``block_size`` manually for more control. Note +that you can also set ``parallel=`` to an integer to indicate the number of +processes to use. + +Output dask arrays +------------------ + +By default, the reprojection functions will do the computation immmediately and +return Numpy arrays for the reprojected array and optionally the footprint (this +is regardless of whether dask or Numpy arrays were passed in, or any of the +parallelization options above). However, by setting ``return_type='dask'``, you +can make the functions delay any computation and return dask arrays:: + + >>> array, footprint = reproject_interp((input_array, wcs_in), wcs_out, + ... shape_out=(2048, 2048), block_size=(256, 256), + ... return_type='dask') + >>> array + dask.array + +You can then compute the array or a section of the array yourself whenever you need, or use the +result in further dask expressions. + +.. warning:: The reprojection does not currently work reliably when using multiple threads, so + it is important to make sure you use a dask scheduler that is not multi-threaded. + At the time of writing, the default dask scheduler is ``threads``, so the scheduler + needs to be explicitly set to a different one. + +Using dask.distributed +---------------------- + +The `dask.distributed `_ package makes it +possible to use distributed schedulers for dask. In order to compute +reprojections with dask.distributed, you should make use of the +``return_type='dask'`` option mentioned above so that you can compute the dask +array once the distributed scheduler has been set up. As mentioned in `Output +dask arrays`_, you should make sure that you limit any cluster to have one +thread per process or the results may be unreliable. diff --git a/docs/index.rst b/docs/index.rst index 00be3ec47..6552ad26d 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -137,6 +137,7 @@ that you want to reproject. noncelestial footprints mosaicking + dask Reference/API ============= From 9b810c75e28cccdcf657af6b623c60442856fbaf Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Fri, 14 Jul 2023 10:51:41 +0100 Subject: [PATCH 153/366] Added regression test for roundtrip_coords not being passed through in blocked reprojection --- reproject/interpolation/tests/test_core.py | 54 +++++++++++++++++++++- 1 file changed, 53 insertions(+), 1 deletion(-) diff --git a/reproject/interpolation/tests/test_core.py b/reproject/interpolation/tests/test_core.py index 1941eefb0..459ee9a7f 100644 --- a/reproject/interpolation/tests/test_core.py +++ b/reproject/interpolation/tests/test_core.py @@ -555,7 +555,6 @@ def test_reproject_roundtrip(file_format): raise ValueError("file_format should be fits or asdf") # Reproject to an observer on Venus - target_wcs.wcs.cdelt = ([24, 24] * u.arcsec).to(u.deg) target_wcs.wcs.crpix = [64, 64] venus = get_body_heliographic_stonyhurst("venus", date) @@ -572,6 +571,59 @@ def test_reproject_roundtrip(file_format): return array_footprint_to_hdulist(output, footprint, header_out) +def test_reproject_roundtrip_kwarg(): + # Make sure that the roundtrip_coords keyword argument has an effect. This + # is a regression test for a bug that caused the keyword argument to be + # ignored when in parallel/blocked mode. + + pytest.importorskip("sunpy", minversion="2.1.0") + from sunpy.coordinates.ephemeris import get_body_heliographic_stonyhurst + from sunpy.map import Map + + map_aia = Map(get_pkg_data_filename("data/aia_171_level1.fits", package="reproject.tests")) + + # Reproject to an observer on Venus + target_wcs = map_aia.wcs.deepcopy() + target_wcs.wcs.cdelt = ([24, 24] * u.arcsec).to(u.deg) + target_wcs.wcs.crpix = [64, 64] + venus = get_body_heliographic_stonyhurst("venus", map_aia.date) + target_wcs.wcs.aux.hgln_obs = venus.lon.to_value(u.deg) + target_wcs.wcs.aux.hglt_obs = venus.lat.to_value(u.deg) + target_wcs.wcs.aux.dsun_obs = venus.radius.to_value(u.m) + + output_roundtrip_1 = reproject_interp( + map_aia, target_wcs, shape_out=(128, 128), return_footprint=False, roundtrip_coords=True + ) + output_roundtrip_2 = reproject_interp( + map_aia, + target_wcs, + shape_out=(128, 128), + return_footprint=False, + roundtrip_coords=True, + block_size=(32, 32), + ) + + assert_allclose(output_roundtrip_1, output_roundtrip_2) + + output_noroundtrip_1 = reproject_interp( + map_aia, target_wcs, shape_out=(128, 128), return_footprint=False, roundtrip_coords=False + ) + output_noroundtrip_2 = reproject_interp( + map_aia, + target_wcs, + shape_out=(128, 128), + return_footprint=False, + roundtrip_coords=False, + block_size=(32, 32), + ) + + assert_allclose(output_noroundtrip_1, output_noroundtrip_2) + + # The array with round-tripping should have more NaN values: + assert np.sum(np.isnan(output_roundtrip_1)) > 9500 + assert np.sum(np.isnan(output_noroundtrip_1)) < 7000 + + @pytest.mark.parametrize("roundtrip_coords", (False, True)) @pytest.mark.remote_data def test_identity_with_offset(roundtrip_coords): From 03c222fadf92f5bad5a8f7639c88c225c304105b Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Fri, 14 Jul 2023 10:59:29 +0100 Subject: [PATCH 154/366] Added regression test for order not being passed through in blocked reprojection --- reproject/interpolation/tests/test_core.py | 35 ++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/reproject/interpolation/tests/test_core.py b/reproject/interpolation/tests/test_core.py index 459ee9a7f..3e6654f34 100644 --- a/reproject/interpolation/tests/test_core.py +++ b/reproject/interpolation/tests/test_core.py @@ -844,3 +844,38 @@ def test_interp_input_output_types(valid_celestial_input_data, valid_celestial_o assert_allclose(output_ref, output_test) assert_allclose(footprint_ref, footprint_test) + + +@pytest.mark.parametrize("block_size", [None, (32, 32)]) +def test_reproject_order(block_size): + # Check that the order keyword argument has an effect. This is a regression + # test for a bug that caused the order= keyword argument to be ignored when + # in parallel/blocked reprojection. + + with fits.open(get_pkg_data_filename("data/galactic_2d.fits", package="reproject.tests")) as pf: + hdu_in = pf[0] + + header_out = hdu_in.header.copy() + header_out["CTYPE1"] = "RA---TAN" + header_out["CTYPE2"] = "DEC--TAN" + header_out["CRVAL1"] = 266.39311 + header_out["CRVAL2"] = -28.939779 + + array_out_bilinear = reproject_interp( + hdu_in, + header_out, + return_footprint=False, + order="bilinear", + block_size=block_size, + ) + + array_out_biquadratic = reproject_interp( + hdu_in, + header_out, + return_footprint=False, + order="biquadratic", + block_size=block_size, + ) + + with pytest.raises(AssertionError): + assert_allclose(array_out_bilinear, array_out_biquadratic) From 362ce3454d26a2a5e9921dd18f77fb79ec031cae Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Fri, 14 Jul 2023 12:18:31 +0100 Subject: [PATCH 155/366] Fix bug related to broadcasting --- reproject/common.py | 22 ++++++++++++++++------ reproject/interpolation/tests/test_core.py | 14 ++++++++++++++ 2 files changed, 30 insertions(+), 6 deletions(-) diff --git a/reproject/common.py b/reproject/common.py index 8af8ef9c3..425ada4ea 100644 --- a/reproject/common.py +++ b/reproject/common.py @@ -152,9 +152,6 @@ def _reproject_dispatcher( if output_footprint is None and return_footprint: output_footprint = np.zeros(shape_out, dtype=float) - if block_size is not None and len(block_size) < len(shape_out): - block_size = [-1] * (len(shape_out) - len(block_size)) + list(block_size) - shape_in = array_in.shape # When in parallel mode, we want to make sure we avoid having to copy the @@ -162,9 +159,12 @@ def _reproject_dispatcher( # the input array to a Numpy memory map and load it in inside each process # as a memory-mapped array. We need to be careful how this gets passed to # reproject_single_block so we pass a variable that can be either a string - # or the array itself (for synchronous mode). + # or the array itself (for synchronous mode). If the input array is a dask + # array we should always write it out to a memmap even in synchronous mode + # otherwise map_blocks gets confused if it gets two dask arrays and tries + # to iterate over both. - if parallel: + if isinstance(array_in, da.core.Array) or parallel: array_in_or_path = as_delayed_memmap_path(array_in, tmp_dir) else: # Here we could set array_in_or_path to array_in_path if it @@ -208,9 +208,19 @@ def reproject_single_block(a, array_or_path, block_info=None): # but isn't actually used otherwise - this is deliberate. if block_size: + if wcs_in.low_level_wcs.pixel_n_dim < len(shape_out) and len(block_size) < len( + shape_out + ): + block_size = [-1] * (len(shape_out) - len(block_size)) + list(block_size) array_out_dask = da.empty(shape_out, chunks=block_size) else: - array_out_dask = da.empty(shape_out).rechunk(block_size_limit=8 * 1024**2) + if wcs_in.low_level_wcs.pixel_n_dim < len(shape_out): + chunks = (-1,) * (len(shape_out) - wcs_in.low_level_wcs.pixel_n_dim) + chunks += ("auto",) * wcs_in.low_level_wcs.pixel_n_dim + else: + chunks = None + array_out_dask = da.empty(shape_out) + array_out_dask = array_out_dask.rechunk(block_size_limit=8 * 1024**2, chunks=chunks) result = da.map_blocks( reproject_single_block, diff --git a/reproject/interpolation/tests/test_core.py b/reproject/interpolation/tests/test_core.py index 3e6654f34..cee625cd9 100644 --- a/reproject/interpolation/tests/test_core.py +++ b/reproject/interpolation/tests/test_core.py @@ -2,6 +2,7 @@ import itertools +import dask.array as da import numpy as np import pytest from astropy import units as u @@ -879,3 +880,16 @@ def test_reproject_order(block_size): with pytest.raises(AssertionError): assert_allclose(array_out_bilinear, array_out_biquadratic) + + +def test_reproject_block_size_broadcasting(): + # Regression test for a bug that caused the default chunk size to be + # inadequate when using broadcasting in parallel mode + + array_in = np.ones((200, 200, 200)) + wcs_in = WCS(naxis=2) + wcs_out = WCS(naxis=2) + + array_out = reproject_interp( + (array_in, wcs_in), wcs_out, shape_out=(300, 300), parallel=1, return_footprint=False + ) From 6345f8693c3bd729783ff061488654f50950dbbe Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Fri, 14 Jul 2023 12:53:22 +0100 Subject: [PATCH 156/366] Catch cases where block_size has been set incorrectly --- reproject/common.py | 21 +++++++---- reproject/interpolation/tests/test_core.py | 44 ++++++++++++++++++++-- 2 files changed, 55 insertions(+), 10 deletions(-) diff --git a/reproject/common.py b/reproject/common.py index 425ada4ea..af549caae 100644 --- a/reproject/common.py +++ b/reproject/common.py @@ -19,7 +19,7 @@ def as_delayed_memmap_path(array, tmp_dir): if isinstance(array, da.core.Array): array_path, _ = _dask_to_numpy_memmap(array, tmp_dir) else: - array_path = tempfile.mktemp() + array_path = os.path.join(tmp_dir, f"{uuid.uuid4()}.npy") array_memmapped = np.memmap( array_path, dtype=float, @@ -193,12 +193,14 @@ def reproject_single_block(a, array_or_path, block_info=None): if array_or_path is None: raise ValueError() + shape_out = block_info[None]["chunk-shape"][1:] + array, footprint = reproject_func( array_in, wcs_in, wcs_out_sub, - block_info[None]["chunk-shape"][1:], - array_out=np.zeros(block_info[None]["chunk-shape"][1:]), + shape_out=shape_out, + array_out=np.zeros(shape_out), **reproject_func_kwargs, ) @@ -208,10 +210,15 @@ def reproject_single_block(a, array_or_path, block_info=None): # but isn't actually used otherwise - this is deliberate. if block_size: - if wcs_in.low_level_wcs.pixel_n_dim < len(shape_out) and len(block_size) < len( - shape_out - ): - block_size = [-1] * (len(shape_out) - len(block_size)) + list(block_size) + if wcs_in.low_level_wcs.pixel_n_dim < len(shape_out): + if len(block_size) < len(shape_out): + block_size = [-1] * (len(shape_out) - len(block_size)) + list(block_size) + else: + for i in range(len(shape_out) - wcs_in.low_level_wcs.pixel_n_dim): + if block_size[i] != -1 and block_size[i] != shape_out[i]: + raise ValueError( + "block shape for extra broadcasted dimensions should cover entire array along those dimensions" + ) array_out_dask = da.empty(shape_out, chunks=block_size) else: if wcs_in.low_level_wcs.pixel_n_dim < len(shape_out): diff --git a/reproject/interpolation/tests/test_core.py b/reproject/interpolation/tests/test_core.py index cee625cd9..7d2100e12 100644 --- a/reproject/interpolation/tests/test_core.py +++ b/reproject/interpolation/tests/test_core.py @@ -886,10 +886,48 @@ def test_reproject_block_size_broadcasting(): # Regression test for a bug that caused the default chunk size to be # inadequate when using broadcasting in parallel mode - array_in = np.ones((200, 200, 200)) + array_in = np.ones((350, 250, 150)) wcs_in = WCS(naxis=2) wcs_out = WCS(naxis=2) - array_out = reproject_interp( - (array_in, wcs_in), wcs_out, shape_out=(300, 300), parallel=1, return_footprint=False + reproject_interp( + (array_in, wcs_in), + wcs_out, + shape_out=(300, 300), + parallel=1, + return_footprint=False, + ) + + # Specifying a block size that is missing the extra dimension should work fine: + + reproject_interp( + (array_in, wcs_in), + wcs_out, + shape_out=(300, 300), + parallel=1, + return_footprint=False, + block_size=(100, 100), ) + + # Specifying a block size with the extra dimension should work provided it matches the final output shape + + reproject_interp( + (array_in, wcs_in), + wcs_out, + shape_out=(300, 300), + parallel=1, + return_footprint=False, + block_size=(350, 100, 100), + ) + + # But it should fail if we specify a block size that is smaller that the total array shape + + with pytest.raises(ValueError, match="block shape for extra broadcasted dimensions"): + reproject_interp( + (array_in, wcs_in), + wcs_out, + shape_out=(300, 300), + parallel=1, + return_footprint=False, + block_size=(100, 100, 100), + ) From 1b8b7c04651659ab2dbfb0e16266a53ec65b4104 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Fri, 14 Jul 2023 13:27:44 +0100 Subject: [PATCH 157/366] Fix case where chunks=None --- reproject/common.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/reproject/common.py b/reproject/common.py index af549caae..b46490c5b 100644 --- a/reproject/common.py +++ b/reproject/common.py @@ -224,10 +224,13 @@ def reproject_single_block(a, array_or_path, block_info=None): if wcs_in.low_level_wcs.pixel_n_dim < len(shape_out): chunks = (-1,) * (len(shape_out) - wcs_in.low_level_wcs.pixel_n_dim) chunks += ("auto",) * wcs_in.low_level_wcs.pixel_n_dim + rechunk_kwargs = {"chunks": chunks} else: - chunks = None + rechunk_kwargs = {} array_out_dask = da.empty(shape_out) - array_out_dask = array_out_dask.rechunk(block_size_limit=8 * 1024**2, chunks=chunks) + array_out_dask = array_out_dask.rechunk( + block_size_limit=8 * 1024**2, **rechunk_kwargs + ) result = da.map_blocks( reproject_single_block, From 07858683b01ad35aa536b5e73c4df4330a589b1f Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 17 Jul 2023 16:41:29 +0000 Subject: [PATCH 158/366] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/psf/black: 23.3.0 → 23.7.0](https://github.com/psf/black/compare/23.3.0...23.7.0) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 359ae8947..fc2693425 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -67,7 +67,7 @@ repos: exclude: ".*(data.*|extern.*|cextern)$" - repo: https://github.com/psf/black - rev: 23.3.0 + rev: 23.7.0 hooks: - id: black From 410034490896210e6014bee783afc742316a4620 Mon Sep 17 00:00:00 2001 From: Sam Van Kooten Date: Tue, 18 Jul 2023 11:01:44 -0600 Subject: [PATCH 159/366] Update docstring for adaptive defaults --- reproject/adaptive/high_level.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/reproject/adaptive/high_level.py b/reproject/adaptive/high_level.py index 1dbd4e645..5b4f28764 100644 --- a/reproject/adaptive/high_level.py +++ b/reproject/adaptive/high_level.py @@ -108,9 +108,8 @@ def reproject_adaptive( The averaging kernel to use. Allowed values are 'Hann' and 'Gaussian'. Case-insensitive. The Gaussian kernel produces better photometric accuracy and stronger anti-aliasing at the cost of some blurring (on - the scale of a few pixels). If not specified, the Hann kernel is used - by default, but this will change to the Gaussian kernel in a future - release. + the scale of a few pixels). If not specified, the Gaussain kernel is + used by default. kernel_width : double The width of the kernel in pixels, measuring to +/- 1 sigma for the Gaussian window. Does not apply to the Hann window. Reducing this width @@ -131,8 +130,8 @@ def reproject_adaptive( accuracy. boundary_mode : str How to handle when the sampling region includes regions outside the - bounds of the input image. The default is ``ignore``, but this will - change to ``strict`` in a future release. Allowed values are: + bounds of the input image. The default is ``strict``. Allowed values + are: * ``strict`` --- Output pixels will be NaN if any input sample falls outside the input image. From 16d9003302f7cf7b1f3d869cfe958bd4ca061e7e Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 31 Jul 2023 16:42:40 +0000 Subject: [PATCH 160/366] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/asottile/pyupgrade: v3.9.0 → v3.10.1](https://github.com/asottile/pyupgrade/compare/v3.9.0...v3.10.1) - [github.com/PyCQA/flake8: 6.0.0 → 6.1.0](https://github.com/PyCQA/flake8/compare/6.0.0...6.1.0) --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index fc2693425..e18835272 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -20,7 +20,7 @@ repos: - "--filter-files" - repo: https://github.com/asottile/pyupgrade - rev: v3.9.0 + rev: v3.10.1 hooks: - id: pyupgrade args: ["--py38-plus"] @@ -55,7 +55,7 @@ repos: # F822: undefined name in __all__ # F823: local variable name referenced before assignment - repo: https://github.com/PyCQA/flake8 - rev: 6.0.0 + rev: 6.1.0 hooks: - id: flake8 args: From 479bbbc73fbfeb3d1db6cc6279b8fcafa8eb9dc9 Mon Sep 17 00:00:00 2001 From: James Davies Date: Fri, 18 Aug 2023 10:40:17 +0200 Subject: [PATCH 161/366] Fix docs mosaic page rendering --- docs/mosaicking.rst | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/mosaicking.rst b/docs/mosaicking.rst index 184e5c0f5..703b91926 100644 --- a/docs/mosaicking.rst +++ b/docs/mosaicking.rst @@ -16,7 +16,7 @@ We describe these in the sections below. For the examples on this page we will use the `PyVO `_ module to retrieve tiles from the 2MASS survey -around the M17 region:: +around the M17 region: .. doctest-requires:: pyvo @@ -50,7 +50,7 @@ case you can skip straight to :ref:`coadding`. You can optionally provide options to try and constrain the solution, as we will see below. To start off, let's consider the simplest example, which is to call :func:`~reproject.mosaicking.find_optimal_celestial_wcs` -with the files downloaded above, but no additional information:: +with the files downloaded above, but no additional information: .. doctest-requires:: pyvo @@ -62,7 +62,7 @@ should be a list where each element is either a filename, an HDU object (e.g. `~astropy.io.fits.PrimaryHDU` or `~astropy.io.fits.ImageHDU`), an `~astropy.io.fits.HDUList` object, or a tuple of ``(array, wcs)``. In the example above, we have passed a list of HDUs. We can now look at the output -WCS and shape:: +WCS and shape: .. doctest-requires:: pyvo @@ -93,7 +93,7 @@ case above, the images are in equatorial coordinates, so the final WCS is also in equatorial coordinates. We can force the output WCS to instead be in Galactic coordinates by setting the ``frame=`` argument to a coordinate frame object such as :class:`~astropy.coordinates.Galactic` or one of the string -shortcuts defined in astropy (e.g. ``'fk5'``, ``'galactic'``, etc.):: +shortcuts defined in astropy (e.g. ``'fk5'``, ``'galactic'``, etc.): .. doctest-requires:: pyvo @@ -171,7 +171,7 @@ Pixel resolution By default, the final mosaic will have the pixel resolution (i.e. the pixel scale along the pixel axes) of the highest resolution input image, but this can -be overriden using the ``resolution=`` keyword argument:: +be overriden using the ``resolution=`` keyword argument: .. doctest-requires:: pyvo @@ -186,7 +186,7 @@ Finally, you can customize the projection to use as well as the reference coordinate. To change the projection from the default (which is the gnomonic projection, or ``TAN``), you can use the ``projection=`` keyword argument, which should be set to a `valid three-letter FITS-WCS projection -code `_:: +code `_: .. doctest-requires:: pyvo @@ -195,7 +195,7 @@ code `_:: To customize the reference coordinate (where the projection is centered) you can set the ``reference=`` keyword argument to an astropy -:class:`~astropy.coordinates.SkyCoord` object:: +:class:`~astropy.coordinates.SkyCoord` object: .. doctest-requires:: pyvo @@ -213,7 +213,7 @@ Assuming that you have a set of images that you want to combine into a mosaic, as well as a target header or WCS and shape (which you either determined independently, or with :ref:`optimal-wcs`), you can make use of the :func:`~reproject.mosaicking.reproject_and_coadd` function to produce the -mosaic:: +mosaic: .. doctest-requires:: pyvo @@ -295,7 +295,7 @@ We can take a look at the output: In some cases, including the above example, each tile that was used to compute the mosaic has an arbitrary offset due e.g. to different observing conditions. The :func:`~reproject.mosaicking.reproject_and_coadd` includes an option to -match the backgrounds (assuming a constant additive offset in each image):: +match the backgrounds (assuming a constant additive offset in each image): .. doctest-requires:: pyvo From 09f2adcb794327ba96b1e3b2e62d259af12d5349 Mon Sep 17 00:00:00 2001 From: Sam Van Kooten Date: Fri, 25 Aug 2023 19:49:10 -0600 Subject: [PATCH 162/366] Simplify "views" in reproject_and_coadd tests --- reproject/mosaicking/tests/test_coadd.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/reproject/mosaicking/tests/test_coadd.py b/reproject/mosaicking/tests/test_coadd.py index 8452a16fc..28d7cea0e 100644 --- a/reproject/mosaicking/tests/test_coadd.py +++ b/reproject/mosaicking/tests/test_coadd.py @@ -41,11 +41,10 @@ def _get_tiles(self, views): input_data = [] - for jmin, jmax, imin, imax in views: - array = self.array[jmin:jmax, imin:imax].copy() + for view in views: + array = self.array[view].copy() wcs = self.wcs.deepcopy() - wcs.wcs.crpix[0] -= imin - wcs.wcs.crpix[1] -= jmin + wcs = wcs[view] input_data.append((array, wcs)) return input_data @@ -58,7 +57,7 @@ def _nonoverlapping_views(self): views = [] for i in range(4): for j in range(5): - views.append((je[j], je[j + 1], ie[i], ie[i + 1])) + views.append(np.s_[je[j] : je[j + 1], ie[i] : ie[i + 1]]) return views @@ -70,7 +69,7 @@ def _overlapping_views(self): views = [] for i in range(4): for j in range(5): - views.append((je[j], je[j + 1] + 10, ie[i], ie[i + 1] + 10)) + views.append(np.s_[je[j] : je[j + 1] + 10, ie[i] : ie[i + 1] + 10]) return views From 7d5ecea31f0cce434e57b7f0bcd1528126898f55 Mon Sep 17 00:00:00 2001 From: Sam Van Kooten Date: Fri, 25 Aug 2023 18:28:18 -0600 Subject: [PATCH 163/366] Use more points to find image bounds in moasics --- reproject/mosaicking/coadd.py | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/reproject/mosaicking/coadd.py b/reproject/mosaicking/coadd.py index efe2089c4..8c1e067ab 100644 --- a/reproject/mosaicking/coadd.py +++ b/reproject/mosaicking/coadd.py @@ -125,16 +125,21 @@ def reproject_and_coadd( # Since we might be reprojecting small images into a large mosaic we # want to make sure that for each image we reproject to an array with - # minimal footprint. We therefore find the pixel coordinates of corners - # in the initial image and transform this to pixel coordinates in the - # final image to figure out the final WCS and shape to reproject to for - # each tile. Note that in future if we are worried about significant - # distortions of the edges in the reprojection process we could simply - # add arbitrary numbers of midpoints to this list. + # minimal footprint. We therefore find the pixel coordinates of the + # edges of the initial image and transform this to pixel coordinates in + # the final image to figure out the final WCS and shape to reproject to + # for each tile. We strike a balance between transforming only the + # input-image corners, which is fast but can cause clipping in cases of + # significant distortion (when the edges of the input image become + # convex in the output projection), and transforming every edge pixel, + # which provides a lot of redundant information. ny, nx = array_in.shape - xc = np.array([-0.5, nx - 0.5, nx - 0.5, -0.5]) - yc = np.array([-0.5, -0.5, ny - 0.5, ny - 0.5]) - xc_out, yc_out = wcs_out.world_to_pixel(wcs_in.pixel_to_world(xc, yc)) + n_per_edge = 10 + xs = np.linspace(-0.5, nx - 0.5, n_per_edge) + ys = np.linspace(-0.5, ny - 0.5, n_per_edge) + xs = np.concatenate((xs, np.full(n_per_edge, xs[-1]), xs, np.full(n_per_edge, xs[0]))) + ys = np.concatenate((np.full(n_per_edge, ys[0]), ys, np.full(n_per_edge, ys[-1]), ys)) + xc_out, yc_out = wcs_out.world_to_pixel(wcs_in.pixel_to_world(xs, ys)) # Determine the cutout parameters From 9f62ba25430c29fba3da090d89f64f9c0ebc51e9 Mon Sep 17 00:00:00 2001 From: Sam Van Kooten Date: Fri, 25 Aug 2023 19:49:43 -0600 Subject: [PATCH 164/366] Add 'first' and 'last' modes for mosaicking --- reproject/mosaicking/coadd.py | 29 +++++++++++++++---- reproject/mosaicking/tests/test_coadd.py | 36 ++++++++++++++++++++++-- 2 files changed, 58 insertions(+), 7 deletions(-) diff --git a/reproject/mosaicking/coadd.py b/reproject/mosaicking/coadd.py index efe2089c4..3f2bdc9a9 100644 --- a/reproject/mosaicking/coadd.py +++ b/reproject/mosaicking/coadd.py @@ -73,9 +73,12 @@ def reproject_and_coadd( `~astropy.io.fits.HDUList` instance, specifies the HDU to use. reproject_function : callable The function to use for the reprojection - combine_function : { 'mean', 'sum', 'median' } + combine_function : { 'mean', 'sum', 'median', 'first', 'last' } The type of function to use for combining the values into the final - image. + image. For 'first' and 'last', respectively, the reprojected images are + simply overlaid on top of each other. With respect to the order of the + input images in ``input_data``, either the first or the last image to + cover a region of overlap determines the output data for that region. match_background : bool Whether to match the backgrounds of the images. background_reference : `None` or `int` @@ -94,8 +97,8 @@ def reproject_and_coadd( # Validate inputs - if combine_function not in ("mean", "sum", "median"): - raise ValueError("combine_function should be one of mean/sum/median") + if combine_function not in ("mean", "sum", "median", "first", "last"): + raise ValueError("combine_function should be one of mean/sum/median/first/last") if reproject_function is None: raise ValueError( @@ -233,7 +236,23 @@ def reproject_and_coadd( if combine_function == "mean": with np.errstate(invalid="ignore"): final_array /= final_footprint - + elif combine_function == "first": + for array in arrays: + mask = final_footprint[array.view_in_original_array] == 0 + final_footprint[array.view_in_original_array] = np.where( + mask, array.footprint, final_footprint[array.view_in_original_array] + ) + final_array[array.view_in_original_array] = np.where( + mask, array.array, final_array[array.view_in_original_array] + ) + elif combine_function == "last": + for array in arrays: + final_footprint[array.view_in_original_array] = np.where( + array.footprint, array.footprint, final_footprint[array.view_in_original_array] + ) + final_array[array.view_in_original_array] = np.where( + array.footprint > 0, array.array, final_array[array.view_in_original_array] + ) elif combine_function == "median": # Here we need to operate in chunks since we could otherwise run # into memory issues diff --git a/reproject/mosaicking/tests/test_coadd.py b/reproject/mosaicking/tests/test_coadd.py index 28d7cea0e..e069dd900 100644 --- a/reproject/mosaicking/tests/test_coadd.py +++ b/reproject/mosaicking/tests/test_coadd.py @@ -8,7 +8,7 @@ from astropy.io import fits from astropy.io.fits import Header from astropy.wcs import WCS -from numpy.testing import assert_allclose +from numpy.testing import assert_allclose, assert_equal from reproject import reproject_exact, reproject_interp from reproject.mosaicking.coadd import reproject_and_coadd @@ -73,7 +73,7 @@ def _overlapping_views(self): return views - @pytest.mark.parametrize("combine_function", ["mean", "sum"]) + @pytest.mark.parametrize("combine_function", ["mean", "sum", "first", "last"]) def test_coadd_no_overlap(self, combine_function, reproject_function): # Make sure that if all tiles are exactly non-overlapping, and # we use 'sum' or 'mean', we get the exact input array back. @@ -108,6 +108,38 @@ def test_coadd_with_overlap(self, reproject_function): assert_allclose(array, self.array, atol=ATOL) + @pytest.mark.parametrize("combine_function", ["first", "last"]) + def test_coadd_with_overlap_first_last(self, reproject_function, combine_function): + views = self._overlapping_views + input_data = self._get_tiles(views) + + # Make each of the overlapping tiles different + for i, (array, wcs) in enumerate(input_data): + input_data[i] = (np.full_like(array, i), wcs) + + array, footprint = reproject_and_coadd( + input_data, + self.wcs, + shape_out=self.array.shape, + combine_function=combine_function, + reproject_function=reproject_function, + ) + + # Test that either the correct tile sets the output value in the overlap regions + test_sequence = list(enumerate(views)) + if combine_function == "last": + test_sequence = test_sequence[::-1] + for i, view in test_sequence: + # Each tile in test_sequence should overwrite teh following tiles + # in the overlap regions. We'll use nans to mark pixels in the + # output array that have already been set by a preceeding tile, so + # we'll go through, check that each tile matches the non-nan pixels + # in its region, and then set that whole region to nan. + output_tile = array[view] + output_values = output_tile[np.isfinite(output_tile)] + assert_equal(output_values, i) + array[view] = np.nan + def test_coadd_background_matching(self, reproject_function): # Test out the background matching From 6642ab64dcbf4aca0e4cfdf8f76bc4fd64d1060f Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Thu, 7 Sep 2023 10:50:17 +0100 Subject: [PATCH 165/366] Change n_per_edge to 11 to make sure there is a point in the middle of the edge --- reproject/mosaicking/coadd.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reproject/mosaicking/coadd.py b/reproject/mosaicking/coadd.py index 8c1e067ab..966041801 100644 --- a/reproject/mosaicking/coadd.py +++ b/reproject/mosaicking/coadd.py @@ -134,7 +134,7 @@ def reproject_and_coadd( # convex in the output projection), and transforming every edge pixel, # which provides a lot of redundant information. ny, nx = array_in.shape - n_per_edge = 10 + n_per_edge = 11 xs = np.linspace(-0.5, nx - 0.5, n_per_edge) ys = np.linspace(-0.5, ny - 0.5, n_per_edge) xs = np.concatenate((xs, np.full(n_per_edge, xs[-1]), xs, np.full(n_per_edge, xs[0]))) From c4746974fcb4a47900b1609cf91da7563ef40a54 Mon Sep 17 00:00:00 2001 From: Sam Van Kooten Date: Thu, 17 Aug 2023 13:29:05 -0500 Subject: [PATCH 166/366] Add modes for nan and inf handling to adaptive algo --- docs/celestial.rst | 15 +++- reproject/adaptive/core.py | 8 ++ reproject/adaptive/deforest.pyx | 76 +++++++++++++----- reproject/adaptive/high_level.py | 18 +++++ reproject/adaptive/tests/test_core.py | 108 +++++++++++++++++++++++++- 5 files changed, 200 insertions(+), 25 deletions(-) diff --git a/docs/celestial.rst b/docs/celestial.rst index 3aceaa256..035538515 100644 --- a/docs/celestial.rst +++ b/docs/celestial.rst @@ -173,7 +173,7 @@ integer or a string giving the order of the interpolation. Supported strings include: * ``'nearest-neighbor'``: zeroth order interpolation -* ``'bilinear'``: fisst order interpolation +* ``'bilinear'``: first order interpolation * ``'biquadratic'``: second order interpolation * ``'bicubic'``: third order interpolation @@ -279,7 +279,7 @@ image, a range of boundary modes can be applied, and this is set with the would have been assigned to the ignored samples exceeds a set fraction of the total weight across the entire sampling region, set by the ``boundary_ignore_threshold`` argument. In that case, acts as ``strict``. -* ``nearest`` --- Samples outside the input image are replaced by the nearst +* ``nearest`` --- Samples outside the input image are replaced by the nearest in-bounds input pixel. The input image can also be marked as being cyclic or periodic in the x and/or @@ -287,6 +287,17 @@ y axes with the ``x_cyclic`` and ``y_cyclic`` flags. If these are set, samples will wrap around to the opposite side of the image, ignoring the ``boundary_mode`` for that axis. +This implementation includes several options for handling ``nan`` and ``inf`` +values in the input data, set via the ``bad_val_mode`` argument: + +* ``strict`` --- Values of ``nan`` or ``inf`` in the input data are propagated + to every output value which samples them. +* ``ignore`` --- When a sampled input value is ``nan`` or ``inf``, that input + pixel is ignored (affected neither the accumulated sum of weighted samples + nor the accumulated sum of weights). +* ``constant`` --- Input values of ``nan`` and ``inf`` are replaced with a + constant value, set via the ``bad_fill_value`` argument. + Algorithm Description --------------------- diff --git a/reproject/adaptive/core.py b/reproject/adaptive/core.py index 195905b4d..b74d18ceb 100644 --- a/reproject/adaptive/core.py +++ b/reproject/adaptive/core.py @@ -45,6 +45,8 @@ def _reproject_adaptive_2d( boundary_ignore_threshold=0.5, x_cyclic=False, y_cyclic=False, + bad_val_mode="strict", + bad_fill_value=0, ): """ Reproject celestial slices from an n-d array from one WCS to another @@ -87,6 +89,10 @@ def _reproject_adaptive_2d( Threshold for 'ignore_threshold' boundary mode, ranging from 0 to 1. x_cyclic, y_cyclic : bool Marks in input-image axis as cyclic. + bad_val_mode : str + NaN and inf handling mode + bad_fill_value : float + Fill value for 'constant' bad value mode Returns ------- @@ -167,6 +173,8 @@ def _reproject_adaptive_2d( boundary_ignore_threshold=boundary_ignore_threshold, x_cyclic=x_cyclic, y_cyclic=y_cyclic, + bad_val_mode=bad_val_mode, + bad_fill_value=bad_fill_value, ) array_out.shape = shape_out diff --git a/reproject/adaptive/deforest.pyx b/reproject/adaptive/deforest.pyx index c2dd15a39..60f3b2ef6 100644 --- a/reproject/adaptive/deforest.pyx +++ b/reproject/adaptive/deforest.pyx @@ -43,6 +43,7 @@ cdef double nan = np.nan cdef extern from "math.h": int isnan(double x) nogil + int isinf(double x) nogil @cython.boundscheck(False) @@ -134,6 +135,12 @@ cdef double gaussian_filter(double x, double y, double width) nogil: @cython.cdivision(True) cdef double clip(double x, double vmin, double vmax, int cyclic, int out_of_range_nearest) nogil: + """Applies bounary conditions to an intended array coordinate. + + Specifically, if the point is outside the array bounds, this function wraps + the coordinate if the boundary is periodic, or clamps to the nearest valid + coordinate if desired, or else returns NaN. + """ if x < vmin: if cyclic: while x < vmin: @@ -164,6 +171,8 @@ cdef bint sample_array(double[:,:,:] source, double[:] dest, y = clip(y, 0, source.shape[1] - 1, y_cyclic, out_of_range_nearest) if isnan(x) or isnan(y): + # Indicates the coordinate is outside the array's bounds and the + # boundary-handling mode doesn't provide an alternative coordinate. return False # Cython doesn't like a return type of (double[:], bint), so we put the @@ -343,6 +352,10 @@ BOUNDARY_MODES['ignore'] = 4 BOUNDARY_MODES['ignore_threshold'] = 5 BOUNDARY_MODES['nearest'] = 6 +BAD_VAL_MODES = {} +BAD_VAL_MODES['strict'] = 1 +BAD_VAL_MODES['constant'] = 2 +BAD_VAL_MODES['ignore'] = 3 @cython.boundscheck(False) @cython.wraparound(False) @@ -355,6 +368,7 @@ def map_coordinates(double[:,:,:] source, double[:,:,:] target, Ci, int max_samp str kernel='gaussian', double kernel_width=1.3, double sample_region_width=4, str boundary_mode="strict", double boundary_fill_value=0, double boundary_ignore_threshold=0.5, + str bad_val_mode="strict", double bad_fill_value=0, ): # n.b. the source and target arrays are expected to contain three # dimensions---the last two are the image dimensions, while the first @@ -375,6 +389,13 @@ def map_coordinates(double[:,:,:] source, double[:,:,:] target, Ci, int max_samp raise ValueError( f"boundary_mode '{boundary_mode}' not recognized") from None + cdef int bad_val_flag + try: + bad_val_flag = BAD_VAL_MODES[bad_val_mode.lower()] + except KeyError: + raise ValueError( + f"bad_val_mode '{bad_val_mode}' not recognized") from None + cdef np.ndarray[np.float64_t, ndim=3] pixel_target cdef int delta if center_jacobian: @@ -477,7 +498,7 @@ def map_coordinates(double[:,:,:] source, double[:,:,:] target, Ci, int max_samp cdef double[:] transformed = np.zeros((2,)) cdef double[:] current_pixel_source = np.zeros((2,)) cdef double[:] current_offset = np.zeros((2,)) - cdef double weight_sum + cdef double[:] weight_sum = np.empty(source.shape[0]) cdef double ignored_weight_sum cdef double weight cdef double[:] value = np.empty(source.shape[0]) @@ -488,7 +509,7 @@ def map_coordinates(double[:,:,:] source, double[:,:,:] target, Ci, int max_samp cdef double top, bottom, left, right cdef double determinant cdef bint has_sampled_this_row - cdef bint is_good_sample + cdef bint sample_in_bounds with nogil: # Iterate through each pixel in the output image. for yi in range(target.shape[1]): @@ -572,12 +593,19 @@ def map_coordinates(double[:,:,:] source, double[:,:,:] target, Ci, int max_samp if singularities_nan: target[:,yi,xi] = nan else: - is_good_sample = sample_array( + sample_in_bounds = sample_array( source, value, current_pixel_source[0], current_pixel_source[1], x_cyclic, y_cyclic, out_of_range_nearest=boundary_flag == 6) - if is_good_sample: - target[:,yi,xi] = value + if sample_in_bounds: + for i in range(target.shape[0]): + if bad_val_flag != 1 and (isnan(value[i]) or isinf(value[i])): + if bad_val_flag == 2: + target[i,yi,xi] = bad_fill_value + else: + target[i,yi,xi] = nan + else: + target[i,yi,xi] = value[i] elif boundary_flag == 2 or boundary_flag == 3: target[:,yi,xi] = boundary_fill_value else: @@ -657,7 +685,7 @@ def map_coordinates(double[:,:,:] source, double[:,:,:] target, Ci, int max_samp top = bottom target[:,yi,xi] = 0 - weight_sum = 0 + weight_sum[:] = 0 ignored_weight_sum = 0 # Iterate through that bounding box in the input image. @@ -704,35 +732,41 @@ def map_coordinates(double[:,:,:] source, double[:,:,:] target, Ci, int max_samp continue has_sampled_this_row = True - is_good_sample = sample_array( + sample_in_bounds = sample_array( source, value, current_pixel_source[0], current_pixel_source[1], x_cyclic, y_cyclic, out_of_range_nearest=(boundary_flag == 6)) if ((boundary_flag == 2 or boundary_flag == 3) - and not is_good_sample): + and not sample_in_bounds): value[:] = boundary_fill_value - is_good_sample = True + sample_in_bounds = True - if is_good_sample: + if sample_in_bounds: for i in range(target.shape[0]): + if bad_val_flag != 1 and (isnan(value[i]) or isinf(value[i])): + if bad_val_flag == 2: + value[i] = bad_fill_value + else: + # bad_val_flag is 3: 'ignore' + continue target[i,yi,xi] += weight * value[i] - weight_sum += weight + weight_sum[i] += weight else: if boundary_flag == 5: ignored_weight_sum += weight - if (boundary_flag == 5 and - ignored_weight_sum / (ignored_weight_sum + weight_sum) - > boundary_ignore_threshold): - target[:,yi,xi] = nan - else: - if conserve_flux: - determinant = fabs(det2x2(Ji)) + if boundary_flag == 5: for i in range(target.shape[0]): - target[i,yi,xi] /= weight_sum - if conserve_flux: - target[i,yi,xi] *= determinant + if (ignored_weight_sum / (ignored_weight_sum + weight_sum[i]) + > boundary_ignore_threshold): + target[i,yi,xi] = nan + if conserve_flux: + determinant = fabs(det2x2(Ji)) + for i in range(target.shape[0]): + target[i,yi,xi] /= weight_sum[i] + if conserve_flux: + target[i,yi,xi] *= determinant if progress: with gil: sys.stdout.write("\r%d/%d done" % (yi+1, target.shape[1])) diff --git a/reproject/adaptive/high_level.py b/reproject/adaptive/high_level.py index 85d18b9a2..347a9413f 100644 --- a/reproject/adaptive/high_level.py +++ b/reproject/adaptive/high_level.py @@ -30,6 +30,8 @@ def reproject_adaptive( boundary_ignore_threshold=0.5, x_cyclic=False, y_cyclic=False, + bad_val_mode="strict", + bad_fill_value=0, ): """ Reproject a 2D array from one WCS to another using the DeForest (2004) @@ -169,6 +171,20 @@ def reproject_adaptive( Indicates that the x or y axis of the input image should be treated as cyclic or periodic. Overrides the boundary mode for that axis, so that out-of-bounds samples wrap to the other side of the image. + bad_val_mode : str + How to handle values of ``nan`` and ``inf`` in the input data. The + default is ``strct``. Allowed values are: + + * ``strict`` --- Values of ``nan`` or ``inf`` in the input data are + propagated to every output value which samples them. + * ``ignore`` --- When a sampled input value is ``nan`` or ``inf``, + that input pixel is ignored (affected neither the accumulated sum + of weighted samples nor the accumulated sum of weights). + * ``constant`` --- Input values of ``nan`` and ``inf`` are replaced + with a constant value, set via the ``bad_fill_value`` argument. + + bad_fill_value : double + The constant value used by the ``constant`` bad-value mode. Returns ------- @@ -211,6 +227,8 @@ def reproject_adaptive( boundary_ignore_threshold=boundary_ignore_threshold, x_cyclic=x_cyclic, y_cyclic=y_cyclic, + bad_val_mode=bad_val_mode, + bad_fill_value=bad_fill_value, ), return_type=return_type, ) diff --git a/reproject/adaptive/tests/test_core.py b/reproject/adaptive/tests/test_core.py index e4f578cea..2ea9bd4ed 100644 --- a/reproject/adaptive/tests/test_core.py +++ b/reproject/adaptive/tests/test_core.py @@ -679,6 +679,90 @@ def test_boundary_modes(x_cyclic, y_cyclic, rotated): assert not np.any(np.isnan(data_out)) +@pytest.mark.parametrize("bad_val", (np.nan, np.inf)) +def test_bad_val_modes(bad_val): + bad_val_checker = np.isnan if np.isnan(bad_val) else np.isinf + data_in = np.ones((30, 30)) + data_in[15, 15] = bad_val + + wcs_in = WCS(naxis=2) + wcs_in.wcs.crpix = [15.5, 15.5] + wcs_in.wcs.crval = [0, 0] + wcs_in.wcs.cdelt = [1, 1] + + # Our reprojection will be a no-op, so it's easy to reason about what + # should happen + wcs_out = wcs_in.deepcopy() + + data_out = reproject_adaptive( + (data_in, wcs_in), + wcs_out, + shape_out=(30, 30), + return_footprint=False, + boundary_mode="ignore", + sample_region_width=3, + bad_val_mode="strict", + ) + + # With a sample_region_width of 3, we expect a 3x3 box of nans centered on + # the input-image nan. + assert np.all(bad_val_checker(data_out[14:17, 14:17])) + # And they should be the only nans + assert np.sum(bad_val_checker(data_out)) == 9 + + data_out = reproject_adaptive( + (data_in, wcs_in), + wcs_out, + shape_out=(30, 30), + return_footprint=False, + boundary_mode="ignore", + sample_region_width=3, + bad_val_mode="constant", + bad_fill_value=10, + ) + + # Now, where there were nans, we should get values > 1, since 10 is our + # fill value. + assert np.all(data_out[14:17, 14:17] > 1) + data_out[14:17, 14:17] = 1 + np.testing.assert_equal(data_out, 1) + + data_out = reproject_adaptive( + (data_in, wcs_in), + wcs_out, + shape_out=(30, 30), + return_footprint=False, + boundary_mode="ignore", + sample_region_width=3, + bad_val_mode="ignore", + ) + + # Now the nan should be ignored and we should only get 1s coming out + np.testing.assert_equal(data_out, 1) + + +def test_invald_bad_val_mode(): + data_in = np.ones((30, 30)) + + wcs_in = WCS(naxis=2) + wcs_in.wcs.crpix = [15.5, 15.5] + wcs_in.wcs.crval = [0, 0] + wcs_in.wcs.cdelt = [1, 1] + + wcs_out = wcs_in.deepcopy() + + with pytest.raises(ValueError, match="bad_val_mode 'invalid_mode' not recognized"): + reproject_adaptive( + (data_in, wcs_in), + wcs_out, + shape_out=(30, 30), + return_footprint=False, + boundary_mode="ignore", + sample_region_width=3, + bad_val_mode="invalid_mode", + ) + + def prepare_test_data(file_format): pytest.importorskip("sunpy", minversion="2.1.0") from sunpy.coordinates.ephemeris import get_body_heliographic_stonyhurst @@ -757,7 +841,11 @@ def test_reproject_adaptive_uncentered_jacobian(): def _setup_for_broadcast_test( - conserve_flux=False, boundary_mode="strict", kernel="gaussian", center_jacobian=False + conserve_flux=False, + boundary_mode="strict", + kernel="gaussian", + center_jacobian=False, + bad_val_mode="strict", ): with fits.open(get_pkg_data_filename("data/galactic_2d.fits", package="reproject.tests")) as pf: hdu_in = pf[0] @@ -772,6 +860,10 @@ def _setup_for_broadcast_test( image_stack = np.stack((data, data.T, data[::-1], data[:, ::-1])) + # Ensure we exercise the bad-value handling modes + image_stack[1, 30, 30] = np.nan + image_stack[2, 20, 40] = np.inf + # Build the reference array through un-broadcast reprojections array_ref = np.empty_like(image_stack) footprint_ref = np.empty_like(image_stack) @@ -783,6 +875,7 @@ def _setup_for_broadcast_test( boundary_mode=boundary_mode, kernel=kernel, center_jacobian=center_jacobian, + bad_val_mode=bad_val_mode, ) array_ref[i] = array_out footprint_ref[i] = footprint_out @@ -833,13 +926,18 @@ def test_broadcast_reprojection(input_extra_dims, output_shape, input_as_wcs, ou def _test_broadcast_reprojection_algo_specific_options( - conserve_flux=False, boundary_mode="strict", kernel="gaussian", center_jacobian=False + conserve_flux=False, + boundary_mode="strict", + kernel="gaussian", + center_jacobian=False, + bad_val_mode="strict", ): image_stack, array_ref, footprint_ref, header_in, header_out = _setup_for_broadcast_test( conserve_flux=conserve_flux, boundary_mode=boundary_mode, kernel=kernel, center_jacobian=center_jacobian, + bad_val_mode=bad_val_mode, ) array_broadcast, footprint_broadcast = reproject_adaptive( @@ -850,6 +948,7 @@ def _test_broadcast_reprojection_algo_specific_options( boundary_mode=boundary_mode, kernel=kernel, center_jacobian=center_jacobian, + bad_val_mode=bad_val_mode, ) np.testing.assert_array_equal(footprint_broadcast, footprint_ref) @@ -879,6 +978,11 @@ def test_broadcast_reprojection_center_jacobian(center_jacobian): _test_broadcast_reprojection_algo_specific_options(center_jacobian=center_jacobian) +@pytest.mark.parametrize("bad_val_mode", ("strict", "ignore", "constant")) +def test_broadcast_reprojection_bad_val_mode(bad_val_mode): + _test_broadcast_reprojection_algo_specific_options(bad_val_mode=bad_val_mode) + + def test_adaptive_input_output_types( valid_celestial_input_data, valid_celestial_output_projections ): From a4312de81b0429a8363486f0913a31373f824cd5 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Thu, 7 Sep 2023 10:53:42 +0100 Subject: [PATCH 167/366] Rename bad_val_mode to bad_value_mode --- docs/celestial.rst | 2 +- reproject/adaptive/core.py | 6 +++--- reproject/adaptive/deforest.pyx | 14 ++++++------- reproject/adaptive/high_level.py | 6 +++--- reproject/adaptive/tests/test_core.py | 30 +++++++++++++-------------- 5 files changed, 29 insertions(+), 29 deletions(-) diff --git a/docs/celestial.rst b/docs/celestial.rst index 035538515..20c6f6528 100644 --- a/docs/celestial.rst +++ b/docs/celestial.rst @@ -288,7 +288,7 @@ will wrap around to the opposite side of the image, ignoring the ``boundary_mode`` for that axis. This implementation includes several options for handling ``nan`` and ``inf`` -values in the input data, set via the ``bad_val_mode`` argument: +values in the input data, set via the ``bad_value_mode`` argument: * ``strict`` --- Values of ``nan`` or ``inf`` in the input data are propagated to every output value which samples them. diff --git a/reproject/adaptive/core.py b/reproject/adaptive/core.py index b74d18ceb..d09f2257d 100644 --- a/reproject/adaptive/core.py +++ b/reproject/adaptive/core.py @@ -45,7 +45,7 @@ def _reproject_adaptive_2d( boundary_ignore_threshold=0.5, x_cyclic=False, y_cyclic=False, - bad_val_mode="strict", + bad_value_mode="strict", bad_fill_value=0, ): """ @@ -89,7 +89,7 @@ def _reproject_adaptive_2d( Threshold for 'ignore_threshold' boundary mode, ranging from 0 to 1. x_cyclic, y_cyclic : bool Marks in input-image axis as cyclic. - bad_val_mode : str + bad_value_mode : str NaN and inf handling mode bad_fill_value : float Fill value for 'constant' bad value mode @@ -173,7 +173,7 @@ def _reproject_adaptive_2d( boundary_ignore_threshold=boundary_ignore_threshold, x_cyclic=x_cyclic, y_cyclic=y_cyclic, - bad_val_mode=bad_val_mode, + bad_value_mode=bad_value_mode, bad_fill_value=bad_fill_value, ) diff --git a/reproject/adaptive/deforest.pyx b/reproject/adaptive/deforest.pyx index 60f3b2ef6..4af8c56d7 100644 --- a/reproject/adaptive/deforest.pyx +++ b/reproject/adaptive/deforest.pyx @@ -352,10 +352,10 @@ BOUNDARY_MODES['ignore'] = 4 BOUNDARY_MODES['ignore_threshold'] = 5 BOUNDARY_MODES['nearest'] = 6 -BAD_VAL_MODES = {} -BAD_VAL_MODES['strict'] = 1 -BAD_VAL_MODES['constant'] = 2 -BAD_VAL_MODES['ignore'] = 3 +BAD_VALUE_MODES = {} +BAD_VALUE_MODES['strict'] = 1 +BAD_VALUE_MODES['constant'] = 2 +BAD_VALUE_MODES['ignore'] = 3 @cython.boundscheck(False) @cython.wraparound(False) @@ -368,7 +368,7 @@ def map_coordinates(double[:,:,:] source, double[:,:,:] target, Ci, int max_samp str kernel='gaussian', double kernel_width=1.3, double sample_region_width=4, str boundary_mode="strict", double boundary_fill_value=0, double boundary_ignore_threshold=0.5, - str bad_val_mode="strict", double bad_fill_value=0, + str bad_value_mode="strict", double bad_fill_value=0, ): # n.b. the source and target arrays are expected to contain three # dimensions---the last two are the image dimensions, while the first @@ -391,10 +391,10 @@ def map_coordinates(double[:,:,:] source, double[:,:,:] target, Ci, int max_samp cdef int bad_val_flag try: - bad_val_flag = BAD_VAL_MODES[bad_val_mode.lower()] + bad_val_flag = BAD_VALUE_MODES[bad_value_mode.lower()] except KeyError: raise ValueError( - f"bad_val_mode '{bad_val_mode}' not recognized") from None + f"bad_value_mode '{bad_value_mode}' not recognized") from None cdef np.ndarray[np.float64_t, ndim=3] pixel_target cdef int delta diff --git a/reproject/adaptive/high_level.py b/reproject/adaptive/high_level.py index 347a9413f..fe25c4784 100644 --- a/reproject/adaptive/high_level.py +++ b/reproject/adaptive/high_level.py @@ -30,7 +30,7 @@ def reproject_adaptive( boundary_ignore_threshold=0.5, x_cyclic=False, y_cyclic=False, - bad_val_mode="strict", + bad_value_mode="strict", bad_fill_value=0, ): """ @@ -171,7 +171,7 @@ def reproject_adaptive( Indicates that the x or y axis of the input image should be treated as cyclic or periodic. Overrides the boundary mode for that axis, so that out-of-bounds samples wrap to the other side of the image. - bad_val_mode : str + bad_value_mode : str How to handle values of ``nan`` and ``inf`` in the input data. The default is ``strct``. Allowed values are: @@ -227,7 +227,7 @@ def reproject_adaptive( boundary_ignore_threshold=boundary_ignore_threshold, x_cyclic=x_cyclic, y_cyclic=y_cyclic, - bad_val_mode=bad_val_mode, + bad_value_mode=bad_value_mode, bad_fill_value=bad_fill_value, ), return_type=return_type, diff --git a/reproject/adaptive/tests/test_core.py b/reproject/adaptive/tests/test_core.py index 2ea9bd4ed..9eaa56884 100644 --- a/reproject/adaptive/tests/test_core.py +++ b/reproject/adaptive/tests/test_core.py @@ -680,7 +680,7 @@ def test_boundary_modes(x_cyclic, y_cyclic, rotated): @pytest.mark.parametrize("bad_val", (np.nan, np.inf)) -def test_bad_val_modes(bad_val): +def test_bad_value_modes(bad_val): bad_val_checker = np.isnan if np.isnan(bad_val) else np.isinf data_in = np.ones((30, 30)) data_in[15, 15] = bad_val @@ -701,7 +701,7 @@ def test_bad_val_modes(bad_val): return_footprint=False, boundary_mode="ignore", sample_region_width=3, - bad_val_mode="strict", + bad_value_mode="strict", ) # With a sample_region_width of 3, we expect a 3x3 box of nans centered on @@ -717,7 +717,7 @@ def test_bad_val_modes(bad_val): return_footprint=False, boundary_mode="ignore", sample_region_width=3, - bad_val_mode="constant", + bad_value_mode="constant", bad_fill_value=10, ) @@ -734,14 +734,14 @@ def test_bad_val_modes(bad_val): return_footprint=False, boundary_mode="ignore", sample_region_width=3, - bad_val_mode="ignore", + bad_value_mode="ignore", ) # Now the nan should be ignored and we should only get 1s coming out np.testing.assert_equal(data_out, 1) -def test_invald_bad_val_mode(): +def test_invald_bad_value_mode(): data_in = np.ones((30, 30)) wcs_in = WCS(naxis=2) @@ -751,7 +751,7 @@ def test_invald_bad_val_mode(): wcs_out = wcs_in.deepcopy() - with pytest.raises(ValueError, match="bad_val_mode 'invalid_mode' not recognized"): + with pytest.raises(ValueError, match="bad_value_mode 'invalid_mode' not recognized"): reproject_adaptive( (data_in, wcs_in), wcs_out, @@ -759,7 +759,7 @@ def test_invald_bad_val_mode(): return_footprint=False, boundary_mode="ignore", sample_region_width=3, - bad_val_mode="invalid_mode", + bad_value_mode="invalid_mode", ) @@ -845,7 +845,7 @@ def _setup_for_broadcast_test( boundary_mode="strict", kernel="gaussian", center_jacobian=False, - bad_val_mode="strict", + bad_value_mode="strict", ): with fits.open(get_pkg_data_filename("data/galactic_2d.fits", package="reproject.tests")) as pf: hdu_in = pf[0] @@ -875,7 +875,7 @@ def _setup_for_broadcast_test( boundary_mode=boundary_mode, kernel=kernel, center_jacobian=center_jacobian, - bad_val_mode=bad_val_mode, + bad_value_mode=bad_value_mode, ) array_ref[i] = array_out footprint_ref[i] = footprint_out @@ -930,14 +930,14 @@ def _test_broadcast_reprojection_algo_specific_options( boundary_mode="strict", kernel="gaussian", center_jacobian=False, - bad_val_mode="strict", + bad_value_mode="strict", ): image_stack, array_ref, footprint_ref, header_in, header_out = _setup_for_broadcast_test( conserve_flux=conserve_flux, boundary_mode=boundary_mode, kernel=kernel, center_jacobian=center_jacobian, - bad_val_mode=bad_val_mode, + bad_value_mode=bad_value_mode, ) array_broadcast, footprint_broadcast = reproject_adaptive( @@ -948,7 +948,7 @@ def _test_broadcast_reprojection_algo_specific_options( boundary_mode=boundary_mode, kernel=kernel, center_jacobian=center_jacobian, - bad_val_mode=bad_val_mode, + bad_value_mode=bad_value_mode, ) np.testing.assert_array_equal(footprint_broadcast, footprint_ref) @@ -978,9 +978,9 @@ def test_broadcast_reprojection_center_jacobian(center_jacobian): _test_broadcast_reprojection_algo_specific_options(center_jacobian=center_jacobian) -@pytest.mark.parametrize("bad_val_mode", ("strict", "ignore", "constant")) -def test_broadcast_reprojection_bad_val_mode(bad_val_mode): - _test_broadcast_reprojection_algo_specific_options(bad_val_mode=bad_val_mode) +@pytest.mark.parametrize("bad_value_mode", ("strict", "ignore", "constant")) +def test_broadcast_reprojection_bad_value_mode(bad_value_mode): + _test_broadcast_reprojection_algo_specific_options(bad_value_mode=bad_value_mode) def test_adaptive_input_output_types( From c4730d2ac47a1ab3f1be2ffa6729a73ff431f60a Mon Sep 17 00:00:00 2001 From: Fabian Jankowski Date: Thu, 7 Sep 2023 10:58:22 +0100 Subject: [PATCH 168/366] Implement new combine function to compute the two dimensional maximum map over all co-added arrays. This is useful for PSF calculations, for instance. --- reproject/mosaicking/coadd.py | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/reproject/mosaicking/coadd.py b/reproject/mosaicking/coadd.py index 3f2bdc9a9..b54fe5bc8 100644 --- a/reproject/mosaicking/coadd.py +++ b/reproject/mosaicking/coadd.py @@ -73,7 +73,7 @@ def reproject_and_coadd( `~astropy.io.fits.HDUList` instance, specifies the HDU to use. reproject_function : callable The function to use for the reprojection - combine_function : { 'mean', 'sum', 'median', 'first', 'last' } + combine_function : { 'mean', 'sum', 'median', 'first', 'last', 'max' } The type of function to use for combining the values into the final image. For 'first' and 'last', respectively, the reprojected images are simply overlaid on top of each other. With respect to the order of the @@ -97,8 +97,8 @@ def reproject_and_coadd( # Validate inputs - if combine_function not in ("mean", "sum", "median", "first", "last"): - raise ValueError("combine_function should be one of mean/sum/median/first/last") + if combine_function not in ("mean", "sum", "median", "first", "last", "max"): + raise ValueError("combine_function should be one of mean/sum/median/first/last/max") if reproject_function is None: raise ValueError( @@ -253,6 +253,16 @@ def reproject_and_coadd( final_array[array.view_in_original_array] = np.where( array.footprint > 0, array.array, final_array[array.view_in_original_array] ) + elif combine_function == "max": + for array in arrays: + array.array[array.footprint == 0] = 0 + + old_vals = final_array[array.view_in_original_array] + new_vals = array.array * array.footprint + + final_array[array.view_in_original_array] = np.maximum(old_vals, new_vals) + final_footprint[array.view_in_original_array] += array.footprint + elif combine_function == "median": # Here we need to operate in chunks since we could otherwise run # into memory issues From 8e3c6e743427124fe74c2175736b1fb6b290744e Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Thu, 7 Sep 2023 11:41:10 +0100 Subject: [PATCH 169/366] Added combine_function=="min", added test for "min" and "max", and fixed implementation --- reproject/mosaicking/coadd.py | 48 +++++++++++++----------- reproject/mosaicking/tests/test_coadd.py | 21 ++++++++--- 2 files changed, 41 insertions(+), 28 deletions(-) diff --git a/reproject/mosaicking/coadd.py b/reproject/mosaicking/coadd.py index b54fe5bc8..8df9809b5 100644 --- a/reproject/mosaicking/coadd.py +++ b/reproject/mosaicking/coadd.py @@ -73,7 +73,7 @@ def reproject_and_coadd( `~astropy.io.fits.HDUList` instance, specifies the HDU to use. reproject_function : callable The function to use for the reprojection - combine_function : { 'mean', 'sum', 'median', 'first', 'last', 'max' } + combine_function : { 'mean', 'sum', 'median', 'first', 'last', 'min', 'max' } The type of function to use for combining the values into the final image. For 'first' and 'last', respectively, the reprojected images are simply overlaid on top of each other. With respect to the order of the @@ -97,8 +97,8 @@ def reproject_and_coadd( # Validate inputs - if combine_function not in ("mean", "sum", "median", "first", "last", "max"): - raise ValueError("combine_function should be one of mean/sum/median/first/last/max") + if combine_function not in ("mean", "sum", "median", "first", "last", "min", "max"): + raise ValueError("combine_function should be one of mean/sum/median/first/last/min/max") if reproject_function is None: raise ValueError( @@ -223,6 +223,11 @@ def reproject_and_coadd( final_array = np.zeros(shape_out) final_footprint = np.zeros(shape_out) + if combine_function == "min": + final_array[...] = np.inf + elif combine_function == "max": + final_array[...] = -np.inf + if combine_function in ("mean", "sum"): for array in arrays: # By default, values outside of the footprint are set to NaN @@ -236,32 +241,28 @@ def reproject_and_coadd( if combine_function == "mean": with np.errstate(invalid="ignore"): final_array /= final_footprint - elif combine_function == "first": + + elif combine_function in ("first", "last", "min", "max"): for array in arrays: - mask = final_footprint[array.view_in_original_array] == 0 + if combine_function == "first": + mask = final_footprint[array.view_in_original_array] == 0 + elif combine_function == "last": + mask = array.footprint > 0 + elif combine_function == "min": + mask = (array.footprint > 0) & ( + array.array < final_array[array.view_in_original_array] + ) + elif combine_function == "max": + mask = (array.footprint > 0) & ( + array.array > final_array[array.view_in_original_array] + ) + final_footprint[array.view_in_original_array] = np.where( mask, array.footprint, final_footprint[array.view_in_original_array] ) final_array[array.view_in_original_array] = np.where( mask, array.array, final_array[array.view_in_original_array] ) - elif combine_function == "last": - for array in arrays: - final_footprint[array.view_in_original_array] = np.where( - array.footprint, array.footprint, final_footprint[array.view_in_original_array] - ) - final_array[array.view_in_original_array] = np.where( - array.footprint > 0, array.array, final_array[array.view_in_original_array] - ) - elif combine_function == "max": - for array in arrays: - array.array[array.footprint == 0] = 0 - - old_vals = final_array[array.view_in_original_array] - new_vals = array.array * array.footprint - - final_array[array.view_in_original_array] = np.maximum(old_vals, new_vals) - final_footprint[array.view_in_original_array] += array.footprint elif combine_function == "median": # Here we need to operate in chunks since we could otherwise run @@ -269,4 +270,7 @@ def reproject_and_coadd( raise NotImplementedError("combine_function='median' is not yet implemented") + if combine_function in ("min", "max"): + final_array[final_footprint == 0] = 0.0 + return final_array, final_footprint diff --git a/reproject/mosaicking/tests/test_coadd.py b/reproject/mosaicking/tests/test_coadd.py index e069dd900..1d668992a 100644 --- a/reproject/mosaicking/tests/test_coadd.py +++ b/reproject/mosaicking/tests/test_coadd.py @@ -108,14 +108,17 @@ def test_coadd_with_overlap(self, reproject_function): assert_allclose(array, self.array, atol=ATOL) - @pytest.mark.parametrize("combine_function", ["first", "last"]) + @pytest.mark.parametrize("combine_function", ["first", "last", "min", "max"]) def test_coadd_with_overlap_first_last(self, reproject_function, combine_function): views = self._overlapping_views input_data = self._get_tiles(views) # Make each of the overlapping tiles different for i, (array, wcs) in enumerate(input_data): - input_data[i] = (np.full_like(array, i), wcs) + # We give each tile integer values that range from 0 to 19 but we + # deliberately don't make the first one 0 and the last one 19 so + # that min/max differs from first/last. + input_data[i] = (np.full_like(array, (i + 7) % 20), wcs) array, footprint = reproject_and_coadd( input_data, @@ -127,17 +130,23 @@ def test_coadd_with_overlap_first_last(self, reproject_function, combine_functio # Test that either the correct tile sets the output value in the overlap regions test_sequence = list(enumerate(views)) + if combine_function == "last": test_sequence = test_sequence[::-1] + elif combine_function == "min": + test_sequence = test_sequence[13:] + test_sequence[:13] + elif combine_function == "max": + test_sequence = (test_sequence[13:] + test_sequence[:13])[::-1] + for i, view in test_sequence: - # Each tile in test_sequence should overwrite teh following tiles - # in the overlap regions. We'll use nans to mark pixels in the - # output array that have already been set by a preceeding tile, so + # Each tile in test_sequence should overwrite the following tiles + # in the overlap regions. We'll use NaNs to mark pixels in the + # output array that have already been set by a preceding tile, so # we'll go through, check that each tile matches the non-nan pixels # in its region, and then set that whole region to nan. output_tile = array[view] output_values = output_tile[np.isfinite(output_tile)] - assert_equal(output_values, i) + assert_equal(output_values, (i + 7) % 20) array[view] = np.nan def test_coadd_background_matching(self, reproject_function): From be66ca0db38f8e2ac1600b269fe0cd3d807215c6 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Thu, 7 Sep 2023 16:04:20 +0100 Subject: [PATCH 170/366] Skip Python 3.12 wheels --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index ecc716bac..1ba3b13d7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -11,7 +11,7 @@ build-backend = 'setuptools.build_meta' write_to = "reproject/version.py" [tool.cibuildwheel] -skip = "cp36-* pp* *-musllinux* cp310-win32" +skip = "cp36-* pp* *-musllinux* cp310-win32 cp312-*" test-skip = "*-macosx_arm64 *-manylinux_aarch64" [tool.isort] From 802cc36c3e2e76ef345c3637b4b04809baee5d9f Mon Sep 17 00:00:00 2001 From: astrofrog Date: Thu, 7 Sep 2023 16:30:37 +0000 Subject: [PATCH 171/366] Update CHANGELOG --- CHANGES.md | 100 +++++++++++++++++++++++++++++++++++------------------ 1 file changed, 67 insertions(+), 33 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index c553e4d29..7294c63bf 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,37 @@ +## v0.12.0 - 2023-09-07 + + +### What's Changed + +#### Bug Fixes + +- Fix support for NDData objects with dask .data attributes by @astrofrog in https://github.com/astropy/reproject/pull/365 +- Fix docs mosaic page rendering by @jdavies-st in https://github.com/astropy/reproject/pull/381 + +#### New Features + +- Add despike_jacobian option for adaptive resampling by @svank in https://github.com/astropy/reproject/pull/366 +- Refactor blocked/parallel reprojection by @astrofrog in https://github.com/astropy/reproject/pull/374 +- Add 'first' and 'last' moasicking modes to reproject_and_coadd by @svank in https://github.com/astropy/reproject/pull/383 +- Add modes for nan and inf handling to adaptive algo by @svank in https://github.com/astropy/reproject/pull/380 +- Added new combine function to compute the minimum and maximum by @fjankowsk in https://github.com/astropy/reproject/pull/369 + +#### Other Changes + +- TST: Update URL for Scientific Python nightlies by @pllim in https://github.com/astropy/reproject/pull/368 +- Dask support improvements by @astrofrog in https://github.com/astropy/reproject/pull/367 +- Fix --remote-data tests by @astrofrog in https://github.com/astropy/reproject/pull/375 +- Update docstring for adaptive defaults by @svank in https://github.com/astropy/reproject/pull/378 +- Use more points to find image bounds in moasics by @svank in https://github.com/astropy/reproject/pull/382 +- Skip Python 3.12 wheels by @astrofrog in https://github.com/astropy/reproject/pull/385 + +### New Contributors + +- @jdavies-st made their first contribution in https://github.com/astropy/reproject/pull/381 +- @fjankowsk made their first contribution in https://github.com/astropy/reproject/pull/369 + +**Full Changelog**: https://github.com/astropy/reproject/compare/v0.11.0...v0.12.0 + ## v0.11.0 - 2023-05-19 @@ -66,9 +100,9 @@ ## 0.9 - 2022-09-02 - Drop support for Python 3.7. -- +- - Infrastructure and packaging updates. -- +- - Made many improvements, bug fixes, and significant speed-ups for the adaptive - resampling algorithm, `reproject_adaptive`. These bug fixes may cause - changes to the reprojected images, which are typically negligible. @@ -76,13 +110,13 @@ - Gaussian filter kernel, a menu of boundary-handling modes, and a - `center_jacobian` flag to trade speed for accuracy with rapidly-varying - transformations. -- +- - Added a `roundtrip_coords` argument to `reproject_adaptive` and - `reproject_interp`. By default, all coordinate transformations are run in - both directions to handle some situations where they are ambiguous. This can - be disabled by setting `roundtrip_coords=False` which may offer a - significant speed increase. -- +- ## 0.8 - 2021-08-11 @@ -91,34 +125,34 @@ - possible to solve for the optimal WCS for a set of images that couldn't fit - into memory all at once, since the actual data aren't needed for optimal WCS - determination. [#242] -- +- - Fix implementation of `hdu_weights` in `reproject_and_coadd`. [#249] -- +- ## 0.7.1 - 2020-05-29 - Fixed compatibility with Astropy 4.1. [#234] -- +- - Updated minimum requirement for SciPy. [#236] -- +- ## 0.7 - 2020-04-02 - Made C extension in overlapArea.c thread-safe by removing global - variables. [#211] -- +- - Made it possible to control whether to output debugging information - from overlapArea.c by setting DEBUG_OVERLAP_AREA=1 at build-time. [#211] -- +- - Fix compatibility with astropy v4.0.1. [#227] -- +- - Disable parallelization by default in `reproject_exact` - this can be - enabled with `parallel=True`. [#227] -- +- - Fixed a bug with `reproject_exact` with `parallel=False` and - `return_footprint=False`, which caused the footprint to be returned - anyway. [#227] -- +- - The infrastructure of the package has been updated in line with the - APE 17 roadmap (https://github.com/astropy/astropy-APEs/blob/main/APE17.rst). - The main changes are that the `python setup.py test` and @@ -127,7 +161,7 @@ - (https://tox.readthedocs.io) package and run `tox -e test` and - `tox -e build_docs`. It is also possible to run pytest and sphinx - directly. [#228] -- +- ## 0.6 - 2019-11-01 @@ -136,20 +170,20 @@ - `independent_celestial_slices=` argument to `reproject_interp` has - been deprecated since it is no longer needed, as transformations are - automatically done in the most efficient way possible. [#166] -- +- - Include a warning for high resolution images with `reproject_exact`, - since if the pixels are <0.05", precision issues can occur. [#200] -- +- - Added a new `reproject_and_coadd` function for doing mosaicking of - individual images, and added section in documentation about mosaicking. - [#186] -- +- - Added a new reproject.adaptive sub-package that implements the DeForest - (2004) algorithm for reprojection. [#52] -- +- - Fixed a bug that caused 'exact' reprojection results to have numerical - issues when doing identity transformations. [#190] -- +- ## 0.5.1 - 2019-09-01 @@ -160,33 +194,33 @@ - Improve parse_output_projection to make it so that the output projection - can be specified as a filename. [#150] -- +- - Fixed a bug that caused HEALPix maps in RING order to not be correctly - interpreted. [#163] -- +- - Make it possible to specify the output array for reprojection using the - `output_array=` keyword argument. [#115] -- +- ## 0.4 - 2018-01-29 - Refactored HEALPix reprojection code to use the astropy-healpix package - instead of healpy. [#139] -- +- - Added the ability to specify an output array in `reproject_interp`, which - permits the use of memory-mapped arrays and therefore provides the capability - to handle data cubes much larger than memory [#115] -- +- - Fix test 32-bit test failures. [#146] -- +- - Fix an issue with reprojecting images where there are two solutions along - the line of sight by forcing round-tripping of coordinate conversions [#129] -- +- - Explicitly define default HDU as 0 for normal reprojection and 1 for - HEALPix reprojection. [#119] -- +- - Added a function to find the optimal WCS for a set of images. [#136, #137] -- +- ## 0.3.2 - 2017-10-22 @@ -197,22 +231,22 @@ ## 0.3.1 - 2016-07-07 - Include missing license file in tarball. -- +- - Updated documentation to remove warnings about early versions. -- +- ## 0.3 - 2016-07-06 - Allow users to pass a `field=` option to `reproject_from_healpix` - to access different fields in a HEALPIX file. [#86] -- +- - Significant improvements to performance when the input data is a large - memory-mapped array. [#105] -- +- - Significant refactoring of interpolating reprojection to improve support for - n-dimensional arrays, optionally including two celestial axes (in which - case the coordinate transformation is taken into account). [#96, #102] -- +- ## 0.2 - 2015-10-29 From 90639ed2351368a8c9a5d19c0a653bec60f4c933 Mon Sep 17 00:00:00 2001 From: Ole Streicher Date: Fri, 8 Sep 2023 09:11:46 +0200 Subject: [PATCH 172/366] Fix TestReprojectAndCoAdd failure on i386 On i386, this test resulted in a failure because of relative differences ~2e-16. As floats are compared, minimal floating point differences my appear. This patch replaces the strict equal with an allclose test. --- reproject/mosaicking/tests/test_coadd.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/reproject/mosaicking/tests/test_coadd.py b/reproject/mosaicking/tests/test_coadd.py index 1d668992a..ad7373e52 100644 --- a/reproject/mosaicking/tests/test_coadd.py +++ b/reproject/mosaicking/tests/test_coadd.py @@ -8,7 +8,7 @@ from astropy.io import fits from astropy.io.fits import Header from astropy.wcs import WCS -from numpy.testing import assert_allclose, assert_equal +from numpy.testing import assert_allclose from reproject import reproject_exact, reproject_interp from reproject.mosaicking.coadd import reproject_and_coadd @@ -146,7 +146,7 @@ def test_coadd_with_overlap_first_last(self, reproject_function, combine_functio # in its region, and then set that whole region to nan. output_tile = array[view] output_values = output_tile[np.isfinite(output_tile)] - assert_equal(output_values, (i + 7) % 20) + assert_allclose(output_values, (i + 7) % 20) array[view] = np.nan def test_coadd_background_matching(self, reproject_function): From b8f8e4e43787bd40dc4c3eea0d0cd2f81542734a Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 8 Sep 2023 13:36:03 +0000 Subject: [PATCH 173/366] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- CHANGES.md | 66 +++++++++++++++++++++++++++--------------------------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 7294c63bf..efa181e0f 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -100,9 +100,9 @@ ## 0.9 - 2022-09-02 - Drop support for Python 3.7. -- +- - Infrastructure and packaging updates. -- +- - Made many improvements, bug fixes, and significant speed-ups for the adaptive - resampling algorithm, `reproject_adaptive`. These bug fixes may cause - changes to the reprojected images, which are typically negligible. @@ -110,13 +110,13 @@ - Gaussian filter kernel, a menu of boundary-handling modes, and a - `center_jacobian` flag to trade speed for accuracy with rapidly-varying - transformations. -- +- - Added a `roundtrip_coords` argument to `reproject_adaptive` and - `reproject_interp`. By default, all coordinate transformations are run in - both directions to handle some situations where they are ambiguous. This can - be disabled by setting `roundtrip_coords=False` which may offer a - significant speed increase. -- +- ## 0.8 - 2021-08-11 @@ -125,34 +125,34 @@ - possible to solve for the optimal WCS for a set of images that couldn't fit - into memory all at once, since the actual data aren't needed for optimal WCS - determination. [#242] -- +- - Fix implementation of `hdu_weights` in `reproject_and_coadd`. [#249] -- +- ## 0.7.1 - 2020-05-29 - Fixed compatibility with Astropy 4.1. [#234] -- +- - Updated minimum requirement for SciPy. [#236] -- +- ## 0.7 - 2020-04-02 - Made C extension in overlapArea.c thread-safe by removing global - variables. [#211] -- +- - Made it possible to control whether to output debugging information - from overlapArea.c by setting DEBUG_OVERLAP_AREA=1 at build-time. [#211] -- +- - Fix compatibility with astropy v4.0.1. [#227] -- +- - Disable parallelization by default in `reproject_exact` - this can be - enabled with `parallel=True`. [#227] -- +- - Fixed a bug with `reproject_exact` with `parallel=False` and - `return_footprint=False`, which caused the footprint to be returned - anyway. [#227] -- +- - The infrastructure of the package has been updated in line with the - APE 17 roadmap (https://github.com/astropy/astropy-APEs/blob/main/APE17.rst). - The main changes are that the `python setup.py test` and @@ -161,7 +161,7 @@ - (https://tox.readthedocs.io) package and run `tox -e test` and - `tox -e build_docs`. It is also possible to run pytest and sphinx - directly. [#228] -- +- ## 0.6 - 2019-11-01 @@ -170,20 +170,20 @@ - `independent_celestial_slices=` argument to `reproject_interp` has - been deprecated since it is no longer needed, as transformations are - automatically done in the most efficient way possible. [#166] -- +- - Include a warning for high resolution images with `reproject_exact`, - since if the pixels are <0.05", precision issues can occur. [#200] -- +- - Added a new `reproject_and_coadd` function for doing mosaicking of - individual images, and added section in documentation about mosaicking. - [#186] -- +- - Added a new reproject.adaptive sub-package that implements the DeForest - (2004) algorithm for reprojection. [#52] -- +- - Fixed a bug that caused 'exact' reprojection results to have numerical - issues when doing identity transformations. [#190] -- +- ## 0.5.1 - 2019-09-01 @@ -194,33 +194,33 @@ - Improve parse_output_projection to make it so that the output projection - can be specified as a filename. [#150] -- +- - Fixed a bug that caused HEALPix maps in RING order to not be correctly - interpreted. [#163] -- +- - Make it possible to specify the output array for reprojection using the - `output_array=` keyword argument. [#115] -- +- ## 0.4 - 2018-01-29 - Refactored HEALPix reprojection code to use the astropy-healpix package - instead of healpy. [#139] -- +- - Added the ability to specify an output array in `reproject_interp`, which - permits the use of memory-mapped arrays and therefore provides the capability - to handle data cubes much larger than memory [#115] -- +- - Fix test 32-bit test failures. [#146] -- +- - Fix an issue with reprojecting images where there are two solutions along - the line of sight by forcing round-tripping of coordinate conversions [#129] -- +- - Explicitly define default HDU as 0 for normal reprojection and 1 for - HEALPix reprojection. [#119] -- +- - Added a function to find the optimal WCS for a set of images. [#136, #137] -- +- ## 0.3.2 - 2017-10-22 @@ -231,22 +231,22 @@ ## 0.3.1 - 2016-07-07 - Include missing license file in tarball. -- +- - Updated documentation to remove warnings about early versions. -- +- ## 0.3 - 2016-07-06 - Allow users to pass a `field=` option to `reproject_from_healpix` - to access different fields in a HEALPIX file. [#86] -- +- - Significant improvements to performance when the input data is a large - memory-mapped array. [#105] -- +- - Significant refactoring of interpolating reprojection to improve support for - n-dimensional arrays, optionally including two celestial axes (in which - case the coordinate transformation is taken into account). [#96, #102] -- +- ## 0.2 - 2015-10-29 From aff1ab44ee46766fdc2a01080682e1d180b4c7a8 Mon Sep 17 00:00:00 2001 From: "Adam Ginsburg (keflavich)" Date: Mon, 13 Mar 2023 17:13:14 -0400 Subject: [PATCH 174/366] Add ability to specify output array in coadd --- reproject/mosaicking/coadd.py | 57 ++++++++++++++++++++++++----------- 1 file changed, 39 insertions(+), 18 deletions(-) diff --git a/reproject/mosaicking/coadd.py b/reproject/mosaicking/coadd.py index cece83407..5da01b11a 100644 --- a/reproject/mosaicking/coadd.py +++ b/reproject/mosaicking/coadd.py @@ -22,6 +22,8 @@ def reproject_and_coadd( combine_function="mean", match_background=False, background_reference=None, + output_array=None, + output_footprint=None, **kwargs, ): """ @@ -85,6 +87,14 @@ def reproject_and_coadd( If `None`, the background matching will make it so that the average of the corrections for all images is zero. If an integer, this specifies the index of the image to use as a reference. + output_array : array or None + The final output array. Specify this if you already have an + appropriately-shaped array to store the data in. Must match shape + specified with `shape_out` or derived from the output projection. + output_footprint : array or None + The final output footprint array. Specify this if you already have an + appropriately-shaped array to store the data in. Must match shape + specified with `shape_out` or derived from the output projection. kwargs Keyword arguments to be passed to the reprojection function. """ @@ -109,6 +119,17 @@ def reproject_and_coadd( wcs_out, shape_out = parse_output_projection(output_projection, shape_out=shape_out) + if output_array is not None and output_array.shape != shape_out: + raise ValueError( + "If you specify an output array, it must have a shape matching " + f"the output shape {shape_out}" + ) + if output_footprint is not None and output_footprint.shape != shape_out: + raise ValueError( + "If you specify an output footprint array, it must have a shape matching " + f"the output shape {shape_out}" + ) + # Start off by reprojecting individual images to the final projection arrays = [] @@ -223,15 +244,15 @@ def reproject_and_coadd( # At this point, the images are now ready to be co-added. - # TODO: provide control over final dtype - - final_array = np.zeros(shape_out) - final_footprint = np.zeros(shape_out) + if output_array is None: + output_array = np.zeros(shape_out) + if output_footprint is None: + output_footprint = np.zeros(shape_out) if combine_function == "min": - final_array[...] = np.inf + output_array[...] = np.inf elif combine_function == "max": - final_array[...] = -np.inf + output_array[...] = -np.inf if combine_function in ("mean", "sum"): for array in arrays: @@ -240,33 +261,33 @@ def reproject_and_coadd( # means/sums. array.array[array.footprint == 0] = 0 - final_array[array.view_in_original_array] += array.array * array.footprint - final_footprint[array.view_in_original_array] += array.footprint + output_array[array.view_in_original_array] += array.array * array.footprint + output_footprint[array.view_in_original_array] += array.footprint if combine_function == "mean": with np.errstate(invalid="ignore"): - final_array /= final_footprint + output_array /= output_footprint elif combine_function in ("first", "last", "min", "max"): for array in arrays: if combine_function == "first": - mask = final_footprint[array.view_in_original_array] == 0 + mask = output_footprint[array.view_in_original_array] == 0 elif combine_function == "last": mask = array.footprint > 0 elif combine_function == "min": mask = (array.footprint > 0) & ( - array.array < final_array[array.view_in_original_array] + array.array < output_array[array.view_in_original_array] ) elif combine_function == "max": mask = (array.footprint > 0) & ( - array.array > final_array[array.view_in_original_array] + array.array > output_array[array.view_in_original_array] ) - final_footprint[array.view_in_original_array] = np.where( - mask, array.footprint, final_footprint[array.view_in_original_array] + output_footprint[array.view_in_original_array] = np.where( + mask, array.footprint, output_footprint[array.view_in_original_array] ) - final_array[array.view_in_original_array] = np.where( - mask, array.array, final_array[array.view_in_original_array] + output_array[array.view_in_original_array] = np.where( + mask, array.array, output_array[array.view_in_original_array] ) elif combine_function == "median": @@ -276,6 +297,6 @@ def reproject_and_coadd( raise NotImplementedError("combine_function='median' is not yet implemented") if combine_function in ("min", "max"): - final_array[final_footprint == 0] = 0.0 + output_array[output_footprint == 0] = 0.0 - return final_array, final_footprint + return output_array, output_footprint From 097327fec196b4e70376ca2a4b160f382308ca98 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Mon, 11 Sep 2023 10:18:09 +0100 Subject: [PATCH 175/366] Added unit test for output array specification in coadd --- reproject/mosaicking/coadd.py | 2 -- reproject/mosaicking/tests/test_coadd.py | 25 ++++++++++++++++++++++++ 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/reproject/mosaicking/coadd.py b/reproject/mosaicking/coadd.py index 5da01b11a..76fefaaa9 100644 --- a/reproject/mosaicking/coadd.py +++ b/reproject/mosaicking/coadd.py @@ -103,8 +103,6 @@ def reproject_and_coadd( # up memory usage. We could probably still have references to array # objects, but we'd just make sure these were memory mapped - # TODO: add support for specifying output array - # Validate inputs if combine_function not in ("mean", "sum", "median", "first", "last", "min", "max"): diff --git a/reproject/mosaicking/tests/test_coadd.py b/reproject/mosaicking/tests/test_coadd.py index ad7373e52..6e03ab6e9 100644 --- a/reproject/mosaicking/tests/test_coadd.py +++ b/reproject/mosaicking/tests/test_coadd.py @@ -108,6 +108,31 @@ def test_coadd_with_overlap(self, reproject_function): assert_allclose(array, self.array, atol=ATOL) + def test_coadd_with_outputs(self, tmp_path, reproject_function): + # Test the options to specify output array/footprint + + input_data = self._get_tiles(self._overlapping_views) + + output_array = np.memmap( + tmp_path / "array.np", mode="w+", dtype=float, shape=self.array.shape + ) + output_footprint = np.memmap( + tmp_path / "footprint.np", mode="w+", dtype=float, shape=self.array.shape + ) + + array, footprint = reproject_and_coadd( + input_data, + self.wcs, + shape_out=self.array.shape, + combine_function="mean", + reproject_function=reproject_function, + output_array=output_array, + output_footprint=output_footprint, + ) + + assert_allclose(output_array, self.array, atol=ATOL) + assert_allclose(output_footprint, footprint, atol=ATOL) + @pytest.mark.parametrize("combine_function", ["first", "last", "min", "max"]) def test_coadd_with_overlap_first_last(self, reproject_function, combine_function): views = self._overlapping_views From c72464ac19b46afbe09d7009018e8e8f9040d0cd Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Mon, 11 Sep 2023 10:54:56 +0100 Subject: [PATCH 176/366] Fixed docs error --- reproject/mosaicking/coadd.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/reproject/mosaicking/coadd.py b/reproject/mosaicking/coadd.py index 76fefaaa9..66c06ddcb 100644 --- a/reproject/mosaicking/coadd.py +++ b/reproject/mosaicking/coadd.py @@ -90,11 +90,11 @@ def reproject_and_coadd( output_array : array or None The final output array. Specify this if you already have an appropriately-shaped array to store the data in. Must match shape - specified with `shape_out` or derived from the output projection. + specified with ``shape_out`` or derived from the output projection. output_footprint : array or None The final output footprint array. Specify this if you already have an appropriately-shaped array to store the data in. Must match shape - specified with `shape_out` or derived from the output projection. + specified with ``shape_out`` or derived from the output projection. kwargs Keyword arguments to be passed to the reprojection function. """ From 63c16273af047a969fc1d99c9c2f01b7abf00c7c Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 11 Sep 2023 16:40:38 +0000 Subject: [PATCH 177/366] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/psf/black: 23.7.0 → 23.9.1](https://github.com/psf/black/compare/23.7.0...23.9.1) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index e18835272..e4f29f479 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -67,7 +67,7 @@ repos: exclude: ".*(data.*|extern.*|cextern)$" - repo: https://github.com/psf/black - rev: 23.7.0 + rev: 23.9.1 hooks: - id: black From e6e9d1f3661ddc6c8f95b4ec8ebecf27d577e096 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Tue, 12 Sep 2023 15:49:56 +0100 Subject: [PATCH 178/366] Fixed a bug that caused reprojected dask arrays to not be computable due to a temporary directory being removed --- reproject/common.py | 13 +++++++--- reproject/interpolation/tests/test_core.py | 28 ++++++++++++++++++++++ 2 files changed, 38 insertions(+), 3 deletions(-) diff --git a/reproject/common.py b/reproject/common.py index b46490c5b..9a368ffe1 100644 --- a/reproject/common.py +++ b/reproject/common.py @@ -100,7 +100,7 @@ def _reproject_dispatcher( # We set up a global temporary directory since this will be used e.g. to # store memory mapped Numpy arrays and zarr arrays. - with tempfile.TemporaryDirectory() as tmp_dir: + with tempfile.TemporaryDirectory() as local_tmp_dir: if array_out is None: array_out = np.zeros(shape_out, dtype=float) elif array_out.shape != tuple(shape_out): @@ -132,7 +132,7 @@ def _reproject_dispatcher( ) if isinstance(array_in, da.core.Array): - _, array_in = _dask_to_numpy_memmap(array_in, tmp_dir) + _, array_in = _dask_to_numpy_memmap(array_in, local_tmp_dir) try: return reproject_func( @@ -165,6 +165,13 @@ def _reproject_dispatcher( # to iterate over both. if isinstance(array_in, da.core.Array) or parallel: + # If return_type=='dask', + if return_type == "dask": + # We should use a temporary directory that will persist beyond + # the call to the reproject function. + tmp_dir = tempfile.mkdtemp() + else: + tmp_dir = local_tmp_dir array_in_or_path = as_delayed_memmap_path(array_in, tmp_dir) else: # Here we could set array_in_or_path to array_in_path if it @@ -273,7 +280,7 @@ def reproject_single_block(a, array_or_path, block_info=None): else: workers = {} - zarr_path = os.path.join(tmp_dir, f"{uuid.uuid4()}.zarr") + zarr_path = os.path.join(local_tmp_dir, f"{uuid.uuid4()}.zarr") with dask.config.set(scheduler="processes", **workers): result.to_zarr(zarr_path) diff --git a/reproject/interpolation/tests/test_core.py b/reproject/interpolation/tests/test_core.py index 7d2100e12..7209803d2 100644 --- a/reproject/interpolation/tests/test_core.py +++ b/reproject/interpolation/tests/test_core.py @@ -931,3 +931,31 @@ def test_reproject_block_size_broadcasting(): return_footprint=False, block_size=(100, 100, 100), ) + + +def test_reproject_dask_return_type(): + # Regression test for a bug that caused dask arrays to not be computable + # when using return_type='dask' when the input was a dask array. + + array_in = da.ones((350, 250, 150)) + wcs_in = WCS(naxis=2) + wcs_out = WCS(naxis=2) + + result_numpy = reproject_interp( + (array_in, wcs_in), + wcs_out, + shape_out=(300, 300), + return_type="numpy", + return_footprint=False, + ) + + result_dask = reproject_interp( + (array_in, wcs_in), + wcs_out, + shape_out=(300, 300), + block_size=(100, 100), + return_type="dask", + return_footprint=False, + ) + + assert_allclose(result_numpy, result_dask.compute(scheduler="synchronous")) From eedc7fb03dd0faead4a41b497eccc8dc88dbe9d7 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Tue, 12 Sep 2023 16:10:30 +0100 Subject: [PATCH 179/366] Fix compatibility with Cython 3.0.2 and update version in pyproject.toml --- pyproject.toml | 2 +- reproject/adaptive/deforest.pyx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 1ba3b13d7..db3d53c8c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ requires = ["setuptools", "wheel", "extension-helpers", "oldest-supported-numpy", - "cython==0.29.32"] + "cython==3.0.2"] build-backend = 'setuptools.build_meta' [tool.setuptools_scm] diff --git a/reproject/adaptive/deforest.pyx b/reproject/adaptive/deforest.pyx index 4af8c56d7..70d5f3970 100644 --- a/reproject/adaptive/deforest.pyx +++ b/reproject/adaptive/deforest.pyx @@ -211,7 +211,7 @@ cdef void calculate_jacobian(double[:, :] Ji, int center_jacobian, @cython.wraparound(False) @cython.nonecheck(False) @cython.cdivision(True) -cdef int cmp_func(const void* a, const void* b) nogil: +cdef int cmp_func(const void* a, const void* b) noexcept nogil: cdef double a_v = (a)[0] cdef double b_v = (b)[0] if a_v < b_v: From cf080638bf588fd0b722a1143935024bd76dd104 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Thu, 14 Sep 2023 10:11:21 +0100 Subject: [PATCH 180/366] Added numpydoc pre-commit hook and fix validation issues for high level reproject_* functions --- .pre-commit-config.yaml | 7 ++++ pyproject.toml | 10 +++++ reproject/adaptive/high_level.py | 44 ++++++++++++++++----- reproject/common.py | 20 ++++++---- reproject/healpix/high_level.py | 14 +++---- reproject/interpolation/high_level.py | 44 ++++++++++++--------- reproject/spherical_intersect/high_level.py | 36 ++++++++++++----- 7 files changed, 123 insertions(+), 52 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index e4f29f479..890d01778 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -71,5 +71,12 @@ repos: hooks: - id: black + - repo: https://github.com/numpy/numpydoc + rev: main + hooks: + - id: numpydoc-validation + files: ".*(high_level.*)$" + exclude: ".*(tests.*)$" + ci: autofix_prs: false diff --git a/pyproject.toml b/pyproject.toml index db3d53c8c..b52fd4487 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -30,3 +30,13 @@ length_sort_sections = ["future", "stdlib"] [tool.black] line-length = 100 target-version = ['py38'] + +[tool.numpydoc_validation] +checks = [ + "all", # report on all checks, except the below + "EX01", + "SA01", + "SS06", + "ES01", + "GL08", +] diff --git a/reproject/adaptive/high_level.py b/reproject/adaptive/high_level.py index fe25c4784..85393cc19 100644 --- a/reproject/adaptive/high_level.py +++ b/reproject/adaptive/high_level.py @@ -12,12 +12,6 @@ def reproject_adaptive( output_projection, shape_out=None, hdu_in=0, - output_array=None, - return_footprint=True, - output_footprint=None, - block_size=None, - parallel=False, - return_type=None, center_jacobian=False, despike_jacobian=False, roundtrip_coords=True, @@ -32,6 +26,12 @@ def reproject_adaptive( y_cyclic=False, bad_value_mode="strict", bad_fill_value=0, + output_array=None, + output_footprint=None, + return_footprint=True, + block_size=None, + parallel=False, + return_type=None, ): """ Reproject a 2D array from one WCS to another using the DeForest (2004) @@ -42,7 +42,7 @@ def reproject_adaptive( Parameters ---------- - input_data + input_data : object The input data to reproject. This can be: * The name of a FITS file as a `str` or a `pathlib.Path` object @@ -73,8 +73,6 @@ def reproject_adaptive( hdu_in : int or str, optional If ``input_data`` is a FITS file or an `~astropy.io.fits.HDUList` instance, specifies the HDU to use. - return_footprint : bool - Whether to return the footprint in addition to the output array. center_jacobian : bool A Jacobian matrix is calculated, representing d(input image coordinate) / d(output image coordinate), @@ -185,11 +183,37 @@ def reproject_adaptive( bad_fill_value : double The constant value used by the ``constant`` bad-value mode. + output_array : None or `~numpy.ndarray` + An array in which to store the reprojected data. This can be any numpy + array including a memory map, which may be helpful when dealing with + extremely large files. + output_footprint : `~numpy.ndarray`, optional + An array in which to store the footprint of reprojected data. This can be + any numpy array including a memory map, which may be helpful when dealing with + extremely large files. + return_footprint : bool + Whether to return the footprint in addition to the output array. + block_size : tuple or 'auto', optional + The size of blocks in terms of output array pixels that each block will handle + reprojecting. Extending out from (0,0) coords positively, block sizes + are clamped to output space edges when a block would extend past edge. + Specifying ``'auto'`` means that reprojection will be done in blocks with + the block size automatically determined. If ``block_size`` is not + specified or set to `None`, the reprojection will not be carried out in + blocks. + parallel : bool or int, optional + If `True`, the reprojection is carried out in parallel, and if a + positive integer, this specifies the number of processes to use. + The reprojection will be parallelized over output array blocks specified + by ``block_size`` (if the block size is not set, it will be determined + automatically). + return_type : {'numpy', 'dask'}, optional + Whether to return numpy or dask arrays - defaults to 'numpy'. Returns ------- array_new : `~numpy.ndarray` - The reprojected array + The reprojected array. footprint : `~numpy.ndarray` Footprint of the input array in the output array. Values of 0 indicate no coverage or valid values in the input image, while values of 1 diff --git a/reproject/common.py b/reproject/common.py index 9a368ffe1..e9f4951c2 100644 --- a/reproject/common.py +++ b/reproject/common.py @@ -63,10 +63,14 @@ def _reproject_dispatcher( Target shape wcs_out: `~astropy.wcs.WCS` Target WCS - block_size: tuple, optional + block_size: tuple or 'auto', optional The size of blocks in terms of output array pixels that each block will handle reprojecting. Extending out from (0,0) coords positively, block sizes - are clamped to output space edges when a block would extend past edge + are clamped to output space edges when a block would extend past edge. + Specifying ``'auto'`` means that reprojection will be done in blocks with + the block size automatically determined. If ``block_size`` is not + specified or set to `None`, the reprojection will not be carried out in + blocks. array_out : `~numpy.ndarray`, optional An array in which to store the reprojected data. This can be any numpy array including a memory map, which may be helpful when dealing with @@ -77,12 +81,12 @@ def _reproject_dispatcher( An array in which to store the footprint of reprojected data. This can be any numpy array including a memory map, which may be helpful when dealing with extremely large files. - parallel : bool or int - Flag for parallel implementation. If ``True``, a parallel implementation - is chosen, the number of processes selected automatically to be equal to - the number of logical CPUs detected on the machine. If ``False``, a - serial implementation is chosen. If the flag is a positive integer ``n`` - greater than one, a parallel implementation using ``n`` processes is chosen. + parallel : bool or int, optional + If `True`, the reprojection is carried out in parallel, and if a + positive integer, this specifies the number of processes to use. + The reprojection will be parallelized over output array blocks specified + by ``block_size`` (if the block size is not set, it will be determined + automatically). reproject_func_kwargs : dict, optional Keyword arguments to pass through to ``reproject_func`` return_type : {'numpy', 'dask'}, optional diff --git a/reproject/healpix/high_level.py b/reproject/healpix/high_level.py index 9f20a6590..4df2c581f 100644 --- a/reproject/healpix/high_level.py +++ b/reproject/healpix/high_level.py @@ -14,7 +14,7 @@ def reproject_from_healpix( Parameters ---------- - input_data + input_data : object The input data to reproject. This can be: * The name of a HEALPIX FITS file @@ -33,7 +33,7 @@ def reproject_from_healpix( 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) + it is generally 0). order : int or str, optional The order of the interpolation (if ``mode`` is set to ``'interpolation'``). This can be either one of the following strings: @@ -54,7 +54,7 @@ def reproject_from_healpix( Returns ------- array_new : `~numpy.ndarray` - The reprojected array + The reprojected array. footprint : `~numpy.ndarray` Footprint of the input array in the output array. Values of 0 indicate no coverage or valid values in the input image, while values of 1 @@ -86,7 +86,7 @@ def reproject_to_healpix( Parameters ---------- - input_data + input_data : object The input data to reproject. This can be: * The name of a FITS file @@ -98,7 +98,7 @@ def reproject_to_healpix( `~astropy.io.fits.Header` object coord_system_out : `~astropy.coordinates.BaseCoordinateFrame` or str - The output coordinate system for the HEALPIX projection + The output coordinate system for the HEALPIX projection. hdu_in : int or str, optional If ``input_data`` is a FITS file or an `~astropy.io.fits.HDUList` instance, specifies the HDU to use. @@ -114,14 +114,14 @@ def reproject_to_healpix( or an integer. A value of ``0`` indicates nearest neighbor interpolation. nested : bool - The order of the healpix_data, either nested (True) or ring (False) + The order of the healpix_data, either nested (`True`) or ring (`False`). nside : int, optional The resolution of the HEALPIX projection. Returns ------- array_new : `~numpy.ndarray` - The reprojected array + The reprojected array. footprint : `~numpy.ndarray` Footprint of the input array in the output array. Values of 0 indicate no coverage or valid values in the input image, while values of 1 diff --git a/reproject/interpolation/high_level.py b/reproject/interpolation/high_level.py index e4209c1aa..e4918b350 100644 --- a/reproject/interpolation/high_level.py +++ b/reproject/interpolation/high_level.py @@ -21,14 +21,14 @@ def reproject_interp( output_projection, shape_out=None, hdu_in=0, + order="bilinear", + roundtrip_coords=True, output_array=None, - return_footprint=True, output_footprint=None, + return_footprint=True, block_size=None, parallel=False, return_type=None, - order="bilinear", - roundtrip_coords=True, ): """ Reproject data to a new projection using interpolation (this is typically @@ -36,7 +36,7 @@ def reproject_interp( Parameters ---------- - input_data + input_data : object The input data to reproject. This can be: * The name of a FITS file as a `str` or a `pathlib.Path` object @@ -78,32 +78,40 @@ def reproject_interp( or an integer. A value of ``0`` indicates nearest neighbor interpolation. + roundtrip_coords : bool + Whether to verify that coordinate transformations are defined in both + directions. output_array : None or `~numpy.ndarray` An array in which to store the reprojected data. This can be any numpy array including a memory map, which may be helpful when dealing with extremely large files. + output_footprint : `~numpy.ndarray`, optional + An array in which to store the footprint of reprojected data. This can be + any numpy array including a memory map, which may be helpful when dealing with + extremely large files. return_footprint : bool Whether to return the footprint in addition to the output array. - block_size : None or tuple of (int, int) - If not none, a blocked projection will be performed where the output space is - reprojected to one block at a time, this is useful for memory limited scenarios - such as dealing with very large arrays or high resolution output spaces. - parallel : bool or int - Flag for parallel implementation. If ``True``, a parallel implementation - is chosen, the number of processes selected automatically to be equal to - the number of logical CPUs detected on the machine. If ``False``, a - serial implementation is chosen. If the flag is a positive integer ``n`` - greater than one, a parallel implementation using ``n`` processes is chosen. - roundtrip_coords : bool - Whether to verify that coordinate transformations are defined in both - directions. + block_size : tuple or 'auto', optional + The size of blocks in terms of output array pixels that each block will handle + reprojecting. Extending out from (0,0) coords positively, block sizes + are clamped to output space edges when a block would extend past edge. + Specifying ``'auto'`` means that reprojection will be done in blocks with + the block size automatically determined. If ``block_size`` is not + specified or set to `None`, the reprojection will not be carried out in + blocks. + parallel : bool or int, optional + If `True`, the reprojection is carried out in parallel, and if a + positive integer, this specifies the number of processes to use. + The reprojection will be parallelized over output array blocks specified + by ``block_size`` (if the block size is not set, it will be determined + automatically). return_type : {'numpy', 'dask'}, optional Whether to return numpy or dask arrays - defaults to 'numpy'. Returns ------- array_new : `~numpy.ndarray` - The reprojected array + The reprojected array. footprint : `~numpy.ndarray` Footprint of the input array in the output array. Values of 0 indicate no coverage or valid values in the input image, while values of 1 diff --git a/reproject/spherical_intersect/high_level.py b/reproject/spherical_intersect/high_level.py index 3aaca8fff..203dd54a3 100644 --- a/reproject/spherical_intersect/high_level.py +++ b/reproject/spherical_intersect/high_level.py @@ -14,8 +14,8 @@ def reproject_exact( shape_out=None, hdu_in=0, output_array=None, - return_footprint=True, output_footprint=None, + return_footprint=True, block_size=None, parallel=False, return_type=None, @@ -26,7 +26,7 @@ def reproject_exact( Parameters ---------- - input_data + input_data : object The input data to reproject. This can be: * The name of a FITS file as a `str` or a `pathlib.Path` object @@ -57,19 +57,37 @@ def reproject_exact( hdu_in : int or str, optional If ``input_data`` is a FITS file or an `~astropy.io.fits.HDUList` instance, specifies the HDU to use. - parallel : bool or int - Flag for parallel implementation. If ``True``, a parallel implementation - is chosen, the number of processes selected automatically to be equal to - the number of logical CPUs detected on the machine. If ``False``, a - serial implementation is chosen. If the flag is a positive integer ``n`` - greater than one, a parallel implementation using ``n`` processes is chosen. + output_array : None or `~numpy.ndarray` + An array in which to store the reprojected data. This can be any numpy + array including a memory map, which may be helpful when dealing with + extremely large files. + output_footprint : `~numpy.ndarray`, optional + An array in which to store the footprint of reprojected data. This can be + any numpy array including a memory map, which may be helpful when dealing with + extremely large files. return_footprint : bool Whether to return the footprint in addition to the output array. + block_size : tuple or 'auto', optional + The size of blocks in terms of output array pixels that each block will handle + reprojecting. Extending out from (0,0) coords positively, block sizes + are clamped to output space edges when a block would extend past edge. + Specifying ``'auto'`` means that reprojection will be done in blocks with + the block size automatically determined. If ``block_size`` is not + specified or set to `None`, the reprojection will not be carried out in + blocks. + parallel : bool or int, optional + If `True`, the reprojection is carried out in parallel, and if a + positive integer, this specifies the number of processes to use. + The reprojection will be parallelized over output array blocks specified + by ``block_size`` (if the block size is not set, it will be determined + automatically). + return_type : {'numpy', 'dask'}, optional + Whether to return numpy or dask arrays - defaults to 'numpy'. Returns ------- array_new : `~numpy.ndarray` - The reprojected array + The reprojected array. footprint : `~numpy.ndarray` Footprint of the input array in the output array. Values of 0 indicate no coverage or valid values in the input image, while values of 1 From 788b4c26d45c3a85c115698874cc015ce667bf05 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Thu, 14 Sep 2023 10:20:39 +0100 Subject: [PATCH 181/366] Implement block_size='auto' --- reproject/common.py | 2 +- reproject/interpolation/tests/test_core.py | 28 ++++++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/reproject/common.py b/reproject/common.py index e9f4951c2..d53884af4 100644 --- a/reproject/common.py +++ b/reproject/common.py @@ -220,7 +220,7 @@ def reproject_single_block(a, array_or_path, block_info=None): # NOTE: the following array is just used to set up the iteration in map_blocks # but isn't actually used otherwise - this is deliberate. - if block_size: + if block_size is not None and block_size != "auto": if wcs_in.low_level_wcs.pixel_n_dim < len(shape_out): if len(block_size) < len(shape_out): block_size = [-1] * (len(shape_out) - len(block_size)) + list(block_size) diff --git a/reproject/interpolation/tests/test_core.py b/reproject/interpolation/tests/test_core.py index 7209803d2..38c3d9092 100644 --- a/reproject/interpolation/tests/test_core.py +++ b/reproject/interpolation/tests/test_core.py @@ -959,3 +959,31 @@ def test_reproject_dask_return_type(): ) assert_allclose(result_numpy, result_dask.compute(scheduler="synchronous")) + + +def test_auto_block_size(): + # Unit test to make sure that specifying block_size='auto' works + + array_in = da.ones((350, 250, 150)) + wcs_in = WCS(naxis=2) + wcs_out = WCS(naxis=2) + + # When block size and parallel aren't specified, can't return as dask arrays + with pytest.raises(ValueError, match='Output cannot be returned as dask arrays'): + reproject_interp( + (array_in, wcs_in), + wcs_out, + shape_out=(300, 300), + return_type="dask", + ) + + array_out, footprint_out = reproject_interp( + (array_in, wcs_in), + wcs_out, + shape_out=(300, 300), + return_type="dask", + block_size='auto', + ) + + assert array_out.chunksize == (350, 54, 54) + assert footprint_out.chunksize == (350, 54, 54) From 19f1675b3de82578748944e43ffaef5dad43d892 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Thu, 14 Sep 2023 10:38:22 +0100 Subject: [PATCH 182/366] Fix more docstring issues --- .pre-commit-config.yaml | 2 +- reproject/interpolation/tests/test_core.py | 4 ++-- reproject/mosaicking/background.py | 19 +++++++++++++++++-- reproject/mosaicking/coadd.py | 15 ++++++++++++--- reproject/mosaicking/subset_array.py | 8 +++----- reproject/mosaicking/wcs_helpers.py | 6 +++--- 6 files changed, 38 insertions(+), 16 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 890d01778..e5aede8b1 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -75,7 +75,7 @@ repos: rev: main hooks: - id: numpydoc-validation - files: ".*(high_level.*)$" + files: ".*(high_level|mosaicking).*$" exclude: ".*(tests.*)$" ci: diff --git a/reproject/interpolation/tests/test_core.py b/reproject/interpolation/tests/test_core.py index 38c3d9092..3827c0f7f 100644 --- a/reproject/interpolation/tests/test_core.py +++ b/reproject/interpolation/tests/test_core.py @@ -969,7 +969,7 @@ def test_auto_block_size(): wcs_out = WCS(naxis=2) # When block size and parallel aren't specified, can't return as dask arrays - with pytest.raises(ValueError, match='Output cannot be returned as dask arrays'): + with pytest.raises(ValueError, match="Output cannot be returned as dask arrays"): reproject_interp( (array_in, wcs_in), wcs_out, @@ -982,7 +982,7 @@ def test_auto_block_size(): wcs_out, shape_out=(300, 300), return_type="dask", - block_size='auto', + block_size="auto", ) assert array_out.chunksize == (350, 54, 54) diff --git a/reproject/mosaicking/background.py b/reproject/mosaicking/background.py index 26fd9c571..7dd3f4612 100644 --- a/reproject/mosaicking/background.py +++ b/reproject/mosaicking/background.py @@ -11,6 +11,16 @@ def determine_offset_matrix(arrays): """ Given a list of ReprojectedArraySubset, determine the offset matrix between all arrays. + + Parameters + ---------- + arrays : list + The list of ReprojectedArraySubset objects to determine the offsets for. + + Returns + ------- + `numpy.ndarray` + The offset matrix. """ N = len(arrays) @@ -59,9 +69,9 @@ def solve_corrections_sgd(offset_matrix, eta_initial=1, eta_half_life=100, rtol= ---------- offset_matrix : `~numpy.ndarray` The NxN matrix giving the offsets between all images (or NaN if - an offset could not be determined) + an offset could not be determined). eta_initial : float - The initial learning rate to use + The initial learning rate to use. eta_half_life : float The number of iterations after which the learning rate should be decreased by a factor $e$. @@ -71,6 +81,11 @@ def solve_corrections_sgd(offset_matrix, eta_initial=1, eta_half_life=100, rtol= atol : float The absolute tolerance to use to determine if the corrections have converged. + + Returns + ------- + `numpy.ndarray` + The corrections for each frame. """ if offset_matrix.ndim != 2 or offset_matrix.shape[0] != offset_matrix.shape[1]: diff --git a/reproject/mosaicking/coadd.py b/reproject/mosaicking/coadd.py index 66c06ddcb..0f4692034 100644 --- a/reproject/mosaicking/coadd.py +++ b/reproject/mosaicking/coadd.py @@ -17,8 +17,8 @@ def reproject_and_coadd( shape_out=None, input_weights=None, hdu_in=None, - reproject_function=None, hdu_weights=None, + reproject_function=None, combine_function="mean", match_background=False, background_reference=None, @@ -74,7 +74,7 @@ def reproject_and_coadd( If one or more items in ``input_weights`` is a FITS file or an `~astropy.io.fits.HDUList` instance, specifies the HDU to use. reproject_function : callable - The function to use for the reprojection + The function to use for the reprojection. combine_function : { 'mean', 'sum', 'median', 'first', 'last', 'min', 'max' } The type of function to use for combining the values into the final image. For 'first' and 'last', respectively, the reprojected images are @@ -95,8 +95,17 @@ def reproject_and_coadd( The final output footprint array. Specify this if you already have an appropriately-shaped array to store the data in. Must match shape specified with ``shape_out`` or derived from the output projection. - kwargs + **kwargs Keyword arguments to be passed to the reprojection function. + + Returns + ------- + array : `~numpy.ndarray` + The co-added array. + footprint : `~numpy.ndarray` + Footprint of the co-added array. Values of 0 indicate no coverage or + valid values in the input image, while values of 1 indicate valid + values. """ # TODO: add support for saving intermediate files to disk to avoid blowing diff --git a/reproject/mosaicking/subset_array.py b/reproject/mosaicking/subset_array.py index 69731b248..37bde7ebb 100644 --- a/reproject/mosaicking/subset_array.py +++ b/reproject/mosaicking/subset_array.py @@ -6,11 +6,9 @@ class ReprojectedArraySubset: - """ - The aim of this class is to represent a subset of an array and - footprint extracted (or meant to represent extracted) versions - from larger arrays and footprints. - """ + # The aim of this class is to represent a subset of an array and + # footprint extracted (or meant to represent extracted) versions + # from larger arrays and footprints. # NOTE: we can't use Cutout2D here because it's much more convenient # to work with position being the lower left corner of the cutout diff --git a/reproject/mosaicking/wcs_helpers.py b/reproject/mosaicking/wcs_helpers.py index 79f314461..b232bdfaf 100644 --- a/reproject/mosaicking/wcs_helpers.py +++ b/reproject/mosaicking/wcs_helpers.py @@ -71,12 +71,12 @@ def find_optimal_celestial_wcs( instance, specifies the HDU to use. frame : str or `~astropy.coordinates.BaseCoordinateFrame` The coordinate system for the final image (defaults to the frame of - the first image specified) + the first image specified). auto_rotate : bool Whether to rotate the header to minimize the final image area (if - `True`, requires shapely>=1.6 to be installed) + `True`, requires shapely>=1.6 to be installed). projection : str - Three-letter code for the WCS projection + Three-letter code for the WCS projection. resolution : `~astropy.units.Quantity` The resolution of the final image. If not specified, this is the smallest resolution of the input images. From f7fdf295fb448aa302b1993d2dc6d8e973343186 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Thu, 14 Sep 2023 11:17:37 +0100 Subject: [PATCH 183/366] Fix test for different dask versions --- reproject/interpolation/tests/test_core.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/reproject/interpolation/tests/test_core.py b/reproject/interpolation/tests/test_core.py index 3827c0f7f..512332a1b 100644 --- a/reproject/interpolation/tests/test_core.py +++ b/reproject/interpolation/tests/test_core.py @@ -985,5 +985,5 @@ def test_auto_block_size(): block_size="auto", ) - assert array_out.chunksize == (350, 54, 54) - assert footprint_out.chunksize == (350, 54, 54) + assert array_out.chunksize[0] == 350 + assert footprint_out.chunksize[0] == 350 From a5657031c1d8bc4740dcc0ece0dc2804c8371f07 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 25 Sep 2023 16:41:41 +0000 Subject: [PATCH 184/366] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/asottile/pyupgrade: v3.10.1 → v3.13.0](https://github.com/asottile/pyupgrade/compare/v3.10.1...v3.13.0) - [github.com/numpy/numpydoc: main → v1.6.0rc2](https://github.com/numpy/numpydoc/compare/main...v1.6.0rc2) --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index e5aede8b1..b04920964 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -20,7 +20,7 @@ repos: - "--filter-files" - repo: https://github.com/asottile/pyupgrade - rev: v3.10.1 + rev: v3.13.0 hooks: - id: pyupgrade args: ["--py38-plus"] @@ -72,7 +72,7 @@ repos: - id: black - repo: https://github.com/numpy/numpydoc - rev: main + rev: v1.6.0rc2 hooks: - id: numpydoc-validation files: ".*(high_level|mosaicking).*$" From 0e1b6fc5da6190069bd4d073a452db7f7b8cf896 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 2 Oct 2023 16:41:17 +0000 Subject: [PATCH 185/366] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/numpy/numpydoc: v1.6.0rc2 → v1.6.0](https://github.com/numpy/numpydoc/compare/v1.6.0rc2...v1.6.0) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index b04920964..947e9336c 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -72,7 +72,7 @@ repos: - id: black - repo: https://github.com/numpy/numpydoc - rev: v1.6.0rc2 + rev: v1.6.0 hooks: - id: numpydoc-validation files: ".*(high_level|mosaicking).*$" From 9781da15ac2a3cddb808abdb52cf623379130ecb Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 9 Oct 2023 16:28:13 +0000 Subject: [PATCH 186/366] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/pre-commit/pre-commit-hooks: v4.4.0 → v4.5.0](https://github.com/pre-commit/pre-commit-hooks/compare/v4.4.0...v4.5.0) - [github.com/asottile/pyupgrade: v3.13.0 → v3.15.0](https://github.com/asottile/pyupgrade/compare/v3.13.0...v3.15.0) --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 947e9336c..888be6ac6 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.4.0 + rev: v4.5.0 hooks: - id: check-added-large-files - id: check-case-conflict @@ -20,7 +20,7 @@ repos: - "--filter-files" - repo: https://github.com/asottile/pyupgrade - rev: v3.13.0 + rev: v3.15.0 hooks: - id: pyupgrade args: ["--py38-plus"] From 1ba6ac3bb8310de8cf67a1c272e81d6badfb72fe Mon Sep 17 00:00:00 2001 From: David Stansby Date: Wed, 11 Oct 2023 15:51:18 +0100 Subject: [PATCH 187/366] Add Python 3.11 testing --- .github/workflows/ci_workflows.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/ci_workflows.yml b/.github/workflows/ci_workflows.yml index 27a1f0e46..1b430ef4a 100644 --- a/.github/workflows/ci_workflows.yml +++ b/.github/workflows/ci_workflows.yml @@ -17,14 +17,17 @@ jobs: - macos: py38-test-oldestdeps - macos: py39-test - macos: py310-test + - macos: py311-test - linux: py38-test-oldestdeps - linux: py38-test - linux: py39-test - linux: py310-test + - linux: py311-test - linux: py311-test-devdeps - windows: py38-test-oldestdeps - windows: py39-test - windows: py310-test + - windows: py311-test libraries: | apt: - libopenblas-dev From a6f10c6ea8f7bb02e6871802ed8cf6a6bd0e1f82 Mon Sep 17 00:00:00 2001 From: David Stansby Date: Wed, 11 Oct 2023 15:51:34 +0100 Subject: [PATCH 188/366] Remove Python 3.7 from tox --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index cc387ab3f..7f79d2e77 100644 --- a/tox.ini +++ b/tox.ini @@ -1,6 +1,6 @@ [tox] envlist = - py{37,38,39,310,311}-{test}{-oldestdeps,-numpy121} + py{38,39,310,311}-{test}{-oldestdeps,-numpy121} build_docs codestyle isolated_build = True From 9d0ea84613188f188b6a18c2abfa04946297d13d Mon Sep 17 00:00:00 2001 From: David Stansby Date: Wed, 18 Oct 2023 14:24:10 +0100 Subject: [PATCH 189/366] Fix tests --- docs/mosaicking.rst | 2 +- reproject/mosaicking/tests/test_wcs_helpers.py | 2 +- setup.cfg | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/mosaicking.rst b/docs/mosaicking.rst index 703b91926..49d4094d9 100644 --- a/docs/mosaicking.rst +++ b/docs/mosaicking.rst @@ -162,7 +162,7 @@ Note that this requires `Shapely >> shape_out - (1800, 2202) + (1800, 2201) As expected, the optimal shape is smaller than was returned previously. diff --git a/reproject/mosaicking/tests/test_wcs_helpers.py b/reproject/mosaicking/tests/test_wcs_helpers.py index c15b6d79d..43b1f8dff 100644 --- a/reproject/mosaicking/tests/test_wcs_helpers.py +++ b/reproject/mosaicking/tests/test_wcs_helpers.py @@ -225,7 +225,7 @@ def test_args_tuple_header(self): identity_expected_crpix = 20.630112, 15.649142 frame_projection_expected_crpix = 25.381691, 23.668728 frame_projection_expected_shape = 46, 50 - auto_rotate_expected_crpix = 20.520875, 15.503349 + auto_rotate_expected_crpix = 20.513458, 15.513241 multiple_size_expected_crpix = 27.279739, 17.29016 diff --git a/setup.cfg b/setup.cfg index aa3e9f760..b78ad0f27 100644 --- a/setup.cfg +++ b/setup.cfg @@ -31,6 +31,7 @@ all = shapely test = pytest-astropy + shapely>=2.0.2 # 2.0.2 fixed a bug that causes changes in test results testall = shapely sunpy[map]>=2.1;platform_machine!='i686' From 2aa5628b8621181e32cb5f55624caf51076f5792 Mon Sep 17 00:00:00 2001 From: David Stansby Date: Wed, 11 Oct 2023 15:51:46 +0100 Subject: [PATCH 190/366] Add python 3.12 to tox --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index 7f79d2e77..1919b3595 100644 --- a/tox.ini +++ b/tox.ini @@ -1,6 +1,6 @@ [tox] envlist = - py{38,39,310,311}-{test}{-oldestdeps,-numpy121} + py{38,39,310,311,312}-{test}{-oldestdeps,-numpy121} build_docs codestyle isolated_build = True From fb8ec0bd5bc397c969dc3d40b5d3785bb2ed3019 Mon Sep 17 00:00:00 2001 From: David Stansby Date: Wed, 18 Oct 2023 09:41:28 +0100 Subject: [PATCH 191/366] Add Python 3.12 testing --- .github/workflows/ci_workflows.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci_workflows.yml b/.github/workflows/ci_workflows.yml index 1b430ef4a..68a0fd9b4 100644 --- a/.github/workflows/ci_workflows.yml +++ b/.github/workflows/ci_workflows.yml @@ -18,16 +18,19 @@ jobs: - macos: py39-test - macos: py310-test - macos: py311-test + - macos: py312-test - linux: py38-test-oldestdeps - linux: py38-test - linux: py39-test - linux: py310-test - linux: py311-test - - linux: py311-test-devdeps + - linux: py312-test + - linux: py312-test-devdeps - windows: py38-test-oldestdeps - windows: py39-test - windows: py310-test - windows: py311-test + - windows: py312-test libraries: | apt: - libopenblas-dev From 2750c2c8cf06ec81b270ea3425304ff41c7e4ddd Mon Sep 17 00:00:00 2001 From: David Stansby Date: Wed, 18 Oct 2023 19:40:16 +0100 Subject: [PATCH 192/366] bump cython version --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index b52fd4487..fba79d77e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ requires = ["setuptools", "wheel", "extension-helpers", "oldest-supported-numpy", - "cython==3.0.2"] + "cython==3.0.4"] build-backend = 'setuptools.build_meta' [tool.setuptools_scm] From 02d3a04b016c9c03351ba5d422abf6ba916613c6 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Thu, 19 Oct 2023 13:41:14 +0100 Subject: [PATCH 193/366] Use pip --pre on Python 3.12 to get aiohttp pre-release that is compatible with Python 3.12 --- tox.ini | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tox.ini b/tox.ini index 1919b3595..c38c9c4fa 100644 --- a/tox.ini +++ b/tox.ini @@ -15,6 +15,7 @@ setenv = MPLBACKEND = Agg PYTEST_COMMAND = pytest --arraydiff --arraydiff-default-format=fits --pyargs reproject --cov reproject --cov-config={toxinidir}/setup.cfg {toxinidir}/docs --remote-data devdeps: PIP_EXTRA_INDEX_URL = https://pypi.anaconda.org/astropy/simple https://pypi.anaconda.org/scientific-python-nightly-wheels/simple + py312: PIP_PRE=1 changedir = .tmp/{envname} deps = @@ -33,6 +34,7 @@ deps = devdeps: git+https://github.com/astropy/asdf-astropy.git devdeps: git+https://github.com/spacetelescope/gwcs.git#egg=gwcs #devdeps: git+https://github.com/sunpy/sunpy.git#egg=sunpy + extras = test # Don't run the more complex tests on oldestdeps because it pulls in a nest From 9cf94293afcc72cbefdf992cfa1eda30aaed5f27 Mon Sep 17 00:00:00 2001 From: David Stansby Date: Thu, 19 Oct 2023 14:36:44 +0100 Subject: [PATCH 194/366] Fix tox environment list --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index c38c9c4fa..25ab35d7e 100644 --- a/tox.ini +++ b/tox.ini @@ -1,6 +1,6 @@ [tox] envlist = - py{38,39,310,311,312}-{test}{-oldestdeps,-numpy121} + py{38,39,310,311,312}-{test}{,-oldestdeps,-numpy121} build_docs codestyle isolated_build = True From 90553522f4e8826ba9e2137971568f68960e5f5c Mon Sep 17 00:00:00 2001 From: David Stansby Date: Thu, 19 Oct 2023 14:37:10 +0100 Subject: [PATCH 195/366] Remove unused MANIFEST.in entries --- MANIFEST.in | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/MANIFEST.in b/MANIFEST.in index ba172e5b3..c9c889b05 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,18 +1,14 @@ include README.rst include CHANGES.md -include LICENSE.md +include LICENSE include pyproject.toml -include setup.py include setup.cfg include pyproject.toml -recursive-include reproject *.pyx *.c *.pxd +recursive-include reproject *.pyx *.c recursive-include docs * -recursive-include licenses * -recursive-include cextern * -recursive-include scripts * prune build prune docs/_build From ae077d5047fd38b1daaa5535a59d404b708cd3c2 Mon Sep 17 00:00:00 2001 From: David Stansby Date: Thu, 19 Oct 2023 16:29:41 +0100 Subject: [PATCH 196/366] Don't install sunpy for 3.12 tests --- setup.cfg | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index b78ad0f27..6dcd7b19f 100644 --- a/setup.cfg +++ b/setup.cfg @@ -34,7 +34,9 @@ test = shapely>=2.0.2 # 2.0.2 fixed a bug that causes changes in test results testall = shapely - sunpy[map]>=2.1;platform_machine!='i686' + # Once a release of reproject is done for Python 3.12, + # can remove python_version specifier here + sunpy[map]>=2.1;python_version<"3.12" and platform_machine!="i686" asdf gwcs pyvo From 63ff80f946db24748f4ff487e20581f39f30cd6a Mon Sep 17 00:00:00 2001 From: Leo Singer Date: Thu, 12 Oct 2023 17:47:27 -0400 Subject: [PATCH 197/366] Compute footprint in healpix_to_image --- reproject/healpix/core.py | 10 +++-- reproject/healpix/tests/test_healpix.py | 57 ++++++++++++++++++++++--- 2 files changed, 58 insertions(+), 9 deletions(-) diff --git a/reproject/healpix/core.py b/reproject/healpix/core.py index 345868caf..8a894701b 100644 --- a/reproject/healpix/core.py +++ b/reproject/healpix/core.py @@ -69,15 +69,17 @@ def healpix_to_image( hp = HEALPix(nside=nside, order="nested" if nested else "ring") if order == 1: - data = hp.interpolate_bilinear_lonlat(lon_in, lat_in, healpix_data) + with np.errstate(invalid="ignore"): + data = hp.interpolate_bilinear_lonlat(lon_in, lat_in, healpix_data) + footprint = (~np.isnan(data)).astype(float) elif order == 0: - ipix = hp.lonlat_to_healpix(lon_in, lat_in) + with np.errstate(invalid="ignore"): + ipix = hp.lonlat_to_healpix(lon_in, lat_in) data = healpix_data[ipix] + footprint = (ipix != -1).astype(float) else: raise ValueError("Only nearest-neighbor and bilinear interpolation are supported") - footprint = np.ones(data.shape, bool) - return data, footprint diff --git a/reproject/healpix/tests/test_healpix.py b/reproject/healpix/tests/test_healpix.py index 7833d1ec3..8b68adfb6 100644 --- a/reproject/healpix/tests/test_healpix.py +++ b/reproject/healpix/tests/test_healpix.py @@ -12,18 +12,19 @@ from ...interpolation.tests.test_core import as_high_level_wcs from ...tests.test_high_level import ALL_DTYPES from ..high_level import reproject_from_healpix, reproject_to_healpix +from ..utils import parse_coord_system DATA = os.path.join(os.path.dirname(__file__), "data") -def get_reference_header(oversample=2, nside=1): +def get_reference_header(overscan=1, oversample=2, nside=1): reference_header = fits.Header() reference_header.update( { "CDELT1": -180.0 / (oversample * 4 * nside), "CDELT2": 180.0 / (oversample * 4 * nside), - "CRPIX1": oversample * 4 * nside, - "CRPIX2": oversample * 2 * nside, + "CRPIX1": overscan * oversample * 4 * nside, + "CRPIX2": overscan * oversample * 2 * nside, "CRVAL1": 180.0, "CRVAL2": 0.0, "CTYPE1": "RA---CAR", @@ -31,14 +32,60 @@ def get_reference_header(oversample=2, nside=1): "CUNIT1": "deg", "CUNIT2": "deg", "NAXIS": 2, - "NAXIS1": oversample * 8 * nside, - "NAXIS2": oversample * 4 * nside, + "NAXIS1": overscan * oversample * 8 * nside, + "NAXIS2": overscan * oversample * 4 * nside, } ) return reference_header +@pytest.mark.parametrize( + "nside,nested,healpix_system,image_system,dtype,order", + itertools.product( + [1, 2, 4, 8, 16, 32, 64], + [True, False], + "C", + "C", + ALL_DTYPES, + ["bilinear", "nearest-neighbor"], + ), +) +def test_reproject_healpix_to_image_footprint( + nside, nested, healpix_system, image_system, dtype, order +): + """Test that HEALPix->WCS conversion correctly flags pixels that do not + have valid WCS coordinates.""" + + npix = nside_to_npix(nside) + healpix_data = np.random.uniform(size=npix).astype(dtype) + + reference_header = get_reference_header(overscan=2, oversample=2, nside=nside) + + wcs_out = WCS(reference_header) + shape_out = reference_header["NAXIS2"], reference_header["NAXIS1"] + + image_data, footprint = reproject_from_healpix( + (healpix_data, healpix_system), + wcs_out, + shape_out=shape_out, + order=order, + nested=nested, + ) + + if order == "bilinear": + expected_footprint = ~np.isnan(image_data) + else: + coord_system_in = parse_coord_system(healpix_system) + yinds, xinds = np.indices(shape_out) + world_in = wcs_out.pixel_to_world(xinds, yinds).transform_to(coord_system_in) + world_in_unitsph = world_in.represent_as("unitspherical") + lon_in, lat_in = world_in_unitsph.lon, world_in_unitsph.lat + expected_footprint = ~(np.isnan(lon_in) | np.isnan(lat_in)) + + np.testing.assert_array_equal(footprint, expected_footprint) + + @pytest.mark.parametrize( "wcsapi,nside,nested,healpix_system,image_system,dtype", itertools.product([True, False], [1, 2, 4, 8, 16, 32, 64], [True, False], "C", "C", ALL_DTYPES), From 2b92d30a68e8f510c0a70629915a2b074f7a150c Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 23 Oct 2023 16:28:07 +0000 Subject: [PATCH 198/366] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/psf/black: 23.9.1 → 23.10.0](https://github.com/psf/black/compare/23.9.1...23.10.0) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 888be6ac6..b05dbee9f 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -67,7 +67,7 @@ repos: exclude: ".*(data.*|extern.*|cextern)$" - repo: https://github.com/psf/black - rev: 23.9.1 + rev: 23.10.0 hooks: - id: black From 44ac42ee527b2888a570911d7451872ea46f2066 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Tue, 24 Oct 2023 10:56:58 +0100 Subject: [PATCH 199/366] Enable Python 3.12 wheel building --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index fba79d77e..d3c78668d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -11,7 +11,7 @@ build-backend = 'setuptools.build_meta' write_to = "reproject/version.py" [tool.cibuildwheel] -skip = "cp36-* pp* *-musllinux* cp310-win32 cp312-*" +skip = "cp36-* pp* *-musllinux*" test-skip = "*-macosx_arm64 *-manylinux_aarch64" [tool.isort] From b40ddc1c86547fb58f498f218f8569284673cfdf Mon Sep 17 00:00:00 2001 From: astrofrog Date: Tue, 24 Oct 2023 13:06:55 +0000 Subject: [PATCH 200/366] Update CHANGELOG --- CHANGES.md | 97 +++++++++++++++++++++++++++++++++++------------------- 1 file changed, 64 insertions(+), 33 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index efa181e0f..7263d0ff0 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,34 @@ +## v0.13.0 - 2023-10-24 + + +### What's Changed + +#### Bug Fixes + +- Fix TestReprojectAndCoAdd failure on i386 by @olebole in https://github.com/astropy/reproject/pull/386 +- Fixed a bug that caused reprojected dask arrays to not be computable due to a temporary directory being removed by @astrofrog in https://github.com/astropy/reproject/pull/390 + +#### New Features + +- Add ability to specify output array and footprint in reproject_and_coadd by @astrofrog in https://github.com/astropy/reproject/pull/387 +- Added ability to set `block_size='auto'` and fix missing parameters in docstrings by @astrofrog in https://github.com/astropy/reproject/pull/392 +- Compute footprint in healpix_to_image by @lpsinger in https://github.com/astropy/reproject/pull/400 + +#### Other Changes + +- Fix compatibility with Cython 3.0.2 and update version in pyproject.toml by @astrofrog in https://github.com/astropy/reproject/pull/391 +- Add tests on Python 3.11 by @dstansby in https://github.com/astropy/reproject/pull/401 +- Add testing on Python 3.12 by @dstansby in https://github.com/astropy/reproject/pull/399 +- Python 3.12 testing by @dstansby in https://github.com/astropy/reproject/pull/403 +- Add testing on Python 3.12 by @astrofrog in https://github.com/astropy/reproject/pull/402 +- Enable Python 3.12 wheel building by @astrofrog in https://github.com/astropy/reproject/pull/405 + +### New Contributors + +- @olebole made their first contribution in https://github.com/astropy/reproject/pull/386 + +**Full Changelog**: https://github.com/astropy/reproject/compare/v0.12.0...v0.13.0 + ## v0.12.0 - 2023-09-07 @@ -100,9 +131,9 @@ ## 0.9 - 2022-09-02 - Drop support for Python 3.7. -- +- - Infrastructure and packaging updates. -- +- - Made many improvements, bug fixes, and significant speed-ups for the adaptive - resampling algorithm, `reproject_adaptive`. These bug fixes may cause - changes to the reprojected images, which are typically negligible. @@ -110,13 +141,13 @@ - Gaussian filter kernel, a menu of boundary-handling modes, and a - `center_jacobian` flag to trade speed for accuracy with rapidly-varying - transformations. -- +- - Added a `roundtrip_coords` argument to `reproject_adaptive` and - `reproject_interp`. By default, all coordinate transformations are run in - both directions to handle some situations where they are ambiguous. This can - be disabled by setting `roundtrip_coords=False` which may offer a - significant speed increase. -- +- ## 0.8 - 2021-08-11 @@ -125,34 +156,34 @@ - possible to solve for the optimal WCS for a set of images that couldn't fit - into memory all at once, since the actual data aren't needed for optimal WCS - determination. [#242] -- +- - Fix implementation of `hdu_weights` in `reproject_and_coadd`. [#249] -- +- ## 0.7.1 - 2020-05-29 - Fixed compatibility with Astropy 4.1. [#234] -- +- - Updated minimum requirement for SciPy. [#236] -- +- ## 0.7 - 2020-04-02 - Made C extension in overlapArea.c thread-safe by removing global - variables. [#211] -- +- - Made it possible to control whether to output debugging information - from overlapArea.c by setting DEBUG_OVERLAP_AREA=1 at build-time. [#211] -- +- - Fix compatibility with astropy v4.0.1. [#227] -- +- - Disable parallelization by default in `reproject_exact` - this can be - enabled with `parallel=True`. [#227] -- +- - Fixed a bug with `reproject_exact` with `parallel=False` and - `return_footprint=False`, which caused the footprint to be returned - anyway. [#227] -- +- - The infrastructure of the package has been updated in line with the - APE 17 roadmap (https://github.com/astropy/astropy-APEs/blob/main/APE17.rst). - The main changes are that the `python setup.py test` and @@ -161,7 +192,7 @@ - (https://tox.readthedocs.io) package and run `tox -e test` and - `tox -e build_docs`. It is also possible to run pytest and sphinx - directly. [#228] -- +- ## 0.6 - 2019-11-01 @@ -170,20 +201,20 @@ - `independent_celestial_slices=` argument to `reproject_interp` has - been deprecated since it is no longer needed, as transformations are - automatically done in the most efficient way possible. [#166] -- +- - Include a warning for high resolution images with `reproject_exact`, - since if the pixels are <0.05", precision issues can occur. [#200] -- +- - Added a new `reproject_and_coadd` function for doing mosaicking of - individual images, and added section in documentation about mosaicking. - [#186] -- +- - Added a new reproject.adaptive sub-package that implements the DeForest - (2004) algorithm for reprojection. [#52] -- +- - Fixed a bug that caused 'exact' reprojection results to have numerical - issues when doing identity transformations. [#190] -- +- ## 0.5.1 - 2019-09-01 @@ -194,33 +225,33 @@ - Improve parse_output_projection to make it so that the output projection - can be specified as a filename. [#150] -- +- - Fixed a bug that caused HEALPix maps in RING order to not be correctly - interpreted. [#163] -- +- - Make it possible to specify the output array for reprojection using the - `output_array=` keyword argument. [#115] -- +- ## 0.4 - 2018-01-29 - Refactored HEALPix reprojection code to use the astropy-healpix package - instead of healpy. [#139] -- +- - Added the ability to specify an output array in `reproject_interp`, which - permits the use of memory-mapped arrays and therefore provides the capability - to handle data cubes much larger than memory [#115] -- +- - Fix test 32-bit test failures. [#146] -- +- - Fix an issue with reprojecting images where there are two solutions along - the line of sight by forcing round-tripping of coordinate conversions [#129] -- +- - Explicitly define default HDU as 0 for normal reprojection and 1 for - HEALPix reprojection. [#119] -- +- - Added a function to find the optimal WCS for a set of images. [#136, #137] -- +- ## 0.3.2 - 2017-10-22 @@ -231,22 +262,22 @@ ## 0.3.1 - 2016-07-07 - Include missing license file in tarball. -- +- - Updated documentation to remove warnings about early versions. -- +- ## 0.3 - 2016-07-06 - Allow users to pass a `field=` option to `reproject_from_healpix` - to access different fields in a HEALPIX file. [#86] -- +- - Significant improvements to performance when the input data is a large - memory-mapped array. [#105] -- +- - Significant refactoring of interpolating reprojection to improve support for - n-dimensional arrays, optionally including two celestial axes (in which - case the coordinate transformation is taken into account). [#96, #102] -- +- ## 0.2 - 2015-10-29 From 123c919a7cfee129e9026f459c57b6f9d1e2fb90 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Mon, 30 Oct 2023 10:33:05 +0000 Subject: [PATCH 201/366] Updated docstrings for output_projection and shape_out to indicate that any APE-14 WCS is acceptable --- reproject/adaptive/high_level.py | 12 +++++++----- reproject/interpolation/high_level.py | 12 +++++++----- reproject/mosaicking/coadd.py | 12 +++++++----- reproject/spherical_intersect/high_level.py | 12 +++++++----- 4 files changed, 28 insertions(+), 20 deletions(-) diff --git a/reproject/adaptive/high_level.py b/reproject/adaptive/high_level.py index 85393cc19..7c9d1c881 100644 --- a/reproject/adaptive/high_level.py +++ b/reproject/adaptive/high_level.py @@ -64,12 +64,14 @@ def reproject_adaptive( coordinate information. The coordinate transformation will be computed once and then each image will be reprojected, offering a speedup over reprojecting each image individually. - output_projection : `~astropy.wcs.WCS` or `~astropy.io.fits.Header` - The output projection, which can be either a `~astropy.wcs.WCS` - or a `~astropy.io.fits.Header` instance. + output_projection : `~astropy.wcs.wcsapi.BaseLowLevelWCS` or `~astropy.wcs.wcsapi.BaseHighLevelWCS` or `~astropy.io.fits.Header` + The output projection, which can be either a + `~astropy.wcs.wcsapi.BaseLowLevelWCS`, + `~astropy.wcs.wcsapi.BaseHighLevelWCS`, or a `~astropy.io.fits.Header` + instance. shape_out : tuple, optional - If ``output_projection`` is a `~astropy.wcs.WCS` instance, the - shape of the output data should be specified separately. + If ``output_projection`` is a WCS instance, the shape of the output + data should be specified separately. hdu_in : int or str, optional If ``input_data`` is a FITS file or an `~astropy.io.fits.HDUList` instance, specifies the HDU to use. diff --git a/reproject/interpolation/high_level.py b/reproject/interpolation/high_level.py index e4918b350..64036a868 100644 --- a/reproject/interpolation/high_level.py +++ b/reproject/interpolation/high_level.py @@ -58,12 +58,14 @@ def reproject_interp( coordinate information. The coordinate transformation will be computed once and then each image will be reprojected, offering a speedup over reprojecting each image individually. - output_projection : `~astropy.wcs.WCS` or `~astropy.io.fits.Header` - The output projection, which can be either a `~astropy.wcs.WCS` - or a `~astropy.io.fits.Header` instance. + output_projection : `~astropy.wcs.wcsapi.BaseLowLevelWCS` or `~astropy.wcs.wcsapi.BaseHighLevelWCS` or `~astropy.io.fits.Header` + The output projection, which can be either a + `~astropy.wcs.wcsapi.BaseLowLevelWCS`, + `~astropy.wcs.wcsapi.BaseHighLevelWCS`, or a `~astropy.io.fits.Header` + instance. shape_out : tuple, optional - If ``output_projection`` is a `~astropy.wcs.WCS` instance, the - shape of the output data should be specified separately. + If ``output_projection`` is a WCS instance, the shape of the output + data should be specified separately. hdu_in : int or str, optional If ``input_data`` is a FITS file or an `~astropy.io.fits.HDUList` instance, specifies the HDU to use. diff --git a/reproject/mosaicking/coadd.py b/reproject/mosaicking/coadd.py index 0f4692034..52ff949e8 100644 --- a/reproject/mosaicking/coadd.py +++ b/reproject/mosaicking/coadd.py @@ -50,12 +50,14 @@ def reproject_and_coadd( * An `~astropy.nddata.NDData` object from which the ``.data`` and ``.wcs`` attributes will be used as the input data. - output_projection : `~astropy.wcs.WCS` or `~astropy.io.fits.Header` - The output projection, which can be either a `~astropy.wcs.WCS` - or a `~astropy.io.fits.Header` instance. + output_projection : `~astropy.wcs.wcsapi.BaseLowLevelWCS` or `~astropy.wcs.wcsapi.BaseHighLevelWCS` or `~astropy.io.fits.Header` + The output projection, which can be either a + `~astropy.wcs.wcsapi.BaseLowLevelWCS`, + `~astropy.wcs.wcsapi.BaseHighLevelWCS`, or a `~astropy.io.fits.Header` + instance. shape_out : tuple, optional - If ``output_projection`` is a `~astropy.wcs.WCS` instance, the - shape of the output data should be specified separately. + If ``output_projection`` is a WCS instance, the shape of the output + data should be specified separately. input_weights : iterable If specified, this should be an iterable with the same length as ``input_data``, where each item is one of: diff --git a/reproject/spherical_intersect/high_level.py b/reproject/spherical_intersect/high_level.py index 203dd54a3..d7c549b81 100644 --- a/reproject/spherical_intersect/high_level.py +++ b/reproject/spherical_intersect/high_level.py @@ -48,12 +48,14 @@ def reproject_exact( coordinate information. The coordinate transformation will be computed once and then each image will be reprojected, offering a speedup over reprojecting each image individually. - output_projection : `~astropy.wcs.WCS` or `~astropy.io.fits.Header` - The output projection, which can be either a `~astropy.wcs.WCS` - or a `~astropy.io.fits.Header` instance. + output_projection : `~astropy.wcs.wcsapi.BaseLowLevelWCS` or `~astropy.wcs.wcsapi.BaseHighLevelWCS` or `~astropy.io.fits.Header` + The output projection, which can be either a + `~astropy.wcs.wcsapi.BaseLowLevelWCS`, + `~astropy.wcs.wcsapi.BaseHighLevelWCS`, or a `~astropy.io.fits.Header` + instance. shape_out : tuple, optional - If ``output_projection`` is a `~astropy.wcs.WCS` instance, the - shape of the output data should be specified separately. + If ``output_projection`` is a WCS instance, the shape of the output + data should be specified separately. hdu_in : int or str, optional If ``input_data`` is a FITS file or an `~astropy.io.fits.HDUList` instance, specifies the HDU to use. From bb185b7199f1f3882c7ac457640c002f441901f6 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Mon, 30 Oct 2023 11:03:27 +0000 Subject: [PATCH 202/366] Codestyle fixes --- CHANGES.md | 66 +++++++++++++++++++++++++++--------------------------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 7263d0ff0..10e887508 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -131,9 +131,9 @@ ## 0.9 - 2022-09-02 - Drop support for Python 3.7. -- +- - Infrastructure and packaging updates. -- +- - Made many improvements, bug fixes, and significant speed-ups for the adaptive - resampling algorithm, `reproject_adaptive`. These bug fixes may cause - changes to the reprojected images, which are typically negligible. @@ -141,13 +141,13 @@ - Gaussian filter kernel, a menu of boundary-handling modes, and a - `center_jacobian` flag to trade speed for accuracy with rapidly-varying - transformations. -- +- - Added a `roundtrip_coords` argument to `reproject_adaptive` and - `reproject_interp`. By default, all coordinate transformations are run in - both directions to handle some situations where they are ambiguous. This can - be disabled by setting `roundtrip_coords=False` which may offer a - significant speed increase. -- +- ## 0.8 - 2021-08-11 @@ -156,34 +156,34 @@ - possible to solve for the optimal WCS for a set of images that couldn't fit - into memory all at once, since the actual data aren't needed for optimal WCS - determination. [#242] -- +- - Fix implementation of `hdu_weights` in `reproject_and_coadd`. [#249] -- +- ## 0.7.1 - 2020-05-29 - Fixed compatibility with Astropy 4.1. [#234] -- +- - Updated minimum requirement for SciPy. [#236] -- +- ## 0.7 - 2020-04-02 - Made C extension in overlapArea.c thread-safe by removing global - variables. [#211] -- +- - Made it possible to control whether to output debugging information - from overlapArea.c by setting DEBUG_OVERLAP_AREA=1 at build-time. [#211] -- +- - Fix compatibility with astropy v4.0.1. [#227] -- +- - Disable parallelization by default in `reproject_exact` - this can be - enabled with `parallel=True`. [#227] -- +- - Fixed a bug with `reproject_exact` with `parallel=False` and - `return_footprint=False`, which caused the footprint to be returned - anyway. [#227] -- +- - The infrastructure of the package has been updated in line with the - APE 17 roadmap (https://github.com/astropy/astropy-APEs/blob/main/APE17.rst). - The main changes are that the `python setup.py test` and @@ -192,7 +192,7 @@ - (https://tox.readthedocs.io) package and run `tox -e test` and - `tox -e build_docs`. It is also possible to run pytest and sphinx - directly. [#228] -- +- ## 0.6 - 2019-11-01 @@ -201,20 +201,20 @@ - `independent_celestial_slices=` argument to `reproject_interp` has - been deprecated since it is no longer needed, as transformations are - automatically done in the most efficient way possible. [#166] -- +- - Include a warning for high resolution images with `reproject_exact`, - since if the pixels are <0.05", precision issues can occur. [#200] -- +- - Added a new `reproject_and_coadd` function for doing mosaicking of - individual images, and added section in documentation about mosaicking. - [#186] -- +- - Added a new reproject.adaptive sub-package that implements the DeForest - (2004) algorithm for reprojection. [#52] -- +- - Fixed a bug that caused 'exact' reprojection results to have numerical - issues when doing identity transformations. [#190] -- +- ## 0.5.1 - 2019-09-01 @@ -225,33 +225,33 @@ - Improve parse_output_projection to make it so that the output projection - can be specified as a filename. [#150] -- +- - Fixed a bug that caused HEALPix maps in RING order to not be correctly - interpreted. [#163] -- +- - Make it possible to specify the output array for reprojection using the - `output_array=` keyword argument. [#115] -- +- ## 0.4 - 2018-01-29 - Refactored HEALPix reprojection code to use the astropy-healpix package - instead of healpy. [#139] -- +- - Added the ability to specify an output array in `reproject_interp`, which - permits the use of memory-mapped arrays and therefore provides the capability - to handle data cubes much larger than memory [#115] -- +- - Fix test 32-bit test failures. [#146] -- +- - Fix an issue with reprojecting images where there are two solutions along - the line of sight by forcing round-tripping of coordinate conversions [#129] -- +- - Explicitly define default HDU as 0 for normal reprojection and 1 for - HEALPix reprojection. [#119] -- +- - Added a function to find the optimal WCS for a set of images. [#136, #137] -- +- ## 0.3.2 - 2017-10-22 @@ -262,22 +262,22 @@ ## 0.3.1 - 2016-07-07 - Include missing license file in tarball. -- +- - Updated documentation to remove warnings about early versions. -- +- ## 0.3 - 2016-07-06 - Allow users to pass a `field=` option to `reproject_from_healpix` - to access different fields in a HEALPIX file. [#86] -- +- - Significant improvements to performance when the input data is a large - memory-mapped array. [#105] -- +- - Significant refactoring of interpolating reprojection to improve support for - n-dimensional arrays, optionally including two celestial axes (in which - case the coordinate transformation is taken into account). [#96, #102] -- +- ## 0.2 - 2015-10-29 From 02b10c6852c386493a450cca7b9a81712a5244cf Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Mon, 30 Oct 2023 11:05:39 +0000 Subject: [PATCH 203/366] Add tests to make sure that the healpix functions work with the full range of valid inputs/outputs (including e.g. APE-14 WCS) and update docstrings. --- reproject/healpix/high_level.py | 25 +++++++++------ reproject/healpix/tests/test_healpix.py | 42 +++++++++++++++++++++++++ 2 files changed, 58 insertions(+), 9 deletions(-) diff --git a/reproject/healpix/high_level.py b/reproject/healpix/high_level.py index 4df2c581f..f9ed1ed65 100644 --- a/reproject/healpix/high_level.py +++ b/reproject/healpix/high_level.py @@ -24,12 +24,14 @@ def reproject_from_healpix( second element is a `~astropy.coordinates.BaseCoordinateFrame` instance or a string alias for a coordinate frame. - output_projection : `~astropy.wcs.WCS` or `~astropy.io.fits.Header` - The output projection, which can be either a `~astropy.wcs.WCS` - or a `~astropy.io.fits.Header` instance. + output_projection : `~astropy.wcs.wcsapi.BaseLowLevelWCS` or `~astropy.wcs.wcsapi.BaseHighLevelWCS` or `~astropy.io.fits.Header` + The output projection, which can be either a + `~astropy.wcs.wcsapi.BaseLowLevelWCS`, + `~astropy.wcs.wcsapi.BaseHighLevelWCS`, or a `~astropy.io.fits.Header` + instance. shape_out : tuple, optional - If ``output_projection`` is a `~astropy.wcs.WCS` instance, the - shape of the output data should be specified separately. + If ``output_projection`` is a WCS instance, the shape of the output + data should be specified separately. 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 @@ -89,13 +91,18 @@ def reproject_to_healpix( input_data : object The input data to reproject. This can be: - * The name of a FITS file + * The name of a FITS file as a `str` or a `pathlib.Path` object * An `~astropy.io.fits.HDUList` object - * An image HDU object such as a `~astropy.io.fits.PrimaryHDU` or - `~astropy.io.fits.ImageHDU` instance + * An image HDU object such as a `~astropy.io.fits.PrimaryHDU`, + `~astropy.io.fits.ImageHDU`, or `~astropy.io.fits.CompImageHDU` + instance * A tuple where the first element is a `~numpy.ndarray` and the - second element is either a `~astropy.wcs.WCS` or a + second element is either a + `~astropy.wcs.wcsapi.BaseLowLevelWCS`, + `~astropy.wcs.wcsapi.BaseHighLevelWCS`, or a `~astropy.io.fits.Header` object + * An `~astropy.nddata.NDData` object from which the ``.data`` and + ``.wcs`` attributes will be used as the input data. coord_system_out : `~astropy.coordinates.BaseCoordinateFrame` or str The output coordinate system for the HEALPIX projection. diff --git a/reproject/healpix/tests/test_healpix.py b/reproject/healpix/tests/test_healpix.py index 8b68adfb6..84d4a4e7d 100644 --- a/reproject/healpix/tests/test_healpix.py +++ b/reproject/healpix/tests/test_healpix.py @@ -138,3 +138,45 @@ def test_reproject_invalid_order(): os.path.join(DATA, "bayestar.fits.gz"), reference_header, order="bicubic" ) assert exc.value.args[0] == "Only nearest-neighbor and bilinear interpolation are supported" + + +def test_reproject_to_healpix_input_types(valid_celestial_input_data): + array_ref, wcs_in_ref, input_value, kwargs_in = valid_celestial_input_data + + # Compute reference + + healpix_data_ref, footprint_ref = reproject_to_healpix((array_ref, wcs_in_ref), "C", nside=64) + + # Compute test + + healpix_data_test, footprint_test = reproject_to_healpix( + input_value, "C", nside=64, **kwargs_in + ) + + # Make sure there are some valid values + + assert np.sum(~np.isnan(healpix_data_ref)) == 3 + + np.testing.assert_allclose(healpix_data_ref, healpix_data_test) + np.testing.assert_allclose(footprint_ref, footprint_test) + + +def test_reproject_from_healpix_output_types(valid_celestial_output_projections): + wcs_out_ref, shape_ref, output_value, kwargs_out = valid_celestial_output_projections + + array_input = np.random.random(12 * 64**2) + + # Compute reference + + output_ref, footprint_ref = reproject_from_healpix( + (array_input, "C"), wcs_out_ref, nested=True, shape_out=shape_ref + ) + + # Compute test + + output_test, footprint_test = reproject_from_healpix( + (array_input, "C"), output_value, nested=True, **kwargs_out + ) + + np.testing.assert_allclose(output_ref, output_test) + np.testing.assert_allclose(footprint_ref, footprint_test) From e2c8297afbb2fb24bb4d33dcfbe437291a88f730 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 30 Oct 2023 16:28:08 +0000 Subject: [PATCH 204/366] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/psf/black: 23.10.0 → 23.10.1](https://github.com/psf/black/compare/23.10.0...23.10.1) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index b05dbee9f..f8ef414bc 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -67,7 +67,7 @@ repos: exclude: ".*(data.*|extern.*|cextern)$" - repo: https://github.com/psf/black - rev: 23.10.0 + rev: 23.10.1 hooks: - id: black From 2913005de4d0b2e6dd06d551e78dad98b2f5300e Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 30 Oct 2023 16:29:12 +0000 Subject: [PATCH 205/366] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- CHANGES.md | 66 +++++++++++++++++++++++++++--------------------------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 7263d0ff0..10e887508 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -131,9 +131,9 @@ ## 0.9 - 2022-09-02 - Drop support for Python 3.7. -- +- - Infrastructure and packaging updates. -- +- - Made many improvements, bug fixes, and significant speed-ups for the adaptive - resampling algorithm, `reproject_adaptive`. These bug fixes may cause - changes to the reprojected images, which are typically negligible. @@ -141,13 +141,13 @@ - Gaussian filter kernel, a menu of boundary-handling modes, and a - `center_jacobian` flag to trade speed for accuracy with rapidly-varying - transformations. -- +- - Added a `roundtrip_coords` argument to `reproject_adaptive` and - `reproject_interp`. By default, all coordinate transformations are run in - both directions to handle some situations where they are ambiguous. This can - be disabled by setting `roundtrip_coords=False` which may offer a - significant speed increase. -- +- ## 0.8 - 2021-08-11 @@ -156,34 +156,34 @@ - possible to solve for the optimal WCS for a set of images that couldn't fit - into memory all at once, since the actual data aren't needed for optimal WCS - determination. [#242] -- +- - Fix implementation of `hdu_weights` in `reproject_and_coadd`. [#249] -- +- ## 0.7.1 - 2020-05-29 - Fixed compatibility with Astropy 4.1. [#234] -- +- - Updated minimum requirement for SciPy. [#236] -- +- ## 0.7 - 2020-04-02 - Made C extension in overlapArea.c thread-safe by removing global - variables. [#211] -- +- - Made it possible to control whether to output debugging information - from overlapArea.c by setting DEBUG_OVERLAP_AREA=1 at build-time. [#211] -- +- - Fix compatibility with astropy v4.0.1. [#227] -- +- - Disable parallelization by default in `reproject_exact` - this can be - enabled with `parallel=True`. [#227] -- +- - Fixed a bug with `reproject_exact` with `parallel=False` and - `return_footprint=False`, which caused the footprint to be returned - anyway. [#227] -- +- - The infrastructure of the package has been updated in line with the - APE 17 roadmap (https://github.com/astropy/astropy-APEs/blob/main/APE17.rst). - The main changes are that the `python setup.py test` and @@ -192,7 +192,7 @@ - (https://tox.readthedocs.io) package and run `tox -e test` and - `tox -e build_docs`. It is also possible to run pytest and sphinx - directly. [#228] -- +- ## 0.6 - 2019-11-01 @@ -201,20 +201,20 @@ - `independent_celestial_slices=` argument to `reproject_interp` has - been deprecated since it is no longer needed, as transformations are - automatically done in the most efficient way possible. [#166] -- +- - Include a warning for high resolution images with `reproject_exact`, - since if the pixels are <0.05", precision issues can occur. [#200] -- +- - Added a new `reproject_and_coadd` function for doing mosaicking of - individual images, and added section in documentation about mosaicking. - [#186] -- +- - Added a new reproject.adaptive sub-package that implements the DeForest - (2004) algorithm for reprojection. [#52] -- +- - Fixed a bug that caused 'exact' reprojection results to have numerical - issues when doing identity transformations. [#190] -- +- ## 0.5.1 - 2019-09-01 @@ -225,33 +225,33 @@ - Improve parse_output_projection to make it so that the output projection - can be specified as a filename. [#150] -- +- - Fixed a bug that caused HEALPix maps in RING order to not be correctly - interpreted. [#163] -- +- - Make it possible to specify the output array for reprojection using the - `output_array=` keyword argument. [#115] -- +- ## 0.4 - 2018-01-29 - Refactored HEALPix reprojection code to use the astropy-healpix package - instead of healpy. [#139] -- +- - Added the ability to specify an output array in `reproject_interp`, which - permits the use of memory-mapped arrays and therefore provides the capability - to handle data cubes much larger than memory [#115] -- +- - Fix test 32-bit test failures. [#146] -- +- - Fix an issue with reprojecting images where there are two solutions along - the line of sight by forcing round-tripping of coordinate conversions [#129] -- +- - Explicitly define default HDU as 0 for normal reprojection and 1 for - HEALPix reprojection. [#119] -- +- - Added a function to find the optimal WCS for a set of images. [#136, #137] -- +- ## 0.3.2 - 2017-10-22 @@ -262,22 +262,22 @@ ## 0.3.1 - 2016-07-07 - Include missing license file in tarball. -- +- - Updated documentation to remove warnings about early versions. -- +- ## 0.3 - 2016-07-06 - Allow users to pass a `field=` option to `reproject_from_healpix` - to access different fields in a HEALPIX file. [#86] -- +- - Significant improvements to performance when the input data is a large - memory-mapped array. [#105] -- +- - Significant refactoring of interpolating reprojection to improve support for - n-dimensional arrays, optionally including two celestial axes (in which - case the coordinate transformation is taken into account). [#96, #102] -- +- ## 0.2 - 2015-10-29 From f9119d300071a31397e5ebe71b92768803e39eda Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 13 Nov 2023 16:28:12 +0000 Subject: [PATCH 206/366] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/psf/black: 23.10.1 → 23.11.0](https://github.com/psf/black/compare/23.10.1...23.11.0) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index f8ef414bc..ddb35bf21 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -67,7 +67,7 @@ repos: exclude: ".*(data.*|extern.*|cextern)$" - repo: https://github.com/psf/black - rev: 23.10.1 + rev: 23.11.0 hooks: - id: black From 6d5d4be1ad3b94620f21a5006d6cff29c7e4f839 Mon Sep 17 00:00:00 2001 From: Sam Van Kooten Date: Mon, 20 Nov 2023 10:29:20 -0700 Subject: [PATCH 207/366] Ensure reproject_and_coadd handles bg-matching with one input image --- reproject/mosaicking/coadd.py | 2 +- reproject/mosaicking/tests/test_coadd.py | 26 ++++++++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/reproject/mosaicking/coadd.py b/reproject/mosaicking/coadd.py index 52ff949e8..535114d92 100644 --- a/reproject/mosaicking/coadd.py +++ b/reproject/mosaicking/coadd.py @@ -243,7 +243,7 @@ def reproject_and_coadd( arrays.append(array) # If requested, try and match the backgrounds. - if match_background: + if match_background and len(arrays) > 1: offset_matrix = determine_offset_matrix(arrays) corrections = solve_corrections_sgd(offset_matrix) if background_reference: diff --git a/reproject/mosaicking/tests/test_coadd.py b/reproject/mosaicking/tests/test_coadd.py index 6e03ab6e9..4b2c21a3b 100644 --- a/reproject/mosaicking/tests/test_coadd.py +++ b/reproject/mosaicking/tests/test_coadd.py @@ -210,6 +210,32 @@ def test_coadd_background_matching(self, reproject_function): assert_allclose(array - np.mean(array), self.array - np.mean(self.array), atol=ATOL) + def test_coadd_background_matching_one_array(self, reproject_function): + # Test that background matching doesn't affect the output when there's + # only one input image. + + input_data = [(self.array, self.wcs)] + + array_matched, footprint_matched = reproject_and_coadd( + input_data, + self.wcs, + shape_out=self.array.shape, + combine_function="mean", + reproject_function=reproject_function, + match_background=True, + ) + + array, footprint = reproject_and_coadd( + input_data, + self.wcs, + shape_out=self.array.shape, + combine_function="mean", + reproject_function=reproject_function, + match_background=False, + ) + np.testing.assert_allclose(array, array_matched) + np.testing.assert_allclose(footprint, footprint_matched) + def test_coadd_background_matching_with_nan(self, reproject_function): # Test out the background matching when NaN values are present. We do # this by using three arrays with the same footprint but with different From 3612dfb77168ecd3d3f5b2110ef1ac039b5ce853 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Mon, 27 Nov 2023 11:26:46 +0000 Subject: [PATCH 208/366] Use developer version of PyERFA in devdeps job --- tox.ini | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index 25ab35d7e..a39e80d4c 100644 --- a/tox.ini +++ b/tox.ini @@ -14,7 +14,7 @@ setenv = HOME = {envtmpdir} MPLBACKEND = Agg PYTEST_COMMAND = pytest --arraydiff --arraydiff-default-format=fits --pyargs reproject --cov reproject --cov-config={toxinidir}/setup.cfg {toxinidir}/docs --remote-data - devdeps: PIP_EXTRA_INDEX_URL = https://pypi.anaconda.org/astropy/simple https://pypi.anaconda.org/scientific-python-nightly-wheels/simple + devdeps: PIP_EXTRA_INDEX_URL = https://pypi.anaconda.org/liberfa/simple https://pypi.anaconda.org/astropy/simple https://pypi.anaconda.org/scientific-python-nightly-wheels/simple py312: PIP_PRE=1 changedir = .tmp/{envname} @@ -28,6 +28,7 @@ deps = oldestdeps: dask==2021.8.* devdeps: numpy>=0.0.dev0 + devdeps: pyerfa>=0.0.dev0 devdeps: scipy>=0.0.dev0 devdeps: astropy>=0.0.dev0 devdeps: git+https://github.com/asdf-format/asdf.git#egg=asdf From 366d9d306fb80b2804459ad1d7cea971330c91f5 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Mon, 27 Nov 2023 14:20:43 +0000 Subject: [PATCH 209/366] Use astropy-healpix developer wheel --- pyproject.toml | 4 ++-- reproject/adaptive/deforest.pyx | 1 + tox.ini | 1 + 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index d3c78668d..bd7d931f7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,8 +3,8 @@ requires = ["setuptools", "setuptools_scm", "wheel", "extension-helpers", - "oldest-supported-numpy", - "cython==3.0.4"] + "numpy>=1.25,<2", + "cython>=3.0,<3.1"] build-backend = 'setuptools.build_meta' [tool.setuptools_scm] diff --git a/reproject/adaptive/deforest.pyx b/reproject/adaptive/deforest.pyx index 70d5f3970..42eba34d1 100644 --- a/reproject/adaptive/deforest.pyx +++ b/reproject/adaptive/deforest.pyx @@ -37,6 +37,7 @@ from libc.stdlib cimport qsort import sys +np.import_array() cdef double pi = np.pi cdef double nan = np.nan diff --git a/tox.ini b/tox.ini index a39e80d4c..3017662b6 100644 --- a/tox.ini +++ b/tox.ini @@ -31,6 +31,7 @@ deps = devdeps: pyerfa>=0.0.dev0 devdeps: scipy>=0.0.dev0 devdeps: astropy>=0.0.dev0 + devdeps: astropy-healpix>=0.0.dev0 devdeps: git+https://github.com/asdf-format/asdf.git#egg=asdf devdeps: git+https://github.com/astropy/asdf-astropy.git devdeps: git+https://github.com/spacetelescope/gwcs.git#egg=gwcs From b1cb18f0979ec6f215485611bebd49ec5376bbac Mon Sep 17 00:00:00 2001 From: Sam Van Kooten Date: Mon, 27 Nov 2023 14:11:24 -0700 Subject: [PATCH 210/366] Avoid cython warnings --- reproject/adaptive/deforest.pyx | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/reproject/adaptive/deforest.pyx b/reproject/adaptive/deforest.pyx index 70d5f3970..40641e286 100644 --- a/reproject/adaptive/deforest.pyx +++ b/reproject/adaptive/deforest.pyx @@ -1,4 +1,5 @@ -#cython: boundscheck=False, wraparound=False, nonecheck=False, cdivision=True +#cython: boundscheck=False, wraparound=False, nonecheck=False, cdivision=True, language_level=3str +#distutils: define_macros=NPY_NO_DEPRECATED_API=NPY_1_7_API_VERSION # Cython implementation of the image resampling method described in "On # resampling of Solar Images", C.E. DeForest, Solar Physics 2004 @@ -73,17 +74,6 @@ cdef void svd2x2_decompose(double[:,:] M, double[:,:] U, double[:] s, double[:,: V[1,1] = cos(theta) -@cython.boundscheck(False) -@cython.wraparound(False) -@cython.nonecheck(False) -@cython.cdivision(True) -cdef void mul2x2(double[:,:] A, double[:,:] B, double[:,:] C) nogil: - C[0,0] = A[0,0] * B[0,0] + A[0,1] * B[1,0] - C[0,1] = A[0,0] * B[0,1] + A[0,1] * B[1,1] - C[1,0] = A[1,0] * B[0,0] + A[1,1] * B[1,0] - C[1,1] = A[1,0] * B[0,1] + A[1,1] * B[1,1] - - @cython.boundscheck(False) @cython.wraparound(False) @cython.nonecheck(False) @@ -507,7 +497,7 @@ def map_coordinates(double[:,:,:] source, double[:,:,:] target, Ci, int max_samp cdef double[:] P3 = np.empty((2,)) cdef double[:] P4 = np.empty((2,)) cdef double top, bottom, left, right - cdef double determinant + cdef double determinant = 0 cdef bint has_sampled_this_row cdef bint sample_in_bounds with nogil: From 117bdbcb1c7f12266eb59fd0f25de91b1d80ff86 Mon Sep 17 00:00:00 2001 From: Sam Van Kooten Date: Mon, 27 Nov 2023 15:00:40 -0700 Subject: [PATCH 211/366] Incorporate cython performance hints for exceptions --- reproject/adaptive/deforest.pyx | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/reproject/adaptive/deforest.pyx b/reproject/adaptive/deforest.pyx index 40641e286..7202bec11 100644 --- a/reproject/adaptive/deforest.pyx +++ b/reproject/adaptive/deforest.pyx @@ -51,7 +51,7 @@ cdef extern from "math.h": @cython.wraparound(False) @cython.nonecheck(False) @cython.cdivision(True) -cdef void svd2x2_decompose(double[:,:] M, double[:,:] U, double[:] s, double[:,:] V) nogil: +cdef void svd2x2_decompose(double[:,:] M, double[:,:] U, double[:] s, double[:,:] V) noexcept nogil: cdef double E = (M[0,0] + M[1,1]) / 2 cdef double F = (M[0,0] - M[1,1]) / 2 cdef double G = (M[1,0] + M[0,1]) / 2 @@ -78,7 +78,7 @@ cdef void svd2x2_decompose(double[:,:] M, double[:,:] U, double[:] s, double[:,: @cython.wraparound(False) @cython.nonecheck(False) @cython.cdivision(True) -cdef double det2x2(double[:,:] M) nogil: +cdef double det2x2(double[:,:] M) noexcept nogil: return M[0,0]*M[1,1] - M[0,1]*M[1,0] @@ -86,7 +86,7 @@ cdef double det2x2(double[:,:] M) nogil: @cython.wraparound(False) @cython.nonecheck(False) @cython.cdivision(True) -cdef void svd2x2_compose(double[:,:] U, double[:] s, double[:,:] V, double[:,:] M) nogil: +cdef void svd2x2_compose(double[:,:] U, double[:] s, double[:,:] V, double[:,:] M) noexcept nogil: cdef double tmp00, tmp01, tmp10, tmp11 tmp00 = U[0,0] * s[0] tmp01 = U[0,1] * s[1] @@ -103,7 +103,7 @@ cdef void svd2x2_compose(double[:,:] U, double[:] s, double[:,:] V, double[:,:] @cython.wraparound(False) @cython.nonecheck(False) @cython.cdivision(True) -cdef double hanning_filter(double x, double y) nogil: +cdef double hanning_filter(double x, double y) noexcept nogil: x = fabs(x) y = fabs(y) if x >= 1 or y >= 1: @@ -115,7 +115,7 @@ cdef double hanning_filter(double x, double y) nogil: @cython.wraparound(False) @cython.nonecheck(False) @cython.cdivision(True) -cdef double gaussian_filter(double x, double y, double width) nogil: +cdef double gaussian_filter(double x, double y, double width) noexcept nogil: return exp(-(x*x+y*y) / (width*width) * 2) @@ -124,7 +124,7 @@ cdef double gaussian_filter(double x, double y, double width) nogil: @cython.nonecheck(False) @cython.cdivision(True) cdef double clip(double x, double vmin, double vmax, int cyclic, - int out_of_range_nearest) nogil: + int out_of_range_nearest) noexcept nogil: """Applies bounary conditions to an intended array coordinate. Specifically, if the point is outside the array bounds, this function wraps @@ -156,7 +156,7 @@ cdef double clip(double x, double vmin, double vmax, int cyclic, @cython.cdivision(True) cdef bint sample_array(double[:,:,:] source, double[:] dest, double x, double y, int x_cyclic, int y_cyclic, - bint out_of_range_nearest) nogil: + bint out_of_range_nearest) noexcept nogil: x = clip(x, 0, source.shape[2] - 1, x_cyclic, out_of_range_nearest) y = clip(y, 0, source.shape[1] - 1, y_cyclic, out_of_range_nearest) @@ -178,7 +178,7 @@ cdef bint sample_array(double[:,:,:] source, double[:] dest, cdef void calculate_jacobian(double[:, :] Ji, int center_jacobian, int yi, int xi, double[:, :, :] offset_source_x, double[:, :, :] offset_source_y, - double[:, :, :] Jx, double[:, :, :] Jy) nogil: + double[:, :, :] Jx, double[:, :, :] Jy) noexcept nogil: """ Utility function to calculate the Jacobian at one (yi, xi) location""" if center_jacobian: # Compute the Jacobian for the transformation applied to @@ -216,7 +216,7 @@ cdef int cmp_func(const void* a, const void* b) noexcept nogil: @cython.wraparound(False) @cython.nonecheck(False) @cython.cdivision(True) -cdef void sort(double[:] a) nogil: +cdef void sort(double[:] a) noexcept nogil: qsort(&a[0], a.shape[0], a.strides[0], &cmp_func) @@ -309,7 +309,7 @@ cdef void despike_jacobian(double[:, :, :, :] jacobian): cdef void fill_in_jacobian(double[:, :, :, :] jacobian, int xi, int xfirst, int xlast, int yi, int yfirst, int ylast, - double[:, :] Jmag2, double thresh) nogil: + double[:, :] Jmag2, double thresh) noexcept nogil: """ Utility function that replaces a spiking Jacobian pixel """ jacobian[yi, xi] = 0 cdef int n_contributing = 0 From d436c18af5668c94b19f90880dfd030c87ce1728 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Tue, 28 Nov 2023 13:57:41 +0000 Subject: [PATCH 212/366] Allow Numpy 2.0 and later as a build-time dependency, and use developer version of Numpy in devdeps job --- pyproject.toml | 2 +- tox.ini | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index bd7d931f7..72adcec8e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,7 +3,7 @@ requires = ["setuptools", "setuptools_scm", "wheel", "extension-helpers", - "numpy>=1.25,<2", + "numpy>=1.25", "cython>=3.0,<3.1"] build-backend = 'setuptools.build_meta' diff --git a/tox.ini b/tox.ini index 3017662b6..8274106c1 100644 --- a/tox.ini +++ b/tox.ini @@ -42,6 +42,8 @@ extras = # Don't run the more complex tests on oldestdeps because it pulls in a nest # web of dependencies much newer than our mins !oldestdeps: testall +install_command = + devdeps: python -I -m pip install {opts} {packages} --pre commands = pip freeze !oldestdeps: {env:PYTEST_COMMAND} {posargs} From d08eb19a28fcb32bdb60336d2b0ff8bb3924f6db Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Tue, 28 Nov 2023 14:01:05 +0000 Subject: [PATCH 213/366] Specify install_command for non-devdeps case --- tox.ini | 1 + 1 file changed, 1 insertion(+) diff --git a/tox.ini b/tox.ini index 8274106c1..a310e065a 100644 --- a/tox.ini +++ b/tox.ini @@ -43,6 +43,7 @@ extras = # web of dependencies much newer than our mins !oldestdeps: testall install_command = + !devdeps: python -I -m pip install {opts} {packages} devdeps: python -I -m pip install {opts} {packages} --pre commands = pip freeze From abff5c864b880b24a3f6aab4533c2c23b97a80aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Robert?= Date: Tue, 5 Dec 2023 11:07:51 +0100 Subject: [PATCH 214/366] BLD: pin extension-helpers to 1.* following upstream recommendation --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 72adcec8e..a4eebb3a6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -2,7 +2,7 @@ requires = ["setuptools", "setuptools_scm", "wheel", - "extension-helpers", + "extension-helpers==1.*", "numpy>=1.25", "cython>=3.0,<3.1"] build-backend = 'setuptools.build_meta' From 41c1440d7b048b37e184e35c50c63557d73da538 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Fri, 17 Nov 2023 22:55:23 +0000 Subject: [PATCH 215/366] Added sp-repo-review to pre-commit --- .pre-commit-config.yaml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index ddb35bf21..b20f52772 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -78,5 +78,10 @@ repos: files: ".*(high_level|mosaicking).*$" exclude: ".*(tests.*)$" + - repo: https://github.com/scientific-python/cookie + rev: 2023.11.17 + hooks: + - id: sp-repo-review + ci: autofix_prs: false From 546f24c24305239bd215cfeab8dcf8576ff7529e Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Mon, 20 Nov 2023 21:51:40 +0000 Subject: [PATCH 216/366] Migrated setup.cfg to pyproject.toml --- pyproject.toml | 127 +++++++++++++++++++++++++++++++++++++++++++++++++ setup.cfg | 114 -------------------------------------------- 2 files changed, 127 insertions(+), 114 deletions(-) delete mode 100644 setup.cfg diff --git a/pyproject.toml b/pyproject.toml index a4eebb3a6..65cb68af9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,3 +1,51 @@ +[project] +name = "reproject" +authors = [ + {name = "Thomas Robitaille", email = "thomas.robitaille@gmail.com"}, + {name = "Christoph Deil"}, + {name = "Adam Ginsburg"}, +] +license = {text = "BSD 3-Clause"} +description = "Reproject astronomical images" +urls = {Homepage = "https://reproject.readthedocs.io"} +requires-python = ">=3.8" +dependencies = [ + "numpy>=1.20", + "astropy>=5.0", + "astropy-healpix>=0.6", + "scipy>=1.5", + "dask[array]>=2021.8", + "cloudpickle", + "zarr", + "fsspec", +] +dynamic = ["version"] + +[project.readme] +file = "README.rst" +content-type = "text/x-rst" + +[project.optional-dependencies] +all = ["shapely"] +test = [ + "pytest-astropy", + "shapely>=2.0.2", # 2.0.2 fixed a bug that causes changes in test results +] +testall = [ + "shapely", + # Once a release of reproject is done for Python 3.12, + # can remove python_version specifier here + 'sunpy[map]>=2.1;python_version<"3.12" and platform_machine!="i686"', + "asdf", + "gwcs", + "pyvo", +] +docs = [ + "sphinx-astropy", + "pyvo", + "matplotlib", +] + [build-system] requires = ["setuptools", "setuptools_scm", @@ -7,6 +55,85 @@ requires = ["setuptools", "cython>=3.0,<3.1"] build-backend = 'setuptools.build_meta' +[tool.setuptools] +zip-safe = false +license-files = ["LICENSE"] +include-package-data = false + +[tool.setuptools.packages] +find = {namespaces = false} + +[tool.setuptools.package-data] +"reproject.healpix.tests" = ["data/*"] +"reproject.adaptive.tests" = ["reference/*"] +"reproject.interpolation.tests" = ["reference/*"] +"reproject.mosaicking.tests" = ["reference/*"] +"reproject.spherical_intersect" = ["overlapArea.h", "reproject_slice_c.h", "mNaN.h"] +"reproject.tests" = ["data/*"] + +[tool.extension-helpers] +use_extension_helpers = "true" + +[tool.pytest.ini_options] +minversion = "3.1" +testpaths = ['"reproject"', '"docs"'] +norecursedirs = ["build", "docs/_build"] +astropy_header = true +doctest_plus = "enabled" +text_file_format = "rst" +addopts = "--doctest-rst --arraydiff --arraydiff-default-format=fits --doctest-ignore-import-errors" +filterwarnings = [ + "error", + 'ignore:numpy\.ufunc size changed:RuntimeWarning', + 'ignore:numpy\.ndarray size changed:RuntimeWarning', + "ignore:distutils Version classes are deprecated:DeprecationWarning", + "ignore:No observer defined on WCS:astropy.utils.exceptions.AstropyUserWarning", + "ignore:unclosed file:ResourceWarning", + "ignore:The conversion of these 2D helioprojective coordinates to 3D is all NaNs", + # This is a sunpy < 4.1 issue with Python 3.11 + "ignore:'xdrlib' is deprecated and slated for removal in Python 3.13", + # This is a pyvo issue with Python 3.11 + "ignore:'cgi' is deprecated and slated for removal in Python 3.13", +] + +[tool.coverage.run] +omit = [ + "reproject/_astropy_init*", + "reproject/conftest.py", + "reproject/*setup_package*", + "reproject/tests/*", + "reproject/*/tests/*", + "reproject/extern/*", + "reproject/version*", + "*/reproject/_astropy_init*", + "*/reproject/conftest.py", + "*/reproject/*setup_package*", + "*/reproject/tests/*", + "*/reproject/*/tests/*", + "*/reproject/extern/*", + "*/reproject/version*", +] + +[tool.coverage.report] +exclude_lines = [ + # Have to re-enable the standard pragma + "pragma: no cover", + # Don't complain about packages we have installed + "except ImportError", + # Don't complain if tests don't hit assertions + "raise AssertionError", + "raise NotImplementedError", + # Don't complain about script hooks + 'def main\(.*\):', + # Ignore branches that don't pertain to this version of Python + "pragma: py{ignore_python_version}", + # Don't complain about IPython completion helper + "def _ipython_key_completions_", +] + +[tool.flake8] +max-line-length = "100" + [tool.setuptools_scm] write_to = "reproject/version.py" diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index 6dcd7b19f..000000000 --- a/setup.cfg +++ /dev/null @@ -1,114 +0,0 @@ -[metadata] -name = reproject -author = Thomas Robitaille, Christoph Deil, Adam Ginsburg -author_email = thomas.robitaille@gmail.com -license = BSD 3-Clause -license_files = LICENSE -url = https://reproject.readthedocs.io -description = Reproject astronomical images -long_description = file: README.rst -long_description_content_type = text/x-rst -edit_on_github = False -github_project = astropy/reproject - -[options] -zip_safe = False -packages = find: -python_requires = >=3.8 -setup_requires = setuptools_scm -install_requires = - numpy>=1.20 - astropy>=5.0 - astropy-healpix>=0.6 - scipy>=1.5 - dask[array]>=2021.8 - cloudpickle - zarr - fsspec - -[options.extras_require] -all = - shapely -test = - pytest-astropy - shapely>=2.0.2 # 2.0.2 fixed a bug that causes changes in test results -testall = - shapely - # Once a release of reproject is done for Python 3.12, - # can remove python_version specifier here - sunpy[map]>=2.1;python_version<"3.12" and platform_machine!="i686" - asdf - gwcs - pyvo -docs = - sphinx-astropy - pyvo - matplotlib - -[options.package_data] -reproject.healpix.tests = data/* -reproject.adaptive.tests = reference/* -reproject.interpolation.tests = reference/* -reproject.mosaicking.tests = reference/* -reproject.spherical_intersect = overlapArea.h, reproject_slice_c.h, mNaN.h -reproject.tests = data/* - -[extension-helpers] -use_extension_helpers = true - -[tool:pytest] -minversion = 3.1 -testpaths = "reproject" "docs" -norecursedirs = build docs/_build -astropy_header = true -doctest_plus = enabled -text_file_format = rst -addopts = --doctest-rst --arraydiff --arraydiff-default-format=fits --doctest-ignore-import-errors -filterwarnings = - error - ignore:numpy\.ufunc size changed:RuntimeWarning - ignore:numpy\.ndarray size changed:RuntimeWarning - ignore:distutils Version classes are deprecated:DeprecationWarning - ignore:No observer defined on WCS:astropy.utils.exceptions.AstropyUserWarning - ignore:unclosed file:ResourceWarning - ignore:The conversion of these 2D helioprojective coordinates to 3D is all NaNs - # This is a sunpy < 4.1 issue with Python 3.11 - ignore:'xdrlib' is deprecated and slated for removal in Python 3.13 - # This is a pyvo issue with Python 3.11 - ignore:'cgi' is deprecated and slated for removal in Python 3.13 - -[coverage:run] -omit = - reproject/_astropy_init* - reproject/conftest.py - reproject/*setup_package* - reproject/tests/* - reproject/*/tests/* - reproject/extern/* - reproject/version* - */reproject/_astropy_init* - */reproject/conftest.py - */reproject/*setup_package* - */reproject/tests/* - */reproject/*/tests/* - */reproject/extern/* - */reproject/version* - -[coverage:report] -exclude_lines = - # Have to re-enable the standard pragma - pragma: no cover - # Don't complain about packages we have installed - except ImportError - # Don't complain if tests don't hit assertions - raise AssertionError - raise NotImplementedError - # Don't complain about script hooks - def main\(.*\): - # Ignore branches that don't pertain to this version of Python - pragma: py{ignore_python_version} - # Don't complain about IPython completion helper - def _ipython_key_completions_ - -[flake8] -max-line-length = 100 From 18001fd4dfef57599353a26fadfce918b702ea69 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Tue, 28 Nov 2023 14:11:41 +0000 Subject: [PATCH 217/366] Implemented suggestions from sp-repo-review --- .github/dependabot.yml | 4 ++++ .github/workflows/ci_workflows.yml | 1 + .pre-commit-config.yaml | 2 +- pyproject.toml | 7 ++++--- 4 files changed, 10 insertions(+), 4 deletions(-) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 000000000..324e0c245 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,4 @@ +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" diff --git a/.github/workflows/ci_workflows.yml b/.github/workflows/ci_workflows.yml index 68a0fd9b4..e77e8208a 100644 --- a/.github/workflows/ci_workflows.yml +++ b/.github/workflows/ci_workflows.yml @@ -3,6 +3,7 @@ name: CI on: push: pull_request: + workflow_dispatch: concurrency: group: ${{ github.workflow }}-${{ github.ref }} diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index b20f52772..e42df7ba1 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -66,7 +66,7 @@ repos: ] exclude: ".*(data.*|extern.*|cextern)$" - - repo: https://github.com/psf/black + - repo: https://github.com/psf/black-pre-commit-mirror rev: 23.11.0 hooks: - id: black diff --git a/pyproject.toml b/pyproject.toml index 65cb68af9..5317f9a40 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -49,7 +49,6 @@ docs = [ [build-system] requires = ["setuptools", "setuptools_scm", - "wheel", "extension-helpers==1.*", "numpy>=1.25", "cython>=3.0,<3.1"] @@ -75,13 +74,15 @@ find = {namespaces = false} use_extension_helpers = "true" [tool.pytest.ini_options] -minversion = "3.1" +minversion = "6" +log_cli_level = "INFO" +xfail_strict = true testpaths = ['"reproject"', '"docs"'] norecursedirs = ["build", "docs/_build"] astropy_header = true doctest_plus = "enabled" text_file_format = "rst" -addopts = "--doctest-rst --arraydiff --arraydiff-default-format=fits --doctest-ignore-import-errors" +addopts = ["-ra", "--strict-config", "--strict-markers", "--doctest-rst", "--arraydiff", "--arraydiff-default-format=fits", "--doctest-ignore-import-errors"] filterwarnings = [ "error", 'ignore:numpy\.ufunc size changed:RuntimeWarning', From 6a07dcf145b48b5db24330ed8d1125d7fa485f2e Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Tue, 28 Nov 2023 14:22:48 +0000 Subject: [PATCH 218/366] More suggestions from sp-repo-review --- .pre-commit-config.yaml | 14 ++++++++++++++ pyproject.toml | 9 +++++++++ 2 files changed, 23 insertions(+) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index e42df7ba1..73e21e142 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -83,5 +83,19 @@ repos: hooks: - id: sp-repo-review + - repo: https://github.com/codespell-project/codespell + rev: v2.2.6 + hooks: + - id: codespell + args: ["--write-changes"] + additional_dependencies: + - tomli + + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: "v0.1.5" + hooks: + - id: ruff + args: ["--fix", "--show-fixes"] + ci: autofix_prs: false diff --git a/pyproject.toml b/pyproject.toml index 5317f9a40..ff19c43a9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -168,3 +168,12 @@ checks = [ "ES01", "GL08", ] + +[tool.repo-review] +ignore = [ + "MY", # ignore MyPy setting checks + "PC111", # ignore using `blacken-docs` in pre-commit + "PC140", # ignore using `mypy` in pre-commit + "PC180", # ignore using `prettier` in pre-commit + "PC901", # ignore using custom update message (we have many of the default ones in our history already) +] From af4d45a7806db05d9ebe18a9bb699fbaed930e30 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Tue, 28 Nov 2023 14:23:28 +0000 Subject: [PATCH 219/366] Add [tool.ruff] section in pyproject.toml --- pyproject.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index ff19c43a9..a41a20a5e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -177,3 +177,6 @@ ignore = [ "PC180", # ignore using `prettier` in pre-commit "PC901", # ignore using custom update message (we have many of the default ones in our history already) ] + +[tool.ruff] + From 91396c639f73a29a6bbc84f32704523463f811cf Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Tue, 28 Nov 2023 14:28:19 +0000 Subject: [PATCH 220/366] Bumped minimum Python version to 3.9 --- .github/workflows/ci_workflows.yml | 10 +++------- pyproject.toml | 3 +-- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/.github/workflows/ci_workflows.yml b/.github/workflows/ci_workflows.yml index e77e8208a..1ab37e272 100644 --- a/.github/workflows/ci_workflows.yml +++ b/.github/workflows/ci_workflows.yml @@ -15,20 +15,16 @@ jobs: uses: OpenAstronomy/github-actions-workflows/.github/workflows/tox.yml@v1 with: envs: | - - macos: py38-test-oldestdeps - - macos: py39-test + - macos: py39-test-oldestdeps - macos: py310-test - macos: py311-test - macos: py312-test - - linux: py38-test-oldestdeps - - linux: py38-test - - linux: py39-test + - linux: py39-test-oldestdeps - linux: py310-test - linux: py311-test - linux: py312-test - linux: py312-test-devdeps - - windows: py38-test-oldestdeps - - windows: py39-test + - windows: py39-test-oldestdeps - windows: py310-test - windows: py311-test - windows: py312-test diff --git a/pyproject.toml b/pyproject.toml index a41a20a5e..9815c1a78 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,7 +8,7 @@ authors = [ license = {text = "BSD 3-Clause"} description = "Reproject astronomical images" urls = {Homepage = "https://reproject.readthedocs.io"} -requires-python = ">=3.8" +requires-python = ">=3.9" dependencies = [ "numpy>=1.20", "astropy>=5.0", @@ -179,4 +179,3 @@ ignore = [ ] [tool.ruff] - From aa1f88e0694dd268ce2a8272401bcdb0409727bd Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Tue, 28 Nov 2023 14:29:44 +0000 Subject: [PATCH 221/366] Initial codespell and ruff changes --- RELEASE_INSTRUCTIONS.md | 2 +- docs/celestial.rst | 2 +- docs/dask.rst | 2 +- docs/mosaicking.rst | 2 +- reproject/__init__.py | 2 +- reproject/adaptive/deforest.pyx | 2 +- reproject/interpolation/core.py | 2 +- reproject/interpolation/high_level.py | 2 -- reproject/mosaicking/tests/test_wcs_helpers.py | 2 -- reproject/tests/test_utils.py | 2 -- reproject/wcs_utils.py | 1 - 11 files changed, 7 insertions(+), 14 deletions(-) diff --git a/RELEASE_INSTRUCTIONS.md b/RELEASE_INSTRUCTIONS.md index f4e5b7694..a7891fba9 100644 --- a/RELEASE_INSTRUCTIONS.md +++ b/RELEASE_INSTRUCTIONS.md @@ -2,7 +2,7 @@ Making a New Reproject Release ============================== A new release of reproject is almost fully automated. -As a mantainer it should be nice and simple to do, especially if all merged PRs +As a maintainer it should be nice and simple to do, especially if all merged PRs have nice titles and are correctly labelled. Here is the process to follow to make a new release: diff --git a/docs/celestial.rst b/docs/celestial.rst index 20c6f6528..90491ad94 100644 --- a/docs/celestial.rst +++ b/docs/celestial.rst @@ -448,7 +448,7 @@ Multiple images with the same coordinates If you have multiple images with the exact same coordinate system (e.g. a raw image and a corresponding processed image) and want to reproject both to the same output frame, it is faster to compute the coordinate mapping between input -and output pixels only once and re-use this mapping for each reprojection. This +and output pixels only once and reuse this mapping for each reprojection. This is supported by passing multiple input images as an additional dimension in the input data array. When the input array contains more dimensions than the input WCS describes, the extra leading dimensions are taken to represent separate diff --git a/docs/dask.rst b/docs/dask.rst index e23ed67fb..90075f67d 100644 --- a/docs/dask.rst +++ b/docs/dask.rst @@ -76,7 +76,7 @@ processes to use. Output dask arrays ------------------ -By default, the reprojection functions will do the computation immmediately and +By default, the reprojection functions will do the computation immediately and return Numpy arrays for the reprojected array and optionally the footprint (this is regardless of whether dask or Numpy arrays were passed in, or any of the parallelization options above). However, by setting ``return_type='dask'``, you diff --git a/docs/mosaicking.rst b/docs/mosaicking.rst index 49d4094d9..e06cdd149 100644 --- a/docs/mosaicking.rst +++ b/docs/mosaicking.rst @@ -171,7 +171,7 @@ Pixel resolution By default, the final mosaic will have the pixel resolution (i.e. the pixel scale along the pixel axes) of the highest resolution input image, but this can -be overriden using the ``resolution=`` keyword argument: +be overridden using the ``resolution=`` keyword argument: .. doctest-requires:: pyvo diff --git a/reproject/__init__.py b/reproject/__init__.py index 2cef31f4c..85e446232 100644 --- a/reproject/__init__.py +++ b/reproject/__init__.py @@ -6,4 +6,4 @@ from .healpix import reproject_from_healpix, reproject_to_healpix # noqa from .interpolation import reproject_interp # noqa from .spherical_intersect import reproject_exact # noqa -from .version import __version__ +from .version import __version__ # noqa diff --git a/reproject/adaptive/deforest.pyx b/reproject/adaptive/deforest.pyx index 8dc443729..4876576bd 100644 --- a/reproject/adaptive/deforest.pyx +++ b/reproject/adaptive/deforest.pyx @@ -126,7 +126,7 @@ cdef double gaussian_filter(double x, double y, double width) noexcept nogil: @cython.cdivision(True) cdef double clip(double x, double vmin, double vmax, int cyclic, int out_of_range_nearest) noexcept nogil: - """Applies bounary conditions to an intended array coordinate. + """Applies boundary conditions to an intended array coordinate. Specifically, if the point is outside the array bounds, this function wraps the coordinate if the boundary is periodic, or clamps to the nearest valid diff --git a/reproject/interpolation/core.py b/reproject/interpolation/core.py index c4adec34b..a1909ce28 100644 --- a/reproject/interpolation/core.py +++ b/reproject/interpolation/core.py @@ -110,7 +110,7 @@ def _reproject_full( copy=False, ) pixel_out = [p.ravel() for p in pixel_out] - # For each pixel in the ouput array, get the pixel value in the input WCS + # For each pixel in the output array, get the pixel value in the input WCS if roundtrip_coords: pixel_in = pixel_to_pixel_with_roundtrip(wcs_out, wcs_in, *pixel_out[::-1])[::-1] else: diff --git a/reproject/interpolation/high_level.py b/reproject/interpolation/high_level.py index 64036a868..c6a5f6944 100644 --- a/reproject/interpolation/high_level.py +++ b/reproject/interpolation/high_level.py @@ -1,7 +1,5 @@ # Licensed under a 3-clause BSD style license - see LICENSE.rst -import os -from astropy.utils import deprecated_renamed_argument from ..common import _reproject_dispatcher from ..utils import parse_input_data, parse_output_projection diff --git a/reproject/mosaicking/tests/test_wcs_helpers.py b/reproject/mosaicking/tests/test_wcs_helpers.py index 43b1f8dff..f0f77cf47 100644 --- a/reproject/mosaicking/tests/test_wcs_helpers.py +++ b/reproject/mosaicking/tests/test_wcs_helpers.py @@ -5,7 +5,6 @@ from astropy import units as u from astropy.coordinates import FK5, Galactic, SkyCoord from astropy.io import fits -from astropy.nddata import NDData from astropy.wcs import WCS from astropy.wcs.wcsapi import BaseHighLevelWCS, BaseLowLevelWCS, HighLevelWCSWrapper from numpy.testing import assert_allclose, assert_equal @@ -324,7 +323,6 @@ def test_solar_wcs(): pytest.importorskip("sunpy", minversion="2.1.0") # Make sure the WCS <-> frame functions are registered - import sunpy.coordinates wcs_ref = WCS(fits.Header.fromstring(SOLAR_HEADER, sep="\n")) diff --git a/reproject/tests/test_utils.py b/reproject/tests/test_utils.py index c0f27126a..1c5ad3f78 100644 --- a/reproject/tests/test_utils.py +++ b/reproject/tests/test_utils.py @@ -2,8 +2,6 @@ import numpy as np import pytest from astropy.io import fits -from astropy.nddata import NDData -from astropy.utils.data import get_pkg_data_filename from astropy.wcs import WCS from reproject.conftest import set_wcs_array_shape diff --git a/reproject/wcs_utils.py b/reproject/wcs_utils.py index 9fe71862e..1a6baf76c 100644 --- a/reproject/wcs_utils.py +++ b/reproject/wcs_utils.py @@ -8,7 +8,6 @@ from astropy.coordinates import SkyCoord from astropy.wcs import WCS from astropy.wcs.utils import pixel_to_pixel -from astropy.wcs.wcsapi.high_level_api import BaseHighLevelWCS __all__ = ["has_celestial", "pixel_to_pixel_with_roundtrip"] From 728daec0a18da8f16f1617e868d2ef2d640538d1 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Tue, 28 Nov 2023 14:38:00 +0000 Subject: [PATCH 222/366] Fix all pre-commit issues --- .pre-commit-config.yaml | 8 -------- docs/celestial.rst | 2 +- docs/conf.py | 2 +- pyproject.toml | 12 ++++++++++++ reproject/adaptive/tests/test_core.py | 2 +- reproject/common.py | 2 +- reproject/healpix/tests/test_healpix.py | 2 +- reproject/healpix/utils.py | 7 ++++++- reproject/mosaicking/tests/test_coadd.py | 2 +- reproject/spherical_intersect/core.py | 1 + reproject/utils.py | 4 ++-- 11 files changed, 27 insertions(+), 17 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 73e21e142..2cf19ab53 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -11,14 +11,6 @@ repos: - id: trailing-whitespace exclude: ".*(data.*|extern.*|licenses.*|.*.fits)$" - - repo: https://github.com/pycqa/isort - rev: 5.12.0 - hooks: - - id: isort - name: isort (python) - args: - - "--filter-files" - - repo: https://github.com/asottile/pyupgrade rev: v3.15.0 hooks: diff --git a/docs/celestial.rst b/docs/celestial.rst index 90491ad94..633be5def 100644 --- a/docs/celestial.rst +++ b/docs/celestial.rst @@ -61,7 +61,7 @@ Currently, this package implements :ref:`interpolation`, However, the :ref:`adaptive resampling` algorithm provides an option to conserve flux by appropriately rescaling each output - pixel. With this option, an image in flux units need not be coverted + pixel. With this option, an image in flux units need not be converted to surface brightness. .. _common: diff --git a/docs/conf.py b/docs/conf.py index f2cbca48d..73d5bdc6b 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -24,9 +24,9 @@ # Thus, any C-extensions that are needed to build the documentation will *not* # be accessible, and the documentation will not build correctly. +import datetime import os import sys -import datetime from importlib import import_module try: diff --git a/pyproject.toml b/pyproject.toml index 9815c1a78..c75cc61e5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -176,6 +176,18 @@ ignore = [ "PC140", # ignore using `mypy` in pre-commit "PC180", # ignore using `prettier` in pre-commit "PC901", # ignore using custom update message (we have many of the default ones in our history already) + "PC170", # ignore using pygrep + "PY005", # ignore having a tests/ folder ] [tool.ruff] + +[tool.ruff.lint] +extend-select = [ + "B", # flake8-bugbear + "I", # isort + "UP", # pyupgrade +] + +[tool.ruff.lint.extend-per-file-ignores] +"docs/conf.py" = ["F405"] # Sphinx injects variables into namespace diff --git a/reproject/adaptive/tests/test_core.py b/reproject/adaptive/tests/test_core.py index 9eaa56884..42c83edf9 100644 --- a/reproject/adaptive/tests/test_core.py +++ b/reproject/adaptive/tests/test_core.py @@ -350,7 +350,7 @@ def test_reproject_adaptive_flux_conservation(): shears_x = np.linspace(-0.7, 0.7, 3) shears_y = np.linspace(-0.2, 0.2, 3) - for rot, scale_x, scale_y, trans_x, trans_y, shear_x, shear_y in product( + for rot, scale_x, scale_y, _, _, shear_x, shear_y in product( rotations, scales_x, scales_y, translations_x, translations_y, shears_x, shears_y ): wcs_out.wcs.cdelt = scale_x, scale_y diff --git a/reproject/common.py b/reproject/common.py index d53884af4..2bc2279de 100644 --- a/reproject/common.py +++ b/reproject/common.py @@ -1,6 +1,6 @@ import os -import uuid import tempfile +import uuid import dask import dask.array as da diff --git a/reproject/healpix/tests/test_healpix.py b/reproject/healpix/tests/test_healpix.py index 84d4a4e7d..a1e2a3fd6 100644 --- a/reproject/healpix/tests/test_healpix.py +++ b/reproject/healpix/tests/test_healpix.py @@ -1,7 +1,7 @@ # Licensed under a 3-clause BSD style license - see LICENSE.rst -import os import itertools +import os import numpy as np import pytest diff --git a/reproject/healpix/utils.py b/reproject/healpix/utils.py index 1fd962e08..cd8693da1 100644 --- a/reproject/healpix/utils.py +++ b/reproject/healpix/utils.py @@ -1,5 +1,10 @@ import numpy as np -from astropy.coordinates import ICRS, BaseCoordinateFrame, Galactic, frame_transform_graph +from astropy.coordinates import ( + ICRS, + BaseCoordinateFrame, + Galactic, + frame_transform_graph, +) from astropy.io import fits from astropy.io.fits import BinTableHDU, TableHDU diff --git a/reproject/mosaicking/tests/test_coadd.py b/reproject/mosaicking/tests/test_coadd.py index 4b2c21a3b..9738cdf2f 100644 --- a/reproject/mosaicking/tests/test_coadd.py +++ b/reproject/mosaicking/tests/test_coadd.py @@ -179,7 +179,7 @@ def test_coadd_background_matching(self, reproject_function): input_data = self._get_tiles(self._overlapping_views) - for array, wcs in input_data: + for array, _ in input_data: array += random.uniform(-3, 3) # First check that without background matching the arrays don't match diff --git a/reproject/spherical_intersect/core.py b/reproject/spherical_intersect/core.py index f78c663c1..59963f3ae 100644 --- a/reproject/spherical_intersect/core.py +++ b/reproject/spherical_intersect/core.py @@ -37,6 +37,7 @@ def _reproject_celestial( "issues with images that have resolutions below ~0.05 " "arcsec, so the results may not be accurate.", UserWarning, + stacklevel=2, ) # Convert input array to float values. If this comes from a FITS, it might have diff --git a/reproject/utils.py b/reproject/utils.py index 3b5d0d905..c8f1e2ef9 100644 --- a/reproject/utils.py +++ b/reproject/utils.py @@ -1,6 +1,6 @@ import os -import uuid import tempfile +import uuid from pathlib import Path import astropy.nddata @@ -204,7 +204,7 @@ def parse_output_projection(output_projection, shape_in=None, shape_out=None, ou raise ValueError( "Need to specify shape since output header " "does not contain complete shape information" - ) + ) from None elif isinstance(output_projection, (BaseLowLevelWCS, BaseHighLevelWCS)): if isinstance(output_projection, BaseLowLevelWCS) and not isinstance( output_projection, BaseHighLevelWCS From 6f7a2398271e126689c2e978107550e46e216ce6 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Tue, 28 Nov 2023 19:23:45 +0000 Subject: [PATCH 223/366] Changed config filename in tox.ini for coverage --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index a310e065a..55b88a2b2 100644 --- a/tox.ini +++ b/tox.ini @@ -13,7 +13,7 @@ passenv = setenv = HOME = {envtmpdir} MPLBACKEND = Agg - PYTEST_COMMAND = pytest --arraydiff --arraydiff-default-format=fits --pyargs reproject --cov reproject --cov-config={toxinidir}/setup.cfg {toxinidir}/docs --remote-data + PYTEST_COMMAND = pytest --arraydiff --arraydiff-default-format=fits --pyargs reproject --cov reproject --cov-config={toxinidir}/pyproject.toml {toxinidir}/docs --remote-data devdeps: PIP_EXTRA_INDEX_URL = https://pypi.anaconda.org/liberfa/simple https://pypi.anaconda.org/astropy/simple https://pypi.anaconda.org/scientific-python-nightly-wheels/simple py312: PIP_PRE=1 changedir = From ff8d5f253bb2b3a43dcd0cc7e2818ad9b0c974a1 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Thu, 7 Dec 2023 15:50:22 +0000 Subject: [PATCH 224/366] Cleaned up documentation config --- docs/conf.py | 62 +++++++--------------------------------------------- 1 file changed, 8 insertions(+), 54 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 73d5bdc6b..e749204d0 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,33 +1,9 @@ # Licensed under a 3-clause BSD style license - see LICENSE.rst -# -# Astropy documentation build configuration file. -# -# This file is execfile()d with the current directory set to its containing dir. -# -# Note that not all possible configuration values are present in this file. -# -# All configuration values have a default. Some values are defined in -# the global Astropy configuration which is loaded here before anything else. -# See astropy.sphinx.conf for which values are set there. - -# If extensions (or modules to document with autodoc) are in another directory, -# add these directories to sys.path here. If the directory is relative to the -# documentation root, use os.path.abspath to make it absolute, like shown here. -# sys.path.insert(0, os.path.abspath('..')) -# IMPORTANT: the above commented section was generated by sphinx-quickstart, but -# is *NOT* appropriate for astropy or Astropy affiliated packages. It is left -# commented out with this explanation to make it clear why this should not be -# done. If the sys.path entry above is added, when the astropy.sphinx.conf -# import occurs, it will import the *source* version of astropy instead of the -# version installed (if invoked as "make html" or directly with sphinx), or the -# version in the build directory (if "python setup.py build_sphinx" is used). -# Thus, any C-extensions that are needed to build the documentation will *not* -# be accessible, and the documentation will not build correctly. import datetime import os import sys -from importlib import import_module +from importlib import import_module, metadata try: from sphinx_astropy.conf.v1 import * # noqa @@ -35,14 +11,6 @@ print("ERROR: the documentation requires the sphinx-astropy package to be installed") sys.exit(1) -# Get configuration information from setup.cfg -from configparser import ConfigParser - -conf = ConfigParser() - -conf.read([os.path.join(os.path.dirname(__file__), "..", "setup.cfg")]) -setup_cfg = dict(conf.items("metadata")) - # -- General configuration ---------------------------------------------------- # By default, highlight as Python 3. @@ -62,22 +30,21 @@ # -- Project information ------------------------------------------------------ +package_info = metadata.metadata('reproject') + # This does not *have* to match the package name, but typically does -project = setup_cfg["name"] -author = setup_cfg["author"] -copyright = "{}, {}".format(datetime.datetime.now().year, setup_cfg["author"]) +project = package_info["Name"] +author = package_info["Author"] +copyright = "{}, {}".format(datetime.datetime.now().year, package_info["Author"]) # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. -import_module(setup_cfg["name"]) -package = sys.modules[setup_cfg["name"]] - # The short X.Y version. -version = package.__version__.split("-", 1)[0] +version = package_info['Version'].split("-", 1)[0] # The full version, including alpha/beta/rc tags. -release = package.__version__ +release = package_info['Version'] # -- Options for HTML output --------------------------------------------------- @@ -140,19 +107,6 @@ ## -- Options for the edit_on_github extension ---------------------------------------- -if eval(setup_cfg.get("edit_on_github")): - extensions += ["astropy_helpers.sphinx.ext.edit_on_github"] - - versionmod = __import__(setup_cfg["package_name"] + ".version") - edit_on_github_project = setup_cfg["github_project"] - if versionmod.version.release: - edit_on_github_branch = "v" + versionmod.version.version - else: - edit_on_github_branch = "main" - - edit_on_github_source_root = "" - edit_on_github_doc_root = "docs" - nitpicky = True plot_rcparams = {} From 7f012fcb0233a63cdc09836e0ac0a34537c38d57 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Thu, 7 Dec 2023 16:08:50 +0000 Subject: [PATCH 225/366] Fixed code style --- docs/conf.py | 9 ++++----- docs/installation.rst | 2 +- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index e749204d0..e673683ed 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,9 +1,8 @@ # Licensed under a 3-clause BSD style license - see LICENSE.rst import datetime -import os import sys -from importlib import import_module, metadata +from importlib import metadata try: from sphinx_astropy.conf.v1 import * # noqa @@ -30,7 +29,7 @@ # -- Project information ------------------------------------------------------ -package_info = metadata.metadata('reproject') +package_info = metadata.metadata("reproject") # This does not *have* to match the package name, but typically does project = package_info["Name"] @@ -42,9 +41,9 @@ # built documents. # The short X.Y version. -version = package_info['Version'].split("-", 1)[0] +version = package_info["Version"].split("-", 1)[0] # The full version, including alpha/beta/rc tags. -release = package_info['Version'] +release = package_info["Version"] # -- Options for HTML output --------------------------------------------------- diff --git a/docs/installation.rst b/docs/installation.rst index 317c564a5..1d7731e3f 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -9,7 +9,7 @@ Requirements This package has the following hard run time dependencies: -* `Python `__ 3.8 or later +* `Python `__ 3.9 or later * `Numpy `__ 1.20 or later From fc5f771bf1944bb4a5af82d70019ccbb928b55a3 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Thu, 7 Dec 2023 16:25:09 +0000 Subject: [PATCH 226/366] Don't include shapeley in basic test dependencies --- pyproject.toml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index c75cc61e5..5e1340a0e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -29,10 +29,9 @@ content-type = "text/x-rst" all = ["shapely"] test = [ "pytest-astropy", - "shapely>=2.0.2", # 2.0.2 fixed a bug that causes changes in test results ] testall = [ - "shapely", + "shapely>=2.0.2", # 2.0.2 fixed a bug that causes changes in test results # Once a release of reproject is done for Python 3.12, # can remove python_version specifier here 'sunpy[map]>=2.1;python_version<"3.12" and platform_machine!="i686"', From cce445dfa5475b5d831a5d5d5dac8764913b38dd Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Thu, 7 Dec 2023 16:35:59 +0000 Subject: [PATCH 227/366] Added dependabot schedule --- .github/dependabot.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 324e0c245..8ac6b8c49 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -2,3 +2,5 @@ version: 2 updates: - package-ecosystem: "github-actions" directory: "/" + schedule: + interval: "monthly" From 3ea5b63dca985b4fdf14e635b0868eac163a593e Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Thu, 7 Dec 2023 16:36:41 +0000 Subject: [PATCH 228/366] Remove pyupgrade from pre-commit --- .pre-commit-config.yaml | 7 ------- 1 file changed, 7 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 2cf19ab53..9a4fd23f3 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -11,13 +11,6 @@ repos: - id: trailing-whitespace exclude: ".*(data.*|extern.*|licenses.*|.*.fits)$" - - repo: https://github.com/asottile/pyupgrade - rev: v3.15.0 - hooks: - - id: pyupgrade - args: ["--py38-plus"] - exclude: ".*(extern.*|_parsetab.py|_lextab.py)$" - # We list the warnings/errors to check for here rather than in setup.cfg because # we don't want these options to apply whenever anyone calls flake8 from the # command-line or their code editor - in this case all warnings/errors should be From 2cd2476f80d154b6f190d0f839dee9350ea37f30 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Thu, 7 Dec 2023 22:06:02 +0000 Subject: [PATCH 229/366] Don't include testall dependencies for -devdeps job --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index 55b88a2b2..8bda82412 100644 --- a/tox.ini +++ b/tox.ini @@ -41,7 +41,7 @@ extras = test # Don't run the more complex tests on oldestdeps because it pulls in a nest # web of dependencies much newer than our mins - !oldestdeps: testall + !oldestdeps-!devdeps: testall install_command = !devdeps: python -I -m pip install {opts} {packages} devdeps: python -I -m pip install {opts} {packages} --pre From 9879c67512be87573b6a87a748de2b58cc531a33 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 8 Dec 2023 09:53:04 +0000 Subject: [PATCH 230/366] Bump actions/checkout from 2 to 4 Bumps [actions/checkout](https://github.com/actions/checkout) from 2 to 4. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v2...v4) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/update-changelog.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/update-changelog.yaml b/.github/workflows/update-changelog.yaml index 1432b5e02..a496feb88 100644 --- a/.github/workflows/update-changelog.yaml +++ b/.github/workflows/update-changelog.yaml @@ -14,7 +14,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v2 + uses: actions/checkout@v4 with: ref: main From 2b7ce284b9c6fbd679a4f9a2497dca7ade61b561 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 8 Dec 2023 09:53:08 +0000 Subject: [PATCH 231/366] Bump stefanzweifel/git-auto-commit-action from 4 to 5 Bumps [stefanzweifel/git-auto-commit-action](https://github.com/stefanzweifel/git-auto-commit-action) from 4 to 5. - [Release notes](https://github.com/stefanzweifel/git-auto-commit-action/releases) - [Changelog](https://github.com/stefanzweifel/git-auto-commit-action/blob/master/CHANGELOG.md) - [Commits](https://github.com/stefanzweifel/git-auto-commit-action/compare/v4...v5) --- updated-dependencies: - dependency-name: stefanzweifel/git-auto-commit-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/update-changelog.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/update-changelog.yaml b/.github/workflows/update-changelog.yaml index 1432b5e02..59b05f255 100644 --- a/.github/workflows/update-changelog.yaml +++ b/.github/workflows/update-changelog.yaml @@ -26,7 +26,7 @@ jobs: path-to-changelog: CHANGES.md - name: Commit updated CHANGELOG - uses: stefanzweifel/git-auto-commit-action@v4 + uses: stefanzweifel/git-auto-commit-action@v5 with: branch: main commit_message: Update CHANGELOG From 6dc71c13246c50f7d596370b5c7a129876223d08 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Mon, 27 Nov 2023 14:37:16 +0000 Subject: [PATCH 232/366] Add configuration for nightly wheels --- .github/workflows/ci_workflows.yml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/.github/workflows/ci_workflows.yml b/.github/workflows/ci_workflows.yml index 1ab37e272..7186ffc74 100644 --- a/.github/workflows/ci_workflows.yml +++ b/.github/workflows/ci_workflows.yml @@ -1,6 +1,9 @@ name: CI on: + schedule: + # run every day at 4am UTC + - cron: '0 4 * * *' push: pull_request: workflow_dispatch: @@ -45,5 +48,16 @@ jobs: - cp*-macosx_x86_64 - cp*-macosx_arm64 - cp*-win_amd64 + + # Developer wheels (use Numpy dev to build) + upload_to_anaconda: ${{ (github.event_name == 'schedule' || github.event_name == 'workflow_dispatch') }} + anaconda_user: astropy + anaconda_package: reproject + anaconda_keep_n_latest: 10 + env: | + CIBW_BEFORE_BUILD: '${{ ((github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' || github.event_name == 'pull_request') && 'pip install --pre --extra-index-url https://pypi.anaconda.org/scientific-python-nightly-wheels/simple setuptools setuptools_scm numpy>=0.0dev0 extension-helpers') || '' }}' + CIBW_BUILD_FRONTEND: '${{ ((github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' || github.event_name == 'pull_request') && 'pip; args: --no-build-isolation') || 'build' }}' + secrets: pypi_token: ${{ secrets.pypi_token }} + anaconda_token: ${{ secrets.anaconda_token }} From d934f178f592dc7c9092957e8331b902901215d3 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Fri, 8 Dec 2023 11:31:08 +0000 Subject: [PATCH 233/366] Install Cython --- .github/workflows/ci_workflows.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci_workflows.yml b/.github/workflows/ci_workflows.yml index 7186ffc74..7e88f484d 100644 --- a/.github/workflows/ci_workflows.yml +++ b/.github/workflows/ci_workflows.yml @@ -55,7 +55,7 @@ jobs: anaconda_package: reproject anaconda_keep_n_latest: 10 env: | - CIBW_BEFORE_BUILD: '${{ ((github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' || github.event_name == 'pull_request') && 'pip install --pre --extra-index-url https://pypi.anaconda.org/scientific-python-nightly-wheels/simple setuptools setuptools_scm numpy>=0.0dev0 extension-helpers') || '' }}' + CIBW_BEFORE_BUILD: '${{ ((github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' || github.event_name == 'pull_request') && 'pip install --pre --extra-index-url https://pypi.anaconda.org/scientific-python-nightly-wheels/simple setuptools setuptools_scm numpy>=0.0dev0 extension-helpers cython') || '' }}' CIBW_BUILD_FRONTEND: '${{ ((github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' || github.event_name == 'pull_request') && 'pip; args: --no-build-isolation') || 'build' }}' secrets: From e46fbd736268f62b3c162dbe67ae597fb5f7ea66 Mon Sep 17 00:00:00 2001 From: Sam Van Kooten Date: Mon, 20 Nov 2023 14:30:29 -0700 Subject: [PATCH 234/366] Fix mosaicing with 'first' and match_background --- reproject/mosaicking/coadd.py | 2 +- reproject/mosaicking/tests/test_coadd.py | 33 ++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/reproject/mosaicking/coadd.py b/reproject/mosaicking/coadd.py index 535114d92..5d65af9fe 100644 --- a/reproject/mosaicking/coadd.py +++ b/reproject/mosaicking/coadd.py @@ -280,7 +280,7 @@ def reproject_and_coadd( elif combine_function in ("first", "last", "min", "max"): for array in arrays: if combine_function == "first": - mask = output_footprint[array.view_in_original_array] == 0 + mask = (output_footprint[array.view_in_original_array] == 0) & (array.footprint > 0) elif combine_function == "last": mask = array.footprint > 0 elif combine_function == "min": diff --git a/reproject/mosaicking/tests/test_coadd.py b/reproject/mosaicking/tests/test_coadd.py index 9738cdf2f..9827a4356 100644 --- a/reproject/mosaicking/tests/test_coadd.py +++ b/reproject/mosaicking/tests/test_coadd.py @@ -236,6 +236,39 @@ def test_coadd_background_matching_one_array(self, reproject_function): np.testing.assert_allclose(array, array_matched) np.testing.assert_allclose(footprint, footprint_matched) + @pytest.mark.parametrize("combine_function", ["first", "last", "min", "max", "sum"]) + @pytest.mark.parametrize("match_background", [True, False]) + def test_footprint_correct(self, reproject_function, combine_function, match_background): + # Test that the output array is zero outside the returned footprint + # We're running this test over a somewhat large grid of paramters, so + # cut down the array size to avoid increasing the total test runtime + # too much. + slice = np.s_[::3, ::3] + wcs1 = self.wcs[slice] + wcs2 = self.wcs.deepcopy() + # Add a 45-degree rotation + wcs2.wcs.pc = np.array([[0.5, -0.5], [0.5, 0.5]]) + + wcs_out = wcs1.deepcopy() + # Expand the output WCS to go beyond the input images + wcs_out.wcs.cdelt = 2 * wcs1.wcs.cdelt[0], 2 * wcs1.wcs.cdelt[1] + + # Ensure the input data is fully non-zero, so we can tell where data + # got projected to in the output image. + array1 = np.full_like(self.array[slice], 2) + array2 = array1 + 5 + + array, footprint = reproject_and_coadd( + [(array1, wcs1), (array2, wcs2)], + wcs_out, + shape_out=self.array.shape, + combine_function=combine_function, + reproject_function=reproject_function, + match_background=match_background, + ) + + assert np.all((array != 0) == (footprint > 0)) + def test_coadd_background_matching_with_nan(self, reproject_function): # Test out the background matching when NaN values are present. We do # this by using three arrays with the same footprint but with different From 31c420533e4c86b0e44b988485f1f87dd7bf8ac5 Mon Sep 17 00:00:00 2001 From: Sam Van Kooten Date: Mon, 20 Nov 2023 14:33:39 -0700 Subject: [PATCH 235/366] Ensure "empty" output pixels come out as 0, not nan, for "mean" moasic --- reproject/mosaicking/coadd.py | 1 + .../tests/reference/test_coadd_solar_map.fits | Bin 264960 -> 264960 bytes reproject/mosaicking/tests/test_coadd.py | 4 ++-- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/reproject/mosaicking/coadd.py b/reproject/mosaicking/coadd.py index 5d65af9fe..1e68e2ce2 100644 --- a/reproject/mosaicking/coadd.py +++ b/reproject/mosaicking/coadd.py @@ -276,6 +276,7 @@ def reproject_and_coadd( if combine_function == "mean": with np.errstate(invalid="ignore"): output_array /= output_footprint + output_array[output_footprint == 0] = 0 elif combine_function in ("first", "last", "min", "max"): for array in arrays: diff --git a/reproject/mosaicking/tests/reference/test_coadd_solar_map.fits b/reproject/mosaicking/tests/reference/test_coadd_solar_map.fits index 1ec779a00ddf154d927a0b21ee804362dd66b7f8..7d8747d2672f6b851003fa423109a20b6043b216 100644 GIT binary patch delta 130451 zcmZ6zc{o+?8$FyvqEw0o(x6hIl8>Q5EhWknAtjl|GdZJkoWVBBn2%D&0_4Chj_Sxs`y`R0$v+i}Td)*?lCq-sYDzQ?bto^_(>vBJ( zHx=AE;f4kht&j8;j)%l>uZ}5FX&q#lofgk-= za5(b>`1v^lQ~Otde@#L;)jk0KZ3LCEOjQ{I#mqBp1x*m>7BhF>@PJ@b>0Hk*TRk>?A!4oeRp*T zCmZYj+@=Vj-q=sai`GJDIpt!c)iww{oEm1I*Mg9Db>QdjTnLlwP9C@Oh49GVcArC1 z2+Qt`b9{XP!dvB=x(60Q_^fRCUxnike!BLCSN0mhzdd91-iMk2vKrw%Jz3!5T#wu-Y_}~qP@b5!oRqiQbda3L`A6|7eTcTw_Ide}ncV4_ z7!-B}!ur+fR~Z%%RvhWw)iVO&`6*x15E{B3huhXOR^L~hCzYpr*kSm?C zD^4>!1tLY5`dfQL_#?CP@|qe5``m9`cPb~hcK_7VZ@xj;Z1Zq_RXl`M!k<%%V<9Y_ zLkscalMC4R)#cMD2ow8UJM_pXnz$)PqXEKza>;ug@@Thq9@foW1mU_2@6|IXHS(;R z#2dJF5Kc=j33UL3qubg(-;6}qr;=wQ3fmFZKE2_|qBMjxOujI3s0v|c4*gImn}o2e zy0N{pSO}Ae6R4J#2n%T4a+*uNXMc@Tagppdc3=G_s{vtJ`^zAuL1bIJin4QUjAS$%e{g@gAfoJ>}hh9Qpd(o{q$_#?# zY4h~djzgfno>KEn0e_?|W=L;7_%B@!*Q{~?zo9;>U+XmZg$tu~N3y|BPJUvi<^q1` zPv`Mx$g*>hjFX>m82pvfT&!Jfz@OFLP9^1#bvXLAwERCs@Or-ns{5<~@2>aPaf(gg zUDUc{@Ba?G1BUb`3keP|^1sya?t!=NQR70xXz-@IQVH-n2kyJa3riDbf?IQ&c0a`o z+C$`PT01Ry%gh3I?pN!W{9thYd|I*eQ8qZw9H|)|gHmuR_3urX zkq%C7W`)9ee{iH(*B0D74vzOapC{5Lf^f$fMgO*fqu;#1aHb_V>P_<(yx$Jas8bH@ zH(!GNNzgq0#5J&=uNpC5rU>?3dGB3b!C=?6pVK~N2X=Pp@ z6UKIfeUx%E-mz~j*oU6P>Q1mESHA6tk<~A7bN^1sER6y8xQ@ru9jC!oZF_ZBy&UZ6 z9~XYP7Yg>AgEqfRQ@~#Evev4!73@W)FE2}+MaCZ)zh5MPy|CfGpKTWr7{6$g+4;K& z^qBig;{6$cON}=_6&WDlw?|gSv{?wCZlrsBxRH;5l$)AfhZ+&!FzgqSYK?#iswyvj z%fbKV=$$b}y6~4atUT|L3IC;Qd{4yefZsFEBVPMT;TNkhD)C+r{1$D|GQB+(zBj)) z%!(TaU*GQ|PYe9ur^wm4vyTIRM&Yl%?RyZ=p>*|CwjqLS%D%7p_yQAy>zXyU%QHb+ z#So})e(mp^HhPQ2C{Po&4!@|~g^1%X2fqFAMELgBiE1@+2)Az!5~!8R1SRW~#i#lpIP6?`@glVtg4FtZ zhUsJ-MTqi#v`mM9?QiVqUI~F8Gr>La2Lx{7nACMQA+TdPe(5+0!TQCqiyibKFiY~% zIYXk2(TSGXHOn9{z7Z}?xlZ1fw{>

u}Y|GL1(K5UjBtS>DkC!P@asd*7K5tP7Mc zv3vmm{u?uj`eQ_1Z0mb9>pa;}60$q5h%0QLv(-G! zI2`<{vPMPKTJZPhJn?P#3qJR-rE?)2eEXiZaW}?;zc@?Zb?7|!3OCc1s4oJq>)72p z|6;(a+c5WMTMc+e>T-U2jDVN)t%Zt>%m+_A!VpGI1262+d&X7~cmm&ORjoZ_yZhGq zD-GaDj%eIFAOkZp#jEud9jq-M4w@NLdYtETjW|NhZN zBVj89Gsk}vbesgg-{jr99iHIVt)zXR(!fubpWZ%&K-aD~lcg%E;4iv(*Td5cyf1N2 zrvKdzUd8ZsmE5)Dg8wBRy9MyfPxL+9vJ~9k?{{9xdI0W~M@z3J7J+A@EWCq94=Ax;l2M`ao+Z z{6ll9r&blhZ=HqxdG{Ugr6#*KrWA+6SN_E>2fiVEBW2CK9436fMbDo%_a^)TzJGGu zDhI!NpRIoc&;y9kvZi5MV?d_fa9E%s&3!+x-ZrHx#T) z=|I3gn^})j^blxTF-y<<4gzU87mi&DL}1QD(fX1oP=R%8v@{PHBJj;R%|kQm5hQ08 z<>vSlK{Krjm|I)GdSCI98QX}^Mbq=ft~-rT-!ofomue#PfXk+`6YmgueUqeR_kM)- zx|A>E$bmgN#4Vm{OoGL!7iKG?z*kp$-`bcDzJ-E=TE#W+X$=-tw%5s{+z@Zpo=%?q z>Er;vXWzgw`pq59*#p)nv+XsJx52!gEZ8&{2WG5An~r2Tm}@S5Ut%x|jL(i6(ij6^ z9E{J2uUG?y<+GFCn%DO`O&h(4f_RDf>Ed5z5YzbiH1$^ypRKC8s{bA0p{3lR57v;F zcL{ud9fRcjngnLvDM(jW*3DLRhx9(Ph5bYyGIqz3y=j*bF;iyq^T+@q?ma22?lK0I z=25Dlb_$XDbwPKtFC#K%TiN;pF^E#0$6qo$5mD=3EwsK%H6dzZ?APvWIwGHJSu`h9 z8Ijq4rI%^j5b5N;MD6TSL@F%be(01bDF3rl@`e70D0Lo)P4j?kVD@u?=m=yRW+k0F zo(-9%(dO1WWsnXU1+y>Afb`bFQdjB-q=%-hN%R?klrt%GBz7^RmfC;WQDnb{WA}UN z;Y>(+F9og%r$H!-{rA*mGlYEA{Ku4#aMQ_6Ze7(7isQeIn)#A&)XQTwnMokb|6pxj zb`!!IKHcNyx;;XE-k_ z;Cw^)KwDNydY_*vAFnv!n027?AON(a4m4O5hK^r6ieC=0dQt_^M@S4+Js6@`FzcA!vS+ zG>@%;un7U>xvsSca|p7scBCO}55sa-3OP~Bu-TNuaPBTo%LY1%H@Y@Gij72>csc zPhJr$AOOAc@~YI2;0L`<|H*g_zS*XKf9LN3Ux}mBmPv58-8m$Ln+INDPgZp;*={m# zESl*C-lq)V1|=c*%1i5O?;Zz#0cW@J7&6m!-W?AI`oMSl$jQ%$CCqKiDF(?`__2Ar ze)BBBr$RqXN{+4r-?Zs$?g}~ZM_o=HWspNMu<<6*B*6@DwKfL}H` z_iC0kIPP&)56v!vv+9q?=PVZ#XKMG}mIy+a-YL$gv7ixVWg?0n90PW^vwdsTNw7^f zREk(Y=(8_tF8?+J>xXuKYw2|`Q$tKmDs;e@_{zv8I0`iCBQIOm3&Ag^QTKMVAxKZ| zsN~@>1Ox@f-P0Nezbwr=-P=Oob79ghrT>n>=V9TDlV2B8@KbO(9w(g(|3wDNcgTAo zV2y6umPua`Xt62(s#hF>Oph*7vpS35**1S>UzbDh500_<=ywQd+}P+^OMpC5;mM(D zKhU03tcuT@2U=gRTa{@&=!zLjT4H~JzG!4PM5qM1)6P@cd#`{VF`&F>wHxRqAKTAe zr4m8!iThS6y9kDM|NhxY9boKm4@|f{3`W-9GNHyeSsTO(2sZ9J z`Gjpe>i_zKwC10}_Zn45J*MBJQs0pEKFE=4o3j>@=-|YLdNWAI2ZfAeZh$!bIm02^ z7~)B5a)-YUAUt_=rDo|_gv*_)41XI45&!yv6HEd)W`~y8jng2x= YVi;asywPq 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 z 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%0A6Zm)MWJ?ovKBV3y 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 U8l(QY5J)Efh&5TF6!@ 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�J+w}- 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%0A%&o_1I!QaEzXy8+o5)?cddc9PS6)UU2Y|~? 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%0Dihr2k)l1o+a&0Q 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 Date: Mon, 11 Dec 2023 16:28:33 +0000 Subject: [PATCH 236/366] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.1.5 → v0.1.7](https://github.com/astral-sh/ruff-pre-commit/compare/v0.1.5...v0.1.7) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 9a4fd23f3..0bac96b79 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -77,7 +77,7 @@ repos: - tomli - repo: https://github.com/astral-sh/ruff-pre-commit - rev: "v0.1.5" + rev: "v0.1.7" hooks: - id: ruff args: ["--fix", "--show-fixes"] From b689c5eee1d2b86f03dc362eb9b06743817e0f92 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 19 Feb 2024 16:32:02 +0000 Subject: [PATCH 237/366] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/PyCQA/flake8: 6.1.0 → 7.0.0](https://github.com/PyCQA/flake8/compare/6.1.0...7.0.0) - [github.com/psf/black-pre-commit-mirror: 23.11.0 → 24.2.0](https://github.com/psf/black-pre-commit-mirror/compare/23.11.0...24.2.0) - [github.com/scientific-python/cookie: 2023.11.17 → 2024.01.24](https://github.com/scientific-python/cookie/compare/2023.11.17...2024.01.24) - [github.com/astral-sh/ruff-pre-commit: v0.1.7 → v0.2.2](https://github.com/astral-sh/ruff-pre-commit/compare/v0.1.7...v0.2.2) --- .pre-commit-config.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 0bac96b79..b8bc450ce 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -40,7 +40,7 @@ repos: # F822: undefined name in __all__ # F823: local variable name referenced before assignment - repo: https://github.com/PyCQA/flake8 - rev: 6.1.0 + rev: 7.0.0 hooks: - id: flake8 args: @@ -52,7 +52,7 @@ repos: exclude: ".*(data.*|extern.*|cextern)$" - repo: https://github.com/psf/black-pre-commit-mirror - rev: 23.11.0 + rev: 24.2.0 hooks: - id: black @@ -64,7 +64,7 @@ repos: exclude: ".*(tests.*)$" - repo: https://github.com/scientific-python/cookie - rev: 2023.11.17 + rev: 2024.01.24 hooks: - id: sp-repo-review @@ -77,7 +77,7 @@ repos: - tomli - repo: https://github.com/astral-sh/ruff-pre-commit - rev: "v0.1.7" + rev: "v0.2.2" hooks: - id: ruff args: ["--fix", "--show-fixes"] From e2957901e4f85e180a7039af1defc64a1665d412 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Sat, 24 Feb 2024 11:19:28 +0000 Subject: [PATCH 238/366] Added groups: to GitHub workflow config --- .github/dependabot.yml | 4 ++++ .github/workflows/ci_workflows.yml | 5 +++++ reproject/common.py | 4 +--- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 8ac6b8c49..d57929b9e 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -4,3 +4,7 @@ updates: directory: "/" schedule: interval: "monthly" + groups: + actions: + patterns: + - "*" diff --git a/.github/workflows/ci_workflows.yml b/.github/workflows/ci_workflows.yml index 7e88f484d..8eea82225 100644 --- a/.github/workflows/ci_workflows.yml +++ b/.github/workflows/ci_workflows.yml @@ -8,6 +8,11 @@ on: pull_request: workflow_dispatch: +groups: + actions: + patterns: + - "*" + concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true diff --git a/reproject/common.py b/reproject/common.py index 2bc2279de..64f8598a8 100644 --- a/reproject/common.py +++ b/reproject/common.py @@ -239,9 +239,7 @@ def reproject_single_block(a, array_or_path, block_info=None): else: rechunk_kwargs = {} array_out_dask = da.empty(shape_out) - array_out_dask = array_out_dask.rechunk( - block_size_limit=8 * 1024**2, **rechunk_kwargs - ) + array_out_dask = array_out_dask.rechunk(block_size_limit=8 * 1024**2, **rechunk_kwargs) result = da.map_blocks( reproject_single_block, From 500926236a9bd152df5521758e9bf7df178fe12d Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Fri, 23 Feb 2024 09:09:10 +0000 Subject: [PATCH 239/366] Enable testing of wheels on silicon mac --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 5e1340a0e..d8f154507 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -139,7 +139,7 @@ write_to = "reproject/version.py" [tool.cibuildwheel] skip = "cp36-* pp* *-musllinux*" -test-skip = "*-macosx_arm64 *-manylinux_aarch64" +test-skip = "*-manylinux_aarch64" [tool.isort] profile = "black" From 2598895c02beb93ee3750106ca86fde3c83fa29f Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Sat, 24 Feb 2024 11:08:14 +0000 Subject: [PATCH 240/366] Drop support for Python 3.9 following SPEC 0 --- .github/workflows/ci_workflows.yml | 9 +++------ pyproject.toml | 2 +- tox.ini | 2 +- 3 files changed, 5 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ci_workflows.yml b/.github/workflows/ci_workflows.yml index 8eea82225..be5347658 100644 --- a/.github/workflows/ci_workflows.yml +++ b/.github/workflows/ci_workflows.yml @@ -23,17 +23,14 @@ jobs: uses: OpenAstronomy/github-actions-workflows/.github/workflows/tox.yml@v1 with: envs: | - - macos: py39-test-oldestdeps - - macos: py310-test + - macos: py310-test-oldestdeps - macos: py311-test - macos: py312-test - - linux: py39-test-oldestdeps - - linux: py310-test + - linux: py310-test-oldestdeps - linux: py311-test - linux: py312-test - linux: py312-test-devdeps - - windows: py39-test-oldestdeps - - windows: py310-test + - windows: py310-test-oldestdeps - windows: py311-test - windows: py312-test libraries: | diff --git a/pyproject.toml b/pyproject.toml index d8f154507..159cd93e4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,7 +8,7 @@ authors = [ license = {text = "BSD 3-Clause"} description = "Reproject astronomical images" urls = {Homepage = "https://reproject.readthedocs.io"} -requires-python = ">=3.9" +requires-python = ">=3.10" dependencies = [ "numpy>=1.20", "astropy>=5.0", diff --git a/tox.ini b/tox.ini index 8bda82412..dff90228a 100644 --- a/tox.ini +++ b/tox.ini @@ -1,6 +1,6 @@ [tox] envlist = - py{38,39,310,311,312}-{test}{,-oldestdeps,-numpy121} + py{310,311,312}-{test}{,-oldestdeps,-numpy121} build_docs codestyle isolated_build = True From a3f4759f8233297f88fe1c21c04571c7c54203b0 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Wed, 28 Feb 2024 21:02:13 +0000 Subject: [PATCH 241/366] Updated minimum supported versions of NumPy and SciPy --- pyproject.toml | 4 ++-- tox.ini | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 159cd93e4..acff67230 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -10,10 +10,10 @@ description = "Reproject astronomical images" urls = {Homepage = "https://reproject.readthedocs.io"} requires-python = ">=3.10" dependencies = [ - "numpy>=1.20", + "numpy>=1.23", "astropy>=5.0", "astropy-healpix>=0.6", - "scipy>=1.5", + "scipy>=1.9", "dask[array]>=2021.8", "cloudpickle", "zarr", diff --git a/tox.ini b/tox.ini index dff90228a..f3d6b8d8c 100644 --- a/tox.ini +++ b/tox.ini @@ -21,10 +21,10 @@ changedir = deps = numpy121: numpy==1.21.* - oldestdeps: numpy==1.20.* + oldestdeps: numpy==1.23.* oldestdeps: astropy==5.0.* oldestdeps: astropy-healpix==0.6 - oldestdeps: scipy==1.5.* + oldestdeps: scipy==1.9.* oldestdeps: dask==2021.8.* devdeps: numpy>=0.0.dev0 From 4e8f8ea54078a3e7e08aec026d73234d0b659a9c Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Wed, 28 Feb 2024 21:17:54 +0000 Subject: [PATCH 242/366] Fixed ruff errors and bumped RTD Python version to 3.10 --- .readthedocs.yaml | 2 +- pyproject.toml | 4 +--- reproject/healpix/utils.py | 2 +- reproject/mosaicking/coadd.py | 2 +- reproject/mosaicking/tests/test_coadd.py | 2 +- reproject/mosaicking/tests/test_wcs_helpers.py | 4 ++-- reproject/mosaicking/wcs_helpers.py | 4 ++-- reproject/tests/test_utils.py | 2 +- reproject/utils.py | 16 ++++++++-------- 9 files changed, 18 insertions(+), 20 deletions(-) diff --git a/.readthedocs.yaml b/.readthedocs.yaml index 9049c19e7..b4984aa9e 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -2,7 +2,7 @@ version: 2 build: os: ubuntu-20.04 tools: - python: "3.9" + python: "3.10" apt_packages: - graphviz diff --git a/pyproject.toml b/pyproject.toml index acff67230..5fbf89335 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -180,9 +180,7 @@ ignore = [ ] [tool.ruff] - -[tool.ruff.lint] -extend-select = [ +lint.select = [ "B", # flake8-bugbear "I", # isort "UP", # pyupgrade diff --git a/reproject/healpix/utils.py b/reproject/healpix/utils.py index cd8693da1..5e3fc84f4 100644 --- a/reproject/healpix/utils.py +++ b/reproject/healpix/utils.py @@ -33,7 +33,7 @@ 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)): + if isinstance(input_data, TableHDU | BinTableHDU): data = input_data.data header = input_data.header coordinate_system_in = parse_coord_system(header["COORDSYS"]) diff --git a/reproject/mosaicking/coadd.py b/reproject/mosaicking/coadd.py index 1e68e2ce2..8de178e35 100644 --- a/reproject/mosaicking/coadd.py +++ b/reproject/mosaicking/coadd.py @@ -248,7 +248,7 @@ def reproject_and_coadd( corrections = solve_corrections_sgd(offset_matrix) if background_reference: corrections -= corrections[background_reference] - for array, correction in zip(arrays, corrections): + for array, correction in zip(arrays, corrections, strict=True): array.array -= correction # At this point, the images are now ready to be co-added. diff --git a/reproject/mosaicking/tests/test_coadd.py b/reproject/mosaicking/tests/test_coadd.py index b8ff25e0b..d9e3de683 100644 --- a/reproject/mosaicking/tests/test_coadd.py +++ b/reproject/mosaicking/tests/test_coadd.py @@ -386,7 +386,7 @@ def test_coadd_solar_map(): wcs_out = WCS(Header.fromstring(HEADER_SOLAR_OUT, sep="\n")) scales = [1 / 6, 1, 1 / 6] - input_data = tuple((a.data * scale, a.wcs) for (a, scale) in zip(maps, scales)) + input_data = tuple((a.data * scale, a.wcs) for (a, scale) in zip(maps, scales, strict=True)) array, footprint = reproject_and_coadd( input_data, diff --git a/reproject/mosaicking/tests/test_wcs_helpers.py b/reproject/mosaicking/tests/test_wcs_helpers.py index f0f77cf47..96d621a75 100644 --- a/reproject/mosaicking/tests/test_wcs_helpers.py +++ b/reproject/mosaicking/tests/test_wcs_helpers.py @@ -263,11 +263,11 @@ def test_input_types(valid_celestial_input_shapes, iterable): if ( not isinstance(input_value, WCS) - and isinstance(input_value, (BaseLowLevelWCS, BaseHighLevelWCS)) + and isinstance(input_value, BaseLowLevelWCS | BaseHighLevelWCS) ) or ( isinstance(input_value, tuple) and not isinstance(input_value[1], WCS) - and isinstance(input_value[1], (BaseLowLevelWCS, BaseHighLevelWCS)) + and isinstance(input_value[1], BaseLowLevelWCS | BaseHighLevelWCS) ): wcs_ref = WCS(fits.Header.fromstring(APE14_HEADER_REF, sep="\n")) shape_ref = (31, 50) diff --git a/reproject/mosaicking/wcs_helpers.py b/reproject/mosaicking/wcs_helpers.py index b232bdfaf..5a10b2282 100644 --- a/reproject/mosaicking/wcs_helpers.py +++ b/reproject/mosaicking/wcs_helpers.py @@ -107,7 +107,7 @@ def find_optimal_celestial_wcs( iterable = False elif isiterable(input_data): if len(input_data) == 2 and isinstance( - input_data[1], (BaseLowLevelWCS, BaseHighLevelWCS, Header) + input_data[1], BaseLowLevelWCS | BaseHighLevelWCS | Header ): # Since 2-element tuples are valid single inputs we need to check for this iterable = False @@ -245,7 +245,7 @@ def find_optimal_celestial_wcs( # rectangle from shapely.geometry import MultiPoint - mp = MultiPoint(list(zip(xp, yp))) + mp = MultiPoint(list(zip(xp, yp, strict=True))) # The following returns a list of rectangle vertices - in fact there # are 5 coordinates because shapely represents it as a closed polygon diff --git a/reproject/tests/test_utils.py b/reproject/tests/test_utils.py index 1c5ad3f78..1345afa4a 100644 --- a/reproject/tests/test_utils.py +++ b/reproject/tests/test_utils.py @@ -15,7 +15,7 @@ def test_parse_input_data(tmpdir, valid_celestial_input_data, request): array_ref, wcs_ref, input_value, kwargs = valid_celestial_input_data data, wcs = parse_input_data(input_value, **kwargs) - assert isinstance(data, (da.Array, np.ndarray)) + assert isinstance(data, da.Array | np.ndarray) np.testing.assert_allclose(data, array_ref) assert_wcs_allclose(wcs, wcs_ref) diff --git a/reproject/utils.py b/reproject/utils.py index c8f1e2ef9..3ff46555c 100644 --- a/reproject/utils.py +++ b/reproject/utils.py @@ -70,7 +70,7 @@ def parse_input_data(input_data, hdu_in=None): Parse input data to return a Numpy array and WCS object. """ - if isinstance(input_data, (str, Path)): + if isinstance(input_data, str | Path): with fits.open(input_data) as hdul: return parse_input_data(hdul, hdu_in=hdu_in) elif isinstance(input_data, HDUList): @@ -83,9 +83,9 @@ def parse_input_data(input_data, hdu_in=None): else: hdu_in = 0 return parse_input_data(input_data[hdu_in]) - elif isinstance(input_data, (PrimaryHDU, ImageHDU, CompImageHDU)): + elif isinstance(input_data, PrimaryHDU | ImageHDU | CompImageHDU): return input_data.data, WCS(input_data.header) - elif isinstance(input_data, tuple) and isinstance(input_data[0], (np.ndarray, da.core.Array)): + elif isinstance(input_data, tuple) and isinstance(input_data[0], np.ndarray | da.core.Array): if isinstance(input_data[1], Header): return input_data[0], WCS(input_data[1]) else: @@ -114,7 +114,7 @@ def parse_input_shape(input_shape, hdu_in=None): Parse input shape information to return an array shape tuple and WCS object. """ - if isinstance(input_shape, (str, Path)): + if isinstance(input_shape, str | Path): return parse_input_shape(fits.open(input_shape), hdu_in=hdu_in) elif isinstance(input_shape, HDUList): if hdu_in is None: @@ -126,9 +126,9 @@ def parse_input_shape(input_shape, hdu_in=None): else: hdu_in = 0 return parse_input_shape(input_shape[hdu_in]) - elif isinstance(input_shape, (PrimaryHDU, ImageHDU, CompImageHDU)): + elif isinstance(input_shape, PrimaryHDU | ImageHDU | CompImageHDU): return input_shape.shape, WCS(input_shape.header) - elif isinstance(input_shape, tuple) and isinstance(input_shape[0], (np.ndarray, da.core.Array)): + elif isinstance(input_shape, tuple) and isinstance(input_shape[0], np.ndarray | da.core.Array): if isinstance(input_shape[1], Header): return input_shape[0].shape, WCS(input_shape[1]) else: @@ -177,7 +177,7 @@ def parse_input_weights(input_weights, hdu_weights=None): else: hdu_weights = 0 return parse_input_data(input_weights[hdu_weights])[0] - elif isinstance(input_weights, (PrimaryHDU, ImageHDU, CompImageHDU)): + elif isinstance(input_weights, PrimaryHDU | ImageHDU | CompImageHDU): return input_weights.data elif isinstance(input_weights, np.ndarray): return input_weights @@ -205,7 +205,7 @@ def parse_output_projection(output_projection, shape_in=None, shape_out=None, ou "Need to specify shape since output header " "does not contain complete shape information" ) from None - elif isinstance(output_projection, (BaseLowLevelWCS, BaseHighLevelWCS)): + elif isinstance(output_projection, BaseLowLevelWCS | BaseHighLevelWCS): if isinstance(output_projection, BaseLowLevelWCS) and not isinstance( output_projection, BaseHighLevelWCS ): From 2dedbc5edb09285a2ccb78ce0c75ac8727bff86e Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Wed, 28 Feb 2024 21:29:41 +0000 Subject: [PATCH 243/366] Remove incorrect section from workflow --- .github/workflows/ci_workflows.yml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/.github/workflows/ci_workflows.yml b/.github/workflows/ci_workflows.yml index be5347658..bb3fed0f3 100644 --- a/.github/workflows/ci_workflows.yml +++ b/.github/workflows/ci_workflows.yml @@ -8,11 +8,6 @@ on: pull_request: workflow_dispatch: -groups: - actions: - patterns: - - "*" - concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true From 963612e301cdfe612bb37ae59a929b69d89b9cd0 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 4 Mar 2024 16:47:52 +0000 Subject: [PATCH 244/366] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.2.2 → v0.3.0](https://github.com/astral-sh/ruff-pre-commit/compare/v0.2.2...v0.3.0) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index b8bc450ce..a116c9659 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -77,7 +77,7 @@ repos: - tomli - repo: https://github.com/astral-sh/ruff-pre-commit - rev: "v0.2.2" + rev: "v0.3.0" hooks: - id: ruff args: ["--fix", "--show-fixes"] From 07f8614a8776b60f88d9db26e4fb0b080a5769d6 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 12 Mar 2024 19:12:13 +0000 Subject: [PATCH 245/366] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/scientific-python/cookie: 2024.01.24 → 2024.03.10](https://github.com/scientific-python/cookie/compare/2024.01.24...2024.03.10) - [github.com/astral-sh/ruff-pre-commit: v0.3.0 → v0.3.2](https://github.com/astral-sh/ruff-pre-commit/compare/v0.3.0...v0.3.2) --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index a116c9659..821cd5454 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -64,7 +64,7 @@ repos: exclude: ".*(tests.*)$" - repo: https://github.com/scientific-python/cookie - rev: 2024.01.24 + rev: 2024.03.10 hooks: - id: sp-repo-review @@ -77,7 +77,7 @@ repos: - tomli - repo: https://github.com/astral-sh/ruff-pre-commit - rev: "v0.3.0" + rev: "v0.3.2" hooks: - id: ruff args: ["--fix", "--show-fixes"] From 61f7318db4bf1fb2dace46e5b741d74fab512e66 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 12 Mar 2024 19:15:26 +0000 Subject: [PATCH 246/366] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- reproject/interpolation/core.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/reproject/interpolation/core.py b/reproject/interpolation/core.py index a1909ce28..f9bd6be4b 100644 --- a/reproject/interpolation/core.py +++ b/reproject/interpolation/core.py @@ -36,10 +36,8 @@ def _validate_wcs(wcs_in, wcs_out, shape_in, shape_out): if wcs_in.wcs.spec >= 0 and wcs_out.wcs.spec >= 0: if wcs_in.wcs.ctype[wcs_in.wcs.spec] != wcs_out.wcs.ctype[wcs_out.wcs.spec]: raise ValueError( - "The input ({}) and output ({}) spectral " - "coordinate types are not equivalent.".format( - wcs_in.wcs.ctype[wcs_in.wcs.spec], wcs_out.wcs.ctype[wcs_out.wcs.spec] - ) + f"The input ({wcs_in.wcs.ctype[wcs_in.wcs.spec]}) and output ({wcs_out.wcs.ctype[wcs_out.wcs.spec]}) spectral " + "coordinate types are not equivalent." ) elif wcs_in.wcs.spec >= 0: raise ValueError("Input WCS has a spectral component but output WCS does not") From cc76b697fed452ef6e62d7df9e03047cb7051584 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Mon, 25 Mar 2024 11:32:42 +0000 Subject: [PATCH 247/366] Added regression test for multi-threaded issues --- reproject/tests/test_high_level.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/reproject/tests/test_high_level.py b/reproject/tests/test_high_level.py index d90b7aa77..0f2477fc9 100644 --- a/reproject/tests/test_high_level.py +++ b/reproject/tests/test_high_level.py @@ -253,3 +253,24 @@ def test_dimensions_checks(reproject_function): with pytest.raises(ValueError, match="Dimensions to be looped over must match exactly"): array_out, footprint = reproject_function((array_in, w_in), w_out, shape_out=[3, 3, 4, 5]) + + +@pytest.mark.remote_data +@pytest.mark.filterwarnings("ignore::astropy.wcs.wcs.FITSFixedWarning") +@pytest.mark.parametrize( + "reproject_function", [reproject_interp, reproject_adaptive, reproject_exact] +) +@pytest.mark.parametrize("scheduler", ("synchronous", "processes", "threads")) +def test_dask_schedulers(reproject_function, scheduler): + + # Regression test for issues with the multi-threaded scheduler + + hdu1 = fits.open(get_pkg_data_filename("galactic_center/gc_2mass_k.fits"))[0] + hdu2 = fits.open(get_pkg_data_filename("galactic_center/gc_msx_e.fits"))[0] + + array1 = reproject_function(hdu1, hdu2.header, return_footprint=False) + + array2 = reproject_function(hdu1, hdu2.header, return_footprint=False, return_type="dask", block_size=(100, 100)) + array2 = array2.compute(scheduler=scheduler) + + np.testing.assert_allclose(array1, array2, equal_nan=True) From 7ddfe943b0fd5046a6c99e0f7655dab31ef3582d Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Mon, 25 Mar 2024 11:45:20 +0000 Subject: [PATCH 248/366] Fix code style --- reproject/tests/test_high_level.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/reproject/tests/test_high_level.py b/reproject/tests/test_high_level.py index 0f2477fc9..2bd2045aa 100644 --- a/reproject/tests/test_high_level.py +++ b/reproject/tests/test_high_level.py @@ -270,7 +270,9 @@ def test_dask_schedulers(reproject_function, scheduler): array1 = reproject_function(hdu1, hdu2.header, return_footprint=False) - array2 = reproject_function(hdu1, hdu2.header, return_footprint=False, return_type="dask", block_size=(100, 100)) + array2 = reproject_function( + hdu1, hdu2.header, return_footprint=False, return_type="dask", block_size=(100, 100) + ) array2 = array2.compute(scheduler=scheduler) np.testing.assert_allclose(array1, array2, equal_nan=True) From 9ae1a34335effd4680f2919b1550d221781bf1f7 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 25 Mar 2024 16:27:57 +0000 Subject: [PATCH 249/366] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/psf/black-pre-commit-mirror: 24.2.0 → 24.3.0](https://github.com/psf/black-pre-commit-mirror/compare/24.2.0...24.3.0) - [github.com/numpy/numpydoc: v1.6.0 → v1.7.0rc0](https://github.com/numpy/numpydoc/compare/v1.6.0...v1.7.0rc0) - [github.com/astral-sh/ruff-pre-commit: v0.3.2 → v0.3.4](https://github.com/astral-sh/ruff-pre-commit/compare/v0.3.2...v0.3.4) --- .pre-commit-config.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 821cd5454..54374609c 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -52,12 +52,12 @@ repos: exclude: ".*(data.*|extern.*|cextern)$" - repo: https://github.com/psf/black-pre-commit-mirror - rev: 24.2.0 + rev: 24.3.0 hooks: - id: black - repo: https://github.com/numpy/numpydoc - rev: v1.6.0 + rev: v1.7.0rc0 hooks: - id: numpydoc-validation files: ".*(high_level|mosaicking).*$" @@ -77,7 +77,7 @@ repos: - tomli - repo: https://github.com/astral-sh/ruff-pre-commit - rev: "v0.3.2" + rev: "v0.3.4" hooks: - id: ruff args: ["--fix", "--show-fixes"] From 22ef40b7b2a252989ad81d016b236123bab489eb Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Mon, 25 Mar 2024 23:58:30 +0000 Subject: [PATCH 250/366] Possible solution to threading issues, force a deep copy of the WCS object for each chunk --- reproject/common.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/reproject/common.py b/reproject/common.py index 64f8598a8..54052c3eb 100644 --- a/reproject/common.py +++ b/reproject/common.py @@ -187,12 +187,16 @@ def _reproject_dispatcher( def reproject_single_block(a, array_or_path, block_info=None): if a.ndim == 0 or block_info is None or block_info == []: return np.array([a, a]) - slices = [slice(*x) for x in block_info[None]["array-location"][-wcs_out.pixel_n_dim :]] + + wcs_in_cp = wcs_in.deepcopy() + wcs_out_cp = wcs_out.deepcopy() + + slices = [slice(*x) for x in block_info[None]["array-location"][-wcs_out_cp.pixel_n_dim :]] if isinstance(wcs_out, BaseHighLevelWCS): - low_level_wcs = SlicedLowLevelWCS(wcs_out.low_level_wcs, slices=slices) + low_level_wcs = SlicedLowLevelWCS(wcs_out_cp.low_level_wcs, slices=slices) else: - low_level_wcs = SlicedLowLevelWCS(wcs_out, slices=slices) + low_level_wcs = SlicedLowLevelWCS(wcs_out_cp, slices=slices) wcs_out_sub = HighLevelWCSWrapper(low_level_wcs) @@ -208,7 +212,7 @@ def reproject_single_block(a, array_or_path, block_info=None): array, footprint = reproject_func( array_in, - wcs_in, + wcs_in_cp, wcs_out_sub, shape_out=shape_out, array_out=np.zeros(shape_out), From 85b5e7d207fda4916468575a51875547060a36b8 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 1 Apr 2024 16:28:25 +0000 Subject: [PATCH 251/366] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/numpy/numpydoc: v1.7.0rc0 → v1.7.0](https://github.com/numpy/numpydoc/compare/v1.7.0rc0...v1.7.0) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 54374609c..6d05eb7e3 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -57,7 +57,7 @@ repos: - id: black - repo: https://github.com/numpy/numpydoc - rev: v1.7.0rc0 + rev: v1.7.0 hooks: - id: numpydoc-validation files: ".*(high_level|mosaicking).*$" From f6a1e4eadd9627139566135b097d6a6b55cd6d6e Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Fri, 5 Apr 2024 11:05:13 +0100 Subject: [PATCH 252/366] Build against Numpy 2.0.0rc1 or later --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 5fbf89335..955389e26 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -49,7 +49,7 @@ docs = [ requires = ["setuptools", "setuptools_scm", "extension-helpers==1.*", - "numpy>=1.25", + "numpy>=2.0.0rc1", "cython>=3.0,<3.1"] build-backend = 'setuptools.build_meta' From 1bf2a8403b656d332838ffced848f4c7e765317d Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Fri, 5 Apr 2024 11:06:30 +0100 Subject: [PATCH 253/366] Simplify dev wheels configuration since we no longer need to install the developer version of Numpy --- .github/workflows/ci_workflows.yml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.github/workflows/ci_workflows.yml b/.github/workflows/ci_workflows.yml index bb3fed0f3..5e0a27eb0 100644 --- a/.github/workflows/ci_workflows.yml +++ b/.github/workflows/ci_workflows.yml @@ -46,14 +46,11 @@ jobs: - cp*-macosx_arm64 - cp*-win_amd64 - # Developer wheels (use Numpy dev to build) + # Developer wheels upload_to_anaconda: ${{ (github.event_name == 'schedule' || github.event_name == 'workflow_dispatch') }} anaconda_user: astropy anaconda_package: reproject anaconda_keep_n_latest: 10 - env: | - CIBW_BEFORE_BUILD: '${{ ((github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' || github.event_name == 'pull_request') && 'pip install --pre --extra-index-url https://pypi.anaconda.org/scientific-python-nightly-wheels/simple setuptools setuptools_scm numpy>=0.0dev0 extension-helpers cython') || '' }}' - CIBW_BUILD_FRONTEND: '${{ ((github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' || github.event_name == 'pull_request') && 'pip; args: --no-build-isolation') || 'build' }}' secrets: pypi_token: ${{ secrets.pypi_token }} From a702f0862a15aee4ce2eff9270afb1fd3e84e183 Mon Sep 17 00:00:00 2001 From: astrofrog Date: Fri, 5 Apr 2024 11:04:59 +0000 Subject: [PATCH 254/366] Update CHANGELOG --- CHANGES.md | 101 ++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 68 insertions(+), 33 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 10e887508..ec39f3c22 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,38 @@ +## v0.13.1 - 2024-04-05 + + +### What's Changed + +#### Bug Fixes + +* Ensure reproject_and_coadd handles bg-matching with one input image by @svank in https://github.com/astropy/reproject/pull/412 +* Fixes for mosaic output pixels not covered by inputs by @svank in https://github.com/astropy/reproject/pull/413 + +#### Documentation + +* Updated docstrings for output_projection and shape_out to indicate that any APE-14 WCS is acceptable by @astrofrog in https://github.com/astropy/reproject/pull/407 + +#### Other Changes + +* Add tests for full range of inputs/outputs in healpix functions by @astrofrog in https://github.com/astropy/reproject/pull/408 +* Fix Cython warnings by @svank in https://github.com/astropy/reproject/pull/418 +* Fix devdeps job by @astrofrog in https://github.com/astropy/reproject/pull/415 +* BLD: pin extension-helpers to 1.* following upstream recommendation by @neutrinoceros in https://github.com/astropy/reproject/pull/420 +* Added sp-repo-review to pre-commit by @astrofrog in https://github.com/astropy/reproject/pull/411 +* Bump actions/checkout from 2 to 4 by @dependabot in https://github.com/astropy/reproject/pull/422 +* Bump stefanzweifel/git-auto-commit-action from 4 to 5 by @dependabot in https://github.com/astropy/reproject/pull/423 +* Add configuration for nightly wheels by @astrofrog in https://github.com/astropy/reproject/pull/417 +* Fix pre-commit errors by @astrofrog in https://github.com/astropy/reproject/pull/429 +* Enable testing of wheels on silicon mac by @astrofrog in https://github.com/astropy/reproject/pull/428 +* Build against Numpy 2.0.0rc1 or later by @astrofrog in https://github.com/astropy/reproject/pull/436 + +### New Contributors + +* @neutrinoceros made their first contribution in https://github.com/astropy/reproject/pull/420 +* @dependabot made their first contribution in https://github.com/astropy/reproject/pull/422 + +**Full Changelog**: https://github.com/astropy/reproject/compare/v0.13.0...v0.13.1 + ## v0.13.0 - 2023-10-24 @@ -131,9 +166,9 @@ ## 0.9 - 2022-09-02 - Drop support for Python 3.7. -- +- - Infrastructure and packaging updates. -- +- - Made many improvements, bug fixes, and significant speed-ups for the adaptive - resampling algorithm, `reproject_adaptive`. These bug fixes may cause - changes to the reprojected images, which are typically negligible. @@ -141,13 +176,13 @@ - Gaussian filter kernel, a menu of boundary-handling modes, and a - `center_jacobian` flag to trade speed for accuracy with rapidly-varying - transformations. -- +- - Added a `roundtrip_coords` argument to `reproject_adaptive` and - `reproject_interp`. By default, all coordinate transformations are run in - both directions to handle some situations where they are ambiguous. This can - be disabled by setting `roundtrip_coords=False` which may offer a - significant speed increase. -- +- ## 0.8 - 2021-08-11 @@ -156,34 +191,34 @@ - possible to solve for the optimal WCS for a set of images that couldn't fit - into memory all at once, since the actual data aren't needed for optimal WCS - determination. [#242] -- +- - Fix implementation of `hdu_weights` in `reproject_and_coadd`. [#249] -- +- ## 0.7.1 - 2020-05-29 - Fixed compatibility with Astropy 4.1. [#234] -- +- - Updated minimum requirement for SciPy. [#236] -- +- ## 0.7 - 2020-04-02 - Made C extension in overlapArea.c thread-safe by removing global - variables. [#211] -- +- - Made it possible to control whether to output debugging information - from overlapArea.c by setting DEBUG_OVERLAP_AREA=1 at build-time. [#211] -- +- - Fix compatibility with astropy v4.0.1. [#227] -- +- - Disable parallelization by default in `reproject_exact` - this can be - enabled with `parallel=True`. [#227] -- +- - Fixed a bug with `reproject_exact` with `parallel=False` and - `return_footprint=False`, which caused the footprint to be returned - anyway. [#227] -- +- - The infrastructure of the package has been updated in line with the - APE 17 roadmap (https://github.com/astropy/astropy-APEs/blob/main/APE17.rst). - The main changes are that the `python setup.py test` and @@ -192,7 +227,7 @@ - (https://tox.readthedocs.io) package and run `tox -e test` and - `tox -e build_docs`. It is also possible to run pytest and sphinx - directly. [#228] -- +- ## 0.6 - 2019-11-01 @@ -201,20 +236,20 @@ - `independent_celestial_slices=` argument to `reproject_interp` has - been deprecated since it is no longer needed, as transformations are - automatically done in the most efficient way possible. [#166] -- +- - Include a warning for high resolution images with `reproject_exact`, - since if the pixels are <0.05", precision issues can occur. [#200] -- +- - Added a new `reproject_and_coadd` function for doing mosaicking of - individual images, and added section in documentation about mosaicking. - [#186] -- +- - Added a new reproject.adaptive sub-package that implements the DeForest - (2004) algorithm for reprojection. [#52] -- +- - Fixed a bug that caused 'exact' reprojection results to have numerical - issues when doing identity transformations. [#190] -- +- ## 0.5.1 - 2019-09-01 @@ -225,33 +260,33 @@ - Improve parse_output_projection to make it so that the output projection - can be specified as a filename. [#150] -- +- - Fixed a bug that caused HEALPix maps in RING order to not be correctly - interpreted. [#163] -- +- - Make it possible to specify the output array for reprojection using the - `output_array=` keyword argument. [#115] -- +- ## 0.4 - 2018-01-29 - Refactored HEALPix reprojection code to use the astropy-healpix package - instead of healpy. [#139] -- +- - Added the ability to specify an output array in `reproject_interp`, which - permits the use of memory-mapped arrays and therefore provides the capability - to handle data cubes much larger than memory [#115] -- +- - Fix test 32-bit test failures. [#146] -- +- - Fix an issue with reprojecting images where there are two solutions along - the line of sight by forcing round-tripping of coordinate conversions [#129] -- +- - Explicitly define default HDU as 0 for normal reprojection and 1 for - HEALPix reprojection. [#119] -- +- - Added a function to find the optimal WCS for a set of images. [#136, #137] -- +- ## 0.3.2 - 2017-10-22 @@ -262,22 +297,22 @@ ## 0.3.1 - 2016-07-07 - Include missing license file in tarball. -- +- - Updated documentation to remove warnings about early versions. -- +- ## 0.3 - 2016-07-06 - Allow users to pass a `field=` option to `reproject_from_healpix` - to access different fields in a HEALPIX file. [#86] -- +- - Significant improvements to performance when the input data is a large - memory-mapped array. [#105] -- +- - Significant refactoring of interpolating reprojection to improve support for - n-dimensional arrays, optionally including two celestial axes (in which - case the coordinate transformation is taken into account). [#96, #102] -- +- ## 0.2 - 2015-10-29 From f3200be66f6d8bc3dd488a6abc13d912a3ffabc3 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 8 Apr 2024 16:28:13 +0000 Subject: [PATCH 255/366] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/pre-commit/pre-commit-hooks: v4.5.0 → v4.6.0](https://github.com/pre-commit/pre-commit-hooks/compare/v4.5.0...v4.6.0) - [github.com/astral-sh/ruff-pre-commit: v0.3.4 → v0.3.5](https://github.com/astral-sh/ruff-pre-commit/compare/v0.3.4...v0.3.5) --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 6d05eb7e3..a55354b5e 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.5.0 + rev: v4.6.0 hooks: - id: check-added-large-files - id: check-case-conflict @@ -77,7 +77,7 @@ repos: - tomli - repo: https://github.com/astral-sh/ruff-pre-commit - rev: "v0.3.4" + rev: "v0.3.5" hooks: - id: ruff args: ["--fix", "--show-fixes"] From 8850e237f7e805c7054e7e871f21f1845dcaa903 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 8 Apr 2024 16:32:06 +0000 Subject: [PATCH 256/366] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- CHANGES.md | 66 +++++++++++++++++++++++++++--------------------------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index ec39f3c22..8dc1b346e 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -166,9 +166,9 @@ ## 0.9 - 2022-09-02 - Drop support for Python 3.7. -- +- - Infrastructure and packaging updates. -- +- - Made many improvements, bug fixes, and significant speed-ups for the adaptive - resampling algorithm, `reproject_adaptive`. These bug fixes may cause - changes to the reprojected images, which are typically negligible. @@ -176,13 +176,13 @@ - Gaussian filter kernel, a menu of boundary-handling modes, and a - `center_jacobian` flag to trade speed for accuracy with rapidly-varying - transformations. -- +- - Added a `roundtrip_coords` argument to `reproject_adaptive` and - `reproject_interp`. By default, all coordinate transformations are run in - both directions to handle some situations where they are ambiguous. This can - be disabled by setting `roundtrip_coords=False` which may offer a - significant speed increase. -- +- ## 0.8 - 2021-08-11 @@ -191,34 +191,34 @@ - possible to solve for the optimal WCS for a set of images that couldn't fit - into memory all at once, since the actual data aren't needed for optimal WCS - determination. [#242] -- +- - Fix implementation of `hdu_weights` in `reproject_and_coadd`. [#249] -- +- ## 0.7.1 - 2020-05-29 - Fixed compatibility with Astropy 4.1. [#234] -- +- - Updated minimum requirement for SciPy. [#236] -- +- ## 0.7 - 2020-04-02 - Made C extension in overlapArea.c thread-safe by removing global - variables. [#211] -- +- - Made it possible to control whether to output debugging information - from overlapArea.c by setting DEBUG_OVERLAP_AREA=1 at build-time. [#211] -- +- - Fix compatibility with astropy v4.0.1. [#227] -- +- - Disable parallelization by default in `reproject_exact` - this can be - enabled with `parallel=True`. [#227] -- +- - Fixed a bug with `reproject_exact` with `parallel=False` and - `return_footprint=False`, which caused the footprint to be returned - anyway. [#227] -- +- - The infrastructure of the package has been updated in line with the - APE 17 roadmap (https://github.com/astropy/astropy-APEs/blob/main/APE17.rst). - The main changes are that the `python setup.py test` and @@ -227,7 +227,7 @@ - (https://tox.readthedocs.io) package and run `tox -e test` and - `tox -e build_docs`. It is also possible to run pytest and sphinx - directly. [#228] -- +- ## 0.6 - 2019-11-01 @@ -236,20 +236,20 @@ - `independent_celestial_slices=` argument to `reproject_interp` has - been deprecated since it is no longer needed, as transformations are - automatically done in the most efficient way possible. [#166] -- +- - Include a warning for high resolution images with `reproject_exact`, - since if the pixels are <0.05", precision issues can occur. [#200] -- +- - Added a new `reproject_and_coadd` function for doing mosaicking of - individual images, and added section in documentation about mosaicking. - [#186] -- +- - Added a new reproject.adaptive sub-package that implements the DeForest - (2004) algorithm for reprojection. [#52] -- +- - Fixed a bug that caused 'exact' reprojection results to have numerical - issues when doing identity transformations. [#190] -- +- ## 0.5.1 - 2019-09-01 @@ -260,33 +260,33 @@ - Improve parse_output_projection to make it so that the output projection - can be specified as a filename. [#150] -- +- - Fixed a bug that caused HEALPix maps in RING order to not be correctly - interpreted. [#163] -- +- - Make it possible to specify the output array for reprojection using the - `output_array=` keyword argument. [#115] -- +- ## 0.4 - 2018-01-29 - Refactored HEALPix reprojection code to use the astropy-healpix package - instead of healpy. [#139] -- +- - Added the ability to specify an output array in `reproject_interp`, which - permits the use of memory-mapped arrays and therefore provides the capability - to handle data cubes much larger than memory [#115] -- +- - Fix test 32-bit test failures. [#146] -- +- - Fix an issue with reprojecting images where there are two solutions along - the line of sight by forcing round-tripping of coordinate conversions [#129] -- +- - Explicitly define default HDU as 0 for normal reprojection and 1 for - HEALPix reprojection. [#119] -- +- - Added a function to find the optimal WCS for a set of images. [#136, #137] -- +- ## 0.3.2 - 2017-10-22 @@ -297,22 +297,22 @@ ## 0.3.1 - 2016-07-07 - Include missing license file in tarball. -- +- - Updated documentation to remove warnings about early versions. -- +- ## 0.3 - 2016-07-06 - Allow users to pass a `field=` option to `reproject_from_healpix` - to access different fields in a HEALPIX file. [#86] -- +- - Significant improvements to performance when the input data is a large - memory-mapped array. [#105] -- +- - Significant refactoring of interpolating reprojection to improve support for - n-dimensional arrays, optionally including two celestial axes (in which - case the coordinate transformation is taken into account). [#96, #102] -- +- ## 0.2 - 2015-10-29 From 7fee89c321c8a88140ff70a02e64c58aae3e3975 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Tue, 9 Apr 2024 13:20:41 +0100 Subject: [PATCH 257/366] Added explanatory comment and only copy if the WCS is an astropy WCS --- reproject/common.py | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/reproject/common.py b/reproject/common.py index 54052c3eb..5a714d67c 100644 --- a/reproject/common.py +++ b/reproject/common.py @@ -188,10 +188,21 @@ def reproject_single_block(a, array_or_path, block_info=None): if a.ndim == 0 or block_info is None or block_info == []: return np.array([a, a]) - wcs_in_cp = wcs_in.deepcopy() - wcs_out_cp = wcs_out.deepcopy() - - slices = [slice(*x) for x in block_info[None]["array-location"][-wcs_out_cp.pixel_n_dim :]] + # The WCS class from astropy is not thread-safe, see e.g. + # https://github.com/astropy/astropy/issues/16244 + # https://github.com/astropy/astropy/issues/16245 + # To work around these issues, we make sure we do a deep copy of + # the WCS object in here when using FITS WCS. This is a very fast + # operation (<0.1ms) so should not be a concern in terms of + # performance. However we don't deep copy *all* WCSes because + # the APE-14 API does not mandate the existence of the .deepcopy() + # method, and because it should not be necessary for all WCSes. + wcs_in_cp = wcs_in.deepcopy() if isinstance(wcs_in, WCS) else wcs_in + wcs_out_cp = wcs_out.deepcopy() if isinstance(wcs_out, WCS) else wcs_out + + slices = [ + slice(*x) for x in block_info[None]["array-location"][-wcs_out_cp.pixel_n_dim :] + ] if isinstance(wcs_out, BaseHighLevelWCS): low_level_wcs = SlicedLowLevelWCS(wcs_out_cp.low_level_wcs, slices=slices) From 32e072da7d8b524a1a5ba2820fe8d6b7d7145db4 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Tue, 9 Apr 2024 13:59:10 +0100 Subject: [PATCH 258/366] Expand test to GWCS --- reproject/common.py | 13 +++++----- reproject/tests/test_high_level.py | 41 +++++++++++++++++++++++++----- 2 files changed, 41 insertions(+), 13 deletions(-) diff --git a/reproject/common.py b/reproject/common.py index 5a714d67c..61dac92d3 100644 --- a/reproject/common.py +++ b/reproject/common.py @@ -1,6 +1,7 @@ import os import tempfile import uuid +from copy import deepcopy import dask import dask.array as da @@ -193,12 +194,12 @@ def reproject_single_block(a, array_or_path, block_info=None): # https://github.com/astropy/astropy/issues/16245 # To work around these issues, we make sure we do a deep copy of # the WCS object in here when using FITS WCS. This is a very fast - # operation (<0.1ms) so should not be a concern in terms of - # performance. However we don't deep copy *all* WCSes because - # the APE-14 API does not mandate the existence of the .deepcopy() - # method, and because it should not be necessary for all WCSes. - wcs_in_cp = wcs_in.deepcopy() if isinstance(wcs_in, WCS) else wcs_in - wcs_out_cp = wcs_out.deepcopy() if isinstance(wcs_out, WCS) else wcs_out + # operation (<1ms) so should not be a concern in terms of + # performance. For safety, we do this for all WCS objects even if + # they are not FITS WCS. + + wcs_in_cp = deepcopy(wcs_in) + wcs_out_cp = deepcopy(wcs_out) slices = [ slice(*x) for x in block_info[None]["array-location"][-wcs_out_cp.pixel_n_dim :] diff --git a/reproject/tests/test_high_level.py b/reproject/tests/test_high_level.py index 2bd2045aa..09caa1c1a 100644 --- a/reproject/tests/test_high_level.py +++ b/reproject/tests/test_high_level.py @@ -1,9 +1,12 @@ # Licensed under a 3-clause BSD style license - see LICENSE.rst import itertools +import os +from copy import deepcopy import numpy as np import pytest +from astropy import units as u from astropy.io import fits from astropy.utils.data import get_pkg_data_filename from astropy.wcs import WCS @@ -12,6 +15,8 @@ # TODO: add reference comparisons +DATA = os.path.join(os.path.dirname(__file__), "data") + ALL_MODES = ( "nearest-neighbor", "bilinear", @@ -261,17 +266,39 @@ def test_dimensions_checks(reproject_function): "reproject_function", [reproject_interp, reproject_adaptive, reproject_exact] ) @pytest.mark.parametrize("scheduler", ("synchronous", "processes", "threads")) -def test_dask_schedulers(reproject_function, scheduler): - +# @pytest.mark.parametrize("wcs_type", ("astropy.wcs", "gwcs")) +@pytest.mark.parametrize("wcs_type", ("gwcs",)) +def test_dask_schedulers(reproject_function, scheduler, wcs_type): # Regression test for issues with the multi-threaded scheduler - hdu1 = fits.open(get_pkg_data_filename("galactic_center/gc_2mass_k.fits"))[0] - hdu2 = fits.open(get_pkg_data_filename("galactic_center/gc_msx_e.fits"))[0] - - array1 = reproject_function(hdu1, hdu2.header, return_footprint=False) + if wcs_type == "astropy.wcs": + input_data = fits.open(get_pkg_data_filename("galactic_center/gc_2mass_k.fits"))[0] + hdu_out = fits.open(get_pkg_data_filename("galactic_center/gc_msx_e.fits"))[0] + wcs_out = WCS(hdu_out.header) + shape_out = hdu_out.data.shape + elif wcs_type == "gwcs": + asdf = pytest.importorskip("asdf") + aia = asdf.open(os.path.join(DATA, "aia_171_level1.asdf")) + input_data = (aia["data"][...], aia["wcs"]) + wcs_out = deepcopy(aia["wcs"]) + wcs_out.forward_transform.offset_0 = -60.3123 * u.pix + wcs_out.forward_transform.offset_1 = -61.9422 * u.pix + shape_out = aia["data"].shape + + array1 = reproject_function( + input_data, + wcs_out, + shape_out=shape_out, + return_footprint=False, + ) array2 = reproject_function( - hdu1, hdu2.header, return_footprint=False, return_type="dask", block_size=(100, 100) + input_data, + wcs_out, + shape_out=shape_out, + return_footprint=False, + return_type="dask", + block_size=(100, 100), ) array2 = array2.compute(scheduler=scheduler) From 2f69628b20ef8324064b2bdfa2ed7ce66a05a5b6 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Tue, 9 Apr 2024 14:13:44 +0100 Subject: [PATCH 259/366] Fix GWCS test by setting roundtrip_coords=False since there is an unrelated issue when this is True. --- reproject/tests/test_high_level.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/reproject/tests/test_high_level.py b/reproject/tests/test_high_level.py index 09caa1c1a..82791df74 100644 --- a/reproject/tests/test_high_level.py +++ b/reproject/tests/test_high_level.py @@ -271,6 +271,8 @@ def test_dimensions_checks(reproject_function): def test_dask_schedulers(reproject_function, scheduler, wcs_type): # Regression test for issues with the multi-threaded scheduler + kwargs = {} + if wcs_type == "astropy.wcs": input_data = fits.open(get_pkg_data_filename("galactic_center/gc_2mass_k.fits"))[0] hdu_out = fits.open(get_pkg_data_filename("galactic_center/gc_msx_e.fits"))[0] @@ -278,18 +280,22 @@ def test_dask_schedulers(reproject_function, scheduler, wcs_type): shape_out = hdu_out.data.shape elif wcs_type == "gwcs": asdf = pytest.importorskip("asdf") + if reproject_function == reproject_exact: + pytest.skip() aia = asdf.open(os.path.join(DATA, "aia_171_level1.asdf")) input_data = (aia["data"][...], aia["wcs"]) wcs_out = deepcopy(aia["wcs"]) wcs_out.forward_transform.offset_0 = -60.3123 * u.pix wcs_out.forward_transform.offset_1 = -61.9422 * u.pix shape_out = aia["data"].shape + kwargs["roundtrip_coords"] = False array1 = reproject_function( input_data, wcs_out, shape_out=shape_out, return_footprint=False, + **kwargs, ) array2 = reproject_function( @@ -299,6 +305,7 @@ def test_dask_schedulers(reproject_function, scheduler, wcs_type): return_footprint=False, return_type="dask", block_size=(100, 100), + **kwargs, ) array2 = array2.compute(scheduler=scheduler) From 7eb417bbf804c325e00bbc9dca56bfb59b5b3e9c Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Tue, 9 Apr 2024 14:36:17 +0100 Subject: [PATCH 260/366] Only deepcopy FITS WCSes --- reproject/common.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/reproject/common.py b/reproject/common.py index 61dac92d3..f4dca6878 100644 --- a/reproject/common.py +++ b/reproject/common.py @@ -1,11 +1,11 @@ import os import tempfile import uuid -from copy import deepcopy import dask import dask.array as da import numpy as np +from astropy.wcs import WCS from astropy.wcs.wcsapi import BaseHighLevelWCS, SlicedLowLevelWCS from astropy.wcs.wcsapi.high_level_wcs_wrapper import HighLevelWCSWrapper from dask import delayed @@ -194,12 +194,11 @@ def reproject_single_block(a, array_or_path, block_info=None): # https://github.com/astropy/astropy/issues/16245 # To work around these issues, we make sure we do a deep copy of # the WCS object in here when using FITS WCS. This is a very fast - # operation (<1ms) so should not be a concern in terms of - # performance. For safety, we do this for all WCS objects even if - # they are not FITS WCS. + # operation (<0.1ms) so should not be a concern in terms of + # performance. We only need to do this for FITS WCS. - wcs_in_cp = deepcopy(wcs_in) - wcs_out_cp = deepcopy(wcs_out) + wcs_in_cp = wcs_in.deepcopy() if isinstance(wcs_in, WCS) else wcs_in + wcs_out_cp = wcs_out.deepcopy() if isinstance(wcs_out, WCS) else wcs_out slices = [ slice(*x) for x in block_info[None]["array-location"][-wcs_out_cp.pixel_n_dim :] From b42f7c6da84a56ff3b34fd9cb3da8e8dafde364e Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Wed, 10 Apr 2024 07:51:23 +0100 Subject: [PATCH 261/366] Attempt to fix remaining tests --- pyproject.toml | 4 +--- reproject/tests/test_high_level.py | 1 + 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 5fbf89335..c2d1984d2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -32,9 +32,7 @@ test = [ ] testall = [ "shapely>=2.0.2", # 2.0.2 fixed a bug that causes changes in test results - # Once a release of reproject is done for Python 3.12, - # can remove python_version specifier here - 'sunpy[map]>=2.1;python_version<"3.12" and platform_machine!="i686"', + "sunpy[map]>=2.1", "asdf", "gwcs", "pyvo", diff --git a/reproject/tests/test_high_level.py b/reproject/tests/test_high_level.py index 82791df74..90d23ae51 100644 --- a/reproject/tests/test_high_level.py +++ b/reproject/tests/test_high_level.py @@ -279,6 +279,7 @@ def test_dask_schedulers(reproject_function, scheduler, wcs_type): wcs_out = WCS(hdu_out.header) shape_out = hdu_out.data.shape elif wcs_type == "gwcs": + pytest.importorskip("sunpy", minversion="2.1.0") asdf = pytest.importorskip("asdf") if reproject_function == reproject_exact: pytest.skip() From bec001531cb2c54529fd96291f678ae4b9aae461 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 15 Apr 2024 16:28:18 +0000 Subject: [PATCH 262/366] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/psf/black-pre-commit-mirror: 24.3.0 → 24.4.0](https://github.com/psf/black-pre-commit-mirror/compare/24.3.0...24.4.0) - [github.com/astral-sh/ruff-pre-commit: v0.3.5 → v0.3.7](https://github.com/astral-sh/ruff-pre-commit/compare/v0.3.5...v0.3.7) --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index a55354b5e..7dd58166a 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -52,7 +52,7 @@ repos: exclude: ".*(data.*|extern.*|cextern)$" - repo: https://github.com/psf/black-pre-commit-mirror - rev: 24.3.0 + rev: 24.4.0 hooks: - id: black @@ -77,7 +77,7 @@ repos: - tomli - repo: https://github.com/astral-sh/ruff-pre-commit - rev: "v0.3.5" + rev: "v0.3.7" hooks: - id: ruff args: ["--fix", "--show-fixes"] From df8374be844643466976b4e8770fc47a397ac871 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 22 Apr 2024 16:27:33 +0000 Subject: [PATCH 263/366] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.3.7 → v0.4.1](https://github.com/astral-sh/ruff-pre-commit/compare/v0.3.7...v0.4.1) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 7dd58166a..4e5104441 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -77,7 +77,7 @@ repos: - tomli - repo: https://github.com/astral-sh/ruff-pre-commit - rev: "v0.3.7" + rev: "v0.4.1" hooks: - id: ruff args: ["--fix", "--show-fixes"] From 50bb23f4140af985bff8ee0c9f7b02fc7e0322a6 Mon Sep 17 00:00:00 2001 From: "Adam Ginsburg (keflavich)" Date: Mon, 13 Mar 2023 17:13:14 -0400 Subject: [PATCH 264/366] add ability to specify output array in coadd, and allow specification of block size making --- reproject/mosaicking/coadd.py | 46 +++++++++++++++++++---------------- 1 file changed, 25 insertions(+), 21 deletions(-) diff --git a/reproject/mosaicking/coadd.py b/reproject/mosaicking/coadd.py index 8de178e35..649eb78b8 100644 --- a/reproject/mosaicking/coadd.py +++ b/reproject/mosaicking/coadd.py @@ -22,8 +22,9 @@ def reproject_and_coadd( combine_function="mean", match_background=False, background_reference=None, - output_array=None, - output_footprint=None, + final_array=None, + final_footprint=None, + block_sizes=None, **kwargs, ): """ @@ -89,15 +90,19 @@ def reproject_and_coadd( If `None`, the background matching will make it so that the average of the corrections for all images is zero. If an integer, this specifies the index of the image to use as a reference. - output_array : array or None + final_array : array or None The final output array. Specify this if you already have an appropriately-shaped array to store the data in. Must match shape - specified with ``shape_out`` or derived from the output projection. - output_footprint : array or None + specified with `shape_out` or derived from the output + projection. + final_footprint : array or None The final output footprint array. Specify this if you already have an appropriately-shaped array to store the data in. Must match shape - specified with ``shape_out`` or derived from the output projection. - **kwargs + specified with `shape_out` or derived from the output projection. + block_sizes : list of tuples or None + The block size to use for each cube (optional; meant for dask use) + + kwargs Keyword arguments to be passed to the reprojection function. Returns @@ -128,16 +133,12 @@ def reproject_and_coadd( wcs_out, shape_out = parse_output_projection(output_projection, shape_out=shape_out) - if output_array is not None and output_array.shape != shape_out: - raise ValueError( - "If you specify an output array, it must have a shape matching " - f"the output shape {shape_out}" - ) - if output_footprint is not None and output_footprint.shape != shape_out: - raise ValueError( - "If you specify an output footprint array, it must have a shape matching " - f"the output shape {shape_out}" - ) + if final_array is not None and final_array.shape != shape_out: + raise ValueError("If you specify an output array, it must have a shape matching " + f"the output shape {shape_out}") + if final_footprint is not None and final_footprint.shape != shape_out: + raise ValueError("If you specify an output footprint array, it must have a shape matching " + f"the output shape {shape_out}") # Start off by reprojecting individual images to the final projection @@ -203,6 +204,9 @@ def reproject_and_coadd( shape_out_indiv = (jmax - jmin, imax - imin) + if block_sizes is not None: + kwargs['block_size'] = block_sizes[idata] + # TODO: optimize handling of weights by making reprojection functions # able to handle weights, and make the footprint become the combined # footprint + weight map @@ -253,10 +257,10 @@ def reproject_and_coadd( # At this point, the images are now ready to be co-added. - if output_array is None: - output_array = np.zeros(shape_out) - if output_footprint is None: - output_footprint = np.zeros(shape_out) + if final_array is None: + final_array = np.zeros(shape_out) + if final_footprint is None: + final_footprint = np.zeros(shape_out) if combine_function == "min": output_array[...] = np.inf From c012c5b1d60df9d2b5e24173bd4f1a7d768e58d7 Mon Sep 17 00:00:00 2001 From: "Adam Ginsburg (keflavich)" Date: Mon, 13 Mar 2023 17:59:58 -0400 Subject: [PATCH 265/366] allow reproject_and_coadd to operate on cubes --- reproject/mosaicking/coadd.py | 57 ++++++++++++++++++++++++----------- 1 file changed, 39 insertions(+), 18 deletions(-) diff --git a/reproject/mosaicking/coadd.py b/reproject/mosaicking/coadd.py index 649eb78b8..c56dc6dc9 100644 --- a/reproject/mosaicking/coadd.py +++ b/reproject/mosaicking/coadd.py @@ -167,13 +167,23 @@ def reproject_and_coadd( # significant distortion (when the edges of the input image become # convex in the output projection), and transforming every edge pixel, # which provides a lot of redundant information. - ny, nx = array_in.shape - n_per_edge = 11 - xs = np.linspace(-0.5, nx - 0.5, n_per_edge) - ys = np.linspace(-0.5, ny - 0.5, n_per_edge) - xs = np.concatenate((xs, np.full(n_per_edge, xs[-1]), xs, np.full(n_per_edge, xs[0]))) - ys = np.concatenate((np.full(n_per_edge, ys[0]), ys, np.full(n_per_edge, ys[-1]), ys)) - xc_out, yc_out = wcs_out.world_to_pixel(wcs_in.pixel_to_world(xs, ys)) + if array_in.ndim == 2: + ny, nx = array_in.shape + n_per_edge = 11 + xs = np.linspace(-0.5, nx - 0.5, n_per_edge) + ys = np.linspace(-0.5, ny - 0.5, n_per_edge) + xs = np.concatenate((xs, np.full(n_per_edge, xs[-1]), xs, np.full(n_per_edge, xs[0]))) + ys = np.concatenate((np.full(n_per_edge, ys[0]), ys, np.full(n_per_edge, ys[-1]), ys)) + xc_out, yc_out = wcs_out.world_to_pixel(wcs_in.pixel_to_world(xs, ys)) + elif array_in.ndim == 3: + # for cubes, we only handle single corners now + nz, ny, nx = array_in.shape + xc = np.array([-0.5, nx - 0.5, nx - 0.5, -0.5]) + yc = np.array([-0.5, -0.5, ny - 0.5, ny - 0.5]) + zc = np.array([-0.5, nz - 0.5]) + xc_out, yc_out = wcs_out.celestial.world_to_pixel(wcs_in.celestial.pixel_to_world(xc, yc)) + zc_out = wcs_out.spectral.world_to_pixel(wcs_in.spectral.pixel_to_world(zc)) + shape_out_cel = shape_out[1:] # Determine the cutout parameters @@ -183,26 +193,37 @@ def reproject_and_coadd( if np.any(np.isnan(xc_out)) or np.any(np.isnan(yc_out)): imin = 0 - imax = shape_out[1] + imax = shape_out_cel[1] jmin = 0 - jmax = shape_out[0] + jmax = shape_out_cel[0] else: imin = max(0, int(np.floor(xc_out.min() + 0.5))) - imax = min(shape_out[1], int(np.ceil(xc_out.max() + 0.5))) + imax = min(shape_out_cel[1], int(np.ceil(xc_out.max() + 0.5))) jmin = max(0, int(np.floor(yc_out.min() + 0.5))) - jmax = min(shape_out[0], int(np.ceil(yc_out.max() + 0.5))) + jmax = min(shape_out_cel[0], int(np.ceil(yc_out.max() + 0.5))) if imax < imin or jmax < jmin: continue - if isinstance(wcs_out, WCS): - wcs_out_indiv = wcs_out[jmin:jmax, imin:imax] - else: - wcs_out_indiv = SlicedLowLevelWCS( - wcs_out.low_level_wcs, (slice(jmin, jmax), slice(imin, imax)) - ) + if array_in.ndim == 2: + if isinstance(wcs_out, WCS): + wcs_out_indiv = wcs_out[jmin:jmax, imin:imax] + else: + wcs_out_indiv = SlicedLowLevelWCS( + wcs_out.low_level_wcs, (slice(jmin, jmax), slice(imin, imax)) + ) + shape_out_indiv = (jmax - jmin, imax - imin) + elif array_in.ndim == 3: + kmin = max(0, int(np.floor(zc_out.min() + 0.5))) + kmax = min(shape_out[0], int(np.ceil(zc_out.max() + 0.5))) + if isinstance(wcs_out, WCS): + wcs_out_indiv = wcs_out[kmin:kmax, jmin:jmax, imin:imax] + else: + wcs_out_indiv = SlicedLowLevelWCS( + wcs_out.low_level_wcs, (slice(kmin, kmax), slice(jmin, jmax), slice(imin, imax)) + ) + shape_out_indiv = (kmax - kmin, jmax - jmin, imax - imin) - shape_out_indiv = (jmax - jmin, imax - imin) if block_sizes is not None: kwargs['block_size'] = block_sizes[idata] From 71c85a599918adafd9021b02e0aa9d189c1e185f Mon Sep 17 00:00:00 2001 From: "Adam Ginsburg (keflavich)" Date: Mon, 13 Mar 2023 18:04:21 -0400 Subject: [PATCH 266/366] implement astrofrog suggestions --- reproject/mosaicking/coadd.py | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/reproject/mosaicking/coadd.py b/reproject/mosaicking/coadd.py index c56dc6dc9..bac8421e8 100644 --- a/reproject/mosaicking/coadd.py +++ b/reproject/mosaicking/coadd.py @@ -22,8 +22,8 @@ def reproject_and_coadd( combine_function="mean", match_background=False, background_reference=None, - final_array=None, - final_footprint=None, + output_array=None, + output_footprint=None, block_sizes=None, **kwargs, ): @@ -90,17 +90,18 @@ def reproject_and_coadd( If `None`, the background matching will make it so that the average of the corrections for all images is zero. If an integer, this specifies the index of the image to use as a reference. - final_array : array or None + output_array : array or None The final output array. Specify this if you already have an appropriately-shaped array to store the data in. Must match shape specified with `shape_out` or derived from the output projection. - final_footprint : array or None + output_footprint : array or None The final output footprint array. Specify this if you already have an appropriately-shaped array to store the data in. Must match shape specified with `shape_out` or derived from the output projection. block_sizes : list of tuples or None - The block size to use for each cube (optional; meant for dask use) + The block size to use for each dataset. Could also be a single tuple + if you want the sample block size for all data sets kwargs Keyword arguments to be passed to the reprojection function. @@ -133,10 +134,10 @@ def reproject_and_coadd( wcs_out, shape_out = parse_output_projection(output_projection, shape_out=shape_out) - if final_array is not None and final_array.shape != shape_out: + if output_array is not None and output_array.shape != shape_out: raise ValueError("If you specify an output array, it must have a shape matching " f"the output shape {shape_out}") - if final_footprint is not None and final_footprint.shape != shape_out: + if output_footprint is not None and output_footprint.shape != shape_out: raise ValueError("If you specify an output footprint array, it must have a shape matching " f"the output shape {shape_out}") @@ -226,7 +227,10 @@ def reproject_and_coadd( if block_sizes is not None: - kwargs['block_size'] = block_sizes[idata] + if len(block_sizes) == len(input_data) and len(block_sizes[idata]) == len(shape_out): + kwargs['block_size'] = block_sizes[idata] + else: + kwargs['block_size'] = block_sizes # TODO: optimize handling of weights by making reprojection functions # able to handle weights, and make the footprint become the combined @@ -278,10 +282,10 @@ def reproject_and_coadd( # At this point, the images are now ready to be co-added. - if final_array is None: - final_array = np.zeros(shape_out) - if final_footprint is None: - final_footprint = np.zeros(shape_out) + if output_array is None: + output_array = np.zeros(shape_out) + if output_footprint is None: + output_footprint = np.zeros(shape_out) if combine_function == "min": output_array[...] = np.inf From e40477c06c473951f56c8576ee104e948dae163d Mon Sep 17 00:00:00 2001 From: "Adam Ginsburg (keflavich)" Date: Mon, 13 Mar 2023 18:28:46 -0400 Subject: [PATCH 267/366] extend dimensionality of reprojectedarraysubset --- reproject/mosaicking/coadd.py | 3 +- reproject/mosaicking/subset_array.py | 125 +++++++++++++++++++-------- 2 files changed, 92 insertions(+), 36 deletions(-) diff --git a/reproject/mosaicking/coadd.py b/reproject/mosaicking/coadd.py index bac8421e8..7c58d4a59 100644 --- a/reproject/mosaicking/coadd.py +++ b/reproject/mosaicking/coadd.py @@ -214,6 +214,7 @@ def reproject_and_coadd( wcs_out.low_level_wcs, (slice(jmin, jmax), slice(imin, imax)) ) shape_out_indiv = (jmax - jmin, imax - imin) + kmin, kmax = None, None # for reprojectedarraysubset below elif array_in.ndim == 3: kmin = max(0, int(np.floor(zc_out.min() + 0.5))) kmax = min(shape_out[0], int(np.ceil(zc_out.max() + 0.5))) @@ -264,7 +265,7 @@ def reproject_and_coadd( weights[reset] = 0.0 footprint *= weights - array = ReprojectedArraySubset(array, footprint, imin, imax, jmin, jmax) + array = ReprojectedArraySubset(array, footprint, imin, imax, jmin, jmax, kmin, kmax) # TODO: make sure we gracefully handle the case where the # output image is empty (due e.g. to no overlap). diff --git a/reproject/mosaicking/subset_array.py b/reproject/mosaicking/subset_array.py index 37bde7ebb..9e9334a0e 100644 --- a/reproject/mosaicking/subset_array.py +++ b/reproject/mosaicking/subset_array.py @@ -15,35 +15,56 @@ class ReprojectedArraySubset: # rather than the center, which is not well defined for even-sized # cutouts. - def __init__(self, array, footprint, imin, imax, jmin, jmax): + def __init__(self, array, footprint, imin, imax, jmin, jmax, kmin=None, kmax=None): self.array = array self.footprint = footprint self.imin = imin self.imax = imax self.jmin = jmin self.jmax = jmax + self.kmin = kmin + self.kmax = kmax def __repr__(self): - return f"" + if self.kmin is not None: + return f"" + else: + return f"" @property def view_in_original_array(self): - return (slice(self.jmin, self.jmax), slice(self.imin, self.imax)) + if self.kmin is not None: + return (slice(self.kmin, self.kmax), slice(self.jmin, self.jmax), slice(self.imin, self.imax)) + else + return (slice(self.jmin, self.jmax), slice(self.imin, self.imax)) @property def shape(self): - return (self.jmax - self.jmin, self.imax - self.imin) + if self.kmin is not None: + return (self.kmax - self.kmin, self.jmax - self.jmin, self.imax - self.imin) + else: + return (self.jmax - self.jmin, self.imax - self.imin) def overlaps(self, other): # Note that the use of <= or >= instead of < and > is due to # the fact that the max values are exclusive (so +1 above the # last value). - return not ( - self.imax <= other.imin - or other.imax <= self.imin - or self.jmax <= other.jmin - or other.jmax <= self.jmin - ) + if self.kmin is not None: + return not ( + self.imax <= other.imin + or other.imax <= self.imin + or self.jmax <= other.jmin + or other.jmax <= self.jmin + or self.kmax <= other.kmin + or other.kmax <= self.kmin + ) + else: + return not ( + self.imax <= other.imin + or other.imax <= self.imin + or self.jmax <= other.jmin + or other.jmax <= self.jmin + ) def __add__(self, other): return self._operation(other, operator.add) @@ -71,29 +92,63 @@ def _operation(self, other, op): if jmax < jmin: jmax = jmin - # Extract cutout from each - self_array = self.array[ - jmin - self.jmin : jmax - self.jmin, - imin - self.imin : imax - self.imin, - ] - self_footprint = self.footprint[ - jmin - self.jmin : jmax - self.jmin, - imin - self.imin : imax - self.imin, - ] - - other_array = other.array[ - jmin - other.jmin : jmax - other.jmin, - imin - other.imin : imax - other.imin, - ] - other_footprint = other.footprint[ - jmin - other.jmin : jmax - other.jmin, - imin - other.imin : imax - other.imin, - ] - - # Carry out operator and store result in ReprojectedArraySubset - - array = op(self_array, other_array) - footprint = (self_footprint > 0) & (other_footprint > 0) - - return ReprojectedArraySubset(array, footprint, imin, imax, jmin, jmax) + if self.kmin is None: + # Extract cutout from each + + self_array = self.array[ + jmin - self.jmin : jmax - self.jmin, + imin - self.imin : imax - self.imin, + ] + self_footprint = self.footprint[ + jmin - self.jmin : jmax - self.jmin, + imin - self.imin : imax - self.imin, + ] + + other_array = other.array[ + jmin - other.jmin : jmax - other.jmin, + imin - other.imin : imax - other.imin, + ] + other_footprint = other.footprint[ + jmin - other.jmin : jmax - other.jmin, + imin - other.imin : imax - other.imin, + ] + + # Carry out operator and store result in ReprojectedArraySubset + + array = op(self_array, other_array) + footprint = (self_footprint > 0) & (other_footprint > 0) + + return ReprojectedArraySubset(array, footprint, imin, imax, jmin, jmax) + + else: + # Extract cutout from each + + self_array = self.array[ + kmin - self.kmin : kmax - self.kmin, + jmin - self.jmin : jmax - self.jmin, + imin - self.imin : imax - self.imin, + ] + self_footprint = self.footprint[ + kmin - self.kmin : kmax - self.kmin, + jmin - self.jmin : jmax - self.jmin, + imin - self.imin : imax - self.imin, + ] + + other_array = other.array[ + kmin - other.kmin : kmax - other.kmin, + jmin - other.jmin : jmax - other.jmin, + imin - other.imin : imax - other.imin, + ] + other_footprint = other.footprint[ + kmin - other.kmin : kmax - other.kmin, + jmin - other.jmin : jmax - other.jmin, + imin - other.imin : imax - other.imin, + ] + + # Carry out operator and store result in ReprojectedArraySubset + + array = op(self_array, other_array) + footprint = (self_footprint > 0) & (other_footprint > 0) + + return ReprojectedArraySubset(array, footprint, imin, imax, jmin, jmax, kmin, kmax) From a35c415dd99b9c354746c419a311128900cac26a Mon Sep 17 00:00:00 2001 From: "Adam Ginsburg (keflavich)" Date: Mon, 13 Mar 2023 18:31:05 -0400 Subject: [PATCH 268/366] fix typo --- reproject/mosaicking/subset_array.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reproject/mosaicking/subset_array.py b/reproject/mosaicking/subset_array.py index 9e9334a0e..bf0d299a9 100644 --- a/reproject/mosaicking/subset_array.py +++ b/reproject/mosaicking/subset_array.py @@ -35,7 +35,7 @@ def __repr__(self): def view_in_original_array(self): if self.kmin is not None: return (slice(self.kmin, self.kmax), slice(self.jmin, self.jmax), slice(self.imin, self.imax)) - else + else: return (slice(self.jmin, self.jmax), slice(self.imin, self.imax)) @property From e177cbff46fe60eeed226062228b262b45eb82ba Mon Sep 17 00:00:00 2001 From: "Adam Ginsburg (keflavich)" Date: Mon, 13 Mar 2023 18:55:01 -0400 Subject: [PATCH 269/366] only store all the arrays in memory if matching backgrounds; otherwise very wasteful --- reproject/mosaicking/coadd.py | 33 ++++++++++++++++++++++++++------- 1 file changed, 26 insertions(+), 7 deletions(-) diff --git a/reproject/mosaicking/coadd.py b/reproject/mosaicking/coadd.py index 7c58d4a59..be74e5f85 100644 --- a/reproject/mosaicking/coadd.py +++ b/reproject/mosaicking/coadd.py @@ -141,9 +141,15 @@ def reproject_and_coadd( raise ValueError("If you specify an output footprint array, it must have a shape matching " f"the output shape {shape_out}") + if output_array is None: + output_array = np.zeros(shape_out) + if output_footprint is None: + output_footprint = np.zeros(shape_out) + # Start off by reprojecting individual images to the final projection - arrays = [] + if match_background: + arrays = [] for idata in range(len(input_data)): # We need to pre-parse the data here since we need to figure out how to @@ -270,7 +276,18 @@ def reproject_and_coadd( # TODO: make sure we gracefully handle the case where the # output image is empty (due e.g. to no overlap). - arrays.append(array) + if match_background: + arrays.append(array) + else: + if combine_function in ("mean", "sum"): + # By default, values outside of the footprint are set to NaN + # but we set these to 0 here to avoid getting NaNs in the + # means/sums. + array.array[array.footprint == 0] = 0 + + output_array[array.view_in_original_array] += array.array * array.footprint + output_footprint[array.view_in_original_array] += array.footprint + # If requested, try and match the backgrounds. if match_background and len(arrays) > 1: @@ -281,12 +298,14 @@ def reproject_and_coadd( for array, correction in zip(arrays, corrections, strict=True): array.array -= correction - # At this point, the images are now ready to be co-added. + # At this point, the images are now ready to be co-added. - if output_array is None: - output_array = np.zeros(shape_out) - if output_footprint is None: - output_footprint = np.zeros(shape_out) + if combine_function in ("mean", "sum"): + for array in arrays: + # By default, values outside of the footprint are set to NaN + # but we set these to 0 here to avoid getting NaNs in the + # means/sums. + array.array[array.footprint == 0] = 0 if combine_function == "min": output_array[...] = np.inf From 32080b42791e2ed8b17b0c12ab372dc3e5c1f202 Mon Sep 17 00:00:00 2001 From: "Adam Ginsburg (keflavich)" Date: Mon, 13 Mar 2023 21:38:54 -0400 Subject: [PATCH 270/366] add a progressbar option --- reproject/mosaicking/coadd.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/reproject/mosaicking/coadd.py b/reproject/mosaicking/coadd.py index be74e5f85..4ec50e8ee 100644 --- a/reproject/mosaicking/coadd.py +++ b/reproject/mosaicking/coadd.py @@ -102,6 +102,9 @@ def reproject_and_coadd( block_sizes : list of tuples or None The block size to use for each dataset. Could also be a single tuple if you want the sample block size for all data sets + progressbar : False + If specified, use this as a progressbar to track loop iterations over + data sets. kwargs Keyword arguments to be passed to the reprojection function. @@ -130,6 +133,9 @@ def reproject_and_coadd( "reprojection function should be specified with the reproject_function argument" ) + if not progressbar: + progressbar = lambda x: x + # Parse the output projection to avoid having to do it for each wcs_out, shape_out = parse_output_projection(output_projection, shape_out=shape_out) @@ -151,7 +157,7 @@ def reproject_and_coadd( if match_background: arrays = [] - for idata in range(len(input_data)): + for idata in progressbar(range(len(input_data))): # We need to pre-parse the data here since we need to figure out how to # optimize/minimize the size of each output tile (see below). array_in, wcs_in = parse_input_data(input_data[idata], hdu_in=hdu_in) From 600783524ccdb06ae9d23809a4a9d57c7ed2a0f6 Mon Sep 17 00:00:00 2001 From: "Adam Ginsburg (keflavich)" Date: Mon, 13 Mar 2023 23:42:34 -0400 Subject: [PATCH 271/366] add progressbar as kwd --- reproject/mosaicking/coadd.py | 1 + 1 file changed, 1 insertion(+) diff --git a/reproject/mosaicking/coadd.py b/reproject/mosaicking/coadd.py index 4ec50e8ee..5ca605b65 100644 --- a/reproject/mosaicking/coadd.py +++ b/reproject/mosaicking/coadd.py @@ -25,6 +25,7 @@ def reproject_and_coadd( output_array=None, output_footprint=None, block_sizes=None, + progressbar=False, **kwargs, ): """ From 89754902c143c729d3bb5ba3f10f55bbce8a8113 Mon Sep 17 00:00:00 2001 From: "Adam Ginsburg (keflavich)" Date: Mon, 13 Mar 2023 23:46:41 -0400 Subject: [PATCH 272/366] change dimensional subsetting to operate on low-level wcs --- reproject/mosaicking/coadd.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/reproject/mosaicking/coadd.py b/reproject/mosaicking/coadd.py index 5ca605b65..908010c34 100644 --- a/reproject/mosaicking/coadd.py +++ b/reproject/mosaicking/coadd.py @@ -195,8 +195,9 @@ def reproject_and_coadd( xc = np.array([-0.5, nx - 0.5, nx - 0.5, -0.5]) yc = np.array([-0.5, -0.5, ny - 0.5, ny - 0.5]) zc = np.array([-0.5, nz - 0.5]) - xc_out, yc_out = wcs_out.celestial.world_to_pixel(wcs_in.celestial.pixel_to_world(xc, yc)) - zc_out = wcs_out.spectral.world_to_pixel(wcs_in.spectral.pixel_to_world(zc)) + # TODO: figure out what to do here if the low_level_wcs doesn't support subsetting + xc_out, yc_out = wcs_out.low_level_wcs.celestial.world_to_pixel(wcs_in.celestial.pixel_to_world(xc, yc)) + zc_out = wcs_out.low_level_wcs.spectral.world_to_pixel(wcs_in.spectral.pixel_to_world(zc)) shape_out_cel = shape_out[1:] # Determine the cutout parameters From 9a98b4261420f1d7be8e61c21af8dbbfcee2cfd3 Mon Sep 17 00:00:00 2001 From: "Adam Ginsburg (keflavich)" Date: Wed, 15 Mar 2023 11:48:53 -0400 Subject: [PATCH 273/366] add a helpful error msg --- reproject/mosaicking/coadd.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/reproject/mosaicking/coadd.py b/reproject/mosaicking/coadd.py index 908010c34..22823022c 100644 --- a/reproject/mosaicking/coadd.py +++ b/reproject/mosaicking/coadd.py @@ -199,6 +199,8 @@ def reproject_and_coadd( xc_out, yc_out = wcs_out.low_level_wcs.celestial.world_to_pixel(wcs_in.celestial.pixel_to_world(xc, yc)) zc_out = wcs_out.low_level_wcs.spectral.world_to_pixel(wcs_in.spectral.pixel_to_world(zc)) shape_out_cel = shape_out[1:] + else: + raise ValueError(f"Wrong number of dimensions: {array_in.ndim}") # Determine the cutout parameters From 22cfce97f104039a923f77ab572ad0925bcac668 Mon Sep 17 00:00:00 2001 From: "Adam Ginsburg (keflavich)" Date: Sat, 9 Sep 2023 11:02:50 -0400 Subject: [PATCH 274/366] Fix several issues --- reproject/mosaicking/coadd.py | 102 ++++++++++++++------------- reproject/mosaicking/subset_array.py | 19 +++-- reproject/utils.py | 5 +- 3 files changed, 66 insertions(+), 60 deletions(-) diff --git a/reproject/mosaicking/coadd.py b/reproject/mosaicking/coadd.py index 22823022c..62faef59c 100644 --- a/reproject/mosaicking/coadd.py +++ b/reproject/mosaicking/coadd.py @@ -26,6 +26,7 @@ def reproject_and_coadd( output_footprint=None, block_sizes=None, progressbar=False, + blank_pixel_value=np.nan, **kwargs, ): """ @@ -128,6 +129,9 @@ def reproject_and_coadd( if combine_function not in ("mean", "sum", "median", "first", "last", "min", "max"): raise ValueError("combine_function should be one of mean/sum/median/first/last/min/max") + elif combine_function == "median": + # Note to devs: the exception shoudl be raised as early as possible + raise NotImplementedError("combine_function='median' is not yet implemented") if reproject_function is None: raise ValueError( @@ -142,11 +146,15 @@ def reproject_and_coadd( wcs_out, shape_out = parse_output_projection(output_projection, shape_out=shape_out) if output_array is not None and output_array.shape != shape_out: - raise ValueError("If you specify an output array, it must have a shape matching " - f"the output shape {shape_out}") + raise ValueError( + "If you specify an output array, it must have a shape matching " + f"the output shape {shape_out}" + ) if output_footprint is not None and output_footprint.shape != shape_out: - raise ValueError("If you specify an output footprint array, it must have a shape matching " - f"the output shape {shape_out}") + raise ValueError( + "If you specify an output footprint array, it must have a shape matching " + f"the output shape {shape_out}" + ) if output_array is None: output_array = np.zeros(shape_out) @@ -154,7 +162,6 @@ def reproject_and_coadd( output_footprint = np.zeros(shape_out) # Start off by reprojecting individual images to the final projection - if match_background: arrays = [] @@ -189,6 +196,7 @@ def reproject_and_coadd( xs = np.concatenate((xs, np.full(n_per_edge, xs[-1]), xs, np.full(n_per_edge, xs[0]))) ys = np.concatenate((np.full(n_per_edge, ys[0]), ys, np.full(n_per_edge, ys[-1]), ys)) xc_out, yc_out = wcs_out.world_to_pixel(wcs_in.pixel_to_world(xs, ys)) + shape_out_cel = shape_out elif array_in.ndim == 3: # for cubes, we only handle single corners now nz, ny, nx = array_in.shape @@ -196,8 +204,12 @@ def reproject_and_coadd( yc = np.array([-0.5, -0.5, ny - 0.5, ny - 0.5]) zc = np.array([-0.5, nz - 0.5]) # TODO: figure out what to do here if the low_level_wcs doesn't support subsetting - xc_out, yc_out = wcs_out.low_level_wcs.celestial.world_to_pixel(wcs_in.celestial.pixel_to_world(xc, yc)) - zc_out = wcs_out.low_level_wcs.spectral.world_to_pixel(wcs_in.spectral.pixel_to_world(zc)) + xc_out, yc_out = wcs_out.low_level_wcs.celestial.world_to_pixel( + wcs_in.celestial.pixel_to_world(xc, yc) + ) + zc_out = wcs_out.low_level_wcs.spectral.world_to_pixel( + wcs_in.spectral.pixel_to_world(zc) + ) shape_out_cel = shape_out[1:] else: raise ValueError(f"Wrong number of dimensions: {array_in.ndim}") @@ -230,7 +242,7 @@ def reproject_and_coadd( wcs_out.low_level_wcs, (slice(jmin, jmax), slice(imin, imax)) ) shape_out_indiv = (jmax - jmin, imax - imin) - kmin, kmax = None, None # for reprojectedarraysubset below + kmin, kmax = None, None # for reprojectedarraysubset below elif array_in.ndim == 3: kmin = max(0, int(np.floor(zc_out.min() + 0.5))) kmax = min(shape_out[0], int(np.ceil(zc_out.max() + 0.5))) @@ -242,12 +254,11 @@ def reproject_and_coadd( ) shape_out_indiv = (kmax - kmin, jmax - jmin, imax - imin) - if block_sizes is not None: if len(block_sizes) == len(input_data) and len(block_sizes[idata]) == len(shape_out): - kwargs['block_size'] = block_sizes[idata] + kwargs["block_size"] = block_sizes[idata] else: - kwargs['block_size'] = block_sizes + kwargs["block_size"] = block_sizes # TODO: optimize handling of weights by making reprojection functions # able to handle weights, and make the footprint become the combined @@ -298,7 +309,6 @@ def reproject_and_coadd( output_array[array.view_in_original_array] += array.array * array.footprint output_footprint[array.view_in_original_array] += array.footprint - # If requested, try and match the backgrounds. if match_background and len(arrays) > 1: offset_matrix = determine_offset_matrix(arrays) @@ -308,64 +318,58 @@ def reproject_and_coadd( for array, correction in zip(arrays, corrections, strict=True): array.array -= correction - # At this point, the images are now ready to be co-added. - - if combine_function in ("mean", "sum"): - for array in arrays: - # By default, values outside of the footprint are set to NaN - # but we set these to 0 here to avoid getting NaNs in the - # means/sums. - array.array[array.footprint == 0] = 0 - if combine_function == "min": output_array[...] = np.inf elif combine_function == "max": output_array[...] = -np.inf if combine_function in ("mean", "sum"): - for array in arrays: - # By default, values outside of the footprint are set to NaN - # but we set these to 0 here to avoid getting NaNs in the - # means/sums. - array.array[array.footprint == 0] = 0 + if match_background: + # if we're not matching the background, this part has already been done + for array in arrays: + # By default, values outside of the footprint are set to NaN + # but we set these to 0 here to avoid getting NaNs in the + # means/sums. + array.array[array.footprint == 0] = 0 - output_array[array.view_in_original_array] += array.array * array.footprint - output_footprint[array.view_in_original_array] += array.footprint + output_array[array.view_in_original_array] += array.array * array.footprint + output_footprint[array.view_in_original_array] += array.footprint if combine_function == "mean": with np.errstate(invalid="ignore"): output_array /= output_footprint - output_array[output_footprint == 0] = 0 + output_array[output_footprint == 0] = blank_pixel_value elif combine_function in ("first", "last", "min", "max"): - for array in arrays: - if combine_function == "first": - mask = (output_footprint[array.view_in_original_array] == 0) & (array.footprint > 0) - elif combine_function == "last": - mask = array.footprint > 0 - elif combine_function == "min": - mask = (array.footprint > 0) & ( - array.array < output_array[array.view_in_original_array] + if match_background: + for array in arrays: + if combine_function == "first": + mask = output_footprint[array.view_in_original_array] == 0 + elif combine_function == "last": + mask = array.footprint > 0 + elif combine_function == "min": + mask = (array.footprint > 0) & ( + array.array < output_array[array.view_in_original_array] + ) + elif combine_function == "max": + mask = (array.footprint > 0) & ( + array.array > output_array[array.view_in_original_array] + ) + + output_footprint[array.view_in_original_array] = np.where( + mask, array.footprint, output_footprint[array.view_in_original_array] ) - elif combine_function == "max": - mask = (array.footprint > 0) & ( - array.array > output_array[array.view_in_original_array] + output_array[array.view_in_original_array] = np.where( + mask, array.array, output_array[array.view_in_original_array] ) - output_footprint[array.view_in_original_array] = np.where( - mask, array.footprint, output_footprint[array.view_in_original_array] - ) - output_array[array.view_in_original_array] = np.where( - mask, array.array, output_array[array.view_in_original_array] - ) - elif combine_function == "median": # Here we need to operate in chunks since we could otherwise run # into memory issues + # this is redundant, but left as a note-to-devs about where such an implementation belongs raise NotImplementedError("combine_function='median' is not yet implemented") - if combine_function in ("min", "max"): - output_array[output_footprint == 0] = 0.0 + output_array[output_footprint == 0] = blank_pixel_value return output_array, output_footprint diff --git a/reproject/mosaicking/subset_array.py b/reproject/mosaicking/subset_array.py index bf0d299a9..5133644c0 100644 --- a/reproject/mosaicking/subset_array.py +++ b/reproject/mosaicking/subset_array.py @@ -34,7 +34,11 @@ def __repr__(self): @property def view_in_original_array(self): if self.kmin is not None: - return (slice(self.kmin, self.kmax), slice(self.jmin, self.jmax), slice(self.imin, self.imax)) + return ( + slice(self.kmin, self.kmax), + slice(self.jmin, self.jmax), + slice(self.imin, self.imax), + ) else: return (slice(self.jmin, self.jmax), slice(self.imin, self.imax)) @@ -92,26 +96,21 @@ def _operation(self, other, op): if jmax < jmin: jmax = jmin - if self.kmin is None: # Extract cutout from each self_array = self.array[ - jmin - self.jmin : jmax - self.jmin, - imin - self.imin : imax - self.imin, + jmin - self.jmin : jmax - self.jmin, imin - self.imin : imax - self.imin ] self_footprint = self.footprint[ - jmin - self.jmin : jmax - self.jmin, - imin - self.imin : imax - self.imin, + jmin - self.jmin : jmax - self.jmin, imin - self.imin : imax - self.imin ] other_array = other.array[ - jmin - other.jmin : jmax - other.jmin, - imin - other.imin : imax - other.imin, + jmin - other.jmin : jmax - other.jmin, imin - other.imin : imax - other.imin ] other_footprint = other.footprint[ - jmin - other.jmin : jmax - other.jmin, - imin - other.imin : imax - other.imin, + jmin - other.jmin : jmax - other.jmin, imin - other.imin : imax - other.imin ] # Carry out operator and store result in ReprojectedArraySubset diff --git a/reproject/utils.py b/reproject/utils.py index 3ff46555c..9f72eabbe 100644 --- a/reproject/utils.py +++ b/reproject/utils.py @@ -38,7 +38,10 @@ def _dask_to_numpy_memmap(dask_array, tmp_dir): with tempfile.TemporaryDirectory() as zarr_tmp: # First compute and store the dask array to zarr using whatever # the default scheduler is at this point - dask_array.to_zarr(zarr_tmp) + try: + dask_array.to_zarr(zarr_tmp) + except ValueError: + dask_array.rechunk().to_zarr(zarr_tmp) # Load the array back to dask zarr_array = da.from_zarr(zarr_tmp) From bc503a47efaebd09f6ef8ea2985e95f0bef383ad Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Tue, 4 Jun 2024 23:49:19 +0100 Subject: [PATCH 275/366] Rename progressbar to progress_bar, document blank_pixel_value, and remove 'median' as an advertised option since it isn't implemented. --- reproject/mosaicking/coadd.py | 39 ++++++++++++++++------------------- 1 file changed, 18 insertions(+), 21 deletions(-) diff --git a/reproject/mosaicking/coadd.py b/reproject/mosaicking/coadd.py index 62faef59c..0b3e4d4ee 100644 --- a/reproject/mosaicking/coadd.py +++ b/reproject/mosaicking/coadd.py @@ -11,6 +11,10 @@ __all__ = ["reproject_and_coadd"] +def _noop(iterable): + return iterable + + def reproject_and_coadd( input_data, output_projection, @@ -25,7 +29,7 @@ def reproject_and_coadd( output_array=None, output_footprint=None, block_sizes=None, - progressbar=False, + progress_bar=None, blank_pixel_value=np.nan, **kwargs, ): @@ -80,7 +84,7 @@ def reproject_and_coadd( `~astropy.io.fits.HDUList` instance, specifies the HDU to use. reproject_function : callable The function to use for the reprojection. - combine_function : { 'mean', 'sum', 'median', 'first', 'last', 'min', 'max' } + combine_function : { 'mean', 'sum', 'first', 'last', 'min', 'max' } The type of function to use for combining the values into the final image. For 'first' and 'last', respectively, the reprojected images are simply overlaid on top of each other. With respect to the order of the @@ -103,12 +107,15 @@ def reproject_and_coadd( specified with `shape_out` or derived from the output projection. block_sizes : list of tuples or None The block size to use for each dataset. Could also be a single tuple - if you want the sample block size for all data sets - progressbar : False - If specified, use this as a progressbar to track loop iterations over + if you want the sample block size for all data sets. + progress_bar : callable, optional + If specified, use this as a progress_bar to track loop iterations over data sets. + blank_pixel_value : float, optional + Value to use for areas of the resulting mosaic that do not have input + data. - kwargs + **kwargs Keyword arguments to be passed to the reprojection function. Returns @@ -127,19 +134,16 @@ def reproject_and_coadd( # Validate inputs - if combine_function not in ("mean", "sum", "median", "first", "last", "min", "max"): - raise ValueError("combine_function should be one of mean/sum/median/first/last/min/max") - elif combine_function == "median": - # Note to devs: the exception shoudl be raised as early as possible - raise NotImplementedError("combine_function='median' is not yet implemented") + if combine_function not in ("mean", "sum", "first", "last", "min", "max"): + raise ValueError("combine_function should be one of mean/sum/first/last/min/max") if reproject_function is None: raise ValueError( "reprojection function should be specified with the reproject_function argument" ) - if not progressbar: - progressbar = lambda x: x + if progress_bar is None: + progress_bar = _noop # Parse the output projection to avoid having to do it for each @@ -165,7 +169,7 @@ def reproject_and_coadd( if match_background: arrays = [] - for idata in progressbar(range(len(input_data))): + for idata in progress_bar(range(len(input_data))): # We need to pre-parse the data here since we need to figure out how to # optimize/minimize the size of each output tile (see below). array_in, wcs_in = parse_input_data(input_data[idata], hdu_in=hdu_in) @@ -363,13 +367,6 @@ def reproject_and_coadd( mask, array.array, output_array[array.view_in_original_array] ) - elif combine_function == "median": - # Here we need to operate in chunks since we could otherwise run - # into memory issues - - # this is redundant, but left as a note-to-devs about where such an implementation belongs - raise NotImplementedError("combine_function='median' is not yet implemented") - output_array[output_footprint == 0] = blank_pixel_value return output_array, output_footprint From f5e3016d5cbea5fbd648b08b9ccb913b87356ee3 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Wed, 5 Jun 2024 11:54:25 +0100 Subject: [PATCH 276/366] Generalize reproject_and_coadd to N-dimensions and fix test failures --- reproject/array_utils.py | 21 +- reproject/mosaicking/coadd.py | 180 ++++++++---------- reproject/mosaicking/subset_array.py | 152 +++++---------- reproject/mosaicking/tests/test_coadd.py | 1 - .../mosaicking/tests/test_subset_array.py | 42 ++-- 5 files changed, 176 insertions(+), 220 deletions(-) diff --git a/reproject/array_utils.py b/reproject/array_utils.py index ec3a39a2e..1d5aae562 100644 --- a/reproject/array_utils.py +++ b/reproject/array_utils.py @@ -1,6 +1,6 @@ import numpy as np -__all__ = ["map_coordinates"] +__all__ = ["map_coordinates", "sample_array_edges"] def map_coordinates(image, coords, **kwargs): @@ -35,3 +35,22 @@ def map_coordinates(image, coords, **kwargs): values[reset] = kwargs.get("cval", 0.0) return values + + +def sample_array_edges(shape, *, n_samples): + # Given an N-dimensional array shape, sample each edge of the array using + # the requested number of samples (which will include vertices). To do this + # we iterate through the dimensions and for each one we sample the points + # in that dimension and iterate over the combination of other vertices. + # Returns an array with dimensions (N, n_samples) + all_positions = [] + ndim = len(shape) + shape = np.array(shape) + for idim in range(ndim): + for vertex in range(2**ndim): + positions = -0.5 + shape * ((vertex & (2 ** np.arange(ndim))) > 0).astype(int) + positions = np.broadcast_to(positions, (n_samples, ndim)).copy() + positions[:, idim] = np.linspace(-0.5, shape[idim] - 0.5, n_samples) + all_positions.append(positions) + positions = np.unique(np.vstack(all_positions), axis=0).T + return positions diff --git a/reproject/mosaicking/coadd.py b/reproject/mosaicking/coadd.py index 0b3e4d4ee..bbeb652d9 100644 --- a/reproject/mosaicking/coadd.py +++ b/reproject/mosaicking/coadd.py @@ -4,6 +4,7 @@ from astropy.wcs import WCS from astropy.wcs.wcsapi import SlicedLowLevelWCS +from ..array_utils import sample_array_edges from ..utils import parse_input_data, parse_input_weights, parse_output_projection from .background import determine_offset_matrix, solve_corrections_sgd from .subset_array import ReprojectedArraySubset @@ -30,15 +31,13 @@ def reproject_and_coadd( output_footprint=None, block_sizes=None, progress_bar=None, - blank_pixel_value=np.nan, + blank_pixel_value=0, **kwargs, ): """ - Given a set of input images, reproject and co-add these to a single + Given a set of input data, reproject and co-add these to a single final image. - This currently only works with 2-d images with celestial WCS. - Parameters ---------- input_data : iterable @@ -149,24 +148,31 @@ def reproject_and_coadd( wcs_out, shape_out = parse_output_projection(output_projection, shape_out=shape_out) - if output_array is not None and output_array.shape != shape_out: + if output_array is None: + output_array = np.zeros(shape_out) + elif output_array.shape != shape_out: raise ValueError( "If you specify an output array, it must have a shape matching " f"the output shape {shape_out}" ) - if output_footprint is not None and output_footprint.shape != shape_out: + + if output_footprint is None: + output_footprint = np.zeros(shape_out) + elif output_footprint.shape != shape_out: raise ValueError( "If you specify an output footprint array, it must have a shape matching " f"the output shape {shape_out}" ) - if output_array is None: - output_array = np.zeros(shape_out) - if output_footprint is None: - output_footprint = np.zeros(shape_out) + # Define 'on-the-fly' mode: in the case where we don't need to match + # the backgrounds and we are combining with 'mean' or 'sum', we don't + # have to keep track of the intermediate arrays and can just modify + # the output array on-the-fly + on_the_fly = not match_background and combine_function in ("mean", "sum") # Start off by reprojecting individual images to the final projection - if match_background: + + if not on_the_fly: arrays = [] for idata in progress_bar(range(len(input_data))): @@ -192,31 +198,9 @@ def reproject_and_coadd( # significant distortion (when the edges of the input image become # convex in the output projection), and transforming every edge pixel, # which provides a lot of redundant information. - if array_in.ndim == 2: - ny, nx = array_in.shape - n_per_edge = 11 - xs = np.linspace(-0.5, nx - 0.5, n_per_edge) - ys = np.linspace(-0.5, ny - 0.5, n_per_edge) - xs = np.concatenate((xs, np.full(n_per_edge, xs[-1]), xs, np.full(n_per_edge, xs[0]))) - ys = np.concatenate((np.full(n_per_edge, ys[0]), ys, np.full(n_per_edge, ys[-1]), ys)) - xc_out, yc_out = wcs_out.world_to_pixel(wcs_in.pixel_to_world(xs, ys)) - shape_out_cel = shape_out - elif array_in.ndim == 3: - # for cubes, we only handle single corners now - nz, ny, nx = array_in.shape - xc = np.array([-0.5, nx - 0.5, nx - 0.5, -0.5]) - yc = np.array([-0.5, -0.5, ny - 0.5, ny - 0.5]) - zc = np.array([-0.5, nz - 0.5]) - # TODO: figure out what to do here if the low_level_wcs doesn't support subsetting - xc_out, yc_out = wcs_out.low_level_wcs.celestial.world_to_pixel( - wcs_in.celestial.pixel_to_world(xc, yc) - ) - zc_out = wcs_out.low_level_wcs.spectral.world_to_pixel( - wcs_in.spectral.pixel_to_world(zc) - ) - shape_out_cel = shape_out[1:] - else: - raise ValueError(f"Wrong number of dimensions: {array_in.ndim}") + + edges = sample_array_edges(array_in.shape, n_samples=11)[::-1] + edges_out = wcs_out.world_to_pixel(wcs_in.pixel_to_world(*edges))[::-1] # Determine the cutout parameters @@ -224,39 +208,32 @@ def reproject_and_coadd( # such as all-sky images or full solar disk views. In this case we skip # this step and just use the full output WCS for reprojection. - if np.any(np.isnan(xc_out)) or np.any(np.isnan(yc_out)): - imin = 0 - imax = shape_out_cel[1] - jmin = 0 - jmax = shape_out_cel[0] - else: - imin = max(0, int(np.floor(xc_out.min() + 0.5))) - imax = min(shape_out_cel[1], int(np.ceil(xc_out.max() + 0.5))) - jmin = max(0, int(np.floor(yc_out.min() + 0.5))) - jmax = min(shape_out_cel[0], int(np.ceil(yc_out.max() + 0.5))) + ndim_out = len(shape_out) - if imax < imin or jmax < jmin: + skip_data = False + if np.any(np.isnan(edges_out)): + bounds = list(zip([0] * ndim_out, shape_out)) + else: + bounds = [] + for idim in range(ndim_out): + imin = max(0, int(np.floor(edges_out[idim].min() + 0.5))) + imax = min(shape_out[idim], int(np.ceil(edges_out[idim].max() + 0.5))) + bounds.append((imin, imax)) + if imax < imin: + skip_data = True + break + + if skip_data: continue - if array_in.ndim == 2: - if isinstance(wcs_out, WCS): - wcs_out_indiv = wcs_out[jmin:jmax, imin:imax] - else: - wcs_out_indiv = SlicedLowLevelWCS( - wcs_out.low_level_wcs, (slice(jmin, jmax), slice(imin, imax)) - ) - shape_out_indiv = (jmax - jmin, imax - imin) - kmin, kmax = None, None # for reprojectedarraysubset below - elif array_in.ndim == 3: - kmin = max(0, int(np.floor(zc_out.min() + 0.5))) - kmax = min(shape_out[0], int(np.ceil(zc_out.max() + 0.5))) - if isinstance(wcs_out, WCS): - wcs_out_indiv = wcs_out[kmin:kmax, jmin:jmax, imin:imax] - else: - wcs_out_indiv = SlicedLowLevelWCS( - wcs_out.low_level_wcs, (slice(kmin, kmax), slice(jmin, jmax), slice(imin, imax)) - ) - shape_out_indiv = (kmax - kmin, jmax - jmin, imax - imin) + slice_out = tuple([slice(imin, imax) for (imin, imax) in bounds]) + + if isinstance(wcs_out, WCS): + wcs_out_indiv = wcs_out[slice_out] + else: + wcs_out_indiv = SlicedLowLevelWCS(wcs_out.low_level_wcs, slice_out) + + shape_out_indiv = [imax - imin for (imin, imax) in bounds] if block_sizes is not None: if len(block_sizes) == len(input_data) and len(block_sizes[idata]) == len(shape_out): @@ -296,22 +273,20 @@ def reproject_and_coadd( weights[reset] = 0.0 footprint *= weights - array = ReprojectedArraySubset(array, footprint, imin, imax, jmin, jmax, kmin, kmax) + array = ReprojectedArraySubset(array, footprint, bounds) # TODO: make sure we gracefully handle the case where the # output image is empty (due e.g. to no overlap). - if match_background: - arrays.append(array) + if on_the_fly: + # By default, values outside of the footprint are set to NaN + # but we set these to 0 here to avoid getting NaNs in the + # means/sums. + array.array[array.footprint == 0] = 0 + output_array[array.view_in_original_array] += array.array * array.footprint + output_footprint[array.view_in_original_array] += array.footprint else: - if combine_function in ("mean", "sum"): - # By default, values outside of the footprint are set to NaN - # but we set these to 0 here to avoid getting NaNs in the - # means/sums. - array.array[array.footprint == 0] = 0 - - output_array[array.view_in_original_array] += array.array * array.footprint - output_footprint[array.view_in_original_array] += array.footprint + arrays.append(array) # If requested, try and match the backgrounds. if match_background and len(arrays) > 1: @@ -322,11 +297,6 @@ def reproject_and_coadd( for array, correction in zip(arrays, corrections, strict=True): array.array -= correction - if combine_function == "min": - output_array[...] = np.inf - elif combine_function == "max": - output_array[...] = -np.inf - if combine_function in ("mean", "sum"): if match_background: # if we're not matching the background, this part has already been done @@ -336,8 +306,8 @@ def reproject_and_coadd( # means/sums. array.array[array.footprint == 0] = 0 - output_array[array.view_in_original_array] += array.array * array.footprint - output_footprint[array.view_in_original_array] += array.footprint + output_array[array.view_in_original_array] += array.array * array.footprint + output_footprint[array.view_in_original_array] += array.footprint if combine_function == "mean": with np.errstate(invalid="ignore"): @@ -345,28 +315,32 @@ def reproject_and_coadd( output_array[output_footprint == 0] = blank_pixel_value elif combine_function in ("first", "last", "min", "max"): - if match_background: - for array in arrays: - if combine_function == "first": - mask = output_footprint[array.view_in_original_array] == 0 - elif combine_function == "last": - mask = array.footprint > 0 - elif combine_function == "min": - mask = (array.footprint > 0) & ( - array.array < output_array[array.view_in_original_array] - ) - elif combine_function == "max": - mask = (array.footprint > 0) & ( - array.array > output_array[array.view_in_original_array] - ) - - output_footprint[array.view_in_original_array] = np.where( - mask, array.footprint, output_footprint[array.view_in_original_array] + if combine_function == "min": + output_array[...] = np.inf + elif combine_function == "max": + output_array[...] = -np.inf + + for array in arrays: + if combine_function == "first": + mask = output_footprint[array.view_in_original_array] == 0 + elif combine_function == "last": + mask = array.footprint > 0 + elif combine_function == "min": + mask = (array.footprint > 0) & ( + array.array < output_array[array.view_in_original_array] ) - output_array[array.view_in_original_array] = np.where( - mask, array.array, output_array[array.view_in_original_array] + elif combine_function == "max": + mask = (array.footprint > 0) & ( + array.array > output_array[array.view_in_original_array] ) + output_footprint[array.view_in_original_array] = np.where( + mask, array.footprint, output_footprint[array.view_in_original_array] + ) + output_array[array.view_in_original_array] = np.where( + mask, array.array, output_array[array.view_in_original_array] + ) + output_array[output_footprint == 0] = blank_pixel_value return output_array, output_footprint diff --git a/reproject/mosaicking/subset_array.py b/reproject/mosaicking/subset_array.py index 5133644c0..010114e0a 100644 --- a/reproject/mosaicking/subset_array.py +++ b/reproject/mosaicking/subset_array.py @@ -15,60 +15,36 @@ class ReprojectedArraySubset: # rather than the center, which is not well defined for even-sized # cutouts. - def __init__(self, array, footprint, imin, imax, jmin, jmax, kmin=None, kmax=None): + def __init__(self, array, footprint, bounds): self.array = array self.footprint = footprint - self.imin = imin - self.imax = imax - self.jmin = jmin - self.jmax = jmax - self.kmin = kmin - self.kmax = kmax + self.bounds = bounds def __repr__(self): - if self.kmin is not None: - return f"" - else: - return f"" + bounds_str = "[" + ",".join(f"{imin}:{imax}" for (imin, imax) in self.bounds) + "]" + return f"" @property def view_in_original_array(self): - if self.kmin is not None: - return ( - slice(self.kmin, self.kmax), - slice(self.jmin, self.jmax), - slice(self.imin, self.imax), - ) - else: - return (slice(self.jmin, self.jmax), slice(self.imin, self.imax)) + return tuple([slice(imin, imax) for (imin, imax) in self.bounds]) @property def shape(self): - if self.kmin is not None: - return (self.kmax - self.kmin, self.jmax - self.jmin, self.imax - self.imin) - else: - return (self.jmax - self.jmin, self.imax - self.imin) + return tuple((imax - imin) for (imin, imax) in self.bounds) def overlaps(self, other): # Note that the use of <= or >= instead of < and > is due to # the fact that the max values are exclusive (so +1 above the # last value). - if self.kmin is not None: - return not ( - self.imax <= other.imin - or other.imax <= self.imin - or self.jmax <= other.jmin - or other.jmax <= self.jmin - or self.kmax <= other.kmin - or other.kmax <= self.kmin - ) - else: - return not ( - self.imax <= other.imin - or other.imax <= self.imin - or self.jmax <= other.jmin - or other.jmax <= self.jmin + if len(self.bounds) != len(other.bounds): + raise ValueError( + f"Mismatch in number of dimensions, expected " + f"{len(self.bounds)} dimensions and got {len(other.bounds)}" ) + for (imin, imax), (imin_other, imax_other) in zip(self.bounds, other.bounds, strict=False): + if imax <= imin_other or imax_other <= imin: + return False + return True def __add__(self, other): return self._operation(other, operator.add) @@ -83,71 +59,39 @@ def __truediv__(self, other): return self._operation(other, operator.truediv) def _operation(self, other, op): + if len(self.bounds) != len(other.bounds): + raise ValueError( + f"Mismatch in number of dimensions, expected " + f"{len(self.bounds)} dimensions and got {len(other.bounds)}" + ) + # Determine cutout parameters for overlap region - imin = max(self.imin, other.imin) - imax = min(self.imax, other.imax) - jmin = max(self.jmin, other.jmin) - jmax = min(self.jmax, other.jmax) - - if imax < imin: - imax = imin - - if jmax < jmin: - jmax = jmin - - if self.kmin is None: - # Extract cutout from each - - self_array = self.array[ - jmin - self.jmin : jmax - self.jmin, imin - self.imin : imax - self.imin - ] - self_footprint = self.footprint[ - jmin - self.jmin : jmax - self.jmin, imin - self.imin : imax - self.imin - ] - - other_array = other.array[ - jmin - other.jmin : jmax - other.jmin, imin - other.imin : imax - other.imin - ] - other_footprint = other.footprint[ - jmin - other.jmin : jmax - other.jmin, imin - other.imin : imax - other.imin - ] - - # Carry out operator and store result in ReprojectedArraySubset - - array = op(self_array, other_array) - footprint = (self_footprint > 0) & (other_footprint > 0) - - return ReprojectedArraySubset(array, footprint, imin, imax, jmin, jmax) - - else: - # Extract cutout from each - - self_array = self.array[ - kmin - self.kmin : kmax - self.kmin, - jmin - self.jmin : jmax - self.jmin, - imin - self.imin : imax - self.imin, - ] - self_footprint = self.footprint[ - kmin - self.kmin : kmax - self.kmin, - jmin - self.jmin : jmax - self.jmin, - imin - self.imin : imax - self.imin, - ] - - other_array = other.array[ - kmin - other.kmin : kmax - other.kmin, - jmin - other.jmin : jmax - other.jmin, - imin - other.imin : imax - other.imin, - ] - other_footprint = other.footprint[ - kmin - other.kmin : kmax - other.kmin, - jmin - other.jmin : jmax - other.jmin, - imin - other.imin : imax - other.imin, - ] - - # Carry out operator and store result in ReprojectedArraySubset - - array = op(self_array, other_array) - footprint = (self_footprint > 0) & (other_footprint > 0) - - return ReprojectedArraySubset(array, footprint, imin, imax, jmin, jmax, kmin, kmax) + overlap_bounds = [] + self_slices = [] + other_slices = [] + for (imin, imax), (imin_other, imax_other) in zip(self.bounds, other.bounds, strict=False): + imin_overlap = max(imin, imin_other) + imax_overlap = min(imax, imax_other) + if imax_overlap < imin_overlap: + imax_overlap = imin_overlap + overlap_bounds.append((imin_overlap, imax_overlap)) + self_slices.append(slice(imin_overlap - imin, imax_overlap - imin)) + other_slices.append(slice(imin_overlap - imin_other, imax_overlap - imin_other)) + + self_slices = tuple(self_slices) + + self_array = self.array[self_slices] + self_footprint = self.footprint[self_slices] + + other_slices = tuple(other_slices) + + other_array = other.array[other_slices] + other_footprint = other.footprint[other_slices] + + # Carry out operator and store result in ReprojectedArraySubset + + array = op(self_array, other_array) + footprint = (self_footprint > 0) & (other_footprint > 0) + + return ReprojectedArraySubset(array, footprint, overlap_bounds) diff --git a/reproject/mosaicking/tests/test_coadd.py b/reproject/mosaicking/tests/test_coadd.py index d9e3de683..04b7c271c 100644 --- a/reproject/mosaicking/tests/test_coadd.py +++ b/reproject/mosaicking/tests/test_coadd.py @@ -80,7 +80,6 @@ def test_coadd_no_overlap(self, combine_function, reproject_function): input_data = self._get_tiles(self._nonoverlapping_views) - input_data = [(self.array, self.wcs)] array, footprint = reproject_and_coadd( input_data, self.wcs, diff --git a/reproject/mosaicking/tests/test_subset_array.py b/reproject/mosaicking/tests/test_subset_array.py index 3ecde69f9..dc05ef76c 100644 --- a/reproject/mosaicking/tests/test_subset_array.py +++ b/reproject/mosaicking/tests/test_subset_array.py @@ -14,21 +14,35 @@ def setup_method(self, method): self.array1 = np.random.random((123, 87)) self.array2 = np.random.random((123, 87)) self.array3 = np.random.random((123, 87)) + self.array4 = np.random.random((123, 87, 16)) self.footprint1 = (self.array1 > 0.5).astype(int) self.footprint2 = (self.array2 > 0.5).astype(int) self.footprint3 = (self.array3 > 0.5).astype(int) + self.footprint4 = (self.array4 > 0.5).astype(int) self.subset1 = ReprojectedArraySubset( - self.array1[20:88, 34:40], self.footprint1[20:88, 34:40], 34, 40, 20, 88 + self.array1[20:88, 34:40], + self.footprint1[20:88, 34:40], + [(20, 88), (34, 40)], ) self.subset2 = ReprojectedArraySubset( - self.array2[50:123, 37:42], self.footprint2[50:123, 37:42], 37, 42, 50, 123 + self.array2[50:123, 37:42], + self.footprint2[50:123, 37:42], + [(50, 123), (37, 42)], ) self.subset3 = ReprojectedArraySubset( - self.array3[40:50, 11:19], self.footprint3[40:50, 11:19], 11, 19, 40, 50 + self.array3[40:50, 11:19], + self.footprint3[40:50, 11:19], + [(40, 50), (11, 19)], + ) + + self.subset4 = ReprojectedArraySubset( + self.array4[30:35, 40:45, 1:4], + self.footprint4[30:35, 40:45, 1:4], + [(30, 35), (40, 45), (1, 4)], ) def test_repr(self): @@ -55,17 +69,23 @@ def test_overlaps(self): @pytest.mark.parametrize("op", [operator.add, operator.sub, operator.mul, operator.truediv]) def test_arithmetic(self, op): subset = op(self.subset1, self.subset2) - assert subset.imin == 37 - assert subset.imax == 40 - assert subset.jmin == 50 - assert subset.jmax == 88 + assert subset.bounds == [(50, 88), (37, 40)] expected = op(self.array1[50:88, 37:40], self.array2[50:88, 37:40]) assert_equal(subset.array, expected) def test_arithmetic_nooverlap(self): subset = self.subset1 - self.subset3 - assert subset.imin == 34 - assert subset.imax == 34 - assert subset.jmin == 40 - assert subset.jmax == 50 + assert subset.bounds == [(40, 50), (34, 34)] assert subset.shape == (10, 0) + + def test_overlaps_dimension_mismatch(self): + with pytest.raises( + ValueError, match=("Mismatch in number of dimensions, expected 2 dimensions and got 3") + ): + self.subset1.overlaps(self.subset4) + + def test_arithmetic_dimension_mismatch(self): + with pytest.raises( + ValueError, match=("Mismatch in number of dimensions, expected 2 dimensions and got 3") + ): + self.subset1 - self.subset4 From 7974067b22e69fc1bb1d81773d4c7bfba3d34616 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 10 Jun 2024 16:27:58 +0000 Subject: [PATCH 277/366] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/psf/black-pre-commit-mirror: 24.4.0 → 24.4.2](https://github.com/psf/black-pre-commit-mirror/compare/24.4.0...24.4.2) - [github.com/scientific-python/cookie: 2024.03.10 → 2024.04.23](https://github.com/scientific-python/cookie/compare/2024.03.10...2024.04.23) - [github.com/codespell-project/codespell: v2.2.6 → v2.3.0](https://github.com/codespell-project/codespell/compare/v2.2.6...v2.3.0) - [github.com/astral-sh/ruff-pre-commit: v0.4.1 → v0.4.8](https://github.com/astral-sh/ruff-pre-commit/compare/v0.4.1...v0.4.8) --- .pre-commit-config.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 4e5104441..5eb60665c 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -52,7 +52,7 @@ repos: exclude: ".*(data.*|extern.*|cextern)$" - repo: https://github.com/psf/black-pre-commit-mirror - rev: 24.4.0 + rev: 24.4.2 hooks: - id: black @@ -64,12 +64,12 @@ repos: exclude: ".*(tests.*)$" - repo: https://github.com/scientific-python/cookie - rev: 2024.03.10 + rev: 2024.04.23 hooks: - id: sp-repo-review - repo: https://github.com/codespell-project/codespell - rev: v2.2.6 + rev: v2.3.0 hooks: - id: codespell args: ["--write-changes"] @@ -77,7 +77,7 @@ repos: - tomli - repo: https://github.com/astral-sh/ruff-pre-commit - rev: "v0.4.1" + rev: "v0.4.8" hooks: - id: ruff args: ["--fix", "--show-fixes"] From 8cbdcd198036ae190051a6d9e4b6e543ba5e4d17 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Thu, 14 Sep 2023 15:36:55 +0100 Subject: [PATCH 278/366] Updates to documentation to consolidate performance information --- docs/celestial.rst | 103 +------------------ docs/dask.rst | 108 ------------------- docs/index.rst | 4 +- docs/installation.rst | 23 ++--- docs/performance.rst | 234 ++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 247 insertions(+), 225 deletions(-) delete mode 100644 docs/dask.rst create mode 100644 docs/performance.rst diff --git a/docs/celestial.rst b/docs/celestial.rst index 633be5def..a494d5fac 100644 --- a/docs/celestial.rst +++ b/docs/celestial.rst @@ -80,8 +80,8 @@ of the package, e.g.:: >>> from reproject import reproject_exact All functions share a common set of arguments, as well as including -algorithm-dependent arguments. In this section, we take a look at the common -arguments. +algorithm-dependent arguments. In this section, we take a look at some of the +common arguments. The reprojection functions take two main arguments. The first argument is the image to reproject, together with WCS information about the image. This can be @@ -109,19 +109,6 @@ also be specified, and be given the shape of the output image using the Numpy :class:`~astropy.io.fits.Header`, does not contain information about image size). -For the interpolation and adaptive algorithms, an optional third argument, -``roundtrip_coords`` is accepted. By default, after coordinates are transformed -from the output plane to the input plane, the input-plane coordinates are -transformed back to the output plane to ensure that the transformation is -defined in both directions. This doubles the amount of -coordinate-transformation work to be done. In speed-critical situations, where -it is known that the coordinate transformation is defined everywhere, this -extra work can be disabled by setting ``roundtrip_coords=False``. (Note that -this is not a question of whether each output pixel maps to an existing *pixel* -in the input image and vice-versa, but whether it maps to a valid *coordinate* -in the coordinate system of the input image---regardless of whether that -coordinate falls within the bounds of the input image.) - As an example, we start off by opening a FITS file using Astropy:: >>> from astropy.io import fits @@ -403,89 +390,3 @@ details). .. warning:: The :func:`~reproject.reproject_exact` is currently known to have precision issues for images with resolutions <0.05". For now it is therefore best to avoid using it with such images. - -Very large datasets -=================== - -If you have a very large dataset to reproject, i.e., any normal IFU or radio -spectral cube with many individual spectral channels - you may not be able to -hold two copies of the dataset in memory. In this case, you can specify an -output memory mapped array to store the data. For now this only works with the -interpolation reprojection methods. - -.. doctest-skip:: - - >>> outhdr = fits.Header.fromtextfile('cube_header_gal.hdr') - >>> shape = (outhdr['NAXIS3'], outhdr['NAXIS2'], outhdr['NAXIS1']) - >>> outarray = np.memmap(filename='output.np', mode='w+', shape=shape, dtype='float32') - >>> hdu = fits.open('cube_file.fits') - >>> rslt = reproject.reproject_interp(hdu, outhdr, output_array=outarray, - ... return_footprint=False, - ... independent_celestial_slices=True) - >>> newhdu = fits.PrimaryHDU(data=outarray, header=outhdr) - >>> newhdu.writeto('new_cube_file.fits') - -Or if you're dealing with FITS files, you can skip the numpy memmap step and use `FITS large file creation -`_. - -.. doctest-skip:: - - >>> outhdr = fits.Header.fromtextfile('cube_header_gal.hdr') - >>> outhdr.tofile('new_cube.fits') - >>> shape = tuple(outhdr['NAXIS{0}'.format(ii)] for ii in range(1, outhdr['NAXIS']+1)) - >>> with open('new_cube.fits', 'rb+') as fobj: - >>> fobj.seek(len(outhdr.tostring()) + (np.product(shape) * np.abs(outhdr['BITPIX']//8)) - 1) - >>> fobj.write(b'\0') - >>> outhdu = fits.open('new_cube.fits', mode='update') - >>> rslt = reproject.reproject_interp(hdu, outhdr, output_array=outhdu[0].data, - ... return_footprint=False, - ... independent_celestial_slices=True) - >>> outhdu.flush() - -Multiple images with the same coordinates -========================================= - -If you have multiple images with the exact same coordinate system (e.g. a raw -image and a corresponding processed image) and want to reproject both to the -same output frame, it is faster to compute the coordinate mapping between input -and output pixels only once and reuse this mapping for each reprojection. This -is supported by passing multiple input images as an additional dimension in the -input data array. When the input array contains more dimensions than the input -WCS describes, the extra leading dimensions are taken to represent separate -images with the same coordinates, and the reprojection loops over those -dimensions after computing the pixel mapping. For example: - -.. doctest-skip:: - >>> raw_image, header_in = fits.getdata('raw_image.fits', header=True) - >>> bg_subtracted_image = fits.getdata('background_subtracted_image.fits') - >>> header_out = # Prepare your desired output projection here - >>> # Combine the two images into one array - >>> image_stack = np.stack((raw_image, bg_subtracted_image)) - >>> # We provide a header that describes 2 WCS dimensions, but our input - >>> # array shape is (2, ny, nx)---the 'extra' first dimension represents - >>> # separate images sharing the same coordinates. - >>> reprojected, footprint = reproject.reproject_adaptive( - ... (image_stack, header_in), header_out) - >>> # The shape of `reprojected` is (2, ny', nx') - >>> reprojected_raw, reprojected_bg_subtracted = reprojected[0], reprojected[1] - -For :func:`~reproject.reproject_interp` and -:func:`~reproject.reproject_adaptive`, this is approximately twice as fast as -reprojecting the two images separately. For :func:`~reproject.reproject_exact` -the savings are much less significant, as producing the coordinate mapping is a -much smaller portion of the total runtime for this algorithm. - -When the output coordinates are provided as a WCS and a ``shape_out`` tuple, -``shape_out`` may describe the output shape of a single image, in which case -the extra leading dimensions are prepended automatically, or it may include the -extra dimensions, in which case the size of the extra dimensions must match -those of the input data exactly. - -While the reproject functions can accept the name of a FITS file as input, from -which the input data and coordinates are loaded automatically, this multi-image -reprojection feature does not support loading multiple images automatically -from multiple HDUs within one FITS file, as it would be difficult to verify -automatically that the HDUs contain the same exact coordinates. If multiple -HDUs do share coordinates and are to be reprojected together, they must be -separately loaded and combined into a single input array (e.g. using -``np.stack`` as in the above example). diff --git a/docs/dask.rst b/docs/dask.rst deleted file mode 100644 index 90075f67d..000000000 --- a/docs/dask.rst +++ /dev/null @@ -1,108 +0,0 @@ -Integration with dask and parallel processing -============================================= - -The following functions all integrate well with the `dask `_ library. - -* :func:`~reproject.reproject_interp` -* :func:`~reproject.reproject_adaptive` -* :func:`~reproject.reproject_exact` - -This integration has several aspects that we will discuss in the following sections. - -.. testsetup:: - - >>> import numpy as np - >>> import dask.array as da - >>> input_array = np.random.random((1024, 1024)) - >>> dask_array = da.from_array(input_array, chunks=(128, 128)) - >>> from astropy.wcs import WCS - >>> wcs_in = WCS(naxis=2) - >>> wcs_out = WCS(naxis=2) - -Input dask arrays ------------------ - -The three reprojection functions mentioned above can accept dask arrays as -inputs, e.g. assuming you have already constructed a dask array named -``dask_array``:: - - >>> dask_array - dask.array - -you can pass this in as part of the first argument to one of the reprojection -functions:: - - >>> from reproject import reproject_interp - >>> array, footprint = reproject_interp((dask_array, wcs_in), wcs_out, - ... shape_out=(2048, 2048)) - -In general however, we cannot benefit much from the chunking of the input arrays -because any input pixel might in principle contribute to any output pixel. -Therefore, for now, when a dask array is passed as input, it is computed using -the current default scheduler and converted to a Numpy memory-mapped array. This -is done efficiently in terms of memory and never results in the whole dataset -being loaded into memory at any given time. However, this does require -sufficient space on disk to store the array. - -Chunk by chunk reprojection and parallel processing ---------------------------------------------------- - -Regardless of whether a dask or Numpy array is passed in as input to the -reprojection functions, you can specify a block size to use for the -reprojection, and this is used to iterate over chunks in the output array in -chunks. For instance, if you pass in a (1024, 1024) array and specify that the -shape of the output should be a (2048, 2048) array (e.g., via ``shape_out``), -then if you set ``block_size=(256, 256)``:: - - >>> input_array.shape - (1024, 1024) - >>> array, footprint = reproject_interp((input_array, wcs_in), wcs_out, - ... shape_out=(2048, 2048), block_size=(256, 256)) - -the reprojection will be done in 64 separate output chunks. Note however that -this does not break up the input array into chunks since in the general case any -input pixel may contribute to any output pixel. - -By default, the iteration over the output chunks is done in a single -process/thread, but you may specify ``parallel=True`` to process these in -parallel. If you do this, reproject will use multiple processes (rather than -threads) to parallelize the computation (this is because the core reprojection -algorithms we use are not currently thread-safe). If you specify -``parallel=True``, then ``block_size`` will be automatically set to a sensible -default, but you can also set ``block_size`` manually for more control. Note -that you can also set ``parallel=`` to an integer to indicate the number of -processes to use. - -Output dask arrays ------------------- - -By default, the reprojection functions will do the computation immediately and -return Numpy arrays for the reprojected array and optionally the footprint (this -is regardless of whether dask or Numpy arrays were passed in, or any of the -parallelization options above). However, by setting ``return_type='dask'``, you -can make the functions delay any computation and return dask arrays:: - - >>> array, footprint = reproject_interp((input_array, wcs_in), wcs_out, - ... shape_out=(2048, 2048), block_size=(256, 256), - ... return_type='dask') - >>> array - dask.array - -You can then compute the array or a section of the array yourself whenever you need, or use the -result in further dask expressions. - -.. warning:: The reprojection does not currently work reliably when using multiple threads, so - it is important to make sure you use a dask scheduler that is not multi-threaded. - At the time of writing, the default dask scheduler is ``threads``, so the scheduler - needs to be explicitly set to a different one. - -Using dask.distributed ----------------------- - -The `dask.distributed `_ package makes it -possible to use distributed schedulers for dask. In order to compute -reprojections with dask.distributed, you should make use of the -``return_type='dask'`` option mentioned above so that you can compute the dask -array once the distributed scheduler has been set up. As mentioned in `Output -dask arrays`_, you should make sure that you limit any cluster to have one -thread per process or the results may be unreliable. diff --git a/docs/index.rst b/docs/index.rst index 6552ad26d..eddf73456 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -19,7 +19,7 @@ You can install *reproject* with `pip ` or with `conda `_:: - conda install -c astropy reproject + conda install -c conda-forge reproject More detailed installation instructions can be found in :ref:`installation`. @@ -137,7 +137,7 @@ that you want to reproject. noncelestial footprints mosaicking - dask + performance Reference/API ============= diff --git a/docs/installation.rst b/docs/installation.rst index 1d7731e3f..92144e526 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -7,7 +7,7 @@ Installing reproject Requirements ============ -This package has the following hard run time dependencies: +This package has the following dependencies: * `Python `__ 3.9 or later @@ -19,20 +19,15 @@ This package has the following hard run time dependencies: * `astropy-healpix `_ 0.6 or later for HEALPIX image reprojection -and the following optional dependencies: - -* `shapely `_ 1.6 or later for some of the mosaicking functionality - -and to run the tests, you will also need: +* `dask `_ 2021.8 or later -* `Matplotlib `__ +* `zarr `_ -* `pytest-arraydiff `__ +* `fsspec `_ -* `pytest-astropy `__ - -* `pytest-doctestplus `__ +and the following optional dependencies: +* `shapely `_ 1.6 or later for some of the mosaicking functionality Installation ============ @@ -40,14 +35,14 @@ Installation Using pip --------- -To install *reproject* with `pip `_, -simply run: +To install *reproject* with `pip `_, +run:: pip install reproject Using conda ----------- -To install *reproject* with `anaconda `_, simply run:: +To install *reproject* with `conda `_, run:: conda install -c conda-forge reproject diff --git a/docs/performance.rst b/docs/performance.rst new file mode 100644 index 000000000..edde111cd --- /dev/null +++ b/docs/performance.rst @@ -0,0 +1,234 @@ +********************** +Optimizing performance +********************** + +Disabling coordinate transformation round-tripping +================================================== + +For the interpolation and adaptive algorithms, an optional third argument, +``roundtrip_coords`` is accepted. By default, after coordinates are transformed +from the output plane to the input plane, the input-plane coordinates are +transformed back to the output plane to ensure that the transformation is +defined in both directions. This doubles the amount of +coordinate-transformation work to be done. In speed-critical situations, where +it is known that the coordinate transformation is defined everywhere, this +extra work can be disabled by setting ``roundtrip_coords=False``. (Note that +this is not a question of whether each output pixel maps to an existing *pixel* +in the input image and vice-versa, but whether it maps to a valid *coordinate* +in the coordinate system of the input image---regardless of whether that +coordinate falls within the bounds of the input image.) + +Disabling returning the footprint +================================= + +If you don't need the output footprint after reprojection, you can set +``return_footprint=False`` to return only the reprojected array. This can save +memory and in some cases computing time: + +.. doctest-skip:: + + >>> array = reproject_interp(..., return_footprint=False) + +Using memory-mapped output arrays +================================= + +If you are dealing with a large dataset to reproject, you may be want to +write the reprojected array (and optionally the footprint) to an array of your choice, such as for example +a memory-mapped array. For example: + +.. doctest-skip:: + + >>> header_out = fits.Header.fromtextfile('cube_header_gal.hdr') + >>> shape = (header_out['NAXIS3'], header_out['NAXIS2'], header_out['NAXIS1']) + >>> array_out = np.memmap(filename='output.np', mode='w+', + ... shape=shape, dtype='float32') + >>> hdu = fits.open('cube_file.fits') + >>> reproject_interp(hdu, header_out, output_array=array_out, + ... return_footprint=False) + +After the call to :func:`~reproject.reproject_interp`, ``array_out`` will contain the reprojected values. +If you set up a memory-mapped array for the footprint you could also do: + +.. doctest-skip:: + + + >>> reproject_interp(hdu, header_out, output_array=array_out, + ... output_footprint=footprint_out) + +If you are dealing with FITS files, you can skip the numpy memmap step and use `FITS large file creation +`_: + +.. doctest-skip:: + + >>> header_out = fits.Header.fromtextfile('cube_header_gal.hdr') + >>> header_out.tofile('new_cube.fits') + >>> shape = tuple(header_out['NAXIS{0}'.format(ii)] for ii in range(1, header_out['NAXIS']+1)) + >>> with open('new_cube.fits', 'rb+') as fobj: + ... fobj.seek(len(header_out.tostring()) + (np.product(shape) * np.abs(header_out['BITPIX']//8)) - 1) + ... fobj.write(b'\0') + >>> hdu_out = fits.open('new_cube.fits', mode='update') + >>> rslt = reproject.reproject_interp(hdu, header_out, output_array=hdu_out[0].data, + ... return_footprint=False) + >>> hdu_out.flush() + +.. _broadcasting: + +Multiple images with the same coordinates +========================================= + +If you have multiple images with the exact same coordinate system (e.g. a raw +image and a corresponding processed image) and want to reproject both to the +same output frame, it is faster to compute the coordinate mapping between input +and output pixels only once and re-use this mapping for each reprojection. This +is supported by passing multiple input images as an additional dimension in the +input data array. When the input array contains more dimensions than the input +WCS describes, the extra leading dimensions are taken to represent separate +images with the same coordinates, and the reprojection loops over those +dimensions after computing the pixel mapping. For example: + +.. doctest-skip:: + >>> raw_image, header_in = fits.getdata('raw_image.fits', header=True) + >>> bg_subtracted_image = fits.getdata('background_subtracted_image.fits') + >>> header_out = # Prepare your desired output projection here + >>> # Combine the two images into one array + >>> image_stack = np.stack((raw_image, bg_subtracted_image)) + >>> # We provide a header that describes 2 WCS dimensions, but our input + >>> # array shape is (2, ny, nx)---the 'extra' first dimension represents + >>> # separate images sharing the same coordinates. + >>> reprojected, footprint = reproject.reproject_adaptive( + ... (image_stack, header_in), header_out) + >>> # The shape of `reprojected` is (2, ny', nx') + >>> reprojected_raw, reprojected_bg_subtracted = reprojected[0], reprojected[1] + +For :func:`~reproject.reproject_interp` and +:func:`~reproject.reproject_adaptive`, this is approximately twice as fast as +reprojecting the two images separately. For :func:`~reproject.reproject_exact` +the savings are much less significant, as producing the coordinate mapping is a +much smaller portion of the total runtime for this algorithm. + +When the output coordinates are provided as a WCS and a ``shape_out`` tuple, +``shape_out`` may describe the output shape of a single image, in which case +the extra leading dimensions are prepended automatically, or it may include the +extra dimensions, in which case the size of the extra dimensions must match +those of the input data exactly. + +While the reproject functions can accept the name of a FITS file as input, from +which the input data and coordinates are loaded automatically, this multi-image +reprojection feature does not support loading multiple images automatically +from multiple HDUs within one FITS file, as it would be difficult to verify +automatically that the HDUs contain the same exact coordinates. If multiple +HDUs do share coordinates and are to be reprojected together, they must be +separately loaded and combined into a single input array (e.g. using +``np.stack`` as in the above example). + +Chunk by chunk reprojection +=========================== + +.. testsetup:: + + >>> import numpy as np + >>> import dask.array as da + >>> input_array = np.random.random((1024, 1024)) + >>> dask_array = da.from_array(input_array, chunks=(128, 128)) + >>> from astropy.wcs import WCS + >>> wcs_in = WCS(naxis=2) + >>> wcs_out = WCS(naxis=2) + +When calling one of the reprojection functions, you can specify a block size to use for the +reprojection, and this is used to iterate over chunks in the output array in +chunks. For instance, if you pass in a (1024, 1024) array and specify that the +shape of the output should be a (2048, 2048) array (e.g., via ``shape_out``), +then if you set ``block_size=(256, 256)``:: + + >>> input_array.shape + (1024, 1024) + >>> array, footprint = reproject_interp((input_array, wcs_in), wcs_out, + ... shape_out=(2048, 2048), block_size=(256, 256)) + +the reprojection will be done in 64 separate output chunks. Note however that +this does not break up the input array into chunks since in the general case any +input pixel may contribute to any output pixel. + +Multi-process reprojection +========================== + +By default, the iteration over the output chunks is done in a single +process/thread, but you may specify ``parallel=True`` to process these in +parallel. If you do this, reproject will use multiple processes (rather than +threads) to parallelize the computation (this is because the core reprojection +algorithms we use are not currently thread-safe). If you specify +``parallel=True``, then ``block_size`` will be automatically set to a sensible +default, but you can also set ``block_size`` manually for more control. Note +that you can also set ``parallel=`` to an integer to indicate the number of +processes to use. + +By default, in parallel mode, the entire input array will be written to a +temporary file that is then memory-mapped - this is to avoid loading the whole +input array into memory in each process. If you are specifying a WCS with fewer +dimensions than the data to be reprojected, as described in :ref:`broadcasting`, +you can set the block size to be such that the block size along the dimensions +being reprojected cover the whole image, while the other dimensions can be +smaller. For example, if you are reprojecting a spectral cube with dimensions +(500, 2048, 2048) where 500 is the number of spectral channels and (2048, 2048) +is the celestial plane, then if you are reprojecting just the celestial part of +the WCS you can specify a block size of (N, 2048, 2048) and this will enable a +separate reprojection mode where the input array is not written to disk but +where the reprojection is done in truly independent chunks with size (N, 2048, 2048). + +Input dask arrays +================= + +The three main reprojection functions can accept dask arrays as inputs, e.g. +assuming you have already constructed a dask array named ``dask_array``:: + + >>> dask_array + dask.array + +you can pass this in as part of the first argument to one of the reprojection +functions:: + + >>> from reproject import reproject_interp + >>> array, footprint = reproject_interp((dask_array, wcs_in), wcs_out, + ... shape_out=(2048, 2048)) + +In general however, we cannot benefit much from the chunking of the input arrays +because any input pixel might in principle contribute to any output pixel. +Therefore, for now, when a dask array is passed as input, it is computed using +the current default scheduler and converted to a Numpy memory-mapped array. This +is done efficiently in terms of memory and never results in the whole dataset +being loaded into memory at any given time. However, this does require +sufficient space on disk to store the array. + +Output dask arrays +================== + +By default, the reprojection functions will do the computation immmediately and +return Numpy arrays for the reprojected array and optionally the footprint (this +is regardless of whether dask or Numpy arrays were passed in, or any of the +parallelization options above). However, by setting ``return_type='dask'``, you +can make the functions delay any computation and return dask arrays:: + + >>> array, footprint = reproject_interp((input_array, wcs_in), wcs_out, + ... shape_out=(2048, 2048), block_size=(256, 256), + ... return_type='dask') + >>> array + dask.array + +You can then compute the array or a section of the array yourself whenever you need, or use the +result in further dask expressions. + +.. warning:: The reprojection does not currently work reliably when using multiple threads, so + it is important to make sure you use a dask scheduler that is not multi-threaded. + At the time of writing, the default dask scheduler is ``threads``, so the scheduler + needs to be explicitly set to a different one. + +Using dask.distributed +====================== + +The `dask.distributed `_ package makes it +possible to use distributed schedulers for dask. In order to compute +reprojections with dask.distributed, you should make use of the +``return_type='dask'`` option mentioned above so that you can compute the dask +array once the distributed scheduler has been set up. As mentioned in `Output +dask arrays`_, you should make sure that you limit any cluster to have one +thread per process or the results may be unreliable. From 204b718b14ce8172b9e004a399b0e5bec4bc64e7 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Thu, 14 Sep 2023 15:57:43 +0100 Subject: [PATCH 279/366] Fix import in docs --- docs/performance.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/performance.rst b/docs/performance.rst index edde111cd..0db200753 100644 --- a/docs/performance.rst +++ b/docs/performance.rst @@ -140,6 +140,7 @@ chunks. For instance, if you pass in a (1024, 1024) array and specify that the shape of the output should be a (2048, 2048) array (e.g., via ``shape_out``), then if you set ``block_size=(256, 256)``:: + >>> from reproject import reproject_interp >>> input_array.shape (1024, 1024) >>> array, footprint = reproject_interp((input_array, wcs_in), wcs_out, @@ -187,7 +188,6 @@ assuming you have already constructed a dask array named ``dask_array``:: you can pass this in as part of the first argument to one of the reprojection functions:: - >>> from reproject import reproject_interp >>> array, footprint = reproject_interp((dask_array, wcs_in), wcs_out, ... shape_out=(2048, 2048)) From 7ec70e3dfb483d031f68a24417a078b801c5eae3 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Thu, 13 Jun 2024 10:05:52 +0100 Subject: [PATCH 280/366] Fix codestyle --- docs/performance.rst | 4 ++-- reproject/mosaicking/coadd.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/performance.rst b/docs/performance.rst index 0db200753..2be3e7136 100644 --- a/docs/performance.rst +++ b/docs/performance.rst @@ -79,7 +79,7 @@ Multiple images with the same coordinates If you have multiple images with the exact same coordinate system (e.g. a raw image and a corresponding processed image) and want to reproject both to the same output frame, it is faster to compute the coordinate mapping between input -and output pixels only once and re-use this mapping for each reprojection. This +and output pixels only once and reuse this mapping for each reprojection. This is supported by passing multiple input images as an additional dimension in the input data array. When the input array contains more dimensions than the input WCS describes, the extra leading dimensions are taken to represent separate @@ -202,7 +202,7 @@ sufficient space on disk to store the array. Output dask arrays ================== -By default, the reprojection functions will do the computation immmediately and +By default, the reprojection functions will do the computation immediately and return Numpy arrays for the reprojected array and optionally the footprint (this is regardless of whether dask or Numpy arrays were passed in, or any of the parallelization options above). However, by setting ``return_type='dask'``, you diff --git a/reproject/mosaicking/coadd.py b/reproject/mosaicking/coadd.py index bbeb652d9..aa886eec3 100644 --- a/reproject/mosaicking/coadd.py +++ b/reproject/mosaicking/coadd.py @@ -212,7 +212,7 @@ def reproject_and_coadd( skip_data = False if np.any(np.isnan(edges_out)): - bounds = list(zip([0] * ndim_out, shape_out)) + bounds = list(zip([0] * ndim_out, shape_out, strict=False)) else: bounds = [] for idim in range(ndim_out): From 9703985a52a7fb309851eab3292062f44e24f4f5 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Thu, 13 Jun 2024 10:16:16 +0100 Subject: [PATCH 281/366] Don't use --pre on Python 3.12 --- tox.ini | 1 - 1 file changed, 1 deletion(-) diff --git a/tox.ini b/tox.ini index f3d6b8d8c..41efdfa2f 100644 --- a/tox.ini +++ b/tox.ini @@ -15,7 +15,6 @@ setenv = MPLBACKEND = Agg PYTEST_COMMAND = pytest --arraydiff --arraydiff-default-format=fits --pyargs reproject --cov reproject --cov-config={toxinidir}/pyproject.toml {toxinidir}/docs --remote-data devdeps: PIP_EXTRA_INDEX_URL = https://pypi.anaconda.org/liberfa/simple https://pypi.anaconda.org/astropy/simple https://pypi.anaconda.org/scientific-python-nightly-wheels/simple - py312: PIP_PRE=1 changedir = .tmp/{envname} deps = From 38b134b820c50c1f39ea1817ef816049460103d3 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Thu, 13 Jun 2024 10:19:36 +0100 Subject: [PATCH 282/366] Fix documentation warnings --- reproject/mosaicking/coadd.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/reproject/mosaicking/coadd.py b/reproject/mosaicking/coadd.py index aa886eec3..437325644 100644 --- a/reproject/mosaicking/coadd.py +++ b/reproject/mosaicking/coadd.py @@ -98,12 +98,12 @@ def reproject_and_coadd( output_array : array or None The final output array. Specify this if you already have an appropriately-shaped array to store the data in. Must match shape - specified with `shape_out` or derived from the output + specified with ``shape_out`` or derived from the output projection. output_footprint : array or None The final output footprint array. Specify this if you already have an appropriately-shaped array to store the data in. Must match shape - specified with `shape_out` or derived from the output projection. + specified with ``shape_out`` or derived from the output projection. block_sizes : list of tuples or None The block size to use for each dataset. Could also be a single tuple if you want the sample block size for all data sets. From 17a0382c943f8f1ba0622107cd61c85ab2f9deab Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Tue, 4 Jun 2024 13:32:47 +0100 Subject: [PATCH 283/366] Remove debug print statement --- reproject/spherical_intersect/core.py | 1 - reproject/tests/test_array_utils.py | 3 --- 2 files changed, 4 deletions(-) diff --git a/reproject/spherical_intersect/core.py b/reproject/spherical_intersect/core.py index 59963f3ae..65f4e3f43 100644 --- a/reproject/spherical_intersect/core.py +++ b/reproject/spherical_intersect/core.py @@ -148,7 +148,6 @@ def _reproject_celestial( array_new /= weights if broadcasting: - print(array_out.shape, array_new.shape) array_out[i] = array_new if return_footprint: output_footprint[i] = weights diff --git a/reproject/tests/test_array_utils.py b/reproject/tests/test_array_utils.py index ac23d5bcd..c424eda4d 100644 --- a/reproject/tests/test_array_utils.py +++ b/reproject/tests/test_array_utils.py @@ -36,7 +36,4 @@ def test_custom_map_coordinates(): mode="constant", ) - for i in range(coords.shape[1]): - print(coords[:, i], expected[i], result[i]) - assert_allclose(result, expected) From 9df8953c659cf86fc4f167c35902a8fc082f876f Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Tue, 4 Jun 2024 13:34:20 +0100 Subject: [PATCH 284/366] Don't cast image to 64-bit floats in reproject_full as this makes a copy for big-endian (e.g. FITS) data --- reproject/interpolation/core.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/reproject/interpolation/core.py b/reproject/interpolation/core.py index f9bd6be4b..c257b63af 100644 --- a/reproject/interpolation/core.py +++ b/reproject/interpolation/core.py @@ -71,8 +71,7 @@ def _reproject_full( the same coordinate information. The transformation is computed once and "broadcast" across those images. """ - # Make sure image is floating point - array = np.asarray(array, dtype=float) + # shape_out must be exactly a tuple type shape_out = tuple(shape_out) _validate_wcs(wcs_in, wcs_out, array.shape, shape_out) From e38c9bcd5f6fb3d3dd41b96209eb7a9728e01609 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Tue, 4 Jun 2024 13:35:43 +0100 Subject: [PATCH 285/366] Convert data to a proper np.memmap when reading from FITS --- reproject/utils.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/reproject/utils.py b/reproject/utils.py index 9f72eabbe..20840ed47 100644 --- a/reproject/utils.py +++ b/reproject/utils.py @@ -68,6 +68,20 @@ def _dask_to_numpy_memmap(dask_array, tmp_dir): return memmap_path, memmapped_array +def hdu_to_vanilla_memmap(hdu): + """ + Given an HDU object, return a regular Numpy memmap rather than a Numpy + array backed by a memmapped buffer as returned by astropy. + """ + return np.memmap( + hdu.fileinfo()["file"].name, + mode="r", + dtype=hdu.data.dtype, + shape=hdu.data.shape, + offset=hdu.fileinfo()["datLoc"], + ) + + def parse_input_data(input_data, hdu_in=None): """ Parse input data to return a Numpy array and WCS object. @@ -87,7 +101,7 @@ def parse_input_data(input_data, hdu_in=None): hdu_in = 0 return parse_input_data(input_data[hdu_in]) elif isinstance(input_data, PrimaryHDU | ImageHDU | CompImageHDU): - return input_data.data, WCS(input_data.header) + return hdu_to_vanilla_memmap(input_data), WCS(input_data.header) elif isinstance(input_data, tuple) and isinstance(input_data[0], np.ndarray | da.core.Array): if isinstance(input_data[1], Header): return input_data[0], WCS(input_data[1]) From 4adf63ac06baf7a32e72154a18290a08f99cafdb Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Tue, 4 Jun 2024 13:37:38 +0100 Subject: [PATCH 286/366] Modify map_coordinates wrapper so that when dealing with big-endian data, we process 2Gb chunks at a time to avoid blowing up memory, as otherwise a copy of the data is made (to convert to little-endian) --- reproject/array_utils.py | 134 +++++++++++++++++++++++++++++++- reproject/interpolation/core.py | 1 + 2 files changed, 132 insertions(+), 3 deletions(-) diff --git a/reproject/array_utils.py b/reproject/array_utils.py index 1d5aae562..789992791 100644 --- a/reproject/array_utils.py +++ b/reproject/array_utils.py @@ -3,7 +3,90 @@ __all__ = ["map_coordinates", "sample_array_edges"] -def map_coordinates(image, coords, **kwargs): +def find_chunk_shape(shape, max_chunk_size=None): + """ + Given the shape of an n-dimensional array, and the maximum number of + elements in a chunk, return the largest chunk shape to use for iteration. + + This currently assumes the optimal chunk shape to return is for C-contiguous + arrays. + + Parameters + ---------- + shape : iterable + The shape of the n-dimensional array. + max_chunk_size : int, optional + The maximum number of elements per chunk. + """ + + if max_chunk_size is None: + return tuple(shape) + + block_shape = [] + + max_repeat_remaining = max_chunk_size + + for size in shape[::-1]: + + if max_repeat_remaining > size: + block_shape.append(size) + max_repeat_remaining = max_repeat_remaining // size + else: + block_shape.append(max_repeat_remaining) + max_repeat_remaining = 1 + + return tuple(block_shape[::-1]) + + +def iterate_chunks(shape, max_chunk_size): + """ + Given a data shape and a chunk shape (or maximum chunk size), iteratively + return slice objects that can be used to slice the array. + + Parameters + ---------- + shape : iterable + The shape of the n-dimensional array. + max_chunk_size : int + The maximum number of elements per chunk. + """ + + if np.prod(shape) == 0: + return + + chunk_shape = find_chunk_shape(shape, max_chunk_size) + + ndim = len(chunk_shape) + start_index = [0] * ndim + + shape = list(shape) + + while start_index <= shape: + + end_index = [min(start_index[i] + chunk_shape[i], shape[i]) for i in range(ndim)] + + slices = tuple([slice(start_index[i], end_index[i]) for i in range(ndim)]) + + yield slices + + # Update chunk index. What we do is to increment the + # counter for the first dimension, and then if it + # exceeds the number of elements in that direction, + # cycle back to zero and advance in the next dimension, + # and so on. + start_index[0] += chunk_shape[0] + for i in range(ndim - 1): + if start_index[i] >= shape[i]: + start_index[i] = 0 + start_index[i + 1] += chunk_shape[i + 1] + + # We can now check whether the iteration is finished + if start_index[-1] >= shape[-1]: + break + + +def map_coordinates(image, coords, max_chunk_size=None, output=None, **kwargs): + # In the built-in scipy map_coordinates, the values are defined at the # center of the pixels. This means that map_coordinates does not # correctly treat pixels that are in the outer half of the outer pixels. @@ -12,11 +95,21 @@ def map_coordinates(image, coords, **kwargs): # instead pad the array but this was not memory efficient as it ended up # producing a copy of the output array. + # In addition, map_coordinates is not efficient when given big-endian Numpy + # arrays as it will then make a copy, which is an issue when dealing with + # memory-mapped FITS files that might be larger than memory. Therefore, for + # big-endian arrays, we operate in chunks with a size smaller or equal to + # max_chunk_size. + + # TODO: check how this should behave on a big-endian system. + from scipy.ndimage import map_coordinates as scipy_map_coordinates original_shape = image.shape - # We copy the coordinates array as we then modify it in-place below + # We copy the coordinates array as we then modify it in-place below to clip + # to the edges of the array. + coords = coords.copy() for i in range(coords.shape[0]): coords[i][(coords[i] < 0) & (coords[i] >= -0.5)] = 0 @@ -24,7 +117,42 @@ def map_coordinates(image, coords, **kwargs): original_shape[i] - 1 ) - values = scipy_map_coordinates(image, coords, **kwargs) + if image.dtype.isnative: + + values = scipy_map_coordinates(image, coords, prefilter=False, output=output, **kwargs) + + else: + + if output is None: + output = np.repeat(np.nan, coords.shape[1]) + + values = output + + for chunk in iterate_chunks(image.shape, max_chunk_size): + + include = np.ones(coords.shape[1], dtype=bool) + + for idim, slc in enumerate(chunk): + include[(coords[idim] < slc.start) | (coords[idim] >= slc.stop)] = False + + if not np.any(include): + continue + + chunk = list(chunk) + + # Adjust chunks to add padding + for idim, slc in enumerate(chunk): + start = max(0, slc.start - 10) + stop = min(original_shape[i], slc.stop + 10) + chunk[idim] = slice(start, stop) + + chunk = tuple(chunk) + + coords_subset = coords[:, include].copy() + for idim, slc in enumerate(chunk): + coords_subset[idim, :] -= slc.start + + output[include] = scipy_map_coordinates(image[chunk], coords_subset, **kwargs) reset = np.zeros(coords.shape[1], dtype=bool) diff --git a/reproject/interpolation/core.py b/reproject/interpolation/core.py index c257b63af..1215b7397 100644 --- a/reproject/interpolation/core.py +++ b/reproject/interpolation/core.py @@ -125,6 +125,7 @@ def _reproject_full( cval=np.nan, mode="constant", output=array_out_loopable[i].ravel(), + max_chunk_size=256 * 1024**2, ) # n.b. We write the reprojected data into array_out_loopable, but array_out From 1f54a478134f6eb4d7e3ff4630cc159dab4f5b46 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Tue, 4 Jun 2024 14:18:02 +0100 Subject: [PATCH 287/366] Change parallel mode to use multi-threading instead of multi-processing. This provides better performance especially in terms of memory, as only one version of the data is needed, and we can avoid always dumping arrays to disk. Only dump input array to a memmap if it is a dask array, otherwise just pass as-is to workers. --- reproject/common.py | 39 ++++++++++++++++++--------------------- 1 file changed, 18 insertions(+), 21 deletions(-) diff --git a/reproject/common.py b/reproject/common.py index f4dca6878..d78d9b49a 100644 --- a/reproject/common.py +++ b/reproject/common.py @@ -159,18 +159,14 @@ def _reproject_dispatcher( shape_in = array_in.shape - # When in parallel mode, we want to make sure we avoid having to copy the - # input array to all processes for each chunk, so instead we write out - # the input array to a Numpy memory map and load it in inside each process - # as a memory-mapped array. We need to be careful how this gets passed to - # reproject_single_block so we pass a variable that can be either a string - # or the array itself (for synchronous mode). If the input array is a dask - # array we should always write it out to a memmap even in synchronous mode - # otherwise map_blocks gets confused if it gets two dask arrays and tries - # to iterate over both. - - if isinstance(array_in, da.core.Array) or parallel: - # If return_type=='dask', + # As we use the synchronous or threads scheduler, we don't need to worry about + # the data getting copied, so if the data is already a Numpy array (including + # a memory-mapped array) then we don't need to do anything special. However, + # if the input array is a dask array, we should convert it to a Numpy + # memory-mapped array so that it can be used by the various reprojection + # functions (which don't internally work with dask arrays). + + if isinstance(array_in, da.core.Array): if return_type == "dask": # We should use a temporary directory that will persist beyond # the call to the reproject function. @@ -179,10 +175,11 @@ def _reproject_dispatcher( tmp_dir = local_tmp_dir array_in_or_path = as_delayed_memmap_path(array_in, tmp_dir) else: - # Here we could set array_in_or_path to array_in_path if it - # has been set previously, but in synchronous mode it is better to - # simply pass a reference to the memmap array itself to avoid having - # to load the memmap inside each reproject_single_block call. + # Here we could set array_in_or_path to array_in_path if it has + # been set previously, but in synchronous and threaded mode it is + # better to simply pass a reference to the memmap array itself to + # avoid having to load the memmap inside each + # reproject_single_block call. array_in_or_path = array_in def reproject_single_block(a, array_or_path, block_info=None): @@ -282,24 +279,24 @@ def reproject_single_block(a, array_or_path, block_info=None): if parallel: # As discussed in https://github.com/dask/dask/issues/9556, da.store - # will not work well in multiprocessing mode when the destination is a + # will not work well in parallel mode when the destination is a # Numpy array. Instead, in this case we save the dask array to a zarr # array on disk which can be done in parallel, and re-load it as a dask # array. We can then use da.store in the next step using the # 'synchronous' scheduler since that is I/O limited so does not need # to be done in parallel. - if isinstance(parallel, int): + if isinstance(parallel, bool): + workers = {} + else: if parallel > 0: workers = {"num_workers": parallel} else: raise ValueError("The number of processors to use must be strictly positive") - else: - workers = {} zarr_path = os.path.join(local_tmp_dir, f"{uuid.uuid4()}.zarr") - with dask.config.set(scheduler="processes", **workers): + with dask.config.set(scheduler="threads", **workers): result.to_zarr(zarr_path) result = da.from_zarr(zarr_path) From a06c0f319fd447e4071e26baae305d93180b10b5 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Tue, 4 Jun 2024 23:42:17 +0100 Subject: [PATCH 288/366] Modify block size limit --- reproject/common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reproject/common.py b/reproject/common.py index d78d9b49a..57b506627 100644 --- a/reproject/common.py +++ b/reproject/common.py @@ -251,7 +251,7 @@ def reproject_single_block(a, array_or_path, block_info=None): else: rechunk_kwargs = {} array_out_dask = da.empty(shape_out) - array_out_dask = array_out_dask.rechunk(block_size_limit=8 * 1024**2, **rechunk_kwargs) + array_out_dask = array_out_dask.rechunk(block_size_limit=64 * 1024**2, **rechunk_kwargs) result = da.map_blocks( reproject_single_block, From 70db8254bbc3c830fdf1cd732c87fa58b733722b Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Wed, 5 Jun 2024 12:45:59 +0100 Subject: [PATCH 289/366] Use pixel_to_pixel in coaddition to avoid issue with >2-dimensional datasets, and remove repeated line --- reproject/mosaicking/coadd.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/reproject/mosaicking/coadd.py b/reproject/mosaicking/coadd.py index 437325644..deaf09f69 100644 --- a/reproject/mosaicking/coadd.py +++ b/reproject/mosaicking/coadd.py @@ -2,6 +2,7 @@ import numpy as np from astropy.wcs import WCS +from astropy.wcs.utils import pixel_to_pixel from astropy.wcs.wcsapi import SlicedLowLevelWCS from ..array_utils import sample_array_edges @@ -200,7 +201,7 @@ def reproject_and_coadd( # which provides a lot of redundant information. edges = sample_array_edges(array_in.shape, n_samples=11)[::-1] - edges_out = wcs_out.world_to_pixel(wcs_in.pixel_to_world(*edges))[::-1] + edges_out = pixel_to_pixel(wcs_in, wcs_out, *edges)[::-1] # Determine the cutout parameters @@ -312,7 +313,6 @@ def reproject_and_coadd( if combine_function == "mean": with np.errstate(invalid="ignore"): output_array /= output_footprint - output_array[output_footprint == 0] = blank_pixel_value elif combine_function in ("first", "last", "min", "max"): if combine_function == "min": From df890a5bf53fac34ef512fdb64ccdc377af7e798 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Mon, 10 Jun 2024 14:14:42 +0100 Subject: [PATCH 290/366] Improve memory efficiency of reproject_and_coadd to avoid large temporary array memory allocations --- reproject/array_utils.py | 10 ++---- reproject/mosaicking/coadd.py | 68 +++++++++++++++++++++++++++-------- 2 files changed, 56 insertions(+), 22 deletions(-) diff --git a/reproject/array_utils.py b/reproject/array_utils.py index 789992791..5a83ab93d 100644 --- a/reproject/array_utils.py +++ b/reproject/array_utils.py @@ -27,7 +27,6 @@ def find_chunk_shape(shape, max_chunk_size=None): max_repeat_remaining = max_chunk_size for size in shape[::-1]: - if max_repeat_remaining > size: block_shape.append(size) max_repeat_remaining = max_repeat_remaining // size @@ -38,7 +37,7 @@ def find_chunk_shape(shape, max_chunk_size=None): return tuple(block_shape[::-1]) -def iterate_chunks(shape, max_chunk_size): +def iterate_chunks(shape, *, max_chunk_size): """ Given a data shape and a chunk shape (or maximum chunk size), iteratively return slice objects that can be used to slice the array. @@ -62,7 +61,6 @@ def iterate_chunks(shape, max_chunk_size): shape = list(shape) while start_index <= shape: - end_index = [min(start_index[i] + chunk_shape[i], shape[i]) for i in range(ndim)] slices = tuple([slice(start_index[i], end_index[i]) for i in range(ndim)]) @@ -86,7 +84,6 @@ def iterate_chunks(shape, max_chunk_size): def map_coordinates(image, coords, max_chunk_size=None, output=None, **kwargs): - # In the built-in scipy map_coordinates, the values are defined at the # center of the pixels. This means that map_coordinates does not # correctly treat pixels that are in the outer half of the outer pixels. @@ -118,18 +115,15 @@ def map_coordinates(image, coords, max_chunk_size=None, output=None, **kwargs): ) if image.dtype.isnative: - values = scipy_map_coordinates(image, coords, prefilter=False, output=output, **kwargs) else: - if output is None: output = np.repeat(np.nan, coords.shape[1]) values = output - for chunk in iterate_chunks(image.shape, max_chunk_size): - + for chunk in iterate_chunks(image.shape, max_chunk_size=max_chunk_size): include = np.ones(coords.shape[1], dtype=bool) for idim, slc in enumerate(chunk): diff --git a/reproject/mosaicking/coadd.py b/reproject/mosaicking/coadd.py index deaf09f69..f3b8531c6 100644 --- a/reproject/mosaicking/coadd.py +++ b/reproject/mosaicking/coadd.py @@ -1,11 +1,15 @@ # Licensed under a 3-clause BSD style license - see LICENSE.rst +import os +import tempfile +import time +import uuid import numpy as np from astropy.wcs import WCS from astropy.wcs.utils import pixel_to_pixel from astropy.wcs.wcsapi import SlicedLowLevelWCS -from ..array_utils import sample_array_edges +from ..array_utils import iterate_chunks, sample_array_edges from ..utils import parse_input_data, parse_input_weights, parse_output_projection from .background import determine_offset_matrix, solve_corrections_sgd from .subset_array import ReprojectedArraySubset @@ -234,7 +238,7 @@ def reproject_and_coadd( else: wcs_out_indiv = SlicedLowLevelWCS(wcs_out.low_level_wcs, slice_out) - shape_out_indiv = [imax - imin for (imin, imax) in bounds] + shape_out_indiv = tuple([imax - imin for (imin, imax) in bounds]) if block_sizes is not None: if len(block_sizes) == len(input_data) and len(block_sizes[idata]) == len(shape_out): @@ -246,23 +250,51 @@ def reproject_and_coadd( # able to handle weights, and make the footprint become the combined # footprint + weight map - array, footprint = reproject_function( - (array_in, wcs_in), - output_projection=wcs_out_indiv, - shape_out=shape_out_indiv, - hdu_in=hdu_in, - **kwargs, - ) + with tempfile.TemporaryDirectory() as local_tmp_dir: - if weights_in is not None: - weights, _ = reproject_function( - (weights_in, wcs_in), + array = np.memmap( + os.path.join(local_tmp_dir, f"{uuid.uuid4()}.np"), + shape=shape_out_indiv, + mode="w+", + dtype=array_in.dtype, + ) + + footprint = np.memmap( + os.path.join(local_tmp_dir, f"{uuid.uuid4()}.np"), + shape=shape_out_indiv, + mode="w+", + dtype=float, + ) + + array, footprint = reproject_function( + (array_in, wcs_in), output_projection=wcs_out_indiv, shape_out=shape_out_indiv, hdu_in=hdu_in, + output_array=array, + output_footprint=footprint, **kwargs, ) + if weights_in is not None: + + weights = np.memmap( + os.path.join(local_tmp_dir, f"{uuid.uuid4()}.np"), + shape=shape_out_indiv, + mode="w+", + dtype=float, + ) + + weights = reproject_function( + (weights_in, wcs_in), + output_projection=wcs_out_indiv, + shape_out=shape_out_indiv, + hdu_in=hdu_in, + output_array=weights, + return_footprint=False, + **kwargs, + ) + # For the purposes of mosaicking, we mask out NaN values from the array # and set the footprint to 0 at these locations. reset = np.isnan(array) @@ -284,8 +316,13 @@ def reproject_and_coadd( # but we set these to 0 here to avoid getting NaNs in the # means/sums. array.array[array.footprint == 0] = 0 - output_array[array.view_in_original_array] += array.array * array.footprint output_footprint[array.view_in_original_array] += array.footprint + # We now need to do output[view] += array * footprint but to avoid + # the temporary array allocation from array * footprint we modify + # array inplace, which we can do as the array will be discarded at + # the end of the loop. + array.array *= array.footprint + output_array[array.view_in_original_array] += array.array else: arrays.append(array) @@ -341,6 +378,9 @@ def reproject_and_coadd( mask, array.array, output_array[array.view_in_original_array] ) - output_array[output_footprint == 0] = blank_pixel_value + # We need to avoid potentially large memory allocation from output == 0 so + # we operate in chunks. + for chunk in iterate_chunks(output_array.shape, max_chunk_size=256 * 1024**2): + output_array[chunk][output_footprint[chunk] == 0] = blank_pixel_value return output_array, output_footprint From 70741fa87163cf820fbe941705edd9b91853b7e5 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Mon, 10 Jun 2024 16:47:36 +0100 Subject: [PATCH 291/366] Start fixing existing tests --- reproject/array_utils.py | 15 +++++++++++++-- reproject/utils.py | 4 ++++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/reproject/array_utils.py b/reproject/array_utils.py index 5a83ab93d..1d975f20b 100644 --- a/reproject/array_utils.py +++ b/reproject/array_utils.py @@ -83,6 +83,13 @@ def iterate_chunks(shape, *, max_chunk_size): break +def at_least_float32(array): + if array.dtype.kind == "f" and array.dtype.itemsize >= 4: + return array + else: + return array.astype(np.float32) + + def map_coordinates(image, coords, max_chunk_size=None, output=None, **kwargs): # In the built-in scipy map_coordinates, the values are defined at the # center of the pixels. This means that map_coordinates does not @@ -115,7 +122,9 @@ def map_coordinates(image, coords, max_chunk_size=None, output=None, **kwargs): ) if image.dtype.isnative: - values = scipy_map_coordinates(image, coords, prefilter=False, output=output, **kwargs) + values = scipy_map_coordinates( + at_least_float32(image), coords, prefilter=False, output=output, **kwargs + ) else: if output is None: @@ -146,7 +155,9 @@ def map_coordinates(image, coords, max_chunk_size=None, output=None, **kwargs): for idim, slc in enumerate(chunk): coords_subset[idim, :] -= slc.start - output[include] = scipy_map_coordinates(image[chunk], coords_subset, **kwargs) + output[include] = scipy_map_coordinates( + at_least_float32(image[chunk]), coords_subset, **kwargs + ) reset = np.zeros(coords.shape[1], dtype=bool) diff --git a/reproject/utils.py b/reproject/utils.py index 20840ed47..591a7e8a0 100644 --- a/reproject/utils.py +++ b/reproject/utils.py @@ -73,6 +73,10 @@ def hdu_to_vanilla_memmap(hdu): Given an HDU object, return a regular Numpy memmap rather than a Numpy array backed by a memmapped buffer as returned by astropy. """ + + if hdu.fileinfo() is None: + return hdu.data + return np.memmap( hdu.fileinfo()["file"].name, mode="r", From be3dadffab976aff7ac4f5aacebd4dfc7fecab47 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Tue, 11 Jun 2024 10:55:34 +0100 Subject: [PATCH 292/366] Fix indexing bug --- reproject/array_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reproject/array_utils.py b/reproject/array_utils.py index 1d975f20b..dc44a6ad6 100644 --- a/reproject/array_utils.py +++ b/reproject/array_utils.py @@ -146,7 +146,7 @@ def map_coordinates(image, coords, max_chunk_size=None, output=None, **kwargs): # Adjust chunks to add padding for idim, slc in enumerate(chunk): start = max(0, slc.start - 10) - stop = min(original_shape[i], slc.stop + 10) + stop = min(original_shape[idim], slc.stop + 10) chunk[idim] = slice(start, stop) chunk = tuple(chunk) From e2e88917b61b3c5bb842ab2271f052c98c286bd3 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Tue, 11 Jun 2024 11:52:44 +0100 Subject: [PATCH 293/366] Improve robustness of hdu_to_vanilla_memmap --- reproject/tests/test_utils.py | 19 ++++++++++++++++++- reproject/utils.py | 10 ++++++++-- 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/reproject/tests/test_utils.py b/reproject/tests/test_utils.py index 1345afa4a..901ea5b57 100644 --- a/reproject/tests/test_utils.py +++ b/reproject/tests/test_utils.py @@ -6,7 +6,12 @@ from reproject.conftest import set_wcs_array_shape from reproject.tests.helpers import assert_wcs_allclose -from reproject.utils import parse_input_data, parse_input_shape, parse_output_projection +from reproject.utils import ( + hdu_to_vanilla_memmap, + parse_input_data, + parse_input_shape, + parse_output_projection, +) from reproject.wcs_utils import has_celestial @@ -130,3 +135,15 @@ def test_has_celestial(): wwh2 = HighLevelWCSWrapper(SlicedLowLevelWCS(ww, [slice(0, 1), slice(0, 1)])) assert has_celestial(wwh2) + + +class TestHDUToMemmap: + + def test_compressed(self, tmp_path): + + hdu = fits.CompImageHDU(data=np.random.random((128, 128))) + hdu.writeto(tmp_path / "test.fits") + + mmap = hdu_to_vanilla_memmap(hdu) + + np.testing.assert_allclose(hdu.data, mmap) diff --git a/reproject/utils.py b/reproject/utils.py index 591a7e8a0..e2824edf1 100644 --- a/reproject/utils.py +++ b/reproject/utils.py @@ -74,13 +74,19 @@ def hdu_to_vanilla_memmap(hdu): array backed by a memmapped buffer as returned by astropy. """ - if hdu.fileinfo() is None: + if ( + "BSCALE" in hdu.header + or "BZERO" in hdu.header + or hdu.fileinfo() is None + or hdu._data_replaced + or hdu.fileinfo()["file"].compression is not None + ): return hdu.data return np.memmap( hdu.fileinfo()["file"].name, mode="r", - dtype=hdu.data.dtype, + dtype=hdu.data.dtype.newbyteorder(">"), shape=hdu.data.shape, offset=hdu.fileinfo()["datLoc"], ) From 1a566eb077258a230b29097ca1ba04977fb8ab88 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Tue, 11 Jun 2024 11:53:10 +0100 Subject: [PATCH 294/366] Rename function --- reproject/tests/test_utils.py | 4 ++-- reproject/utils.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/reproject/tests/test_utils.py b/reproject/tests/test_utils.py index 901ea5b57..21f1ee408 100644 --- a/reproject/tests/test_utils.py +++ b/reproject/tests/test_utils.py @@ -7,7 +7,7 @@ from reproject.conftest import set_wcs_array_shape from reproject.tests.helpers import assert_wcs_allclose from reproject.utils import ( - hdu_to_vanilla_memmap, + hdu_to_numpy_memmap, parse_input_data, parse_input_shape, parse_output_projection, @@ -144,6 +144,6 @@ def test_compressed(self, tmp_path): hdu = fits.CompImageHDU(data=np.random.random((128, 128))) hdu.writeto(tmp_path / "test.fits") - mmap = hdu_to_vanilla_memmap(hdu) + mmap = hdu_to_numpy_memmap(hdu) np.testing.assert_allclose(hdu.data, mmap) diff --git a/reproject/utils.py b/reproject/utils.py index e2824edf1..70e85c877 100644 --- a/reproject/utils.py +++ b/reproject/utils.py @@ -68,7 +68,7 @@ def _dask_to_numpy_memmap(dask_array, tmp_dir): return memmap_path, memmapped_array -def hdu_to_vanilla_memmap(hdu): +def hdu_to_numpy_memmap(hdu): """ Given an HDU object, return a regular Numpy memmap rather than a Numpy array backed by a memmapped buffer as returned by astropy. @@ -111,7 +111,7 @@ def parse_input_data(input_data, hdu_in=None): hdu_in = 0 return parse_input_data(input_data[hdu_in]) elif isinstance(input_data, PrimaryHDU | ImageHDU | CompImageHDU): - return hdu_to_vanilla_memmap(input_data), WCS(input_data.header) + return hdu_to_numpy_memmap(input_data), WCS(input_data.header) elif isinstance(input_data, tuple) and isinstance(input_data[0], np.ndarray | da.core.Array): if isinstance(input_data[1], Header): return input_data[0], WCS(input_data[1]) From 9bd5d465d80483a2a0c008d35e2bdde42489fbb0 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Tue, 11 Jun 2024 12:07:58 +0100 Subject: [PATCH 295/366] Don't set prefilter option --- reproject/array_utils.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/reproject/array_utils.py b/reproject/array_utils.py index dc44a6ad6..23a73f1cd 100644 --- a/reproject/array_utils.py +++ b/reproject/array_utils.py @@ -121,11 +121,11 @@ def map_coordinates(image, coords, max_chunk_size=None, output=None, **kwargs): original_shape[i] - 1 ) - if image.dtype.isnative: - values = scipy_map_coordinates( - at_least_float32(image), coords, prefilter=False, output=output, **kwargs - ) - + # If the data type is native and we are not doing spline interpolation, + # then scipy_map_coordinates deals properly with memory maps, so we can use + # it without chunking. Otherwise, we need to iterate over data chunks. + if image.dtype.isnative and kwargs["order"] <= 1: + values = scipy_map_coordinates(at_least_float32(image), coords, output=output, **kwargs) else: if output is None: output = np.repeat(np.nan, coords.shape[1]) From 7d52e23228cff0b236e90ddf2b151e08c35e1709 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Tue, 11 Jun 2024 12:18:02 +0100 Subject: [PATCH 296/366] Make sure temporary directory lives for the duration of the reproject_and_coadd call --- reproject/mosaicking/coadd.py | 286 +++++++++++++++++----------------- 1 file changed, 144 insertions(+), 142 deletions(-) diff --git a/reproject/mosaicking/coadd.py b/reproject/mosaicking/coadd.py index f3b8531c6..1fda079c2 100644 --- a/reproject/mosaicking/coadd.py +++ b/reproject/mosaicking/coadd.py @@ -180,77 +180,79 @@ def reproject_and_coadd( if not on_the_fly: arrays = [] - for idata in progress_bar(range(len(input_data))): - # We need to pre-parse the data here since we need to figure out how to - # optimize/minimize the size of each output tile (see below). - array_in, wcs_in = parse_input_data(input_data[idata], hdu_in=hdu_in) - - # We also get the weights map, if specified - if input_weights is None: - weights_in = None - else: - weights_in = parse_input_weights(input_weights[idata], hdu_weights=hdu_weights) - if np.any(np.isnan(weights_in)): - weights_in = np.nan_to_num(weights_in) - - # Since we might be reprojecting small images into a large mosaic we - # want to make sure that for each image we reproject to an array with - # minimal footprint. We therefore find the pixel coordinates of the - # edges of the initial image and transform this to pixel coordinates in - # the final image to figure out the final WCS and shape to reproject to - # for each tile. We strike a balance between transforming only the - # input-image corners, which is fast but can cause clipping in cases of - # significant distortion (when the edges of the input image become - # convex in the output projection), and transforming every edge pixel, - # which provides a lot of redundant information. - - edges = sample_array_edges(array_in.shape, n_samples=11)[::-1] - edges_out = pixel_to_pixel(wcs_in, wcs_out, *edges)[::-1] - - # Determine the cutout parameters - - # In some cases, images might not have valid coordinates in the corners, - # such as all-sky images or full solar disk views. In this case we skip - # this step and just use the full output WCS for reprojection. - - ndim_out = len(shape_out) - - skip_data = False - if np.any(np.isnan(edges_out)): - bounds = list(zip([0] * ndim_out, shape_out, strict=False)) - else: - bounds = [] - for idim in range(ndim_out): - imin = max(0, int(np.floor(edges_out[idim].min() + 0.5))) - imax = min(shape_out[idim], int(np.ceil(edges_out[idim].max() + 0.5))) - bounds.append((imin, imax)) - if imax < imin: - skip_data = True - break - - if skip_data: - continue - - slice_out = tuple([slice(imin, imax) for (imin, imax) in bounds]) - - if isinstance(wcs_out, WCS): - wcs_out_indiv = wcs_out[slice_out] - else: - wcs_out_indiv = SlicedLowLevelWCS(wcs_out.low_level_wcs, slice_out) - - shape_out_indiv = tuple([imax - imin for (imin, imax) in bounds]) - - if block_sizes is not None: - if len(block_sizes) == len(input_data) and len(block_sizes[idata]) == len(shape_out): - kwargs["block_size"] = block_sizes[idata] + with tempfile.TemporaryDirectory() as local_tmp_dir: + + for idata in progress_bar(range(len(input_data))): + # We need to pre-parse the data here since we need to figure out how to + # optimize/minimize the size of each output tile (see below). + array_in, wcs_in = parse_input_data(input_data[idata], hdu_in=hdu_in) + + # We also get the weights map, if specified + if input_weights is None: + weights_in = None + else: + weights_in = parse_input_weights(input_weights[idata], hdu_weights=hdu_weights) + if np.any(np.isnan(weights_in)): + weights_in = np.nan_to_num(weights_in) + + # Since we might be reprojecting small images into a large mosaic we + # want to make sure that for each image we reproject to an array with + # minimal footprint. We therefore find the pixel coordinates of the + # edges of the initial image and transform this to pixel coordinates in + # the final image to figure out the final WCS and shape to reproject to + # for each tile. We strike a balance between transforming only the + # input-image corners, which is fast but can cause clipping in cases of + # significant distortion (when the edges of the input image become + # convex in the output projection), and transforming every edge pixel, + # which provides a lot of redundant information. + + edges = sample_array_edges(array_in.shape, n_samples=11)[::-1] + edges_out = pixel_to_pixel(wcs_in, wcs_out, *edges)[::-1] + + # Determine the cutout parameters + + # In some cases, images might not have valid coordinates in the corners, + # such as all-sky images or full solar disk views. In this case we skip + # this step and just use the full output WCS for reprojection. + + ndim_out = len(shape_out) + + skip_data = False + if np.any(np.isnan(edges_out)): + bounds = list(zip([0] * ndim_out, shape_out, strict=False)) else: - kwargs["block_size"] = block_sizes + bounds = [] + for idim in range(ndim_out): + imin = max(0, int(np.floor(edges_out[idim].min() + 0.5))) + imax = min(shape_out[idim], int(np.ceil(edges_out[idim].max() + 0.5))) + bounds.append((imin, imax)) + if imax < imin: + skip_data = True + break + + if skip_data: + continue + + slice_out = tuple([slice(imin, imax) for (imin, imax) in bounds]) + + if isinstance(wcs_out, WCS): + wcs_out_indiv = wcs_out[slice_out] + else: + wcs_out_indiv = SlicedLowLevelWCS(wcs_out.low_level_wcs, slice_out) + + shape_out_indiv = tuple([imax - imin for (imin, imax) in bounds]) - # TODO: optimize handling of weights by making reprojection functions - # able to handle weights, and make the footprint become the combined - # footprint + weight map + if block_sizes is not None: + if len(block_sizes) == len(input_data) and len(block_sizes[idata]) == len( + shape_out + ): + kwargs["block_size"] = block_sizes[idata] + else: + kwargs["block_size"] = block_sizes - with tempfile.TemporaryDirectory() as local_tmp_dir: + # TODO: optimize handling of weights by making reprojection functions + # able to handle weights, and make the footprint become the combined + # footprint + weight map array = np.memmap( os.path.join(local_tmp_dir, f"{uuid.uuid4()}.np"), @@ -295,89 +297,89 @@ def reproject_and_coadd( **kwargs, ) - # For the purposes of mosaicking, we mask out NaN values from the array - # and set the footprint to 0 at these locations. - reset = np.isnan(array) - array[reset] = 0.0 - footprint[reset] = 0.0 - - # Combine weights and footprint - if weights_in is not None: - weights[reset] = 0.0 - footprint *= weights - - array = ReprojectedArraySubset(array, footprint, bounds) - - # TODO: make sure we gracefully handle the case where the - # output image is empty (due e.g. to no overlap). - - if on_the_fly: - # By default, values outside of the footprint are set to NaN - # but we set these to 0 here to avoid getting NaNs in the - # means/sums. - array.array[array.footprint == 0] = 0 - output_footprint[array.view_in_original_array] += array.footprint - # We now need to do output[view] += array * footprint but to avoid - # the temporary array allocation from array * footprint we modify - # array inplace, which we can do as the array will be discarded at - # the end of the loop. - array.array *= array.footprint - output_array[array.view_in_original_array] += array.array - else: - arrays.append(array) - - # If requested, try and match the backgrounds. - if match_background and len(arrays) > 1: - offset_matrix = determine_offset_matrix(arrays) - corrections = solve_corrections_sgd(offset_matrix) - if background_reference: - corrections -= corrections[background_reference] - for array, correction in zip(arrays, corrections, strict=True): - array.array -= correction - - if combine_function in ("mean", "sum"): - if match_background: - # if we're not matching the background, this part has already been done - for array in arrays: + # For the purposes of mosaicking, we mask out NaN values from the array + # and set the footprint to 0 at these locations. + reset = np.isnan(array) + array[reset] = 0.0 + footprint[reset] = 0.0 + + # Combine weights and footprint + if weights_in is not None: + weights[reset] = 0.0 + footprint *= weights + + array = ReprojectedArraySubset(array, footprint, bounds) + + # TODO: make sure we gracefully handle the case where the + # output image is empty (due e.g. to no overlap). + + if on_the_fly: # By default, values outside of the footprint are set to NaN # but we set these to 0 here to avoid getting NaNs in the # means/sums. array.array[array.footprint == 0] = 0 - - output_array[array.view_in_original_array] += array.array * array.footprint output_footprint[array.view_in_original_array] += array.footprint + # We now need to do output[view] += array * footprint but to avoid + # the temporary array allocation from array * footprint we modify + # array inplace, which we can do as the array will be discarded at + # the end of the loop. + array.array *= array.footprint + output_array[array.view_in_original_array] += array.array + else: + arrays.append(array) + + # If requested, try and match the backgrounds. + if match_background and len(arrays) > 1: + offset_matrix = determine_offset_matrix(arrays) + corrections = solve_corrections_sgd(offset_matrix) + if background_reference: + corrections -= corrections[background_reference] + for array, correction in zip(arrays, corrections, strict=True): + array.array -= correction + + if combine_function in ("mean", "sum"): + if match_background: + # if we're not matching the background, this part has already been done + for array in arrays: + # By default, values outside of the footprint are set to NaN + # but we set these to 0 here to avoid getting NaNs in the + # means/sums. + array.array[array.footprint == 0] = 0 + + output_array[array.view_in_original_array] += array.array * array.footprint + output_footprint[array.view_in_original_array] += array.footprint + + if combine_function == "mean": + with np.errstate(invalid="ignore"): + output_array /= output_footprint + + elif combine_function in ("first", "last", "min", "max"): + if combine_function == "min": + output_array[...] = np.inf + elif combine_function == "max": + output_array[...] = -np.inf - if combine_function == "mean": - with np.errstate(invalid="ignore"): - output_array /= output_footprint - - elif combine_function in ("first", "last", "min", "max"): - if combine_function == "min": - output_array[...] = np.inf - elif combine_function == "max": - output_array[...] = -np.inf - - for array in arrays: - if combine_function == "first": - mask = output_footprint[array.view_in_original_array] == 0 - elif combine_function == "last": - mask = array.footprint > 0 - elif combine_function == "min": - mask = (array.footprint > 0) & ( - array.array < output_array[array.view_in_original_array] + for array in arrays: + if combine_function == "first": + mask = output_footprint[array.view_in_original_array] == 0 + elif combine_function == "last": + mask = array.footprint > 0 + elif combine_function == "min": + mask = (array.footprint > 0) & ( + array.array < output_array[array.view_in_original_array] + ) + elif combine_function == "max": + mask = (array.footprint > 0) & ( + array.array > output_array[array.view_in_original_array] + ) + + output_footprint[array.view_in_original_array] = np.where( + mask, array.footprint, output_footprint[array.view_in_original_array] ) - elif combine_function == "max": - mask = (array.footprint > 0) & ( - array.array > output_array[array.view_in_original_array] + output_array[array.view_in_original_array] = np.where( + mask, array.array, output_array[array.view_in_original_array] ) - output_footprint[array.view_in_original_array] = np.where( - mask, array.footprint, output_footprint[array.view_in_original_array] - ) - output_array[array.view_in_original_array] = np.where( - mask, array.array, output_array[array.view_in_original_array] - ) - # We need to avoid potentially large memory allocation from output == 0 so # we operate in chunks. for chunk in iterate_chunks(output_array.shape, max_chunk_size=256 * 1024**2): From bd88160830c41681f29471f239244aed6c9c9df8 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Tue, 11 Jun 2024 14:30:27 +0100 Subject: [PATCH 297/366] Make sure we probably remove memory mapped arrays from disk --- reproject/mosaicking/coadd.py | 34 ++++++++++++++++++++++++++++++---- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/reproject/mosaicking/coadd.py b/reproject/mosaicking/coadd.py index 1fda079c2..cb50cbc2b 100644 --- a/reproject/mosaicking/coadd.py +++ b/reproject/mosaicking/coadd.py @@ -180,7 +180,7 @@ def reproject_and_coadd( if not on_the_fly: arrays = [] - with tempfile.TemporaryDirectory() as local_tmp_dir: + with tempfile.TemporaryDirectory(ignore_cleanup_errors=True) as local_tmp_dir: for idata in progress_bar(range(len(input_data))): # We need to pre-parse the data here since we need to figure out how to @@ -254,15 +254,19 @@ def reproject_and_coadd( # able to handle weights, and make the footprint become the combined # footprint + weight map + array_path = os.path.join(local_tmp_dir, f"array_{uuid.uuid4()}.np") + array = np.memmap( - os.path.join(local_tmp_dir, f"{uuid.uuid4()}.np"), + array_path, shape=shape_out_indiv, mode="w+", dtype=array_in.dtype, ) + footprint_path = os.path.join(local_tmp_dir, f"footprint_{uuid.uuid4()}.np") + footprint = np.memmap( - os.path.join(local_tmp_dir, f"{uuid.uuid4()}.np"), + footprint_path, shape=shape_out_indiv, mode="w+", dtype=float, @@ -280,8 +284,10 @@ def reproject_and_coadd( if weights_in is not None: + weights_path = os.path.join(local_tmp_dir, f"weights_{uuid.uuid4()}.np") + weights = np.memmap( - os.path.join(local_tmp_dir, f"{uuid.uuid4()}.np"), + weights_path, shape=shape_out_indiv, mode="w+", dtype=float, @@ -307,6 +313,11 @@ def reproject_and_coadd( if weights_in is not None: weights[reset] = 0.0 footprint *= weights + weights = None + try: + os.remove(weights_path) + except PermissionError: + pass array = ReprojectedArraySubset(array, footprint, bounds) @@ -325,6 +336,13 @@ def reproject_and_coadd( # the end of the loop. array.array *= array.footprint output_array[array.view_in_original_array] += array.array + array = None + footprint = None + try: + os.remove(array_path) + os.remove(footprint_path) + except PermissionError: + pass else: arrays.append(array) @@ -380,6 +398,14 @@ def reproject_and_coadd( mask, array.array, output_array[array.view_in_original_array] ) + # Avoid keeping any references to the memory-mapped arrays + if array is not None: + array.array = None + array.footprint = None + array = None + footprint = None + arrays = [] + # We need to avoid potentially large memory allocation from output == 0 so # we operate in chunks. for chunk in iterate_chunks(output_array.shape, max_chunk_size=256 * 1024**2): From 039d889489d23ab7e170f4ce693410d760a57abc Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Wed, 12 Jun 2024 14:28:46 +0100 Subject: [PATCH 298/366] Make use of intermediate memmaps optional and turn off by default --- reproject/mosaicking/coadd.py | 111 ++++++++++++++--------- reproject/mosaicking/tests/test_coadd.py | 19 ++-- 2 files changed, 80 insertions(+), 50 deletions(-) diff --git a/reproject/mosaicking/coadd.py b/reproject/mosaicking/coadd.py index cb50cbc2b..6058a2d9b 100644 --- a/reproject/mosaicking/coadd.py +++ b/reproject/mosaicking/coadd.py @@ -1,7 +1,7 @@ # Licensed under a 3-clause BSD style license - see LICENSE.rst import os +import sys import tempfile -import time import uuid import numpy as np @@ -17,6 +17,9 @@ __all__ = ["reproject_and_coadd"] +IS_WIN = sys.platform == "win32" + + def _noop(iterable): return iterable @@ -37,6 +40,7 @@ def reproject_and_coadd( block_sizes=None, progress_bar=None, blank_pixel_value=0, + intermediate_memmap=False, **kwargs, ): """ @@ -118,6 +122,9 @@ def reproject_and_coadd( blank_pixel_value : float, optional Value to use for areas of the resulting mosaic that do not have input data. + intermediate_memmap : bool, optional + If `True`, use `numpy.memmap` to store intermediate output arrays for + reprojected data. **kwargs Keyword arguments to be passed to the reprojection function. @@ -180,7 +187,7 @@ def reproject_and_coadd( if not on_the_fly: arrays = [] - with tempfile.TemporaryDirectory(ignore_cleanup_errors=True) as local_tmp_dir: + with tempfile.TemporaryDirectory(ignore_cleanup_errors=IS_WIN) as local_tmp_dir: for idata in progress_bar(range(len(input_data))): # We need to pre-parse the data here since we need to figure out how to @@ -254,23 +261,29 @@ def reproject_and_coadd( # able to handle weights, and make the footprint become the combined # footprint + weight map - array_path = os.path.join(local_tmp_dir, f"array_{uuid.uuid4()}.np") + if intermediate_memmap: - array = np.memmap( - array_path, - shape=shape_out_indiv, - mode="w+", - dtype=array_in.dtype, - ) + array_path = os.path.join(local_tmp_dir, f"array_{uuid.uuid4()}.np") + + array = np.memmap( + array_path, + shape=shape_out_indiv, + mode="w+", + dtype=array_in.dtype, + ) - footprint_path = os.path.join(local_tmp_dir, f"footprint_{uuid.uuid4()}.np") + footprint_path = os.path.join(local_tmp_dir, f"footprint_{uuid.uuid4()}.np") - footprint = np.memmap( - footprint_path, - shape=shape_out_indiv, - mode="w+", - dtype=float, - ) + footprint = np.memmap( + footprint_path, + shape=shape_out_indiv, + mode="w+", + dtype=float, + ) + + else: + + array = footprint = None array, footprint = reproject_function( (array_in, wcs_in), @@ -284,14 +297,20 @@ def reproject_and_coadd( if weights_in is not None: - weights_path = os.path.join(local_tmp_dir, f"weights_{uuid.uuid4()}.np") + if intermediate_memmap: - weights = np.memmap( - weights_path, - shape=shape_out_indiv, - mode="w+", - dtype=float, - ) + weights_path = os.path.join(local_tmp_dir, f"weights_{uuid.uuid4()}.np") + + weights = np.memmap( + weights_path, + shape=shape_out_indiv, + mode="w+", + dtype=float, + ) + + else: + + weights = None weights = reproject_function( (weights_in, wcs_in), @@ -313,11 +332,14 @@ def reproject_and_coadd( if weights_in is not None: weights[reset] = 0.0 footprint *= weights - weights = None - try: - os.remove(weights_path) - except PermissionError: - pass + + if intermediate_memmap: + # Remove the reference to the memmap before trying to remove the file itself + weights = None + try: + os.remove(weights_path) + except PermissionError: + pass array = ReprojectedArraySubset(array, footprint, bounds) @@ -336,13 +358,18 @@ def reproject_and_coadd( # the end of the loop. array.array *= array.footprint output_array[array.view_in_original_array] += array.array - array = None - footprint = None - try: - os.remove(array_path) - os.remove(footprint_path) - except PermissionError: - pass + + if intermediate_memmap: + # Remove the references to the memmaps themesleves before + # trying to remove the files thermselves. + array = None + footprint = None + try: + os.remove(array_path) + os.remove(footprint_path) + except PermissionError: + pass + else: arrays.append(array) @@ -363,7 +390,6 @@ def reproject_and_coadd( # but we set these to 0 here to avoid getting NaNs in the # means/sums. array.array[array.footprint == 0] = 0 - output_array[array.view_in_original_array] += array.array * array.footprint output_footprint[array.view_in_original_array] += array.footprint @@ -398,13 +424,12 @@ def reproject_and_coadd( mask, array.array, output_array[array.view_in_original_array] ) - # Avoid keeping any references to the memory-mapped arrays - if array is not None: - array.array = None - array.footprint = None - array = None - footprint = None - arrays = [] + # Avoid keeping any references to the memory-mapped arrays so that the + # files get cleaned up once we exit the context manager. + if intermediate_memmap: + array = None + footprint = None + arrays = [] # We need to avoid potentially large memory allocation from output == 0 so # we operate in chunks. diff --git a/reproject/mosaicking/tests/test_coadd.py b/reproject/mosaicking/tests/test_coadd.py index 04b7c271c..3a5ec3af0 100644 --- a/reproject/mosaicking/tests/test_coadd.py +++ b/reproject/mosaicking/tests/test_coadd.py @@ -24,6 +24,11 @@ def reproject_function(request): return request.param +@pytest.fixture(params=[False, True]) +def intermediate_memmap(request): + return request.param + + class TestReprojectAndCoAdd: def setup_method(self, method): self.wcs = WCS(naxis=2) @@ -74,7 +79,7 @@ def _overlapping_views(self): return views @pytest.mark.parametrize("combine_function", ["mean", "sum", "first", "last"]) - def test_coadd_no_overlap(self, combine_function, reproject_function): + def test_coadd_no_overlap(self, combine_function, reproject_function, intermediate_memmap): # Make sure that if all tiles are exactly non-overlapping, and # we use 'sum' or 'mean', we get the exact input array back. @@ -91,7 +96,7 @@ def test_coadd_no_overlap(self, combine_function, reproject_function): assert_allclose(array, self.array, atol=ATOL) assert_allclose(footprint, 1, atol=ATOL) - def test_coadd_with_overlap(self, reproject_function): + def test_coadd_with_overlap(self, reproject_function, intermediate_memmap): # Here we make the input tiles overlapping. We can only check the # mean, not the sum. @@ -107,7 +112,7 @@ def test_coadd_with_overlap(self, reproject_function): assert_allclose(array, self.array, atol=ATOL) - def test_coadd_with_outputs(self, tmp_path, reproject_function): + def test_coadd_with_outputs(self, tmp_path, reproject_function, intermediate_memmap): # Test the options to specify output array/footprint input_data = self._get_tiles(self._overlapping_views) @@ -173,7 +178,7 @@ def test_coadd_with_overlap_first_last(self, reproject_function, combine_functio assert_allclose(output_values, (i + 7) % 20) array[view] = np.nan - def test_coadd_background_matching(self, reproject_function): + def test_coadd_background_matching(self, reproject_function, intermediate_memmap): # Test out the background matching input_data = self._get_tiles(self._overlapping_views) @@ -209,7 +214,7 @@ def test_coadd_background_matching(self, reproject_function): assert_allclose(array - np.mean(array), self.array - np.mean(self.array), atol=ATOL) - def test_coadd_background_matching_one_array(self, reproject_function): + def test_coadd_background_matching_one_array(self, reproject_function, intermediate_memmap): # Test that background matching doesn't affect the output when there's # only one input image. @@ -268,7 +273,7 @@ def test_footprint_correct(self, reproject_function, combine_function, match_bac assert np.all((array != 0) == (footprint > 0)) - def test_coadd_background_matching_with_nan(self, reproject_function): + def test_coadd_background_matching_with_nan(self, reproject_function, intermediate_memmap): # Test out the background matching when NaN values are present. We do # this by using three arrays with the same footprint but with different # parts masked. @@ -300,7 +305,7 @@ def test_coadd_background_matching_with_nan(self, reproject_function): @pytest.mark.filterwarnings("ignore:unclosed file:ResourceWarning") @pytest.mark.parametrize("mode", ["arrays", "filenames", "hdus"]) - def test_coadd_with_weights(self, tmpdir, reproject_function, mode): + def test_coadd_with_weights(self, tmpdir, reproject_function, mode, intermediate_memmap): # Make sure that things work properly when specifying weights array1 = self.array + 1 From 37536c948252ae952eb99d176860a6604782ea01 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Thu, 13 Jun 2024 10:38:08 +0100 Subject: [PATCH 299/366] Bump minimum required version of astropy-healpix --- pyproject.toml | 2 +- tox.ini | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 0185c5b48..c443951d3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -12,7 +12,7 @@ requires-python = ">=3.10" dependencies = [ "numpy>=1.23", "astropy>=5.0", - "astropy-healpix>=0.6", + "astropy-healpix>=1.0", "scipy>=1.9", "dask[array]>=2021.8", "cloudpickle", diff --git a/tox.ini b/tox.ini index f3d6b8d8c..96e70befe 100644 --- a/tox.ini +++ b/tox.ini @@ -23,7 +23,7 @@ deps = oldestdeps: numpy==1.23.* oldestdeps: astropy==5.0.* - oldestdeps: astropy-healpix==0.6 + oldestdeps: astropy-healpix==1.0.* oldestdeps: scipy==1.9.* oldestdeps: dask==2021.8.* From 414b41853df3df2a623243040a407ff247be9adf Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Thu, 13 Jun 2024 11:06:18 +0100 Subject: [PATCH 300/366] Updated documentation --- docs/index.rst | 1 + docs/performance.rst | 50 +++++++++++++-------------------- docs/performance_mosaicking.rst | 50 +++++++++++++++++++++++++++++++++ 3 files changed, 70 insertions(+), 31 deletions(-) create mode 100644 docs/performance_mosaicking.rst diff --git a/docs/index.rst b/docs/index.rst index eddf73456..528a11050 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -138,6 +138,7 @@ that you want to reproject. footprints mosaicking performance + performance_mosaicking Reference/API ============= diff --git a/docs/performance.rst b/docs/performance.rst index 2be3e7136..88e797dcc 100644 --- a/docs/performance.rst +++ b/docs/performance.rst @@ -1,6 +1,6 @@ -********************** -Optimizing performance -********************** +**************************************************** +Optimizing performance for single-image reprojection +**************************************************** Disabling coordinate transformation round-tripping ================================================== @@ -150,31 +150,18 @@ the reprojection will be done in 64 separate output chunks. Note however that this does not break up the input array into chunks since in the general case any input pixel may contribute to any output pixel. -Multi-process reprojection -========================== +.. _multithreading: + +Multi-threaded reprojection +=========================== By default, the iteration over the output chunks is done in a single process/thread, but you may specify ``parallel=True`` to process these in -parallel. If you do this, reproject will use multiple processes (rather than -threads) to parallelize the computation (this is because the core reprojection -algorithms we use are not currently thread-safe). If you specify -``parallel=True``, then ``block_size`` will be automatically set to a sensible -default, but you can also set ``block_size`` manually for more control. Note -that you can also set ``parallel=`` to an integer to indicate the number of -processes to use. - -By default, in parallel mode, the entire input array will be written to a -temporary file that is then memory-mapped - this is to avoid loading the whole -input array into memory in each process. If you are specifying a WCS with fewer -dimensions than the data to be reprojected, as described in :ref:`broadcasting`, -you can set the block size to be such that the block size along the dimensions -being reprojected cover the whole image, while the other dimensions can be -smaller. For example, if you are reprojecting a spectral cube with dimensions -(500, 2048, 2048) where 500 is the number of spectral channels and (2048, 2048) -is the celestial plane, then if you are reprojecting just the celestial part of -the WCS you can specify a block size of (N, 2048, 2048) and this will enable a -separate reprojection mode where the input array is not written to disk but -where the reprojection is done in truly independent chunks with size (N, 2048, 2048). +parallel. If you do this, reproject will use multiple threads to parallelize the +computation. If you specify ``parallel=True``, then ``block_size`` will be +automatically set to a sensible default, but you can also set ``block_size`` +manually for more control. Note that you can also set ``parallel=`` to an +integer to indicate the number of threads to use. Input dask arrays ================= @@ -197,7 +184,13 @@ Therefore, for now, when a dask array is passed as input, it is computed using the current default scheduler and converted to a Numpy memory-mapped array. This is done efficiently in terms of memory and never results in the whole dataset being loaded into memory at any given time. However, this does require -sufficient space on disk to store the array. +sufficient space on disk to store the array. If your default system temporary +directory does not have sufficient space, you can set the ``TMPDIR`` environment +variable to point at another directory: + + >>> import os + >>> os.environ['TMPDIR'] = '/home/lancelot/tmp' + Output dask arrays ================== @@ -217,11 +210,6 @@ can make the functions delay any computation and return dask arrays:: You can then compute the array or a section of the array yourself whenever you need, or use the result in further dask expressions. -.. warning:: The reprojection does not currently work reliably when using multiple threads, so - it is important to make sure you use a dask scheduler that is not multi-threaded. - At the time of writing, the default dask scheduler is ``threads``, so the scheduler - needs to be explicitly set to a different one. - Using dask.distributed ====================== diff --git a/docs/performance_mosaicking.rst b/docs/performance_mosaicking.rst new file mode 100644 index 000000000..9200b5d1a --- /dev/null +++ b/docs/performance_mosaicking.rst @@ -0,0 +1,50 @@ +************************************* +Optimizing performance for mosaicking +************************************* + +Using memory-mapped output arrays +================================= + +If you are producing a large mosaic, you may be want to write the mosaic and +footprint to an array of your choice, such as for example a memory-mapped array. +For example: + +.. doctest-skip:: + + >>> output_array = np.memmap(filename='array.np', mode='w+', + ... shape=shape_out, dtype='float32') + >>> output_footprint = np.memmap(filename='footprint.np', mode='w+', + ... shape=shape_out, dtype='float32') + >>> reproject_and_coadd(..., + output_array=output_array, + output_footprint=output_footprint) + +Using memory-mapped intermediate arrays +======================================= + +During the mosaicking process, each cube is reprojected to the minimal subset of +the final header that it covers. In some cases, this can result in arrays that +may not fit in memory. In this case, you can use the ``intermediate_memmap`` +option to indicate that all intermediate arrays in the mosaicking process should +use memory-mapped arrays rather than in-memory arrays: + + >>> reproject_and_coadd(..., + intermediate_memmap=True) + +Combined with the above option to specify the output array and footprint for the +final mosaic, it is possible to make sure that no large arrays are ever loaded +into memory. Note however that you will need to make sure you have sufficient disk +space in your temporary directory. If your default system temporary directory does +not have sufficient space, you can set the ``TMPDIR`` environment variable to point +at another directory: + + >>> import os + >>> os.environ['TMPDIR'] = '/home/lancelot/tmp' + +Multi-threading +=============== + +Similarly to single-image reprojection (see :ref:`multithreading`), it is possible +to make use of multi-threading during the mosaicking process by setting the +``parallel=`` option to True or to an integer value to indicate the number of +threads to use. From 7f7f90ba3436f6cd872d7a7f6c6f2332407eec7e Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Thu, 13 Jun 2024 11:21:09 +0100 Subject: [PATCH 301/366] Fix doctest failure --- docs/performance_mosaicking.rst | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/docs/performance_mosaicking.rst b/docs/performance_mosaicking.rst index 9200b5d1a..0985efcba 100644 --- a/docs/performance_mosaicking.rst +++ b/docs/performance_mosaicking.rst @@ -16,8 +16,8 @@ For example: >>> output_footprint = np.memmap(filename='footprint.np', mode='w+', ... shape=shape_out, dtype='float32') >>> reproject_and_coadd(..., - output_array=output_array, - output_footprint=output_footprint) + ... output_array=output_array, + ... output_footprint=output_footprint) Using memory-mapped intermediate arrays ======================================= @@ -28,8 +28,10 @@ may not fit in memory. In this case, you can use the ``intermediate_memmap`` option to indicate that all intermediate arrays in the mosaicking process should use memory-mapped arrays rather than in-memory arrays: +.. doctest-skip:: + >>> reproject_and_coadd(..., - intermediate_memmap=True) + ... intermediate_memmap=True) Combined with the above option to specify the output array and footprint for the final mosaic, it is possible to make sure that no large arrays are ever loaded From 334ef21652e60b5ea46e49ac5b3dd3a21d01bbf9 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Fri, 14 Jun 2024 11:05:01 +0100 Subject: [PATCH 302/366] Address performance issues when using multi-processing --- reproject/common.py | 26 +++++++++++++++++--------- reproject/utils.py | 4 ++-- 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/reproject/common.py b/reproject/common.py index 57b506627..7914914b9 100644 --- a/reproject/common.py +++ b/reproject/common.py @@ -166,14 +166,20 @@ def _reproject_dispatcher( # memory-mapped array so that it can be used by the various reprojection # functions (which don't internally work with dask arrays). - if isinstance(array_in, da.core.Array): - if return_type == "dask": - # We should use a temporary directory that will persist beyond - # the call to the reproject function. - tmp_dir = tempfile.mkdtemp() + if isinstance(array_in, da.core.Array) or return_type == "dask": + if isinstance(array_in, np.memmap): + array_in_or_path = array_in.filename, { + "dtype": array_in.dtype, + "shape": array_in.shape, + } else: - tmp_dir = local_tmp_dir - array_in_or_path = as_delayed_memmap_path(array_in, tmp_dir) + if return_type == "dask": + # We should use a temporary directory that will persist beyond + # the call to the reproject function. + tmp_dir = tempfile.mkdtemp() + else: + tmp_dir = local_tmp_dir + array_in_or_path = as_delayed_memmap_path(array_in, tmp_dir) else: # Here we could set array_in_or_path to array_in_path if it has # been set previously, but in synchronous and threaded mode it is @@ -208,13 +214,15 @@ def reproject_single_block(a, array_or_path, block_info=None): wcs_out_sub = HighLevelWCSWrapper(low_level_wcs) - if isinstance(array_or_path, str): + if isinstance(array_or_path, tuple): + array_in = np.memmap(array_or_path[0], **array_or_path[1]) + elif isinstance(array_or_path, str): array_in = np.memmap(array_or_path, dtype=float, shape=shape_in) else: array_in = array_or_path if array_or_path is None: - raise ValueError() + raise RuntimeError("array_or_path is not set") shape_out = block_info[None]["chunk-shape"][1:] diff --git a/reproject/utils.py b/reproject/utils.py index 70e85c877..b5428857d 100644 --- a/reproject/utils.py +++ b/reproject/utils.py @@ -75,8 +75,8 @@ def hdu_to_numpy_memmap(hdu): """ if ( - "BSCALE" in hdu.header - or "BZERO" in hdu.header + hdu.header.get("BSCALE", 1) != 1 + or hdu.header.get("BZERO", 0) != 0 or hdu.fileinfo() is None or hdu._data_replaced or hdu.fileinfo()["file"].compression is not None From e32f521dc79f4396753de21ae58941e90c4f03fb Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Fri, 14 Jun 2024 11:23:33 +0100 Subject: [PATCH 303/366] Improvements to memmap handling --- reproject/common.py | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/reproject/common.py b/reproject/common.py index 7914914b9..f4d917d7f 100644 --- a/reproject/common.py +++ b/reproject/common.py @@ -166,20 +166,19 @@ def _reproject_dispatcher( # memory-mapped array so that it can be used by the various reprojection # functions (which don't internally work with dask arrays). - if isinstance(array_in, da.core.Array) or return_type == "dask": - if isinstance(array_in, np.memmap): - array_in_or_path = array_in.filename, { - "dtype": array_in.dtype, - "shape": array_in.shape, - } + if isinstance(array_in, np.memmap): + array_in_or_path = array_in.filename, { + "dtype": array_in.dtype, + "shape": array_in.shape, + } + elif isinstance(array_in, da.core.Array) or return_type == "dask": + if return_type == "dask": + # We should use a temporary directory that will persist beyond + # the call to the reproject function. + tmp_dir = tempfile.mkdtemp() else: - if return_type == "dask": - # We should use a temporary directory that will persist beyond - # the call to the reproject function. - tmp_dir = tempfile.mkdtemp() - else: - tmp_dir = local_tmp_dir - array_in_or_path = as_delayed_memmap_path(array_in, tmp_dir) + tmp_dir = local_tmp_dir + array_in_or_path = as_delayed_memmap_path(array_in, tmp_dir) else: # Here we could set array_in_or_path to array_in_path if it has # been set previously, but in synchronous and threaded mode it is @@ -215,9 +214,9 @@ def reproject_single_block(a, array_or_path, block_info=None): wcs_out_sub = HighLevelWCSWrapper(low_level_wcs) if isinstance(array_or_path, tuple): - array_in = np.memmap(array_or_path[0], **array_or_path[1]) + array_in = np.memmap(array_or_path[0], **array_or_path[1], mode="r") elif isinstance(array_or_path, str): - array_in = np.memmap(array_or_path, dtype=float, shape=shape_in) + array_in = np.memmap(array_or_path, dtype=float, shape=shape_in, mode="r") else: array_in = array_or_path From 6f69d538512064f7f918c82860c3a66c8950b30b Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Fri, 14 Jun 2024 11:23:55 +0100 Subject: [PATCH 304/366] Remove sentence about threads --- docs/performance.rst | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/docs/performance.rst b/docs/performance.rst index 88e797dcc..34f283c1b 100644 --- a/docs/performance.rst +++ b/docs/performance.rst @@ -217,6 +217,4 @@ The `dask.distributed `_ package makes possible to use distributed schedulers for dask. In order to compute reprojections with dask.distributed, you should make use of the ``return_type='dask'`` option mentioned above so that you can compute the dask -array once the distributed scheduler has been set up. As mentioned in `Output -dask arrays`_, you should make sure that you limit any cluster to have one -thread per process or the results may be unreliable. +array once the distributed scheduler has been set up. From 354f22277ff0e136ec0a4a05088ca5045430aee2 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Fri, 14 Jun 2024 11:34:51 +0100 Subject: [PATCH 305/366] Make it possible to use the current active dask scheduler --- reproject/adaptive/high_level.py | 7 +++-- reproject/common.py | 31 +++++++++++++-------- reproject/interpolation/high_level.py | 7 +++-- reproject/spherical_intersect/high_level.py | 7 +++-- 4 files changed, 32 insertions(+), 20 deletions(-) diff --git a/reproject/adaptive/high_level.py b/reproject/adaptive/high_level.py index 7c9d1c881..f55820dcf 100644 --- a/reproject/adaptive/high_level.py +++ b/reproject/adaptive/high_level.py @@ -203,12 +203,13 @@ def reproject_adaptive( the block size automatically determined. If ``block_size`` is not specified or set to `None`, the reprojection will not be carried out in blocks. - parallel : bool or int, optional + parallel : bool or int or str, optional If `True`, the reprojection is carried out in parallel, and if a - positive integer, this specifies the number of processes to use. + positive integer, this specifies the number of threads to use. The reprojection will be parallelized over output array blocks specified by ``block_size`` (if the block size is not set, it will be determined - automatically). + automatically). To use the currently active dask scheduler (e.g. + dask.distributed), set this to ``'current-scheduler'``. return_type : {'numpy', 'dask'}, optional Whether to return numpy or dask arrays - defaults to 'numpy'. diff --git a/reproject/common.py b/reproject/common.py index f4d917d7f..fd4939247 100644 --- a/reproject/common.py +++ b/reproject/common.py @@ -82,12 +82,13 @@ def _reproject_dispatcher( An array in which to store the footprint of reprojected data. This can be any numpy array including a memory map, which may be helpful when dealing with extremely large files. - parallel : bool or int, optional + parallel : bool or int or str, optional If `True`, the reprojection is carried out in parallel, and if a - positive integer, this specifies the number of processes to use. + positive integer, this specifies the number of threads to use. The reprojection will be parallelized over output array blocks specified by ``block_size`` (if the block size is not set, it will be determined - automatically). + automatically). To use the currently active dask scheduler (e.g. + dask.distributed), set this to ``'current-scheduler'``. reproject_func_kwargs : dict, optional Keyword arguments to pass through to ``reproject_func`` return_type : {'numpy', 'dask'}, optional @@ -293,18 +294,26 @@ def reproject_single_block(a, array_or_path, block_info=None): # 'synchronous' scheduler since that is I/O limited so does not need # to be done in parallel. - if isinstance(parallel, bool): - workers = {} + zarr_path = os.path.join(local_tmp_dir, f"{uuid.uuid4()}.zarr") + + if parallel == "current-scheduler": + # Just use whatever is the current active scheduler, which can + # be used for e.g. dask.distributed + result.to_zarr(zarr_path) else: - if parallel > 0: - workers = {"num_workers": parallel} + if isinstance(parallel, bool): + workers = {} else: - raise ValueError("The number of processors to use must be strictly positive") + if parallel > 0: + workers = {"num_workers": parallel} + else: + raise ValueError( + "The number of processors to use must be strictly positive" + ) - zarr_path = os.path.join(local_tmp_dir, f"{uuid.uuid4()}.zarr") + with dask.config.set(scheduler="threads", **workers): + result.to_zarr(zarr_path) - with dask.config.set(scheduler="threads", **workers): - result.to_zarr(zarr_path) result = da.from_zarr(zarr_path) if return_footprint: diff --git a/reproject/interpolation/high_level.py b/reproject/interpolation/high_level.py index c6a5f6944..316764758 100644 --- a/reproject/interpolation/high_level.py +++ b/reproject/interpolation/high_level.py @@ -99,12 +99,13 @@ def reproject_interp( the block size automatically determined. If ``block_size`` is not specified or set to `None`, the reprojection will not be carried out in blocks. - parallel : bool or int, optional + parallel : bool or int or str, optional If `True`, the reprojection is carried out in parallel, and if a - positive integer, this specifies the number of processes to use. + positive integer, this specifies the number of threads to use. The reprojection will be parallelized over output array blocks specified by ``block_size`` (if the block size is not set, it will be determined - automatically). + automatically). To use the currently active dask scheduler (e.g. + dask.distributed), set this to ``'current-scheduler'``. return_type : {'numpy', 'dask'}, optional Whether to return numpy or dask arrays - defaults to 'numpy'. diff --git a/reproject/spherical_intersect/high_level.py b/reproject/spherical_intersect/high_level.py index d7c549b81..4272b257c 100644 --- a/reproject/spherical_intersect/high_level.py +++ b/reproject/spherical_intersect/high_level.py @@ -77,12 +77,13 @@ def reproject_exact( the block size automatically determined. If ``block_size`` is not specified or set to `None`, the reprojection will not be carried out in blocks. - parallel : bool or int, optional + parallel : bool or int or str, optional If `True`, the reprojection is carried out in parallel, and if a - positive integer, this specifies the number of processes to use. + positive integer, this specifies the number of threads to use. The reprojection will be parallelized over output array blocks specified by ``block_size`` (if the block size is not set, it will be determined - automatically). + automatically). To use the currently active dask scheduler (e.g. + dask.distributed), set this to ``'current-scheduler'``. return_type : {'numpy', 'dask'}, optional Whether to return numpy or dask arrays - defaults to 'numpy'. From fa2c73a975eb14215326b6665e93f44df1959d7f Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Fri, 14 Jun 2024 11:38:59 +0100 Subject: [PATCH 306/366] Updated docs regarding dask.distributed --- docs/performance.rst | 3 ++- docs/performance_mosaicking.rst | 9 +++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/docs/performance.rst b/docs/performance.rst index 34f283c1b..2300f1917 100644 --- a/docs/performance.rst +++ b/docs/performance.rst @@ -215,6 +215,7 @@ Using dask.distributed The `dask.distributed `_ package makes it possible to use distributed schedulers for dask. In order to compute -reprojections with dask.distributed, you should make use of the +reprojections with dask.distributed, set up the client and then call the reprojection +functions with ``parallel='current-scheduler'``. Alternatively, you can make use of the ``return_type='dask'`` option mentioned above so that you can compute the dask array once the distributed scheduler has been set up. diff --git a/docs/performance_mosaicking.rst b/docs/performance_mosaicking.rst index 0985efcba..db85300c1 100644 --- a/docs/performance_mosaicking.rst +++ b/docs/performance_mosaicking.rst @@ -50,3 +50,12 @@ Similarly to single-image reprojection (see :ref:`multithreading`), it is possib to make use of multi-threading during the mosaicking process by setting the ``parallel=`` option to True or to an integer value to indicate the number of threads to use. + +Using dask.distributed +====================== + +The `dask.distributed `_ package makes +it possible to use distributed schedulers for dask. In order to do mosaicking +with dask.distributed, set up the client and then call +:func:`~reproject.mosaicking.reproject_and_coadd` with the +``parallel='current-scheduler'`` option. From 6d7a774f745e98b002525046f6c6e059fea5ef0e Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Fri, 14 Jun 2024 21:01:37 +0100 Subject: [PATCH 307/366] Take into account offset when saving memmap properties --- reproject/common.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/reproject/common.py b/reproject/common.py index fd4939247..5156b835e 100644 --- a/reproject/common.py +++ b/reproject/common.py @@ -167,10 +167,11 @@ def _reproject_dispatcher( # memory-mapped array so that it can be used by the various reprojection # functions (which don't internally work with dask arrays). - if isinstance(array_in, np.memmap): + if isinstance(array_in, np.memmap) and array_in.flags.c_contiguous: array_in_or_path = array_in.filename, { "dtype": array_in.dtype, "shape": array_in.shape, + "offset": array_in.offset } elif isinstance(array_in, da.core.Array) or return_type == "dask": if return_type == "dask": From ee8c786384d4775feacdfb7e0447780e095cf586 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Fri, 14 Jun 2024 21:19:37 +0100 Subject: [PATCH 308/366] Fix code style --- reproject/common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reproject/common.py b/reproject/common.py index 5156b835e..50d994042 100644 --- a/reproject/common.py +++ b/reproject/common.py @@ -171,7 +171,7 @@ def _reproject_dispatcher( array_in_or_path = array_in.filename, { "dtype": array_in.dtype, "shape": array_in.shape, - "offset": array_in.offset + "offset": array_in.offset, } elif isinstance(array_in, da.core.Array) or return_type == "dask": if return_type == "dask": From 80148690ef68d9492c98832ec6078f012a0100bf Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Fri, 14 Jun 2024 21:56:06 +0100 Subject: [PATCH 309/366] Don't make the memmap-ed arrays read-only as Cython doesn't like this --- reproject/common.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/reproject/common.py b/reproject/common.py index 50d994042..8050910c8 100644 --- a/reproject/common.py +++ b/reproject/common.py @@ -216,9 +216,9 @@ def reproject_single_block(a, array_or_path, block_info=None): wcs_out_sub = HighLevelWCSWrapper(low_level_wcs) if isinstance(array_or_path, tuple): - array_in = np.memmap(array_or_path[0], **array_or_path[1], mode="r") + array_in = np.memmap(array_or_path[0], **array_or_path[1]) elif isinstance(array_or_path, str): - array_in = np.memmap(array_or_path, dtype=float, shape=shape_in, mode="r") + array_in = np.memmap(array_or_path, dtype=float, shape=shape_in) else: array_in = array_or_path From 2387058802c9b00fbfae0558c6700cae52a1675c Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Sun, 16 Jun 2024 20:25:58 +0100 Subject: [PATCH 310/366] Added logging calls --- reproject/mosaicking/coadd.py | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/reproject/mosaicking/coadd.py b/reproject/mosaicking/coadd.py index 6058a2d9b..23dba8e00 100644 --- a/reproject/mosaicking/coadd.py +++ b/reproject/mosaicking/coadd.py @@ -3,6 +3,7 @@ import sys import tempfile import uuid +from logging import getLogger import numpy as np from astropy.wcs import WCS @@ -143,6 +144,10 @@ def reproject_and_coadd( # up memory usage. We could probably still have references to array # objects, but we'd just make sure these were memory mapped + print(__name__) + + logger = getLogger(__name__) + # Validate inputs if combine_function not in ("mean", "sum", "first", "last", "min", "max"): @@ -176,12 +181,17 @@ def reproject_and_coadd( f"the output shape {shape_out}" ) + logger.info(f'Output mosaic will have shape {shape_out}') + # Define 'on-the-fly' mode: in the case where we don't need to match # the backgrounds and we are combining with 'mean' or 'sum', we don't # have to keep track of the intermediate arrays and can just modify # the output array on-the-fly on_the_fly = not match_background and combine_function in ("mean", "sum") + on_the_fly_prefix = 'Using' if on_the_fly else 'Not using' + logger.info(f'{on_the_fly_prefix} on-the-fly mode for adding individual reprojected images to output array') + # Start off by reprojecting individual images to the final projection if not on_the_fly: @@ -190,6 +200,9 @@ def reproject_and_coadd( with tempfile.TemporaryDirectory(ignore_cleanup_errors=IS_WIN) as local_tmp_dir: for idata in progress_bar(range(len(input_data))): + + logger.info(f'Processing input data {idata + 1} of {len(input_data)}') + # We need to pre-parse the data here since we need to figure out how to # optimize/minimize the size of each output tile (see below). array_in, wcs_in = parse_input_data(input_data[idata], hdu_in=hdu_in) @@ -238,6 +251,7 @@ def reproject_and_coadd( break if skip_data: + logger.info('Skipping reprojection as no predicted overlap with final mosaic header') continue slice_out = tuple([slice(imin, imax) for (imin, imax) in bounds]) @@ -265,6 +279,8 @@ def reproject_and_coadd( array_path = os.path.join(local_tmp_dir, f"array_{uuid.uuid4()}.np") + logger.info(f'Creating memory-mapped array with shape {shape_out_indiv} at {array_path}') + array = np.memmap( array_path, shape=shape_out_indiv, @@ -274,6 +290,8 @@ def reproject_and_coadd( footprint_path = os.path.join(local_tmp_dir, f"footprint_{uuid.uuid4()}.np") + logger.info(f'Creating memory-mapped footprint with shape {shape_out_indiv} at {footprint_path}') + footprint = np.memmap( footprint_path, shape=shape_out_indiv, @@ -285,6 +303,8 @@ def reproject_and_coadd( array = footprint = None + logger.info(f'Calling {reproject_function} with shape_out={shape_out_indiv}') + array, footprint = reproject_function( (array_in, wcs_in), output_projection=wcs_out_indiv, @@ -301,6 +321,8 @@ def reproject_and_coadd( weights_path = os.path.join(local_tmp_dir, f"weights_{uuid.uuid4()}.np") + logger.info(f'Creating memory-mapped weights with shape {shape_out_indiv} at {weights_path}') + weights = np.memmap( weights_path, shape=shape_out_indiv, @@ -312,6 +334,8 @@ def reproject_and_coadd( weights = None + logger.info(f'Calling {reproject_function} with shape_out={shape_out_indiv} for weights') + weights = reproject_function( (weights_in, wcs_in), output_projection=wcs_out_indiv, @@ -335,6 +359,7 @@ def reproject_and_coadd( if intermediate_memmap: # Remove the reference to the memmap before trying to remove the file itself + logger.info(f'Removing memory-mapped weight array') weights = None try: os.remove(weights_path) @@ -347,6 +372,7 @@ def reproject_and_coadd( # output image is empty (due e.g. to no overlap). if on_the_fly: + logger.info(f'Adding reprojected array to final array') # By default, values outside of the footprint are set to NaN # but we set these to 0 here to avoid getting NaNs in the # means/sums. @@ -362,6 +388,7 @@ def reproject_and_coadd( if intermediate_memmap: # Remove the references to the memmaps themesleves before # trying to remove the files thermselves. + logger.info(f'Removing memory-mapped array and footprint arrays') array = None footprint = None try: @@ -371,10 +398,12 @@ def reproject_and_coadd( pass else: + logger.info(f'Adding reprojected array to list to combine later') arrays.append(array) # If requested, try and match the backgrounds. if match_background and len(arrays) > 1: + logger.info(f'Match backgrounds') offset_matrix = determine_offset_matrix(arrays) corrections = solve_corrections_sgd(offset_matrix) if background_reference: @@ -384,6 +413,7 @@ def reproject_and_coadd( if combine_function in ("mean", "sum"): if match_background: + logger.info(f'Combining reprojected arrays with function {combine_function}') # if we're not matching the background, this part has already been done for array in arrays: # By default, values outside of the footprint are set to NaN @@ -394,10 +424,12 @@ def reproject_and_coadd( output_footprint[array.view_in_original_array] += array.footprint if combine_function == "mean": + logger.info(f'Handle normalization of output array') with np.errstate(invalid="ignore"): output_array /= output_footprint elif combine_function in ("first", "last", "min", "max"): + logger.info(f'Combining reprojected arrays with function {combine_function}') if combine_function == "min": output_array[...] = np.inf elif combine_function == "max": @@ -433,6 +465,7 @@ def reproject_and_coadd( # We need to avoid potentially large memory allocation from output == 0 so # we operate in chunks. + logger.info(f'Resetting invalid pixels to {blank_pixel_value}') for chunk in iterate_chunks(output_array.shape, max_chunk_size=256 * 1024**2): output_array[chunk][output_footprint[chunk] == 0] = blank_pixel_value From 8e582c5313fea6510498d83c48b5256f754091db Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Sun, 16 Jun 2024 20:42:58 +0100 Subject: [PATCH 311/366] More logging calls --- reproject/common.py | 16 +++++++++++ reproject/mosaicking/coadd.py | 52 +++++++++++++++++++++-------------- reproject/utils.py | 1 + 3 files changed, 48 insertions(+), 21 deletions(-) diff --git a/reproject/common.py b/reproject/common.py index 8050910c8..a1fd624ce 100644 --- a/reproject/common.py +++ b/reproject/common.py @@ -1,3 +1,4 @@ +import logging import os import tempfile import uuid @@ -17,8 +18,11 @@ @delayed(pure=True) def as_delayed_memmap_path(array, tmp_dir): + logger = logging.getLogger(__name__) if isinstance(array, da.core.Array): + logger.info("Writing input dask array to Numpy memory-mapped array") array_path, _ = _dask_to_numpy_memmap(array, tmp_dir) + logger.info(f"Numpy memory-mapped array is now at {array_path}") else: array_path = os.path.join(tmp_dir, f"{uuid.uuid4()}.npy") array_memmapped = np.memmap( @@ -95,6 +99,8 @@ def _reproject_dispatcher( Whether to return numpy or dask arrays - defaults to 'numpy'. """ + logger = logging.getLogger(__name__) + if return_type is None: return_type = "numpy" elif return_type not in ("numpy", "dask"): @@ -138,7 +144,11 @@ def _reproject_dispatcher( ) if isinstance(array_in, da.core.Array): + logger.info("Writing input dask array to Numpy memory-mapped array") _, array_in = _dask_to_numpy_memmap(array_in, local_tmp_dir) + logger.info(f"Numpy memory-mapped array is now at {array_path}") + + logger.info(f"Calling {reproject_func.__name__} in non-dask mode") try: return reproject_func( @@ -262,6 +272,8 @@ def reproject_single_block(a, array_or_path, block_info=None): array_out_dask = da.empty(shape_out) array_out_dask = array_out_dask.rechunk(block_size_limit=64 * 1024**2, **rechunk_kwargs) + logger.info(f"Setting out output dask array with map_blocks") + result = da.map_blocks( reproject_single_block, array_out_dask, @@ -297,6 +309,8 @@ def reproject_single_block(a, array_or_path, block_info=None): zarr_path = os.path.join(local_tmp_dir, f"{uuid.uuid4()}.zarr") + logger.info(f"Computing output array directly to zarr array at {zarr_path}") + if parallel == "current-scheduler": # Just use whatever is the current active scheduler, which can # be used for e.g. dask.distributed @@ -317,6 +331,8 @@ def reproject_single_block(a, array_or_path, block_info=None): result = da.from_zarr(zarr_path) + logger.info(f"Copying output zarr array into output Numpy arrays") + if return_footprint: da.store( [result[0], result[1]], diff --git a/reproject/mosaicking/coadd.py b/reproject/mosaicking/coadd.py index 23dba8e00..56ddb209b 100644 --- a/reproject/mosaicking/coadd.py +++ b/reproject/mosaicking/coadd.py @@ -144,8 +144,6 @@ def reproject_and_coadd( # up memory usage. We could probably still have references to array # objects, but we'd just make sure these were memory mapped - print(__name__) - logger = getLogger(__name__) # Validate inputs @@ -181,7 +179,7 @@ def reproject_and_coadd( f"the output shape {shape_out}" ) - logger.info(f'Output mosaic will have shape {shape_out}') + logger.info(f"Output mosaic will have shape {shape_out}") # Define 'on-the-fly' mode: in the case where we don't need to match # the backgrounds and we are combining with 'mean' or 'sum', we don't @@ -189,8 +187,10 @@ def reproject_and_coadd( # the output array on-the-fly on_the_fly = not match_background and combine_function in ("mean", "sum") - on_the_fly_prefix = 'Using' if on_the_fly else 'Not using' - logger.info(f'{on_the_fly_prefix} on-the-fly mode for adding individual reprojected images to output array') + on_the_fly_prefix = "Using" if on_the_fly else "Not using" + logger.info( + f"{on_the_fly_prefix} on-the-fly mode for adding individual reprojected images to output array" + ) # Start off by reprojecting individual images to the final projection @@ -201,7 +201,7 @@ def reproject_and_coadd( for idata in progress_bar(range(len(input_data))): - logger.info(f'Processing input data {idata + 1} of {len(input_data)}') + logger.info(f"Processing input data {idata + 1} of {len(input_data)}") # We need to pre-parse the data here since we need to figure out how to # optimize/minimize the size of each output tile (see below). @@ -251,7 +251,9 @@ def reproject_and_coadd( break if skip_data: - logger.info('Skipping reprojection as no predicted overlap with final mosaic header') + logger.info( + "Skipping reprojection as no predicted overlap with final mosaic header" + ) continue slice_out = tuple([slice(imin, imax) for (imin, imax) in bounds]) @@ -279,7 +281,9 @@ def reproject_and_coadd( array_path = os.path.join(local_tmp_dir, f"array_{uuid.uuid4()}.np") - logger.info(f'Creating memory-mapped array with shape {shape_out_indiv} at {array_path}') + logger.info( + f"Creating memory-mapped array with shape {shape_out_indiv} at {array_path}" + ) array = np.memmap( array_path, @@ -290,7 +294,9 @@ def reproject_and_coadd( footprint_path = os.path.join(local_tmp_dir, f"footprint_{uuid.uuid4()}.np") - logger.info(f'Creating memory-mapped footprint with shape {shape_out_indiv} at {footprint_path}') + logger.info( + f"Creating memory-mapped footprint with shape {shape_out_indiv} at {footprint_path}" + ) footprint = np.memmap( footprint_path, @@ -303,7 +309,7 @@ def reproject_and_coadd( array = footprint = None - logger.info(f'Calling {reproject_function} with shape_out={shape_out_indiv}') + logger.info(f"Calling {reproject_function.__name__} with shape_out={shape_out_indiv}") array, footprint = reproject_function( (array_in, wcs_in), @@ -321,7 +327,9 @@ def reproject_and_coadd( weights_path = os.path.join(local_tmp_dir, f"weights_{uuid.uuid4()}.np") - logger.info(f'Creating memory-mapped weights with shape {shape_out_indiv} at {weights_path}') + logger.info( + f"Creating memory-mapped weights with shape {shape_out_indiv} at {weights_path}" + ) weights = np.memmap( weights_path, @@ -334,7 +342,9 @@ def reproject_and_coadd( weights = None - logger.info(f'Calling {reproject_function} with shape_out={shape_out_indiv} for weights') + logger.info( + f"Calling {reproject_function.__name__} with shape_out={shape_out_indiv} for weights" + ) weights = reproject_function( (weights_in, wcs_in), @@ -359,7 +369,7 @@ def reproject_and_coadd( if intermediate_memmap: # Remove the reference to the memmap before trying to remove the file itself - logger.info(f'Removing memory-mapped weight array') + logger.info(f"Removing memory-mapped weight array") weights = None try: os.remove(weights_path) @@ -372,7 +382,7 @@ def reproject_and_coadd( # output image is empty (due e.g. to no overlap). if on_the_fly: - logger.info(f'Adding reprojected array to final array') + logger.info(f"Adding reprojected array to final array") # By default, values outside of the footprint are set to NaN # but we set these to 0 here to avoid getting NaNs in the # means/sums. @@ -388,7 +398,7 @@ def reproject_and_coadd( if intermediate_memmap: # Remove the references to the memmaps themesleves before # trying to remove the files thermselves. - logger.info(f'Removing memory-mapped array and footprint arrays') + logger.info(f"Removing memory-mapped array and footprint arrays") array = None footprint = None try: @@ -398,12 +408,12 @@ def reproject_and_coadd( pass else: - logger.info(f'Adding reprojected array to list to combine later') + logger.info(f"Adding reprojected array to list to combine later") arrays.append(array) # If requested, try and match the backgrounds. if match_background and len(arrays) > 1: - logger.info(f'Match backgrounds') + logger.info(f"Match backgrounds") offset_matrix = determine_offset_matrix(arrays) corrections = solve_corrections_sgd(offset_matrix) if background_reference: @@ -413,7 +423,7 @@ def reproject_and_coadd( if combine_function in ("mean", "sum"): if match_background: - logger.info(f'Combining reprojected arrays with function {combine_function}') + logger.info(f"Combining reprojected arrays with function {combine_function}") # if we're not matching the background, this part has already been done for array in arrays: # By default, values outside of the footprint are set to NaN @@ -424,12 +434,12 @@ def reproject_and_coadd( output_footprint[array.view_in_original_array] += array.footprint if combine_function == "mean": - logger.info(f'Handle normalization of output array') + logger.info(f"Handle normalization of output array") with np.errstate(invalid="ignore"): output_array /= output_footprint elif combine_function in ("first", "last", "min", "max"): - logger.info(f'Combining reprojected arrays with function {combine_function}') + logger.info(f"Combining reprojected arrays with function {combine_function}") if combine_function == "min": output_array[...] = np.inf elif combine_function == "max": @@ -465,7 +475,7 @@ def reproject_and_coadd( # We need to avoid potentially large memory allocation from output == 0 so # we operate in chunks. - logger.info(f'Resetting invalid pixels to {blank_pixel_value}') + logger.info(f"Resetting invalid pixels to {blank_pixel_value}") for chunk in iterate_chunks(output_array.shape, max_chunk_size=256 * 1024**2): output_array[chunk][output_footprint[chunk] == 0] = blank_pixel_value diff --git a/reproject/utils.py b/reproject/utils.py index b5428857d..4d2a609e9 100644 --- a/reproject/utils.py +++ b/reproject/utils.py @@ -1,3 +1,4 @@ +import logging import os import tempfile import uuid From 5f0be30ca4cbfb73b116f55c4244f4f8c528b922 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Mon, 17 Jun 2024 10:31:00 +0100 Subject: [PATCH 312/366] Fix condition to detect test call to reproject_single_block, in some cases block_info can be an empty Numpy array --- reproject/common.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/reproject/common.py b/reproject/common.py index a1fd624ce..3d69c3693 100644 --- a/reproject/common.py +++ b/reproject/common.py @@ -200,7 +200,13 @@ def _reproject_dispatcher( array_in_or_path = array_in def reproject_single_block(a, array_or_path, block_info=None): - if a.ndim == 0 or block_info is None or block_info == []: + + if ( + a.ndim == 0 + or block_info is None + or block_info == [] + or (isinstance(block_info, np.ndarray) and block_info.tolist() == []) + ): return np.array([a, a]) # The WCS class from astropy is not thread-safe, see e.g. From 1873b98b849faf67dcfbcd80338b208eaba11131 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Mon, 17 Jun 2024 10:32:26 +0100 Subject: [PATCH 313/366] Put array in wrapper to avoid having dask compute it at any point --- reproject/common.py | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/reproject/common.py b/reproject/common.py index 3d69c3693..43e868941 100644 --- a/reproject/common.py +++ b/reproject/common.py @@ -16,11 +16,27 @@ __all__ = ["_reproject_dispatcher"] +class _ArrayContainer: + # When we set up as_delayed_memmap_path, if we pass a dask array to it, + # dask will actually compute the array before we get to the code inside + # as_delayed_memmap_path, so as a workaround we wrap any array we + # pass in using _ArrayContainer to make sure dask doesn't try and be smart. + def __init__(self, array): + self._array = array + + @delayed(pure=True) def as_delayed_memmap_path(array, tmp_dir): + + # Extract array from _ArrayContainer + if isinstance(array, _ArrayContainer): + array = array._array + else: + raise TypeError("Expected _ArrayContainer in as_delayed_memmap_path") + logger = logging.getLogger(__name__) if isinstance(array, da.core.Array): - logger.info("Writing input dask array to Numpy memory-mapped array") + logger.info("Computing input dask array to Numpy memory-mapped array") array_path, _ = _dask_to_numpy_memmap(array, tmp_dir) logger.info(f"Numpy memory-mapped array is now at {array_path}") else: @@ -190,7 +206,7 @@ def _reproject_dispatcher( tmp_dir = tempfile.mkdtemp() else: tmp_dir = local_tmp_dir - array_in_or_path = as_delayed_memmap_path(array_in, tmp_dir) + array_in_or_path = as_delayed_memmap_path(_ArrayContainer(array_in), tmp_dir) else: # Here we could set array_in_or_path to array_in_path if it has # been set previously, but in synchronous and threaded mode it is From 0860f9fd081f57b665580ec443e47fcc3403d9da Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Mon, 17 Jun 2024 10:57:49 +0100 Subject: [PATCH 314/366] Fix test --- reproject/common.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/reproject/common.py b/reproject/common.py index 43e868941..74d7fc8a5 100644 --- a/reproject/common.py +++ b/reproject/common.py @@ -160,8 +160,8 @@ def _reproject_dispatcher( ) if isinstance(array_in, da.core.Array): - logger.info("Writing input dask array to Numpy memory-mapped array") - _, array_in = _dask_to_numpy_memmap(array_in, local_tmp_dir) + logger.info("Computing input dask array to Numpy memory-mapped array") + array_path, array_in = _dask_to_numpy_memmap(array_in, local_tmp_dir) logger.info(f"Numpy memory-mapped array is now at {array_path}") logger.info(f"Calling {reproject_func.__name__} in non-dask mode") From 3d20a75dc550b32094376842df0b61a7b6f13b03 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 17 Jun 2024 16:28:02 +0000 Subject: [PATCH 315/366] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/PyCQA/flake8: 7.0.0 → 7.1.0](https://github.com/PyCQA/flake8/compare/7.0.0...7.1.0) - [github.com/astral-sh/ruff-pre-commit: v0.4.8 → v0.4.9](https://github.com/astral-sh/ruff-pre-commit/compare/v0.4.8...v0.4.9) --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 5eb60665c..8cef42c0f 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -40,7 +40,7 @@ repos: # F822: undefined name in __all__ # F823: local variable name referenced before assignment - repo: https://github.com/PyCQA/flake8 - rev: 7.0.0 + rev: 7.1.0 hooks: - id: flake8 args: @@ -77,7 +77,7 @@ repos: - tomli - repo: https://github.com/astral-sh/ruff-pre-commit - rev: "v0.4.8" + rev: "v0.4.9" hooks: - id: ruff args: ["--fix", "--show-fixes"] From e9620c84aee3c5b2a6feb0236ee9b1fe63b35da9 Mon Sep 17 00:00:00 2001 From: Sam Van Kooten Date: Wed, 19 Jun 2024 17:32:45 -0600 Subject: [PATCH 316/366] Support readonly arrays in adaptive --- reproject/adaptive/deforest.pyx | 4 ++-- reproject/adaptive/tests/test_core.py | 13 +++++++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/reproject/adaptive/deforest.pyx b/reproject/adaptive/deforest.pyx index 4876576bd..19205d8ce 100644 --- a/reproject/adaptive/deforest.pyx +++ b/reproject/adaptive/deforest.pyx @@ -155,7 +155,7 @@ cdef double clip(double x, double vmin, double vmax, int cyclic, @cython.wraparound(False) @cython.nonecheck(False) @cython.cdivision(True) -cdef bint sample_array(double[:,:,:] source, double[:] dest, +cdef bint sample_array(const double[:,:,:] source, double[:] dest, double x, double y, int x_cyclic, int y_cyclic, bint out_of_range_nearest) noexcept nogil: x = clip(x, 0, source.shape[2] - 1, x_cyclic, out_of_range_nearest) @@ -352,7 +352,7 @@ BAD_VALUE_MODES['ignore'] = 3 @cython.wraparound(False) @cython.nonecheck(False) @cython.cdivision(True) -def map_coordinates(double[:,:,:] source, double[:,:,:] target, Ci, int max_samples_width=-1, +def map_coordinates(const double[:,:,:] source, double[:,:,:] target, Ci, int max_samples_width=-1, int conserve_flux=False, int progress=False, int singularities_nan=False, int x_cyclic=False, int y_cyclic=False, int out_of_range_nan=False, bint center_jacobian=False, bint despiked_jacobian=False, diff --git a/reproject/adaptive/tests/test_core.py b/reproject/adaptive/tests/test_core.py index 42c83edf9..c4f71b0ed 100644 --- a/reproject/adaptive/tests/test_core.py +++ b/reproject/adaptive/tests/test_core.py @@ -1006,3 +1006,16 @@ def test_adaptive_input_output_types( assert_allclose(output_ref, output_test) assert_allclose(footprint_ref, footprint_test) + + +def test_readonly_array(): + wcs1 = WCS(naxis=2) + wcs2 = WCS(naxis=2) + wcs2.wcs.crpix = [2, 2] + + array = np.random.random((128, 128)) + array.flags.writeable = False + + # This should work + reproject_adaptive((array, wcs1), wcs2, shape_out=(128, 128)) + From f264e23e46e90e5dfeabdc63d45dc58173d5a4e4 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Wed, 26 Jun 2024 15:18:40 +0100 Subject: [PATCH 317/366] Fix typo --- reproject/common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reproject/common.py b/reproject/common.py index 74d7fc8a5..e9de813ac 100644 --- a/reproject/common.py +++ b/reproject/common.py @@ -294,7 +294,7 @@ def reproject_single_block(a, array_or_path, block_info=None): array_out_dask = da.empty(shape_out) array_out_dask = array_out_dask.rechunk(block_size_limit=64 * 1024**2, **rechunk_kwargs) - logger.info(f"Setting out output dask array with map_blocks") + logger.info(f"Setting up output dask array with map_blocks") result = da.map_blocks( reproject_single_block, From 2ab5757648b2f235d675b92540b81b45334d0e5e Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Wed, 26 Jun 2024 15:19:11 +0100 Subject: [PATCH 318/366] Removed unused import --- reproject/utils.py | 1 - 1 file changed, 1 deletion(-) diff --git a/reproject/utils.py b/reproject/utils.py index 4d2a609e9..b5428857d 100644 --- a/reproject/utils.py +++ b/reproject/utils.py @@ -1,4 +1,3 @@ -import logging import os import tempfile import uuid From 29e9ba06f3d3104023e2a90773dd89731faf247d Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 15 Jul 2024 16:27:52 +0000 Subject: [PATCH 319/366] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.4.9 → v0.5.2](https://github.com/astral-sh/ruff-pre-commit/compare/v0.4.9...v0.5.2) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 8cef42c0f..f6a5be25b 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -77,7 +77,7 @@ repos: - tomli - repo: https://github.com/astral-sh/ruff-pre-commit - rev: "v0.4.9" + rev: "v0.5.2" hooks: - id: ruff args: ["--fix", "--show-fixes"] From 0ddf569da53c22ea6e34dbb68b40c9d3f5678ed2 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 22 Jul 2024 16:28:00 +0000 Subject: [PATCH 320/366] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.5.2 → v0.5.4](https://github.com/astral-sh/ruff-pre-commit/compare/v0.5.2...v0.5.4) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index f6a5be25b..7bba8d758 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -77,7 +77,7 @@ repos: - tomli - repo: https://github.com/astral-sh/ruff-pre-commit - rev: "v0.5.2" + rev: "v0.5.4" hooks: - id: ruff args: ["--fix", "--show-fixes"] From b4688d491a1d7af8d2b60b45ca7e5c73e6457bc2 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 22 Jul 2024 16:28:25 +0000 Subject: [PATCH 321/366] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- reproject/adaptive/tests/test_core.py | 1 - 1 file changed, 1 deletion(-) diff --git a/reproject/adaptive/tests/test_core.py b/reproject/adaptive/tests/test_core.py index c4f71b0ed..162d908d5 100644 --- a/reproject/adaptive/tests/test_core.py +++ b/reproject/adaptive/tests/test_core.py @@ -1018,4 +1018,3 @@ def test_readonly_array(): # This should work reproject_adaptive((array, wcs1), wcs2, shape_out=(128, 128)) - From ffbb7961c98f1729092a5ea6f8876078c82d1753 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Tue, 23 Jul 2024 14:42:40 +0100 Subject: [PATCH 322/366] Remove new keywords from output header for comparison with reference files --- reproject/adaptive/tests/test_core.py | 7 ++++++- reproject/interpolation/tests/test_core.py | 6 ++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/reproject/adaptive/tests/test_core.py b/reproject/adaptive/tests/test_core.py index c4f71b0ed..97750eb7e 100644 --- a/reproject/adaptive/tests/test_core.py +++ b/reproject/adaptive/tests/test_core.py @@ -816,6 +816,12 @@ def test_reproject_adaptive_roundtrip(file_format): header_out["DATE-OBS"] = header_out["DATE-OBS"].replace("T", " ") + # With sunpy 6.0.0 and later, additional keyword arguments are written out + # so we remove these as they are not important for the comparison with the + # reference files. + header_out.pop('DATE-AVG', None) + header_out.pop('MJD-AVG', None) + return array_footprint_to_hdulist(output, footprint, header_out) @@ -1018,4 +1024,3 @@ def test_readonly_array(): # This should work reproject_adaptive((array, wcs1), wcs2, shape_out=(128, 128)) - diff --git a/reproject/interpolation/tests/test_core.py b/reproject/interpolation/tests/test_core.py index 512332a1b..87407c2a3 100644 --- a/reproject/interpolation/tests/test_core.py +++ b/reproject/interpolation/tests/test_core.py @@ -569,6 +569,12 @@ def test_reproject_roundtrip(file_format): header_out["DATE-OBS"] = header_out["DATE-OBS"].replace("T", " ") + # With sunpy 6.0.0 and later, additional keyword arguments are written out + # so we remove these as they are not important for the comparison with the + # reference files. + header_out.pop('DATE-AVG', None) + header_out.pop('MJD-AVG', None) + return array_footprint_to_hdulist(output, footprint, header_out) From 14e848e4721de09b602f01da39d3a064c918c5ff Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Tue, 23 Jul 2024 15:09:55 +0100 Subject: [PATCH 323/366] Fixed another test and updated reference map for test_coadd_solar_map --- reproject/adaptive/tests/test_core.py | 6 ++++++ .../tests/reference/test_coadd_solar_map.fits | Bin 264960 -> 264960 bytes reproject/mosaicking/tests/test_coadd.py | 7 +++---- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/reproject/adaptive/tests/test_core.py b/reproject/adaptive/tests/test_core.py index 97750eb7e..7c5bbb8c5 100644 --- a/reproject/adaptive/tests/test_core.py +++ b/reproject/adaptive/tests/test_core.py @@ -843,6 +843,12 @@ def test_reproject_adaptive_uncentered_jacobian(): header_out["DATE-OBS"] = header_out["DATE-OBS"].replace("T", " ") + # With sunpy 6.0.0 and later, additional keyword arguments are written out + # so we remove these as they are not important for the comparison with the + # reference files. + header_out.pop('DATE-AVG', None) + header_out.pop('MJD-AVG', None) + return array_footprint_to_hdulist(output, footprint, header_out) diff --git a/reproject/mosaicking/tests/reference/test_coadd_solar_map.fits b/reproject/mosaicking/tests/reference/test_coadd_solar_map.fits index 7d8747d2672f6b851003fa423109a20b6043b216..70fb6612f4241809ba654e209a6b117e0b6d19ff 100644 GIT binary patch literal 264960 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

8_o^pEH6bM&VrJ*rv29Q1+H zLhFb)kM-{j-W`Ix{*jGpH+`(ocTtgIT`vg?9w&zO<}VP${nwy`WCwIJgKP%|&Nt2+ zG%*!HJ(}m?ZXZ_?`kf0-Hte>AAiB{(!C+npB+ITG!{7g)T{DtdTK(vA)@D|8&f`39 z@9S5Z`2ROm@lZ7TASh$MUvs?~uy5zB1YA$T9Nt)9>sl3{x9do5edZ3#U0*zTo;(D4 zX10J?5$XeP+qa0;-ov?Micf{d3i{)8e@i`h2J{6{^4)b0@Ep1NAaL*~P+wc>9Qri^ zRG;RO_dLk0`ZGUgm&SqmMWTy+(qWtNW* z2b#41;m9@4K<9)#n!&}Wud_YdL$Ob0ytz%aXas7Emi%@XOQ5AH^v~s|LBMZ=6rtn` zKo@LPNP3KW4RaS?wM8BQioCmsfZTQb{gg{x+_4e!2ERk2_S1kABR1kU_z5Tzm1()A z2QeSQy{gv{=UlJ0rJB7~!2Cr!U6X)5zA4MY{iwr{tSQN46Z99<+P2vUV!zG5diouk zA1JLl;xVD?v7XF37JSD&3VYpmA84C%MsH2FKZ;`t)0;EseWeccpQk!a3 zZz)xxpEyTcuvP^~%-46mXQTfQ*2sNx2t~fz@ij?DjDX@erF-wlVW0@9NZmgd4@72k z=0@afdp)1xS&w-$x8G`c+qNS&v2E*7)HX^ zyW8;h*jDr-yYM-C)`0sY`$Kli4DRdjILJFS2OgokS*3G-z?rKw|10eXIEwLv8B%D- zi4RIn_d0DUPs=DqtcykNMgCFoe^tmC6{KjL$Nf(V)7mVJb9gUUC6Xl;C`h{Pe1~%V2jeDX;6s1-P`q z%MgzHHR{p*;QY*{REnUMY;Mduw)b^nHO%GRRnkq{*r zQC4vykyRp;oxR7gIUMKMdv7vIDG@0&gj6JCJsL(vkz^!<)bIZN)vM>J)N#(|dw;I` zy51M+&Oc9FKa6=$I!&skyrbYlzrKT0GYSa1RVU?S@cSLAv~I|OIZ*yd31)g#2yd;T z3+3zrl1Fesd#yJ1jKs(58vlgQb(ZV^PcEE?gyCNt?#Y>&Jt7_!LBMGTW$oYS`&pEx zsSqAUKhUCSV;4Q%r#S{^Gmz(2)ykof!~l^$hA&h4jUZ<5W7rFGO~TjL~~Yv(*X|*&UTEx6rRKb+gvLnFq*f*Es$x zbVCGh?B9MjKitQizHS+Xy!n$xo7;?)5FRNxs79~@(v|nac84<|>~Dts(B1o}bDS)? zzK(k}ty(jk1y}S@=*f-7Z9>>{4?Py#OOh1rKfU%+g$TtWjipx1WrjTqc&b!^oTv|L zh4H39(ka#vrOCj#T#(d~_73-b=VX&!M?mnPLVKjyBI-n2FSgD4AZ%~+MH|TuAby_P zk^Sl=g#0n)=syz$VW;`TESLEq?BLSP!+`pLu+;A@<&V1|e<@o!ef^5E#8{E?jsNh_>0I%`BK> ziIy_I6omV(0!jCa_66Xh)@*uo%?bDXFBUhn??6C=o1{J&=kl3(kJb@9pF%9!czkh> z}%zR&R-0YATe78)^lUy#!Ye7FPu9VzMs#m~`qJVKLadP@_0esEDe*`o&@=5y7y zwD-_2_dG4aFA}`pS-&wYb_ajgLaC$saX;AP@uGfV9$Xo62v%k#;2dGuy)W!AxbRk) zxTdp$)7|wIlsmDv095!>{J! zy^Pxr>`nA6w8(K_OEM^yp2!5-oG+pKH#orQ^pkN8-d^yI<+Ty~f;s5B(R1wB(@pT3 zU)iR{o`n)V|DH=U;BCcP#Lj#i+?k2D9ym*c|X; zKK%9pejW*bx$KzbkdK%3`u-zN)ZsrpPt*`aAN%ene=B>CZzr34Ac7Wq3%8FA1)o;{ z=hrhHO&T@e{L19KJmyv1Ng1n{_ySkC9JZ07PvCK~*eKvC_WxzM$~`=UbNehym{lC! zBbL>o*iIqO`tg;Ej5PuZ6=i>zoARu$ouZO$vb^;UA#wgyX8IRNZ4jR^(kP_nUvDK zX4EmND7LBoM@%T}%}41CK~AE#r#r>}>dOWF$h-7{`?uph+trT5!Z(M;UaducHC zjx9h)-Xj;k)B7RhY0lKs67;t|*OzQKu!eJy&i>XeRR|LkasKu6IfU7Ju|}vbBTv{u zTYse-h)TpQsaBk$e!f3X|FHx}uNgm?k9y#o%6Uq4ZW5x6B-txH&p~W?6C?eTO^A=U zoBlEJA|wn~WwDF(Kw`Q~?xs@`q{Zj=KR^yxPHdS-Crv-(RIKhCp^k?fD>d$7A92WT zF!DZdzYMZjGB^Hve*v;QPT3DH(m;B0z3#3ZQ;>XcVC2RJ7l?Lv+H$2GIY><2FXhV3 zfkKkGG>N$+iX8pzm!02$Qpip#bp8iW20QCbc}#$E0qEX-#`(Wb&U;cV8KQ-|mwgI% z;d3Sa%Q)RZh?rK-czh!nh=QWyUh}&kY&*%YuKNyhIq0JqR1X40N+XxRkACQd+?K>M z$d$X!_UjRRg>dQ4`$r$_08*A5Luf${MC>uWDcm#!VIAW{F~%LprC@rOt2zkbd{Zy= z8t{Ao0}%AXx%k9rjsWLBATA^-H)C!jv^AINN^LoWtcQ91dI;!$plv!Ut%ALZ_hqhW zNdocPMlCsa62jbd1wR@w0MS?Gi042ggf=E$xG>)Y!6^ZpRzp+}r1JW}oFDpODYVuP zGh&hZczU$B9`)&622bbS9Dy(g&kqG}8u9CjlJi4(kOP{e)W{(Tr0%RxUci#hSvyp*-x2?+kPbl}HV+)LKY z4`1DjIy~c-Q{PVsK)`OjHi_Ms;iwg)He2M$R z=-Q~GFb081J_Sxc%W$vR)$b&ZJ^`s00(u;{AEgg%r!~X-M91Ri{fCjWQTmNOEL9Ty zBH1m;ywngxD(j55zEf4p*ImK0d(Zfx9`qF?ln5x?#r~^v`(L&j*MrL`N9!DYBXDv$ z_rRHY9s5D-)k27Cs2A%83m|9S)lzu(1)C1M#}_|tVnRLO=U=9C704foY9I;SUxom$ zpt##vn2)s9;}#6Q4SplM>4I*~Kp4IL?+wmReiQ45rL6tIYwHu6=j~t5zqe36R{TvGdq zTAawk`;!r8JXHc7QR&_R47uR%nOXUTc>wbV$sIQxDuIxn+xz+j{=6}+uQNgu0>vwT z>WSlCrc$fdqK^#_3y@wCxd=i3x>}^Vk!vtf^WyP;Ef5<1*(|4t9*E}}ZkKT2UWyoB z>?dahVUMY#5=8BR4HQY9m8Pd;jEbE8fF1CdQ<%{xydz>tqp6hBe64PWhlI-T@gB zjcawOWsuIL#YK1=3~Bo)jh2&-Aoapx<39V*p<F5~=W#C_y3`rbe)%d=1j z!Y1~;zW4w8#P*%B#4S9J`Qvg1>@YV+Wnmd@rHZ*?!h7|MbO@)+@cLy-1NkT}*q^%r zrM}El>F_5Y$N9JKt8N3b=Yg1=COPQ4bG|)hjk!dzp#y>TW5RPSUq_CZekbbdFxuP#`RgC0N3#6!f zUtfx9;5oj$wwq@R`F-kRM}9}+`;IrJOzGzTeIgzY51_8z=htwqsu03{@h`^xeTI8e zk>;JqHw#o&)}s|j#9Z9LU1xT>V@|6}mRbboO|^ZICb-uRXuNT~Q1t+i)F~^y#p)0? zqIP5NstH6$UXF@u#ra<|U!dVLz7HzRO;6WMV7}+k=^eM&z|UqRhVK*RZ@bkKiFd_u z@BUEOWB~hFy=X2)nri?ddx4ZBf^+;1e;VTXMDR0te+7Cm*KzZ*QZU;pgjgI9Ldg_A zhr7;^tI_`}R&*f9s}X#2KhiZE55k-;Ti~bn_;r@Z{clercPL5UR8*}R0`$E4$0Gna zv4T@^-7kr}bQ;ta@Z^UQ$JaY__zn+x@uc6B2NB8Q`S8GVxf9NK7ioN=gTcU)0RU3O1mV3ZOOJuvF z1$`0`hwj)%tAP8<{&UcQ{0X-3S2wKjIT9ApK#0S=YrTwCCm{p8WXitWcgAyeos;_- z!xbQ0VWDQ=@&kgKlo<`?WBi`ozUzZ~GJ=gyifk3`2WEuwRPP7l`TFjPev1Qm-8-^- z<{0j;GTAm|Uf%*=Q^~-xtH_B`mv-Es9Rh;=yKla4&!7)k`LI0$0sT34t+HP!=nE|U zT+Tm^zd!KU!${=0Iz3^mYQ6zpYSevF1S9ZH{XsXE^Z;DJJSk4B2cMzs z!4KklfDm*l#NzlWc>dGy*iC9fo*p@+lMY{>__;fK8?S@gilJES;zRKGxBm2$9l0AHnM4XbNOI8RU5=cUtN9}gY3G5Rx{EzR`FIXkddDz4DsoH#hk zp|wJy4Sek!-c(uP>rl01ZF^}R2>;Ih+rRrD__oO=Ej3`?^8rt&4C5B~36$@v|BJa^ z2A`|$Bs`!0o6F2V-7w%$ufXN<$W z5b&Hia|`!g3m*JOFIfO_#O~o;6XXp~uhASOodt?CGpCMR2gE9gy2oo=fyDChS@YaW zkh*u+l{s{+U3rJ2c-S2`-f_K^9 z`;3sDqBphuUk#*^JCr9huR#*$d0DmZzaTL=him&C=DXv6z1vy-2I4s{?{$<&fOz_Q zx9M$qAU>^L%05;P;srj8uB$jh)b{kN-1udjdxukxXd~Ze{~hYm+ktovej1(}QNtWF zV|AO}GYD#;oi&iA0-|cX{+MVV_F+v+OEDv-Ly*A!^*ZYAiK1qf0oZ$DuxqaRx-5k6 z`SRoKWGj%p!goCoz@FBYOJ3q<=^;W+v5WE9ZRFNZB@Lz00Xfs;tnB=8pg6pFyRBq~ zI!3MHuOXZ>;~$M~@p$9s_axuIL8!_PGkYoJ-^EN&)t5OaSdU!4<|1J6&FP;xC-Gy_hftr+i=6CdCl=CvX zX+WT}IqTX8=CwbZI4njk#plD?4{GrqAfM#o(hq$B!6venTtv(vx}N<&m5u)Pw*!=R z?8OP!FTOTGTZ?=Q4TTYs9uSktc|KGRL2%+@n_i}y5Iio}JWHDZq0Vu>>JnxUUV8eA zOb(u}yYoUeju3D-4eup6!Q51d^nyhP9HjA>urv;MDtAi0zN*iq5zIk&=p@Dqgh^8Sh!2_<{!FFQ{Em zdNBeN4&GzAEI+_M{=C-gs3q!EK^cb$Lbz{UewW&gKANNt_0?rP5GZhQuEu)+e1*T% z8gb#==+AsxssedFfdh9qiw}XbYs`+z&qcxI^LF3cr5tb-p>=%KZ&ROVev~JAbGvX7+~iG4{gm{< zvxvo4mmYbkOvjZEn&STUmAu5UiaKzyt!hzxD-8a|x5q`aP=8OpqTkiK0^ZZ1e4lE# zz;#yp2Vt!QT%(_81)WJj9q3*DP}W)SE=VHn+=YFHPj=q$TSq<|d9%4*_6K-V%_KYT z>i~y0bMXalB~af;trWd`6@2^NK8TFKd!ajVncE0IUwte=p_$UycQ@<$BpSJ1)9%zf z9X_bTr5G&^TA;rp=3kcECFH3-V`#jC``)bw0%oV%aUaUJX}S}+RY_t^Y&^(EKlifh zTA(S;58W|$budR>`bnlO^$`SG6Lv}?FFuU@me{?WlGsakEiH&Y45IZ;-QE~I4GEcd zbV^q?A?@^s66W`|kV!p@{AV&`vxjkrT2w<$AhSxU!ZPFv`-$wI(}A3xm#Lr2OdvCd zil_6+Q^>dzdaF7{1k#(<0|TsgLE3+7e+^J?O}+1#Afy!v$()iY)o$7lH+pfcBrzJI z-!azCti6I5TUlya-3o~LD><`}>kctj{RV&4?Skl2flt%~dNAkBEKcyK1+o-d8Nmtt z(45PAZ~Zd>k`y_0=Vt*3IrV0c%AN+oa$DiwU0?k5r?RU~y#tCsjYFx*7YM(VsdSNr z35Xrvxn4%y)YDKWW=%oB_LqD7^4am>C&2%C33D>cxoOB4Ok@zaBiQKa+_6qL#LJ0NR zezavSi|2YpqtXNP4cgyn(x|_Sy84BozoY&Tmeaw@c)t62aR=PDlB|^8AN+^hz&{`M)vEG9#9Tt}XL{uR zpJJk0@ooX~AaCAk6c_TK-WSY^Q-ObY$OQrMTnN3~Wjnk_AA(OPyvyQl#&dRV+V|f$ z`p=Ed3yBK>$^V3}x`;9Uy~gEsZX;hV{PnAsukdyH9!5Lx^FHd=OEDC=eah| zKjad%@cwZ`-(U5a+E!UX%vXE)8F^d*|H}isXX}uIR%W|3Zaj|qE6rD%m>Apx61X3y z;JsL`V{X0=b%&q9A1}%)LSPxF8R+7^U8Lyh^59O0R1$NoPX7wgo}H zS{sn|xjkOjR)-J<)^~O_n7cc^uhg$o0Rnif-32MA-;ZXu47C?POoN#H>+odAHi#0| zHVA+;osrc(Cpw7a>d$&&SPT*7rtS$B4nySpbS=kjHAvAs$Y5`Tx!meu@2HGYAivW* zs6bnRdRJ$U>v{nAoYg;GA~6r{7Y?wgs4j!6cg@gqb`SJxz&c}Y3Ua9Vy@w@HhtOco zIFap)KE|q^L+p6Yp3?~7p}PzL>pv?#IUR+dk_e(69dcV#8AMr$`@p~TC0zyu`EQHD z9R;Uxo=Qhzo_g&?a!r+ zHh&d)wVoyR@%r`2n9s~sf2OPpZsUILRMo=B9pyNz{SJE<6KqxgA)nP=spxiXh%q?+ z2&v>Jq8{uuOnsM63!M48B9BO%1NVEgEJ51y;FiMPdN~*SXVV{XSnR<5Monv0wsJvm z_*ph2Pr+QHV$i+oTioEI6YgKZZUU|z+{2$4b-*Q=!7y$=8}?7`e3Wwoxr1Mp^rk5n z!B5oFz)q_ce4eK?&Xpn$O;7&Y5U(gCOU9{e->-wro$zjd-uks z1ccp|{N}UNho8$H>3`2TA&BeEqwElzud;Uu+Kfn|zl=uj!ciWe6c}l$#f~Arf-qQS z{u?Nd2OM{k@!rh5mF>Lu63#`9+tM9>fF#6fYJ0aCA}qK3XCzA?GVrX5;n_V9n^W+B zqv4 zm!{bYiDZ=?l?)z8s%aQkm?T1Sf0B}?@ij>HJMWTLfxZt*FFm=PdFTU4q&|2{9O6#P z-_k7;hgh$v|85JdL5xr4R~eT?h;Gvl?>nrBKVDVX{1*h#tCEHZAQFiP_y@%F z(b{~TF9ynwktI8}4v1)14VaZUftJk;Bp$J^P&xWb{(XPB z{n84E)_U$Yoyu-%T5WnxA^a) z@7zHM{kpmHogMO_SlQT&#g0I5)A+Ch+bD#ZH8`C<^#XGxyK{looYynsRkJ?4IPU*pzE+W`?d3oP0#{pjz0 zMQE^E!MqQz5M8_|1Qq^lT9H7Vv&ns?%pU$85PNTjz5^$fgggM66 zL9}MW(s9qT=*M?Rd^&Up^$4zJ|2)iJv~kbej>o(=6^p6wCh9VK13Z>ukS9JzdqxOL ztP^MlX4}-LAhL)z^Fh{SAn)pa>us0@2|3E`U&g;f=F6^d9wI=Y<=dKKJv&JGdCO6N z5p}1R2iPoLj6?9-`?Qm41K=MbJ3v*p5B(NnMou~C*ZZUqnRpEMmE7$Mx-7{3vB=U0 z)*yh_K+r*((!)T|111G^%s;CX^9(#@2Y*^k^R)x$Q({)T>!J1p^FL#A?ShHmBNER= z^Aht!a{tv&&#{2NMy=K6Js0rKYl*!3nGc-aw1=#04}wQMXPK~+Hw3QH#UI;1e_1S3 z=K3(^_iCk#EH1}@jqNSwSM6G0Pb%5`H{%1&v;TcxFuw~PUxGVn`yYdQ)5j<#K!3+^ z)8<1bu&;Nt&PM+T0o*EGt_1&|k8-}SPO%AfADLaVlbzcg;qVGce@pJfsGVv$Ex6>L)=Sd=Th(tsKK z14n0ae1D@~rO-{kQXBgOx}WrqR->=g&*txzDx^j5oOr7tjnBvTI}*KpAnN%`&0S_K zKsAW2?Y-y`-zkYZm$Q^Yd*aJPgzG!6p!l=4dC4iTUnD=vM@LjzF@kkbZ8+r1vQ!A$+X<;|ttJk>KE9aJa{THGH zwdStl^OJJ-z^(Q0?+{^v@|KnsMBJ5eznE45q?oQLReEg*&wjrD5|0m%Vv~bned%%k z=Hl-*WeMai!$qY7+#s8GYsYYYd^Ua&MgNd4^n0Vx09D>b2qppzuKse5&j{ruDCaA348h zSUw26&bknYHOPg_kB%zR(FDTa_dF`>z4W>{qTZ%82!vfs#(8mL;A-{GE0JFfoWy?@ zzP{lN9=Z26_+Oj^H~SMN&iZuV{$AFgH~AiN1!Vg*FE2t`>jMd4t`bO|VT@|nbssrj zvL6oKMP5c~+v&|lu@1H=RL+i`Y)->!RRuX93zXk-}6z3P;93h^Q?{!Uz~0mU_n1*sNJekZ$AX&tus)ym4ml0t;79+ zOz`+@b3)4)`Jj$g8}Agbn zkh}tbJ-teqs-#4f<;rWW;da z(&buRFN3h?mA z&&e=pj$sH(PE6GIL@wEF&yn96eAw^eUhvRl2L#wKhZ6IdAzWU)_+*GAgoV8}=$b}< z+@|P+49gaT^@{`y%dbF$*HM?2us;x4_DW$B_ma`V1V^r+_YkG6v{XuU2PoeBieK+y z52>`013?G*&ex_Nl3XkyX3qHi@xUcWus9<0{Vg}~7zdcfOQsl%vQ#G~3pKJ)1(wsPVKMY7;IoRAS;vmAk z>}zB{2cFaHw@;?NLVvK(Vu^DcLU% zBpKer9M+f*NwdzoR*!k>sn|nJXT%^l^>e?8$0h_mi@8YG(1Fis{{!C*6d-uB^l2+U z_OrMT`*Ww`^CfccT^@&ZAg38}y*5VQU$n4lg259AKP0fuUw}&vwHwCt8;2qER4w;E z+y{g5BHvPMx95Nhl%|&@&?5}}*CStm#{2nMC zses#4><=y=LYMZQC$bGdEw4w2jt+j&c=w_^MP*^=XTD8W%N<7Og~=JfnZK$*78jY zAPvteT)m5Xj`M3&4+`=AVYsdE@lrSLU7dVZr2T=yW-~5uVg}+hB2J%;mx9zA-%h@u z7(j|NecvgKDfC|{^%19jL(D^p-Yx8{pg3LAUAl-quDxej*ltBZ0Jn!#gSk5pGU8N) zWHrEtO1shY?``nO(-$zAL>|(-@cGntA0UYR{tRR84+wp+xYmt5Vc|wkua9fU13`6L zg6@z6c+F=1?mD%II=GZ;p+y5Y|NY(`b&naGJR5lom5JbTye*_Ap&q>W!fNmLd0@Ws zaGRaTapb=_nlrENz`3TPRA)B@2;Ua>xnB5yJ`Sf;kL&33AZkXqsN&ye@2)KkE%b$- zKgW1z6n*KmGTMnBke{`FtdB@q!JZpx#z#Mx!CO9Ta~AiHK|2%$(+%kdi~?Ql*w$5gF9itoQ9^W!e5Lg**TFB-@Fy8p@x1?5+xKzJ)VSNql<=QnrpBzrNS zL=Dj2TzLm!Mb9lh&RF61MYl#vgbjj1X(W&G%VF;7n&4YbB67&=mkU-dqVGzQi*OC^ zHO+I99S*1`W`)M`Ea5)rm8{l9acA^rln<%8V4nIvp^Gc4bC8*z`6alF9&(aQ_7|d$ zA*)Jo`^BRz$Yi21ZMW-$^s+|tdX_avd-w8!A+I{5eVBJjW_Si^Ump@&D_0Hv@JkZ)$CTI9nl=0F{O<*vC| z{0W4pmOZM2HeVpRfnP4R7kjQE2VOp^i-G9lPKOLa+;L90N+{)y07^}y@%iHl5NVtB zZXynKK7y6zl@niqeCOJS6eHA~+tSzN_-Pj$HEP9p4{n9@kNg671grF?N<=^>5xZ#>U$t^ zy`D3;Q4JB}U&Nm(4?sk7=gCjH`yuS;hSryK%wIIqEC>CPfIzosmQLg-1j|cwP^sen z+K#rwWH}Q8NRAy*6M&o&Ja0m#&Yb>aIfA;i+}`JT84$EmvT*40V+ejqISGv5_^T5SevY>;o4*!N#R^%w--{`|7` zzc�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

j6UVOsab) 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

XzK3@cGkmv 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 SsYP4EgWI`fEf-6FVa(GrRx#d073A=iwO~9OmU0 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

#7I&pR_IALkiu! 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 z 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

?jRc++47g@yW~=1Q9h?s6#l%xXA_8*2PjEEQ2w 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

cvRxm-%18g5Bk~0a9i%s+bQxl^ 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 NLZ~!VPUfhT{ttwg6K? Date: Tue, 23 Jul 2024 15:12:23 +0100 Subject: [PATCH 324/366] Fix code style --- reproject/adaptive/tests/test_core.py | 8 ++++---- reproject/interpolation/tests/test_core.py | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/reproject/adaptive/tests/test_core.py b/reproject/adaptive/tests/test_core.py index 7c5bbb8c5..e11cd0e9b 100644 --- a/reproject/adaptive/tests/test_core.py +++ b/reproject/adaptive/tests/test_core.py @@ -819,8 +819,8 @@ def test_reproject_adaptive_roundtrip(file_format): # With sunpy 6.0.0 and later, additional keyword arguments are written out # so we remove these as they are not important for the comparison with the # reference files. - header_out.pop('DATE-AVG', None) - header_out.pop('MJD-AVG', None) + header_out.pop("DATE-AVG", None) + header_out.pop("MJD-AVG", None) return array_footprint_to_hdulist(output, footprint, header_out) @@ -846,8 +846,8 @@ def test_reproject_adaptive_uncentered_jacobian(): # With sunpy 6.0.0 and later, additional keyword arguments are written out # so we remove these as they are not important for the comparison with the # reference files. - header_out.pop('DATE-AVG', None) - header_out.pop('MJD-AVG', None) + header_out.pop("DATE-AVG", None) + header_out.pop("MJD-AVG", None) return array_footprint_to_hdulist(output, footprint, header_out) diff --git a/reproject/interpolation/tests/test_core.py b/reproject/interpolation/tests/test_core.py index 87407c2a3..4df8a3920 100644 --- a/reproject/interpolation/tests/test_core.py +++ b/reproject/interpolation/tests/test_core.py @@ -572,8 +572,8 @@ def test_reproject_roundtrip(file_format): # With sunpy 6.0.0 and later, additional keyword arguments are written out # so we remove these as they are not important for the comparison with the # reference files. - header_out.pop('DATE-AVG', None) - header_out.pop('MJD-AVG', None) + header_out.pop("DATE-AVG", None) + header_out.pop("MJD-AVG", None) return array_footprint_to_hdulist(output, footprint, header_out) From 6ae3c0e5d97e5e48c4356155caa74b1ec953d6b7 Mon Sep 17 00:00:00 2001 From: Stuart Mumford Date: Mon, 15 Apr 2024 14:45:06 +0100 Subject: [PATCH 325/366] Add a script to regenerate the aia asdf file --- reproject/tests/data/generate_asdf.py | 115 ++++++++++++++++++++++++++ 1 file changed, 115 insertions(+) create mode 100644 reproject/tests/data/generate_asdf.py diff --git a/reproject/tests/data/generate_asdf.py b/reproject/tests/data/generate_asdf.py new file mode 100644 index 000000000..d577aa27f --- /dev/null +++ b/reproject/tests/data/generate_asdf.py @@ -0,0 +1,115 @@ +from collections.abc import Iterable + +import astropy.modeling.models as m +import astropy.units as u +from astropy.modeling import CompoundModel, Model +from numpy.typing import ArrayLike + + +def generate_celestial_transform( + crpix: Iterable[float] | u.Quantity, + cdelt: Iterable[float] | u.Quantity, + pc: ArrayLike | u.Quantity, + crval: Iterable[float] | u.Quantity, + lon_pole: float | u.Quantity = None, + projection: Model = None, +) -> CompoundModel: + """ + Create a simple celestial transform from FITS like parameters. + + Supports unitful or unitless parameters, but if any parameters have units + all must have units, if parameters are unitless they are assumed to be in + degrees. + + Parameters + ---------- + crpix + The reference pixel (a length two array). + crval + The world coordinate at the reference pixel (a length two array). + cdelt + The sample interval along the pixel axis + pc + The rotation matrix for the affine transform. If specifying parameters + with units this should have celestial (``u.deg``) units. + lon_pole + The longitude of the celestial pole, defaults to 180 degrees. + projection + The map projection to use, defaults to ``TAN``. + + Notes + ----- + + This function has not been tested with more complex projections. Ensure + that your lon_pole is correct for your projection. + """ + projection = projection if projection is None else m.Pix2Sky_TAN() + spatial_unit = None + if hasattr(crval[0], "unit"): + spatial_unit = crval[0].unit + # TODO: Note this assumption is only valid for certain projections. + if lon_pole is None: + lon_pole = 180 + if spatial_unit is not None: + # Lon pole should always have the units of degrees + lon_pole = u.Quantity(lon_pole, unit=u.deg) + + # Make translation unitful if all parameters have units + translation = (0, 0) + if spatial_unit is not None: + translation *= pc.unit + # If we have units then we need to convert all things to Quantity + # as they might be Parameter classes + crpix = u.Quantity(crpix) + cdelt = u.Quantity(cdelt) + crval = u.Quantity(crval) + lon_pole = u.Quantity(lon_pole) + pc = u.Quantity(pc) + + shift = m.Shift(-crpix[0]) & m.Shift(-crpix[1]) + scale = m.Multiply(cdelt[0]) & m.Multiply(cdelt[1]) + rot = m.AffineTransformation2D(pc, translation=translation) + skyrot = m.RotateNative2Celestial(crval[0], crval[1], lon_pole) + return shift | rot | scale | projection | skyrot + + +def generate_asdf(input_file="aia_171_level1.fits", output_file="aia_171_level1.asdf"): + # Put imports for optional or not dependencies here + import asdf + import sunpy.map + + aia_map = sunpy.map.Map(input_file) + + spatial_unit = aia_map.spatial_units[0] + transform = generate_celestial_transform( + crpix=aia_map.reference_pixel, + cdelt=aia_map.scale, + pc=aia_map.rotation_matrix * u.pix, + crval=aia_map.wcs.wcs.crval * spatial_unit, + ) + + input_frame = cf.Frame2D() + output_frame = cf.CelestialFrame( + reference_frame=aia_map.coordinate_frame, + unit=(u.arcsec, u.arcsec), + axes_names=("Helioprojective Longitude", "Helioprojective Latitude"), + ) + + aia_gwcs = WCS( + forward_transform=transform, + input_frame=input_frame, + output_frame=output_frame, + ) + + tree = { + "data": aia_map.data, + "meta": dict(aia_map.meta), + "wcs": aia_gwcs, + } + + af = asdf.AsdfFile(tree) + af.write_to(output_file) + + +if __name__ == "__main__": + generate_asdf() From b30676e15af8b3fda8d903d9bf599ba9273f756c Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Tue, 16 Jul 2024 11:16:20 +0100 Subject: [PATCH 326/366] Fixed bugs in generate script --- reproject/tests/data/generate_asdf.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/reproject/tests/data/generate_asdf.py b/reproject/tests/data/generate_asdf.py index d577aa27f..6d83c7bcb 100644 --- a/reproject/tests/data/generate_asdf.py +++ b/reproject/tests/data/generate_asdf.py @@ -1,5 +1,8 @@ from collections.abc import Iterable +import gwcs.coordinate_frames as cf +from gwcs import WCS + import astropy.modeling.models as m import astropy.units as u from astropy.modeling import CompoundModel, Model @@ -43,7 +46,7 @@ def generate_celestial_transform( This function has not been tested with more complex projections. Ensure that your lon_pole is correct for your projection. """ - projection = projection if projection is None else m.Pix2Sky_TAN() + projection = m.Pix2Sky_TAN() if projection is None else None spatial_unit = None if hasattr(crval[0], "unit"): spatial_unit = crval[0].unit From 74c575c3d0500b0466d31764c51cc87390919dbb Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Tue, 23 Jul 2024 14:15:39 +0100 Subject: [PATCH 327/366] Fix unit bug --- reproject/tests/data/generate_asdf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reproject/tests/data/generate_asdf.py b/reproject/tests/data/generate_asdf.py index 6d83c7bcb..15e2659db 100644 --- a/reproject/tests/data/generate_asdf.py +++ b/reproject/tests/data/generate_asdf.py @@ -88,7 +88,7 @@ def generate_asdf(input_file="aia_171_level1.fits", output_file="aia_171_level1. crpix=aia_map.reference_pixel, cdelt=aia_map.scale, pc=aia_map.rotation_matrix * u.pix, - crval=aia_map.wcs.wcs.crval * spatial_unit, + crval=aia_map.wcs.wcs.crval * u.deg, ) input_frame = cf.Frame2D() From bf49b6f316cdddd468f22fbe084a75072158b46c Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Tue, 23 Jul 2024 14:17:18 +0100 Subject: [PATCH 328/366] Added re-generated reference file --- reproject/tests/data/aia_171_level1.asdf | 204 +++++++++++++++-------- 1 file changed, 134 insertions(+), 70 deletions(-) diff --git a/reproject/tests/data/aia_171_level1.asdf b/reproject/tests/data/aia_171_level1.asdf index ea8cfe22a..957d76ce3 100644 --- a/reproject/tests/data/aia_171_level1.asdf +++ b/reproject/tests/data/aia_171_level1.asdf @@ -1,24 +1,40 @@ #ASDF 1.0.0 -#ASDF_STANDARD 1.3.0 +#ASDF_STANDARD 1.5.0 %YAML 1.1 %TAG ! tag:stsci.edu:asdf/ --- !core/asdf-1.1.0 -asdf_library: !core/software-1.0.0 {author: Space Telescope Science Institute, homepage: 'http://github.com/spacetelescope/asdf', - name: asdf, version: 2.3.2} +asdf_library: !core/software-1.0.0 {author: The ASDF Developers, homepage: 'http://github.com/asdf-format/asdf', + name: asdf, version: 3.3.0} history: extensions: - !core/extension_metadata-1.0.0 - extension_class: astropy.io.misc.asdf.extension.AstropyExtension - software: {name: astropy, version: 4.0.dev25573} + extension_class: asdf.extension._manifest.ManifestExtension + extension_uri: asdf://asdf-format.org/core/extensions/core-1.5.0 + manifest_software: !core/software-1.0.0 {name: asdf_standard, version: 1.1.1} + software: !core/software-1.0.0 {name: asdf-astropy, version: 0.6.1} - !core/extension_metadata-1.0.0 - extension_class: gwcs.extension.GWCSExtension - software: {name: gwcs, version: 0.12.dev562} + extension_class: asdf.extension._manifest.ManifestExtension + extension_uri: asdf://asdf-format.org/transform/extensions/transform-1.5.0 + manifest_software: !core/software-1.0.0 {name: asdf_transform_schemas, version: 0.5.0} + software: !core/software-1.0.0 {name: asdf-astropy, version: 0.6.1} - !core/extension_metadata-1.0.0 - extension_class: astropy.io.misc.asdf.extension.AstropyAsdfExtension - software: {name: astropy, version: 4.0.dev25573} + extension_class: asdf.extension._manifest.ManifestExtension + extension_uri: asdf://astropy.org/astropy/extensions/units-1.0.0 + software: !core/software-1.0.0 {name: asdf-astropy, version: 0.6.1} - !core/extension_metadata-1.0.0 - extension_class: asdf.extension.BuiltinExtension - software: {name: asdf, version: 2.3.2} + extension_class: asdf.extension._manifest.ManifestExtension + extension_uri: asdf://asdf-format.org/astronomy/gwcs/extensions/gwcs-1.2.0 + manifest_software: !core/software-1.0.0 {name: asdf_wcs_schemas, version: 0.4.0} + software: !core/software-1.0.0 {name: gwcs, version: 0.21.0} + - !core/extension_metadata-1.0.0 + extension_class: asdf.extension._manifest.ManifestExtension + extension_uri: asdf://sunpy.org/extensions/sunpy-1.1.0 + software: !core/software-1.0.0 {name: sunpy, version: 5.1.5} + - !core/extension_metadata-1.0.0 + extension_class: asdf.extension._manifest.ManifestExtension + extension_uri: asdf://asdf-format.org/astronomy/coordinates/extensions/coordinates-1.1.0 + manifest_software: !core/software-1.0.0 {name: asdf_coordinates_schemas, version: 0.3.0} + software: !core/software-1.0.0 {name: asdf-astropy, version: 0.6.1} data: !core/ndarray-1.0.0 source: 0 datatype: float64 @@ -100,8 +116,9 @@ meta: car_rot: 2106 cdelt1: 19.183648 cdelt2: 19.183648 - comment: 'FITS (Flexible Image Transport System) format is defined in ''Astronomyand - Astrophysics'', volume 376, page 359; bibcode: 2001A&A...376..359H' + comment: 'FITS (Flexible Image Transport System) format is defined in ''Astronomy + + and Astrophysics'', volume 376, page 359; bibcode: 2001A&A...376..359H' crln_obs: 22.814522 crlt_obs: -6.820544 crota2: 0.019413 @@ -216,80 +233,124 @@ meta: waveunit: angstrom x0_mp: 2055.060059 y0_mp: 2042.719971 -wcs: ! +wcs: ! name: '' + pixel_shape: null steps: - - ! - frame: ! + - ! + frame: ! axes_names: [x, y] axes_order: [0, 1] - axes_type: [pixel, pixel] - axis_physical_types: ['custom:pixel', 'custom:pixel'] - name: detector - naxes: 2 + axis_physical_types: ['custom:x', 'custom:y'] + name: Frame2D unit: [!unit/unit-1.0.0 pixel, !unit/unit-1.0.0 pixel] - transform: !transform/compose-1.1.0 + transform: !transform/compose-1.2.0 forward: - - !transform/compose-1.1.0 + - !transform/compose-1.2.0 forward: - - !transform/compose-1.1.0 + - !transform/compose-1.2.0 forward: - - !transform/compose-1.1.0 + - !transform/compose-1.2.0 forward: - - !transform/concatenate-1.1.0 + - !transform/concatenate-1.2.0 forward: - !transform/shift-1.2.0 - offset: !unit/quantity-1.1.0 {unit: !unit/unit-1.0.0 pixel, value: -63.5} + inputs: [x] + offset: !unit/quantity-1.1.0 {datatype: float64, unit: !unit/unit-1.0.0 pixel, + value: -63.5} + outputs: [y] - !transform/shift-1.2.0 - offset: !unit/quantity-1.1.0 {unit: !unit/unit-1.0.0 pixel, value: -63.5} - - !transform/concatenate-1.1.0 - forward: - - !transform/multiplyscale-1.0.0 - factor: !unit/quantity-1.1.0 {unit: !unit/unit-1.0.0 arcsec pixel-1, - value: 19.183648} - - !transform/multiplyscale-1.0.0 - factor: !unit/quantity-1.1.0 {unit: !unit/unit-1.0.0 arcsec pixel-1, - value: 19.183648} - - !transform/affine-1.2.0 - matrix: !unit/quantity-1.1.0 - unit: !unit/unit-1.0.0 arcsec - value: !core/ndarray-1.0.0 - source: 1 - datatype: float64 - byteorder: little - shape: [2, 2] - translation: !unit/quantity-1.1.0 - unit: !unit/unit-1.0.0 arcsec - value: !core/ndarray-1.0.0 - source: 2 - datatype: float64 - byteorder: little - shape: [2] - - !transform/gnomonic-1.1.0 {direction: pix2sky} - - !transform/rotate3d-1.2.0 - phi: !unit/quantity-1.1.0 {unit: !unit/unit-1.0.0 arcsec, value: -4.5321722098160535} - psi: !unit/quantity-1.1.0 {unit: !unit/unit-1.0.0 deg, value: 180.0} - theta: !unit/quantity-1.1.0 {unit: !unit/unit-1.0.0 arcsec, value: 2.865574805180813} - name: spatial - - ! + inputs: [x] + offset: !unit/quantity-1.1.0 {datatype: float64, unit: !unit/unit-1.0.0 pixel, + value: -63.5} + outputs: [y] + inputs: [x0, x1] + outputs: [y0, y1] + - !transform/affine-1.3.0 + inputs: [x, y] + matrix: !unit/quantity-1.1.0 + unit: !unit/unit-1.0.0 pixel + value: !core/ndarray-1.0.0 + source: 1 + datatype: float64 + byteorder: little + shape: [2, 2] + outputs: [x, y] + translation: !unit/quantity-1.1.0 + unit: !unit/unit-1.0.0 pixel + value: !core/ndarray-1.0.0 + source: 2 + datatype: float64 + byteorder: little + shape: [2] + inputs: [x0, x1] + outputs: [x, y] + - !transform/concatenate-1.2.0 + forward: + - !transform/multiplyscale-1.0.0 + factor: !unit/quantity-1.1.0 {datatype: float64, unit: !unit/unit-1.0.0 arcsec.pixel**-1, + value: 19.183648} + inputs: [x] + outputs: [y] + - !transform/multiplyscale-1.0.0 + factor: !unit/quantity-1.1.0 {datatype: float64, unit: !unit/unit-1.0.0 arcsec.pixel**-1, + value: 19.183648} + inputs: [x] + outputs: [y] + inputs: [x0, x1] + outputs: [y0, y1] + inputs: [x0, x1] + outputs: [y0, y1] + - !transform/gnomonic-1.2.0 + direction: pix2sky + inputs: [x, y] + outputs: [phi, theta] + inputs: [x0, x1] + outputs: [phi, theta] + - !transform/rotate3d-1.3.0 + direction: native2celestial + inputs: [phi_N, theta_N] + outputs: [alpha_C, delta_C] + phi: !unit/quantity-1.1.0 {datatype: float64, unit: !unit/unit-1.0.0 deg, + value: -0.0012589367249586} + psi: !unit/quantity-1.1.0 {datatype: float64, unit: !unit/unit-1.0.0 deg, + value: 180.0} + theta: !unit/quantity-1.1.0 {datatype: float64, unit: !unit/unit-1.0.0 deg, + value: 0.00079599300143911} + inputs: [x0, x1] + outputs: [alpha_C, delta_C] + - ! frame: ! - axes_names: [helioprojective longitude, helioprojective latitude] + axes_names: [Helioprojective Longitude, Helioprojective Latitude] axes_order: [0, 1] - axis_physical_types: ['custom:pos.helioprojective.lon', 'custom:pos.helioprojective.lat'] - name: helioprojective + axis_physical_types: ['custom:Helioprojective Longitude', 'custom:Helioprojective + Latitude'] + name: CelestialFrame reference_frame: ! frame_attributes: - observer: ! - data: ! + observer: ! + data: ! components: - x: !unit/quantity-1.1.0 {unit: !unit/unit-1.0.0 m, value: 146678938885.5498} - y: !unit/quantity-1.1.0 {unit: !unit/unit-1.0.0 m, value: -33535694.291915894} - z: !unit/quantity-1.1.0 {unit: !unit/unit-1.0.0 m, value: -17547329351.4196} - type: CartesianRepresentation - frame_attributes: {obstime: !time/time-1.1.0 '2011-02-15T00:00:00.340'} + distance: !unit/quantity-1.1.0 {datatype: float64, unit: !unit/unit-1.0.0 m, + value: 147724815127.9968} + lat: ! {datatype: float64, + unit: !unit/unit-1.0.0 deg, value: -6.821923074710445} + lon: ! + datatype: float64 + unit: !unit/unit-1.0.0 deg + value: 359.98690027534224 + wrap_angle: ! { + datatype: float64, unit: !unit/unit-1.0.0 deg, value: 360.0} + type: SphericalRepresentation + frame_attributes: + obstime: !time/time-1.1.0 2011-02-15T00:00:00.340 + rsun: !unit/quantity-1.1.0 {datatype: float64, unit: !unit/unit-1.0.0 km, + value: 696000.0} obstime: !time/time-1.1.0 2011-02-15T00:00:00.340 - rsun: !unit/quantity-1.1.0 {unit: !unit/unit-1.0.0 m, value: 696000000.0} - unit: [!unit/unit-1.0.0 deg, !unit/unit-1.0.0 deg] + rsun: !unit/quantity-1.1.0 {datatype: float64, unit: !unit/unit-1.0.0 km, + value: 696000.0} + unit: [!unit/unit-1.0.0 arcsec, !unit/unit-1.0.0 arcsec] + transform: null ... BLK0cb"s[?????????????????@@ @ @@@ @ @@@@#@@@@@ @"@@"@ @"@$@%@#@&@$@$@)@)@0@-@+@-@/@#@1@@0@@.@1@1@2@@0@/@1@)@/@*@(@/@.@&@(@%@#@&@%@!@(@!@%@)@*@%@&@#@!@ @'@@@@@@$@@@@@@?@???????????????????@???@?@?@@@@ @@@#@@ @ @%@@$@ @'@(@%@@@!@&@@%@&@%@'@-@(@(@,@0@0@0@@5@4@3@@4@@2@2@1@2@/@1@2@*@-@0@,@,@0@+@)@'@*@,@+@,@,@)@(@@@%@@"@@@@ @!@!@@ @@@ @@@ @@ -329,5 +390,8 @@ wcs: ! ????????@??@@"@$@#@#@&@&@@%@ @$@@*@(@(@&@-@1@/@.@.@+@*@/@(@3@@/@1@2@5@8@3@6@@5@5@>@@A@A`@A@A`@E`@K @N@V@[@\ @_@b@bH@`8@a@`@_@b(@a@d@d@e@h@cX@`@`@^@a@cp@cp@c@b@]0@Z@W@@P@G@C@?@;@:@4@4@@3@0@@4@3@/@1@,@(@.@(@%@0@&@)@.@$@)@@@$@'@(@#@'@#@#@ @!@%@??????????????@@"@#@!@%@@!@$@"@!@$@%@+@+@,@/@*@-@0@0@&@/@(@+@,@0@0@0@2@(@2@@5@5@6@8@<@@@ @<@@?@@@E@J @O@R @Q@T@X@VP@R`@W0@T@V@V`@Z@@[ @\@]@`@\@@Z@W0@S`@V@X@]@[@X@U@Q @N@K@@@`@C@;@7@7@7@1@/@4@.@.@*@,@)@.@,@$@)@"@$@#@&@ @!@$@@)@!@%@(@"@&@@ @!@?????????????@?@@@@&@%@#@!@&@$@(@'@%@#@(@&@"@'@'@)@+@(@,@'@)@,@*@1@/@/@.@5@2@@8@@3@4@8@8@9@;@@ @B@I@H @N@@M@P`@L@K@P@L@P@Q@S@S@Sp@T@V0@T`@O@O@N@H@R@@T@Q@S @Q@H@H@F @?@7@@=@4@@6@4@@5@@*@/@'@$@(@+@%@+@)@'@(@"@$@(@"@"@#@$@!@$@@ @"@@"@@"@@@???????????????@ @@ @@"@@"@!@%@@'@'@,@@"@"@@#@(@+@+@+@-@)@-@0@0@%@/@.@.@)@2@@4@1@3@6@:@<@@@@B@C@D@J@J@F`@F@I @C@@G@M@@J@H@M@M@@Q@K@J @I@F@F@E@K@K@K`@H`@D`@@@?@<@@5@@7@@4@5@3@0@0@0@@/@+@&@)@,@*@&@!@+@"@*@ @'@ @@$@#@!@@"@@ @@@??????????????@?@@@@"@ @%@"@$@$@!@ @%@&@!@(@!@'@'@*@&@!@0@+@0@"@/@,@1@,@)@.@.@-@1@4@+@2@@;@@<@@ @>@A@@C@C @?@@A@A@B@@@B@C@B@H@F @K@F@E@@A@A@@B @D@B @B@C@B @?@8@?@<@@6@.@8@4@@.@.@*@/@*@%@(@%@"@-@#@!@"@"@#@'@&@#@"@"@@@ @@@)@!@??????????????@@@@'@!@@ @ @"@"@#@"@!@@&@'@"@"@%@)@!@*@.@%@-@.@*@&@(@,@'@1@&@1@2@@5@4@<@8@<@6@:@<@<@A@<@9@9@?@@@=@A`@@@C@@<@@@=@<@8@4@<@9@?@@A@=@@7@,@6@1@0@@.@+@.@-@)@'@-@0@,@+@'@/@@@"@#@"@$@@#@@@#@!@@@@@ @?@?@????????????@@@@@#@#@$@@@ @(@#@$@ @%@!@+@%@(@@%@(@'@.@'@&@@)@'@.@"@.@0@1@0@*@8@4@7@;@9@5@5@@7@@3@8@3@@9@@7@@6@@9@@5@A @?@@>@7@@:@5@@9@4@@7@5@6@6@3@4@1@.@0@*@&@+@+@&@.@(@%@/@*@#@%@%@(@%@#@@@#@$@@"@@@#@@@ ????????????????????????@@@$@'@ @@@"@@!@#@&@ @%@%@(@#@*@%@$@$@%@&@&@*@(@'@%@*@,@1@,@,@6@3@1@,@1@5@@-@5@,@-@2@6@8@7@@8@4@6@8@5@@7@7@2@@3@@4@@2@6@@9@3@0@4@0@2@*@(@&@&@)@(@+@%@&@$@%@)@ @&@$@@&@"@@@ @$@@@ @@@????????????????????????@@@ @!@@!@@@$@ @!@@"@!@&@&@*@ @+@"@)@%@"@'@$@'@#@+@%@/@-@0@4@@2@/@*@1@0@0@@/@*@/@-@1@2@1@1@1@3@6@@3@2@1@2@@*@3@@(@0@@,@2@-@0@)@,@0@(@(@"@&@+@*@#@)@&@*@)@@!@"@@"@(@@!@ @ @ @ @@@??????????????????@@@#@#@#@ @"@(@ @#@ @!@@+@@!@"@$@%@'@'@$@@%@,@)@*@1@)@.@)@0@@-@)@)@0@.@)@(@&@-@0@+@,@0@@4@%@0@1@1@@4@@3@@'@*@0@0@1@-@0@@.@'@(@+@)@)@!@(@!@)@*@"@!@@@(@(@(@+@'@ @ @@@@!@ @@ ????????????????????????@@@@ @@@"@@#@$@"@"@ @ @@+@&@#@$@!@#@'@)@)@'@(@'@0@-@(@+@,@)@)@1@0@@'@'@.@)@,@!@+@1@*@.@.@*@/@*@/@#@'@&@)@)@+@.@,@&@*@$@!@*@*@!@*@$@!@@#@'@$@&@"@#@@"@$@@!@ @@!???????????????BLK0 v#.-7D /?ͭx46ͭx46? /?BLK0J6Kyu.#H#ASDF BLOCK INDEX %YAML 1.1 ---- [8313, 139439, 139525] +--- +- 11065 +- 142191 +- 142277 ... From f9949bb311dc391fa563e1fbe7190d18a9315f89 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Thu, 25 Jul 2024 13:02:49 +0100 Subject: [PATCH 329/366] Fix codestyle --- reproject/tests/data/generate_asdf.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/reproject/tests/data/generate_asdf.py b/reproject/tests/data/generate_asdf.py index 15e2659db..181f911a6 100644 --- a/reproject/tests/data/generate_asdf.py +++ b/reproject/tests/data/generate_asdf.py @@ -1,11 +1,10 @@ from collections.abc import Iterable -import gwcs.coordinate_frames as cf -from gwcs import WCS - import astropy.modeling.models as m import astropy.units as u +import gwcs.coordinate_frames as cf from astropy.modeling import CompoundModel, Model +from gwcs import WCS from numpy.typing import ArrayLike From cd680c42e7a7ff72864c97c801faa18d294f3319 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Thu, 25 Jul 2024 13:44:05 +0100 Subject: [PATCH 330/366] Updated tox config for devdeps to pin zarr<3 due to incompatibilities of the pre-release with dask --- tox.ini | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/tox.ini b/tox.ini index 05d436888..946f90f07 100644 --- a/tox.ini +++ b/tox.ini @@ -31,10 +31,11 @@ deps = devdeps: scipy>=0.0.dev0 devdeps: astropy>=0.0.dev0 devdeps: astropy-healpix>=0.0.dev0 - devdeps: git+https://github.com/asdf-format/asdf.git#egg=asdf - devdeps: git+https://github.com/astropy/asdf-astropy.git - devdeps: git+https://github.com/spacetelescope/gwcs.git#egg=gwcs - #devdeps: git+https://github.com/sunpy/sunpy.git#egg=sunpy + devdeps: asdf @ git+https://github.com/asdf-format/asdf.git + devdeps: asdf-astropy @ git+https://github.com/astropy/asdf-astropy.git + devdeps: gwcs @ git+https://github.com/spacetelescope/gwcs.git + devdeps: sunpy[map] @ git+https://github.com/sunpy/sunpy.git + devdeps: zarr<3 extras = test From fed0b99180e5e68aa8340cf927420852e9bea3c6 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Thu, 25 Jul 2024 13:53:33 +0100 Subject: [PATCH 331/366] Fix bug with artifacts in reproject_to_healpix which were due to using scipy's map_coordinates directly instead of our wrapper which deals properly with boundaries --- reproject/healpix/core.py | 13 +++++++++--- reproject/healpix/tests/test_healpix.py | 28 +++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 3 deletions(-) diff --git a/reproject/healpix/core.py b/reproject/healpix/core.py index 8a894701b..1bb899fd8 100644 --- a/reproject/healpix/core.py +++ b/reproject/healpix/core.py @@ -2,6 +2,8 @@ from astropy.coordinates import SkyCoord from astropy_healpix import HEALPix, npix_to_nside +from reproject.array_utils import map_coordinates + __all__ = ["healpix_to_image", "image_to_healpix"] ORDER = {} @@ -121,7 +123,6 @@ def image_to_healpix(data, wcs_in, coord_system_out, nside, order="bilinear", ne no coverage or valid values in the input image, while values of 1 indicate valid values. """ - from scipy.ndimage import map_coordinates hp = HEALPix(nside=nside, order="nested" if nested else "ring") @@ -134,13 +135,19 @@ def image_to_healpix(data, wcs_in, coord_system_out, nside, order="bilinear", ne world_out = SkyCoord(lon_out, lat_out, frame=coord_system_out) # Look up pixels in input WCS - yinds, xinds = wcs_in.world_to_pixel(world_out) + xinds, yinds = wcs_in.world_to_pixel(world_out) # Interpolate if isinstance(order, str): order = ORDER[order] - healpix_data = map_coordinates(data, [xinds, yinds], order=order, mode="constant", cval=np.nan) + healpix_data = map_coordinates( + data, + np.array([yinds, xinds]), + order=order, + mode="constant", + cval=np.nan, + ) return healpix_data, (~np.isnan(healpix_data)).astype(float) diff --git a/reproject/healpix/tests/test_healpix.py b/reproject/healpix/tests/test_healpix.py index a1e2a3fd6..c3ce222b2 100644 --- a/reproject/healpix/tests/test_healpix.py +++ b/reproject/healpix/tests/test_healpix.py @@ -180,3 +180,31 @@ def test_reproject_from_healpix_output_types(valid_celestial_output_projections) np.testing.assert_allclose(output_ref, output_test) np.testing.assert_allclose(footprint_ref, footprint_test) + + +def test_reproject_to_healpix_exact_allsky(): + + # Regression test for a bug that caused artifacts in the final image if the + # WCS covered the whole sky - this was due to using scipy's map_coordinates + # one instead of our built-in one which deals properly with the pixels + # around the rim. + + shape_out = (160, 320) + wcs = WCS(naxis=2) + wcs.wcs.crpix = [(shape_out[1] + 1) / 2, (shape_out[0] + 1) / 2] + wcs.wcs.cdelt = np.array([-360.0 / shape_out[1], 180.0 / shape_out[0]]) + wcs.wcs.crval = [0, 0] + wcs.wcs.ctype = ["RA---CAR", "DEC--CAR"] + + array = np.ones(shape_out) + + healpix_array, footprint = reproject_to_healpix( + (array, wcs), + coord_system_out="galactic", + nside=64, + nested=False, + order="bilinear", + ) + + assert np.all(footprint > 0) + assert not np.any(np.isnan(healpix_array)) From ebc038f602fc00f1d7c0bf7deb0005d59f34b168 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Thu, 25 Jul 2024 14:09:27 +0100 Subject: [PATCH 332/366] Updated test --- reproject/healpix/tests/test_healpix.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reproject/healpix/tests/test_healpix.py b/reproject/healpix/tests/test_healpix.py index c3ce222b2..98dbfb5e9 100644 --- a/reproject/healpix/tests/test_healpix.py +++ b/reproject/healpix/tests/test_healpix.py @@ -155,7 +155,7 @@ def test_reproject_to_healpix_input_types(valid_celestial_input_data): # Make sure there are some valid values - assert np.sum(~np.isnan(healpix_data_ref)) == 3 + assert np.sum(~np.isnan(healpix_data_ref)) == 4 np.testing.assert_allclose(healpix_data_ref, healpix_data_test) np.testing.assert_allclose(footprint_ref, footprint_test) From dcd497ba5e329442adb0ce973826297f370202c3 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Thu, 25 Jul 2024 14:02:20 +0100 Subject: [PATCH 333/366] Don't install asdf dev --- tox.ini | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tox.ini b/tox.ini index 946f90f07..74ff982b3 100644 --- a/tox.ini +++ b/tox.ini @@ -31,8 +31,9 @@ deps = devdeps: scipy>=0.0.dev0 devdeps: astropy>=0.0.dev0 devdeps: astropy-healpix>=0.0.dev0 - devdeps: asdf @ git+https://github.com/asdf-format/asdf.git - devdeps: asdf-astropy @ git+https://github.com/astropy/asdf-astropy.git + # For now we don't test with asdf dev due to this issue: https://github.com/asdf-format/asdf/issues/1811 + #devdeps: asdf @ git+https://github.com/asdf-format/asdf.git + #devdeps: asdf-astropy @ git+https://github.com/astropy/asdf-astropy.git devdeps: gwcs @ git+https://github.com/spacetelescope/gwcs.git devdeps: sunpy[map] @ git+https://github.com/sunpy/sunpy.git devdeps: zarr<3 From 1e4bba8d5d9bb42b48c41198e3ec351c4ad81623 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Fri, 14 Jun 2024 13:00:54 +0100 Subject: [PATCH 334/366] Improve memory efficiency of map_coordinates --- reproject/array_utils.py | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/reproject/array_utils.py b/reproject/array_utils.py index 23a73f1cd..cf7e46e4a 100644 --- a/reproject/array_utils.py +++ b/reproject/array_utils.py @@ -90,6 +90,22 @@ def at_least_float32(array): return array.astype(np.float32) +def memory_efficient_access(array, chunk): + # If we access a number of chunks from a memory-mapped array, memory usage + # will increase and could crash e.g. dask.distributed workers. We therefore + # use a temporary memmap to load the data. + if isinstance(array, np.memmap): + array_tmp = np.memmap( + array.filename, + mode="r", + dtype=array.dtype, + shape=array.shape, + ) + return array_tmp[chunk] + else: + return array[chunk] + + def map_coordinates(image, coords, max_chunk_size=None, output=None, **kwargs): # In the built-in scipy map_coordinates, the values are defined at the # center of the pixels. This means that map_coordinates does not @@ -132,9 +148,17 @@ def map_coordinates(image, coords, max_chunk_size=None, output=None, **kwargs): values = output + include = np.ones(coords.shape[1], dtype=bool) + + if kwargs["order"] <= 1: + padding = 1 + else: + padding = 10 + for chunk in iterate_chunks(image.shape, max_chunk_size=max_chunk_size): include = np.ones(coords.shape[1], dtype=bool) + include[...] = True for idim, slc in enumerate(chunk): include[(coords[idim] < slc.start) | (coords[idim] >= slc.stop)] = False @@ -155,8 +179,10 @@ def map_coordinates(image, coords, max_chunk_size=None, output=None, **kwargs): for idim, slc in enumerate(chunk): coords_subset[idim, :] -= slc.start + image_subset = memory_efficient_access(image, chunk) + output[include] = scipy_map_coordinates( - at_least_float32(image[chunk]), coords_subset, **kwargs + at_least_float32(image_subset), coords_subset, **kwargs ) reset = np.zeros(coords.shape[1], dtype=bool) From 759abc7d91d385e271a45d1b54b687bf29dbea0e Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Fri, 14 Jun 2024 20:47:11 +0100 Subject: [PATCH 335/366] Fix copying of memmap to include offset --- reproject/array_utils.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/reproject/array_utils.py b/reproject/array_utils.py index cf7e46e4a..e56637852 100644 --- a/reproject/array_utils.py +++ b/reproject/array_utils.py @@ -94,12 +94,13 @@ def memory_efficient_access(array, chunk): # If we access a number of chunks from a memory-mapped array, memory usage # will increase and could crash e.g. dask.distributed workers. We therefore # use a temporary memmap to load the data. - if isinstance(array, np.memmap): + if isinstance(array, np.memmap) and array.flags.c_contiguous: array_tmp = np.memmap( array.filename, mode="r", dtype=array.dtype, shape=array.shape, + offset=array.offset ) return array_tmp[chunk] else: From 20f6cd8efda0e9452cdd8cdcf46b1a36adfdf420 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Fri, 14 Jun 2024 21:14:40 +0100 Subject: [PATCH 336/366] Fix code style --- reproject/array_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reproject/array_utils.py b/reproject/array_utils.py index e56637852..ad29f3b09 100644 --- a/reproject/array_utils.py +++ b/reproject/array_utils.py @@ -100,7 +100,7 @@ def memory_efficient_access(array, chunk): mode="r", dtype=array.dtype, shape=array.shape, - offset=array.offset + offset=array.offset, ) return array_tmp[chunk] else: From 4013c26e42d7d80b19d71e5e223d6d279c1db06b Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Thu, 25 Jul 2024 15:02:41 +0100 Subject: [PATCH 337/366] Fixed remaining issues --- reproject/array_utils.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/reproject/array_utils.py b/reproject/array_utils.py index ad29f3b09..79078cb94 100644 --- a/reproject/array_utils.py +++ b/reproject/array_utils.py @@ -141,7 +141,7 @@ def map_coordinates(image, coords, max_chunk_size=None, output=None, **kwargs): # If the data type is native and we are not doing spline interpolation, # then scipy_map_coordinates deals properly with memory maps, so we can use # it without chunking. Otherwise, we need to iterate over data chunks. - if image.dtype.isnative and kwargs["order"] <= 1: + if image.dtype.isnative and "order" in kwargs and kwargs["order"] <= 1: values = scipy_map_coordinates(at_least_float32(image), coords, output=output, **kwargs) else: if output is None: @@ -151,13 +151,12 @@ def map_coordinates(image, coords, max_chunk_size=None, output=None, **kwargs): include = np.ones(coords.shape[1], dtype=bool) - if kwargs["order"] <= 1: + if "order" in kwargs and kwargs["order"] <= 1: padding = 1 else: padding = 10 for chunk in iterate_chunks(image.shape, max_chunk_size=max_chunk_size): - include = np.ones(coords.shape[1], dtype=bool) include[...] = True for idim, slc in enumerate(chunk): From 0fc7f2923e39fc7c3a836171e263082eb9067893 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Thu, 25 Jul 2024 15:28:51 +0100 Subject: [PATCH 338/366] Make the optimized memory data access opt-in --- reproject/array_utils.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/reproject/array_utils.py b/reproject/array_utils.py index 79078cb94..df573571f 100644 --- a/reproject/array_utils.py +++ b/reproject/array_utils.py @@ -107,7 +107,7 @@ def memory_efficient_access(array, chunk): return array[chunk] -def map_coordinates(image, coords, max_chunk_size=None, output=None, **kwargs): +def map_coordinates(image, coords, max_chunk_size=None, output=None, optimize_memory=False, **kwargs): # In the built-in scipy map_coordinates, the values are defined at the # center of the pixels. This means that map_coordinates does not # correctly treat pixels that are in the outer half of the outer pixels. @@ -122,6 +122,11 @@ def map_coordinates(image, coords, max_chunk_size=None, output=None, **kwargs): # big-endian arrays, we operate in chunks with a size smaller or equal to # max_chunk_size. + # The optimize_memory option isn't used right not by the rest of reproject + # but it is a mode where if we are in a memory-constrained environment, we + # re-create memmaps for individual chunks to avoid caching the whole array. + # We need to decide how to expose this to users. + # TODO: check how this should behave on a big-endian system. from scipy.ndimage import map_coordinates as scipy_map_coordinates @@ -179,7 +184,10 @@ def map_coordinates(image, coords, max_chunk_size=None, output=None, **kwargs): for idim, slc in enumerate(chunk): coords_subset[idim, :] -= slc.start - image_subset = memory_efficient_access(image, chunk) + if optimize_memory: + image_subset = memory_efficient_access(image, chunk) + else: + image_subset = image[chunk] output[include] = scipy_map_coordinates( at_least_float32(image_subset), coords_subset, **kwargs From 4b3d7b8a148a9a59c0d6537fcfd0ac7d629c66e9 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Thu, 25 Jul 2024 15:35:57 +0100 Subject: [PATCH 339/366] Fix codestyle --- reproject/array_utils.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/reproject/array_utils.py b/reproject/array_utils.py index df573571f..970cd8180 100644 --- a/reproject/array_utils.py +++ b/reproject/array_utils.py @@ -107,7 +107,9 @@ def memory_efficient_access(array, chunk): return array[chunk] -def map_coordinates(image, coords, max_chunk_size=None, output=None, optimize_memory=False, **kwargs): +def map_coordinates( + image, coords, max_chunk_size=None, output=None, optimize_memory=False, **kwargs +): # In the built-in scipy map_coordinates, the values are defined at the # center of the pixels. This means that map_coordinates does not # correctly treat pixels that are in the outer half of the outer pixels. From 83be79e114f4a601c666a0f56fcd82dca9425dd9 Mon Sep 17 00:00:00 2001 From: astrofrog Date: Fri, 26 Jul 2024 11:45:48 +0000 Subject: [PATCH 340/366] Update CHANGELOG --- CHANGES.md | 99 ++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 66 insertions(+), 33 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 8dc1b346e..b5683d1ce 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,36 @@ +## v0.14.0 - 2024-07-26 + + +### What's Changed + +#### Bug Fixes + +* Support readonly arrays in adaptive by @svank in https://github.com/astropy/reproject/pull/452 +* Fix bug with artifacts in reproject_to_healpix by @astrofrog in https://github.com/astropy/reproject/pull/459 + +#### New Features + +* Fix multi-threaded reprojection when using Astropy WCS by @astrofrog in https://github.com/astropy/reproject/pull/434 +* Generalize reproject_and_coadd for N-dimensional data, and add option to specify blank pixel value and progress bar by @keflavich in https://github.com/astropy/reproject/pull/351 +* Improve performance for large datasets and switch to multi-threading by default by @astrofrog in https://github.com/astropy/reproject/pull/443 + +#### Documentation + +* Reorganized performance docs/tips by @astrofrog in https://github.com/astropy/reproject/pull/444 + +#### Other Changes + +* Don't use --pre on Python 3.12 by @astrofrog in https://github.com/astropy/reproject/pull/445 +* Bump minimum required version of astropy-healpix by @astrofrog in https://github.com/astropy/reproject/pull/446 +* Improvements to performance when using dask.distributed by @astrofrog in https://github.com/astropy/reproject/pull/447 +* Add logging calls and fix a couple of dask-related issues by @astrofrog in https://github.com/astropy/reproject/pull/450 +* Fix CI following Sunpy 6.0.0 release by @astrofrog in https://github.com/astropy/reproject/pull/457 +* Add a script to regenerate the aia asdf file and do so by @Cadair in https://github.com/astropy/reproject/pull/439 +* Fix devdeps CI by @astrofrog in https://github.com/astropy/reproject/pull/458 +* Performance improvements for interpolation with map_coordinates by @astrofrog in https://github.com/astropy/reproject/pull/448 + +**Full Changelog**: https://github.com/astropy/reproject/compare/v0.13.1...v0.14.0 + ## v0.13.1 - 2024-04-05 @@ -166,9 +199,9 @@ ## 0.9 - 2022-09-02 - Drop support for Python 3.7. -- +- - Infrastructure and packaging updates. -- +- - Made many improvements, bug fixes, and significant speed-ups for the adaptive - resampling algorithm, `reproject_adaptive`. These bug fixes may cause - changes to the reprojected images, which are typically negligible. @@ -176,13 +209,13 @@ - Gaussian filter kernel, a menu of boundary-handling modes, and a - `center_jacobian` flag to trade speed for accuracy with rapidly-varying - transformations. -- +- - Added a `roundtrip_coords` argument to `reproject_adaptive` and - `reproject_interp`. By default, all coordinate transformations are run in - both directions to handle some situations where they are ambiguous. This can - be disabled by setting `roundtrip_coords=False` which may offer a - significant speed increase. -- +- ## 0.8 - 2021-08-11 @@ -191,34 +224,34 @@ - possible to solve for the optimal WCS for a set of images that couldn't fit - into memory all at once, since the actual data aren't needed for optimal WCS - determination. [#242] -- +- - Fix implementation of `hdu_weights` in `reproject_and_coadd`. [#249] -- +- ## 0.7.1 - 2020-05-29 - Fixed compatibility with Astropy 4.1. [#234] -- +- - Updated minimum requirement for SciPy. [#236] -- +- ## 0.7 - 2020-04-02 - Made C extension in overlapArea.c thread-safe by removing global - variables. [#211] -- +- - Made it possible to control whether to output debugging information - from overlapArea.c by setting DEBUG_OVERLAP_AREA=1 at build-time. [#211] -- +- - Fix compatibility with astropy v4.0.1. [#227] -- +- - Disable parallelization by default in `reproject_exact` - this can be - enabled with `parallel=True`. [#227] -- +- - Fixed a bug with `reproject_exact` with `parallel=False` and - `return_footprint=False`, which caused the footprint to be returned - anyway. [#227] -- +- - The infrastructure of the package has been updated in line with the - APE 17 roadmap (https://github.com/astropy/astropy-APEs/blob/main/APE17.rst). - The main changes are that the `python setup.py test` and @@ -227,7 +260,7 @@ - (https://tox.readthedocs.io) package and run `tox -e test` and - `tox -e build_docs`. It is also possible to run pytest and sphinx - directly. [#228] -- +- ## 0.6 - 2019-11-01 @@ -236,20 +269,20 @@ - `independent_celestial_slices=` argument to `reproject_interp` has - been deprecated since it is no longer needed, as transformations are - automatically done in the most efficient way possible. [#166] -- +- - Include a warning for high resolution images with `reproject_exact`, - since if the pixels are <0.05", precision issues can occur. [#200] -- +- - Added a new `reproject_and_coadd` function for doing mosaicking of - individual images, and added section in documentation about mosaicking. - [#186] -- +- - Added a new reproject.adaptive sub-package that implements the DeForest - (2004) algorithm for reprojection. [#52] -- +- - Fixed a bug that caused 'exact' reprojection results to have numerical - issues when doing identity transformations. [#190] -- +- ## 0.5.1 - 2019-09-01 @@ -260,33 +293,33 @@ - Improve parse_output_projection to make it so that the output projection - can be specified as a filename. [#150] -- +- - Fixed a bug that caused HEALPix maps in RING order to not be correctly - interpreted. [#163] -- +- - Make it possible to specify the output array for reprojection using the - `output_array=` keyword argument. [#115] -- +- ## 0.4 - 2018-01-29 - Refactored HEALPix reprojection code to use the astropy-healpix package - instead of healpy. [#139] -- +- - Added the ability to specify an output array in `reproject_interp`, which - permits the use of memory-mapped arrays and therefore provides the capability - to handle data cubes much larger than memory [#115] -- +- - Fix test 32-bit test failures. [#146] -- +- - Fix an issue with reprojecting images where there are two solutions along - the line of sight by forcing round-tripping of coordinate conversions [#129] -- +- - Explicitly define default HDU as 0 for normal reprojection and 1 for - HEALPix reprojection. [#119] -- +- - Added a function to find the optimal WCS for a set of images. [#136, #137] -- +- ## 0.3.2 - 2017-10-22 @@ -297,22 +330,22 @@ ## 0.3.1 - 2016-07-07 - Include missing license file in tarball. -- +- - Updated documentation to remove warnings about early versions. -- +- ## 0.3 - 2016-07-06 - Allow users to pass a `field=` option to `reproject_from_healpix` - to access different fields in a HEALPIX file. [#86] -- +- - Significant improvements to performance when the input data is a large - memory-mapped array. [#105] -- +- - Significant refactoring of interpolating reprojection to improve support for - n-dimensional arrays, optionally including two celestial axes (in which - case the coordinate transformation is taken into account). [#96, #102] -- +- ## 0.2 - 2015-10-29 From 1444b7e5154f60047b8b52e80185d42e6e789e11 Mon Sep 17 00:00:00 2001 From: Ole Streicher Date: Fri, 26 Jul 2024 16:16:50 +0200 Subject: [PATCH 341/366] Enforce readonly mode for memmap when reading input array --- reproject/common.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/reproject/common.py b/reproject/common.py index e9de813ac..e4f296734 100644 --- a/reproject/common.py +++ b/reproject/common.py @@ -248,9 +248,9 @@ def reproject_single_block(a, array_or_path, block_info=None): wcs_out_sub = HighLevelWCSWrapper(low_level_wcs) if isinstance(array_or_path, tuple): - array_in = np.memmap(array_or_path[0], **array_or_path[1]) + array_in = np.memmap(array_or_path[0], **array_or_path[1], mode="r") elif isinstance(array_or_path, str): - array_in = np.memmap(array_or_path, dtype=float, shape=shape_in) + array_in = np.memmap(array_or_path, dtype=float, shape=shape_in, mode="r") else: array_in = array_or_path From 7e3bac5310ad6dfdb1db52581ac7bfa58dc18864 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 26 Jul 2024 15:09:48 +0000 Subject: [PATCH 342/366] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- CHANGES.md | 66 +++++++++++++++++++++++++++--------------------------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index b5683d1ce..012674386 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -199,9 +199,9 @@ ## 0.9 - 2022-09-02 - Drop support for Python 3.7. -- +- - Infrastructure and packaging updates. -- +- - Made many improvements, bug fixes, and significant speed-ups for the adaptive - resampling algorithm, `reproject_adaptive`. These bug fixes may cause - changes to the reprojected images, which are typically negligible. @@ -209,13 +209,13 @@ - Gaussian filter kernel, a menu of boundary-handling modes, and a - `center_jacobian` flag to trade speed for accuracy with rapidly-varying - transformations. -- +- - Added a `roundtrip_coords` argument to `reproject_adaptive` and - `reproject_interp`. By default, all coordinate transformations are run in - both directions to handle some situations where they are ambiguous. This can - be disabled by setting `roundtrip_coords=False` which may offer a - significant speed increase. -- +- ## 0.8 - 2021-08-11 @@ -224,34 +224,34 @@ - possible to solve for the optimal WCS for a set of images that couldn't fit - into memory all at once, since the actual data aren't needed for optimal WCS - determination. [#242] -- +- - Fix implementation of `hdu_weights` in `reproject_and_coadd`. [#249] -- +- ## 0.7.1 - 2020-05-29 - Fixed compatibility with Astropy 4.1. [#234] -- +- - Updated minimum requirement for SciPy. [#236] -- +- ## 0.7 - 2020-04-02 - Made C extension in overlapArea.c thread-safe by removing global - variables. [#211] -- +- - Made it possible to control whether to output debugging information - from overlapArea.c by setting DEBUG_OVERLAP_AREA=1 at build-time. [#211] -- +- - Fix compatibility with astropy v4.0.1. [#227] -- +- - Disable parallelization by default in `reproject_exact` - this can be - enabled with `parallel=True`. [#227] -- +- - Fixed a bug with `reproject_exact` with `parallel=False` and - `return_footprint=False`, which caused the footprint to be returned - anyway. [#227] -- +- - The infrastructure of the package has been updated in line with the - APE 17 roadmap (https://github.com/astropy/astropy-APEs/blob/main/APE17.rst). - The main changes are that the `python setup.py test` and @@ -260,7 +260,7 @@ - (https://tox.readthedocs.io) package and run `tox -e test` and - `tox -e build_docs`. It is also possible to run pytest and sphinx - directly. [#228] -- +- ## 0.6 - 2019-11-01 @@ -269,20 +269,20 @@ - `independent_celestial_slices=` argument to `reproject_interp` has - been deprecated since it is no longer needed, as transformations are - automatically done in the most efficient way possible. [#166] -- +- - Include a warning for high resolution images with `reproject_exact`, - since if the pixels are <0.05", precision issues can occur. [#200] -- +- - Added a new `reproject_and_coadd` function for doing mosaicking of - individual images, and added section in documentation about mosaicking. - [#186] -- +- - Added a new reproject.adaptive sub-package that implements the DeForest - (2004) algorithm for reprojection. [#52] -- +- - Fixed a bug that caused 'exact' reprojection results to have numerical - issues when doing identity transformations. [#190] -- +- ## 0.5.1 - 2019-09-01 @@ -293,33 +293,33 @@ - Improve parse_output_projection to make it so that the output projection - can be specified as a filename. [#150] -- +- - Fixed a bug that caused HEALPix maps in RING order to not be correctly - interpreted. [#163] -- +- - Make it possible to specify the output array for reprojection using the - `output_array=` keyword argument. [#115] -- +- ## 0.4 - 2018-01-29 - Refactored HEALPix reprojection code to use the astropy-healpix package - instead of healpy. [#139] -- +- - Added the ability to specify an output array in `reproject_interp`, which - permits the use of memory-mapped arrays and therefore provides the capability - to handle data cubes much larger than memory [#115] -- +- - Fix test 32-bit test failures. [#146] -- +- - Fix an issue with reprojecting images where there are two solutions along - the line of sight by forcing round-tripping of coordinate conversions [#129] -- +- - Explicitly define default HDU as 0 for normal reprojection and 1 for - HEALPix reprojection. [#119] -- +- - Added a function to find the optimal WCS for a set of images. [#136, #137] -- +- ## 0.3.2 - 2017-10-22 @@ -330,22 +330,22 @@ ## 0.3.1 - 2016-07-07 - Include missing license file in tarball. -- +- - Updated documentation to remove warnings about early versions. -- +- ## 0.3 - 2016-07-06 - Allow users to pass a `field=` option to `reproject_from_healpix` - to access different fields in a HEALPIX file. [#86] -- +- - Significant improvements to performance when the input data is a large - memory-mapped array. [#105] -- +- - Significant refactoring of interpolating reprojection to improve support for - n-dimensional arrays, optionally including two celestial axes (in which - case the coordinate transformation is taken into account). [#96, #102] -- +- ## 0.2 - 2015-10-29 From 6ca1276118b27d0c2a21560de4ba426fb7906234 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 5 Aug 2024 16:29:29 +0000 Subject: [PATCH 343/366] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/PyCQA/flake8: 7.1.0 → 7.1.1](https://github.com/PyCQA/flake8/compare/7.1.0...7.1.1) - [github.com/numpy/numpydoc: v1.7.0 → v1.8.0rc2](https://github.com/numpy/numpydoc/compare/v1.7.0...v1.8.0rc2) - [github.com/astral-sh/ruff-pre-commit: v0.5.4 → v0.5.6](https://github.com/astral-sh/ruff-pre-commit/compare/v0.5.4...v0.5.6) --- .pre-commit-config.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 7bba8d758..7d5be37f6 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -40,7 +40,7 @@ repos: # F822: undefined name in __all__ # F823: local variable name referenced before assignment - repo: https://github.com/PyCQA/flake8 - rev: 7.1.0 + rev: 7.1.1 hooks: - id: flake8 args: @@ -57,7 +57,7 @@ repos: - id: black - repo: https://github.com/numpy/numpydoc - rev: v1.7.0 + rev: v1.8.0rc2 hooks: - id: numpydoc-validation files: ".*(high_level|mosaicking).*$" @@ -77,7 +77,7 @@ repos: - tomli - repo: https://github.com/astral-sh/ruff-pre-commit - rev: "v0.5.4" + rev: "v0.5.6" hooks: - id: ruff args: ["--fix", "--show-fixes"] From 432f77a52c29a68f105b78f09da80d3f436f5c98 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 12 Aug 2024 16:27:49 +0000 Subject: [PATCH 344/366] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/psf/black-pre-commit-mirror: 24.4.2 → 24.8.0](https://github.com/psf/black-pre-commit-mirror/compare/24.4.2...24.8.0) - [github.com/numpy/numpydoc: v1.8.0rc2 → v1.8.0](https://github.com/numpy/numpydoc/compare/v1.8.0rc2...v1.8.0) - [github.com/astral-sh/ruff-pre-commit: v0.5.6 → v0.5.7](https://github.com/astral-sh/ruff-pre-commit/compare/v0.5.6...v0.5.7) --- .pre-commit-config.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 7d5be37f6..467f4f43e 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -52,12 +52,12 @@ repos: exclude: ".*(data.*|extern.*|cextern)$" - repo: https://github.com/psf/black-pre-commit-mirror - rev: 24.4.2 + rev: 24.8.0 hooks: - id: black - repo: https://github.com/numpy/numpydoc - rev: v1.8.0rc2 + rev: v1.8.0 hooks: - id: numpydoc-validation files: ".*(high_level|mosaicking).*$" @@ -77,7 +77,7 @@ repos: - tomli - repo: https://github.com/astral-sh/ruff-pre-commit - rev: "v0.5.6" + rev: "v0.5.7" hooks: - id: ruff args: ["--fix", "--show-fixes"] From 00e31f714b7ebd63b35312a7d41251eaccf3433a Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 23 Sep 2024 16:28:06 +0000 Subject: [PATCH 345/366] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/scientific-python/cookie: 2024.04.23 → 2024.08.19](https://github.com/scientific-python/cookie/compare/2024.04.23...2024.08.19) - [github.com/astral-sh/ruff-pre-commit: v0.5.7 → v0.6.7](https://github.com/astral-sh/ruff-pre-commit/compare/v0.5.7...v0.6.7) --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 467f4f43e..b0aafe6a0 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -64,7 +64,7 @@ repos: exclude: ".*(tests.*)$" - repo: https://github.com/scientific-python/cookie - rev: 2024.04.23 + rev: 2024.08.19 hooks: - id: sp-repo-review @@ -77,7 +77,7 @@ repos: - tomli - repo: https://github.com/astral-sh/ruff-pre-commit - rev: "v0.5.7" + rev: "v0.6.7" hooks: - id: ruff args: ["--fix", "--show-fixes"] From c3b8fbd49c7f9f71dd47ed0b167868f4679a80b8 Mon Sep 17 00:00:00 2001 From: "P. L. Lim" <2090236+pllim@users.noreply.github.com> Date: Fri, 27 Sep 2024 11:46:54 -0400 Subject: [PATCH 346/366] MNT: Use hash for Action workflow versions and update if needed --- .github/workflows/ci_workflows.yml | 4 ++-- .github/workflows/update-changelog.yaml | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci_workflows.yml b/.github/workflows/ci_workflows.yml index 5e0a27eb0..bb62be62e 100644 --- a/.github/workflows/ci_workflows.yml +++ b/.github/workflows/ci_workflows.yml @@ -15,7 +15,7 @@ concurrency: jobs: tests: - uses: OpenAstronomy/github-actions-workflows/.github/workflows/tox.yml@v1 + uses: OpenAstronomy/github-actions-workflows/.github/workflows/tox.yml@924441154cf3053034c6513d5e06c69d262fb9a6 # v1.13.0 with: envs: | - macos: py310-test-oldestdeps @@ -35,7 +35,7 @@ jobs: publish: needs: tests - uses: OpenAstronomy/github-actions-workflows/.github/workflows/publish.yml@v1 + uses: OpenAstronomy/github-actions-workflows/.github/workflows/publish.yml@924441154cf3053034c6513d5e06c69d262fb9a6 # v1.13.0 with: test_extras: test test_command: pytest -p no:warnings --pyargs reproject diff --git a/.github/workflows/update-changelog.yaml b/.github/workflows/update-changelog.yaml index 10651e043..7347b6e17 100644 --- a/.github/workflows/update-changelog.yaml +++ b/.github/workflows/update-changelog.yaml @@ -14,19 +14,19 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v4 + uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 with: ref: main - name: Update Changelog - uses: stefanzweifel/changelog-updater-action@v1 + uses: stefanzweifel/changelog-updater-action@61ce794778aa787ea8d204d9fe2928543cb2fe40 # v1.11.0 with: release-notes: ${{ github.event.release.body }} latest-version: ${{ github.event.release.name }} path-to-changelog: CHANGES.md - name: Commit updated CHANGELOG - uses: stefanzweifel/git-auto-commit-action@v5 + uses: stefanzweifel/git-auto-commit-action@8621497c8c39c72f3e2a999a26b4ca1b5058a842 # v5.0.1 with: branch: main commit_message: Update CHANGELOG From e6735eb65f86f1b105a52f88d20760405e9d2ac5 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Thu, 3 Oct 2024 14:13:15 +0100 Subject: [PATCH 347/366] Ingore zarr warning --- pyproject.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index c443951d3..115102318 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -92,6 +92,9 @@ filterwarnings = [ "ignore:'xdrlib' is deprecated and slated for removal in Python 3.13", # This is a pyvo issue with Python 3.11 "ignore:'cgi' is deprecated and slated for removal in Python 3.13", + # Issue with zarr and dask mismatch + "ignore:ignoring keyword argument 'read_only'" + ] [tool.coverage.run] From 93e7fcb39f4acfa147391d307505004b68b4aa2c Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Thu, 3 Oct 2024 14:13:40 +0100 Subject: [PATCH 348/366] Re-generated AIA ASDF file with latest sunpy --- reproject/tests/data/aia_171_level1.asdf | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/reproject/tests/data/aia_171_level1.asdf b/reproject/tests/data/aia_171_level1.asdf index 957d76ce3..a74307161 100644 --- a/reproject/tests/data/aia_171_level1.asdf +++ b/reproject/tests/data/aia_171_level1.asdf @@ -12,11 +12,6 @@ history: extension_uri: asdf://asdf-format.org/core/extensions/core-1.5.0 manifest_software: !core/software-1.0.0 {name: asdf_standard, version: 1.1.1} software: !core/software-1.0.0 {name: asdf-astropy, version: 0.6.1} - - !core/extension_metadata-1.0.0 - extension_class: asdf.extension._manifest.ManifestExtension - extension_uri: asdf://asdf-format.org/transform/extensions/transform-1.5.0 - manifest_software: !core/software-1.0.0 {name: asdf_transform_schemas, version: 0.5.0} - software: !core/software-1.0.0 {name: asdf-astropy, version: 0.6.1} - !core/extension_metadata-1.0.0 extension_class: asdf.extension._manifest.ManifestExtension extension_uri: asdf://astropy.org/astropy/extensions/units-1.0.0 @@ -28,8 +23,13 @@ history: software: !core/software-1.0.0 {name: gwcs, version: 0.21.0} - !core/extension_metadata-1.0.0 extension_class: asdf.extension._manifest.ManifestExtension - extension_uri: asdf://sunpy.org/extensions/sunpy-1.1.0 - software: !core/software-1.0.0 {name: sunpy, version: 5.1.5} + extension_uri: asdf://asdf-format.org/transform/extensions/transform-1.5.0 + manifest_software: !core/software-1.0.0 {name: asdf_transform_schemas, version: 0.5.0} + software: !core/software-1.0.0 {name: asdf-astropy, version: 0.6.1} + - !core/extension_metadata-1.0.0 + extension_class: asdf.extension._manifest.ManifestExtension + extension_uri: asdf://sunpy.org/extensions/sunpy-1.1.1 + software: !core/software-1.0.0 {name: sunpy, version: 6.0.2} - !core/extension_metadata-1.0.0 extension_class: asdf.extension._manifest.ManifestExtension extension_uri: asdf://asdf-format.org/astronomy/coordinates/extensions/coordinates-1.1.0 @@ -332,21 +332,21 @@ wcs: ! data: ! components: distance: !unit/quantity-1.1.0 {datatype: float64, unit: !unit/unit-1.0.0 m, - value: 147724815127.9968} + value: 147724815127.99683} lat: ! {datatype: float64, - unit: !unit/unit-1.0.0 deg, value: -6.821923074710445} + unit: !unit/unit-1.0.0 deg, value: -6.821923074710443} lon: ! datatype: float64 unit: !unit/unit-1.0.0 deg - value: 359.98690027534224 + value: 359.9868885126059 wrap_angle: ! { datatype: float64, unit: !unit/unit-1.0.0 deg, value: 360.0} type: SphericalRepresentation frame_attributes: - obstime: !time/time-1.1.0 2011-02-15T00:00:00.340 + obstime: !time/time-1.1.0 2011-02-15T00:00:01.340 rsun: !unit/quantity-1.1.0 {datatype: float64, unit: !unit/unit-1.0.0 km, value: 696000.0} - obstime: !time/time-1.1.0 2011-02-15T00:00:00.340 + obstime: !time/time-1.1.0 2011-02-15T00:00:01.340 rsun: !unit/quantity-1.1.0 {datatype: float64, unit: !unit/unit-1.0.0 km, value: 696000.0} unit: [!unit/unit-1.0.0 arcsec, !unit/unit-1.0.0 arcsec] From d3577ed91c6ffefee895a0ac10687ad67da73fd9 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Thu, 3 Oct 2024 14:14:05 +0100 Subject: [PATCH 349/366] Moved AIA data loading into a fixture to avoid duplication and re-generated reference files due to changes in Sunpy 6.0.0 and 6.0.1 --- pyproject.toml | 2 +- .../test_reproject_adaptive_roundtrip.fits | Bin 270720 -> 270720 bytes ...eproject_adaptive_uncentered_jacobian.fits | Bin 270720 -> 270720 bytes reproject/adaptive/tests/test_core.py | 48 ++----------- reproject/conftest.py | 40 +++++++++++ .../test_reproject_celestial_2d_gal2equ.fits | Bin 40320 -> 40320 bytes .../test_reproject_celestial_3d_equ2gal.fits | Bin 11520 -> 11520 bytes .../reference/test_reproject_roundtrip.fits | Bin 270720 -> 270720 bytes .../tests/reference/test_small_cutout.fits | Bin 11520 -> 11520 bytes reproject/interpolation/tests/test_core.py | 67 ++++-------------- .../tests/reference/test_coadd_solar_map.fits | Bin 264960 -> 264960 bytes reproject/mosaicking/tests/test_coadd.py | 4 +- .../mosaicking/tests/test_wcs_helpers.py | 2 +- reproject/tests/data/aia_171_level1.asdf | 8 +-- reproject/tests/test_high_level.py | 2 +- 15 files changed, 67 insertions(+), 106 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 115102318..509aca3d7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -32,7 +32,7 @@ test = [ ] testall = [ "shapely>=2.0.2", # 2.0.2 fixed a bug that causes changes in test results - "sunpy[map]>=2.1", + "sunpy[map]>=6.0.1", "asdf", "gwcs", "pyvo", diff --git a/reproject/adaptive/tests/reference/test_reproject_adaptive_roundtrip.fits b/reproject/adaptive/tests/reference/test_reproject_adaptive_roundtrip.fits index 0f08aaecfe814a5871dbf6679b60f4f7863cade0..4a3d3f2212b36d18498ca7167f8f569b97da04f1 100644 GIT binary patch delta 54774 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 B#u)$r delta 54764 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 z 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 diff --git a/reproject/adaptive/tests/reference/test_reproject_adaptive_uncentered_jacobian.fits b/reproject/adaptive/tests/reference/test_reproject_adaptive_uncentered_jacobian.fits index 3394b84819eefd498c1a21b5ea07a26c3fa5d6a7..342d0f804d868bc9aa14771f5a556d683e8efd68 100644 GIT binary patch delta 53551 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 delta 12636 zcmZ9ycRZGV`1fz`GRmw-Xqcs;Qm<1;i3UoPq`g!=X&|~(lpj{7%u_N*5| z=dv9owNuq-wXC+;FXM!|rnf^if1FWswPx7}aT(3^Z*8bQPoW{`1a+^}d{h^Nv~Wyf zQFlYgkT-7tb&Vd8l50IsQ(?2Zd|x+eH+~wEy6J~TDbLmvvkPcKZ(HQMU^L{Em4BT6 z4-G~-rxtva%R|jF0rg}?D5{sNZoF;lgWB&~{#W(|-Gk$i?O#C}{jgNn@{n zjfTDc`g5cd(CBN>&qJ}6Hr^RO8L_+74qhas`fHzX#BO}f>Y^VwAR}H(A`>u_E5EL zQXAK!{^Y4g&DI;_@h@9G-)*HXYF6q$Zrl>0~>|Vu%V0hz`Y37 z2Sk5xA{c14bk{j{&o(lC&NE8YEoKnvECbj%vI?kKKgD*lR!9BRn(Fp|b7)PZFY3ya zK=X*?GVLNeG%Pxl?N*_KTGeSuO~tqP}0*R4n0?+K3LY%3b=N+Y(Y*q~9>cjX41 z@2I=rp7rK=nvz$A+*JF7#;Ki_v>g?~XkxCElP)}g=9}z_ z<zYplF5KD(RySmMfxWNB7e3(Nxs$td5pkpNgjLn^))dOQB_L--m<<0kkGL z1e$rBj5L!8iVTw#p`7hJvK#Gh`RfD4KcMaqzZK)YXc;Q1`g3R!?R|0m!_&*r`n&k7 z{6IKbH2*~|iur=3n>RKem|lxoW@&k#vI}{WnZxdXexYG$ZKTLmHPmf1+ILac2lb0Q zB|ZDdhqlJY!Zl2t{27ZLgV*bwXtnIXv`_}SKCLR)g*0Ib8rb-r^^ln)$c%O zrNFJG%FmH2;Z12fo#;DX^(NXR_Frkyqoc7P_wP!&t1x@aHqPBvCsMDgluJ$ZNV}!7s+e{={ zUdPXDd-@rz8EG9GR!DIv9_cka`@jc5z=Xx}hk zhK^>V?G}D?^ek%JG*mf?_VVSnOGbpzW@Nf7L;DN2O2naO^rS#y1vv~MjM z)D>?cU>O=yDH}~Ttoe?{owrz3$EQi{hO~t!7NIFF?u&7gAR3)?7KHecfx0oD=Q*T= zCdwP?al=h${c3#CM5+{>OD}QGD11e8-N|j!J6q9qQR1e8`~Z49*BY-Z-+}J1k}Olh zZRjwmb=%|Qi`Iv`a~32lL0kRhGn6&CXcoKT7%8CsFValfp3-peaZ6!3+70Ge(m0>d zWUu!lN;nX$sha$_dI@F#~mb$gel4O`?IoC-7oyHA> z^f8j?rWMgI>%-9TUi6nua}YZ2{9M^8AA#;$zu8*?9nnpD3d_bAbY-}A+%bQLmS1u7 z;`hmDZPheYXp%;Y@S)Ex%Ua~n)LQYKKeQihF@H{<3ZQdNjvAq^h@QtH!>9h}q2t9*uF%3}wCb0| z*T)&4{Y%to)6gk&JU3m>q_fa!etBeEiH8m`E0fbs<7nD0TWgxhL{nGi6Awv8v>dHl zt~X5yK%>H*#dI4VG)`pEqJ;C&Cem8^fi?$S^K-vc&D=nzk{tJq=xNl8l(KJG9YtHo ze|md0Ezza(i|M~5AKiYt8C;t+=W|Q=-UNGrAOgFFDse zLw9{E=h?{32qisAz_fJOeYDG_{sQou0{w6++|yDRU8HqZL{ z&;xvQZ@+ZE(Yg@bteGWg+2d$WFo^cm)<)aNhjE1y^U-CzjGMT)6Wy1;uax!7LT9;P z$24_2T7^U+#p9CDtVNmE6Jm;%(8Kg=_N&oBsj9p5LPZ}Pxia4@l9=eQvkREXSd1=T z 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 Jb%+is{|ETx`zQba delta 55888 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

d{{C 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 TZm)g1Gl_r~@qsf*?U3d_oq5>) diff --git a/reproject/interpolation/tests/reference/test_small_cutout.fits b/reproject/interpolation/tests/reference/test_small_cutout.fits index 10947a27da1a21affa80db4843751078db05f883..81e2a033ce7a7177006b9294e4507e9812be2297 100644 GIT binary patch delta 740 zcmXBSO=uHA7{>A0&8AHW$tEVG5@Q&ZL@E&*YAz++9lx+3h)QWCEx4f2LJ!)DAc#BI zN))UJB9(d>Jcx=)CD4FKSoL5iB4Vu{?ZL&1por+fgWBLc)6;MM?;duCeR+jf=JtkN zN(iwNJsyK*uI0nf^&Exz#AOMwR;w2s_-852V5>ZXwu8Z%fE_nF7~FDvnL_${M8Ng) zXE==Cr3|)DH&ts(nn06tZnuMje)NknqTLke#M}!H7FOOVyN2FU^yb@>%Fe+9K02Jg z}aCCWFC`)Pu2~CmfjmQDU&TZb;cp2Piu4RyV4{hW%6-bM85K5UlTFP|qx= zHip-3YBpv$=9ybjHQ!;3$RzgmC*L{DJ)IIT(;M+%{D|;j+t+7G-QBllvCJ`d!~K&| zVkaM#8Qi*f*oXQ06%IE|bP1RyI~^D=yrD2u{G|+J$9=SR>=lDXwNG_uwItBAe}=*A zw+jNM`)nU3?~hWcYe&tBRDR*_s|*Hu3mj@z!-3?`X`y`H^kFJca$xfFtTM6fJVTpj z!xFg*V-|(Mp4}eQA7?ovQ+2fUZ{8Hj*h@;tL1IS+7MUE_kq6@v`A;7O9N7M8%!kIQ p_Z;etHV0~*>P1#-S}m{j*Z&~Layj>m(po%vGIWhbjT%q!)PE?or8EEl delta 739 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 IXDXx6=C81y&6e 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

dt}f$Y(zK86`5Ks$Do$+hJ%iaz z71 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 z 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 w;2|Hlm*AfMQ{Sk`cXG1eA#B{+@Sv0wonP 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 HLiYX-BKN4j delta 190553 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�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�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 diff --git a/reproject/mosaicking/tests/test_coadd.py b/reproject/mosaicking/tests/test_coadd.py index 7aefa11fb..6641ea481 100644 --- a/reproject/mosaicking/tests/test_coadd.py +++ b/reproject/mosaicking/tests/test_coadd.py @@ -371,9 +371,7 @@ def test_coadd_solar_map(): # and combine them into a single one. This uses weight maps that are not # uniform and also include NaN values. - # The reference image was generated for sunpy 6.0.0 - it will not work - # for previous versions. - pytest.importorskip("sunpy", minversion="6.0.0") + pytest.importorskip("sunpy", minversion="6.0.1") from sunpy.map import Map, all_coordinates_from_map # Load in three images from different viewpoints around the Sun diff --git a/reproject/mosaicking/tests/test_wcs_helpers.py b/reproject/mosaicking/tests/test_wcs_helpers.py index 96d621a75..f82439132 100644 --- a/reproject/mosaicking/tests/test_wcs_helpers.py +++ b/reproject/mosaicking/tests/test_wcs_helpers.py @@ -320,7 +320,7 @@ def test_solar_wcs(): # Regression test for issues that occurred when trying to find # the optimal WCS for a set of solar WCSes - pytest.importorskip("sunpy", minversion="2.1.0") + pytest.importorskip("sunpy", minversion="6.0.1") # Make sure the WCS <-> frame functions are registered diff --git a/reproject/tests/data/aia_171_level1.asdf b/reproject/tests/data/aia_171_level1.asdf index a74307161..001010942 100644 --- a/reproject/tests/data/aia_171_level1.asdf +++ b/reproject/tests/data/aia_171_level1.asdf @@ -12,10 +12,6 @@ history: extension_uri: asdf://asdf-format.org/core/extensions/core-1.5.0 manifest_software: !core/software-1.0.0 {name: asdf_standard, version: 1.1.1} software: !core/software-1.0.0 {name: asdf-astropy, version: 0.6.1} - - !core/extension_metadata-1.0.0 - extension_class: asdf.extension._manifest.ManifestExtension - extension_uri: asdf://astropy.org/astropy/extensions/units-1.0.0 - software: !core/software-1.0.0 {name: asdf-astropy, version: 0.6.1} - !core/extension_metadata-1.0.0 extension_class: asdf.extension._manifest.ManifestExtension extension_uri: asdf://asdf-format.org/astronomy/gwcs/extensions/gwcs-1.2.0 @@ -26,6 +22,10 @@ history: extension_uri: asdf://asdf-format.org/transform/extensions/transform-1.5.0 manifest_software: !core/software-1.0.0 {name: asdf_transform_schemas, version: 0.5.0} software: !core/software-1.0.0 {name: asdf-astropy, version: 0.6.1} + - !core/extension_metadata-1.0.0 + extension_class: asdf.extension._manifest.ManifestExtension + extension_uri: asdf://astropy.org/astropy/extensions/units-1.0.0 + software: !core/software-1.0.0 {name: asdf-astropy, version: 0.6.1} - !core/extension_metadata-1.0.0 extension_class: asdf.extension._manifest.ManifestExtension extension_uri: asdf://sunpy.org/extensions/sunpy-1.1.1 diff --git a/reproject/tests/test_high_level.py b/reproject/tests/test_high_level.py index 90d23ae51..6bdb2cec8 100644 --- a/reproject/tests/test_high_level.py +++ b/reproject/tests/test_high_level.py @@ -279,7 +279,7 @@ def test_dask_schedulers(reproject_function, scheduler, wcs_type): wcs_out = WCS(hdu_out.header) shape_out = hdu_out.data.shape elif wcs_type == "gwcs": - pytest.importorskip("sunpy", minversion="2.1.0") + pytest.importorskip("sunpy", minversion="6.0.1") asdf = pytest.importorskip("asdf") if reproject_function == reproject_exact: pytest.skip() From 1e5c275f958a817949f7b0891141fbb3a429022b Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Fri, 11 Oct 2024 13:10:01 +0100 Subject: [PATCH 350/366] Skip aia_test_data figure if sunpy is not available --- reproject/conftest.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/reproject/conftest.py b/reproject/conftest.py index e9360b7ce..d8e7ecf2b 100644 --- a/reproject/conftest.py +++ b/reproject/conftest.py @@ -228,6 +228,8 @@ def valid_celestial_output_projections(request, simple_celestial_wcs): @pytest.fixture(params=["fits", "asdf"]) def aia_test_data(request): + pytest.importorskip("sunpy", minversion="6.0.1") + from sunpy.coordinates.ephemeris import get_body_heliographic_stonyhurst from sunpy.map import Map From cd6ec244bfc0e8ec84d4b0c74442e61161294c91 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Nov 2024 17:50:01 +0000 Subject: [PATCH 351/366] Bump actions/checkout from 4.2.0 to 4.2.2 in the actions group Bumps the actions group with 1 update: [actions/checkout](https://github.com/actions/checkout). Updates `actions/checkout` from 4.2.0 to 4.2.2 - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/d632683dd7b4114ad314bca15554477dd762a938...11bd71901bbe5b1630ceea73d27597364c9af683) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-patch dependency-group: actions ... Signed-off-by: dependabot[bot] --- .github/workflows/update-changelog.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/update-changelog.yaml b/.github/workflows/update-changelog.yaml index 7347b6e17..60ff63ba2 100644 --- a/.github/workflows/update-changelog.yaml +++ b/.github/workflows/update-changelog.yaml @@ -14,7 +14,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: ref: main From 4abd56203faa84adb1bf56e534e583bec81ec164 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 18 Nov 2024 16:28:49 +0000 Subject: [PATCH 352/366] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/pre-commit/pre-commit-hooks: v4.6.0 → v5.0.0](https://github.com/pre-commit/pre-commit-hooks/compare/v4.6.0...v5.0.0) - [github.com/psf/black-pre-commit-mirror: 24.8.0 → 24.10.0](https://github.com/psf/black-pre-commit-mirror/compare/24.8.0...24.10.0) - [github.com/astral-sh/ruff-pre-commit: v0.6.7 → v0.7.4](https://github.com/astral-sh/ruff-pre-commit/compare/v0.6.7...v0.7.4) --- .pre-commit-config.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index b0aafe6a0..d6be7ed50 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.6.0 + rev: v5.0.0 hooks: - id: check-added-large-files - id: check-case-conflict @@ -52,7 +52,7 @@ repos: exclude: ".*(data.*|extern.*|cextern)$" - repo: https://github.com/psf/black-pre-commit-mirror - rev: 24.8.0 + rev: 24.10.0 hooks: - id: black @@ -77,7 +77,7 @@ repos: - tomli - repo: https://github.com/astral-sh/ruff-pre-commit - rev: "v0.6.7" + rev: "v0.7.4" hooks: - id: ruff args: ["--fix", "--show-fixes"] From 9e65ae19fd3a0f5bdb4d7fd1b4e5f14c0ac590ef Mon Sep 17 00:00:00 2001 From: astrofrog Date: Tue, 19 Nov 2024 00:11:58 +0000 Subject: [PATCH 353/366] Update CHANGELOG --- CHANGES.md | 83 ++++++++++++++++++++++++++++++++---------------------- 1 file changed, 50 insertions(+), 33 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 012674386..a87eeaf81 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,20 @@ +## v0.14.1 - 2024-11-19 + + +### What's Changed + +#### Bug Fixes + +* Enforce readonly mode for memmap when reading input array by @olebole in https://github.com/astropy/reproject/pull/461 +* Fix Continuous Integration folllowing changes in Sunpy 6.0.0 and 6.0.1 by @astrofrog in https://github.com/astropy/reproject/pull/472 + +#### Other Changes + +* MNT: Use hash for Action workflow versions and update if needed by @pllim in https://github.com/astropy/reproject/pull/469 +* Bump actions/checkout from 4.2.0 to 4.2.2 in the actions group by @dependabot in https://github.com/astropy/reproject/pull/476 + +**Full Changelog**: https://github.com/astropy/reproject/compare/v0.14.0...v0.14.1 + ## v0.14.0 - 2024-07-26 @@ -199,9 +216,9 @@ ## 0.9 - 2022-09-02 - Drop support for Python 3.7. -- +- - Infrastructure and packaging updates. -- +- - Made many improvements, bug fixes, and significant speed-ups for the adaptive - resampling algorithm, `reproject_adaptive`. These bug fixes may cause - changes to the reprojected images, which are typically negligible. @@ -209,13 +226,13 @@ - Gaussian filter kernel, a menu of boundary-handling modes, and a - `center_jacobian` flag to trade speed for accuracy with rapidly-varying - transformations. -- +- - Added a `roundtrip_coords` argument to `reproject_adaptive` and - `reproject_interp`. By default, all coordinate transformations are run in - both directions to handle some situations where they are ambiguous. This can - be disabled by setting `roundtrip_coords=False` which may offer a - significant speed increase. -- +- ## 0.8 - 2021-08-11 @@ -224,34 +241,34 @@ - possible to solve for the optimal WCS for a set of images that couldn't fit - into memory all at once, since the actual data aren't needed for optimal WCS - determination. [#242] -- +- - Fix implementation of `hdu_weights` in `reproject_and_coadd`. [#249] -- +- ## 0.7.1 - 2020-05-29 - Fixed compatibility with Astropy 4.1. [#234] -- +- - Updated minimum requirement for SciPy. [#236] -- +- ## 0.7 - 2020-04-02 - Made C extension in overlapArea.c thread-safe by removing global - variables. [#211] -- +- - Made it possible to control whether to output debugging information - from overlapArea.c by setting DEBUG_OVERLAP_AREA=1 at build-time. [#211] -- +- - Fix compatibility with astropy v4.0.1. [#227] -- +- - Disable parallelization by default in `reproject_exact` - this can be - enabled with `parallel=True`. [#227] -- +- - Fixed a bug with `reproject_exact` with `parallel=False` and - `return_footprint=False`, which caused the footprint to be returned - anyway. [#227] -- +- - The infrastructure of the package has been updated in line with the - APE 17 roadmap (https://github.com/astropy/astropy-APEs/blob/main/APE17.rst). - The main changes are that the `python setup.py test` and @@ -260,7 +277,7 @@ - (https://tox.readthedocs.io) package and run `tox -e test` and - `tox -e build_docs`. It is also possible to run pytest and sphinx - directly. [#228] -- +- ## 0.6 - 2019-11-01 @@ -269,20 +286,20 @@ - `independent_celestial_slices=` argument to `reproject_interp` has - been deprecated since it is no longer needed, as transformations are - automatically done in the most efficient way possible. [#166] -- +- - Include a warning for high resolution images with `reproject_exact`, - since if the pixels are <0.05", precision issues can occur. [#200] -- +- - Added a new `reproject_and_coadd` function for doing mosaicking of - individual images, and added section in documentation about mosaicking. - [#186] -- +- - Added a new reproject.adaptive sub-package that implements the DeForest - (2004) algorithm for reprojection. [#52] -- +- - Fixed a bug that caused 'exact' reprojection results to have numerical - issues when doing identity transformations. [#190] -- +- ## 0.5.1 - 2019-09-01 @@ -293,33 +310,33 @@ - Improve parse_output_projection to make it so that the output projection - can be specified as a filename. [#150] -- +- - Fixed a bug that caused HEALPix maps in RING order to not be correctly - interpreted. [#163] -- +- - Make it possible to specify the output array for reprojection using the - `output_array=` keyword argument. [#115] -- +- ## 0.4 - 2018-01-29 - Refactored HEALPix reprojection code to use the astropy-healpix package - instead of healpy. [#139] -- +- - Added the ability to specify an output array in `reproject_interp`, which - permits the use of memory-mapped arrays and therefore provides the capability - to handle data cubes much larger than memory [#115] -- +- - Fix test 32-bit test failures. [#146] -- +- - Fix an issue with reprojecting images where there are two solutions along - the line of sight by forcing round-tripping of coordinate conversions [#129] -- +- - Explicitly define default HDU as 0 for normal reprojection and 1 for - HEALPix reprojection. [#119] -- +- - Added a function to find the optimal WCS for a set of images. [#136, #137] -- +- ## 0.3.2 - 2017-10-22 @@ -330,22 +347,22 @@ ## 0.3.1 - 2016-07-07 - Include missing license file in tarball. -- +- - Updated documentation to remove warnings about early versions. -- +- ## 0.3 - 2016-07-06 - Allow users to pass a `field=` option to `reproject_from_healpix` - to access different fields in a HEALPIX file. [#86] -- +- - Significant improvements to performance when the input data is a large - memory-mapped array. [#105] -- +- - Significant refactoring of interpolating reprojection to improve support for - n-dimensional arrays, optionally including two celestial axes (in which - case the coordinate transformation is taken into account). [#96, #102] -- +- ## 0.2 - 2015-10-29 From 7c0e1557088170f7ba4f7c36ddc44466191a13c0 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 25 Nov 2024 16:28:18 +0000 Subject: [PATCH 354/366] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.7.4 → v0.8.0](https://github.com/astral-sh/ruff-pre-commit/compare/v0.7.4...v0.8.0) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index d6be7ed50..06729ba6c 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -77,7 +77,7 @@ repos: - tomli - repo: https://github.com/astral-sh/ruff-pre-commit - rev: "v0.7.4" + rev: "v0.8.0" hooks: - id: ruff args: ["--fix", "--show-fixes"] From 5fbcd3c763e7ee18a115b2d1c42d798802aaf57b Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 25 Nov 2024 16:28:34 +0000 Subject: [PATCH 355/366] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- CHANGES.md | 68 +++++++++++++++++++++++++++--------------------------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index a87eeaf81..5f2996563 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -6,7 +6,7 @@ #### Bug Fixes * Enforce readonly mode for memmap when reading input array by @olebole in https://github.com/astropy/reproject/pull/461 -* Fix Continuous Integration folllowing changes in Sunpy 6.0.0 and 6.0.1 by @astrofrog in https://github.com/astropy/reproject/pull/472 +* Fix Continuous Integration following changes in Sunpy 6.0.0 and 6.0.1 by @astrofrog in https://github.com/astropy/reproject/pull/472 #### Other Changes @@ -216,9 +216,9 @@ ## 0.9 - 2022-09-02 - Drop support for Python 3.7. -- +- - Infrastructure and packaging updates. -- +- - Made many improvements, bug fixes, and significant speed-ups for the adaptive - resampling algorithm, `reproject_adaptive`. These bug fixes may cause - changes to the reprojected images, which are typically negligible. @@ -226,13 +226,13 @@ - Gaussian filter kernel, a menu of boundary-handling modes, and a - `center_jacobian` flag to trade speed for accuracy with rapidly-varying - transformations. -- +- - Added a `roundtrip_coords` argument to `reproject_adaptive` and - `reproject_interp`. By default, all coordinate transformations are run in - both directions to handle some situations where they are ambiguous. This can - be disabled by setting `roundtrip_coords=False` which may offer a - significant speed increase. -- +- ## 0.8 - 2021-08-11 @@ -241,34 +241,34 @@ - possible to solve for the optimal WCS for a set of images that couldn't fit - into memory all at once, since the actual data aren't needed for optimal WCS - determination. [#242] -- +- - Fix implementation of `hdu_weights` in `reproject_and_coadd`. [#249] -- +- ## 0.7.1 - 2020-05-29 - Fixed compatibility with Astropy 4.1. [#234] -- +- - Updated minimum requirement for SciPy. [#236] -- +- ## 0.7 - 2020-04-02 - Made C extension in overlapArea.c thread-safe by removing global - variables. [#211] -- +- - Made it possible to control whether to output debugging information - from overlapArea.c by setting DEBUG_OVERLAP_AREA=1 at build-time. [#211] -- +- - Fix compatibility with astropy v4.0.1. [#227] -- +- - Disable parallelization by default in `reproject_exact` - this can be - enabled with `parallel=True`. [#227] -- +- - Fixed a bug with `reproject_exact` with `parallel=False` and - `return_footprint=False`, which caused the footprint to be returned - anyway. [#227] -- +- - The infrastructure of the package has been updated in line with the - APE 17 roadmap (https://github.com/astropy/astropy-APEs/blob/main/APE17.rst). - The main changes are that the `python setup.py test` and @@ -277,7 +277,7 @@ - (https://tox.readthedocs.io) package and run `tox -e test` and - `tox -e build_docs`. It is also possible to run pytest and sphinx - directly. [#228] -- +- ## 0.6 - 2019-11-01 @@ -286,20 +286,20 @@ - `independent_celestial_slices=` argument to `reproject_interp` has - been deprecated since it is no longer needed, as transformations are - automatically done in the most efficient way possible. [#166] -- +- - Include a warning for high resolution images with `reproject_exact`, - since if the pixels are <0.05", precision issues can occur. [#200] -- +- - Added a new `reproject_and_coadd` function for doing mosaicking of - individual images, and added section in documentation about mosaicking. - [#186] -- +- - Added a new reproject.adaptive sub-package that implements the DeForest - (2004) algorithm for reprojection. [#52] -- +- - Fixed a bug that caused 'exact' reprojection results to have numerical - issues when doing identity transformations. [#190] -- +- ## 0.5.1 - 2019-09-01 @@ -310,33 +310,33 @@ - Improve parse_output_projection to make it so that the output projection - can be specified as a filename. [#150] -- +- - Fixed a bug that caused HEALPix maps in RING order to not be correctly - interpreted. [#163] -- +- - Make it possible to specify the output array for reprojection using the - `output_array=` keyword argument. [#115] -- +- ## 0.4 - 2018-01-29 - Refactored HEALPix reprojection code to use the astropy-healpix package - instead of healpy. [#139] -- +- - Added the ability to specify an output array in `reproject_interp`, which - permits the use of memory-mapped arrays and therefore provides the capability - to handle data cubes much larger than memory [#115] -- +- - Fix test 32-bit test failures. [#146] -- +- - Fix an issue with reprojecting images where there are two solutions along - the line of sight by forcing round-tripping of coordinate conversions [#129] -- +- - Explicitly define default HDU as 0 for normal reprojection and 1 for - HEALPix reprojection. [#119] -- +- - Added a function to find the optimal WCS for a set of images. [#136, #137] -- +- ## 0.3.2 - 2017-10-22 @@ -347,22 +347,22 @@ ## 0.3.1 - 2016-07-07 - Include missing license file in tarball. -- +- - Updated documentation to remove warnings about early versions. -- +- ## 0.3 - 2016-07-06 - Allow users to pass a `field=` option to `reproject_from_healpix` - to access different fields in a HEALPIX file. [#86] -- +- - Significant improvements to performance when the input data is a large - memory-mapped array. [#105] -- +- - Significant refactoring of interpolating reprojection to improve support for - n-dimensional arrays, optionally including two celestial axes (in which - case the coordinate transformation is taken into account). [#96, #102] -- +- ## 0.2 - 2015-10-29 From 6a7777c0f2bb90a0726992c420be497f1899b1f4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 Dec 2024 17:43:30 +0000 Subject: [PATCH 356/366] Bump the actions group with 2 updates Bumps the actions group with 2 updates: [OpenAstronomy/github-actions-workflows](https://github.com/openastronomy/github-actions-workflows) and [stefanzweifel/changelog-updater-action](https://github.com/stefanzweifel/changelog-updater-action). Updates `OpenAstronomy/github-actions-workflows` from 1.13.0 to 1.14.0 - [Release notes](https://github.com/openastronomy/github-actions-workflows/releases) - [Commits](https://github.com/openastronomy/github-actions-workflows/compare/924441154cf3053034c6513d5e06c69d262fb9a6...d68193b68216da64eafaa618f53c59f5d271c56e) Updates `stefanzweifel/changelog-updater-action` from 1.11.0 to 1.12.0 - [Release notes](https://github.com/stefanzweifel/changelog-updater-action/releases) - [Changelog](https://github.com/stefanzweifel/changelog-updater-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/stefanzweifel/changelog-updater-action/compare/61ce794778aa787ea8d204d9fe2928543cb2fe40...a938690fad7edf25368f37e43a1ed1b34303eb36) --- updated-dependencies: - dependency-name: OpenAstronomy/github-actions-workflows dependency-type: direct:production update-type: version-update:semver-minor dependency-group: actions - dependency-name: stefanzweifel/changelog-updater-action dependency-type: direct:production update-type: version-update:semver-minor dependency-group: actions ... Signed-off-by: dependabot[bot] --- .github/workflows/ci_workflows.yml | 4 ++-- .github/workflows/update-changelog.yaml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci_workflows.yml b/.github/workflows/ci_workflows.yml index bb62be62e..3688769be 100644 --- a/.github/workflows/ci_workflows.yml +++ b/.github/workflows/ci_workflows.yml @@ -15,7 +15,7 @@ concurrency: jobs: tests: - uses: OpenAstronomy/github-actions-workflows/.github/workflows/tox.yml@924441154cf3053034c6513d5e06c69d262fb9a6 # v1.13.0 + uses: OpenAstronomy/github-actions-workflows/.github/workflows/tox.yml@d68193b68216da64eafaa618f53c59f5d271c56e # v1.14.0 with: envs: | - macos: py310-test-oldestdeps @@ -35,7 +35,7 @@ jobs: publish: needs: tests - uses: OpenAstronomy/github-actions-workflows/.github/workflows/publish.yml@924441154cf3053034c6513d5e06c69d262fb9a6 # v1.13.0 + uses: OpenAstronomy/github-actions-workflows/.github/workflows/publish.yml@d68193b68216da64eafaa618f53c59f5d271c56e # v1.14.0 with: test_extras: test test_command: pytest -p no:warnings --pyargs reproject diff --git a/.github/workflows/update-changelog.yaml b/.github/workflows/update-changelog.yaml index 60ff63ba2..c5bdf4e0d 100644 --- a/.github/workflows/update-changelog.yaml +++ b/.github/workflows/update-changelog.yaml @@ -19,7 +19,7 @@ jobs: ref: main - name: Update Changelog - uses: stefanzweifel/changelog-updater-action@61ce794778aa787ea8d204d9fe2928543cb2fe40 # v1.11.0 + uses: stefanzweifel/changelog-updater-action@a938690fad7edf25368f37e43a1ed1b34303eb36 # v1.12.0 with: release-notes: ${{ github.event.release.body }} latest-version: ${{ github.event.release.name }} From 0b0f37821a69cf30a585f756846499cb6e5b8cab Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Jan 2025 17:47:42 +0000 Subject: [PATCH 357/366] Bump OpenAstronomy/github-actions-workflows in the actions group Bumps the actions group with 1 update: [OpenAstronomy/github-actions-workflows](https://github.com/openastronomy/github-actions-workflows). Updates `OpenAstronomy/github-actions-workflows` from 1.14.0 to 1.15.0 - [Release notes](https://github.com/openastronomy/github-actions-workflows/releases) - [Commits](https://github.com/openastronomy/github-actions-workflows/compare/d68193b68216da64eafaa618f53c59f5d271c56e...9f1f43251dde69da8613ea8e11144f05cdea41d5) --- updated-dependencies: - dependency-name: OpenAstronomy/github-actions-workflows dependency-type: direct:production update-type: version-update:semver-minor dependency-group: actions ... Signed-off-by: dependabot[bot] --- .github/workflows/ci_workflows.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci_workflows.yml b/.github/workflows/ci_workflows.yml index 3688769be..c830a78b6 100644 --- a/.github/workflows/ci_workflows.yml +++ b/.github/workflows/ci_workflows.yml @@ -15,7 +15,7 @@ concurrency: jobs: tests: - uses: OpenAstronomy/github-actions-workflows/.github/workflows/tox.yml@d68193b68216da64eafaa618f53c59f5d271c56e # v1.14.0 + uses: OpenAstronomy/github-actions-workflows/.github/workflows/tox.yml@9f1f43251dde69da8613ea8e11144f05cdea41d5 # v1.15.0 with: envs: | - macos: py310-test-oldestdeps @@ -35,7 +35,7 @@ jobs: publish: needs: tests - uses: OpenAstronomy/github-actions-workflows/.github/workflows/publish.yml@d68193b68216da64eafaa618f53c59f5d271c56e # v1.14.0 + uses: OpenAstronomy/github-actions-workflows/.github/workflows/publish.yml@9f1f43251dde69da8613ea8e11144f05cdea41d5 # v1.15.0 with: test_extras: test test_command: pytest -p no:warnings --pyargs reproject From 08b2a6c24adc5ff4c37cde35b29ab6550a7f1514 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 6 Jan 2025 16:30:17 +0000 Subject: [PATCH 358/366] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.8.0 → v0.8.6](https://github.com/astral-sh/ruff-pre-commit/compare/v0.8.0...v0.8.6) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 06729ba6c..4c3bc349d 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -77,7 +77,7 @@ repos: - tomli - repo: https://github.com/astral-sh/ruff-pre-commit - rev: "v0.8.0" + rev: "v0.8.6" hooks: - id: ruff args: ["--fix", "--show-fixes"] From 4e820d9de78e208751fc0bc8cb30c100d502acb9 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Sat, 1 Feb 2025 13:50:39 +0000 Subject: [PATCH 359/366] Fix a bug that caused big-endian dask arrays to not be reprojected correctly --- reproject/interpolation/tests/test_core.py | 27 ++++++++++++++++++++++ reproject/utils.py | 2 +- 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/reproject/interpolation/tests/test_core.py b/reproject/interpolation/tests/test_core.py index 1a2d1f208..992efe4dc 100644 --- a/reproject/interpolation/tests/test_core.py +++ b/reproject/interpolation/tests/test_core.py @@ -954,3 +954,30 @@ def test_auto_block_size(): assert array_out.chunksize[0] == 350 assert footprint_out.chunksize[0] == 350 + + +def test_bigendian_dask(): + + # Regression test for an endianness issue that occurred when the input was + # passed in as (dask_array, wcs) and the dask array was big endian. + + array_in_le = da.ones((350, 250, 150), dtype=">f8") + array_in_be = da.ones((350, 250, 150), dtype=" Date: Sat, 1 Feb 2025 14:09:28 +0000 Subject: [PATCH 360/366] Pin zarr to <3 for now as it does not properly support big-endian arrays --- pyproject.toml | 2 +- tox.ini | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 509aca3d7..4fc32813a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -16,7 +16,7 @@ dependencies = [ "scipy>=1.9", "dask[array]>=2021.8", "cloudpickle", - "zarr", + "zarr<3", "fsspec", ] dynamic = ["version"] diff --git a/tox.ini b/tox.ini index 74ff982b3..70f5e9449 100644 --- a/tox.ini +++ b/tox.ini @@ -36,7 +36,6 @@ deps = #devdeps: asdf-astropy @ git+https://github.com/astropy/asdf-astropy.git devdeps: gwcs @ git+https://github.com/spacetelescope/gwcs.git devdeps: sunpy[map] @ git+https://github.com/sunpy/sunpy.git - devdeps: zarr<3 extras = test From d4764c2adfeab3febde7d2e6071a74bf0a49bde3 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Sat, 1 Feb 2025 14:16:11 +0000 Subject: [PATCH 361/366] Cast dask array to float to avoid issues with zarr 3 --- pyproject.toml | 2 +- reproject/utils.py | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 4fc32813a..509aca3d7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -16,7 +16,7 @@ dependencies = [ "scipy>=1.9", "dask[array]>=2021.8", "cloudpickle", - "zarr<3", + "zarr", "fsspec", ] dynamic = ["version"] diff --git a/reproject/utils.py b/reproject/utils.py index df5bf5b78..f65303cd4 100644 --- a/reproject/utils.py +++ b/reproject/utils.py @@ -31,6 +31,11 @@ def _dask_to_numpy_memmap(dask_array, tmp_dir): if isinstance(dask_array.ravel()[0].compute(), da.Array): dask_array = dask_array.compute() + # Cast the dask array to regular float for two reasons - first, zarr 3.0.0 + # and later doesn't support big-endian arrays, and also we need to anyway + # make a native float memory mapped array below. + dask_array = dask_array.astype(float, copy=False) + # NOTE: here we use a new TemporaryDirectory context manager for the zarr # array because we can remove the temporary directory straight away after # converting the input to a Numpy memory mapped array. From 9a487fe21cb9a0fe5e60620328b4951bb393637b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 1 Feb 2025 18:00:49 +0000 Subject: [PATCH 362/366] Bump the actions group with 2 updates Bumps the actions group with 2 updates: [OpenAstronomy/github-actions-workflows](https://github.com/openastronomy/github-actions-workflows) and [stefanzweifel/git-auto-commit-action](https://github.com/stefanzweifel/git-auto-commit-action). Updates `OpenAstronomy/github-actions-workflows` from 1.15.0 to 1.16.0 - [Release notes](https://github.com/openastronomy/github-actions-workflows/releases) - [Commits](https://github.com/openastronomy/github-actions-workflows/compare/9f1f43251dde69da8613ea8e11144f05cdea41d5...8c0fde6f7e926df6ed7057255d29afa9c1ad5320) Updates `stefanzweifel/git-auto-commit-action` from 5.0.1 to 5.1.0 - [Release notes](https://github.com/stefanzweifel/git-auto-commit-action/releases) - [Changelog](https://github.com/stefanzweifel/git-auto-commit-action/blob/master/CHANGELOG.md) - [Commits](https://github.com/stefanzweifel/git-auto-commit-action/compare/8621497c8c39c72f3e2a999a26b4ca1b5058a842...e348103e9026cc0eee72ae06630dbe30c8bf7a79) --- updated-dependencies: - dependency-name: OpenAstronomy/github-actions-workflows dependency-type: direct:production update-type: version-update:semver-minor dependency-group: actions - dependency-name: stefanzweifel/git-auto-commit-action dependency-type: direct:production update-type: version-update:semver-minor dependency-group: actions ... Signed-off-by: dependabot[bot] --- .github/workflows/ci_workflows.yml | 4 ++-- .github/workflows/update-changelog.yaml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci_workflows.yml b/.github/workflows/ci_workflows.yml index c830a78b6..82ede2035 100644 --- a/.github/workflows/ci_workflows.yml +++ b/.github/workflows/ci_workflows.yml @@ -15,7 +15,7 @@ concurrency: jobs: tests: - uses: OpenAstronomy/github-actions-workflows/.github/workflows/tox.yml@9f1f43251dde69da8613ea8e11144f05cdea41d5 # v1.15.0 + uses: OpenAstronomy/github-actions-workflows/.github/workflows/tox.yml@8c0fde6f7e926df6ed7057255d29afa9c1ad5320 # v1.16.0 with: envs: | - macos: py310-test-oldestdeps @@ -35,7 +35,7 @@ jobs: publish: needs: tests - uses: OpenAstronomy/github-actions-workflows/.github/workflows/publish.yml@9f1f43251dde69da8613ea8e11144f05cdea41d5 # v1.15.0 + uses: OpenAstronomy/github-actions-workflows/.github/workflows/publish.yml@8c0fde6f7e926df6ed7057255d29afa9c1ad5320 # v1.16.0 with: test_extras: test test_command: pytest -p no:warnings --pyargs reproject diff --git a/.github/workflows/update-changelog.yaml b/.github/workflows/update-changelog.yaml index c5bdf4e0d..0418b4a65 100644 --- a/.github/workflows/update-changelog.yaml +++ b/.github/workflows/update-changelog.yaml @@ -26,7 +26,7 @@ jobs: path-to-changelog: CHANGES.md - name: Commit updated CHANGELOG - uses: stefanzweifel/git-auto-commit-action@8621497c8c39c72f3e2a999a26b4ca1b5058a842 # v5.0.1 + uses: stefanzweifel/git-auto-commit-action@e348103e9026cc0eee72ae06630dbe30c8bf7a79 # v5.1.0 with: branch: main commit_message: Update CHANGELOG From 172ff1c3f4c67928c4f04dff37deaf89b24be44f Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Sat, 1 Feb 2025 18:09:17 +0000 Subject: [PATCH 363/366] Expand test to check for 4-byte floats --- reproject/interpolation/tests/test_core.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/reproject/interpolation/tests/test_core.py b/reproject/interpolation/tests/test_core.py index 992efe4dc..707445a9f 100644 --- a/reproject/interpolation/tests/test_core.py +++ b/reproject/interpolation/tests/test_core.py @@ -956,13 +956,14 @@ def test_auto_block_size(): assert footprint_out.chunksize[0] == 350 -def test_bigendian_dask(): +@pytest.mark.parametrize('itemsize', (4, 8)) +def test_bigendian_dask(itemsize): # Regression test for an endianness issue that occurred when the input was # passed in as (dask_array, wcs) and the dask array was big endian. - array_in_le = da.ones((350, 250, 150), dtype=">f8") - array_in_be = da.ones((350, 250, 150), dtype="f{itemsize}") + array_in_be = da.ones((350, 250, 150), dtype=f" Date: Sat, 1 Feb 2025 18:10:54 +0000 Subject: [PATCH 364/366] Fix codestyle --- reproject/interpolation/tests/test_core.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reproject/interpolation/tests/test_core.py b/reproject/interpolation/tests/test_core.py index 707445a9f..63171e1e0 100644 --- a/reproject/interpolation/tests/test_core.py +++ b/reproject/interpolation/tests/test_core.py @@ -956,7 +956,7 @@ def test_auto_block_size(): assert footprint_out.chunksize[0] == 350 -@pytest.mark.parametrize('itemsize', (4, 8)) +@pytest.mark.parametrize("itemsize", (4, 8)) def test_bigendian_dask(itemsize): # Regression test for an endianness issue that occurred when the input was From 56c5031cca4b45f4193d24030428eb83c2dec529 Mon Sep 17 00:00:00 2001 From: "P. L. Lim" <2090236+pllim@users.noreply.github.com> Date: Tue, 11 Feb 2025 18:26:58 -0500 Subject: [PATCH 365/366] MNT: Replace ubuntu-20.04 with ubuntu-22.04 --- .readthedocs.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.readthedocs.yaml b/.readthedocs.yaml index b4984aa9e..73b602d3d 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -1,6 +1,6 @@ version: 2 build: - os: ubuntu-20.04 + os: ubuntu-22.04 tools: python: "3.10" apt_packages: From 0ef4287c301806090e591bca06a9797d96f25662 Mon Sep 17 00:00:00 2001 From: Stuart Mumford Date: Wed, 12 Mar 2025 10:19:01 +0000 Subject: [PATCH 366/366] Add py313 and aarch64 --- .github/workflows/ci_workflows.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci_workflows.yml b/.github/workflows/ci_workflows.yml index 82ede2035..591c59a46 100644 --- a/.github/workflows/ci_workflows.yml +++ b/.github/workflows/ci_workflows.yml @@ -21,13 +21,17 @@ jobs: - macos: py310-test-oldestdeps - macos: py311-test - macos: py312-test + - macos: py313-test - linux: py310-test-oldestdeps - linux: py311-test - linux: py312-test + runs-on: ubuntu-24.04-arm + - linux: py313-test - linux: py312-test-devdeps - windows: py310-test-oldestdeps - windows: py311-test - windows: py312-test + - windows: py313-test libraries: | apt: - libopenblas-dev @@ -41,7 +45,8 @@ jobs: test_command: pytest -p no:warnings --pyargs reproject targets: | - cp*-manylinux_x86_64 - - cp*-manylinux_aarch64 + - target: cp*-manylinux_aarch64 + runs-on: ubuntu-24.04-arm - cp*-macosx_x86_64 - cp*-macosx_arm64 - cp*-win_amd64

C`P+_5)PaI@AtE9 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