Quickstart Tutorial¶

[1]:
from IPython.display import Image

import warnings
warnings.filterwarnings('ignore')
[2]:
import eleanor
import numpy as np
from astropy import units as u
import matplotlib.pyplot as plt
from astropy.coordinates import SkyCoord

1.1 Updating eleanor¶

To update eleanor to work with newly digested sectors on MAST (Mikulski Archive for Space Telescopes), one does not need to update the eleanor version, but rather we have everything you need set-up in eleanor.Update().

This call will download a target in the CVZ of either the northern or southern ecliptic hemisphere to create quality flags for all targets in this sector. This will also download the co-trending basis vectors (CBVs) used to best detrend systematics from our light curves. All of this data is stored in a hidden local directory: ~/.eleanor/metadata/sNNNN, where NNNN corresponds to the sector.

This shouldn’t take long, but the exact time will depend on the strength of your internet connection.

[3]:
eleanor.Update(sector=27)
Inflating...
This is the first light curve you have made for this sector. Getting eleanor metadata products for Sector 27...
This will only take a minute, and only needs to be done once. Any other light curves you make in this sector will be faster.
Target Acquired
Quality Flags Assured
Success! Sector 27 now available.
[3]:

If you are having trouble creating a light curve later down the line, it may be worth clearing this directory and trying to redownload all of the files.

1.2 Fast Light Curves¶

As a simple first example, we will use $$\texttt{eleanor}$$ to create a target pixel file (TPF) and light curve for a given TIC target.

First, we will load the data for TIC 38846515 (WASP-100), a relatively bright star observed in Sector 1. $$\texttt{eleanor}$$ is able to extract a light curve easily and automatically for this unblended object. Calling source will assign a $$\textit{Gaia}$$ DR2 ID, TESS magnitude, sector, camera, ccd, and associated postcard.

[4]:
star = eleanor.Source(tic=38846515, sector=1)

print('Found TIC {0} (Gaia {1}), with TESS magnitude {2}, RA {3}, and Dec {4}'
.format(star.tic, star.gaia, star.tess_mag, star.coords[0], star.coords[1]))
Found TIC 38846515 (Gaia 4675352109658261376), with TESS magnitude 10.3157, RA 68.9597092399964, and Dec -64.0270368810381

Here, the software is connecting to the MAST servers to download an $$\texttt{eleanor}$$ postcard file, postcard background file, and the pointing model for that sector-camera-CCD. A postcard is a 104 $$\times$$ 148 pixel cutout region of the entire FFI. The postcard background file is a 2D-interpolated background model across the postcard. The pointing model is an additional file created that performs an affine transformation on top of the WCS to ensure the most accurate pixel location of your target is.

If you do not know what sector your target was observed in, calling eleanor.Source() without a sector keyword passed through explicitly will return the most recent sector for which the star was observed. If you think your target may have been observed in multiple sectors, you may want to consider eleanor.multi_sectors(), as described below.

In addition to passing in a TIC ID, we can also pass through a Simbad-resolvable name, a Gaia DR2 ID, or an RA/Dec pair, either as a tuple or an Astropy SkyCoord object. For example, the following three calls all point to the same target:

[5]:
star = eleanor.Source(name='WASP-100', sector=1)

print('Found TIC {0} (Gaia {1}), with TESS magnitude {2}, RA {3}, and Dec {4}'
.format(star.tic, star.gaia, star.tess_mag, star.coords[0], star.coords[1]))

coords = (68.959732, -64.02704)
# or
coords = SkyCoord(ra=68.959732, dec=-64.02704, unit=(u.deg, u.deg))

star = eleanor.Source(coords=coords, sector=1)

print('Found TIC {0} (Gaia {1}), with TESS magnitude {2}, RA {3}, and Dec {4}'
.format(star.tic, star.gaia, star.tess_mag, star.coords[0], star.coords[1]))

star = eleanor.Source(gaia=4675352109658261376, sector=1)

print('Found TIC {0} (Gaia {1}), with TESS magnitude {2}, RA {3}, and Dec {4}'
.format(star.tic, star.gaia, star.tess_mag, star.coords[0], star.coords[1]))
Found TIC 38846515 (Gaia 4675352109658261376), with TESS magnitude 10.3157, RA 68.95970916666666, and Dec -64.02703666666666
Found TIC 38846515 (Gaia 4675352109658261376), with TESS magnitude 10.3157, RA 68.959732, and Dec -64.02704
Created TAP+ (v1.2.1) - Connection:
Host: gea.esac.esa.int
Use HTTPS: True
Port: 443
SSL Port: 443
Created TAP+ (v1.2.1) - Connection:
Use HTTPS: True
Port: 443
SSL Port: 443
Found TIC 38846515 (Gaia 4675352109658261376), with TESS magnitude 10.3157, RA 68.95981586357303, and Dec -64.02703903114245

Gaia has quite a bit of precision on the star’s position! Now that we have our Source information, we simply call the eleanor.TargetData() function, which will extract a target pixel file from the postcard, perform aperture photometry, and complete some simple systematics corrections. This function chooses an aperture which optimizes the light curve for transit searches.

We can also set some operations when calling this function. For example, do_pca = True tells $$\texttt{eleanor}$$ to use the cotrending basis vectors on a background subtracted light curve without any additional correction. And do_psf = True creates a point spread function modeled light curve of our target.

We can also pass through a regressors argument that is either a list of arrays of length time or 'corner'. These will be passed in as other terms to regress against in the detrending. If 'corner' is set, it will use the four pixels in the corners of the TPF to infer the background. regressors is an optional argument: if it is not set, no additional regressors will be used. This is shown in more detail in the tips, tricks, and hacks notebook.

In addition to automatically choosing an aperture, eleanor.TargetData() also tries a few different background subtractions to remove as much background noise as possible. The three options it tries are:

• 1D postcard background : A constant ($$3\mu + 2med$$) is calculated from each postcard frame

• 1D TPF background : A constant ($$3\mu + 2med$$) is calculated from each TPF frame

• 2D background : The 2D background pixels are subtracted from the TPF

For both 1D options, stars are masked before estimating the background value.

[6]:
data = eleanor.TargetData(star, height=15, width=15, bkg_size=31, do_psf=True, do_pca=True, regressors='corner')
WARNING: Logging before flag parsing goes to stderr.
W1006 09:40:37.417151 4633564608 module_wrapper.py:139] From //anaconda3/lib/python3.7/site-packages/eleanor/targetdata.py:841: The name tf.logging.set_verbosity is deprecated. Please use tf.compat.v1.logging.set_verbosity instead.

W1006 09:40:37.417876 4633564608 module_wrapper.py:139] From //anaconda3/lib/python3.7/site-packages/eleanor/targetdata.py:841: The name tf.logging.ERROR is deprecated. Please use tf.compat.v1.logging.ERROR instead.

100%|██████████| 1282/1282 [00:11<00:00, 112.48it/s]

Let’s look at our new light curves!

Plotted below are the following: - Black : the raw light curve (aperture $$\times$$ TPF + background subtraction) - Red : the corrected light curve (black light curve + additional corrections) - Green : the PCA light curve (aperture $$\times$$ TPF + background subtraction + cotrending basis vectors) - Blue : the PSF modeled light curve

Before plotting, we can use quality flags to mask poor quality data. Our quality flags are taken from the two-minute $$\textit{TESS}$$ light curves and binned to 30-minutes. We are very generous with our masking, so any flag that applies to the entire FFI is marked in data.quality. We also introduce our own quality flag for when the pointing model significantly deviates from a best-fit line in the sector (see Figure 2 in Feinstein et al. 2019).

[7]:
plt.figure(figsize=(15,5))

q = data.quality == 0

plt.plot(data.time[q], data.raw_flux[q]/np.nanmedian(data.raw_flux[q])+0.06, 'k')
plt.plot(data.time[q], data.corr_flux[q]/np.nanmedian(data.corr_flux[q]) + 0.03, 'r')
plt.plot(data.time[q], data.pca_flux[q]/np.nanmedian(data.pca_flux[q]), 'g')
plt.plot(data.time[q], data.psf_flux[q]/np.nanmedian(data.psf_flux[q]) - 0.02, 'b')
plt.ylabel('Normalized Flux')
plt.xlabel('Time [BJD - 2457000]')
plt.title('WASP-100');

There’s a planet there!

For this star, we found the postcard level 1D background did a good job at removing additional noise in the data. We can check which background model was used by calling:

[8]:
data.bkg_type
[8]:
'2D_BKG_MODEL'

We can even plot the background as a function of time to see what was removed from our light curve. All three background models are saved and can be called by the following:

• 1D postcard background : data.bkg_type = ‘PC_LEVEL’ : keyword = data.flux_bkg

• 1D TPF background : data.bkg_type = ‘TPF_LEVEL’ : keyword = data.tpf_flux_bkg

• 2D TPF backround : data.bkg_type = ‘TPF_2D_LEVEL’ : keyword = data.bkg_tpf

[9]:
plt.figure(figsize=(15,5))

plt.plot(data.time, data.flux_bkg, 'k', label='1D postcard', linewidth=3)
plt.plot(data.time, data.tpf_flux_bkg, 'r--', label='1D TPF', linewidth=2)
plt.ylabel('Normalized Flux')
plt.xlabel('Time [BJD - 2457000]')
plt.legend();

And to see what a frame of the 2D background looks like, we can plot it as

[10]:
fig, (ax1, ax2) = plt.subplots(ncols=2, figsize=(10,4))
ax1.imshow(data.tpf[0])
ax1.set_title('Target Pixel File')
ax2.imshow(data.bkg_tpf[0])
ax2.set_title('2D interpolated background');

What does our automatically chosen aperture look like? And how does it compare to where the star falls on the TPF?

[11]:
fig, (ax1, ax2) = plt.subplots(ncols=2, figsize=(10,4))
ax1.imshow(data.tpf[0])
ax1.set_title('Target Pixel File')
ax2.imshow(data.aperture)
ax2.set_title('Aperture');

Let’s save these data to a FITS file so we have them later. By default, this will be saved to a ~/.eleanor directory, but that can be changed by setting a directory.

[12]:
data.save()

With one line of code, we can pass this through as a lightkurve object and begin using the functionality of that package.

[13]:
lk = data.to_lightkurve()
lk.plot()
[13]:
<matplotlib.axes._subplots.AxesSubplot at 0x15fbe25c0>

1.3 Stars Observed in Multiple Sectors¶

$$\texttt{eleanor}$$ has some built-in tools to make it easier to work with stars that are observed in multiple TESS sectors. Let’s consider WASP-100 again, which was observed in both Sectors 1 and 2. When we previously called eleanor.Source(), we can instead call eleanor.multi_sectors(), which will make a list of Source objects for all sectors requested.

[14]:
star = eleanor.multi_sectors(tic=38846515, sectors=[1,2])
I1006 09:40:53.029156 4633564608 query.py:325] Found cached file /Users/arcticfox/.eleanor/mastDownload/HLSP/hlsp_eleanor_tess_ffi_postcard-s0001-4-1-cal-0902-1078_tess_v2_pc/hlsp_eleanor_tess_ffi_postcard-s0001-4-1-cal-0902-1078_tess_v2_bkg.fits with expected size 78955200.
I1006 09:40:53.230607 4633564608 query.py:325] Found cached file /Users/arcticfox/.eleanor/mastDownload/HLSP/hlsp_eleanor_tess_ffi_postcard-s0001-4-1-cal-0902-1078_tess_v2_pc/hlsp_eleanor_tess_ffi_postcard-s0001-4-1-cal-0902-1078_tess_v2_pc.fits with expected size 158022720.
I1006 09:40:53.416737 4633564608 query.py:325] Found cached file /Users/arcticfox/.eleanor/mastDownload/HLSP/hlsp_eleanor_tess_ffi_postcard-s0001-4-1-cal-0902-1078_tess_v2_pc/hlsp_eleanor_tess_ffi_postcard-s0001-4-1_tess_v2_pm.txt with expected size 237847.

In this case, I only wanted sectors 1 and 2. However, I could have passed through sectors='all' to get all sectors in which this star was observed.

[15]:
print(star)
[<eleanor.source.Source object at 0x16082cd68>, <eleanor.source.Source object at 0x127af8ba8>]

Yep, it’s a list of objects! We can then call eleanor.TargetData() on each.

[16]:
plt.figure(figsize=(15,5))

data = []
plot_fmt = ['k.', 'r.']

for s in star:
datum = eleanor.TargetData(s, height=15, width=15, bkg_size=31, do_psf=False, do_pca=False)
data.append(datum)

for sector, datum in enumerate(data):
q = datum.quality == 0
plt.plot(datum.time[q], datum.corr_flux[q]/np.median(datum.corr_flux[q]), plot_fmt[sector])

plt.ylabel('Normalized Flux', fontsize=24)
plt.xlabel('Time', fontsize=24)

plt.show()
We’re still exploring what the best practices are generally. Good practices seem to differ across the detector, but here’s what we believe so far. If you discover anything that works well for you, we and other $$\texttt{eleanor}$$ users would surely love to know them! You can also find some tips, tricks, and hacks in the next example notebook.