Exoplanet Data and TESS Light Curves Using Python Requests

Exo.mast provides several API tools to obtain the planet parameter information for exoplanets available via that interface. Also, when available you can use these tools to download and plot the TESS light curves.

It is all done via a RESTful API, but it is not available through astroquery. We will show you how to construct the url, do the get Request and manipulate the resulting information.

Imports
Get Planet Names
Get Planet Properties
Get TCE Light Curve
Get TCE Phase Folded Plot
Additional Resources
About This Notebook


Import statements

We need the following python packages to do the imports.

  • numpy for array manipulation
  • requests to make the RESTful API get requests
  • matplotlib.pyplot to plot
  • pandas to manipulate data
  • IPython.display.display and IPython.display.HTML to render the plot delivered by the API

We also specify two base URLs that we use repeatedly for these requests. The planet url is used to query the planet table. The Data Validation url is used to query the Data Validation database which holds the header and light curves that are contained in the Data Validation time series files. There is one entry per TCE. TCEs are events (they have a period and an epoch) found in the 2-min cadence data by the TESS Ames Pipeline. Targets with TCEs have this type of time series which have been flattened and can be easily folded at the period of the reported transit.

In [1]:
import numpy as np
import requests
import matplotlib.pyplot as plt
import pandas as p
from IPython.display import display, HTML

%matplotlib inline

planeturl = "https://exo.mast.stsci.edu/api/v0.1/exoplanets/"
dvurl = "https://exo.mast.stsci.edu/api/v0.1/dvdata/tess/"
header={}

Query Planet Table for WASP-18b

Get all of the known identifiers for WASP-18 b. This query takes one parameter, the name of the planet. The output is json which can be converted to a python dictionary.

In [2]:
planet_name = "WASP-18 b"

url = planeturl + "/identifiers/"

myparams = {"name":planet_name}

r = requests.get(url = url, params = myparams, headers = header)
print(r.headers.get('content-type'))
application/json; charset=UTF-8

If you take a look at the resulting dictionary it includes all the planet Names. It also includes tessID and tessTCE. These two fields will exist if there is a TCE on this TIC ID with the same period. We feed these into a variable for later use.

In [3]:
planet_names = r.json()
ticid = planet_names['tessID']
tce = planet_names['tessTCE']
planet_names
Out[3]:
{'canonicalName': 'WASP-18 b',
 'starName': 'WASP-18',
 'ra': 24.354208334287005,
 'dec': -45.677930555343636,
 'planetNames': ['CPC 0 759 b',
  'UBV 1689 b',
  'HIP 7562 b',
  'PPM 306061 b',
  '2MASS J01372503-4540404 b',
  'TYC 8040-72-1 b',
  'WASP-18 b',
  'SAO 215585 b',
  'GEN# +1.00010069 b',
  '[CS62] E1 13 b',
  'HIC 7562 b',
  'Gaia DR1 4955371363037611136 b',
  'CPD-46 168 b',
  'CD-46 449 b',
  'GSC 08040-00072 b',
  'HD 10069 b'],
 'keplerID': None,
 'keplerTCE': None,
 'tessID': 100100827,
 'tessTCE': 'TCE_1'}

Get Planet Properties

Get the planet properties for WASP-18 b. We hold two exoplanet tables, the composite table at NExScI and Exoplanets.org. If the planet exists in both tables, you will get back an array of dictionaries where the first element is the NExScI's planet properties and the second is Exoplanet.org's properties.

In [4]:
url = planeturl + planet_name + "/properties/"

r = requests.get(url = url, headers = header)

planet_prop = r.json()

print("Catalog 1: " + planet_prop[0]['catalog_name'])
print("Catalog 2: " + planet_prop[1]['catalog_name'])
planet_prop[0].keys()
Catalog 1: exoplanets.org
Catalog 2: nexsci
Out[4]:
dict_keys(['canonical_name', 'exoplanetID', 'catalog_name', 'planet_name', 'disposition', 'modified_date', 'star_name', 'component', 'Rs', 'Rs_unit', 'Rs_upper', 'Rs_lower', 'Rs_ref', 'Rs_url', 'Ms', 'Ms_unit', 'Ms_upper', 'Ms_lower', 'Ms_ref', 'Ms_url', 'Fe/H', 'Fe/H_upper', 'Fe/H_lower', 'Fe/H_ref', 'Fe/H_url', 'stellar_gravity', 'stellar_gravity_upper', 'stellar_gravity_lower', 'stellar_gravity_ref', 'stellar_gravity_url', 'Teff', 'Teff_unit', 'Teff_upper', 'Teff_lower', 'Teff_ref', 'Teff_url', 'Vmag', 'Vmag_unit', 'Vmag_upper', 'Vmag_lower', 'Vmag_ref', 'Vmag_url', 'Jmag', 'Jmag_unit', 'Jmag_upper', 'Jmag_lower', 'Jmag_ref', 'Jmag_url', 'Hmag', 'Hmag_unit', 'Hmag_upper', 'Hmag_lower', 'Hmag_ref', 'Hmag_url', 'Kmag', 'Kmag_unit', 'Kmag_upper', 'Kmag_lower', 'Kmag_ref', 'Kmag_url', 'RA', 'DEC', 'distance', 'distance_unit', 'distance_upper', 'distance_lower', 'distance_ref', 'distance_url', 'Rp', 'Rp_unit', 'Rp_upper', 'Rp_lower', 'Rp_ref', 'Rp_url', 'Mp', 'Mp_unit', 'Mp_upper', 'Mp_lower', 'Mp_ref', 'Mp_url', 'Tp', 'Tp_unit', 'Tp_upper', 'Tp_lower', 'Tp_ref', 'Tp_url', 'surface_gravity', 'surface_gravity_unit', 'surface_gravity_upper', 'surface_gravity_lower', 'surface_gravity_ref', 'surface_gravity_url', 'orbital_period', 'orbital_period_unit', 'orbital_period_upper', 'orbital_period_lower', 'orbital_period_ref', 'orbital_period_url', 'orbital_distance', 'orbital_distance_unit', 'orbital_distance_upper', 'orbital_distance_lower', 'orbital_distance_ref', 'orbital_distance_url', 'inclination', 'inclination_unit', 'inclination_upper', 'inclination_lower', 'inclination_ref', 'inclination_url', 'eccentricity', 'eccentricity_unit', 'eccentricity_upper', 'eccentricity_lower', 'eccentricity_ref', 'eccentricity_url', 'omega', 'omega_unit', 'omega_upper', 'omega_lower', 'omega_ref', 'omega_url', 'transit_duration', 'transit_duration_unit', 'transit_duration_upper', 'transit_duration_lower', 'transit_duration_ref', 'transit_duration_url', 'transit_time', 'transit_time_unit', 'transit_time_upper', 'transit_time_lower', 'transit_time_ref', 'transit_time_url', 'a/Rs', 'a/Rs_unit', 'a/Rs_upper', 'a/Rs_lower', 'a/Rs_ref', 'a/Rs_url', 'Rp/Rs', 'Rp/Rs_unit', 'Rp/Rs_upper', 'Rp/Rs_lower', 'Rp/Rs_ref', 'Rp/Rs_url', 'impact_parameter', 'impact_parameter_unit', 'impact_parameter_upper', 'impact_parameter_lower', 'impact_parameter_ref', 'impact_parameter_url', 'transit_depth', 'transit_depth_unit', 'transit_depth_upper', 'transit_depth_lower', 'transit_depth_ref', 'transit_depth_url', 'constellation', 'Tmag', 'Tmag_unit', 'Tmag_upper', 'Tmag_lower', 'Tmag_ref', 'Tmag_url', 'SNR', 'SNR_unit', 'SNR_upper', 'SNR_lower', 'SNR_ref', 'SNR_url', 'snr_emission_15', 'snr_emission_5', 'snr_transmission_K', 'pmRA', 'pmRA_upper', 'pmRA_lower', 'pmDec', 'pmDec_upper', 'pmDec_lower', 'pm_unit', 'pm_ref', 'pm_url', 'dayside_temperature'])
In [5]:
print("WASP-18 b Properties")
print("Stellar Mass %f %s" % (planet_prop[0]['Ms'], planet_prop[0]['Ms_unit'] ) )
print("Planet Mass %f %s" % (planet_prop[0]['Mp'], planet_prop[0]['Mp_unit'] ) )
print("Planet Mass Reference: %s" % (planet_prop[0]['Mp_ref']))
WASP-18 b Properties
Stellar Mass 1.220000 M_sun
Planet Mass 10.200600 M_Jupiter
Planet Mass Reference: Calculated from MSINI and I

Get TCE Data and Header for WASP-18 b

First, we provide some background. TESS does a search of the postage-stamp, two-minute cadence data for transit signals. For every signal it identifies with its Transit Planet Search (TPS) module, it creates something called a Threshold Crossing Event (TCE). TCEs are periodic signals that exceed a nominal signal-to-noise requirement. Some are consistent with transiting planets, others are eclipsing binaries, and others are more consistent with variable stars or noise in the data. The exported products include a data validation time series file that includes a median detrended time series as well as the time series it used to do the planet search.

Here we retrieve the header and data associated with the TCE that corresponds to the ephemeris of WASP-18 b from the data validation time series files. The info request provides the header information from the primary header and the data header for the specified TCE. Note: this can be done for any TCE, not just those associated with known planets.

Because many TESS targets are observed in more than one sector and the TESS mission does multi-sector searches, we must first determine which sector's search found a TCE on WASP-18. We can then choose from those sector ranges for our query.

In [6]:
url = dvurl + str(ticid) + '/tces/'
myparams = {"tce" : tce}

r = requests.get(url = url, params = myparams, headers = header)
sectorInfo = r.json()

sectors = [x[:11] for x in sectorInfo["TCE"] if tce in x]
sectors
Out[6]:
['s0001-s0003', 's0002-s0002', 's0003-s0003']
In [7]:
url = dvurl + str(ticid) + '/info/'
myparams = {"tce" : tce,
            "sector" : sectors[0]}

r = requests.get(url = url, params = myparams, headers = header)
tceInfo = r.json()
tceInfo.keys()
tceInfo['DV Data Header'].keys()

print("TIC: %s" % tceInfo['DV Primary Header']['OBJECT'])
print("Planet Radius: %f" % tceInfo['DV Data Header']['PRADIUS'])
TIC: TIC 100100827
Planet Radius: 12.979840

Plot the light curve data for WASP-18 b

Use the table request to get the time and median detrended light curve for WASP-18 b. Notice that you can retrieve all the different types of light curves available via this API call. Some of the more useful ones are:

  • LC_DETREND is the median detrended light curve.
  • LC_INIT is the initial light curve used to search for the TCE.
  • MODEL_INIT is a light curve of the transit model.
In [8]:
# Get The data
url = dvurl + str(ticid) + '/table/'
myparams = {"tce" : tce,
            "sector" : sectors[0]}

r = requests.get(url = url, params = myparams, headers = header)
tce_data = r.json()
In [9]:
data = p.DataFrame.from_dict(tce_data['data'])
data.columns
Out[9]:
Index(['TICID', 'EXTNAME', 'TIME', 'TIMECORR', 'CADENCENO', 'PHASE', 'LC_INIT',
       'LC_INIT_ERR', 'LC_WHITE', 'LC_DETREND', 'MODEL_INIT', 'MODEL_WHITE',
       'DATE_OBS', 'TESSDatetime', 'SECTORS'],
      dtype='object')
In [10]:
detrend = data['LC_DETREND']
model = data['MODEL_INIT']
time = data['TIME']

plt.figure(figsize=(14,4))
plt.plot(time,detrend,'.',lw=0.4)
plt.plot(time,model,'r-',lw=0.6)
plt.xlabel('TIME (BTJD)')
plt.ylabel('Relative Flux')
Out[10]:
Text(0, 0.5, 'Relative Flux')

Request a Bokeh Phased Light Curve

The API allows you to pull down a Bokeh plot of the phased light curve, folded and binned at the period of the identified TCE. Bokeh provides a zoomable, pannable plot to examine.

In [11]:
url = dvurl + str(ticid) + '/phaseplot/'
myparams = {"tce" : tce,
            "sector" : sectors[0]}

r = requests.get(url = url, params = myparams, headers = header)
In [12]:
display(HTML(str(r.content.decode('utf-8'))))
Phased Light Curve