WASP-189

Example of using Multivisit to measure eclipse depths in multiple data sets.

See help(MultiVisit) for more information on the use of this feature.

N.B. the data provided with the pycheops distribution do not include metadata so it is not possible to calculate a correction for the ramp effect (thermal settling) with these example data.

In [1]:
%pylab inline
from pycheops import MultiVisit
from pycheops import Dataset
from uncertainties import ufloat
from os.path import join,abspath,dirname
from pycheops import __file__ as pycheops_file
from pycheops.core import load_config
from shutil import copy
from pycheops.constants import c, au
Populating the interactive namespace from numpy and matplotlib

Planet properties

The parameters D, W and b that specify the geometry of the star-planet system cannot be determined from the eclipse so we have to give their values to the fitting routine. Here we used the values and errors from Lendl et al. (2020). Each paramater is defined as a ufloat so that the uncertainty on these values is accounted for in the analysis.

In [2]:
# Transit paramaters from  Lendl et al., 2020
BJD_0 = 2458926.541696  
P = 2.7240330
T14 = ufloat(4.336, 0.0056)   # In hours
b = ufloat(0.478,0.01)
k = ufloat(0.07045, 0.00014)
D = k**2
W = T14/24/P
a_c = 0.05053 * au/c / 86400
print(f'Light travel time correction = {a_c*86400:0.1f} s')
Light travel time correction = 25.2 s

Copy light curve data from source distribution to pycheops data cache

In [3]:
file_keys = [f'CH_PR100041_TG00020{i}_V0102' for i in range(1,5)]
src = join(dirname(abspath(pycheops_file)),'examples','Notebooks')
config = load_config()
cache_path = config['DEFAULT']['data_cache_path']
print(f'Copying data from {src} to {cache_path}')
for file in file_keys:
    copy(join(src,file+'.tgz'),cache_path)
Copying data from /Users/pflm/miniconda3/lib/python3.7/site-packages/pycheops-0.9.9-py3.7.egg/pycheops/examples/Notebooks to /Users/pflm/pycheops/data
In [4]:
Datasets=[]
for file_key in file_keys:
    d = Dataset(file_key,verbose=False, metadata=False)
    Datasets.append(d)
    _ = d.get_lightcurve('DEFAULT',verbose=False,decontaminate=False)
    t_,f_,e_ = d.clip_outliers(verbose=False)
    t_mid =  d.bjd_ref+np.median(t_)
    print(file_key,len(t_),d.bjd_ref)
CH_PR100041_TG000201_V0102 736 2458927
CH_PR100041_TG000202_V0102 793 2458935
CH_PR100041_TG000203_V0102 814 2458938
CH_PR100041_TG000204_V0102 934 2458946

Star properties object

In [5]:
from pycheops. starproperties import StarProperties
star = StarProperties(d.target)
print(star)
Identifier : WASP-189
Coordinates: 15:02:44.87 -03:01:53.0
T_eff :  7996 +/-  99 K   [SWEET-Cat]
log g :  3.98 +/- 0.05    [SWEET-Cat]
log rho : -0.75 +/- 0.09  (solar units)
h_1 : 0.790 +/- 0.100     [ATLAS]
h_2 : 0.532 +/- 0.100     [ATLAS]

Fit with decorrelation

Selection of decorrelation parameters and other details of these fits are quite arbitary here. In reality, a much more careful analysis similar to the one shown in KELT11b-tutorial would be needed.

These decorrelation parameters selected and saved in the last fit to each the individual datasets are used in Multivisit.

In [6]:
lmfits = []
for d in Datasets:
    T_0 = floor((d.bjd_ref-BJD_0)/P)*P + BJD_0 - d.bjd_ref
    d.lmfit_eclipse(T_0 = T_0,
                    P = P, 
                    dfdt=(-0.1,0.1),
                    d2fdt2=(-0.1,0.1),
                    L=(1e-6,2e-4,1e-3),
                    W =W, D=D, b=b,
                    dfdsinphi=(-1,1),dfdcosphi=(-1,1),
                    dfdsin2phi=(-1,1),dfdcos2phi=(-1,1),
                    dfdsin3phi=(-1,1),dfdcos3phi=(-1,1)
                   )
    lmfits.append(d.lmfit)
    f = d.plot_lmfit(title=d.file_key)
    d.save()  # Saves the results as a .dataset file for use in Multivisit 

Create a Multivisit object using saved datasets in the working directory

The option id_kws={'dace':False} can be used to look for target information from SWEET-Cat if the target is not listed on the tables on DACE.

We can also set our own values for the star properties - here we use the values from Lendel et al.

In [7]:
M = MultiVisit('WASP-189', id_kws={'dace':False, 
                                   'teff':ufloat(8000,80), 
                                   'logg':ufloat(3.9,0.2),
                                   'metal':ufloat(0.29,0.13)})
Identifier : WASP-189
Coordinates: 15:02:44.87 -03:01:53.0
T_eff :  8000 +/-  80 K   [User]
log g :  3.90 +/- 0.20    [User]
[M/H] : +0.29 +/- 0.13    [User]
log rho : -0.89 +/- 0.33  (solar units)
h_1 : 0.787 +/- 0.100     [ATLAS]
h_2 : 0.529 +/- 0.100     [ATLAS]


 N  file_key                   Aperture last_ GP  Glint pipe_ver
 ---------------------------------------------------------------------------
  1 CH_PR100041_TG000201_V0102 DEFAULT  lmfit No  No    cn03-20200703T111359
  2 CH_PR100041_TG000202_V0102 DEFAULT  lmfit No  No    cn03-20200703T111359
  3 CH_PR100041_TG000203_V0102 DEFAULT  lmfit No  No    cn03-20200703T111359
  4 CH_PR100041_TG000204_V0102 DEFAULT  lmfit No  No    cn03-20200703T111359

fit_eclipse with eclipse depth variation (edv)

The option edv=True is used so that different planet/star flux ratios, $L_1, L_2, \dots$ are used for each dataset. A Gaussian prior with a mean value L=85 ppm and standard deviation ebv_prior=50 ppm is imposed on $L_1, L_2, \dots$.

The parameter log_sigma_w specifies additional uncorrelated white noise per data-point $\log \sigma_w$.

N.B. the fit automagically includes the following model to account for trends in the data correlated with roll angle: $$\sum_{j=1}^n \alpha_j\sin(j\cdot\Omega t) + \beta_j\cos(j\cdot\Omega t),$$ where, $\Omega$ is the angular rotation frequency of the spacecraft. The free parameters $\alpha_j$ and $\beta_j$ are never calculated explicitly. Instead, they are implicitly accounted for in the calculation of the likelihood using the trick explained by Rodrigo et al. (2017RNAAS...1....7L). The number of terms in this model ($n=3$ by default, recommended) can be set using the parameter nroll. To disable this feature using unroll=False.

In [8]:
# Time of transit closest to mid-point of datasets
T_0 = M.tzero(BJD_0, P)
result = M.fit_eclipse(T_0=T_0, P=P, D=D, W=W, b=b, a_c=a_c, 
                    L = 85e-6, edv=True, edv_prior=50e-6,
                    log_sigma_w=ufloat(-10,5),
                    burn=1024, steps=256, nwalkers=128) 
Running burn-in ..
100%|██████████| 1024/1024 [09:52<00:00,  1.73it/s]
Running sampler ..
100%|██████████| 256/256 [02:28<00:00,  1.72it/s]

Plot the fit

The green line is the eclipse light curve with no instrumental effects or trends. The brown line is the fit to the data. You can use the keywords add_gaps and gap_tol to control whether/how this fit is plotted with gaps.

In [9]:
M.plot_fit(binwidth=0.0025, data_offset=0.001, res_offset=0.001);
In [10]:
M.plot_fit(binwidth=0.0025,detrend=True);

Trail plot

It is good practice to check that the emcee sampler has converged by inspecting a plot of the parameter values v. step number for all the walkers. This can be done using the trail_plot command. The trails should look like noise, i.e., no trends in the mean or variance of the walkers v. step number.

In [11]:
M.trail_plot();
In [12]:
print(M.fit_report(min_correl=0.8))
[[Fit Statistics]]
    # fitting method   = emcee
    # function evals   = 109543
    # data points      = 3277
    # variables        = 20
    chi-square         = 4048.61708
    reduced chi-square = 1.24305099
    Akaike info crit   = -51055.1526
    Bayesian info crit = -50933.2589
    RMS residual       = 94.2 ppm
[[Variables]]
    D:            0.00495989 +/- 2.0158e-05 (0.41%) (init = 0.004963202)
    W:            0.06631167 +/- 8.5473e-05 (0.13%) (init = 0.06632323)
    b:            0.47900526 +/- 0.01027237 (2.14%) (init = 0.478)
    P:            2.724033 (fixed)
    T_0:          1937.438 (fixed)
    f_c:          0 (fixed)
    f_s:          0 (fixed)
    L:            8.5e-05 (fixed)
    a_c:          0.0002918369 (fixed)
    k:            0.07042652 == 'sqrt(D)'
    aR:           4.59509865 == 'sqrt((1+k)**2-b**2)/W/pi'
    sini:         0.99455189 == 'sqrt(1 - (b/aR)**2)'
    logrho:      -0.75585352 == 'log10(4.3275e-4*((1+k)**2-b**2)**1.5/W**3/P**2)'
    e:            0.00000000 == 'f_c**2 + f_s**2'
    log_sigma_w: -10.0675905 +/- 0.05863810 (0.58%) (init = -10)
    L_01:         9.1238e-05 +/- 1.1888e-05 (13.03%) (init = 8.5e-05)
    c_01:         0.99999401 +/- 3.9099e-05 (0.00%) (init = 0.9998754)
    dfdt_01:     -3.4552e-04 +/- 2.0460e-05 (5.92%) (init = -0.000346778)
    d2fdt2_01:    0.00153796 +/- 2.3211e-04 (15.09%) (init = 0.001687511)
    L_02:         8.9582e-05 +/- 1.1242e-05 (12.55%) (init = 8.5e-05)
    c_02:         1.00001611 +/- 2.0540e-05 (0.00%) (init = 0.9999875)
    dfdt_02:     -9.1495e-05 +/- 2.7068e-05 (29.58%) (init = -9.400196e-05)
    d2fdt2_02:    0.00168730 +/- 2.3790e-04 (14.10%) (init = 0.001710742)
    L_03:         7.8801e-05 +/- 1.0688e-05 (13.56%) (init = 8.5e-05)
    c_03:         0.99996348 +/- 1.7389e-05 (0.00%) (init = 0.9999191)
    dfdt_03:      1.2672e-05 +/- 2.4514e-05 (193.45%) (init = 1.919225e-05)
    d2fdt2_03:    6.7109e-04 +/- 2.1200e-04 (31.59%) (init = 0.0005542108)
    L_04:         9.5450e-05 +/- 9.3054e-06 (9.75%) (init = 8.5e-05)
    c_04:         1.00000668 +/- 9.2647e-06 (0.00%) (init = 0.9999864)
    dfdt_04:      1.6505e-04 +/- 2.0545e-05 (12.45%) (init = 0.000170799)
    d2fdt2_04:   -2.3846e-04 +/- 1.8920e-04 (79.34%) (init = -0.0002722958)
[[Priors]]
    D:           0.00496320 +/- 1.9726e-05
    W:           0.06632323 +/- 8.5657e-05
    b:           0.47800000 +/- 0.01000000
    log_sigma_w:-10.0000000 +/- 5.00000000
    L_01:        8.5000e-05 +/- 5.0000e-05
    L_02:        8.5000e-05 +/- 5.0000e-05
    L_03:        8.5000e-05 +/- 5.0000e-05
    L_04:        8.5000e-05 +/- 5.0000e-05
[[Software versions]]
    CHEOPS DRP : cn03-20200703T111359
    pycheops   : 0.9.9
    lmfit      : 1.0.1

Parameter correlation (corner) plot

Use plotkeys='all' to show all the free parameters in this corner plot. Here we are using some neat python syntax to generate a list of parameters incuding all the ttv values.

In [13]:
pk = ['D'] + [f'L_{k+1:02d}' for k in range(len(M.datasets))] 
M.corner_plot(plotkeys=pk,show_ticklabels=True);

Plot of eclipse depth v. time.

The $\pm$1-$\sigma$ range from Lendl et al. is shown for comparison.

If you are fitting transits with transit-time variations (ttv=True) then you can use M.ttv_plot() to produce a nice O-C diagram.

In [14]:
fig,ax = subplots()
for j in range(4):
    t = M.datasets[j].lc['time'].mean() - 1900
    l = M.result.params[f'L_{j+1:02d}'].value*1e6
    lerr = M.result.params[f'L_{j+1:02d}'].stderr*1e6
    ax.errorbar(t,l,yerr=lerr,fmt='bo',capsize=4)
axhline(87.9-4.3,c='darkcyan',ls=':',label='Lendl et al.')
axhline(87.9+4.3,c='darkcyan',ls=':')
ax.set_xlabel('BJD - 2458900')
ax.set_ylabel(r'$dF_{\rm occ}$')
ax.legend();

© Pierre Maxted, Keele University, 2020