forked from nespinoza/juliet
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathjuliet.py
2776 lines (2564 loc) · 165 KB
/
juliet.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
from mpl_toolkits.axes_grid.inset_locator import inset_axes
# Import batman, for lightcurve models:
import batman
# Import radvel, for RV models:
import radvel
# Import george for detrending:
import george
# Import celerite for detrending:
import celerite
# Plotting functions:
import seaborn as sns
import argparse
import matplotlib
import matplotlib.pyplot as plt
# Import dynesty for dynamic nested sampling:
try:
import dynesty
except:
print('Dynesty installation not detected.')
# Import multinest for (importance) nested sampling:
try:
import pymultinest
except:
print('(py)MultiNest installation (or libmultinest.dylib) not detected. juliet will only work with dynesty')
import sys
from scipy.interpolate import interp1d
import numpy as np
import utils
import os
import time
# Prepare the celerite term:
import celerite
from celerite import terms
# This class was written by Daniel Foreman-Mackey for his paper:
# https://github.com/dfm/celerite/blob/master/paper/figures/rotation/rotation.ipynb
class RotationTerm(terms.Term):
parameter_names = ("log_amp", "log_timescale", "log_period", "log_factor")
def get_real_coefficients(self, params):
log_amp, log_timescale, log_period, log_factor = params
f = np.exp(log_factor)
return (
np.exp(log_amp) * (1.0 + f) / (2.0 + f),
np.exp(-log_timescale),
)
def get_complex_coefficients(self, params):
log_amp, log_timescale, log_period, log_factor = params
f = np.exp(log_factor)
return (
np.exp(log_amp) / (2.0 + f),
0.0,
np.exp(-log_timescale),
2*np.pi*np.exp(-log_period),
)
# Definition of user-defined arguments. Useful when juliet is used in command-line mode:
parser = argparse.ArgumentParser()
# This reads the lightcurve file. First column is time, second column is flux, third flux_err, fourth telescope name:
parser.add_argument('-lcfile', default=None)
# This reads the RV data. First column is time, second column is rv, third rv_err, fourth telescope name:
parser.add_argument('-rvfile', default=None)
# This reads the external parameters to fit to the photometry GP:
parser.add_argument('-lceparamfile', default=None)
# This reads the external parameters to fit to the RV GP:
parser.add_argument('-rveparamfile', default=None)
# This reads an output folder:
parser.add_argument('-ofolder', default='results')
# This defines the limb-darkening to be used. Can be either common to all instruments (e.g., give 'quadratic' as input),
# or it can be different for every instrument, in which case you must pass a comma separated list of instrument-ldlaw pair, e.g.
# 'TESS-quadratic,CHAT-linear', etc.:
parser.add_argument('-ldlaw', default='quadratic')
# Lightcurve time definitions (e.g., 'TESS-TDB,CHAT-UTC', etc.). If not given, it is assumed all lightcurves are in TDB:
parser.add_argument('-lctimedef', default='TDB')
# Radial-velocities time definitions (e.g., 'HARPS-TDB,CORALIE-UTC', etc.). If not given, it is assumed all RVs are in UTC:
parser.add_argument('-rvtimedef', default='UTC')
# This reads the prior file:
parser.add_argument('-priorfile', default=None)
# This defines if rv units are m/s (ms) or km/s (kms); useful for plotting. Default is m/s:
parser.add_argument('-rvunits', default='ms')
# This defines the minimum chunk of RV data that activates multi-panel plots:
parser.add_argument('-nrvchunk', default = 100)
# Decide if binned RVs will be plotted at the end:
parser.add_argument('--plotbinnedrvs', dest='plotbinnedrvs', action='store_true')
# Allow user to change the maximum eccentricity for the fits; helps avoid issue that Batman can run into with high eccentricities
parser.add_argument('-ecclim', default=0.95,type=float)
# Define stellar density mean and stdev if you have it --- this will help with a constrained transit fit:
parser.add_argument('-sdensity_mean', default=None)
parser.add_argument('-sdensity_sigma', default=None)
# Define if the sampling for p and b in Espinoza (2018) wants to be used; define pl and pu (this assumes
# sampling parameters in prior file are r1 and r2):
#parser.add_argument('--efficient_bp', dest='efficient_bp', action='store_true')
parser.add_argument('-pl', default=0.)
parser.add_argument('-pu', default=1.)
# Flux limits for the phased photometry plots:
parser.add_argument('-phasefluxplotlim', default=None)
# Number of live points:
parser.add_argument('-nlive', default=1000)
# Number of samples to draw from posterior to compute models:
parser.add_argument('-nsims', default=5000)
# Number of oversample points in rvs:
parser.add_argument('-noversample', default=1000)
# Dealing with supersampling for long exposure times for LC. n_supersamp is the number of
# supersampled points, exptime_supersamp the exposure time and instrument_supersamp the instrument
# for which you want to apply supersampling. If you need several instruments to have supersampling,
# you can give these input as comma separated values, e.g., '-instrument_supersamp TESS,K2 -n_supersamp 20,30 -exptime_supersamp 0.020434,0.020434'
# will give values of n_supersamp of 20 and 30 to TESS and K2 lightcurves, respectively, and both of them with texp of 0.020434 days.
parser.add_argument('-n_supersamp', default=None)
parser.add_argument('-exptime_supersamp', default=None)
parser.add_argument('-instrument_supersamp', default=None)
# Define if HODLRSolver wants to be used for george. Only applied to photometric GPs:
parser.add_argument('--george_hodlr', dest='george_hodlr', action='store_true')
# Define if Dynamic Nested Sampling is to be used:
parser.add_argument('--dynamic', dest='dynamic', action='store_true')
# Define if dynesty will be used:
parser.add_argument('--use_dynesty', dest='use_dynesty', action='store_true')
# Define some arguments for dynesty runs (see https://dynesty.readthedocs.io/en/latest/api.html). First, bounded method for dynesty:
parser.add_argument('-dynesty_bound', default='multi')
# Method used to sample uniformly within the likelihood constraint, conditioned on the provided bounds:
parser.add_argument('-dynesty_sample', default='rwalk')
# Number of threads to use within dynesty (giving a number here assumes one wants to perform multithreading):
parser.add_argument('-dynesty_nthreads', default='none')
args = parser.parse_args()
# Check george hodlr flag:
george_hodlr = args.george_hodlr
# Check the dynesty and dynamic flag:
use_dynesty = args.use_dynesty
dynamic = args.dynamic
# Check dynesty options:
dynesty_bound = args.dynesty_bound
dynesty_sample = args.dynesty_sample
dynesty_nthreads = args.dynesty_nthreads
from dynesty.utils import resample_equal
print('\n\t \t ---------------------------------------------\n')
print('\t \t juliet v.2.0 ')
print('\t \t ')
print('\t \t Authors: N. Espinoza, D. Kossakowski')
print('\t \t Contact: espinoza at mpia.de\n')
print('\t \t ---------------------------------------------\n')
# If not ran already, run dynesty, save posterior samples and evidences to pickle file:
# For this, first check if dynamic or normal NS is going to be used:
if use_dynesty:
if dynamic:
prefix = 'dynamic_dynesty_'
print('\t Running DYNAMIC NESTED SAMPLING (dynesty)')
else:
prefix = 'dynesty_'
print('\t Running NESTED SAMPLING (dynesty)')
else:
prefix = 'multinest_'
print('\t Running NESTED SAMPLING (multinest)')
# Output folder:
out_folder = args.ofolder+'/'
if not os.path.exists(out_folder):
os.mkdir(out_folder)
# Read lightcurves and rvs --- save them to out_folder if not already there:
lcfilename = args.lcfile
rvfilename = args.rvfile
if (not os.path.exists(out_folder+'lc.dat')) and (lcfilename is not None):
os.system('cp '+lcfilename+' '+out_folder+'lc.dat')
if (not os.path.exists(out_folder+'rvs.dat')) and (rvfilename is not None):
os.system('cp '+rvfilename+' '+out_folder+'rvs.dat')
# Read the eparam files for lightcurves and rvs --- save them to out_folder if not already there:
lceparamfile = args.lceparamfile
rveparamfile = args.rveparamfile
if (not os.path.exists(out_folder+'lc_eparams.dat')) and (lceparamfile is not None):
os.system('cp '+lceparamfile+' '+out_folder+'lc_eparams.dat')
if (not os.path.exists(out_folder+'rv_eparams.dat')) and (rveparamfile is not None):
os.system('cp '+rveparamfile+' '+out_folder+'rv_eparams.dat')
sd_mean,sd_sigma = args.sdensity_mean,args.sdensity_sigma
stellar_density = False
if sd_mean is not None:
sd_mean,sd_sigma = np.double(sd_mean),np.double(sd_sigma)
stellar_density = True
if lcfilename is not None:
t_lc,f_lc,ferr_lc,instruments_lc,instrument_indexes_lc,\
ninstruments_lc,inames_lc, lm_boolean, lm_arguments = utils.readlc(lcfilename)
print('\t Photometric instruments:',inames_lc)
# First of all, generate a dictionary for each instrument. This will save the
# lightcurve objects:
lc_dictionary = {}
for i in range(ninstruments_lc):
lc_dictionary[inames_lc[i]] = {}
# Save if a given instrument will receive resampling:
lc_dictionary[inames_lc[i]]['resampling'] = False
# Save if a given instrument has GP fitting ON:
lc_dictionary[inames_lc[i]]['GPDetrend'] = False
# Save if transit fitting will be done for a given dataset/instrument (this is so users can fit photometry with, e.g., GPs):
lc_dictionary[inames_lc[i]]['TransitFit'] = False
# Convert times from TDB to UTC to match RVs. First, see if there is more than one instrument
# time definition. If not, assume all times are TDB and thus convert all times to UTC:
lctimedefs = args.lctimedef.split(',')
if len(lctimedefs) == 1:
t_lc = utils.convert_time(lctimedefs[0].split('-')[-1].lower()+'->utc',t_lc)
else:
for lctimedef in lctimedefs:
instrument,timedef = lctimedef.split('-')
t_lc[instrument_indexes_lc[instrument]] = utils.convert_time(timedef.split()[0].lower()+'->utc',t_lc[instrument_indexes_lc[instrument]])
# Extract limb-darkening law. If just one is given, assume same LD law for all instruments. If not, assume a
# different law for each instrument:
ld_laws = args.ldlaw.split(',')
if len(ld_laws) == 1:
for i in range(ninstruments_lc):
lc_dictionary[inames_lc[i]]['ldlaw'] = (ld_laws[0].split('-')[-1]).split()[0].lower()
else:
for ld_law in ld_laws:
instrument,ld = ld_law.split('-')
lc_dictionary[instrument.split()[0]]['ldlaw'] = ld.split()[0].lower()
# Extract phased-flux plot limits:
phasefluxplotlim = args.phasefluxplotlim
if phasefluxplotlim is not None:
ylow_pflux,yhigh_pflux = args.phasefluxplotlim.split(',')
ylow_pflux = np.double(ylow_pflux)
yhigh_pflux = np.double(yhigh_pflux)
phasefluxlims = True
else:
phasefluxlims = False
# Extract supersampling parameters for transit model. If not given for each instrument, assume all must be
# resampled:
if args.instrument_supersamp is not None:
instrument_ss = args.instrument_supersamp.split(',')
n_ss = np.array(args.n_supersamp.split(',')).astype('int')
exptime_ss = np.array(args.exptime_supersamp.split(',')).astype('double')
for i in range(len(instrument_ss)):
lc_dictionary[instrument_ss[i]]['resampling'] = True
lc_dictionary[instrument_ss[i]]['nresampling'] = n_ss[i]
lc_dictionary[instrument_ss[i]]['exptimeresampling'] = exptime_ss[i]
else:
if args.n_supersamp is not None:
n_ss = int(args.n_supersamp)
else:
n_ss = args.n_supersamp
if args.exptime_supersamp is not None:
exptime_ss = float(args.exptime_supersamp)
for i in range(ninstruments_lc):
lc_dictionary[inames_lc[i]]['resampling'] = True
lc_dictionary[inames_lc[i]]['nresampling'] = n_ss
lc_dictionary[inames_lc[i]]['exptimeresampling'] = exptime_ss
else:
exptime_ss = args.exptime_supersamp
###################
else:
inames_lc = []
if rvfilename is not None:
t_rv,rv_rv,rverr_rv,instruments_rv,instrument_indexes_rv,\
ninstruments_rv,inames_rv,lm_rv_boolean, lm_rv_arguments = utils.readlc(rvfilename)
rvresiduals = np.zeros(len(t_rv))
rvresiduals_err = np.zeros(len(t_rv))
idx_ordered_rv = np.argsort(len(t_rv))
# First of all, generate a dictionary for RVs. This will save the
# RV objects:
rv_dictionary = {}
rv_dictionary['GPDetrend'] = False
for i in range(ninstruments_rv):
rv_dictionary[inames_rv[i]] = {}
# lc_dictionary[inames_lc[i]]['resampling'] = False
# lc_dictionary[inames_lc[i]]['GPDetrend'] = False
else:
inames_rv = []
# Read priors, number of planets that transit and number of planets in RVs:
priorfile = args.priorfile
priors,n_transit,n_rv,numbering_transit,numbering_rv,n_params = utils.readpriors(priorfile)
# Check if RV line will be fitted:
if 'rv_slope' in priors.keys():
if 'rv_quad' in priors.keys():
fitrvquad = True
fitrvline = False
else:
fitrvline = True
fitrvquad = False
else:
fitrvline = False
fitrvquad = False
# Check if stellar density will be fitted instead of the a/R_*:
if 'rho' in priors.keys():
fitrho = True
print('\t Fitting of stellar density detected.')
else:
fitrho = False
# Check for which instruments transits will be fitted:
if lcfilename is not None:
for i in range(ninstruments_lc):
for pri in priors.keys():
if pri[0:2] == 'q1':
if inames_lc[i] in pri.split('_'):
lc_dictionary[inames_lc[i]]['TransitFit'] = True
print('\t Transit fit detected for instrument ',inames_lc[i])
# Check eccentricity parametrization for each planet in the juliet numbering scheme.
# 0 = ecc, omega 1: ecosomega,esinomega 2: sqrt(e)cosomega, sqrt(e)sinomega
ecc_parametrization = {} # np.zeros(np.max([n_transit,n_rv]))
ecc_parametrization['rv'] = {}
ecc_parametrization['transit'] = {}
# At the same time, check if efficient exploration of the (b,p) plane was forced for each planet
# (i.e., check if planets where constrained by r1,r2 or by b,p directly). Assume it was not for
# each planet:
efficient_bp = n_transit*[False]
# First for the transiting planets:
for n in range(n_transit):
i = numbering_transit[n]
if 'ecosomega_p'+str(i) in priors.keys():
ecc_parametrization['transit'][i] = 1
print('\t ecosomega,esinomega parametrization for transiting planet ',i)
elif 'secosomega_p'+str(i) in priors.keys():
ecc_parametrization['transit'][i] = 2
print('\t sqrt(e)cosomega,sqrt(e)sinomega parametrization for transiting planet ',i)
else:
ecc_parametrization['transit'][i] = 0
print('\t e,omega parametrization for transiting planet ',i)
if 'r1_p'+str(i) in priors.keys():
efficient_bp[n] = True
print('\t Efficient sampling of the (b,p) plane detected for planet p'+str(i))
pl,pu = np.double(args.pl),np.double(args.pu)
Ar = (pu - pl)/(2. + pl + pu)
for n in range(n_rv):
i = numbering_rv[n]
if 'ecosomega_p'+str(i) in priors.keys():
ecc_parametrization['rv'][i] = 1
print('\t ecosomega,esinomega parametrization for RV planet ',i)
elif 'secosomega_p'+str(i) in priors.keys():
ecc_parametrization['rv'][i] = 2
print('\t sqrt(e)cosomega,sqrt(e)sinomega parametrization for RV planet ',i)
else:
ecc_parametrization['rv'][i] = 0
print('\t e,omega parametrization for RV planet ',i)
# Save prior file to output folder if not already there:
if not os.path.exists(out_folder+'priors.dat'):
os.system('cp '+priorfile+' '+out_folder+'priors.dat')
# RV units:
rvunits = args.rvunits
# nrvchunk:
nrvchunk = np.double(args.nrvchunk)
# If binned rvs will be plotted:
plotbinnedrvs = args.plotbinnedrvs
if lcfilename is not None:
# Float the times (batman doesn't like non-float 64):
t_lc = t_lc.astype('float64')
# Extract external parameter file for LC GP:
if lceparamfile is not None:
GPDict = utils.readeparams(lceparamfile)
for instrument in GPDict.keys():
print('\t Fitting photometric GP to ',instrument,' instrument.')
# Detect the GP the user wants for this instrument using the priors dictionary.
# For this, consider the possibility that the user might want to combine the GP parameters
# between instruments, so iterate.
for pnames in priors.keys():
vec = pnames.split('_')
if (vec[0] == 'GP') and ('alpha0' in vec[1]) and (instrument in vec):
# Detected multi-dimensional squared-exponential GP:
lc_dictionary[instrument]['GPType'] = 'SEKernel'
break
if (vec[0] == 'GP') and ('Gamma' in vec[1]) and (instrument in vec):
# Detected exp-sine-squared kernel:
lc_dictionary[instrument]['GPType'] = 'ExpSineSquaredSEKernel'
break
if (vec[0] == 'GP') and ('B' in vec[1]) and (instrument in vec):
# Detected celerite quasi-periodic kernel:
lc_dictionary[instrument]['GPType'] = 'CeleriteQPKernel'
break
if (vec[0] == 'GP') and ('timescale' in vec[1]) and (instrument in vec):
# If already defined, then this is a multiplication of Matern and Exp kernels:
if 'GPType' in lc_dictionary[instrument]:
lc_dictionary[instrument]['GPType'] = 'CeleriteMaternExpKernel'
break
# Detected celerite Exp kernel:
lc_dictionary[instrument]['GPType'] = 'CeleriteExpKernel'
if (vec[0] == 'GP') and ('rho' in vec[1]) and (instrument in vec):
# If already defined, then this is a multiplication of Matern and Exp kernels:
if 'GPType' in lc_dictionary[instrument]:
lc_dictionary[instrument]['GPType'] = 'CeleriteMaternExpKernel'
break
# Detected celerite matern:
lc_dictionary[instrument]['GPType'] = 'CeleriteMatern'
if (vec[0] == 'GP') and ('Plife' in vec[1]) and (instrument in vec):
# Detected celerite SHO:
lc_dictionary[instrument]['GPType'] = 'CeleriteSHOKernel'
break
print('\t Detected ',lc_dictionary[instrument]['GPType'],'for the GP')
# For each instrument for which there are external parameters, activate GP:
lc_dictionary[instrument]['GPDetrend'] = True
# Save variables, standarize them if GP is SEKernel:
lc_dictionary[instrument]['X'] = GPDict[instrument]['variables']
lc_dictionary[instrument]['nX'] = lc_dictionary[instrument]['X'].shape[1]
print('\t (',lc_dictionary[instrument]['nX'],'external parameters)')
if lc_dictionary[instrument]['GPType'] == 'SEKernel':
for i in range(lc_dictionary[instrument]['nX']):
lc_dictionary[instrument]['X'][:,i] = (lc_dictionary[instrument]['X'][:,i] - \
np.mean(lc_dictionary[instrument]['X'][:,i]))/\
np.sqrt(np.var(lc_dictionary[instrument]['X'][:,i]))
if lc_dictionary[instrument]['GPType'] == 'CeleriteQPKernel':
rot_kernel = terms.TermSum(RotationTerm(log_amp=np.log(10.),\
log_timescale=np.log(10.0),\
log_period=np.log(3.0),\
log_factor=np.log(1.0)))
# Now that we know the type of GP, we extract the "instrument" corresponding to each GP
# parameter. For example, for the ExpSineSquaredKernel, it might happen the user wants
# to have a common GP_Prot parameter shared along many instruments, e.g., GP_Prot_TESS_K2_RV,
# which means the user wants a common Prot for TESS and K2 photometry, and also for the RVs. However,
# the same GP might have a different Gamma, i.e., there might be a GP_Gamma_TESS, GP_Gamma_K2 and GP_Gamma_RV.
# The idea here is to, e.g., in the case of TESS photometry, gather lc_dictionary[instrument]['GP_Prot'] =
# 'TESS_K2_RV', and lc_dictionary[instrument]['GP_Gamma'] = 'TESS':
for GPvariable in ['B','C','L','Prot']:
for pnames in priors.keys():
vec = pnames.split('_')
if (vec[0] == 'GP') and (GPvariable in vec[1]) and (instrument in vec):
lc_dictionary[instrument]['GP_'+GPvariable] = '_'.join(vec[2:])
# Jitter term:
kernel_jitter = terms.JitterTerm(np.log(100*1e-6))
# Wrap GP object to compute likelihood:
kernel = rot_kernel + kernel_jitter
lc_dictionary[instrument]['GPObject'] = celerite.GP(kernel, mean=0.0)
# Note order of GP Vector: logB, logL, logProt, logC, logJitter
lc_dictionary[instrument]['GPVector'] = np.zeros(5)
lc_dictionary[instrument]['X'] = lc_dictionary[instrument]['X'][:,0]
number_of_zeros = len(np.where(ferr_lc[instrument_indexes_lc[instrument]]==0.)[0])
if number_of_zeros == len(instrument_indexes_lc[instrument]):
lc_dictionary[instrument]['GPObject'].compute(lc_dictionary[instrument]['X'])
else:
lc_dictionary[instrument]['GPObject'].compute(lc_dictionary[instrument]['X'],yerr=ferr_lc[instrument_indexes_lc[instrument]])
if lc_dictionary[instrument]['GPType'] == 'CeleriteExpKernel':
exp_kernel = terms.RealTerm(log_a=np.log(10.), log_c=np.log(10.))
for GPvariable in ['sigma','timescale']:
for pnames in priors.keys():
vec = pnames.split('_')
if (vec[0] == 'GP') and (GPvariable in vec[1]) and (instrument in vec):
lc_dictionary[instrument]['GP_'+GPvariable] = '_'.join(vec[2:])
# Jitter term:
kernel_jitter = terms.JitterTerm(np.log(100*1e-6))
# Wrap GP object to compute likelihood:
kernel = exp_kernel + kernel_jitter
lc_dictionary[instrument]['GPObject'] = celerite.GP(kernel, mean=0.0)
# Note order of GP Vector: logsigma, log(1/timescale), logJitter
lc_dictionary[instrument]['GPVector'] = np.zeros(3)
lc_dictionary[instrument]['X'] = lc_dictionary[instrument]['X'][:,0]
number_of_zeros = len(np.where(ferr_lc[instrument_indexes_lc[instrument]]==0.)[0])
if number_of_zeros == len(instrument_indexes_lc[instrument]):
lc_dictionary[instrument]['GPObject'].compute(lc_dictionary[instrument]['X'])
else:
lc_dictionary[instrument]['GPObject'].compute(lc_dictionary[instrument]['X'],yerr=ferr_lc[instrument_indexes_lc[instrument]])
if lc_dictionary[instrument]['GPType'] == 'CeleriteMatern':
matern_kernel = terms.Matern32Term(log_sigma=np.log(10.), log_rho=np.log(10.))
for GPvariable in ['sigma','rho']:
for pnames in priors.keys():
vec = pnames.split('_')
if (vec[0] == 'GP') and (GPvariable in vec[1]) and (instrument in vec):
lc_dictionary[instrument]['GP_'+GPvariable] = '_'.join(vec[2:])
# Jitter term:
kernel_jitter = terms.JitterTerm(np.log(100*1e-6))
# Wrap GP object to compute likelihood:
kernel = matern_kernel + kernel_jitter
lc_dictionary[instrument]['GPObject'] = celerite.GP(kernel, mean=0.0)
# Note order of GP Vector: logsigma, log(rho), logJitter
lc_dictionary[instrument]['GPVector'] = np.zeros(3)
lc_dictionary[instrument]['X'] = lc_dictionary[instrument]['X'][:,0]
number_of_zeros = len(np.where(ferr_lc[instrument_indexes_lc[instrument]]==0.)[0])
if number_of_zeros == len(instrument_indexes_lc[instrument]):
lc_dictionary[instrument]['GPObject'].compute(lc_dictionary[instrument]['X'])
else:
lc_dictionary[instrument]['GPObject'].compute(lc_dictionary[instrument]['X'],yerr=ferr_lc[instrument_indexes_lc[instrument]])
if lc_dictionary[instrument]['GPType'] == 'CeleriteMaternExpKernel':
matern_kernel = terms.Matern32Term(log_sigma=np.log(10.), log_rho=np.log(10.))
exp_kernel = terms.RealTerm(log_a=np.log(10.), log_c=np.log(10.))
for GPvariable in ['sigma','rho','timescale']:
for pnames in priors.keys():
vec = pnames.split('_')
if (vec[0] == 'GP') and (GPvariable in vec[1]) and (instrument in vec):
lc_dictionary[instrument]['GP_'+GPvariable] = '_'.join(vec[2:])
# Jitter term:
kernel_jitter = terms.JitterTerm(np.log(100*1e-6))
# Wrap GP object to compute likelihood:
kernel = exp_kernel*matern_kernel + kernel_jitter
lc_dictionary[instrument]['GPObject'] = celerite.GP(kernel, mean=0.0)
# IMPORTANT: here first term of GP vector is log_a (our log GP_sigma), second log_c (our log 1/timescale),
# third is log_sigma of matern (which we set to one), fourth is log_rho and fifth is the log jitter. However,
# we dont change the log_sigma of the matern, so this stays as zero forever and ever.
lc_dictionary[instrument]['GPVector'] = np.zeros(5)
lc_dictionary[instrument]['X'] = lc_dictionary[instrument]['X'][:,0]
number_of_zeros = len(np.where(ferr_lc[instrument_indexes_lc[instrument]]==0.)[0])
if number_of_zeros == len(instrument_indexes_lc[instrument]):
lc_dictionary[instrument]['GPObject'].compute(lc_dictionary[instrument]['X'])
else:
lc_dictionary[instrument]['GPObject'].compute(lc_dictionary[instrument]['X'],yerr=ferr_lc[instrument_indexes_lc[instrument]])
if lc_dictionary[instrument]['GPType'] == 'CeleriteSHOKernel':
sho_kernel = terms.SHOTerm(log_S0=np.log(10.), log_Q=np.log(10.),log_omega0=np.log(10.))
for GPvariable in ['C0','Prot','Plife']:
for pnames in priors.keys():
vec = pnames.split('_')
if (vec[0] == 'GP') and (GPvariable in vec[1]) and (instrument in vec):
lc_dictionary[instrument]['GP_'+GPvariable] = '_'.join(vec[2:])
# Jitter term:
kernel_jitter = terms.JitterTerm(np.log(100*1e-6))
# Wrap GP object to compute likelihood:
kernel = sho_kernel + kernel_jitter
lc_dictionary[instrument]['GPObject'] = celerite.GP(kernel, mean=0.0)
# Note order of GP Vector: log_S0, log_Q, log_omega0 and log_sigma. Note, however, that we dont fit for those
# parameters directly.
lc_dictionary[instrument]['GPVector'] = np.zeros(4)
lc_dictionary[instrument]['X'] = lc_dictionary[instrument]['X'][:,0]
number_of_zeros = len(np.where(ferr_lc[instrument_indexes_lc[instrument]]==0.)[0])
if number_of_zeros == len(instrument_indexes_lc[instrument]):
lc_dictionary[instrument]['GPObject'].compute(lc_dictionary[instrument]['X'])
else:
lc_dictionary[instrument]['GPObject'].compute(lc_dictionary[instrument]['X'],yerr=ferr_lc[instrument_indexes_lc[instrument]])
if lc_dictionary[instrument]['GPType'] == 'ExpSineSquaredSEKernel':
for GPvariable in ['sigma','alpha','Gamma','Prot']:
for pnames in priors.keys():
vec = pnames.split('_')
if (vec[0] == 'GP') and (GPvariable in vec[1]) and (instrument in vec):
lc_dictionary[instrument]['GP_'+GPvariable] = '_'.join(vec[2:])
# Generate GP Base Kernel (Constant * ExpSquared * ExpSine2):
K1 = 1.*george.kernels.ExpSquaredKernel(metric = 1.0)
K2 = george.kernels.ExpSine2Kernel(gamma=1.0,log_period=1.0)
lc_dictionary[instrument]['GPKernelBase'] = K1*K2
# Generate Jitter term:
lc_dictionary[instrument]['GPKernelJitter'] = george.modeling.ConstantModel(np.log((200.*1e-6)**2.))
# Generate full kernel (i.e., GP plus jitter), generating full GP object:
if george_hodlr:
lc_dictionary[instrument]['GPObject'] = george.GP(lc_dictionary[instrument]['GPKernelBase'], mean=0.0,fit_mean=False,\
white_noise=lc_dictionary[instrument]['GPKernelJitter'],\
fit_white_noise=True,solver=george.HODLRSolver)
else:
lc_dictionary[instrument]['GPObject'] = george.GP(lc_dictionary[instrument]['GPKernelBase'], mean=0.0,fit_mean=False,\
white_noise=lc_dictionary[instrument]['GPKernelJitter'],\
fit_white_noise=True)
# Create the parameter vector --- note its dim: GP_sigma (+1) + GP_alpha (+1) + GP_Gamma (+1) + GP_Prot (+1) + Jitter term (+1): 5.
# Given how we defined the vector, first parameter of vector is jitter, second log_(GP_sigma**2), third 1./(2*alpha), fourth Gamma and fifth logProt.
lc_dictionary[instrument]['GPVector'] = np.zeros(5)
# Finally, compute GP object. Note we add the lightcurve uncertainties here, which are added in quadrature to the
# diagonal terms in the covariance matrix by george internally:
number_of_zeros = len(np.where(ferr_lc[instrument_indexes_lc[instrument]]==0.)[0])
if number_of_zeros == len(instrument_indexes_lc[instrument]):
lc_dictionary[instrument]['GPObject'].compute(lc_dictionary[instrument]['X'])
else:
lc_dictionary[instrument]['GPObject'].compute(lc_dictionary[instrument]['X'],yerr=ferr_lc[instrument_indexes_lc[instrument]])
if lc_dictionary[instrument]['GPType'] == 'SEKernel':
GPvariables = ['sigma']
for ialpha in range(lc_dictionary[instrument]['nX']):
GPvariables = GPvariables + ['alpha'+str(ialpha)]
for GPvariable in GPvariables:
for pnames in priors.keys():
vec = pnames.split('_')
if (vec[0] == 'GP') and (GPvariable in vec[1]) and (instrument in vec):
lc_dictionary[instrument]['GP_'+GPvariable] = '_'.join(vec[2:])
# Generate GPExpSquared Base Kernel:
lc_dictionary[instrument]['GPKernelBase'] = 1.*george.kernels.ExpSquaredKernel(np.ones(lc_dictionary[instrument]['nX']),\
ndim=lc_dictionary[instrument]['nX'],\
axes=range(lc_dictionary[instrument]['nX']))
# Generate Jitter term:
lc_dictionary[instrument]['GPKernelJitter'] = george.modeling.ConstantModel(np.log((200.*1e-6)**2.))
# Generate full kernel (i.e., GPExpSquared plus jitter), generating full GP object:
if george_hodlr:
lc_dictionary[instrument]['GPObject'] = george.GP(lc_dictionary[instrument]['GPKernelBase'], mean=0.0,fit_mean=False,\
white_noise=lc_dictionary[instrument]['GPKernelJitter'],\
fit_white_noise=True,solver=george.HODLRSolver)
else:
lc_dictionary[instrument]['GPObject'] = george.GP(lc_dictionary[instrument]['GPKernelBase'], mean=0.0,fit_mean=False,\
white_noise=lc_dictionary[instrument]['GPKernelJitter'],\
fit_white_noise=True)
# Create the parameter vector --- note it equals number of external parameters plus 2: amplitude of the GP
# component and jitter:
lc_dictionary[instrument]['GPVector'] = np.zeros(lc_dictionary[instrument]['X'].shape[1] + 2)
# Finally, compute GP object. Note we add the lightcurve uncertainties here, which are added in quadrature to the
# diagonal terms in the covariance matrix by george internally:
number_of_zeros = len(np.where(ferr_lc[instrument_indexes_lc[instrument]]==0.)[0])
if number_of_zeros == len(instrument_indexes_lc[instrument]):
lc_dictionary[instrument]['GPObject'].compute(lc_dictionary[instrument]['X'])
else:
lc_dictionary[instrument]['GPObject'].compute(lc_dictionary[instrument]['X'],yerr=ferr_lc[instrument_indexes_lc[instrument]])
# Extract external parameter file for RV GP:
if rveparamfile is not None:
GPDict = utils.readeparams(rveparamfile,RV=True)
print('\t Fitting RV GP.')
rv_dictionary['GPDetrend'] = True
# Detect the GP the user wants for the RVs using the priors dictionary.
# For this, consider the possibility that the user might want to combine the GP parameters
# between instruments, so iterate.
for pnames in priors.keys():
vec = pnames.split('_')
if (vec[0] == 'GP') and ('alpha0' in vec[1]) and ('rv' in vec[-1].lower()):
# Detected multi-dimensional squared-exponential GP:
rv_dictionary['GPType'] = 'SEKernel'
break
if (vec[0] == 'GP') and ('Gamma' in vec[1]) and ('rv' in vec[-1].lower()):
# Detected exp-sine-squared kernel:
rv_dictionary['GPType'] = 'ExpSineSquaredSEKernel'
break
if (vec[0] == 'GP') and ('timescale' in vec[1]) and ('rv' in vec[-1].lower()):
# If already defined, then this is a multiplication of Matern and Exp kernels:
if 'GPType' in rv_dictionary:
rv_dictionary['GPType'] = 'CeleriteMaternExpKernel'
break
# Detected celerite Exp kernel:
rv_dictionary['GPType'] = 'CeleriteExpKernel'
if (vec[0] == 'GP') and ('rho' in vec[1]) and ('rv' in vec[-1].lower()):
# If already defined, then this is a multiplication of Matern and Exp kernels:
if 'GPType' in rv_dictionary:
rv_dictionary['GPType'] = 'CeleriteMaternExpKernel'
break
# Detected celerite matern:
rv_dictionary['GPType'] = 'CeleriteMatern'
if (vec[0] == 'GP') and ('B' in vec[1]) and ('rv' in vec[-1].lower()):
# Detected celerite quasi-periodic kernel:
rv_dictionary['GPType'] = 'CeleriteQPKernel'
break
if (vec[0] == 'GP') and ('Plife' in vec[1]) and ('rv' in vec[-1].lower()):
# Detected celerite SHO:
rv_dictionary['GPType'] = 'CeleriteSHOKernel'
break
print('\t Detected ',rv_dictionary['GPType'],'for the GP')
# Save variables, standarize them if GP is SEKernel:
rv_dictionary['X'] = GPDict['variables']
rv_dictionary['nX'] = rv_dictionary['X'].shape[1]
print('\t (',rv_dictionary['nX'],'external parameters)')
if rv_dictionary['GPType'] == 'SEKernel':
for i in range(rv_dictionary['nX']):
rv_dictionary['X'][:,i] = (rv_dictionary['X'][:,i] - \
np.mean(rv_dictionary['X'][:,i]))/\
np.sqrt(np.var(rv_dictionary['X'][:,i]))
print('\t Not yet supported SEKernel for RVs (do you really need it?).')
import sys
sys.exit()
if rv_dictionary['GPType'] == 'CeleriteQPKernel':
# Now that we know the type of GP, we extract the "instrument" corresponding to each GP
# parameter. For example, for the ExpSineSquaredKernel, it might happen the user wants
# to have a common GP_Prot parameter shared along many instruments, e.g., GP_Prot_TESS_K2_RV,
# which means the user wants a common Prot for TESS and K2 photometry, and also for the RVs. However,
# the same GP might have a different Gamma, i.e., there might be a GP_Gamma_TESS, GP_Gamma_K2 and GP_Gamma_RV.
# The idea here is to, e.g., in the case of TESS photometry, gather lc_dictionary[instrument]['GP_Prot'] =
# 'TESS_K2_RV', and lc_dictionary[instrument]['GP_Gamma'] = 'TESS':
for GPvariable in ['B','C','L','Prot']:
for pnames in priors.keys():
vec = pnames.split('_')
if (vec[0] == 'GP') and (GPvariable in vec[1]) and ('rv' in vec[-1].lower()):
rv_dictionary['GP_'+GPvariable] = '_'.join(vec[2:])
#for instrument in inames_rv:
rot_kernel = terms.TermSum(RotationTerm(log_amp=np.log(10.),\
log_timescale=np.log(10.0),\
log_period=np.log(3.0),\
log_factor=np.log(1.0)))
# Jitter term; dont add it, jitters will be added directly on the log-like (see Espinoza+2018).
#kernel_jitter = terms.JitterTerm(np.log(100*1e-6))
# Wrap GP object to compute likelihood:
kernel = rot_kernel #+ kernel_jitter
rv_dictionary['GPObject'] = celerite.GP(kernel, mean=0.0)
# Note order of GP Vector: logB, logL, logProt, logC, logJitter
rv_dictionary['GPVector'] = np.zeros(4)
rv_dictionary['X'] = rv_dictionary['X'][:,0]
rv_dictionary['GPObject'].compute(rv_dictionary['X'],yerr=rverr_rv)
if rv_dictionary['GPType'] == 'CeleriteSHOKernel':
# Now that we know the type of GP, we extract the "instrument" corresponding to each GP
# parameter. For example, for the ExpSineSquaredKernel, it might happen the user wants
# to have a common GP_Prot parameter shared along many instruments, e.g., GP_Prot_TESS_K2_RV,
# which means the user wants a common Prot for TESS and K2 photometry, and also for the RVs. However,
# the same GP might have a different Gamma, i.e., there might be a GP_Gamma_TESS, GP_Gamma_K2 and GP_Gamma_RV.
# The idea here is to, e.g., in the case of TESS photometry, gather lc_dictionary[instrument]['GP_Prot'] =
# 'TESS_K2_RV', and lc_dictionary[instrument]['GP_Gamma'] = 'TESS':
for GPvariable in ['C0','Prot','Plife']:
for pnames in priors.keys():
vec = pnames.split('_')
if (vec[0] == 'GP') and (GPvariable in vec[1]) and ('rv' in vec[-1].lower()):
rv_dictionary['GP_'+GPvariable] = '_'.join(vec[2:])
#for instrument in inames_rv:
sho_kernel = terms.SHOTerm(log_S0=np.log(10.), log_Q=np.log(10.),log_omega0=np.log(10.))
# Jitter term; dont add it, jitters will be added directly on the log-like (see Espinoza+2018).
#kernel_jitter = terms.JitterTerm(np.log(100*1e-6))
# Wrap GP object to compute likelihood:
kernel = sho_kernel #+ kernel_jitter
rv_dictionary['GPObject'] = celerite.GP(kernel, mean=0.0)
# Note order of GP Vector: logS0, logQ, logomega0
rv_dictionary['GPVector'] = np.zeros(3)
rv_dictionary['X'] = rv_dictionary['X'][:,0]
rv_dictionary['GPObject'].compute(rv_dictionary['X'],yerr=rverr_rv)
if rv_dictionary['GPType'] == 'ExpSineSquaredSEKernel':
for GPvariable in ['sigma','alpha','Gamma','Prot']:
for pnames in priors.keys():
vec = pnames.split('_')
if (vec[0] == 'GP') and (GPvariable in vec[1]) and ('rv' in vec[-1].lower()):
rv_dictionary['GP_'+GPvariable] = '_'.join(vec[2:])
#for instrument in inames_rv:
# Generate GP Base Kernel (Constant * ExpSquared * ExpSine2):
K1 = 1.*george.kernels.ExpSquaredKernel(metric = 1.0)
K2 = george.kernels.ExpSine2Kernel(gamma=1.0,log_period=1.0)
# Generate kernel part:
rv_dictionary['GPKernelBase'] = K1*K2
# Generate Jitter term:
#rv_dictionary['GPKernelJitter'] = george.modeling.ConstantModel(np.log((200.*1e-6)**2.))
# Generate full kernel (i.e., GP plus jitter), generating full GP object:
rv_dictionary['GPObject'] = george.GP(rv_dictionary['GPKernelBase'], mean=0.0,fit_mean=False,\
fit_white_noise=False)#,solver=george.HODLRSolver, seed=42)
# Create the parameter vector --- note its dim: GP_sigma (+1) + GP_alpha (+1) + GP_Gamma (+1) + GP_Prot (+1): 4.
# Given how we defined the vector, first parameter of vector log_(GP_sigma**2), 2 1./(2*alpha), 3 Gamma and 4 logProt.
rv_dictionary['GPVector'] = np.zeros(4)
# Finally, compute GP object.
rv_dictionary['GPObject'].compute(rv_dictionary['X'],yerr=rverr_rv)
if rv_dictionary['GPType'] == 'CeleriteExpKernel':
exp_kernel = terms.RealTerm(log_a=np.log(10.), log_c=np.log(10.))
for GPvariable in ['sigma','timescale']:
for pnames in priors.keys():
vec = pnames.split('_')
if (vec[0] == 'GP') and (GPvariable in vec[1]) and ('rv' in vec[-1].lower()):
rv_dictionary['GP_'+GPvariable] = '_'.join(vec[2:])
# Wrap GP object to compute likelihood:
kernel = exp_kernel
rv_dictionary['GPObject'] = celerite.GP(kernel, mean=0.0)
# Note order of GP Vector: logsigma, log(1/timescale)
rv_dictionary['GPVector'] = np.zeros(2)
rv_dictionary['X'] = rv_dictionary['X'][:,0]
rv_dictionary['GPObject'].compute(rv_dictionary['X'],yerr=rverr_rv)
if rv_dictionary['GPType'] == 'CeleriteMatern':
matern_kernel = terms.Matern32Term(log_sigma=np.log(10.), log_rho=np.log(10.))
for GPvariable in ['sigma','rho']:
for pnames in priors.keys():
vec = pnames.split('_')
if (vec[0] == 'GP') and (GPvariable in vec[1]) and ('rv' in vec[-1].lower()):
rv_dictionary['GP_'+GPvariable] = '_'.join(vec[2:])
# Wrap GP object to compute likelihood:
kernel = matern_kernel# + kernel_jitter
rv_dictionary['GPObject'] = celerite.GP(kernel, mean=0.0)
# Note order of GP Vector: logsigma, log(1/timescale)
rv_dictionary['GPVector'] = np.zeros(2)
rv_dictionary['X'] = rv_dictionary['X'][:,0]
rv_dictionary['GPObject'].compute(rv_dictionary['X'],yerr=rverr_rv)
if rv_dictionary['GPType'] == 'CeleriteMaternExpKernel':
matern_kernel = terms.Matern32Term(log_sigma=np.log(10.), log_rho=np.log(10.))
exp_kernel = terms.RealTerm(log_a=np.log(10.), log_c=np.log(10.))
for GPvariable in ['sigma','rho','timescale']:
for pnames in priors.keys():
vec = pnames.split('_')
if (vec[0] == 'GP') and (GPvariable in vec[1]) and ('rv' in vec[-1].lower()):
rv_dictionary['GP_'+GPvariable] = '_'.join(vec[2:])
# Wrap GP object to compute likelihood:
kernel = exp_kernel*matern_kernel# + kernel_jitter
rv_dictionary['GPObject'] = celerite.GP(kernel, mean=0.0)
# IMPORTANT: here first term of GP vector is log_a (our log GP_sigma), second log_c (our log 1/timescale),
# third is log_sigma of matern (which we set to sigma = one, so log_sigma = 0), fourth is log_rho. However,
# we dont change the log_sigma of the matern, so this stays as zero forever and ever.
rv_dictionary['GPVector'] = np.zeros(4)
rv_dictionary['X'] = rv_dictionary['X'][:,0]
rv_dictionary['GPObject'].compute(rv_dictionary['X'],yerr=rverr_rv)
# Other inputs like, e.g., nlive points:
n_live_points = int(args.nlive)
# Number of simulations:
n_sims = int(args.nsims)
# Number of oversamples in rvs:
noversample = int(args.noversample)
# Define transit-related functions:
def reverse_ld_coeffs(ld_law, q1, q2):
if ld_law == 'quadratic':
coeff1 = 2.*np.sqrt(q1)*q2
coeff2 = np.sqrt(q1)*(1.-2.*q2)
elif ld_law=='squareroot':
coeff1 = np.sqrt(q1)*(1.-2.*q2)
coeff2 = 2.*np.sqrt(q1)*q2
elif ld_law=='logarithmic':
coeff1 = 1.-np.sqrt(q1)*q2
coeff2 = 1.-np.sqrt(q1)
elif ld_law == 'linear':
return q1,q2
return coeff1,coeff2
def init_batman(t,law, n_ss=None, exptime_ss=None):
"""
This function initializes the batman code.
"""
params = batman.TransitParams()
params.t0 = 0.
params.per = 1.
params.rp = 0.1
params.a = 15.
params.inc = 87.
params.ecc = 0.
params.w = 90.
if law == 'linear':
params.u = [0.5]
else:
params.u = [0.1,0.3]
params.limb_dark = law
if n_ss is None or exptime_ss is None:
m = batman.TransitModel(params, t)
else:
m = batman.TransitModel(params, t, supersample_factor=n_ss, exp_time=exptime_ss)
return params,m
def get_transit_model(t,t0,P,p,a,inc,q1,q2,ld_law,n_ss,exptime_ss):
params,m = init_batman(t,law=ld_law,n_ss=n_ss,exptime_ss=exptime_ss)
coeff1,coeff2 = reverse_ld_coeffs(ld_law, q1, q2)
params.t0 = t0
params.per = P
params.rp = p
params.a = a
params.inc = inc
if ld_law == 'linear':
params.u = [coeff1]
else:
params.u = [coeff1,coeff2]
return m.light_curve(params)
def init_radvel(nplanets=1):
return radvel.model.Parameters(nplanets,basis='per tc e w k')
def transform_prior(val,pinfo):
if pinfo['type'] == 'uniform':
return utils.transform_uniform(val,pinfo['value'][0],pinfo['value'][1])
if pinfo['type'] == 'normal':
return utils.transform_normal(val,pinfo['value'][0],pinfo['value'][1])
if pinfo['type'] == 'truncatednormal':
return utils.transform_truncated_normal(val,pinfo['value'][0],pinfo['value'][1],a=pinfo['value'][2],b=pinfo['value'][3])
if pinfo['type'] == 'jeffreys':
return utils.transform_loguniform(val,pinfo['value'][0],pinfo['value'][1])
if pinfo['type'] == 'beta':
return utils.transform_beta(val,pinfo['value'][0],pinfo['value'][1])
if pinfo['type'] == 'exponential':
return utils.transform_exponential(val)
if lcfilename is not None:
# Initialize batman for each different lightcurve:
for instrument in lc_dictionary.keys():
if lc_dictionary[instrument]['resampling']:
lc_dictionary[instrument]['params'],lc_dictionary[instrument]['m'] = init_batman(t_lc[instrument_indexes_lc[instrument]], \
law=lc_dictionary[instrument]['ldlaw'], \
n_ss=lc_dictionary[instrument]['nresampling'],\
exptime_ss=lc_dictionary[instrument]['exptimeresampling'])
else:
lc_dictionary[instrument]['params'],lc_dictionary[instrument]['m'] = init_batman(t_lc[instrument_indexes_lc[instrument]], \
law=lc_dictionary[instrument]['ldlaw'])
if rvfilename is not None:
# Initialize radvel:
radvel_params = init_radvel(nplanets=n_rv)
# Define gaussian log-likelihood:
log2pi = np.log(2.*np.pi)
def gaussian_log_likelihood(residuals,errors,jitter):
taus = 1./(errors**2 + jitter**2)
return -0.5*(len(residuals)*log2pi+np.sum(np.log(1./taus)+taus*(residuals**2)))
# Now define priors and log-likelihood. Note here the idea is that any fixed parameters don't
# receive prior numbering.
transformed_priors = np.zeros(n_params)
def prior(cube, ndim=None, nparams=None):
pcounter = 0
for pname in priors.keys():
if priors[pname]['type'] != 'fixed':
if use_dynesty:
transformed_priors[pcounter] = transform_prior(cube[pcounter],priors[pname])
else:
cube[pcounter] = transform_prior(cube[pcounter],priors[pname])
pcounter += 1
if use_dynesty:
return transformed_priors
if lcfilename is not None:
lcones = np.ones(len(t_lc))
# Gravitational constant (for stellar density):
G = 6.67408e-11 # mks
# Maximum eccentricity limit:
ecclim = np.double(args.ecclim)
# Save corresponding name of LD input instrument to the respective instrument (this is to link LD coefficients between
# different "instruments"):
if lcfilename is not None:
ld_iname = {}
for pname in priors.keys():
if pname[0:2] == 'q1':
vec = pname.split('_')
if len(vec)>2:
for i in range(len(inames_lc)):
if inames_lc[i] in vec:
ld_iname[inames_lc[i]] = '_'.join(vec[1:])
else:
ld_iname[vec[1]] = vec[1]
#priors['q1_'+instrument]
def loglike(cube, ndim=None, nparams=None):
# Evaluate the log-likelihood. For this, first extract all inputs:
pcounter = 0
for pname in priors.keys():
if priors[pname]['type'] != 'fixed':
priors[pname]['cvalue'] = cube[pcounter]
pcounter += 1
# Photometric terms first:
if lcfilename is not None:
# Before everything continues, make sure periods are chronologically ordered (this is to avoid multiple modes due to
# periods "jumping" between planet numbering):
for n in range(n_transit):
i = numbering_transit[n]
if n == 0:
cP = priors['P_p'+str(i)]['cvalue']
else:
if cP < priors['P_p'+str(i)]['cvalue']:
cP = priors['P_p'+str(i)]['cvalue']
else:
return -1e101
if cP < 0.:
return -1e101
# Generate lightcurve models for each instrument:
lcmodel = np.copy(lcones)
for instrument in inames_lc:
# For each transit model iterate through the
# number of planets, multiplying their transit models:
if lc_dictionary[instrument]['TransitFit']:
for n in range(n_transit):
i = numbering_transit[n]
if lc_dictionary[instrument]['ldlaw'] != 'linear':
coeff1,coeff2 = reverse_ld_coeffs(lc_dictionary[instrument]['ldlaw'],priors['q1_'+ld_iname[instrument]]['cvalue'],\
priors['q2_'+ld_iname[instrument]]['cvalue'])
lc_dictionary[instrument]['params'].u = [coeff1,coeff2]
else:
lc_dictionary[instrument]['params'].u = [priors['q1_'+ld_iname[instrument]]['cvalue']]
if efficient_bp[n]:
if not fitrho:
a,r1,r2,t0,P = priors['a_p'+str(i)]['cvalue'],priors['r1_p'+str(i)]['cvalue'],\
priors['r2_p'+str(i)]['cvalue'], priors['t0_p'+str(i)]['cvalue'], \
priors['P_p'+str(i)]['cvalue']
else:
rho,r1,r2,t0,P = priors['rho']['cvalue'],priors['r1_p'+str(i)]['cvalue'],\
priors['r2_p'+str(i)]['cvalue'], priors['t0_p'+str(i)]['cvalue'], \
priors['P_p'+str(i)]['cvalue']
a = ((rho*G*((P*24.*3600.)**2))/(3.*np.pi))**(1./3.)
if r1 > Ar: