diff --git a/CHANGES.rst b/CHANGES.rst index df832402..4027b550 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -10,6 +10,8 @@ - require asdf 2.14.4 [#241] +- Add support for astropy.nddata.uncertainty classes [#239] + 0.6.1 (2024-04-05) ------------------ diff --git a/asdf_astropy/converters/__init__.py b/asdf_astropy/converters/__init__.py index e18b0b0a..92d7730a 100644 --- a/asdf_astropy/converters/__init__.py +++ b/asdf_astropy/converters/__init__.py @@ -19,6 +19,7 @@ "AstropyTableConverter", "AsdfTableConverter", "NdarrayMixinConverter", + "UncertaintyConverter", "TimeDeltaConverter", "TimeConverter", "CompoundConverter", @@ -52,6 +53,7 @@ SpectralCoordConverter, ) from .fits import AsdfFitsConverter, AstropyFitsConverter, FitsConverter +from .nddata import UncertaintyConverter from .table import AsdfTableConverter, AstropyTableConverter, ColumnConverter, NdarrayMixinConverter from .time import TimeConverter, TimeDeltaConverter from .transform import ( diff --git a/asdf_astropy/converters/nddata/__init__.py b/asdf_astropy/converters/nddata/__init__.py new file mode 100644 index 00000000..641fc093 --- /dev/null +++ b/asdf_astropy/converters/nddata/__init__.py @@ -0,0 +1,5 @@ +__all__ = [ + "UncertaintyConverter", +] + +from .uncertainty import UncertaintyConverter diff --git a/asdf_astropy/converters/nddata/tests/__init__.py b/asdf_astropy/converters/nddata/tests/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/asdf_astropy/converters/nddata/tests/test_uncertainty.py b/asdf_astropy/converters/nddata/tests/test_uncertainty.py new file mode 100644 index 00000000..5e4c817f --- /dev/null +++ b/asdf_astropy/converters/nddata/tests/test_uncertainty.py @@ -0,0 +1,37 @@ +import asdf +import numpy as np +import pytest +from astropy import units as u +from astropy.nddata import StdDevUncertainty, UnknownUncertainty, VarianceUncertainty + + +def create_uncertainty(): + uncert = np.arange(100).reshape(10, 10) + uncertainty_stddev_1 = StdDevUncertainty(uncert, unit="m") + uncertainty_stddev_2 = StdDevUncertainty([2], unit="m") + uncertainty_unknown_1 = UnknownUncertainty(uncert, unit="m") + uncertainty_unknown_2 = UnknownUncertainty([0.4], unit=u.adu) + uncertainty_variance_1 = VarianceUncertainty(uncert, unit="m") + uncertainty_variance_2 = VarianceUncertainty([0.4], unit=u.adu) + return [ + uncertainty_stddev_1, + uncertainty_stddev_2, + uncertainty_unknown_1, + uncertainty_unknown_2, + uncertainty_variance_1, + uncertainty_variance_2, + ] + + +@pytest.mark.parametrize("uncertainty", create_uncertainty()) +def test_uncertainty_serialization(uncertainty, tmp_path): + file_path = tmp_path / "test_uncertainty.asdf" + with asdf.AsdfFile() as af: + af["uncertainty"] = uncertainty + af.write_to(file_path) + + with asdf.open(file_path) as af: + loaded_uncert = af["uncertainty"] + assert type(loaded_uncert) == type(uncertainty) + assert (loaded_uncert._array == uncertainty._array).all() + assert loaded_uncert.unit == uncertainty.unit diff --git a/asdf_astropy/converters/nddata/uncertainty.py b/asdf_astropy/converters/nddata/uncertainty.py new file mode 100644 index 00000000..e807a2c7 --- /dev/null +++ b/asdf_astropy/converters/nddata/uncertainty.py @@ -0,0 +1,35 @@ +class UncertaintyConverter: + tags = ("tag:astropy.org:astropy/nddata/uncertainty-*",) + types = ( + "astropy.nddata.nduncertainty.StdDevUncertainty", + "astropy.nddata.nduncertainty.UnknownUncertainty", + "astropy.nddata.nduncertainty.VarianceUncertainty", + ) + + # Mapping of uncertainty class name (attribute of astropy.nddata) + # and code "name" stored in the data file. + # This will need to be separately versioned if the schema is updated. + _class_name_to_code = { + "StdDevUncertainty": "stddev", + "UnknownUncertainty": "unknown", + "VarianceUncertainty": "variance", + } + _class_code_to_name = {v: k for k, v in _class_name_to_code.items()} + + def from_yaml_tree(self, node, tag, ctx): + import astropy.nddata + + class_name = self._class_code_to_name[node["name"]] + + return getattr(astropy.nddata, class_name)(node["array"], unit=node.get("unit")) + + def to_yaml_tree(self, obj, tag, ctx): + node = { + "name": self._class_name_to_code[obj.__class__.__name__], + "array": obj.array, + } + + if obj.unit is not None: + node["unit"] = obj.unit + + return node diff --git a/asdf_astropy/extensions.py b/asdf_astropy/extensions.py index e3ea27e5..ffaf0904 100644 --- a/asdf_astropy/extensions.py +++ b/asdf_astropy/extensions.py @@ -12,6 +12,7 @@ from .converters.coordinates.sky_coord import SkyCoordConverter from .converters.coordinates.spectral_coord import SpectralCoordConverter from .converters.fits.fits import AsdfFitsConverter, AstropyFitsConverter +from .converters.nddata.uncertainty import UncertaintyConverter from .converters.table.table import AsdfTableConverter, AstropyTableConverter, ColumnConverter, NdarrayMixinConverter from .converters.time.time import TimeConverter from .converters.time.time_delta import TimeDeltaConverter @@ -476,6 +477,7 @@ AstropyTableConverter(), AstropyFitsConverter(), NdarrayMixinConverter(), + UncertaintyConverter(), ] _COORDINATES_MANIFEST_URIS = [ @@ -493,6 +495,7 @@ _ASTROPY_EXTENSION_MANIFEST_URIS = [ + "asdf://astropy.org/astropy/manifests/astropy-1.3.0", "asdf://astropy.org/astropy/manifests/astropy-1.2.0", "asdf://astropy.org/astropy/manifests/astropy-1.1.0", "asdf://astropy.org/astropy/manifests/astropy-1.0.0", diff --git a/asdf_astropy/resources/manifests/astropy-1.2.0.yaml b/asdf_astropy/resources/manifests/astropy-1.2.0.yaml index a3429f33..80f63bb1 100644 --- a/asdf_astropy/resources/manifests/astropy-1.2.0.yaml +++ b/asdf_astropy/resources/manifests/astropy-1.2.0.yaml @@ -6,15 +6,15 @@ description: |- model classes, which are handled by an implementation of the ASDF transform extension. asdf_standard_requirement: - gte: 1.6.0 + gte: 1.1.0 tags: -- tag_uri: tag:astropy.org:astropy/time/timedelta-1.1.0 - schema_uri: http://astropy.org/schemas/astropy/time/timedelta-1.1.0 +- tag_uri: tag:astropy.org:astropy/time/timedelta-1.0.0 + schema_uri: http://astropy.org/schemas/astropy/time/timedelta-1.0.0 title: Represents an instance of TimeDelta from astropy description: |- Represents the time difference between two times. -- tag_uri: tag:astropy.org:astropy/fits/fits-1.1.0 - schema_uri: http://astropy.org/schemas/astropy/fits/fits-1.1.0 +- tag_uri: tag:astropy.org:astropy/fits/fits-1.0.0 + schema_uri: http://astropy.org/schemas/astropy/fits/fits-1.0.0 title: A FITS file inside of an ASDF file. description: |- This schema is useful for distributing ASDF files that can @@ -28,12 +28,12 @@ tags: be accessible in its proper form in the ASDF file. Only image and binary table extensions are supported. -- tag_uri: tag:astropy.org:astropy/table/table-1.2.0 - schema_uri: http://astropy.org/schemas/astropy/table/table-1.2.0 +- tag_uri: tag:astropy.org:astropy/table/table-1.1.0 + schema_uri: http://astropy.org/schemas/astropy/table/table-1.1.0 title: A table. description: |- A table is represented as a list of columns, where each entry is a - [column](ref:http://stsci.edu/schemas/asdf/table/column-1.1.0) + [column](ref:http://stsci.edu/schemas/asdf/core/column-1.0.0) object, containing the data and some additional information. The data itself may be stored inline as text, or in binary in either @@ -42,8 +42,8 @@ tags: Each column in the table must have the same first (slowest moving) dimension. -- tag_uri: tag:astropy.org:astropy/transform/units_mapping-1.1.0 - schema_uri: http://astropy.org/schemas/astropy/transform/units_mapping-1.1.0 +- tag_uri: tag:astropy.org:astropy/transform/units_mapping-1.0.0 + schema_uri: http://astropy.org/schemas/astropy/transform/units_mapping-1.0.0 title: Mapper that operates on the units of the input. description: |- This transform operates on the units of the input, first converting to @@ -54,3 +54,8 @@ tags: title: NdarrayMixin column. description: |- Represents an astropy.table.NdarrayMixin instance. +- tag_uri: tag:astropy.org:astropy/nddata/uncertainty-1.0.0 + schema_uri: http://astropy.org/schemas/astropy/nddata/uncertainty-1.0.0 + title: Represent an astropy.nddata uncertainty + description: |- + Represents an instance of an astropy.nddata uncertainty diff --git a/asdf_astropy/resources/manifests/astropy-1.3.0.yaml b/asdf_astropy/resources/manifests/astropy-1.3.0.yaml new file mode 100644 index 00000000..53c3720a --- /dev/null +++ b/asdf_astropy/resources/manifests/astropy-1.3.0.yaml @@ -0,0 +1,61 @@ +id: asdf://astropy.org/astropy/manifests/astropy-1.3.0 +extension_uri: asdf://astropy.org/astropy/extensions/astropy-1.3.0 +title: Astropy extension 1.3.0 +description: |- + A set of tags for serializing astropy objects. This does not include most + model classes, which are handled by an implementation of the ASDF + transform extension. +asdf_standard_requirement: + gte: 1.6.0 +tags: +- tag_uri: tag:astropy.org:astropy/time/timedelta-1.1.0 + schema_uri: http://astropy.org/schemas/astropy/time/timedelta-1.1.0 + title: Represents an instance of TimeDelta from astropy + description: |- + Represents the time difference between two times. +- tag_uri: tag:astropy.org:astropy/fits/fits-1.1.0 + schema_uri: http://astropy.org/schemas/astropy/fits/fits-1.1.0 + title: A FITS file inside of an ASDF file. + description: |- + This schema is useful for distributing ASDF files that can + automatically be converted to FITS files by specifying the exact + content of the resulting FITS file. + + Not all kinds of data in FITS are directly representable in ASDF. + For example, applying an offset and scale to the data using the + `BZERO` and `BSCALE` keywords. In these cases, it will not be + possible to store the data in the native format from FITS and also + be accessible in its proper form in the ASDF file. + + Only image and binary table extensions are supported. +- tag_uri: tag:astropy.org:astropy/table/table-1.2.0 + schema_uri: http://astropy.org/schemas/astropy/table/table-1.2.0 + title: A table. + description: |- + A table is represented as a list of columns, where each entry is a + [column](ref:http://stsci.edu/schemas/asdf/table/column-1.1.0) + object, containing the data and some additional information. + + The data itself may be stored inline as text, or in binary in either + row- or column-major order by use of the `strides` property on the + individual column arrays. + + Each column in the table must have the same first (slowest moving) + dimension. +- tag_uri: tag:astropy.org:astropy/transform/units_mapping-1.1.0 + schema_uri: http://astropy.org/schemas/astropy/transform/units_mapping-1.1.0 + title: Mapper that operates on the units of the input. + description: |- + This transform operates on the units of the input, first converting to + the expected input units, then assigning replacement output units without + further conversion. +- tag_uri: tag:astropy.org:astropy/table/ndarraymixin-1.0.0 + schema_uri: http://astropy.org/schemas/astropy/table/ndarraymixin-1.0.0 + title: NdarrayMixin column. + description: |- + Represents an astropy.table.NdarrayMixin instance. +- tag_uri: tag:astropy.org:astropy/nddata/uncertainty-1.0.0 + schema_uri: http://astropy.org/schemas/astropy/nddata/uncertainty-1.0.0 + title: Represent an astropy.nddata uncertainty + description: |- + Represents an instance of an astropy.nddata uncertainty diff --git a/asdf_astropy/resources/schemas/nddata/uncertainty-1.0.0.yaml b/asdf_astropy/resources/schemas/nddata/uncertainty-1.0.0.yaml new file mode 100644 index 00000000..63d10b45 --- /dev/null +++ b/asdf_astropy/resources/schemas/nddata/uncertainty-1.0.0.yaml @@ -0,0 +1,22 @@ +%YAML 1.1 +--- +$schema: "http://stsci.edu/schemas/yaml-schema/draft-01" +id: "http://astropy.org/schemas/astropy/nddata/uncertainty-1.0.0" + +title: Represents an astropy.nddata uncertainty class + +description: >- + This object represents an uncertainty. + +allOf: + - type: object + properties: + name: + enum: ["stddev", "unknown", "variance"] + array: + type: object + unit: + anyOf: + - tag: "tag:stsci.edu:asdf/unit/unit-*" + - tag: "tag:astropy.org:astropy/units/unit-1.*" + required: [name, array] diff --git a/docs/asdf-astropy/schemas.rst b/docs/asdf-astropy/schemas.rst index 5c60b849..30d5ea4c 100644 --- a/docs/asdf-astropy/schemas.rst +++ b/docs/asdf-astropy/schemas.rst @@ -30,6 +30,19 @@ fits/fits-1.0.0 fits/fits-1.0.0 +NDData +------ + +The following schemas are associated with **astropy** objects from the +:ref:`astropy_nddata` submodule: + +nddata/uncertainty-1.0.0 +^^^^^^^^^^^^^^^^^^^^^^^^ + +.. asdf-autoschemas:: + + nddata/uncertainty-1.0.0 + Table ----- diff --git a/pyproject.toml b/pyproject.toml index 8855a8ff..5cfadc13 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -96,6 +96,7 @@ extend-ignore = [ # Individually ignored checks "SLF001", # private-member-access "FBT001", # boolean positional arguments in function definition + "RUF012", # mutable class attributes should be annotated with typing.ClassVar ] extend-exclude = ["docs/*"]