Skip to content

Commit 928e6a1

Browse files
committed
Compatibility with specutils 2.0
1 parent 5e7ea5c commit 928e6a1

19 files changed

+164
-126
lines changed

CHANGES.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ Bug Fixes
1515
Other changes
1616
^^^^^^^^^^^^^
1717

18+
- Compatibility with specutils 2.0. [#260]
1819

1920
1.5.1 (2024-03-08)
2021
------------------

MANIFEST.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
include README.rst
22
include CHANGES.rst
33
include pyproject.toml
4+
include conftest.py
45

56
recursive-include docs *
67
recursive-include licenses *

conftest.py

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@
66
from astropy.io import fits
77
from astropy.nddata import CCDData, NDData, VarianceUncertainty
88
from astropy.utils.data import get_pkg_data_filename
9-
from specutils import Spectrum1D, SpectralAxis
9+
from specutils import SpectralAxis
10+
11+
from specreduce.compat import SPECUTILS_LT_2, Spectrum
1012

1113
try:
1214
from pytest_astropy_header.display import PYTEST_HEADER_MODULES, TESTED_VERSIONS
@@ -31,9 +33,13 @@ def _mk_test_data(imgtype, nrows=30, ncols=10):
3133
flux = image * u.DN
3234
uncert = VarianceUncertainty(image_ones)
3335
if imgtype == "spec_no_axis":
34-
image = Spectrum1D(flux, uncertainty=uncert)
36+
if SPECUTILS_LT_2:
37+
kwargs = {}
38+
else:
39+
kwargs = {"spectral_axis_index": image.ndim - 1}
40+
image = Spectrum(flux, uncertainty=uncert, **kwargs)
3541
else: # "spec"
36-
image = Spectrum1D(flux, spectral_axis=np.arange(ncols) * u.um, uncertainty=uncert)
42+
image = Spectrum(flux, spectral_axis=np.arange(ncols) * u.um, uncertainty=uncert)
3743
return image
3844

3945

@@ -71,10 +77,15 @@ def all_images():
7177
sax = SpectralAxis(np.linspace(14.377, 3.677, flux.shape[-1]) * u.um)
7278
unc = VarianceUncertainty(np.random.rand(*flux.shape))
7379

80+
if SPECUTILS_LT_2:
81+
kwargs = {}
82+
else:
83+
kwargs = {"spectral_axis_index": img.ndim - 1}
84+
7485
all_images = {}
7586
all_images['arr'] = img
76-
all_images['s1d'] = Spectrum1D(flux, spectral_axis=sax, uncertainty=unc)
77-
all_images['s1d_pix'] = Spectrum1D(flux, uncertainty=unc)
87+
all_images['s1d'] = Spectrum(flux, spectral_axis=sax, uncertainty=unc)
88+
all_images['s1d_pix'] = Spectrum(flux, uncertainty=unc, **kwargs)
7889
all_images['ccd'] = CCDData(img, uncertainty=unc, unit=flux.unit)
7990
all_images['ndd'] = NDData(img, uncertainty=unc, unit=flux.unit)
8091
all_images['qnt'] = img * flux.unit
@@ -86,7 +97,7 @@ def spec1d():
8697
np.random.seed(7)
8798
flux = np.random.random(50)*u.Jy
8899
sa = np.arange(0, 50)*u.pix
89-
spec = Spectrum1D(flux, spectral_axis=sa)
100+
spec = Spectrum(flux, spectral_axis=sa)
90101
return spec
91102

92103

@@ -97,7 +108,7 @@ def spec1d_with_emission_line():
97108
flux = (np.random.randn(200) +
98109
10*np.exp(-0.01*((sa.value-130)**2)) +
99110
sa.value/100) * u.Jy
100-
spec = Spectrum1D(flux, spectral_axis=sa)
111+
spec = Spectrum(flux, spectral_axis=sa)
101112
return spec
102113

103114

@@ -108,7 +119,7 @@ def spec1d_with_absorption_line():
108119
flux = (np.random.randn(200) -
109120
10*np.exp(-0.01*((sa.value-130)**2)) +
110121
sa.value/100) * u.Jy
111-
spec = Spectrum1D(flux, spectral_axis=sa)
122+
spec = Spectrum(flux, spectral_axis=sa)
112123
return spec
113124

114125

notebook_sandbox/apo05.ipynb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
"from astropy import units as u\n",
2525
"\n",
2626
"from astropy.nddata import CCDData, StdDevUncertainty\n",
27-
"from specutils import Spectrum1D\n",
27+
"from specreduce.compat import Spectrum as Spectrum1D\n",
2828
"from ccdproc import trim_image, Combiner\n",
2929
"\n",
3030
"# need to get import to work in notebook w/o global package install\n",

notebook_sandbox/fluxcal_demo.ipynb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
"from ccdproc import trim_image\n",
1515
"from astropy import units as u\n",
1616
"\n",
17-
"from specutils import Spectrum1D\n",
17+
"from specreduce.compat import Spectrum as Spectrum1D\n",
1818
"from astropy.nddata import StdDevUncertainty\n",
1919
"\n",
2020
"# need to get import to work in notebook w/o global package install\n",

notebook_sandbox/wavecal_demo.ipynb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
"from astropy.nddata import StdDevUncertainty\n",
1818
"import astropy.units as u\n",
1919
"\n",
20-
"from specutils import Spectrum1D\n",
20+
"from specreduce.compat import Spectrum as Spectrum1D\n",
2121
"from specutils.fitting import fit_generic_continuum\n",
2222
"\n",
2323
"from specreduce.calibration_data import load_pypeit_calibration_lines\n",

specreduce/background.py

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
66
import numpy as np
77
from astropy import units as u
88
from astropy.utils.decorators import deprecated_attribute
9-
from specutils import Spectrum1D
109

10+
from specreduce.compat import SPECUTILS_LT_2, Spectrum
1111
from specreduce.core import _ImageParser, MaskingOption, ImageLike
1212
from specreduce.extract import _ap_weight_image
1313
from specreduce.tracing import Trace, FlatTrace
@@ -84,7 +84,7 @@ class Background(_ImageParser):
8484
"apply_nan_only",
8585
)
8686

87-
# TO-DO: update bkg_array with Spectrum1D alternative (is bkg_image enough?)
87+
# TO-DO: update bkg_array with Spectrum alternative (is bkg_image enough?)
8888
bkg_array = deprecated_attribute("bkg_array", "1.3")
8989

9090
def __post_init__(self):
@@ -303,12 +303,18 @@ def bkg_image(self, image=None):
303303
304304
Returns
305305
-------
306-
`~specutils.Spectrum1D` object with same shape as ``image``.
306+
spec : `~specutils.Spectrum1D`
307+
Spectrum object with same shape as ``image``.
307308
"""
308309
image = self._parse_image(image)
309-
return Spectrum1D(
310-
np.tile(self._bkg_array, (image.shape[0], 1)) * image.unit,
311-
spectral_axis=image.spectral_axis,
310+
arr = np.tile(self._bkg_array, (image.shape[0], 1))
311+
if SPECUTILS_LT_2:
312+
kwargs = {}
313+
else:
314+
kwargs = {"spectral_axis_index": arr.ndim - 1}
315+
return Spectrum(
316+
arr * image.unit,
317+
spectral_axis=image.spectral_axis, **kwargs
312318
)
313319

314320
def bkg_spectrum(self, image=None, bkg_statistic="sum"):
@@ -355,7 +361,7 @@ def bkg_spectrum(self, image=None, bkg_statistic="sum"):
355361
# can't collapse with a spectral axis in pixels because
356362
# SpectralCoord only allows frequency/wavelength equivalent units...
357363
ext1d = statistic_function(bkg_image.flux, axis=self.crossdisp_axis)
358-
return Spectrum1D(ext1d, bkg_image.spectral_axis)
364+
return Spectrum(ext1d, bkg_image.spectral_axis)
359365

360366
def sub_image(self, image=None):
361367
"""
@@ -369,15 +375,17 @@ def sub_image(self, image=None):
369375
370376
Returns
371377
-------
372-
`~specutils.Spectrum1D` object with same shape as ``image``.
378+
spec : `~specutils.Spectrum1D`
379+
Spectrum object with same shape as ``image``.
373380
"""
374381
image = self._parse_image(image)
375382

376-
# a compare_wcs argument is needed for Spectrum1D.subtract() in order to
383+
# a compare_wcs argument is needed for Spectrum.subtract() in order to
377384
# avoid a TypeError from SpectralCoord when image's spectral axis is in
378385
# pixels. it is not needed when image's spectral axis has physical units
379386
kwargs = {"compare_wcs": None} if image.spectral_axis.unit == u.pix else {}
380-
387+
if not SPECUTILS_LT_2:
388+
kwargs["spectral_axis_index"] = image.flux.ndim - 1
381389
# https://docs.astropy.org/en/stable/nddata/mixins/ndarithmetic.html
382390
return image.subtract(self.bkg_image(image), **kwargs)
383391

@@ -406,7 +414,7 @@ def sub_spectrum(self, image=None):
406414
# can't collapse with a spectral axis in pixels because
407415
# SpectralCoord only allows frequency/wavelength equivalent units...
408416
ext1d = np.nansum(sub_image.flux, axis=self.crossdisp_axis)
409-
return Spectrum1D(ext1d, spectral_axis=sub_image.spectral_axis)
417+
return Spectrum(ext1d, spectral_axis=sub_image.spectral_axis)
410418

411419
def __rsub__(self, image):
412420
"""

specreduce/calibration_data.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,10 @@
1212
from astropy.utils.data import download_file
1313
from astropy.utils.exceptions import AstropyUserWarning
1414
from astropy.coordinates import SpectralCoord
15-
16-
from specutils import Spectrum1D
1715
from specutils.utils.wcs_utils import vac_to_air
1816

17+
from specreduce.compat import Spectrum
18+
1919
__all__ = [
2020
'get_available_line_catalogs',
2121
'load_pypeit_calibration_lines',
@@ -187,7 +187,7 @@ def load_MAST_calspec(
187187
filename: str | Path,
188188
cache: bool | Literal['update'] = True,
189189
show_progress: bool = False
190-
) -> Spectrum1D | None:
190+
) -> Spectrum | None:
191191
"""
192192
Load a standard star spectrum from the ``calspec`` database at MAST. These spectra are provided
193193
in FITS format and are described in detail at:
@@ -238,7 +238,7 @@ def load_MAST_calspec(
238238
# supported directly by astropy.units. mJy is chosen since it's the JWST
239239
# standard and can easily be converted to/from AB magnitudes.
240240
flux_mjy = synphot.units.convert_flux(wave, flux, u.mJy)
241-
spectrum = Spectrum1D(spectral_axis=wave, flux=flux_mjy)
241+
spectrum = Spectrum(spectral_axis=wave, flux=flux_mjy)
242242
return spectrum
243243

244244

@@ -247,7 +247,7 @@ def load_onedstds(
247247
specfile: str = "EG131.dat",
248248
cache: bool | Literal['update'] = True,
249249
show_progress: bool = False
250-
) -> Spectrum1D | None:
250+
) -> Spectrum | None:
251251
"""
252252
This is a convenience function for loading a standard star spectrum from the 'onedstds'
253253
dataset in the ``specreduce_data`` package. They will be downloaded from the
@@ -290,11 +290,11 @@ def load_onedstds(
290290
# the specreduce_data standard star spectra all provide fluxes in AB mag
291291
flux = t['ABmag'].data * u.ABmag
292292
flux = flux.to(u.mJy) # convert to linear flux units
293-
spectrum = Spectrum1D(spectral_axis=spectral_axis, flux=flux)
293+
spectrum = Spectrum(spectral_axis=spectral_axis, flux=flux)
294294
return spectrum
295295

296296

297-
class AtmosphericExtinction(Spectrum1D):
297+
class AtmosphericExtinction(Spectrum):
298298
"""
299299
Spectrum container for atmospheric extinction in magnitudes as a function of wavelength.
300300
If extinction and spectral_axis are provided, this will use them to build a custom model.
@@ -353,7 +353,7 @@ def __init__(
353353
extinction = u.Magnitude(
354354
extinction,
355355
u.MagUnit(u.dimensionless_unscaled)
356-
).to(u.dimensionless_unscaled) # Spectrum1D wants this to be linear
356+
).to(u.dimensionless_unscaled) # Spectrum wants this to be linear
357357
elif isinstance(extinction, (u.LogUnit, u.Magnitude)) or extinction.unit == u.mag:
358358
# if in log or magnitudes, recast into Magnitude with dimensionless physical units
359359
extinction = u.Magnitude(

specreduce/compat.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import specutils
2+
from astropy.utils import minversion
3+
4+
__all__ = []
5+
6+
SPECUTILS_LT_2 = not minversion(specutils, "2.0.dev")
7+
8+
if SPECUTILS_LT_2:
9+
from specutils import Spectrum1D as Spectrum
10+
else:
11+
from specutils import Spectrum

specreduce/core.py

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
# Licensed under a 3-clause BSD style license - see LICENSE.rst
22

3-
from copy import deepcopy
43
import inspect
4+
from copy import deepcopy
55
from dataclasses import dataclass
66
from typing import Literal
77

88
import numpy as np
99
from astropy import units as u
1010
from astropy.nddata import VarianceUncertainty, NDData
11-
from specutils import Spectrum1D
11+
12+
from specreduce.compat import SPECUTILS_LT_2, Spectrum
1213

1314
__all__ = ["SpecreduceOperation"]
1415

@@ -21,14 +22,14 @@
2122

2223
class _ImageParser:
2324
"""
24-
Coerces images from accepted formats to Spectrum1D objects for
25+
Coerces images from accepted formats to Spectrum objects for
2526
internal use in specreduce's operation classes.
2627
2728
Fills any and all of uncertainty, mask, units, and spectral axis
2829
that are missing in the provided image with generic values.
2930
Accepted image types are:
3031
31-
- `~specutils.spectra.spectrum1d.Spectrum1D` (preferred)
32+
- `~specutils.Spectrum1D` (preferred)
3233
- `~astropy.nddata.ccddata.CCDData`
3334
- `~astropy.nddata.ndddata.NDDData`
3435
- `~astropy.units.quantity.Quantity`
@@ -49,9 +50,9 @@ class _ImageParser:
4950

5051
def _parse_image(
5152
self, image: ImageLike, disp_axis: int = 1, mask_treatment: MaskingOption = "apply"
52-
) -> Spectrum1D:
53+
) -> Spectrum:
5354
"""
54-
Convert all accepted image types to a consistently formatted Spectrum1D object.
55+
Convert all accepted image types to a consistently formatted Spectrum object.
5556
5657
Parameters
5758
----------
@@ -81,7 +82,7 @@ def _parse_image(
8182
8283
Returns
8384
-------
84-
Spectrum1D
85+
Spectrum
8586
"""
8687
# would be nice to handle (cross)disp_axis consistently across
8788
# operations (public attribute? private attribute? argument only?) so
@@ -96,7 +97,7 @@ def _parse_image(
9697
@staticmethod
9798
def _get_data_from_image(
9899
image: ImageLike, disp_axis: int = 1, mask_treatment: MaskingOption = "apply"
99-
) -> Spectrum1D:
100+
) -> Spectrum:
100101
"""
101102
Extract data array from various input types for `image`.
102103
@@ -112,13 +113,13 @@ def _get_data_from_image(
112113
113114
Returns
114115
-------
115-
Spectrum1D
116+
Spectrum
116117
"""
117118
if isinstance(image, u.quantity.Quantity):
118119
img = image.value
119120
elif isinstance(image, np.ndarray):
120121
img = image
121-
else: # NDData, including CCDData and Spectrum1D
122+
else: # NDData, including CCDData and Spectrum
122123
img = image.data
123124

124125
mask = getattr(image, "mask", None)
@@ -136,7 +137,7 @@ def _get_data_from_image(
136137
)
137138

138139
# mask (handled above) and uncertainty are set as None when they aren't
139-
# specified upon creating a Spectrum1D object, so we must check whether
140+
# specified upon creating a Spectrum object, so we must check whether
140141
# these attributes are absent *and* whether they are present but set as None
141142
if hasattr(image, "uncertainty"):
142143
uncertainty = image.uncertainty
@@ -147,8 +148,13 @@ def _get_data_from_image(
147148

148149
spectral_axis = getattr(image, "spectral_axis", np.arange(img.shape[disp_axis]) * u.pix)
149150

150-
img = Spectrum1D(
151-
img * unit, spectral_axis=spectral_axis, uncertainty=uncertainty, mask=mask
151+
if SPECUTILS_LT_2:
152+
kwargs = {}
153+
else:
154+
kwargs = {"spectral_axis_index": img.ndim - 1}
155+
img = Spectrum(
156+
img * unit, spectral_axis=spectral_axis, uncertainty=uncertainty, mask=mask,
157+
**kwargs
152158
)
153159
return img
154160

0 commit comments

Comments
 (0)