Advanced Spectrum List Patterns and Workarounds

muler is built on specutils, which has many spectrum list operations, but not everything we’d ideally want. In this notebook, we introduce a few workarounds to deal with common patterns.

[1]:
%config Completer.use_jedi = False
[2]:
from muler.igrins import IGRINSSpectrum, IGRINSSpectrumList
from specutils import Spectrum1D, SpectrumList
import numpy as np
import matplotlib.pyplot as pltf
import astropy.units as u
%matplotlib inline
%config InlineBackend.figure_format='retina'
---------------------------------------------------------------------------
ModuleNotFoundError                       Traceback (most recent call last)
Cell In[2], line 1
----> 1 from muler.igrins import IGRINSSpectrum, IGRINSSpectrumList
      2 from specutils import Spectrum1D, SpectrumList
      3 import numpy as np

File ~/checkouts/readthedocs.org/user_builds/muler/envs/latest/lib/python3.8/site-packages/muler/igrins.py:15
     13 import json
     14 from matplotlib import pyplot as plt
---> 15 from muler.echelle import EchelleSpectrum, EchelleSpectrumList
     16 from muler.utilities import Slit, concatenate_orders, resample_list, roll_along_axis, edge_normalize, isolate_and_normalize_hi_order, round_to_multiple, photometry
     17 from astropy.time import Time

File ~/checkouts/readthedocs.org/user_builds/muler/envs/latest/lib/python3.8/site-packages/muler/echelle.py:31
     29 from scipy.ndimage import median_filter, gaussian_filter1d
     30 import specutils
---> 31 from muler.utilities import apply_numpy_mask, is_list, resample_list
     34 # from barycorrpy import get_BC_vel
     35 from astropy.coordinates import SkyCoord, EarthLocation

File ~/checkouts/readthedocs.org/user_builds/muler/envs/latest/lib/python3.8/site-packages/muler/utilities.py:15
     13 from astropy.convolution import convolve, Gaussian1DKernel
     14 from scipy.ndimage import binary_dilation
---> 15 from astroquery.simbad import Simbad
     16 Simbad.add_votable_fields('V', 'B', 'J', 'H', 'K', 'parallax')
     17 LinInterpResampler = LinearInterpolatedResampler()

ModuleNotFoundError: No module named 'astroquery'

Simply tell the IGRINS Spectrum where your file is located.

[3]:
path = 'https://github.com/OttoStruve/muler_example_data/raw/main/IGRINS/01_IGRINS_test_data/'
filename1='SDCH_20201202_0059.spec_a0v.fits'
full_path = path + filename1
[4]:
spec_list = IGRINSSpectrumList.read(full_path).normalize()
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[4], line 1
----> 1 spec_list = IGRINSSpectrumList.read(full_path).normalize()

NameError: name 'IGRINSSpectrumList' is not defined
[5]:
spec_list.remove_nans().trim_edges().normalize(order_index=15).plot(color=None, ylo=0, yhi=1.5);
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[5], line 1
----> 1 spec_list.remove_nans().trim_edges().normalize(order_index=15).plot(color=None, ylo=0, yhi=1.5);

NameError: name 'spec_list' is not defined

Unifying H and K bands into a single IGRINSSpectrumList

[6]:
filename2 = 'SDCK_20201202_0059.spec_a0v.fits'
spec_list2 = IGRINSSpectrumList.read(path+filename2).normalize()
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[6], line 2
      1 filename2 = 'SDCK_20201202_0059.spec_a0v.fits'
----> 2 spec_list2 = IGRINSSpectrumList.read(path+filename2).normalize()

NameError: name 'IGRINSSpectrumList' is not defined

The extend() method works in-place, overriding the first argument! We have to copy the spec_list if we want to keep it pristine:

[7]:
import copy
[8]:
full_H_and_K_spectrum = copy.deepcopy(spec_list)
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[8], line 1
----> 1 full_H_and_K_spectrum = copy.deepcopy(spec_list)

NameError: name 'spec_list' is not defined
[9]:
full_H_and_K_spectrum.extend(spec_list2)
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[9], line 1
----> 1 full_H_and_K_spectrum.extend(spec_list2)

NameError: name 'full_H_and_K_spectrum' is not defined
[10]:
len(spec_list), len(spec_list2), len(full_H_and_K_spectrum)
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[10], line 1
----> 1 len(spec_list), len(spec_list2), len(full_H_and_K_spectrum)

NameError: name 'spec_list' is not defined
[11]:
full_H_and_K_spectrum.remove_nans().trim_edges().flatten().plot(color=None, ylo=0.8, yhi=1.2);
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[11], line 1
----> 1 full_H_and_K_spectrum.remove_nans().trim_edges().flatten().plot(color=None, ylo=0.8, yhi=1.2);

NameError: name 'full_H_and_K_spectrum' is not defined

Workarounds

Here we introduce workarounds to three friction points.

Issue 1: Slicing a IGRINSSpectrumList

First, we want to be able to index into the IGRINSSpectrumList in the same way you would with a regular old Python List, and still get back an IGRINSSpectrumList.

[12]:
type(spec_list)
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[12], line 1
----> 1 type(spec_list)

NameError: name 'spec_list' is not defined
[13]:
sublist = spec_list[4:7]
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[13], line 1
----> 1 sublist = spec_list[4:7]

NameError: name 'spec_list' is not defined
[14]:
type(sublist)
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[14], line 1
----> 1 type(sublist)

NameError: name 'sublist' is not defined

No! We want this sublist to be an IGRINSSpectrumList

[15]:
sublist_IGRINS = IGRINSSpectrumList(sublist)
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[15], line 1
----> 1 sublist_IGRINS = IGRINSSpectrumList(sublist)

NameError: name 'IGRINSSpectrumList' is not defined
[16]:
type(sublist_IGRINS)
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[16], line 1
----> 1 type(sublist_IGRINS)

NameError: name 'sublist_IGRINS' is not defined

Yay! This workaround achieved our goal.

Issue 2: Create an IGRINSSpectrum from a bare (flux, wavelength) array

Often we have already preprocessed a spectrum and have it in-hand as a pair of wavelength coordinates and flux values. We wish to turn this most elemental format of a spectrum into a IGRINSSpectrum.

[17]:
import astropy.units as u
[18]:
whole_spectrum = spec_list.remove_nans().trim_edges().normalize().stitch()
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[18], line 1
----> 1 whole_spectrum = spec_list.remove_nans().trim_edges().normalize().stitch()

NameError: name 'spec_list' is not defined
[19]:
len(whole_spectrum.flux.value)
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[19], line 1
----> 1 len(whole_spectrum.flux.value)

NameError: name 'whole_spectrum' is not defined
[20]:
wavelength = whole_spectrum.wavelength.value
flux = whole_spectrum.flux.value
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[20], line 1
----> 1 wavelength = whole_spectrum.wavelength.value
      2 flux = whole_spectrum.flux.value

NameError: name 'whole_spectrum' is not defined

Can we “round-trip” this pair of coordinates and flux values back into an IGRINSSpectrum?

[21]:
roundtrip_spec = IGRINSSpectrum(spectral_axis=wavelength*u.Angstrom,
                                flux=flux*u.dimensionless_unscaled, )
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[21], line 1
----> 1 roundtrip_spec = IGRINSSpectrum(spectral_axis=wavelength*u.Angstrom,
      2                                 flux=flux*u.dimensionless_unscaled, )

NameError: name 'IGRINSSpectrum' is not defined
[22]:
type(roundtrip_spec)
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[22], line 1
----> 1 type(roundtrip_spec)

NameError: name 'roundtrip_spec' is not defined

Some methods will work out-of-the-box:

[23]:
roundtrip_spec.normalize() # works fine!
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[23], line 1
----> 1 roundtrip_spec.normalize() # works fine!

NameError: name 'roundtrip_spec' is not defined

But others do not!

[24]:
output = whole_spectrum.barycentric_correct() # works fine!
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[24], line 1
----> 1 output = whole_spectrum.barycentric_correct() # works fine!

NameError: name 'whole_spectrum' is not defined

Watch out! This next line will not work!

[25]:
force = False

if force:
    roundtrip_spec.barycentric_correct() # Oops! Will not work!