How to save a LightCurve in FITS format?#

Once you have detrended or altered a lightcurve in some way, you may want to save it as a FITS file. This allows you to easily share the file with your collaborators or submit your lightcurves as a MAST High Level Science Product (HLSP). Lightkurve provides a to_fits() method which will easily convert your LightCurve object into a fits file.

Below is a brief demostration showing how to_fits() works.

Note: if you are considering contributing a HLSP you may want to read the guidelines for contributing fits files. These include which fits headers are required/suggested for your HLSP to be accepted.

Example: editing and writing a lightcurve#

First we’ll obtain a random Kepler lightcurve from MAST.

[1]:
from lightkurve import search_lightcurve
lc = search_lightcurve('KIC 757076', author="Kepler", quarter=3).download()

Now we’ll make some edits to the lightcurve. Below we use the PDCSAP flux from MAST, remove NaN values using the .remove_nans() function, and clip out any outliers using the .remove_outliers().

[2]:
lc = lc.remove_nans().remove_outliers()
lc.scatter();
../../_images/tutorials_2-creating-light-curves_2-1-saving-a-light-curve_4_0.png

Now we can use the to_fits method to save the lightcurve to a file called output.fits.

[3]:
lc.to_fits(path='demo-lightcurve.fits', overwrite=True)
WARNING: TimeDeltaMissingUnitWarning: Numerical value without unit or explicit format passed to TimeDelta, assuming days [astropy.time.core]

Let’s take a look at the file and check that it behaved as we expect

[4]:
from astropy.io import fits
hdu = fits.open('demo-lightcurve.fits')
type(hdu)
[4]:
astropy.io.fits.hdu.hdulist.HDUList
[5]:
hdu.info()
Filename: demo-lightcurve.fits
No.    Name      Ver    Type      Cards   Dimensions   Format
  0  PRIMARY       1 PrimaryHDU      26   ()
  1  LIGHTCURVE    1 BinTableHDU     28   4133R x 7C   [D, E, E, J, K, D, D]

hdu is a set of astropy.io.fits objects, which is what we would expect. Lets take a look at the header of the first extension.

[6]:
hdu[0].header
[6]:
SIMPLE  =                    T / conforms to FITS standards
BITPIX  =                    8 / array data type
NAXIS   =                    0 / number of array dimensions
EXTEND  =                    T / file contains extensions
NEXTEND =                    2 / number of standard extensions
EXTNAME = 'PRIMARY '           / name of extension
EXTVER  =                    1 / extension version number (not format version)
ORIGIN  = 'Unofficial data product' / institution responsible for file
DATE    = '2023-11-03'         / file creation date.
CREATOR = 'lightkurve.LightCurve.to_fits()' / pipeline job and program used t
TELESCOP= 'KEPLER  '           / telescope
INSTRUME= 'Kepler Photometer'  / detector type
OBJECT  = '757076  '           / string version of target id
KEPLERID=               757076 / unique Kepler target identifier
CHANNEL =                   56 / CCD channel
RADESYS = 'ICRS    '           / reference frame of celestial coordinates
RA_OBJ  =            291.03872 / [deg] right ascension
DEC_OBJ =             36.59813 / [deg] declination
EQUINOX =                 2000 / equinox of celestial coordinate system
PROCVER = '2.4.2   '
MISSION = 'Kepler  '
DATE-OBS= '8730-10-27T17:53:34.165'
QUARTER =                    3
CAMPAIGN=
CHECKSUM= 'DaZDGUY9DZYCDZY9'   / HDU checksum updated 2023-11-03T10:13:18
DATASUM = '0       '           / data unit checksum updated 2023-11-03T10:13:18

Looks like it has all the correct information about the target. What about the second extension?

[7]:
hdu[1].header
[7]:
XTENSION= 'BINTABLE'           / binary table extension
BITPIX  =                    8 / array data type
NAXIS   =                    2 / number of array dimensions
NAXIS1  =                   44 / length of dimension 1
NAXIS2  =                 4133 / length of dimension 2
PCOUNT  =                    0 / number of group parameters
GCOUNT  =                    1 / number of groups
TFIELDS =                    7 / number of table fields
TTYPE1  = 'TIME    '
TFORM1  = 'D       '
TUNIT1  = 'bkjd    '
TTYPE2  = 'FLUX    '
TFORM2  = 'E       '
TUNIT2  = 'e-/s    '
TTYPE3  = 'FLUX_ERR'
TFORM3  = 'E       '
TUNIT3  = 'e-/s    '
TTYPE4  = 'CADENCENO'
TFORM4  = 'J       '
TTYPE5  = 'SAP_QUALITY'
TFORM5  = 'K       '
TTYPE6  = 'MOM_CENTR1'
TFORM6  = 'D       '
TTYPE7  = 'MOM_CENTR2'
TFORM7  = 'D       '
EXTNAME = 'LIGHTCURVE'
CHECKSUM= 'BFhBCEe9BEeABEe7'   / HDU checksum updated 2023-11-03T10:13:18
DATASUM = '390244549'          / data unit checksum updated 2023-11-03T10:13:18

This extension has 7 columns, TIME, FLUX, FLUX_ERR, SAP_QUALITY, CADENCENO, MOM_CENTR1, and MOM_CENTR2. What if we wanted to add new keywords to our fits file? HLSP products require some extra keywords. Let’s add some keywords to explain who made the data, and what our HLSP is.

[8]:
lc.to_fits(path='demo-lightcurve.fits',
           overwrite=True,
           HLSPLEAD='Kepler/K2 GO office',
           HLSPNAME='TUTORIAL',
           CITATION='HEDGES2018')
[9]:
hdu = fits.open('demo-lightcurve.fits')
[10]:
hdu[0].header
[10]:
SIMPLE  =                    T / conforms to FITS standards
BITPIX  =                    8 / array data type
NAXIS   =                    0 / number of array dimensions
EXTEND  =                    T / file contains extensions
NEXTEND =                    2 / number of standard extensions
EXTNAME = 'PRIMARY '           / name of extension
EXTVER  =                    1 / extension version number (not format version)
ORIGIN  = 'Unofficial data product' / institution responsible for file
DATE    = '2023-11-03'         / file creation date.
CREATOR = 'lightkurve.LightCurve.to_fits()' / pipeline job and program used t
TELESCOP= 'KEPLER  '           / telescope
INSTRUME= 'Kepler Photometer'  / detector type
OBJECT  = '757076  '           / string version of target id
KEPLERID=               757076 / unique Kepler target identifier
CHANNEL =                   56 / CCD channel
RADESYS = 'ICRS    '           / reference frame of celestial coordinates
RA_OBJ  =            291.03872 / [deg] right ascension
DEC_OBJ =             36.59813 / [deg] declination
EQUINOX =                 2000 / equinox of celestial coordinate system
PROCVER = '2.4.2   '
HLSPLEAD= 'Kepler/K2 GO office'
HLSPNAME= 'TUTORIAL'
CITATION= 'HEDGES2018'
MISSION = 'Kepler  '
DATE-OBS= '8730-10-27T17:53:34.165'
QUARTER =                    3
CAMPAIGN=
CHECKSUM= 'ZG6RhF3QZF3QfF3Q'   / HDU checksum updated 2023-11-03T10:13:18
DATASUM = '0       '           / data unit checksum updated 2023-11-03T10:13:18

Now our new keywords are included in the primary header! What about if we want to add more data columns to our fits file? We can simply add data columns in the same way. Let’s add the data quality to our fits file.

[11]:
demo_vector = lc.fold(period=1.23456789).phase
demo_vector
[11]:
<TimeDelta object: scale='None' format='jd' value=[-0.61680215 -0.61654885 -0.61577695 ...  0.61676757  0.61705094
  0.61726379]>
[12]:
lc.to_fits(path='demo-lightcurve.fits',
           overwrite=True,
           HLSPLEAD='Kepler/K2 GO office',
           HLSPNAME='TUTORIAL',
           CITATION='HEDGES2018',
           DEMO_COLUMN=demo_vector)
[13]:
hdu = fits.open('demo-lightcurve.fits')
[14]:
hdu[1].data.columns
[14]:
ColDefs(
    name = 'TIME'; format = 'D'; unit = 'bkjd'
    name = 'FLUX'; format = 'E'; unit = 'e-/s'
    name = 'FLUX_ERR'; format = 'E'; unit = 'e-/s'
    name = 'CADENCENO'; format = 'J'
    name = 'SAP_QUALITY'; format = 'K'
    name = 'MOM_CENTR1'; format = 'D'
    name = 'MOM_CENTR2'; format = 'D'
)

The vector is populated as a data column in the HDU extension. Once all your lightcurves are saved as fits files and you have a README file, you can consider submitting your data products to MAST.