diff --git a/CHANGES.rst b/CHANGES.rst index 7b0dd3146..b2b8c1673 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,4 +1,4 @@ -1.18.0 (unreleased) +1.19.0 (unreleased) ------------------- New Features @@ -7,15 +7,41 @@ New Features Bug Fixes ^^^^^^^^^ +Other Changes and Additions +^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +1.18.0 (2024-10-16) +------------------- + +New Features +^^^^^^^^^^^^ + +- New ``Spectrum1D.with_spectral_axis_and_flux_units`` method to convert both + spectral axis and flux units at the same time. [#1184] + +Bug Fixes +^^^^^^^^^ + - Fixed ``Spectrum1D.with_flux_unit()`` not converting uncertainty along with flux unit. [#1181] +- Fixed ``mwmVisit`` SDSS-V ``Spectrum1D`` and ``SpectrumList`` default loader + being unable to load files containing only BOSS instrument spectra. [#1185] + +- Fixed automatic format detection for SDSS-V ``SpectrumList`` default loaders. [#1185] + +- Fixed extracting a spectral region when one of spectrum/region is in wavelength + and the other is in frequency units. [#1187] + + Other Changes and Additions ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - Replaced ``LevMarLSQFitter`` with ``TRFLSQFitter`` as the former is no longer recommended by ``astropy``. [#1180] +- "Multi" loaders have been removed from SDSS-V ``SpectrumList`` default loaders. [#1185] + 1.17.0 (2024-10-04) ------------------- diff --git a/specutils/io/default_loaders/sdss_v.py b/specutils/io/default_loaders/sdss_v.py index ee4fdf10b..cdd5a750c 100644 --- a/specutils/io/default_loaders/sdss_v.py +++ b/specutils/io/default_loaders/sdss_v.py @@ -1,10 +1,12 @@ """Register reader functions for various spectral formats.""" +import warnings from typing import Optional import numpy as np from astropy.units import Unit, Quantity, Angstrom from astropy.nddata import StdDevUncertainty, InverseVariance from astropy.io.fits import HDUList, BinTableHDU, ImageHDU +from astropy.utils.exceptions import AstropyUserWarning from ...spectra import Spectrum1D, SpectrumList from ..registers import data_loader @@ -198,9 +200,10 @@ def load_sdss_apStar_1D(file_obj, idx: int = 0, **kwargs): @data_loader( - "SDSS-V apStar multi", + "SDSS-V apStar", identifier=apStar_identify, dtype=SpectrumList, + force=True, priority=10, extensions=["fits"], ) @@ -274,9 +277,10 @@ def load_sdss_apVisit_1D(file_obj, **kwargs): @data_loader( - "SDSS-V apVisit multi", + "SDSS-V apVisit", identifier=apVisit_identify, dtype=SpectrumList, + force=True, priority=10, extensions=["fits"], ) @@ -326,8 +330,6 @@ def load_sdss_apVisit_list(file_obj, **kwargs): # BOSS REDUX products (specLite, specFull, custom coadd files, etc) - - @data_loader( "SDSS-V spec", identifier=spec_sdss5_identify, @@ -353,6 +355,8 @@ def load_sdss_spec_1D(file_obj, *args, hdu: Optional[int] = None, **kwargs): """ if hdu is None: # TODO: how should we handle this -- multiple things in file, but the user cannot choose. + warnings.warn('HDU not specified. Loading coadd spectrum (HDU1)', + AstropyUserWarning) hdu = 1 # defaulting to coadd # raise ValueError("HDU not specified! Please specify a HDU to load.") elif hdu in [2, 3, 4]: @@ -363,9 +367,10 @@ def load_sdss_spec_1D(file_obj, *args, hdu: Optional[int] = None, **kwargs): @data_loader( - "SDSS-V spec multi", + "SDSS-V spec", identifier=spec_sdss5_identify, dtype=SpectrumList, + force=True, priority=5, extensions=["fits"], ) @@ -449,7 +454,10 @@ def _load_BOSS_HDU(hdulist: HDUList, hdu: int, **kwargs): priority=20, extensions=["fits"], ) -def load_sdss_mwm_1d(file_obj, hdu: Optional[int] = None, **kwargs): +def load_sdss_mwm_1d(file_obj, + hdu: Optional[int] = None, + visit: Optional[int] = None, + **kwargs): """ Load an unspecified spec file as a Spectrum1D. @@ -457,8 +465,10 @@ def load_sdss_mwm_1d(file_obj, hdu: Optional[int] = None, **kwargs): ---------- file_obj : str, file-like, or HDUList FITS file name, file object, or HDUList.. - hdu : int + hdu : Optional[int] Specified HDU to load. + visit : Optional[int] + Specified visit index to load. Returns ------- @@ -475,17 +485,22 @@ def load_sdss_mwm_1d(file_obj, hdu: Optional[int] = None, **kwargs): # TODO: how should we handle this -- multiple things in file, but the user cannot choose. if hdu is None: - for i in range(len(hdulist)): + for i in range(1, len(hdulist)): if hdulist[i].header.get("DATASUM") != "0": hdu = i + warnings.warn( + 'HDU not specified. Loading spectrum at (HDU{})'. + format(i), AstropyUserWarning) break - return _load_mwmVisit_or_mwmStar_hdu(hdulist, hdu, **kwargs) + # load spectra and return + return _load_mwmVisit_or_mwmStar_hdu(hdulist, hdu) @data_loader( - "SDSS-V mwm multi", + "SDSS-V mwm", identifier=mwm_identify, + force=True, dtype=SpectrumList, priority=20, extensions=["fits"], @@ -501,12 +516,8 @@ def load_sdss_mwm_list(file_obj, **kwargs): Returns ------- - SpectrumList - The spectra contained in the file, where: - Spectrum1D - A given spectra of nD flux - None - If there are no spectra for that spectrograph/observatory + SpectrumList[Spectrum1D] + A list spectra from each visit with each instrument at each observatory (mwmVisit), or the coadd from each instrument/observatory (mwmStar). """ spectra = SpectrumList() with read_fileobj_or_hdulist(file_obj, memmap=False, **kwargs) as hdulist: @@ -521,10 +532,6 @@ def load_sdss_mwm_list(file_obj, **kwargs): for hdu in range(1, len(hdulist)): if hdulist[hdu].header.get("DATASUM") == "0": # Skip zero data HDU's - # TODO: validate if we want this printed warning or not. - # it might get annoying & fill logs with useless alerts. - print("WARNING: HDU{} ({}) is empty.".format( - hdu, hdulist[hdu].name)) continue spectra.append(_load_mwmVisit_or_mwmStar_hdu(hdulist, hdu)) return spectra @@ -543,8 +550,8 @@ def _load_mwmVisit_or_mwmStar_hdu(hdulist: HDUList, hdu: int, **kwargs): Returns ------- - Spectrum1D - The spectrum with nD flux contained in the HDU. + list[Spectrum1D] + List of spectrum with 1D flux contained in the HDU. """ if hdulist[hdu].header.get("DATASUM") == "0": @@ -579,12 +586,11 @@ def _load_mwmVisit_or_mwmStar_hdu(hdulist: HDUList, hdu: int, **kwargs): mask = mask != 0 # collapse shape if 1D spectra in 2D array - # NOTE: this fixes a jdaviz handling bug for 2D of shape 1, # it could be that it's expected to be parsed this way. if flux.shape[0] == 1: - flux = flux[0] - e_flux = e_flux[0] - mask = mask[0] + flux = np.ravel(flux) + e_flux = e_flux[0] # different class + mask = np.ravel(mask) # Create metadata meta = dict() @@ -594,24 +600,26 @@ def _load_mwmVisit_or_mwmStar_hdu(hdulist: HDUList, hdu: int, **kwargs): meta["snr"] = np.array(hdulist[hdu].data["snr"]) # Add identifiers (obj, telescope, mjd, datatype) - # TODO: need to see what metadata we're interested in for the MWM files. meta["telescope"] = hdulist[hdu].data["telescope"] meta["instrument"] = hdulist[hdu].header.get("INSTRMNT") - try: + try: # get obj if exists meta["obj"] = hdulist[hdu].data["obj"] except KeyError: pass + + # choose between mwmVisit/Star via KeyError except try: - meta["date"] = hdulist[hdu].data["date_obs"] - meta["mjd"] = hdulist[hdu].data["mjd"] + meta['mjd'] = hdulist[hdu].data['mjd'] meta["datatype"] = "mwmVisit" except KeyError: - meta["mjd"] = (str(hdulist[hdu].data["min_mjd"][0]) + "->" + - str(hdulist[hdu].data["max_mjd"][0])) + meta["min_mjd"] = str(hdulist[hdu].data["min_mjd"][0]) + meta["max_mjd"] = str(hdulist[hdu].data["max_mjd"][0]) meta["datatype"] = "mwmStar" finally: meta["name"] = hdulist[hdu].name + meta["sdss_id"] = hdulist[hdu].data['sdss_id'] + # drop back a list of Spectrum1Ds to unpack return Spectrum1D( spectral_axis=spectral_axis, flux=flux, diff --git a/specutils/io/default_loaders/tests/test_sdss_v.py b/specutils/io/default_loaders/tests/test_sdss_v.py index f70490482..fae2bf1ff 100644 --- a/specutils/io/default_loaders/tests/test_sdss_v.py +++ b/specutils/io/default_loaders/tests/test_sdss_v.py @@ -1,19 +1,25 @@ +import os +import warnings # noqa ; required for pytest + import numpy as np import pytest - from astropy.io import fits -from astropy.units import Unit, Angstrom +from astropy.units import Angstrom, Unit +from astropy.utils.exceptions import AstropyUserWarning from specutils import Spectrum1D, SpectrumList -def generate_apogee_hdu(observatory="APO", with_wl=True, datasum="0"): +def generate_apogee_hdu(observatory="APO", + with_wl=True, + datasum="0", + nvisits=1): wl = (10**(4.179 + 6e-6 * np.arange(8575))).reshape((1, -1)) - flux = np.zeros_like(wl) - ivar = np.zeros_like(wl) - pixel_flags = np.zeros_like(wl) - continuum = np.zeros_like(wl) - nmf_rectified_model_flux = np.zeros_like(wl) + flux = np.array([np.zeros_like(wl)] * nvisits) + ivar = np.array([np.zeros_like(wl)] * nvisits) + pixel_flags = np.array([np.zeros_like(wl)] * nvisits) + continuum = np.array([np.zeros_like(wl)] * nvisits) + nmf_rectified_model_flux = np.array([np.zeros_like(wl)] * nvisits) columns = [ fits.Column(name="spectrum_pk_id", array=[159783564], format="K"), @@ -25,34 +31,7 @@ def generate_apogee_hdu(observatory="APO", with_wl=True, datasum="0"): fits.Column(name="apred", array=[b"1.2"], format="3A"), fits.Column(name="obj", array=[b"2M19534321+6705175"], format="18A"), fits.Column(name="telescope", array=[b"apo25m"], format="6A"), - fits.Column(name="min_mjd", array=[59804], format="J"), - fits.Column(name="max_mjd", array=[59866], format="J"), - fits.Column(name="n_entries", array=[-1], format="J"), - fits.Column(name="n_visits", array=[5], format="J"), - fits.Column(name="n_good_visits", array=[5], format="J"), - fits.Column(name="n_good_rvs", array=[5], format="J"), - fits.Column(name="snr", array=[46.56802], format="E"), - fits.Column(name="mean_fiber", array=[256.0], format="E"), - fits.Column(name="std_fiber", array=[0.0], format="E"), - fits.Column(name="spectrum_flags", array=[1048576], format="J"), - fits.Column(name="v_rad", array=[-56.7284381], format="E"), - fits.Column(name="e_v_rad", array=[5.35407624], format="E"), - fits.Column(name="std_v_rad", array=[10.79173857], format="E"), - fits.Column(name="median_e_v_rad", array=[16.19418386], format="E"), - fits.Column(name="doppler_teff", array=[7169.0107], format="E"), - fits.Column(name="doppler_e_teff", array=[9.405238], format="E"), - fits.Column(name="doppler_logg", array=[2.981389], format="E"), - fits.Column(name="doppler_e_logg", array=[0.01916536], format="E"), - fits.Column(name="doppler_fe_h", array=[-1.20532212], format="E"), - fits.Column(name="doppler_e_fe_h", array=[0.0093738], format="E"), - fits.Column(name="doppler_rchi2", array=[1.1424173], format="E"), - fits.Column(name="doppler_flags", array=[0], format="J"), - fits.Column(name="xcorr_v_rad", array=[np.nan], format="E"), - fits.Column(name="xcorr_v_rel", array=[np.nan], format="E"), - fits.Column(name="xcorr_e_v_rel", array=[np.nan], format="E"), - fits.Column(name="ccfwhm", array=[np.nan], format="E"), - fits.Column(name="autofwhm", array=[np.nan], format="E"), - fits.Column(name="n_components", array=[1], format="J"), + fits.Column(name="snr", array=[50], format="E"), ] if with_wl: columns.append( @@ -60,6 +39,14 @@ def generate_apogee_hdu(observatory="APO", with_wl=True, datasum="0"): array=wl, format="8575E", dim="(8575)")) + columns += [ + fits.Column(name="min_mjd", array=[59804], format="J"), + fits.Column(name="max_mjd", array=[59866], format="J"), + ] + else: + columns += [ + fits.Column(name="mjd", array=[59804], format="J"), + ] columns += [ fits.Column(name="flux", array=flux, format="8575E", dim="(8575)"), fits.Column(name="ivar", array=ivar, format="8575E", dim="(8575)"), @@ -97,10 +84,13 @@ def generate_apogee_hdu(observatory="APO", with_wl=True, datasum="0"): return fits.BinTableHDU.from_columns(columns, header=header) -def generate_boss_hdu(observatory="APO", with_wl=True, datasum="0"): +def generate_boss_hdu(observatory="APO", with_wl=True, datasum="0", nvisits=1): wl = (10**(3.5523 + 1e-4 * np.arange(4648))).reshape((1, -1)) - flux = ivar = continuum = pixel_flags = nmf_rectified_model_flux = np.zeros_like( - wl) + flux = np.array([np.zeros_like(wl)] * nvisits) + ivar = np.array([np.zeros_like(wl)] * nvisits) + pixel_flags = np.array([np.zeros_like(wl)] * nvisits) + continuum = np.array([np.zeros_like(wl)] * nvisits) + nmf_rectified_model_flux = np.array([np.zeros_like(wl)] * nvisits) columns = [ fits.Column(name="spectrum_pk_id", array=[0], format="K"), fits.Column(name="release", array=["sdss5"], format="5A"), @@ -110,32 +100,23 @@ def generate_boss_hdu(observatory="APO", with_wl=True, datasum="0"): fits.Column(name="sdss_id", array=[42], format="K"), fits.Column(name="run2d", array=["6_1_2"], format="6A"), fits.Column(name="telescope", array=["apo25m"], format="6A"), - fits.Column(name="min_mjd", array=[54], format="J"), - fits.Column(name="max_mjd", array=[488], format="J"), - fits.Column(name="n_visits", array=[1], format="J"), - fits.Column(name="n_good_visits", array=[1], format="J"), - fits.Column(name="n_good_rvs", array=[1], format="J"), - fits.Column(name="v_rad", array=[0], format="E"), - fits.Column(name="e_v_rad", array=[1], format="E"), - fits.Column(name="std_v_rad", array=[1], format="E"), - fits.Column(name="median_e_v_rad", array=[3], format="E"), - fits.Column(name="xcsao_teff", array=[5000], format="E"), - fits.Column(name="xcsao_e_teff", array=[10], format="E"), - fits.Column(name="xcsao_logg", array=[4], format="E"), - fits.Column(name="xcsao_e_logg", array=[3], format="E"), - fits.Column(name="xcsao_fe_h", array=[0], format="E"), - fits.Column(name="xcsao_e_fe_h", array=[5], format="E"), - fits.Column(name="xcsao_meanrxc", array=[0], format="E"), fits.Column(name="snr", array=[50], format="E"), - fits.Column(name="gri_gaia_transform_flags", array=[1], format="J"), - fits.Column(name="zwarning_flags", array=[0], format="J"), ] + if with_wl: columns.append( fits.Column(name="wavelength", array=wl, format="4648E", dim="(4648)")) + columns += [ + fits.Column(name="min_mjd", array=[54], format="J"), + fits.Column(name="max_mjd", array=[488], format="J"), + ] + else: + columns += [ + fits.Column(name="mjd", array=[59804], format="J"), + ] columns += [ fits.Column(name="flux", array=flux, format="4648E", dim="(4648)"), fits.Column(name="ivar", array=ivar, format="4648E", dim="(4648)"), @@ -312,7 +293,7 @@ def fake_primary_hdu(): ])) -def mwm_HDUList(hduflags, with_wl): +def mwm_HDUList(hduflags, with_wl, **kwargs): hdulist = [fake_primary_hdu()] for i, flag in enumerate(hduflags): obs = ["APO", "LCO"] @@ -320,12 +301,14 @@ def mwm_HDUList(hduflags, with_wl): hdulist.append( generate_boss_hdu(obs[i % 2], with_wl=with_wl, - datasum=str(flag))) + datasum=str(flag), + **kwargs)) else: hdulist.append( generate_apogee_hdu(obs[i % 2], with_wl=with_wl, - datasum=str(flag))) + datasum=str(flag), + **kwargs)) print(hdulist) return fits.HDUList(hdulist) @@ -451,25 +434,59 @@ def spec_HDUList(n_spectra): return hdulist +@pytest.mark.parametrize( + "file_obj, hdu, with_wl, hduflags, nvisits", + [ + ("mwm-temp", None, False, [0, 0, 1, 0], 1), # visit + ("mwm-temp", None, False, [0, 1, 1, 0], 3), # multi-ext visits + ("mwm-temp", None, True, [0, 0, 1, 0], 1), # star + ("mwm-temp", None, True, [0, 1, 1, 0], 1), + ], +) +def test_mwm_1d_nohdu(file_obj, hdu, with_wl, hduflags, nvisits): + """Test mwm Spectrum1D loader when HDU isn't specified""" + tmpfile = str(file_obj) + ".fits" + mwm_HDUList(hduflags, with_wl, nvisits=nvisits).writeto(tmpfile, + overwrite=True) + + with pytest.warns(AstropyUserWarning): + data = Spectrum1D.read(tmpfile, hdu=hdu) + assert isinstance(data, Spectrum1D) + assert isinstance(data.meta["header"], fits.Header) + if data.meta["instrument"].lower() == "apogee": + length = 8575 + elif data.meta["instrument"].lower() == "boss": + length = 4648 + else: + raise ValueError( + "INSTRMNT tag in test HDU header is not set properly.") + assert len(data.spectral_axis.value) == length + assert data.flux.value.shape[-1] == length + if nvisits > 1: + assert data.flux.value.shape[0] == nvisits + + assert data.spectral_axis.unit == Angstrom + assert data.flux.unit == Unit("1e-17 erg / (s cm2 Angstrom)") + os.remove(tmpfile) + + # TEST MWM loaders @pytest.mark.parametrize( - "file_obj, hdu, with_wl, hduflags", + "file_obj, hdu, with_wl, hduflags, nvisits", [ - ("mwm-temp", None, False, [0, 0, 1, 0]), - ("mwm-temp", 3, False, [0, 0, 1, 0]), - ("mwm-temp", None, True, [0, 1, 1, 0]), - ("mwm-temp", 2, True, [0, 1, 1, 0]), + ("mwm-temp", 3, False, [0, 0, 1, 0], 1), + ("mwm-temp", 3, False, [0, 1, 1, 0], 5), + ("mwm-temp", 3, True, [0, 0, 1, 0], 1), + ("mwm-temp", 2, True, [0, 1, 1, 0], 1), ], ) -def test_mwm_1d(file_obj, hdu, with_wl, hduflags): +def test_mwm_1d(file_obj, hdu, with_wl, hduflags, nvisits): """Test mwm Spectrum1D loader""" tmpfile = str(file_obj) + ".fits" - mwm_HDUList(hduflags, with_wl).writeto(tmpfile, overwrite=True) + mwm_HDUList(hduflags, with_wl, nvisits=nvisits).writeto(tmpfile, + overwrite=True) - if hdu is None: - data = Spectrum1D.read(tmpfile) - else: - data = Spectrum1D.read(tmpfile, hdu=hdu) + data = Spectrum1D.read(tmpfile, hdu=hdu) assert isinstance(data, Spectrum1D) assert isinstance(data.meta["header"], fits.Header) if data.meta["instrument"].lower() == "apogee": @@ -480,24 +497,36 @@ def test_mwm_1d(file_obj, hdu, with_wl, hduflags): raise ValueError( "INSTRMNT tag in test HDU header is not set properly.") assert len(data.spectral_axis.value) == length - assert len(data.flux.value) == length + assert data.flux.value.shape[-1] == length + if nvisits > 1: + assert data.flux.value.shape[0] == nvisits + assert data.spectral_axis.unit == Angstrom assert data.flux.unit == Unit("1e-17 erg / (s cm2 Angstrom)") + os.remove(tmpfile) @pytest.mark.parametrize( "file_obj, with_wl, hduflags", [ + ("mwm-temp", False, [0, 0, 1, 1]), + ("mwm-temp", False, [0, 1, 1, 0]), + ("mwm-temp", False, [1, 1, 0, 0]), ("mwm-temp", False, [1, 1, 1, 1]), - ("mwm-temp", True, [0, 1, 0, 1]), + ("mwm-temp", True, [0, 0, 1, 1]), + ("mwm-temp", True, [0, 1, 1, 0]), + ("mwm-temp", True, [1, 1, 0, 0]), + ("mwm-temp", True, [1, 1, 1, 1]), ], ) def test_mwm_list(file_obj, with_wl, hduflags): """Test mwm SpectrumList loader""" tmpfile = str(file_obj) + ".fits" - mwm_HDUList(hduflags, with_wl).writeto(tmpfile, overwrite=True) + nvisits = 1 if with_wl else 3 + mwm_HDUList(hduflags, with_wl, nvisits=nvisits).writeto(tmpfile, + overwrite=True) - data = SpectrumList.read(tmpfile, format="SDSS-V mwm multi") + data = SpectrumList.read(tmpfile) assert isinstance(data, SpectrumList) for i in range(len(data)): assert isinstance(data[i], Spectrum1D) @@ -509,10 +538,17 @@ def test_mwm_list(file_obj, with_wl, hduflags): else: raise ValueError( "INSTRMNT tag in test HDU header is not set properly.") + if with_wl: + assert data[i].meta['datatype'].lower() == 'mwmstar' + else: + assert data[i].meta['datatype'].lower() == 'mwmvisit' assert len(data[i].spectral_axis.value) == length - assert len(data[i].flux.value) == length + assert data[i].flux.value.shape[-1] == length + if nvisits > 1: + assert data[i].flux.value.shape[0] == nvisits assert data[i].spectral_axis.unit == Angstrom assert data[i].flux.unit == Unit("1e-17 erg / (s cm2 Angstrom)") + os.remove(tmpfile) @pytest.mark.parametrize( @@ -530,6 +566,7 @@ def test_mwm_1d_fail_spec(file_obj, hdu, hduflags): mwm_HDUList(hduflags, True).writeto(tmpfile, overwrite=True) with pytest.raises(IndexError): Spectrum1D.read(tmpfile, hdu=hdu) + os.remove(tmpfile) @pytest.mark.parametrize( @@ -546,6 +583,7 @@ def test_mwm_1d_fail(file_obj, with_wl): with pytest.raises(ValueError): Spectrum1D.read(tmpfile) + os.remove(tmpfile) @pytest.mark.parametrize( @@ -561,7 +599,8 @@ def test_mwm_list_fail(file_obj, with_wl): mwm_HDUList([0, 0, 0, 0], with_wl).writeto(tmpfile, overwrite=True) with pytest.raises(ValueError): - SpectrumList.read(tmpfile, format="SDSS-V mwm multi") + SpectrumList.read(tmpfile) + os.remove(tmpfile) @pytest.mark.parametrize( @@ -589,6 +628,7 @@ def test_spec_1d(file_obj, n_spectra): assert len(data.mask) == 10 assert data[i].meta["header"].get("foobar") == "barfoo" + os.remove(tmpfile) @pytest.mark.parametrize( @@ -603,7 +643,7 @@ def test_spec_list(file_obj, n_spectra): tmpfile = str(file_obj) + ".fits" spec_HDUList(n_spectra).writeto(tmpfile, overwrite=True) - data = SpectrumList.read(tmpfile, format="SDSS-V spec multi") + data = SpectrumList.read(tmpfile) assert isinstance(data, SpectrumList) assert len(data) == n_spectra + 1 for i in range(n_spectra): @@ -613,6 +653,7 @@ def test_spec_list(file_obj, n_spectra): assert data[i].spectral_axis.unit == Angstrom assert len(data[i].mask) == 10 assert data[i].meta["header"].get("foobar") == "barfoo" + os.remove(tmpfile) @pytest.mark.parametrize( @@ -631,6 +672,7 @@ def test_spec_1d_fail_hdu(file_obj, hdu): with pytest.raises(ValueError): Spectrum1D.read(tmpfile, hdu=hdu) + os.remove(tmpfile) @pytest.mark.parametrize( @@ -653,6 +695,7 @@ def test_apStar_1D(file_obj, idx): assert data.spectral_axis.unit == Angstrom assert data.meta["header"].get("foobar") == "barfoo" + os.remove(tmpfile) @pytest.mark.parametrize( @@ -666,7 +709,7 @@ def test_apStar_list(file_obj, n_spectra): tmpfile = str(file_obj) + ".fits" apStar_HDUList(n_spectra).writeto(tmpfile, overwrite=True) - data = SpectrumList.read(tmpfile, format="SDSS-V apStar multi") + data = SpectrumList.read(tmpfile) assert isinstance(data, SpectrumList) assert len(data) == n_spectra for i in range(len(data)): @@ -675,6 +718,7 @@ def test_apStar_list(file_obj, n_spectra): assert data[i].flux.unit == Unit("1e-17 erg / (s cm2 Angstrom)") assert len(data[i].spectral_axis.value) == 10 assert data[i].meta["header"].get("foobar") == "barfoo" + os.remove(tmpfile) @pytest.mark.parametrize( @@ -689,7 +733,8 @@ def test_apStar_fail_list(file_obj): apStar_HDUList(1).writeto(tmpfile, overwrite=True) with pytest.raises(ValueError): - SpectrumList.read(tmpfile, format="SDSS-V apStar multi") + SpectrumList.read(tmpfile) + os.remove(tmpfile) @pytest.mark.parametrize( @@ -707,6 +752,7 @@ def test_apVisit_1D(file_obj): assert np.array_equal(data.spectral_axis.value, np.arange(1, 31, 1)) assert len(data.flux.value) == 30 assert data.meta["header"].get("foobar") == "barfoo" + os.remove(tmpfile) @pytest.mark.parametrize( @@ -719,6 +765,7 @@ def test_apVisit_list(file_obj): tmpfile = str(file_obj) + ".fits" apVisit_HDUList().writeto(tmpfile, overwrite=True) - data = SpectrumList.read(tmpfile, format="SDSS-V apVisit multi") + data = SpectrumList.read(tmpfile) assert isinstance(data, SpectrumList) assert len(data) == 3 + os.remove(tmpfile) diff --git a/specutils/manipulation/extract_spectral_region.py b/specutils/manipulation/extract_spectral_region.py index 720754130..a9e5e2963 100644 --- a/specutils/manipulation/extract_spectral_region.py +++ b/specutils/manipulation/extract_spectral_region.py @@ -103,7 +103,11 @@ def _subregion_to_edge_pixels(subregion, spectrum): right_index = _edge_value_to_pixel(right_reg_in_spec_unit, spectrum, order, "right") - return left_index, right_index + # If the spectrum is in wavelength and region is in Hz (for example), these still might be reversed + if left_index < right_index: + return left_index, right_index + else: + return right_index, left_index def extract_region(spectrum, region, return_single_spectrum=False): diff --git a/specutils/manipulation/resample.py b/specutils/manipulation/resample.py index e208d2d14..b02596c30 100644 --- a/specutils/manipulation/resample.py +++ b/specutils/manipulation/resample.py @@ -461,8 +461,8 @@ def resample1d(self, orig_spectrum, fin_spec_axis): if self.extrapolation_treatment == 'zero_fill': fill_val = 0 - origedges = orig_spectrum.spectral_axis.bin_edges - off_edges = (fin_spec_axis < origedges[0]) | (origedges[-1] < fin_spec_axis) + orig_edges = orig_spectrum.spectral_axis.bin_edges + off_edges = (fin_spec_axis < np.min(orig_edges)) | (np.max(orig_edges) < fin_spec_axis) out_flux_val[off_edges] = fill_val if new_unc is not None: new_unc.array[off_edges] = fill_val diff --git a/specutils/spectra/spectrum_mixin.py b/specutils/spectra/spectrum_mixin.py index 6479c0df5..06fa32123 100644 --- a/specutils/spectra/spectrum_mixin.py +++ b/specutils/spectra/spectrum_mixin.py @@ -86,6 +86,26 @@ def new_flux_unit(self, unit, equivalencies=None, suppress_conversion=False): return self.with_flux_unit(unit, equivalencies=equivalencies, suppress_conversion=suppress_conversion) + def _convert_flux(self, unit, equivalencies=None, suppress_conversion=False): + """This is always done in-place. + Also see :meth:`with_flux_unit`.""" + + if not suppress_conversion: + if equivalencies is None: + equivalencies = eq.spectral_density(self.spectral_axis) + + new_data = self.flux.to(unit, equivalencies=equivalencies) + + self._data = new_data.value + self._unit = new_data.unit + else: + self._unit = u.Unit(unit) + + if self.uncertainty is not None: + self.uncertainty = StdDevUncertainty( + self.uncertainty.represent_as(StdDevUncertainty).quantity.to( + unit, equivalencies=equivalencies)) + def with_flux_unit(self, unit, equivalencies=None, suppress_conversion=False): """Returns a new spectrum with a different flux unit. If uncertainty is defined, it will be converted to @@ -107,30 +127,14 @@ def with_flux_unit(self, unit, equivalencies=None, suppress_conversion=False): Returns ------- - `~specutils.Spectrum1D` + new_spec : `~specutils.Spectrum1D` A new spectrum with the converted flux array (and uncertainty, if applicable). """ new_spec = deepcopy(self) - - if not suppress_conversion: - if equivalencies is None: - equivalencies = eq.spectral_density(self.spectral_axis) - - new_data = self.flux.to( - unit, equivalencies=equivalencies) - - new_spec._data = new_data.value - new_spec._unit = new_data.unit - else: - new_spec._unit = u.Unit(unit) - - if self.uncertainty is not None: - new_spec.uncertainty = StdDevUncertainty( - self.uncertainty.represent_as(StdDevUncertainty).quantity.to( - unit, equivalencies=equivalencies)) - + new_spec._convert_flux( + unit, equivalencies=equivalencies, suppress_conversion=suppress_conversion) return new_spec @property @@ -175,7 +179,7 @@ def velocity(self): Returns ------- - ~`astropy.units.Quantity` + new_data : `~astropy.units.Quantity` The converted dispersion array in the new dispersion space. """ if self.rest_value is None: @@ -202,8 +206,7 @@ def with_spectral_unit(self, unit, velocity_convention=None, self.with_spectral_axis_unit(unit, velocity_convention=velocity_convention, rest_value=rest_value) - def with_spectral_axis_unit(self, unit, velocity_convention=None, - rest_value=None): + def with_spectral_axis_unit(self, unit, velocity_convention=None, rest_value=None): """ Returns a new spectrum with a different spectral axis unit. Note that this creates a new object using the converted spectral axis and thus drops the original WCS, if it existed, @@ -230,11 +233,9 @@ def with_spectral_axis_unit(self, unit, velocity_convention=None, even if your spectrum has air wavelength units """ - - velocity_convention = velocity_convention if velocity_convention is not None else self.velocity_convention # noqa + velocity_convention = velocity_convention if velocity_convention is not None else self.velocity_convention # noqa rest_value = rest_value if rest_value is not None else self.rest_value - unit = self._new_wcs_argument_validation(unit, velocity_convention, - rest_value) + unit = self._new_wcs_argument_validation(unit, velocity_convention, rest_value) # Store the original unit information and WCS for posterity meta = deepcopy(self._meta) @@ -252,6 +253,24 @@ def with_spectral_axis_unit(self, unit, velocity_convention=None, return self.__class__(flux=self.flux, spectral_axis=new_spectral_axis, meta=meta, uncertainty=self.uncertainty, mask=self.mask) + def with_spectral_axis_and_flux_units(self, spectral_axis_unit, flux_unit, + velocity_convention=None, rest_value=None, + flux_equivalencies=None, suppress_flux_conversion=False): + """Perform :meth:`with_spectral_axis_unit` and :meth:`with_flux_unit` together. + See the respective methods for input and output definitions. + + Returns + ------- + new_spec : `~specutils.Spectrum1D` + Spectrum in requested units. + + """ + new_spec = self.with_spectral_axis_unit( + spectral_axis_unit, velocity_convention=velocity_convention, rest_value=rest_value) + new_spec._convert_flux( + flux_unit, equivalencies=flux_equivalencies, suppress_conversion=suppress_flux_conversion) + return new_spec + def _new_wcs_argument_validation(self, unit, velocity_convention, rest_value): # Allow string specification of units, for example diff --git a/specutils/tests/test_region_extract.py b/specutils/tests/test_region_extract.py index f40eb5b9f..2c46f6f12 100644 --- a/specutils/tests/test_region_extract.py +++ b/specutils/tests/test_region_extract.py @@ -18,10 +18,9 @@ def test_region_simple(simulated_spectra): - np.random.seed(42) spectrum = simulated_spectra.s1_um_mJy_e1 - uncertainty = StdDevUncertainty(0.1*np.random.random(len(spectrum.flux))*u.mJy) + uncertainty = StdDevUncertainty(np.full(spectrum.flux.shape, 0.1) * u.mJy) spectrum.uncertainty = uncertainty region = SpectralRegion(0.6*u.um, 0.8*u.um) @@ -112,10 +111,8 @@ def test_pixel_spectralaxis_extraction(): def test_slab_simple(simulated_spectra): - np.random.seed(42) - spectrum = simulated_spectra.s1_um_mJy_e1 - uncertainty = StdDevUncertainty(0.1*np.random.random(len(spectrum.flux))*u.mJy) + uncertainty = StdDevUncertainty(np.full(spectrum.flux.shape, 0.1) * u.mJy) spectrum.uncertainty = uncertainty sub_spectrum = spectral_slab(spectrum, 0.6*u.um, 0.8*u.um) @@ -148,6 +145,16 @@ def test_region_ghz(simulated_spectra): assert_quantity_allclose(sub_spectrum.flux, sub_spectrum_flux_expected) +def test_region_ghz_spectrum_wave(simulated_spectra): + spectrum = simulated_spectra.s1_um_mJy_e1 + region = SpectralRegion(499654.09666667*u.GHz, 374740.5725*u.GHz) + + sub_spectrum = extract_region(spectrum, region) + + sub_spectrum_flux_expected = FLUX_ARRAY * u.mJy + assert_quantity_allclose(sub_spectrum.flux, sub_spectrum_flux_expected) + + def test_region_simple_check_ends(simulated_spectra): np.random.seed(42) @@ -168,13 +175,11 @@ def test_region_simple_check_ends(simulated_spectra): assert sub_spectrum.spectral_axis.value[-1] == 25 -def test_region_empty(simulated_spectra): - np.random.seed(42) - +def test_region_empty(): empty_spectrum = Spectrum1D(spectral_axis=[]*u.um, flux=[]*u.Jy) # Region past upper range of spectrum - spectrum = Spectrum1D(spectral_axis=np.linspace(1, 25, 25)*u.um, flux=np.random.random(25)*u.Jy) + spectrum = Spectrum1D(spectral_axis=np.linspace(1, 25, 25)*u.um, flux=np.ones(25)*u.Jy) region = SpectralRegion(28*u.um, 30*u.um) sub_spectrum = extract_region(spectrum, region) @@ -185,7 +190,7 @@ def test_region_empty(simulated_spectra): assert sub_spectrum.flux.unit == empty_spectrum.flux.unit # Region below lower range of spectrum - spectrum = Spectrum1D(spectral_axis=np.linspace(1, 25, 25)*u.um, flux=np.random.random(25)*u.Jy) + spectrum = Spectrum1D(spectral_axis=np.linspace(1, 25, 25)*u.um, flux=np.ones(25)*u.Jy) region = SpectralRegion(0.1*u.um, 0.3*u.um) sub_spectrum = extract_region(spectrum, region) @@ -212,10 +217,8 @@ def test_region_empty(simulated_spectra): def test_region_descending(simulated_spectra): - np.random.seed(42) - spectrum = simulated_spectra.s1_um_mJy_e1 - uncertainty = StdDevUncertainty(0.1*np.random.random(len(spectrum.flux))*u.mJy) + uncertainty = StdDevUncertainty(np.full(spectrum.flux.shape, 0.1) * u.mJy) spectrum.uncertainty = uncertainty region = SpectralRegion(0.8*u.um, 0.6*u.um) @@ -244,10 +247,8 @@ def test_descending_spectral_axis(simulated_spectra): def test_region_two_sub(simulated_spectra): - np.random.seed(42) - spectrum = simulated_spectra.s1_um_mJy_e1 - uncertainty = StdDevUncertainty(0.1*np.random.random(len(spectrum.flux))*u.mJy) + uncertainty = StdDevUncertainty(np.full(spectrum.flux.shape, 0.1) * u.mJy) spectrum.uncertainty = uncertainty region = SpectralRegion([(0.6*u.um, 0.8*u.um), (0.86*u.um, 0.89*u.um)]) @@ -284,10 +285,8 @@ def test_region_two_sub(simulated_spectra): def test_bounding_region(simulated_spectra): - np.random.seed(42) - spectrum = simulated_spectra.s1_um_mJy_e1 - uncertainty = StdDevUncertainty(0.1*np.random.random(len(spectrum.flux))*u.mJy) + uncertainty = StdDevUncertainty(np.full(spectrum.flux.shape, 0.1) * u.mJy) spectrum.uncertainty = uncertainty region = SpectralRegion([(0.6*u.um, 0.8*u.um), (0.86*u.um, 0.89*u.um)]) diff --git a/specutils/tests/test_spectrum1d.py b/specutils/tests/test_spectrum1d.py index 7b8e1aac4..39ea23278 100644 --- a/specutils/tests/test_spectrum1d.py +++ b/specutils/tests/test_spectrum1d.py @@ -25,7 +25,7 @@ def test_empty_spectrum(): def test_create_from_arrays(): spec = Spectrum1D(spectral_axis=np.arange(50) * u.AA, - flux=np.random.randn(50) * u.Jy) + flux=np.ones(50) * u.Jy) assert isinstance(spec.spectral_axis, SpectralCoord) assert spec.spectral_axis.size == 50 @@ -36,7 +36,7 @@ def test_create_from_arrays(): # Test creating spectrum with unknown arguments with pytest.raises(ValueError): spec = Spectrum1D(wavelength=np.arange(1, 50) * u.nm, - flux=np.random.randn(48) * u.Jy) + flux=np.ones(48) * u.Jy) def test_create_from_multidimensional_arrays(): @@ -47,7 +47,7 @@ def test_create_from_multidimensional_arrays(): """ freqs = np.arange(50) * u.GHz - flux = np.random.random((5, len(freqs))) * u.Jy + flux = np.ones((5, len(freqs))) * u.Jy spec = Spectrum1D(spectral_axis=freqs, flux=flux) assert (spec.frequency == freqs).all() @@ -55,15 +55,15 @@ def test_create_from_multidimensional_arrays(): # Mis-matched lengths should raise an exception (unless freqs is one longer # than flux, in which case it's interpreted as bin edges) - freqs = np.arange(50) * u.GHz - flux = np.random.random((5, len(freqs)-10)) * u.Jy + flux = np.ones((5, len(freqs) - 10)) * u.Jy with pytest.raises(ValueError): spec = Spectrum1D(spectral_axis=freqs, flux=flux) def test_create_from_quantities(): - spec = Spectrum1D(spectral_axis=np.arange(1, 50) * u.nm, - flux=np.random.randn(49) * u.Jy) + wav = np.arange(1, 50) * u.nm + flux = np.ones(49) * u.Jy + spec = Spectrum1D(spectral_axis=wav, flux=flux) assert isinstance(spec.spectral_axis, SpectralCoord) assert spec.spectral_axis.unit == u.nm @@ -72,13 +72,12 @@ def test_create_from_quantities(): # Mis-matched lengths should raise an exception (unless freqs is one longer # than flux, in which case it's interpreted as bin edges) with pytest.raises(ValueError): - spec = Spectrum1D(spectral_axis=np.arange(1, 50) * u.nm, - flux=np.random.randn(47) * u.Jy) + spec = Spectrum1D(spectral_axis=wav, flux=np.ones(47) * u.Jy) def test_create_implicit_wcs(): spec = Spectrum1D(spectral_axis=np.arange(50) * u.AA, - flux=np.random.randn(50) * u.Jy) + flux=np.ones(50) * u.Jy) assert isinstance(spec.wcs, gwcs.wcs.WCS) @@ -90,7 +89,7 @@ def test_create_implicit_wcs(): def test_create_implicit_wcs_with_spectral_unit(): spec = Spectrum1D(spectral_axis=np.arange(1, 50) * u.nm, - flux=np.random.randn(49) * u.Jy) + flux=np.ones(49) * u.Jy) assert isinstance(spec.wcs, gwcs.wcs.WCS) @@ -102,8 +101,8 @@ def test_create_implicit_wcs_with_spectral_unit(): def test_create_with_spectral_coord(): - spectral_coord = SpectralCoord(np.arange(5100, 5150)*u.AA, radial_velocity=u.Quantity(1000.0, "km/s")) - flux = np.random.randn(50)*u.Jy + spectral_coord = SpectralCoord(np.arange(5100, 5150) * u.AA, radial_velocity=u.Quantity(1000.0, "km/s")) + flux = np.ones(50) * u.Jy spec = Spectrum1D(spectral_axis=spectral_coord, flux=flux) assert spec.radial_velocity == u.Quantity(1000.0, "km/s") @@ -137,26 +136,24 @@ def test_spectral_axis_conversions(): assert np.all(spec.spectral_axis == np.array([400, 500]) * u.angstrom) assert spec.spectral_axis.unit == u.angstrom - spec = Spectrum1D(spectral_axis=np.arange(50) * u.AA, - flux=np.random.randn(50) * u.Jy) + flux = np.ones(49) * u.Jy + spec = Spectrum1D(spectral_axis=np.arange(flux.size) * u.AA, flux=flux) assert spec.wavelength.unit == u.AA - spec = Spectrum1D(spectral_axis=np.arange(1, 50) * u.nm, - flux=np.random.randn(49) * u.Jy) + spec = Spectrum1D(spectral_axis=np.arange(1, 50) * u.nm, flux=flux) assert spec.frequency.unit == u.GHz with pytest.raises(ValueError): spec.velocity - spec = Spectrum1D(spectral_axis=np.arange(100, 150) * u.nm, - flux=np.random.randn(49) * u.Jy) + spec = Spectrum1D(spectral_axis=np.arange(100, 150) * u.nm, flux=flux) - new_spec = spec.with_spectral_axis_unit(u.km/u.s, rest_value=125*u.um, - velocity_convention="relativistic") + new_spec = spec.with_spectral_axis_unit(u.km / u.s, rest_value=125 * u.um, + velocity_convention="relativistic") - assert new_spec.spectral_axis.unit == u.km/u.s + assert new_spec.spectral_axis.unit == u.km / u.s assert new_spec.wcs.world_axis_units[0] == "km.s**-1" # Make sure meta stored the old WCS correctly assert new_spec.meta["original_wcs"].world_axis_units[0] == "nm" @@ -164,10 +161,10 @@ def test_spectral_axis_conversions(): wcs_dict = {"CTYPE1": "WAVE", "CRVAL1": 3.622e3, "CDELT1": 8e-2, "CRPIX1": 0, "CUNIT1": "Angstrom"} - wcs_spec = Spectrum1D(flux=np.random.randn(49) * u.Jy, wcs=WCS(wcs_dict), + wcs_spec = Spectrum1D(flux=flux, wcs=WCS(wcs_dict), meta={'header': wcs_dict.copy()}) - new_spec = wcs_spec.with_spectral_axis_unit(u.km/u.s, rest_value=125*u.um, - velocity_convention="relativistic") + new_spec = wcs_spec.with_spectral_axis_unit(u.km / u.s, rest_value=125 * u.um, + velocity_convention="relativistic") new_spec.meta['original_wcs'].wcs.crval = [3.777e-7] new_spec.meta['header']['CRVAL1'] = 3777.0 @@ -175,9 +172,26 @@ def test_spectral_axis_conversions(): assert wcs_spec.meta['header']['CRVAL1'] == 3622. +def test_spectral_axis_and_flux_conversions(): + """A little bit from both sets of tests.""" + spec = Spectrum1D(spectral_axis=np.arange(100, 150) * u.nm, + flux=np.ones(49) * u.Jy) + + new_spec = spec.with_spectral_axis_and_flux_units( + u.km / u.s, u.uJy, rest_value=125 * u.um, velocity_convention="relativistic") + + assert new_spec.spectral_axis.unit == u.km/u.s + assert new_spec.wcs.world_axis_units[0] == "km.s**-1" + # Make sure meta stored the old WCS correctly + assert new_spec.meta["original_wcs"].world_axis_units[0] == "nm" + assert new_spec.meta["original_spectral_axis_unit"] == "nm" + assert new_spec.flux.unit == u.uJy + assert_allclose(new_spec.flux.value, 1000000) + + def test_spectral_slice(): spec = Spectrum1D(spectral_axis=np.linspace(100, 1000, 10) * u.nm, - flux=np.random.random(10) * u.Jy) + flux=np.ones(10) * u.Jy) sliced_spec = spec[300*u.nm:600*u.nm] assert np.all(sliced_spec.spectral_axis == [300, 400, 500] * u.nm) @@ -192,7 +206,7 @@ def test_spectral_slice(): # Test higher dimensional slicing spec = Spectrum1D(spectral_axis=np.linspace(100, 1000, 10) * u.nm, - flux=np.random.random((10, 10)) * u.Jy) + flux=np.ones((10, 10)) * u.Jy) sliced_spec = spec[300*u.nm:600*u.nm] assert np.all(sliced_spec.spectral_axis == [300, 400, 500] * u.nm) @@ -302,7 +316,7 @@ def test_flux_unit_conversion(): def test_wcs_transformations(): # Test with a GWCS spec = Spectrum1D(spectral_axis=np.arange(1, 50) * u.nm, - flux=np.random.randn(49) * u.Jy) + flux=np.ones(49) * u.Jy) pix_axis = spec.wcs.world_to_pixel(np.arange(20, 30) * u.nm) disp_axis = spec.wcs.pixel_to_world(np.arange(20, 30)) @@ -359,24 +373,19 @@ def test_create_explicit_fitswcs(): def test_create_with_uncertainty(): spec = Spectrum1D(spectral_axis=np.arange(1, 50) * u.nm, - flux=np.random.sample(49) * u.Jy, - uncertainty=StdDevUncertainty(np.random.sample(49) * 0.1)) + flux=np.ones(49) * u.Jy, + uncertainty=StdDevUncertainty(np.ones(49) * 0.1)) assert isinstance(spec.uncertainty, StdDevUncertainty) - - spec = Spectrum1D(spectral_axis=np.arange(1, 50) * u.nm, - flux=np.random.sample(49) * u.Jy, - uncertainty=StdDevUncertainty(np.random.sample(49) * 0.1)) - assert spec.flux.unit == spec.uncertainty.unit # If flux and uncertainty are different sizes then raise exception - wavelengths = np.arange(0, 10) - flux=100*np.abs(np.random.randn(3, 4, 10))*u.Jy - uncertainty = StdDevUncertainty(np.abs(np.random.randn(3, 2, 10))*u.Jy) + wavelengths = np.arange(10) * u.um + flux= np.ones((3, 4, 10)) * u.Jy + uncertainty = StdDevUncertainty(np.ones((3, 2, 10)) * u.Jy) with pytest.raises(ValueError): - Spectrum1D(spectral_axis=wavelengths*u.um, flux=flux, uncertainty=uncertainty) + Spectrum1D(spectral_axis=wavelengths, flux=flux, uncertainty=uncertainty) @pytest.mark.parametrize("flux_unit", ["adu", "ct/s", "count"]) @@ -407,7 +416,7 @@ def test_read_linear_solution(remote_data_path): def test_energy_photon_flux(): spec = Spectrum1D(spectral_axis=np.linspace(100, 1000, 10) * u.nm, - flux=np.random.randn(10)*u.Jy) + flux=np.ones(10) * u.Jy) assert spec.energy.size == 10 assert spec.photon_flux.size == 10 assert spec.photon_flux.unit == u.photon * u.cm**-2 * u.s**-1 * u.nm**-1 @@ -415,7 +424,7 @@ def test_energy_photon_flux(): def test_flux_nans_propagate_to_mask(): """Check that indices in input flux with NaNs get propagated to the mask""" - flux = np.random.randn(10) + flux = np.ones(10) nan_idx = [0, 3, 5] flux[nan_idx] = np.nan spec = Spectrum1D(spectral_axis=np.linspace(100, 1000, 10) * u.nm, @@ -424,16 +433,15 @@ def test_flux_nans_propagate_to_mask(): def test_repr(): - spec_with_wcs = Spectrum1D(spectral_axis=np.linspace(100, 1000, 10) * u.nm, - flux=np.random.random(10) * u.Jy) + wav = np.linspace(100, 1000, 10) * u.nm + flux = np.ones(10) * u.Jy + spec_with_wcs = Spectrum1D(spectral_axis=wav, flux=flux) result = repr(spec_with_wcs) assert result.startswith('