Source code for astrobase.services.tesslightcurves

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# tesslightcurves.py - Luke Bouma (bouma.luke@gmail.com) - Nov 2019
# License: MIT - see the LICENSE file for the full text.

'''
Useful tools for acquiring TESS light-curves.  This module contains a number of
non-standard dependencies, including lightkurve, eleanor, and astroquery.

Light-curve retrieval: get light-curves from all sectors for a tic_id::

    get_two_minute_spoc_lightcurves
    get_hlsp_lightcurves
    get_eleanor_lightcurves

Visibility queries: check if an ra/dec was observed::

    is_two_minute_spoc_lightcurve_available
    get_tess_visibility_given_ticid
    get_tess_visibility_given_ticids

Still TODO::

    get_cpm_lightcurve

'''

#############
## LOGGING ##
#############

import logging
from astrobase import log_sub, log_fmt, log_date_fmt

DEBUG = False
if DEBUG:
    level = logging.DEBUG
else:
    level = logging.INFO
LOGGER = logging.getLogger(__name__)
logging.basicConfig(
    level=level,
    style=log_sub,
    format=log_fmt,
    datefmt=log_date_fmt,
)

LOGDEBUG = LOGGER.debug
LOGINFO = LOGGER.info
LOGWARNING = LOGGER.warning
LOGERROR = LOGGER.error
LOGEXCEPTION = LOGGER.exception


#############
## IMPORTS ##
#############

from glob import glob
import os
import numpy as np
import json

from astropy.coordinates import SkyCoord

# This module contains a number of non-standard dependencies, including
# lightkurve, astroquery, and eleanor.
#
# $ conda install -c conda-forge lightkurve
# $ conda install -c astropy astroquery
# $ pip install eleanor
#

try:
    from lightkurve.search import search_lightcurvefile
    lightkurve_dependency = True
except ImportError:
    lightkurve_dependency = False

try:
    from astroquery.mast import Tesscut
    from astroquery.mast import Observations
    astroquery_dependency = True
except ImportError:
    astroquery_dependency = False

try:
    import eleanor
    eleanor_dependency = True
except ImportError:
    eleanor_dependency = False

deps = {
    'lightkurve': lightkurve_dependency,
    'astroquery': astroquery_dependency,
    'eleanor': eleanor_dependency
}

for k,v in deps.items():
    if not v:
        wrn = (
            'Failed to import {:s} dependency. Trying anyway.'.
            format(k)
        )
        LOGWARNING(wrn)

from astrobase.services.mast import tic_objectsearch


##########
## WORK ##
##########

[docs]def get_two_minute_spoc_lightcurves(tic_id, download_dir=None): """This downloads 2-minute TESS SPOC light curves. Parameters ---------- tic_id : str The TIC ID of the object as a string. Returns ------- lcfiles : list or None List of light-curve file paths. None if none are found and downloaded. """ if not lightkurve_dependency: LOGERROR( "The lightkurve package is required for this function to work." ) return None if not isinstance(download_dir, str): errmsg = ( 'get_two_minute_spoc_lightcurves: failed to get valid download_dir' ) LOGERROR(errmsg) return None search_str = 'TIC ' + tic_id res = search_lightcurvefile(search_str, cadence='short', mission='TESS') if len(res.table) == 0: errmsg = ( 'failed to get any SC data for TIC{}. need other LC source.'. format(tic_id) ) LOGERROR(errmsg) return None res.download_all(download_dir=download_dir) lcfiles = glob(os.path.join(download_dir, 'mastDownload', 'TESS', '*{}*'.format(tic_id), '*{}*.fits'.format(tic_id) )) return lcfiles
[docs]def get_hlsp_lightcurves(tic_id, hlsp_products=('CDIPS', 'TASOC', 'PATHOS'), download_dir=None, verbose=True): """This downloads TESS HLSP light curves for a given TIC ID. Parameters ---------- tic_id : str The TIC ID of the object as a string. hlsp_products : sequence of str List of desired HLSP products to search. For instance, ["CDIPS"]. download_dir : str Path of directory to which light-curve will be downloaded. Returns ------- lcfiles : list or None List of light-curve file paths. None if none are found and downloaded. """ if not astroquery_dependency: LOGERROR( "The astroquery package is required for this function to work." ) return None lcfiles = [] for hlsp in hlsp_products: obs_table = Observations.query_criteria( target_name=tic_id, provenance_name=hlsp ) if verbose: LOGINFO(f'Found {len(obs_table)} {hlsp} light-curves.') if len(obs_table) == 0: if verbose: LOGINFO("Did not find light-curves. Escaping.") return None # Get list of available products for this Observation. cdips_products = Observations.get_product_list(obs_table) # Download the products for this Observation. manifest = Observations.download_products(cdips_products, download_dir=download_dir) if verbose: LOGINFO("Done") if len(manifest) >= 1: lcfiles.append(list(manifest['Local Path'])) # # flatten lcfiles list # if len(lcfiles) >= 1: return_lcfiles = [item for sublist in lcfiles for item in sublist] else: return_lcfiles = None return return_lcfiles
[docs]def get_eleanor_lightcurves(tic_id, download_dir=None, targetdata_kwargs=None): """This downloads light curves from the Eleanor project for a given TIC ID. Parameters ---------- tic_id : str The TIC ID of the object as a string. download_dir : str The light curve FITS files will be downloaded here. targetdata_kwargs : dict Optional dictionary of keys and values to be passed ``eleanor.TargetData`` (see https://adina.feinste.in/eleanor/api.html). For instance, you might pass ``{'height':8, 'width':8, 'do_pca':True, 'do_psf':True, 'crowded_field':False}`` to run these settings through to eleanor. The default options used if targetdata_kwargs is None are as follows:: { height=15, width=15, save_postcard=True, do_pca=False, do_psf=False, bkg_size=31, crowded_field=True, cal_cadences=None, try_load=True, regressors=None } Returns ------- lcfiles : list or None List of light-curve file paths. These are saved as CSV, rather than FITS, by this function. """ if not eleanor_dependency: LOGERROR( "The eleanor package is required for this function to work." ) return None stars = eleanor.multi_sectors(tic=np.int64(tic_id), sectors='all', tc=False) for star in stars: if targetdata_kwargs is None: d = eleanor.TargetData(star, height=15, width=15, save_postcard=True, do_pca=False, do_psf=False, bkg_size=31, crowded_field=True, cal_cadences=None, try_load=True) else: d = eleanor.TargetData(star, **targetdata_kwargs) d.save(directory=download_dir) lcfiles = glob(os.path.join( download_dir, 'hlsp_eleanor_tess_ffi_tic{}*.fits'.format(tic_id) )) return lcfiles
[docs]def is_two_minute_spoc_lightcurve_available(tic_id): """ This checks if a 2-minute TESS SPOC light curve is available for the TIC ID. Parameters ---------- tic_id : str The TIC ID of the object as a string. Returns ------- result : bool True if a 2 minute SPOC light-curve is available, else False. """ if not lightkurve_dependency: LOGERROR( "The lightkurve package is required for this function to work." ) return False search_str = 'TIC ' + tic_id res = search_lightcurvefile(search_str, cadence='short', mission='TESS') if len(res.table) == 0: return False else: return True
[docs]def get_tess_visibility_given_ticid(tic_id): """ This checks if a given TIC ID is visible in a TESS sector. Parameters ---------- tic_id : str The TIC ID of the object as a string. Returns ------- sector_str,full_sector_str : tuple of strings The first element of the tuple contains a string list of the sector numbers where the object is visible. The second element of the tuple contains a string list of the full sector names where the object is visible. For example, "[16, 17]" and "[tess-s0016-1-4, tess-s0017-2-3]". If empty, will return "[]" and "[]". """ if not astroquery_dependency: LOGERROR( "The astroquery package is required for this function to work." ) return None, None ticres = tic_objectsearch(tic_id) with open(ticres['cachefname'], 'r') as json_file: data = json.load(json_file) ra = data['data'][0]['ra'] dec = data['data'][0]['dec'] coord = SkyCoord(ra, dec, unit="deg") sector_table = Tesscut.get_sectors(coord) sector_str = list(sector_table['sector']) full_sector_str = list(sector_table['sectorName']) return sector_str, full_sector_str
[docs]def get_tess_visibility_given_ticids(ticids): """This gets TESS visibility info for an iterable container of TIC IDs. Parameters ---------- ticids : iterable of str The TIC IDs to look up. Returns ------- tuple Returns a two-element tuple containing lists of the sector numbers and the full names of the sectors containing the requested TIC IDs. """ if not astroquery_dependency: LOGERROR( "The astroquery package is required for this function to work." ) return None, None sector_strs, full_sector_strs = [], [] for ticid in ticids: sector_str, full_sector_str = ( get_tess_visibility_given_ticid(ticid) ) sector_strs.append(sector_str) full_sector_strs.append(full_sector_str) return sector_strs, full_sector_strs