This is the development version of the TESS Science Support Center website. For the live website, visit https://heasarc.gsfc.nasa.gov/docs/tess

Aperture Photometry

Understanding Aperture Photometry & Making Custom Apertures.

Learning Goals

In our previous tutorials (LightCurve Objects and LightCurveFile Objects) we learned about aperture photometry and the Simple Aperture Photometry (SAP) and Pre-search Data Conditioning SAP (PDCSAP) flux.

We have not however discussed how these apertures are chosen by the pipeline or how to examine them.

In this proposal we will cover the following, - How apertures are defined. - How can we examine a pre-defined aperture. - How we can define and modify an aperture and recover the lightcurve.

SPOC Apertures

In aperture photometry A set of pixels in the image are chosen and we sum those to produce a single flux value. The SPOC produces an optimal aperture, which is used by default in Lightkurve, and determined by the data processing pipeline. This is the default aperture used by Lightkurve and is optimized to ensure that the stellar signal has a high signal to noise ratio, with minimal contamination from the background.

We sum these same pre-selected pixels for every image at each cadence to produce a light curve.

There are however, some cases where you might want to produce your own aperture. The field may be crowded, or you may wish to change the aperture size to change the relative contribution of the background. Lightkurve offers tools to select pixels programmatically.

We will show you how to examine the pre-selected aperture and how to modify it if you wish.

Imports

This tutorial requires: - Lightkurve - Matplotlib - numpy

Defining Terms

  • Target Pixel File (TPF): A file containing the original CCD pixel observations from which light curves are extracted.
  • LightCurve Object: Obtained from a TPF and contains lightcurve information derived using simple aperture photometry.
  • LightCurveFile Object: Obtained from MAST and contains both SAP flux and PSDCSAP flux.
  • Cadence: The rate at which TESS photometric observations are stored.
  • Sector: One of TESS’s 27 (to date) observing periods, approximately ~27 days in duration.
  • Simple Aperture Photometry (SAP): The act of summing all pixel values in a pre-defined aperture as a function of time.
  • Pre-search Data Conditioning SAP flux (PDCSAP) flux : SAP flux from which long term trends have been removed using so-called Co-trending Basis Vectors (CBVs). PDCSAP flux is usually cleaner data than the SAP flux and will have fewer systematic trends.
%matplotlib inline
import numpy as np
import lightkurve as lk
import matplotlib.pyplot as plt

Downloading the Data

For this tutorial lets use the L 98-59 System again, focusing on planet c. First let’s search for a TPF for this object.

search_result = lk.search_targetpixelfile('TIC 307210830')
search_result
SearchResult containing 7 data products.
#observationauthortarget_nameproductFilenamedistance
0TESS Sector 2SPOC307210830tess2018234235059-s0002-0000000307210830-0121-s_tp.fits0.0
1TESS Sector 5SPOC307210830tess2018319095959-s0005-0000000307210830-0125-s_tp.fits0.0
2TESS Sector 8SPOC307210830tess2019032160000-s0008-0000000307210830-0136-s_tp.fits0.0
3TESS Sector 9SPOC307210830tess2019058134432-s0009-0000000307210830-0139-s_tp.fits0.0
4TESS Sector 10SPOC307210830tess2019085135100-s0010-0000000307210830-0140-s_tp.fits0.0
5TESS Sector 11SPOC307210830tess2019112060037-s0011-0000000307210830-0143-s_tp.fits0.0
6TESS Sector 12SPOC307210830tess2019140104343-s0012-0000000307210830-0144-s_tp.fits0.0

Lets pick and download the data for sector 2.

tpf_file = search_result[0].download()
tpf_file
TessTargetPixelFile(TICID: 307210830)

We now have a TPF file for our object in sector 2. The optimal aperture is stored in the TPF as the pipeline_mask property. We can have a look at it by calling it here:

tpf_file.pipeline_mask
array([[False, False, False, False, False, False, False, False, False,
        False, False],
       [False, False, False, False, False, False, False, False, False,
        False, False],
       [False, False, False, False, False, False, False, False, False,
        False, False],
       [False, False, False, False, False, False, False, False, False,
        False, False],
       [False, False, False, False, False, False, False, False, False,
        False, False],
       [False, False, False, False,  True,  True,  True, False, False,
        False, False],
       [False, False, False, False,  True,  True,  True,  True, False,
        False, False],
       [False, False, False, False,  True,  True,  True,  True, False,
        False, False],
       [False, False, False, False, False,  True,  True, False, False,
        False, False],
       [False, False, False, False, False, False, False, False, False,
        False, False],
       [False, False, False, False, False, False, False, False, False,
        False, False]])

As you can see, it is a boolean array detailing which pixels are included. We can plot this aperture over the top of our TPF using the plot() function, and passing in the mask to the aperture_mask keyword.

tpf_file.plot(aperture_mask=tpf_file.pipeline_mask);

We now see the SPOC optimal aperture mask overlaid on top of our object of interest.

Using the provided optimal aperture in pipeline_mask and the TPF we can perform simple aperture photometry via the extract_aperture_photometry function as shown below,

lc = tpf_file.extract_aperture_photometry()
lc
TessLightCurve targetid=307210830 length=18317
timefluxflux_errcentroid_colcentroid_rowcadencenoquality
electron / selectron / spixpix
objectfloat32float32float64float64int32int32
1354.108823127242721566.34960937516.116119384765625664.6090864691554339.4764484490161911910
1354.110211988899421563.8867187516.118038177490234664.6261723169015339.46842003296774911920
1354.11298971215321475.16210937516.089221954345703664.606630403678339.4604662968742911940
1354.114378573809721583.3085937516.12527084350586664.6414481151693339.4832617761526911950
1354.115767435524321575.64062516.121679306030273664.6354584758038339.4735678477034911960
1354.117156297180421563.101562516.115528106689453664.6334974032626339.472138768046911970
1354.118545158894721552.93554687516.112627029418945664.625177003332339.46675685339096911980
1354.119934020551521532.9023437516.10567855834961664.6301979867933339.4699372207359911990
1354.121322882266721533.82812516.105731964111328664.6262018316135339.46553338843912000
.....................
1381.500103252329421262.49414062516.291688919067383664.5744500858646339.35132780163921109130
1381.501492120737821289.82812516.302898406982422664.5797804765874339.34913985203471109140
1381.502880989145821266.351562516.29288673400879664.5790106545255339.35133129076251109150
1381.504269857438221234.84570312516.279603958129883664.5730941550626339.35556313817051109160
1381.505658725846621244.95312516.281909942626953664.5782007755507339.34683164655671109170
1381.507047594255521210.757812516.267162322998047664.5770708377116339.34423590600691109180
1381.50843646254821231.0117187516.27315330505371664.5786574675517339.342172455105361109190
1381.509825330956321250.46679687516.277507781982422664.5722297003167339.35132729757531109200
1381.511214199248821236.3554687516.2720890045166664.582152318805339.34521784277111109210
1381.512603067657721265.8398437516.278945922851562664.5729270180528339.3497104930431109220

The same result can also be obtained via,

lc = tpf_file.to_lightcurve(aperture_mask=tpf_file.pipeline_mask)

Creating your own masks

You don’t necessarily have to pass in the pipeline_mask to the plot() function, it can be any mask you choose yourself, provided it is the right shape. We will now explain how to adjust this mask using the create_threshold_mask function. This method will identify the pixels in the TPF which show a median flux that is brighter than threshold times the standard deviation above the overall median. The standard deviation is estimated in a robust way by multiplying the Median Absolute Deviation (MAD) with 1.4826. In this example we will pick 10 as our threshold.

target_mask = tpf_file.create_threshold_mask(threshold=10, reference_pixel='center')
n_target_pixels = target_mask.sum()
n_target_pixels
13

We have now created a target mask which covers 13 pixels. Lets plot this up and see what it looks like.

tpf_file.plot(aperture_mask=target_mask, mask_color='r');

We see that this is slightly different to the optimal aperture previously defined. It also looks like there might be too many background pixels included. Lets see if we can adjust this.

Specify an Aperture Array

We need to define a new aperture array such that our aperture will cover our object of interest only. We have seen that apertures are defined within boolean arrays, based on this lets make up a new array.

aper_new = np.zeros(tpf_file.shape[1:], dtype=bool)
aper_new[5:8, 4:7] = True
tpf_file.plot(aperture_mask=aper_new, mask_color='red')
<matplotlib.axes._subplots.AxesSubplot at 0x7fa1790b85c0>

OK great, it looks like we have covered our object,but not included too much background. Lets now make this into a lightcurve.

target_lc_new = tpf_file.to_lightcurve(aperture_mask=aper_new)
target_lc_new
TessLightCurve targetid=307210830 length=18317
timefluxflux_errcentroid_colcentroid_rowcadencenoquality
electron / selectron / spixpix
objectfloat32float32float64float64int32int32
1354.108823127242720049.2695312515.38568115234375664.5377949438838339.37615072204693911910
1354.110211988899420029.601562515.380707740783691664.5539189658597339.3674922369538911920
1354.11298971215319982.4726562515.365057945251465664.5351012281784339.36145689194564911940
1354.114378573809719994.11523437515.368595123291016664.5664578479339.38061605675574911950
1354.115767435524320003.72851562515.371517181396484664.5610361707227339.37135598634495911960
1354.117156297180419999.6054687515.368359565734863664.5594608485563339.3702259541033911970
1354.118545158894720019.804687515.375614166259766664.5527183393958339.3662359200094911980
1354.119934020551519982.78710937515.361979484558105664.5567752197887339.36859919546725911990
1354.121322882266720002.7851562515.369135856628418664.5538246562905339.3650181579328912000
.....................
1381.500103252329419974.76367187515.560335159301758664.5105008707154339.26136725018211109130
1381.501492120737819988.50976562515.56753158569336664.5151411160459339.258406292831641109140
1381.502880989145819959.515.55550765991211664.5136800320821339.26059863050071109150
1381.504269857438219942.93164062515.547529220581055664.5087002343405339.26546005638551109160
1381.505658725846619961.89062515.553683280944824664.514156119857339.25745224781591109170
1381.507047594255519938.5351562515.542488098144531664.5135819475233339.255103314948371109180
1381.50843646254819942.6835937515.544118881225586664.513915654913339.252426356578161109190
1381.509825330956319965.84179687515.549737930297852664.5082952466375339.26156579826621109200
1381.511214199248819945.67187515.542808532714844664.5173951664624339.255683625987841109210
1381.512603067657719982.89062515.55328369140625664.5085713660441339.26051245835061109220

Compare Data & Lightcurves

We have now created lightcurve objects using both the SPOC pre-defined aperture and a mask of our own devising. Lets plot these lightcurves up and examine the difference.

lc.plot();
target_lc_new.plot();

As you can see the lightcurves from the two apertures look very different. It looks as if the aperture we have defined manually may have less background or contaminating flux from scattered than the SPOC aperture. We will learn more about contamination in our next tutorial.