Query Pan-STARRS DR2 catalog using MAST API

The new MAST interface to the Pan-STARRS catalog supports queries to both the DR1 and DR2 PS1 catalogs. It also has an associated API, which is used in this script.

This script shows how to query the Pan-STARRS DR2 catalog using the PS1 search API. The examples show how to do a simple cone search, how to manipulate the table of results, and how to get a light curve from the table of detections.

This notebook is available for download.

In [1]:
from astropy.io import ascii
from astropy.table import Table

import sys
import re
import numpy as np
import matplotlib.pyplot as plt
import json
import requests

try: # Python 3.x
    from urllib.parse import quote as urlencode
    from urllib.request import urlretrieve
except ImportError:  # Python 2.x
    from urllib import pathname2url as urlencode
    from urllib import urlretrieve

try: # Python 3.x
    import http.client as httplib 
except ImportError:  # Python 2.x
    import httplib   

Useful functions

Execute PS1 searches and resolve names using MAST query.

In [2]:
def ps1cone(ra,dec,radius,table="mean",release="dr1",format="csv",columns=None,
           baseurl="https://catalogs.mast.stsci.edu/api/v0.1/panstarrs", verbose=False,
           **kw):
    """Do a cone search of the PS1 catalog
    
    Parameters
    ----------
    ra (float): (degrees) J2000 Right Ascension
    dec (float): (degrees) J2000 Declination
    radius (float): (degrees) Search radius (<= 0.5 degrees)
    table (string): mean, stack, or detection
    release (string): dr1 or dr2
    format: csv, votable, json
    columns: list of column names to include (None means use defaults)
    baseurl: base URL for the request
    verbose: print info about request
    **kw: other parameters (e.g., 'nDetections.min':2)
    """
    
    data = kw.copy()
    data['ra'] = ra
    data['dec'] = dec
    data['radius'] = radius
    return ps1search(table=table,release=release,format=format,columns=columns,
                    baseurl=baseurl, verbose=verbose, **data)


def ps1search(table="mean",release="dr1",format="csv",columns=None,
           baseurl="https://catalogs.mast.stsci.edu/api/v0.1/panstarrs", verbose=False,
           **kw):
    """Do a general search of the PS1 catalog (possibly without ra/dec/radius)
    
    Parameters
    ----------
    table (string): mean, stack, or detection
    release (string): dr1 or dr2
    format: csv, votable, json
    columns: list of column names to include (None means use defaults)
    baseurl: base URL for the request
    verbose: print info about request
    **kw: other parameters (e.g., 'nDetections.min':2).  Note this is required!
    """
    
    data = kw.copy()
    if not data:
        raise ValueError("You must specify some parameters for search")
    checklegal(table,release)
    if format not in ("csv","votable","json"):
        raise ValueError("Bad value for format")
    url = f"{baseurl}/{release}/{table}.{format}"
    if columns:
        # check that column values are legal
        # create a dictionary to speed this up
        dcols = {}
        for col in ps1metadata(table,release)['name']:
            dcols[col.lower()] = 1
        badcols = []
        for col in columns:
            if col.lower().strip() not in dcols:
                badcols.append(col)
        if badcols:
            raise ValueError('Some columns not found in table: {}'.format(', '.join(badcols)))
        # two different ways to specify a list of column values in the API
        # data['columns'] = columns
        data['columns'] = '[{}]'.format(','.join(columns))

# either get or post works
#    r = requests.post(url, data=data)
    r = requests.get(url, params=data)

    if verbose:
        print(r.url)
    r.raise_for_status()
    if format == "json":
        return r.json()
    else:
        return r.text


def checklegal(table,release):
    """Checks if this combination of table and release is acceptable
    
    Raises a VelueError exception if there is problem
    """
    
    releaselist = ("dr1", "dr2")
    if release not in ("dr1","dr2"):
        raise ValueError("Bad value for release (must be one of {})".format(', '.join(releaselist)))
    if release=="dr1":
        tablelist = ("mean", "stack")
    else:
        tablelist = ("mean", "stack", "detection")
    if table not in tablelist:
        raise ValueError("Bad value for table (for {} must be one of {})".format(release, ", ".join(tablelist)))


def ps1metadata(table="mean",release="dr1",
           baseurl="https://catalogs.mast.stsci.edu/api/v0.1/panstarrs"):
    """Return metadata for the specified catalog and table
    
    Parameters
    ----------
    table (string): mean, stack, or detection
    release (string): dr1 or dr2
    baseurl: base URL for the request
    
    Returns an astropy table with columns name, type, description
    """
    
    checklegal(table,release)
    url = f"{baseurl}/{release}/{table}/metadata"
    r = requests.get(url)
    r.raise_for_status()
    v = r.json()
    # convert to astropy table
    tab = Table(rows=[(x['name'],x['type'],x['description']) for x in v],
               names=('name','type','description'))
    return tab


def mastQuery(request):
    """Perform a MAST query.

    Parameters
    ----------
    request (dictionary): The MAST request json object

    Returns head,content where head is the response HTTP headers, and content is the returned data
    """
    
    server='mast.stsci.edu'

    # Grab Python Version 
    version = ".".join(map(str, sys.version_info[:3]))

    # Create Http Header Variables
    headers = {"Content-type": "application/x-www-form-urlencoded",
               "Accept": "text/plain",
               "User-agent":"python-requests/"+version}

    # Encoding the request as a json string
    requestString = json.dumps(request)
    requestString = urlencode(requestString)
    
    # opening the https connection
    conn = httplib.HTTPSConnection(server)

    # Making the query
    conn.request("POST", "/api/v0/invoke", "request="+requestString, headers)

    # Getting the response
    resp = conn.getresponse()
    head = resp.getheaders()
    content = resp.read().decode('utf-8')

    # Close the https connection
    conn.close()

    return head,content


def resolve(name):
    """Get the RA and Dec for an object using the MAST name resolver
    
    Parameters
    ----------
    name (str): Name of object

    Returns RA, Dec tuple with position"""

    resolverRequest = {'service':'Mast.Name.Lookup',
                       'params':{'input':name,
                                 'format':'json'
                                },
                      }
    headers,resolvedObjectString = mastQuery(resolverRequest)
    resolvedObject = json.loads(resolvedObjectString)
    # The resolver returns a variety of information about the resolved object, 
    # however for our purposes all we need are the RA and Dec
    try:
        objRa = resolvedObject['resolvedCoordinate'][0]['ra']
        objDec = resolvedObject['resolvedCoordinate'][0]['decl']
    except IndexError as e:
        raise ValueError("Unknown object '{}'".format(name))
    return (objRa, objDec)

Use metadata query to get information on available columns

This query works for any of the tables in the API (mean, stack, detection).

In [3]:
meta = ps1metadata("mean","dr2")
meta
Out[3]:
Table length=124
nametypedescription
str17str12str142
objNamecharIAU name for this object.
objAltName1charAlternate name for this object.
objAltName2charAltername name for this object.
objAltName3charAltername name for this object.
objIDlongUnique object identifier.
uniquePspsOBidlongUnique internal PSPS object identifier.
ippObjIDlongIPP internal object identifier.
surveyIDunsignedByteSurvey identifier. Details in the Survey table.
htmIDlongHierarchical triangular mesh (Szalay 2007) index.
zoneIDintLocal zone index, found by dividing the sky into bands of declination 1/2 arcminute in height: zoneID = floor((90 + declination)/0.0083333).
.........
yMeanPSFMagMaxfloatMaximum PSF magnitude from y filter detections.
yMeanKronMagfloatMean Kron (1980) magnitude from y filter detections.
yMeanKronMagErrfloatError in mean Kron (1980) magnitude from y filter detections.
yMeanKronMagStdfloatStandard deviation of Kron (1980) magnitudes from y filter detections.
yMeanKronMagNptshortNumber of measurements included in mean Kron (1980) magnitude from y filter detections.
yMeanApMagfloatMean aperture magnitude from y filter detections.
yMeanApMagErrfloatError in mean aperture magnitude from y filter detections.
yMeanApMagStdfloatStandard deviation of aperture magnitudes from y filter detections.
yMeanApMagNptshortNumber of measurements included in mean aperture magnitude from y filter detections.
yFlagsintInformation flag bitmask for mean object from y filter detections. Values listed in ObjectFilterFlags.

Simple positional query

Search mean object table with nDetections > 1.

This searches the mean object catalog for objects within 50 arcsec of M87 (RA=187.706, Dec=12.391 in degrees). Note that the results are restricted to objects with nDetections>1, where nDetections is the total number of times the object was detected on the single-epoch images in any filter at any time. Objects with nDetections=1 tend to be artifacts, so this is a quick way to eliminate most spurious objects from the catalog.

In [4]:
ra = 187.706
dec = 12.391
radius = 50.0/3600.0
constraints = {'nDetections.gt':1}

# strip blanks and weed out blank and commented-out values
columns = """objID,raMean,decMean,nDetections,ng,nr,ni,nz,ny,
    gMeanPSFMag,rMeanPSFMag,iMeanPSFMag,zMeanPSFMag,yMeanPSFMag""".split(',')
columns = [x.strip() for x in columns]
columns = [x for x in columns if x and not x.startswith('#')]
results = ps1cone(ra,dec,radius,release='dr2',columns=columns,verbose=True,**constraints)
# print first few lines
lines = results.split('\n')
print(len(lines),"rows in results -- first 5 rows:")
print('\n'.join(lines[:6]))
https://catalogs.mast.stsci.edu/api/v0.1/panstarrs/dr2/mean.csv?nDetections.gt=1&ra=187.706&dec=12.391&radius=0.013888888888888888&columns=%5BobjID%2CraMean%2CdecMean%2CnDetections%2Cng%2Cnr%2Cni%2Cnz%2Cny%2CgMeanPSFMag%2CrMeanPSFMag%2CiMeanPSFMag%2CzMeanPSFMag%2CyMeanPSFMag%5D
47 rows in results -- first 5 rows:
objID,raMean,decMean,nDetections,ng,nr,ni,nz,ny,gMeanPSFMag,rMeanPSFMag,iMeanPSFMag,zMeanPSFMag,yMeanPSFMag
122881877112164157,187.71126262,12.40316813,2,0,0,2,0,0,-999.0,-999.0,19.040800094604492,-999.0,-999.0
122881877105204765,187.71049731,12.4035168,2,0,0,2,0,0,-999.0,-999.0,19.13680076599121,-999.0,-999.0
122881877095525205,187.70951703,12.40389466,2,0,0,2,0,0,-999.0,-999.0,18.8612003326416,-999.0,-999.0
122881877085143466,187.7085017546712,12.402395900723272,37,9,11,7,7,3,18.826099395751953,18.006799697875977,17.76689910888672,17.875099182128906,17.68120002746582
122851877124637737,187.7124557950916,12.38104290416518,50,9,10,15,9,7,19.32469940185547,18.029600143432617,17.798799514770508,17.930400848388672,17.508899688720703

Convert the results to an astropy table

The CSV results string is easily converted to an astropy table. This table is easily manipulated to extract information on individual columns or rows.

In [5]:
tab = ascii.read(results)
# improve the format
for filter in 'grizy':
    col = filter+'MeanPSFMag'
    try:
        tab[col].format = ".4f"
        tab[col][tab[col] == -999.0] = np.nan
    except KeyError:
        print("{} not found".format(col))
tab
Out[5]:
Table length=45
objIDraMeandecMeannDetectionsngnrninznygMeanPSFMagrMeanPSFMagiMeanPSFMagzMeanPSFMagyMeanPSFMag
int64float64float64int64int64int64int64int64int64float64float64float64float64float64
122881877112164157187.7112626212.40316813200200nannan19.0408nannan
122881877105204765187.7104973112.4035168200200nannan19.1368nannan
122881877095525205187.7095170312.40389466200200nannan18.8612nannan
122881877085143466187.708501754671212.4023959007232723791177318.826118.006817.766917.875117.6812
122851877124637737187.712455795091612.3810429041651850910159719.324718.029617.798817.930417.5089
122851877115528846187.7113434112.3818190921100023.352417.3302nannannan
122851877109888891187.7109880912.38200012200200nannannannannan
122851877096718451187.7096956412.3815862821010020.5734nannannannan
122851877090477761187.7090115013851612.3810892888729622967115019.746817.746117.491517.6221nan
122851877062673559187.7062530603321312.37754523330390671230120.521018.906318.9627nan18.1421
..........................................
122861877056308967187.7055994812.39030211200002nannannannan14.9985
122861877050678994187.7051254260855212.39043055705990652210017.028715.084316.1690nannan
122861877044629638187.7045159812.39086724200020nannannan15.5294nan
122861876979515942187.6979510211019712.387841767202454487138818.271317.494817.288417.433817.1059
122861876952960254187.6953439612.38300575200002nannannannan18.6671
122861876947177869187.6946955512.38944923202000nan18.3706nannannan
122861876946987056187.6944285912.38863934201010nan17.1852nan18.3888nan
122861876946372059187.6947232612.38464777200020nannannan18.8606nan
122861876944428797187.6944805712.39022676200020nannannan20.1199nan
122861876929948166187.693062605509612.38979569026209672032019.7578nan18.551919.2260nan

Query for a single object by objID

It is possible to query the catalog directly on the object identifier without any RA/Dec restriction. This might not be particularly useful when search for objects, but it is very useful when searching the detection table for time-series data.

In [6]:
results1 = ps1search(release='dr2',columns=columns,verbose=True,objid=122851876947049923)
tab1 = ascii.read(results1)
# improve the format
for filter in 'grizy':
    col = filter+'MeanPSFMag'
    try:
        tab1[col].format = ".4f"
        tab1[col][tab1[col] == -999.0] = np.nan
    except KeyError:
        print("{} not found".format(col))
tab1
https://catalogs.mast.stsci.edu/api/v0.1/panstarrs/dr2/mean.csv?objid=122851876947049923&columns=%5BobjID%2CraMean%2CdecMean%2CnDetections%2Cng%2Cnr%2Cni%2Cnz%2Cny%2CgMeanPSFMag%2CrMeanPSFMag%2CiMeanPSFMag%2CzMeanPSFMag%2CyMeanPSFMag%5D
Out[6]:
Table length=1
objIDraMeandecMeannDetectionsngnrninznygMeanPSFMagrMeanPSFMagiMeanPSFMagzMeanPSFMagyMeanPSFMag
int64float64float64int64int64int64int64int64int64float64float64float64float64float64
122851876947049923187.6947638696897512.38281785133128282123019.867418.592719.069218.1394nan

Search stack objects at same position

There is no need for the nDetections limit for stack objects, which can in fact have nDetections = 0 for objects that are too faint to be detected on single-epoch images. But we require primaryDetection=1 in order to eliminate duplicate sources at the edges of the skycell regions used for processing. (There is another column bestDetection that would be better suited for this test but is currently not correct in the database.)

In [7]:
sconstraints = {'primaryDetection':1}

scolumns = """objID,raMean,decMean,nDetections,ng,nr,ni,nz,ny,
    nStackDetections,primaryDetection,
    gPSFMag,rPSFMag,iPSFMag,zPSFMag,yPSFMag""".split(',')
# strip blanks and weed out blank and commented-out values
scolumns = [x.strip() for x in scolumns]
scolumns = [x for x in scolumns if x and not x.startswith('#')]

sresults = ps1cone(ra,dec,radius,table="stack",release="dr2",columns=scolumns,verbose=True,**sconstraints)
stab = ascii.read(sresults)
for col in scolumns:
    try:
        stab[col]
    except KeyError:
        print(col,"not found")
# improve the format
for filter in 'grizy':
    col = filter+'PSFMag'
    try:
        stab[col].format = ".4f"
        stab[col][stab[col] == -999.0] = np.nan
    except KeyError:
        print("{} not found".format(col))
stab
https://catalogs.mast.stsci.edu/api/v0.1/panstarrs/dr2/stack.csv?primaryDetection=1&ra=187.706&dec=12.391&radius=0.013888888888888888&columns=%5BobjID%2CraMean%2CdecMean%2CnDetections%2Cng%2Cnr%2Cni%2Cnz%2Cny%2CnStackDetections%2CprimaryDetection%2CgPSFMag%2CrPSFMag%2CiPSFMag%2CzPSFMag%2CyPSFMag%5D
Out[7]:
Table length=36
objIDraMeandecMeannDetectionsngnrninznynStackDetectionsprimaryDetectiongPSFMagrPSFMagiPSFMagzPSFMagyPSFMag
int64float64float64int64int64int64int64int64int64int64int64float64float64float64float64float64
122851876947049923187.6947602812.3828158212308119.606218.609418.643918.5461nan
122851877008084336187.7008012512.3782806415325503119.5519nan18.484418.4317nan
122851877049294726187.7049523912.3785145272221011nan18.2340nannannan
122851877062673559187.7062495612.377548347123011121.2941nannan18.3366nan
122851877090477761187.7090282412.38101615296711508118.8032nan17.751517.705117.2640
122851877096718451187.7096956412.381586282101002118.8283nannannannan
122851877124637737187.7124607712.3810060950910159710119.170018.027717.996217.876417.4738
122861876929948166187.6930488412.389786177203208119.445918.503318.405418.3286nan
122861876979515942187.6978931512.387796884487138810118.471217.416417.396317.339616.9031
122861877059169881187.7059196112.391126045410101481210115.524614.367014.460314.357314.0464
................................................
122881877005964357187.7005972912.403200290000001118.370319.445518.2562nan17.8106
122881877006554483187.7006476112.403320610000001118.612419.278618.6711nan18.0966
122881877009724884187.7009791312.403658650000002119.191318.038019.972919.420318.9587
122881877012225066187.7011335712.403809860000002118.340817.435818.0109nan17.8479
122881877035996077187.7035991612.4046549400000011nannannan18.8570nan
122881877039606059187.7039606512.4046399300000011nannannan18.9860nan
122881877043806061187.7043808212.4046419200000011nannannan18.9075nan
122881877047836068187.7047837612.4046475300000011nannannan18.7860nan
122881877058306170187.7058305212.4047321900000011nannannan19.4987nan
122881877085143466187.708499612.40240408379117739119.050018.031718.187317.959317.5065

Explore the tables

Match the stack and mean tables and look at the subset of sources that are detected in both catalogs. The commented-out lines show how to restrict the joined table to only stack detections and to only stack non-detections.

In [8]:
from astropy.table import join
jtab = join(stab,tab,join_type='outer')
jtab.sort('objID')
jtab_both = jtab[(jtab['primaryDetection']==1) & (jtab['nDetections']>1)]

#jtab[jtab['nStackDetections'].mask].show_in_notebook()
#jtab[~jtab['nStackDetections'].mask].show_in_notebook()
#jtab.show_in_notebook()
jtab_both.show_in_notebook()
Out[8]:
Table length=15
idxobjIDraMeandecMeannDetectionsngnrninznynStackDetectionsprimaryDetectiongPSFMagrPSFMagiPSFMagzPSFMagyPSFMaggMeanPSFMagrMeanPSFMagiMeanPSFMagzMeanPSFMagyMeanPSFMag
0122851876947049923187.6947602812.3828158212308119.606218.609418.643918.5461nan----------
1122851877008084336187.7008012512.3782806415325503119.5519nan18.484418.4317nan----------
2122851877049294726187.7049523912.3785145272221011nan18.2340nannannan----------
3122851877062673559187.7062495612.377548347123011121.2941nannan18.3366nan----------
4122851877090477761187.7090282412.38101615296711508118.8032nan17.751517.705117.2640----------
5122851877096718451187.7096956412.381586282101002118.8283nannannannan20.5734nannannannan
6122851877124637737187.7124607712.3810060950910159710119.170018.027717.996217.876417.4738----------
7122861876929948166187.6930488412.389786177203208119.445918.503318.405418.3286nan----------
8122861876979515942187.6978931512.387796884487138810118.471217.416417.396317.339616.9031----------
9122861877059169881187.7059196112.391126045410101481210115.524614.367014.460314.357314.0464----------
10122871876925340070187.6924706512.391281926212103119.4965nannannannan----------
11122871876941435711187.694079912.39593264012106119.3669nan19.562418.2735nan----------
12122871877012582071187.7013003112.3929293732000121nan17.618717.6713nannan17.3948nannannannan
13122871877026011277187.7025378412.3923406262131216101110116.264915.839615.807615.473315.1352----------
14122881877085143466187.708499612.40240408379117739119.050018.031718.187317.959317.5065----------

Plot RA & Dec positions of mean and stack objects

Note that raMean and decMean are defined for all objects, including stack-only objects. For objects detected only on the stacked images, the raStack and decStack values are given in the raMean and decMean columns. That makes it simple to analyze the positions without testing to see what positions are available.

In [9]:
plt.rcParams.update({'font.size': 16})
plt.figure(1,(10,10))
plt.plot(tab['raMean'], tab['decMean'], 'ro', label='Mean (nDet>1)')
plt.plot(stab['raMean'], stab['decMean'], 'bo', label='Stack')
plt.plot(jtab_both['raMean'], jtab_both['decMean'], 'go', label='Both')

plt.xlabel('RA [deg]')
plt.ylabel('Dec [deg]')
plt.legend(loc='best')
Out[9]:
<matplotlib.legend.Legend at 0x7fad6050abd0>

Get DR2 light curve for RR Lyrae star KQ UMa

This time we start with the object name, use the MAST name resolver (which relies on Simbad and NED) to convert the name to RA and Dec, and then query the PS1 DR2 mean object catalog at that position. A small search radius is used so only a single object is returned.

In [10]:
objname = 'KQ UMa'
ra, dec = resolve(objname)
radius = 1.0/3600.0 # radius = 1 arcsec

results = ps1cone(ra,dec,radius,release='dr2',columns=columns,**constraints)
tab = ascii.read(results)
# improve the format
for filter in 'grizy':
    col = filter+'MeanPSFMag'
    tab[col].format = ".4f"
    tab[col][tab[col] == -999.0] = np.nan
tab
Out[10]:
Table length=1
objIDraMeandecMeannDetectionsngnrninznygMeanPSFMagrMeanPSFMagiMeanPSFMagzMeanPSFMagyMeanPSFMag
int64float64float64int64int64int64int64int64int64float64float64float64float64float64
190361393344112894139.3344530533415868.635059161692316681021131415.040214.553014.210914.281414.3041

Get the detection information

Extract all the objects with the same object ID from the Detection table, which contains all the individual measurements for this source.

In [11]:
def addfilter(dtab):
    """Add filter name as column in detection table by translating filterID
    
    This modifies the table in place.  If the 'filter' column already exists,
    the table is returned unchanged.
    """
    if 'filter' not in dtab.colnames:
        # the filterID value goes from 1 to 5 for grizy
        id2filter = np.array(list('grizy'))
        dtab['filter'] = id2filter[(dtab['filterID']-1).data]
    return dtab

objid = tab['objID'][0]
dconstraints = {'objID': objid}
dcolumns = ("""objID,detectID,filterID,obsTime,ra,dec,psfFlux,psfFluxErr,psfMajorFWHM,psfMinorFWHM,
            psfQfPerfect,apFlux,apFluxErr,infoFlag,infoFlag2,infoFlag3""").split(',')
# strip blanks and weed out blank and commented-out values
dcolumns = [x.strip() for x in dcolumns]
dcolumns = [x for x in dcolumns if x and not x.startswith('#')]

dresults = ps1search(table='detection',release='dr2',columns=dcolumns,**dconstraints)
dtab = addfilter(ascii.read(dresults))
dtab.sort('obsTime')
dtab
Out[11]:
Table length=66
objIDdetectIDfilterIDobsTimeradecpsfFluxpsfFluxErrpsfMajorFWHMpsfMinorFWHMpsfQfPerfectapFluxapFluxErrinfoFlaginfoFlag2infoFlag3filter
int64int64int64float64float64float64float64float64float64float64float64float64float64int64int64int64str1
190361393344112894115636667260000047555257.3668569139.3344571368.635076150.0053549301810562612.6245999833918177e-052.52838993072509772.46773004531860350.99893498420715330.0054337601177394391.6695599697413854e-0510276051712824119360y
190361393344112894115637788260000041555257.3780646139.3344492968.635064050.0053131799213588242.175470035581384e-051.80981004238128661.77730000019073490.9998009800910950.0053228801116347311.7123500583693385e-05102760517128124815424y
190361393344112894118025265260000028455281.2528285139.3344724368.635075920.0055569200776517391.2427400179149117e-051.10782003402709960.94930297136306760.99753999710083010.0056169801391661171.2113199773011729e-051027605171287374912z
190361393344112894118026233260000017455281.2625151139.334475368.635075050.0061457999981939791.3183999726606999e-051.08669996261596680.96525698900222780.95558398962020870.0062605198472738271.2802999663108494e-051027605171287374912z
190361393344112894142663796520000019455527.6381469139.3344465768.635072650.0059421500191092491.2578700079757255e-051.17021000385284421.13960003852844240.99738001823425290.00599543983116745951.2208300177007914e-051027605171287374912z
190361393344112894142665071520000011455527.6508919139.3344496268.635065570.0056905299425125121.2433300071279518e-051.19210994243621831.13375997543334960.99726498126983640.0058556799776852131.2070199773006607e-051027605171287374912z
190361393344112894145454631520000040555555.5464962139.334454468.635057820.0051887100562453272.3331000193138607e-051.7920800447463991.5582100152969360.99710899591445920.0052703698165714741.846049963205587e-05102760517128124815424y
190361393344112894145455366520000043555555.5538486139.3344503468.635063180.00530473003163933752.412799949524924e-052.67563009262084962.53515005111694340.99757897853851320.0053064199164509771.8608299797051586e-05102760517128124815424y
190361393344112894153347716310000010155634.477414139.334520768.635035770.008261919952929021.1407400052121375e-051.8818500041961671.76733994483947750.99291598796844480.0086170500144362451.1423299838497769e-05102760517128124782656g
190361393344112894153348968310000008155634.4899457139.3344882168.635061460.0077372998930513861.102680016629165e-051.81031000614166261.6051800251007080.99846100807189940.0079217199236154561.0940600077447016e-05102760517128124782656g
...................................................
190361393344112894287165374630000023556972.6539243139.3344422668.635060870.0068781701847910882.793259955069516e-051.53685998916625981.28813004493713380.99164700508117680.006999140139669182.165849946322851e-051027605171287374912y
190361393344112894287265116630000028556973.6513438139.3344499168.635057590.0066537698730826382.7703999876393937e-051.85618996620178221.7039699554443360.99864000082015990.0066310600377619272.1315599951776676e-051027605171287342144y
190361393344112894292749002110000016357028.4902893139.3344456668.635054610.008394139818847181.04748996818671e-051.3614300489425661.27152001857757570.99895197153091430.00837877020239831.0504700185265392e-051027605171287374912i
190361393344112894292750349110000014357028.5037591139.3344381568.635052570.0079491101205348971.0610299796098843e-051.72116005420684811.57716000080108640.77404999732971190.0079624699428677561.0174900126003195e-0510276051712834880i
190361393344112894292751697110000015357028.5172405139.3344468768.635054780.0078300703316926961.0761000339698512e-051.84072995185852051.67585003376007080.96224999427795410.0078459298238158231.0093400305777323e-051027605171287374912i
190361393344112894292753047110000013357028.5307354139.3344598968.635057350.0077435597777366641.0687500434869435e-051.65031003952026371.50487995147705080.90084999799728390.0076969000510871411.0031199963123072e-051027605171287374912i
190361393344112894292946394110000006357030.4642095139.3344259268.635050160.0075043598189949999.697640052763745e-060.97598302364349370.89162099361419680.99755698442459110.0078761596232652661.0390699571871664e-051038254151287374912i
190361393344112894292947736110000005357030.4776274139.3344310868.635050470.0069853798486292369.485050213697832e-060.81121301651000980.77280598878860470.33228701353073120.0075997500680387021.0191800356551539e-0510382541516034880i
190361393344112894292949077110000009357030.4910438139.3344463868.635047120.0071627101860940469.458200111112092e-060.98116999864578250.8997709751129150.7189530134201050.0074969301931560041.0068500159832183e-0510382541516034880i
190361393344112894292950418110000012357030.5044608139.334445468.635056390.0072493501938879499.486019735049922e-061.126080036163331.02964997291564940.53823602199554440.0073431697674095639.905939805321395e-0610276051716034880i

Plot the light curves

The psfFlux values from the Detection table are converted from Janskys to AB magnitudes. Measurements in the 5 different filters are plotted separately.

In [12]:
# convert flux in Jy to magnitudes
t = dtab['obsTime']
mag = -2.5*np.log10(dtab['psfFlux']) + 8.90
xlim = np.array([t.min(),t.max()])
xlim = xlim + np.array([-1,1])*0.02*(xlim[1]-xlim[0])

plt.rcParams.update({'font.size': 14})
plt.figure(1,(10,10))
for i, filter in enumerate("grizy"):
    plt.subplot(511+i)
    w = np.where(dtab['filter']==filter)
    plt.plot(t[w],mag[w],'-o')
    plt.ylabel(filter+' [mag]')
    plt.xlim(xlim)
    plt.gca().invert_yaxis()
    if i==0:
        plt.title(objname)
plt.xlabel('Time [MJD]')
plt.tight_layout()

Plot differences from the mean magnitudes in the initial search.

In [13]:
# convert flux in Jy to magnitudes
t = dtab['obsTime']
mag = -2.5*np.log10(dtab['psfFlux']) + 8.90
xlim = np.array([t.min(),t.max()])
xlim = xlim + np.array([-1,1])*0.02*(xlim[1]-xlim[0])

plt.rcParams.update({'font.size': 14})
plt.figure(1,(10,10))
for i, filter in enumerate("grizy"):
    plt.subplot(511+i)
    w = np.where(dtab['filter']==filter)
    magmean = tab[filter+'MeanPSFMag'][0]
    plt.plot(t[w],mag[w] - magmean,'-o')
    plt.ylabel('{} [mag - {:.2f}]'.format(filter,magmean))
    plt.xlim(xlim)
    plt.gca().invert_yaxis()
    if i==0:
        plt.title(objname)
plt.xlabel('Time [MJD]')
plt.tight_layout()

Identify bad data

There is one clearly bad $z$ magnitude with a very large difference. Select the bad point and look at it in more detail.

Note that indexing a table (or numpy array) with a logical expression selects just the rows where that expression is true.

In [14]:
dtab[ (dtab['filter']=='z') & (np.abs(mag-tab['zMeanPSFMag'][0]) > 2) ]
Out[14]:
Table length=1
objIDdetectIDfilterIDobsTimeradecpsfFluxpsfFluxErrpsfMajorFWHMpsfMinorFWHMpsfQfPerfectapFluxapFluxErrinfoFlaginfoFlag2infoFlag3filter
int64int64int64float64float64float64float64float64float64float64float64float64float64int64int64int64str1
190361393344112894183252627520000234455933.5264577139.3348816868.635322730.000317944999551400546.730079803674016e-061.0753699541091921.01530003547668460.322986006736755370.000213217004784382882.3693899038335076e-0610276045312832768z

From examining this table, it looks like psfQfPerfect is bad. This flag is the PSF-weighted fraction of unmasked pixels in the image (see the documentation for more details). Values near unity indicate good data that is not significantly affected by bad pixels.

Check all the psfQfPerfect values for the $z$ filter to see if this value really is unusual. The list of values below are sorted by magnitude. The bad point is the only value with psfQfPerfect < 0.95.

In [15]:
w = np.where(dtab['filter']=='z')
zdtab = dtab[w]
zdtab['mag'] = mag[w]
zdtab['dmag'] = zdtab['mag'] - tab['zMeanPSFMag'][0]
ii = np.argsort(-np.abs(zdtab['dmag']))
zdtab = zdtab[ii]
zdtab['objID','obsTime','mag','dmag','psfQfPerfect']
Out[15]:
Table length=13
objIDobsTimemagdmagpsfQfPerfect
int64float64float64float64float64
19036139334411289455933.526457717.644120002375643.36272027550796440.32298600673675537
19036139334411289456289.615934613.890659485398782-0.390740241468893860.9978110194206238
19036139334411289456289.624111213.91680636450095-0.36459336236672610.9883689880371094
19036139334411289456351.416848313.998972898415117-0.282426828452559060.9992570281028748
19036139334411289455281.252828514.5379146235145240.256514896646848460.9975399971008301
19036139334411289456351.42407614.03250199848879-0.248897728378885570.9991869926452637
19036139334411289455527.650891914.5121182178471530.230718490979477050.9972649812698364
19036139334411289456648.567601914.05642973518955-0.224969991678126040.9979820251464844
19036139334411289455527.638146914.4651409698327030.183741242965027140.9973800182342529
19036139334411289455281.262515114.4285539425673250.147154215699648820.9555839896202087
19036139334411289455933.53451514.3076397779975860.026240051129910570.9974889755249023
19036139334411289456019.296878214.278635858578017-0.00276386828965868860.9976540207862854
19036139334411289456019.303801414.2828912729625550.00149154609487922580.9973379969596863

Repeat the plot with bad psfQfPerfect values excluded

Do the plot again but exclude low psfQfPerfect values.

In [16]:
# convert flux in Jy to magnitudes
t = dtab['obsTime']
mag = -2.5*np.log10(dtab['psfFlux']) + 8.90
magmean = 0.0*mag
for filter in "grizy":
    magmean[dtab['filter']==filter] = tab[filter+'MeanPSFMag'][0]
dmag = mag - magmean
dmag1 = dmag[dtab['psfQfPerfect']>0.9]
# fix the x and y axis ranges
xlim = np.array([t.min(),t.max()])
xlim = xlim + np.array([-1,1])*0.02*(xlim[1]-xlim[0])
# flip axis direction for magnitude
ylim = np.array([dmag1.max(),dmag1.min()])
ylim = ylim + np.array([-1,1])*0.02*(ylim[1]-ylim[0])

plt.rcParams.update({'font.size': 14})
plt.figure(1,(10,10))
for i, filter in enumerate("grizy"):
    plt.subplot(511+i)
    w = np.where((dtab['filter']==filter) & (dtab['psfQfPerfect']>0.9))[0]
    plt.plot(t[w],dmag[w],'-o')
    plt.ylabel('{} [mag - {:.2f}]'.format(filter,magmean[w[0]]))
    plt.xlim(xlim)
    plt.ylim(ylim)
    if i==0:
        plt.title(objname)
plt.xlabel('Time [MJD]')
plt.tight_layout()

Plot versus the periodic phase instead of epoch

Plot versus phase using known RR Lyr period from Simbad (table J/AJ/132/1202/table4).

In [17]:
period = 0.48636
# convert flux in Jy to magnitudes
t = (dtab['obsTime'] % period) / period
mag = -2.5*np.log10(dtab['psfFlux']) + 8.90
magmean = 0.0*mag
for filter in "grizy":
    magmean[dtab['filter']==filter] = tab[filter+'MeanPSFMag'][0]
dmag = mag - magmean
dmag1 = dmag[dtab['psfQfPerfect']>0.9]
# fix the x and y axis ranges
xlim = np.array([t.min(),t.max()])
xlim = xlim + np.array([-1,1])*0.02*(xlim[1]-xlim[0])
# flip axis direction for magnitude
ylim = np.array([dmag1.max(),dmag1.min()])
ylim = ylim + np.array([-1,1])*0.02*(ylim[1]-ylim[0])

plt.rcParams.update({'font.size': 14})
plt.figure(1,(10,10))
for i, filter in enumerate("grizy"):
    plt.subplot(511+i)
    w = np.where((dtab['filter']==filter) & (dtab['psfQfPerfect']>0.9))[0]
    w = w[np.argsort(t[w])]
    plt.plot(t[w],dmag[w],'-o')
    plt.ylabel('{} [mag - {:.2f}]'.format(filter,magmean[w[0]]))
    plt.xlim(xlim)
    plt.ylim(ylim)
    if i==0:
        plt.title(objname)
plt.xlabel('Phase')
plt.tight_layout()

Repeat search using eclipsing binary KIC 2161623

From Villanova Kepler Eclipsing Binaries

In [18]:
objname = 'KIC 2161623'
ra, dec = resolve(objname)
radius = 1.0/3600.0 # radius = 1 arcsec

results = ps1cone(ra,dec,radius,release='dr2',columns=columns,**constraints)
tab = ascii.read(results)
# improve the format
for filter in 'grizy':
    col = filter+'MeanPSFMag'
    tab[col].format = ".4f"
    tab[col][tab[col] == -999.0] = np.nan
tab
Out[18]:
Table length=1
objIDraMeandecMeannDetectionsngnrninznygMeanPSFMagrMeanPSFMagiMeanPSFMagzMeanPSFMagyMeanPSFMag
int64float64float64int64int64int64int64int64int64float64float64float64float64float64
153102917444859851291.7444628363461437.5909988815496967101612151414.599814.282114.158714.200414.0672

Get the detection information

This time include the psfQfPerfect limit directly in the database query.

In [19]:
objid = tab['objID'][0]
dconstraints = {'objID': objid, 'psfQfPerfect.min': 0.9}
dcolumns = ("""objID,detectID,filterID,obsTime,ra,dec,psfFlux,psfFluxErr,psfMajorFWHM,psfMinorFWHM,
            psfQfPerfect,apFlux,apFluxErr,infoFlag,infoFlag2,infoFlag3""").split(',')
# strip blanks and weed out blank and commented-out values
dcolumns = [x.strip() for x in dcolumns]
dcolumns = [x for x in dcolumns if x and not x.startswith('#')]

dresults = ps1search(table='detection',release='dr2',columns=dcolumns,**dconstraints)
dtab = addfilter(ascii.read(dresults))
dtab.sort('obsTime')

# add magnitude and difference from mean
dtab['magmean'] = 0.0
for filter in "grizy":
    dtab['magmean'][dtab['filter']==filter] = tab[filter+'MeanPSFMag'][0]
dtab['mag'] = -2.5*np.log10(dtab['psfFlux']) + 8.90
dtab['dmag'] = dtab['mag']-dtab['magmean']
dtab
Out[19]:
Table length=45
objIDdetectIDfilterIDobsTimeradecpsfFluxpsfFluxErrpsfMajorFWHMpsfMinorFWHMpsfQfPerfectapFluxapFluxErrinfoFlaginfoFlag2infoFlag3filtermagmeanmagdmag
int64int64int64float64float64float64float64float64float64float64float64float64float64int64int64int64str1float64float64float64
15310291744485985190150443710000088155002.5047803291.7444639137.59100060.0050100800581276426.656870027654804e-061.98743999004364011.81210994720458980.99942100048065190.0051741302013397227.312250090762973e-06102760517128124815424g14.59980010986328114.6503883357683760.05058822590509493
15310291744485985190151959710000087155002.5199475291.7444661137.591001560.0048733400180935866.488439794338774e-061.98486995697021481.70792996883392330.99923402070999150.0051154200918972497.277139957295731e-06102760517128124815424g14.59980010986328114.6804332159392350.08063310607595398
15310291744485985191252543710000102255013.5256677291.7444598537.590994540.0070024300366640098.784800229477696e-061.90187001228332521.68689000606536870.99896198511123660.0072351298294961459.829150258156005e-061027605171287374912r14.28209972381591814.2868780541162330.004778330300315048
15310291744485985192647667550000182555027.4768617291.74446637.590992850.0081122303381562232.6664600227377377e-052.3229200839996341.9615499973297120.9850310087203980.0084130996838212012.082009996229317e-0510276051712891228224y14.0671997070312514.12714931626390.05994960923264969
15310291744485985192648828550000192555027.4884628291.7444632437.591005730.008293369784951213.020310032297857e-052.3402800559997562.17639994621276860.99913799762725830.0084521695971488952.102739927067887e-051027605171287342144y14.0671997070312514.1031724246355310.035972717604281
15310291744485985192660989520000165455027.6100795291.7444580637.590994570.0069263200275599961.6556199625483714e-051.89796996116638181.69029998779296880.99891000986099240.0072323698550462721.3667499842995312e-05102760517128124782656z14.20040035247802714.2987436148452520.09834326236722468
15310291744485985192662082520000201455027.6210005291.7444631337.590995560.0066813300363719461.744519977364689e-052.2634000778198241.91744005680084230.91887098550796510.0064151501283049581.287440045416588e-05102760517128124782656z14.20040035247802714.3378426873890580.13744233491103053
153102917444859851127445747460000143355375.4577459291.744461937.591002320.007896520197391519.349960237159394e-061.18533003330230711.1006400585174560.99713802337646480.0079050604254007349.893489732348826e-061027605171287374912i14.15869998931884814.156410623675626-0.002289365643221686
153102917444859851127446972550000139355375.4699934291.7444632437.590997540.0078705502673983579.564520041749347e-061.1771099567413331.04335999488830570.99752199649810790.0080287801101803789.981689800042659e-06102760517128108038208i14.15869998931884814.159987257494830.0012872681759823479
153102917444859851131334556560000161255414.3458067291.744462237.590998720.00714862998574972158.948089998739306e-061.34801995754241941.23956000804901120.99781697988510130.0071537699550390249.466310075367801e-061027605171287374912r14.28209972381591814.264442953762824-0.01765677005309385
............................................................
153102917444859851213319324300000159556234.1934155291.7444637337.59100090.0085115097463130952.4282200683956034e-051.04504001140594480.84960597753524780.99497002363204960.0084235202521085742.412610047031194e-051027605171287374912y14.0671997070312514.0749834980439310.007783791012681007
153102917444859851213319880300000163556234.1989869291.7444691537.591000790.0085221603512763982.367329943808727e-051.03870999813079830.84396898746490480.99497699737548830.0083322497084736822.4253400624729693e-0510276051712874483776y14.0671997070312514.073625746149950.006426039118700544
153102917444859851213320467110000165456234.2048575291.7444633337.590995520.00754798995330929761.3891100024920888e-050.93704801797866820.82230299711227420.99817299842834470.0080153997987508771.4522999663313385e-051027768391287374912z14.20040035247802714.2054217164271570.005021363949129665
153102917444859851213321027110000162456234.2104586291.7444522937.590995960.00759054021909832951.3905600098951254e-050.93437999486923220.83032298088073730.99937498569488530.0079094897955656051.4424699656956363e-051027604531287374912z14.20040035247802714.199318285615558-0.0010820668624695884
153102917444859851241032265120000094156511.322909291.7444565737.590997520.00528640998527407658.342700311914086e-061.1512299776077271.1078900098800660.99872201681137080.0053748399950563918.813500244286843e-061027605171287374912g14.59980010986328114.592097895936636-0.007702213926645385
153102917444859851241033321120000081156511.3334693291.7444594537.590996430.0052788900211453448.312629688589368e-061.07897996902465820.9873229861259460.99527901411056520.00533459987491369258.777709808782674e-061027605171287374912g14.59980010986328114.59364346467332-0.006156645189960841
153102917444859851242235689140000144256523.3571377291.7444643937.590992970.0064329900778830059.546060027787462e-061.55096995830535891.5288800001144410.99764800071716310.0065434300340712079.120750291913282e-06102760517128124782656r14.28209972381591814.3789677961158930.09686807229997463
153102917444859851242243557360000154356523.4358431291.744467337.591000050.00759372999891638769.650440006225836e-061.45764994621276861.390280008316040.99797397851943970.00769224017858505259.745829629537184e-06102760517128124782656i14.15869998931884814.1988621215148270.04016213219597908
153102917444859851245335559360000173556554.3560604291.7444648837.590998740.0087070502340793611.6109999705804512e-051.42905998229980471.38935005664825440.95964598655700680.0088688395917415621.3969700376037508e-0510276051712840929344y14.0671997070312514.050322374833616-0.016877332197633876
153102917444859851289819482550000153456999.1950108291.7444611937.591003540.0079714497551321981.649939986236859e-051.19251000881195071.03857004642486570.99764001369476320.0079429801553487781.4659800399385858e-0510276051712874483776z14.20040035247802714.146156717401661-0.0542436350763662
In [20]:
t = dtab['obsTime']
dmag = dtab['dmag']
xlim = np.array([t.min(),t.max()])
xlim = xlim + np.array([-1,1])*0.02*(xlim[1]-xlim[0])
ylim = np.array([dmag.max(),dmag.min()])
ylim = ylim + np.array([-1,1])*0.02*(ylim[1]-ylim[0])

plt.rcParams.update({'font.size': 14})
plt.figure(1,(10,10))
for i, filter in enumerate("grizy"):
    plt.subplot(511+i)
    w = np.where(dtab['filter']==filter)[0]
    plt.plot(t[w],dmag[w],'-o')
    magmean = dtab['magmean'][w[0]]
    plt.ylabel('{} [mag - {:.2f}]'.format(filter,magmean))
    plt.xlim(xlim)
    plt.ylim(ylim)
    if i==0:
        plt.title(objname)
plt.xlabel('Time [MJD]')
plt.tight_layout()

Plot versus phase using known period

Eclipsing binaries basically vary by same amount in all filters since it is a geometrical effect, so combine the data into a single light curve. Wrap using known period and plot versus phase.

In [21]:
period = 2.2834698
bjd0 = 54999.599837
t = ((dtab['obsTime']-bjd0) % period) / period
dmag = dtab['dmag']
w = np.argsort(t)
t = t[w]
dmag = dmag[w]
xlim = np.array([t.min(),t.max()])
xlim = xlim + np.array([-1,1])*0.02*(xlim[1]-xlim[0])
ylim = np.array([dmag.max(),dmag.min()])
ylim = ylim + np.array([-1,1])*0.02*(ylim[1]-ylim[0])

plt.rcParams.update({'font.size': 14})
plt.figure(1,(10,6))
plt.plot(t,dmag,'-o')
plt.xlim(xlim)
plt.ylim(ylim)
plt.xlabel('Phase')
plt.ylabel('Delta magnitude from mean [mag]')
plt.title(objname)
plt.tight_layout()

Repeat search for another eclipsing binary KIC 8153568

In [22]:
objname = 'KIC 8153568'
ra, dec = resolve(objname)
radius = 1.0/3600.0 # radius = 1 arcsec

results = ps1cone(ra,dec,radius,release='dr2',columns=columns,**constraints)
tab = ascii.read(results)
# improve the format
for filter in 'grizy':
    col = filter+'MeanPSFMag'
    tab[col].format = ".4f"
    tab[col][tab[col] == -999.0] = np.nan
tab
Out[22]:
Table length=1
objIDraMeandecMeannDetectionsngnrninznygMeanPSFMagrMeanPSFMagiMeanPSFMagzMeanPSFMagyMeanPSFMag
int64float64float64int64int64int64int64int64int64float64float64float64float64float64
160802869044447231286.9044500512221744.0054794547150988161531101615.182514.989914.890715.199914.8484
In [23]:
objid = tab['objID'][0]
dconstraints = {'objID': objid, 'psfQfPerfect.min': 0.9}
dcolumns = ("""objID,detectID,filterID,obsTime,ra,dec,psfFlux,psfFluxErr,psfMajorFWHM,psfMinorFWHM,
            psfQfPerfect,apFlux,apFluxErr,infoFlag,infoFlag2,infoFlag3""").split(',')
# strip blanks and weed out blank and commented-out values
dcolumns = [x.strip() for x in dcolumns]
dcolumns = [x for x in dcolumns if x and not x.startswith('#')]

dresults = ps1search(table='detection',release='dr2',columns=dcolumns,**dconstraints)
dtab = addfilter(ascii.read(dresults))
dtab.sort('obsTime')

# add magnitude and difference from mean
dtab['magmean'] = 0.0
for filter in "grizy":
    dtab['magmean'][dtab['filter']==filter] = tab[filter+'MeanPSFMag'][0]
dtab['mag'] = -2.5*np.log10(dtab['psfFlux']) + 8.90
dtab['dmag'] = dtab['mag']-dtab['magmean']
dtab
Out[23]:
Table length=60
objIDdetectIDfilterIDobsTimeradecpsfFluxpsfFluxErrpsfMajorFWHMpsfMinorFWHMpsfQfPerfectapFluxapFluxErrinfoFlaginfoFlag2infoFlag3filtermagmeanmagdmag
int64int64int64float64float64float64float64float64float64float64float64float64float64int64int64int64str1float64float64float64
16080286904444723191336429430000113155014.3646518286.9044337444.005475750.0031089999247342355.980649802950211e-061.77999997138977051.23810994625091550.99833500385284420.0031244698911905295.692909780918853e-061027605171287374912g15.18249988555908215.168448221176604-0.01405166438247818
16080286904444723191337480430000116155014.3751566286.9044389444.005475020.0031305600423365836.001439942338038e-061.66938996315002441.32772994041442870.99819701910018920.00315612996928393845.713579867006047e-06102760517128124815424g15.18249988555908215.160944905703076-0.021554979856006184
16080286904444723191338674430000149255014.3869718286.9044446944.005482940.00388367008417844777.313420155696804e-061.85869002342224121.36463999748229980.99769699573516850.00385689991526305687.132980044843862e-061027605171287374912r14.98989963531494114.926894175737598-0.06300545957734371
16080286904444723191339651330000132255014.3967402286.9044510944.005479350.00376664008945226677.475080110452836e-061.54463005065917971.44604003429412840.99898302555084230.0039351601153612147.260589882207569e-061027605171287342144r14.98989963531494114.960114688961145-0.02978494635379647
16080286904444723191952738340000243355020.5277331286.9044503644.005482940.0016464700456708674.894850007985951e-061.32537996768951421.2557699680328370.97693598270416260.00160921004135161643.8023099477868527e-06102760517128124782656i14.89070034027099615.858615415064950.9679150747939538
16080286904444723191953928340000228355020.5396333286.9044471644.005488080.00182494998443871745.4300599003909156e-061.67410004138946531.5688699483871460.98126101493835450.00180357997305691243.995270162704401e-06102760517128124782656i14.89070034027099615.746872583881680.8561722436106844
160802869044447231126057565120000092155361.57592286.9044531744.005474510.0030957299750298266.723119895468699e-061.74979996681213381.62422001361846920.99882602691650390.00310044991783797746.6938700911123306e-061027605171287374912g15.18249988555908215.173092319156346-0.009407566402735767
160802869044447231126058741130000111155361.587678286.9044515744.005480650.0030281499493867166.5775197981565725e-061.62838995456695561.52578997611999510.99838697910308840.00310619990341365346.764249974366976e-061027605171287374912g15.18249988555908215.1970565575907340.01455667203165234
160802869044447231126352519210000114255364.5254296286.9044465344.005478780.0036774401087313896.877689884277061e-061.00426995754241940.94952398538589480.96945202350616460.00371408998034894477.080509931256529e-061027605171287342144r14.98989963531494114.986135978798345-0.003763656516596825
160802869044447231126841763510000133355369.4179097286.904446344.005481060.0041252099908888347.12431983629358e-061.58422994613647461.49049997329711910.93467801809310910.0041566598229110247.160439963627141e-06102760517128124815424i14.89070034027099614.86138484774693-0.0293154925240664
............................................................
160802869044447231269459912610000126356795.5993847286.9044495844.005473260.0039810100570321087.129880032152869e-061.2938699722290041.17932999134063720.92977499961853030.0039993599057197577.305519829969853e-061027605171287374912i14.89070034027099614.9000168131937160.009316472922719754
160802869044447231269460179610000135356795.6020632286.9044475744.005475340.0039909100160002717.163299869716866e-061.29076004028320311.22738003730773930.94575601816177370.0039874999783933167.244019798235968e-061027605171287374912i14.89070034027099614.8973201606170560.0066198203460601235
160802869044447231269460446610000127356795.6047349286.9044458144.00547310.0039646998047828677.156480023695622e-061.29112005233764651.25599002838134770.96938198804855350.0039926599711179737.251020178955514e-061027605171287374912i14.89070034027099614.9044742264535230.013773886182526951
160802869044447231269547221010000084356796.4724798286.904438144.005477580.0040906001813709748.327579962497111e-061.06857001781463621.05897998809814450.98544800281524660.0040811798535287388.182380042853765e-061027605171287342144i14.89070034027099614.870532416818648-0.02016792345234819
160802869044447231269548553010000082356796.4857957286.9044308744.005473380.0040280800312757497.612170065840473e-061.24870002269744871.14948999881744380.98370701074600220.0040692100301384937.582740181533154e-061027605171287342144i14.89070034027099614.88725477330241-0.0034455669685868884
160802869044447231280532535510000127356906.3256294286.9044445344.005473280.0040027601644396787.167820058384677e-061.28373003005981450.99674600362777710.99751597642898560.0040260502137243757.325410024350276e-061027605171287374912i14.89070034027099614.8941010774357350.0034007371647390983
160802869044447231280533813510000137356906.3384004286.9044442244.005476040.0039442800916731367.267320143000688e-061.2923899888992311.20358002185821530.99959999322891240.0040074600838124757.284520052053267e-061027605171287374912i14.89070034027099614.9100806311647120.01938029089371618
160802869044447231280535090510000128356906.3511641286.9044459244.005472860.00396795012056827557.270130026881816e-061.162809967994691.09028005599975590.99821501970291140.0039971699006855497.332159839279484e-061027605171287374912i14.89070034027099614.9035844895419450.012884149270949052
160802869044447231280536365510000128356906.3639219286.9044454744.005475980.0040202899836003787.50502022128785e-061.27920997142791751.23943996429443360.99912101030349730.00407407013699412357.388349786197068e-061027605171287374912i14.89070034027099614.88935655028919-0.001343789981806509
160802869044447231281422935550000135456915.2295351286.904444144.005471620.0040525100193917751.1075700058427174e-051.31142997741699221.13384997844696040.99958801269531250.0040521197952330111.0371900316386018e-05102760517128124815424z15.19989967346191414.880689756830217-0.3192099166316975
In [24]:
t = dtab['obsTime']
dmag = dtab['dmag']
xlim = np.array([t.min(),t.max()])
xlim = xlim + np.array([-1,1])*0.02*(xlim[1]-xlim[0])
ylim = np.array([dmag.max(),dmag.min()])
ylim = ylim + np.array([-1,1])*0.02*(ylim[1]-ylim[0])

plt.rcParams.update({'font.size': 14})
plt.figure(1,(10,10))
for i, filter in enumerate("grizy"):
    plt.subplot(511+i)
    w = np.where(dtab['filter']==filter)[0]
    plt.plot(t[w],dmag[w],'-o')
    magmean = dtab['magmean'][w[0]]
    plt.ylabel('{} [mag - {:.2f}]'.format(filter,magmean))
    plt.xlim(xlim)
    plt.ylim(ylim)
    if i==0:
        plt.title(objname)
plt.xlabel('Time [MJD]')
plt.tight_layout()

Eclipsing binaries basically vary by same amount in all filters since it is a geometrical effect, so combine the data into a single light curve.

Wrap using known period and plot versus phase. Plot two periods of the light curve this time.

This nice light curve appears to show a secondary eclipse.

In [25]:
period = 3.6071431
bjd0 = 54999.289794
t = ((dtab['obsTime']-bjd0) % period) / period
dmag = dtab['dmag']
w = np.argsort(t)
# extend to two periods
nw = len(w)
w = np.append(w,w)
t = t[w]
# add one to second period
t[-nw:] += 1
dmag = dmag[w]
xlim = [0,2.0]
ylim = np.array([dmag.max(),dmag.min()])
ylim = ylim + np.array([-1,1])*0.02*(ylim[1]-ylim[0])

plt.rcParams.update({'font.size': 14})
plt.figure(1,(12,6))
plt.plot(t,dmag,'-o')
plt.xlim(xlim)
plt.ylim(ylim)
plt.xlabel('Phase')
plt.ylabel('Delta magnitude from mean [mag]')
plt.title(objname)
plt.tight_layout()
In [ ]: