Telluric correction with muler and gollum

Telluric correction can be complicated, with the right approach depending on one’s own science application. In this notebook we demonstrate one uncontroversial, albeit imperfect approach: dividing by an observed A0V standard, and multiplying back by an A0V template.

August 24, 2021: This method is under active development

[1]:
%config InlineBackend.figure_format='retina'
from muler.hpf import HPFSpectrum
---------------------------------------------------------------------------
ModuleNotFoundError                       Traceback (most recent call last)
Cell In[1], line 2
      1 get_ipython().run_line_magic('config', "InlineBackend.figure_format='retina'")
----> 2 from muler.hpf import HPFSpectrum

File ~/checkouts/readthedocs.org/user_builds/muler/envs/latest/lib/python3.8/site-packages/muler/hpf.py:14
     12 import warnings
     13 import logging
---> 14 from muler.echelle import EchelleSpectrum, EchelleSpectrumList
     15 from muler.utilities import resample_list
     16 import numpy as np

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'
[2]:
path = 'https://github.com/OttoStruve/muler_example_data/raw/main/HPF/01_A0V_standards/'
filename = 'Goldilocks_20210517T054403_v1.0_0060.spectra.fits'
spectrum = HPFSpectrum(file=path+filename, order=19)
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[2], line 3
      1 path = 'https://github.com/OttoStruve/muler_example_data/raw/main/HPF/01_A0V_standards/'
      2 filename = 'Goldilocks_20210517T054403_v1.0_0060.spectra.fits'
----> 3 spectrum = HPFSpectrum(file=path+filename, order=19)

NameError: name 'HPFSpectrum' is not defined
[3]:
spectrum = spectrum.normalize().sky_subtract(method='vector').deblaze().normalize()
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[3], line 1
----> 1 spectrum = spectrum.normalize().sky_subtract(method='vector').deblaze().normalize()

NameError: name 'spectrum' is not defined

Retrieve and doctor the model with gollum

gollum is a sister project to muler. While not a dependency, it has lots of parallel use cases and we hope you will install it too. It makes it easy to retrieve and manipulate theoretical models. Follow the instructions in gollum for downloading the raw high-resolution PHOENIX models.

[4]:
from gollum.phoenix import PHOENIXSpectrum
[5]:
wl_lo, wl_hi = spectrum.wavelength.min().value*0.998, spectrum.wavelength.max().value*1.002
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[5], line 1
----> 1 wl_lo, wl_hi = spectrum.wavelength.min().value*0.998, spectrum.wavelength.max().value*1.002

NameError: name 'spectrum' is not defined
[6]:
native_resolution_template = PHOENIXSpectrum(teff=9600, logg=4.5, download=True,
                                             wl_lo=wl_lo, wl_hi=wl_hi).normalize()
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[6], line 2
      1 native_resolution_template = PHOENIXSpectrum(teff=9600, logg=4.5, download=True,
----> 2                                              wl_lo=wl_lo, wl_hi=wl_hi).normalize()

NameError: name 'wl_lo' is not defined
[7]:
ax = spectrum.plot(ylo=0.0, yhi=1.5, label='Observed A0V')
native_resolution_template.plot(ax=ax, label='Native resolution A0V template')
ax.set_xlim(10_820, 10_960)
ax.legend();
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[7], line 1
----> 1 ax = spectrum.plot(ylo=0.0, yhi=1.5, label='Observed A0V')
      2 native_resolution_template.plot(ax=ax, label='Native resolution A0V template')
      3 ax.set_xlim(10_820, 10_960)

NameError: name 'spectrum' is not defined

The deep Hydrogen lines appear to have a different shape, since the A0V template is shown with near-infinite resolution. Let’s “doctor the template” with rotational broadening.

[8]:
A0V_model = native_resolution_template.rotationally_broaden(130.0)
A0V_model = A0V_model.instrumental_broaden() # "just works" for HPF
A0V_model = A0V_model.rv_shift(25.0)
A0V_model = A0V_model.resample(spectrum)
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[8], line 1
----> 1 A0V_model = native_resolution_template.rotationally_broaden(130.0)
      2 A0V_model = A0V_model.instrumental_broaden() # "just works" for HPF
      3 A0V_model = A0V_model.rv_shift(25.0)

NameError: name 'native_resolution_template' is not defined
[9]:
ax = spectrum.plot(ylo=0.0, yhi=1.5, label='Observed A0V')
A0V_model.plot(ax=ax, label='Fine-tuned A0V model')
ax.legend();
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[9], line 1
----> 1 ax = spectrum.plot(ylo=0.0, yhi=1.5, label='Observed A0V')
      2 A0V_model.plot(ax=ax, label='Fine-tuned A0V model')
      3 ax.legend();

NameError: name 'spectrum' is not defined

Awesome! That’s a better fit! Let’s compute the ratio.

[10]:
telluric_response = spectrum / A0V_model
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[10], line 1
----> 1 telluric_response = spectrum / A0V_model

NameError: name 'spectrum' is not defined
[11]:
ax = telluric_response.plot(ylo=0);
ax.axhline(1.0, linestyle='dotted', color='k');
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[11], line 1
----> 1 ax = telluric_response.plot(ylo=0);
      2 ax.axhline(1.0, linestyle='dotted', color='k');

NameError: name 'telluric_response' is not defined

We see that the telluric response exhibits sharp discrete lines from the atmosphere. The spectrum also exhibits a smooth continuum variation likely attributable to the imperfections in the A0V templates. The imperfect A0V templates are a known limitation of this telluric correction strategy.