Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions .github/workflows/ci_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -144,3 +144,21 @@ jobs:
token: ${{ secrets.CODECOV_TOKEN }}
files: ./coverage.xml
verbose: true
asdf-schemas:
name: Test ASDF Schemas
runs-on: ubuntu-latest
steps:
- name: Check out repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
fetch-depth: 0
path: photutils
- name: Set up Python 3.13
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version: '3.13'
- name: Install dependencies
run: cd photutils && python -m pip install -e .[test]
- name: Validate ASDF schemas
run: cd photutils && pytest photutils/resources/schemas
7 changes: 7 additions & 0 deletions photutils/converters/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
from . functional_models import AiryDiskPSFConverter
from . apertures import CircularApertureConverter

__all__ = [
'AiryDiskPSFConverter',
'CircularApertureConverter',
]
37 changes: 37 additions & 0 deletions photutils/converters/apertures.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Licensed under a 3-clause BSD style license - see LICENSE.rst

"""
Converters to and from the ASDF data format for photutils apertures.
"""

from asdf.extension import Converter

__all__ = ['CircularApertureConverter']


class CircularApertureConverter(Converter):
"""
ASDF converter for CircularAperture.
"""

tags = ('tag:astropy.org:photutils/aperture/circular_aperture-*',)
types = ('photutils.aperture.circle.CircularAperture',)

def to_yaml_tree(self, obj, tag, ctx): # noqa: ARG002
if obj.positions.shape == (2,):
pos = obj.positions.tolist()
else:
pos = obj.positions

return {
'positions': pos,
'r': obj.r,
}

def from_yaml_tree(self, node, tag, ctx): # noqa: ARG002
from photutils.aperture.circle import CircularAperture

return CircularAperture(
positions=node['positions'],
r=node['r'],
)
39 changes: 39 additions & 0 deletions photutils/converters/functional_models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Licensed under a 3-clause BSD style license - see LICENSE.rst

"""
Converters to and from the ASDF format for photutils.psf.functional_models.
"""

from asdf_astropy.converters.transform.core import (TransformConverterBase,
parameter_to_value)

__all__ = ['AiryDiskPSFConverter']


class AiryDiskPSFConverter(TransformConverterBase):
"""
Converter for AiryDiskPSF.
"""

tags = ('tag:astropy.org:photutils/psf/airy_disk_psf-*',)
types = ('photutils.psf.AiryDiskPSF',)

def to_yaml_tree_transform(self, model, tag, ctx): # noqa: ARG002
return {
'flux': parameter_to_value(model.flux),
'x_0': parameter_to_value(model.x_0),
'y_0': parameter_to_value(model.y_0),
'radius': parameter_to_value(model.radius),
'bbox_factor': model.bbox_factor,
}

def from_yaml_tree_transform(self, node, tag, ctx): # noqa: ARG002
from photutils.psf import AiryDiskPSF

return AiryDiskPSF(
flux=node['flux'],
x_0=node['x_0'],
y_0=node['y_0'],
radius=node['radius'],
bbox_factor=node['bbox_factor'],
)
Empty file.
30 changes: 30 additions & 0 deletions photutils/converters/tests/test_apertures.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Licensed under a 3-clause BSD style license - see LICENSE.rst

"""
Tests for the photutils aperture converters.
"""
import asdf
import numpy as np

from photutils.aperture import CircularAperture

apertures = [
CircularAperture(positions=[(1, 2), (3, 4)], r=5),
CircularAperture(positions=(5, 6), r=7),
]


def test_aperture_converters(tmp_path):
"""
Test that the aperture converters can round-trip an aperture object.
"""
for aperture in apertures:
with asdf.AsdfFile() as af:
af['aperture'] = aperture
af.write_to(tmp_path / 'aperture.asdf')

with asdf.open(tmp_path / 'aperture.asdf') as af:
aperture2 = af['aperture']

assert np.all(aperture.positions == aperture2.positions)
assert aperture.r == aperture2.r
35 changes: 35 additions & 0 deletions photutils/converters/tests/test_psf.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Licensed under a 3-clause BSD style license - see LICENSE.rst

"""
Tests for the photutils PSF converters.
"""
import asdf
from astropy import units as u

from photutils.psf import AiryDiskPSF

psfs = [
AiryDiskPSF(flux=1 * u.Jy, x_0=0 * u.arcsec, y_0=0 * u.arcsec,
radius=1 * u.arcsec, bbox_factor=2),
AiryDiskPSF(flux=2 * u.Jy, x_0=1 * u.arcsec, y_0=1 * u.arcsec,
radius=2 * u.arcsec, bbox_factor=3),
]


def test_psf_converters(tmp_path):
"""
Test that the PSF converters can round-trip a PSF object.
"""
for psf in psfs:
with asdf.AsdfFile() as af:
af['psf'] = psf
af.write_to(tmp_path / 'psf.asdf')

with asdf.open(tmp_path / 'psf.asdf') as af:
psf2 = af['psf']

assert psf.flux == psf2.flux
assert psf.x_0 == psf2.x_0
assert psf.y_0 == psf2.y_0
assert psf.radius == psf2.radius
assert psf.bbox_factor == psf2.bbox_factor
78 changes: 78 additions & 0 deletions photutils/extension.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# Licensed under a 3-clause BSD style license - see LICENSE.rst
"""
ASDF extension for photutils.
"""
import importlib.resources as importlib_resources

from asdf.extension import ManifestExtension
from asdf.resource import DirectoryResourceMapping

from .converters import apertures # import CircularApertureConverter
from .converters import functional_models # import AiryDiskPSFConverter

__all__ = [
'PHOTUTILS_APERTURE_CONVERTERS',
'PHOTUTILS_MANIFEST_URIS',
'PHOTUTILS_PSF_CONVERTERS',
]

PHOTUTILS_PSF_CONVERTERS = [
functional_models.AiryDiskPSFConverter(),
]

PHOTUTILS_APERTURE_CONVERTERS = [
apertures.CircularApertureConverter(),

]

PHOTUTILS_CONVERTERS = PHOTUTILS_PSF_CONVERTERS + PHOTUTILS_APERTURE_CONVERTERS


# The order here is important; asdf will prefer to use extensions
# that occur earlier in the list.
PHOTUTILS_MANIFEST_URIS = [
'asdf://astropy.org/photutils/manifests/photutils-1.0.0',
]


def get_extensions():
"""
Get the gwcs.converters extension.
This method is registered with the asdf.extensions entry point.

Returns
-------
list
A list of ASDF extensions.
"""
return [
ManifestExtension.from_uri(
uri,
converters=PHOTUTILS_CONVERTERS,
)
for uri in PHOTUTILS_MANIFEST_URIS
]


def get_resource_mappings():
"""
Get the resource mapping instances for the photutils schemas
and manifests. This method is registered with the
asdf.resource_mappings entry point.

Returns
-------
list
A list of collections.abc.Mapping of ASDF resource mappings.
"""
from . import resources

resources_root = importlib_resources.files(resources)

return [
DirectoryResourceMapping(resources_root / 'schemas',
'asdf://astropy.org/photutils/schemas',
recursive=True),
DirectoryResourceMapping(resources_root / 'manifests',
'asdf://astropy.org/photutils/manifests'),
]
Empty file added photutils/resources/__init__.py
Empty file.
14 changes: 14 additions & 0 deletions photutils/resources/manifests/photutils-1.0.0.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
id: asdf://astropy.org/photutils/manifests/photutils-1.0.0
extension_uri: asdf://astropy.org/photutils/extensions/photutils-1.0.0
title: Photutils extension 1.0.0
description: |-
A set of tags for serializing photutils objects.
tags:
- tag_uri: tag:astropy.org:photutils/aperture/circular_aperture-1.0.0
schema_uri: asdf://astropy.org/photutils/schemas/aperture/circular_aperture-1.0.0
title: CircularAperture
description: Circular aperture with one or more positions.
- tag_uri: tag:astropy.org:photutils/psf/airy_disk_psf-1.0.0
schema_uri: asdf://astropy.org/photutils/schemas/psf/airy_disk_psf-1.0.0
title: AiryDiskPSF
description: A 2D Airy disk PSF model.
38 changes: 38 additions & 0 deletions photutils/resources/schemas/aperture/circular_aperture-1.0.0.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
%YAML 1.1
---
$schema: "http://stsci.edu/schemas/yaml-schema/draft-01"
id: "asdf://astropy.org/photutils/schemas/aperture/circular_aperture-1.0.0"
title: CircularAperture
description: |
ASDF schema for serializing a CircularAperture with one or more positions.
examples:
-
- A CircularAperture with a single position and radius of 5 pixels.
- |
!<tag:astropy.org:photutils/aperture/circular_aperture-1.0.0>
positions: [1.0, 2.0]
r: 5.0

type: object
properties:
positions:
title: Aperture position(s).
description: |
The pixel coordinates of the aperture center(s) in one of the following formats:

* single ``(x, y)`` pair as a tuple, list, or `~numpy.ndarray`
* tuple, list, or `~numpy.ndarray` of ``(x, y)`` pairs
anyOf:
- type: array
items:
type: number
maxItems: 2
minItems: 2
- tag: "tag:stsci.edu:asdf/core/ndarray-1.*"
r:
title: Aperture radius.
description: |
The radius of the circular aperture in pixels.
type: number
required: [positions, r]
...
56 changes: 56 additions & 0 deletions photutils/resources/schemas/psf/airy_disk_psf-1.0.0.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
%YAML 1.1
---
$schema: "http://stsci.edu/schemas/yaml-schema/draft-01"
id: "asdf://astropy.org/photutils/schemas/psf/airy_disk_psf-1.0.0"
title: AiryDiskPSF
description: |
ASDF schema for serializing a 2D Airy disk PSF model.

examples:
-
- An AiryDiskPSF with a flux of 71.4, centered at (24.3, 25.2) pixels, and a radius of 5 pixels.
psf.AiryDiskPSF(flux=71.4, x_0=24.3, y_0=25.2, radius=5)
- |
!<tag:astropy.org:photutils/psf/airy_disk_psf-1.0.0>
bbox_factor: 10.0
bounds:
radius: [1.1754943508222875e-38, null]
fixed: {radius: true}
flux: 71.4
inputs: [x, y]
outputs: [z]
radius: 5.0
x_0: 24.3
y_0: 25.2


allOf:
- $ref: "http://stsci.edu/schemas/asdf/transform/transform-1.4.0"
- type: object
properties:
flux:
anyOf:
- tag: "tag:stsci.edu:asdf/unit/quantity-1.*"
- type: number
description: Total integrated flux of the PSF.
x_0:
anyOf:
- tag: "tag:stsci.edu:asdf/unit/quantity-1.*"
- type: number
description: "X coordinate of the PSF center (pixel coordinates)."
y_0:
anyOf:
- tag: "tag:stsci.edu:asdf/unit/quantity-1.*"
- type: number
description: "Y coordinate of the PSF center (pixel coordinates)."
radius:
anyOf:
- tag: "tag:stsci.edu:asdf/unit/quantity-1.*"
- type: number
description: "Characteristic radius of the Airy disk (e.g. first dark ring) in pixels."
bbox_factor:
type: number
description: Factor by which to multiply the radius to determine the size of the bounding box.

required: [flux, x_0, y_0, radius]
...
Loading
Loading