1
|
|
2
|
|
3
|
|
4
|
|
5
|
|
6
|
|
7
|
|
8
|
|
9
|
|
10
|
|
11
|
|
12
|
|
13
|
|
14
|
|
15
|
|
16
|
|
17
|
|
18
|
|
19
|
|
20
|
|
21
|
import ctools as ct
|
22
|
import gammalib as gl
|
23
|
from math import sqrt,log10,floor
|
24
|
import sys
|
25
|
|
26
|
class base(object):
|
27
|
def __init__(self):
|
28
|
self.classname = self.__class__.__name__
|
29
|
self.errorcolor = "\033[31m"
|
30
|
self.infocolor = "\033[34m"
|
31
|
self.warningcolor = "\033[33m"
|
32
|
self.successcolor = "\033[32m"
|
33
|
self.endcolor = "\033[0m"
|
34
|
self.prependfunction = True
|
35
|
self.basemembers = ["classname","errorcolor","infocolor","warningcolor","successcolor","endcolor","prependfunction"]
|
36
|
|
37
|
def error(self,message, functionname = ""):
|
38
|
printstring = ""
|
39
|
if functionname == "":
|
40
|
printstring = "\n"+self.errorcolor+"*** Error ["+self.classname+"]: "+message+" ***\n"+self.endcolor
|
41
|
else:
|
42
|
printstring = "\n"+self.errorcolor+"*** Error ["+self.classname+"::"+functionname+"]: "+message+" ***\n"+self.endcolor
|
43
|
sys.exit(printstring)
|
44
|
|
45
|
def info(self,message,newline=True):
|
46
|
printstring = self.infocolor+"["+self.classname+"]: "+message+self.endcolor
|
47
|
if newline:
|
48
|
print printstring
|
49
|
else:
|
50
|
print self.infocolor+message+self.endcolor,
|
51
|
sys.stdout.flush()
|
52
|
|
53
|
def warning(self,message,functionname = ""):
|
54
|
printstring = ""
|
55
|
if functionname == "":
|
56
|
printstring = self.warningcolor+"["+self.classname+"] Warning: "+message+self.endcolor
|
57
|
else:
|
58
|
printstring = self.warningcolor+"["+self.classname+"::"+functionname+"] Warning: "+message+self.endcolor
|
59
|
print printstring
|
60
|
|
61
|
def success(self,message):
|
62
|
printstring = self.successcolor+"["+self.classname+"]: "+message+self.endcolor
|
63
|
print printstring
|
64
|
|
65
|
def progress(self,message="."):
|
66
|
string = self.infocolor+message+self.endcolor
|
67
|
print string
|
68
|
|
69
|
|
70
|
class ResultsSorage(dict,base):
|
71
|
"""class to store the results in the good format"""
|
72
|
def __init__(self,*arg,**kw):
|
73
|
super(ResultsSorage, self).__init__(*arg, **kw)
|
74
|
base.__init__(self)
|
75
|
self["dnde"] = {}
|
76
|
self["ulim_dnde"] = []
|
77
|
self["time"] = {}
|
78
|
self["ener"] = {}
|
79
|
self["TS"] = []
|
80
|
self["Iflux"] = {}
|
81
|
|
82
|
self["time"]["tmin_value"] = []
|
83
|
self["time"]["tmax_value"] = []
|
84
|
self["time"]["unit"] = "sec"
|
85
|
|
86
|
self["ener"]["value"] = []
|
87
|
self["ener"]["ed_value"] = []
|
88
|
self["ener"]["eu_value"] = []
|
89
|
self["ener"]["unit"] = "MeV"
|
90
|
|
91
|
self["dnde"]["value"] = []
|
92
|
self["dnde"]["ed_value"] = []
|
93
|
self["dnde"]["eu_value"] = []
|
94
|
self["dnde"]["unit"] = "ph/cm2/s/MeV"
|
95
|
|
96
|
self["Iflux"]["value"] = []
|
97
|
self["Iflux"]["ed_value"] = []
|
98
|
self["Iflux"]["eu_value"] = []
|
99
|
self["Iflux"]["unit"] = "ph/cm2/s"
|
100
|
|
101
|
def Print(self):
|
102
|
self.success("Results are :")
|
103
|
for k in self.iterkeys():
|
104
|
self.info("Key name : "+k)
|
105
|
try :
|
106
|
for ki in self[k].iterkeys():
|
107
|
print "\t",ki,"\t",self[k][ki]
|
108
|
except:
|
109
|
print self[k]
|
110
|
print
|
111
|
|
112
|
class Analyser(base):
|
113
|
def __init__(self):
|
114
|
super(Analyser,self).__init__()
|
115
|
self.info("Creating "+self.classname+" object")
|
116
|
self.m_obs = None
|
117
|
self.like = None
|
118
|
|
119
|
self.m_ebinalg = "LOG"
|
120
|
self.m_eunit = "TeV"
|
121
|
self.m_enumbins = 10
|
122
|
self.m_usepnt = True
|
123
|
self.m_coordsys = "CEL"
|
124
|
self.m_proj = "TAN"
|
125
|
self.m_binned = False
|
126
|
self.m_edisp = False
|
127
|
self.m_stat = "POISSON"
|
128
|
|
129
|
self.m_source_set = False
|
130
|
self.m_files_set = False
|
131
|
self.m_time_set = False
|
132
|
self.m_energy_set = False
|
133
|
self.m_irfs_set = False
|
134
|
self.m_roi_set = False
|
135
|
self.m_xml_set = False
|
136
|
|
137
|
def set_xml(self,xml):
|
138
|
self.m_xml = xml
|
139
|
ind = xml.find(".xml")
|
140
|
if not ind==-1:
|
141
|
self.m_xml_out = xml[:xml.find(".xml")]+"_results"+xml[xml.find(".xml"):]
|
142
|
self.m_xml_set = True
|
143
|
|
144
|
def set_roi(self,roi):
|
145
|
self.m_roi = float(roi)
|
146
|
self.m_nxpix = int(self.m_roi*100)
|
147
|
self.m_nypix = int(self.m_roi*100)
|
148
|
self.m_binsz = 0.05
|
149
|
self.m_roi_set = True
|
150
|
|
151
|
def set_irfs(self,caldb,irf):
|
152
|
self.m_caldb = caldb
|
153
|
self.m_irf = irf
|
154
|
self.m_irfs_set = True
|
155
|
|
156
|
def set_Fitsfiles(self,evtfile,tag="CTA_analysis"):
|
157
|
self.m_evtfile = "event_selected_"+tag+".fits"
|
158
|
self.m_raw_evt = evtfile
|
159
|
self.m_cntfile = "countmap_"+tag+".fits"
|
160
|
self.m_files_set = True
|
161
|
|
162
|
def set_source(self,name,ra,dec):
|
163
|
self.m_ra = ra
|
164
|
self.m_dec = dec
|
165
|
self.m_name = name
|
166
|
self.m_source_set = True
|
167
|
|
168
|
def validate(self):
|
169
|
if not self.m_time_set :
|
170
|
self.error("Time range of the observations not set")
|
171
|
|
172
|
if not self.m_files_set :
|
173
|
self.error("FITS files of the observations not set")
|
174
|
|
175
|
if not self.m_source_set :
|
176
|
self.error("Source not set")
|
177
|
|
178
|
if not self.m_energy_set :
|
179
|
self.error("Energy range of the observations not set")
|
180
|
|
181
|
if not self.m_irfs_set :
|
182
|
self.error("IRFs to use not set")
|
183
|
|
184
|
if not self.m_roi_set :
|
185
|
self.error("ROI to use not set")
|
186
|
|
187
|
if not self.m_xml_set :
|
188
|
self.error("XML files for the sky model not set")
|
189
|
|
190
|
def copy(self,analyse,tag="CTA_analysis_copy"):
|
191
|
self.set_xml(analyse.m_xml)
|
192
|
self.set_irfs(analyse.m_caldb,analyse.m_irf)
|
193
|
self.set_roi(analyse.m_roi)
|
194
|
self.set_time_boundary(analyse.m_tmin,analyse.m_tmax)
|
195
|
self.set_energy_boundary(analyse.m_emin,analyse.m_emax)
|
196
|
self.set_source(analyse.m_name,analyse.m_ra,analyse.m_dec)
|
197
|
self.set_Fitsfiles(analyse.m_evtfile,tag)
|
198
|
|
199
|
|
200
|
def set_obs(self,obs):
|
201
|
self.m_obs = obs
|
202
|
|
203
|
def set_time_boundary(self,tmin,tmax):
|
204
|
"""Set the start and stop time of the observation"""
|
205
|
self.m_tmin = float(tmin)
|
206
|
self.m_tmax = float(tmax)
|
207
|
self.m_time_set = True
|
208
|
|
209
|
def set_energy_boundary(self,emin,emax):
|
210
|
"""Set the energy bounds of the observation"""
|
211
|
self.m_emin = emin
|
212
|
self.m_emax = emax
|
213
|
ebounds = gl.GEbounds(gl.GEnergy(emin, self.m_eunit), \
|
214
|
gl.GEnergy(emax, self.m_eunit))
|
215
|
|
216
|
if self.m_obs:
|
217
|
for obs in self.m_obs:
|
218
|
obs.events().ebounds(ebounds)
|
219
|
self.m_energy_set = True
|
220
|
|
221
|
def ctselect(self):
|
222
|
|
223
|
self.info("Running ctselect to cut on events")
|
224
|
self.validate()
|
225
|
if self.m_obs:
|
226
|
filter = ct.ctselect(self.m_obs)
|
227
|
else:
|
228
|
filter = ct.ctselect()
|
229
|
|
230
|
filter["infile"] = self.m_raw_evt
|
231
|
filter["outfile"] = self.m_evtfile
|
232
|
filter["usepnt"].boolean(self.m_usepnt)
|
233
|
filter["ra"] = self.m_ra
|
234
|
filter["dec"] = self.m_dec
|
235
|
filter["rad"] = self.m_roi
|
236
|
filter["tmin"] = self.m_tmin
|
237
|
filter["tmax"] = self.m_tmax
|
238
|
filter["emin"].real(self.m_emin)
|
239
|
filter["emax"].real(self.m_emax)
|
240
|
filter["expr"] = ""
|
241
|
filter.logFileOpen()
|
242
|
|
243
|
filter.run()
|
244
|
if not(self.m_obs):
|
245
|
filter.save()
|
246
|
|
247
|
if self.m_obs:
|
248
|
|
249
|
|
250
|
|
251
|
self.m_obs = filter.obs().copy()
|
252
|
|
253
|
def ctbin(self,log=False,debug=False):
|
254
|
|
255
|
self.validate()
|
256
|
self.info("Running ctbin to create count map")
|
257
|
if self.m_obs:
|
258
|
bin = ct.ctbin(self.m_obs)
|
259
|
else:
|
260
|
bin = ct.ctbin()
|
261
|
bin["evfile"] = self.m_evtfile
|
262
|
bin["outfile"] = self.m_cntfile
|
263
|
bin["ebinalg"].string(self.m_ebinalg)
|
264
|
bin["emin"].real(self.m_emin)
|
265
|
bin["emax"].real(self.m_emax)
|
266
|
Nbdecade = log10(self.m_emax)-log10(self.m_emin)
|
267
|
bin["enumbins"].integer(int(self.m_enumbins*Nbdecade))
|
268
|
bin["usepnt"].boolean(self.m_usepnt)
|
269
|
bin["nxpix"].integer(self.m_nxpix)
|
270
|
bin["nypix"].integer(self.m_nypix)
|
271
|
bin["binsz"].real(self.m_binsz)
|
272
|
bin["coordsys"].string(self.m_coordsys)
|
273
|
bin["proj"].string(self.m_proj)
|
274
|
|
275
|
|
276
|
if log:
|
277
|
bin.logFileOpen()
|
278
|
|
279
|
|
280
|
if debug:
|
281
|
bin["debug"].boolean(True)
|
282
|
|
283
|
|
284
|
|
285
|
bin.execute()
|
286
|
if self.m_obs:
|
287
|
|
288
|
|
289
|
|
290
|
self.m_obs = bin.obs().copy()
|
291
|
|
292
|
def create_fit(self,log=False,debug=False):
|
293
|
self.validate()
|
294
|
|
295
|
self.info("Fitting Data using ctlike")
|
296
|
if self.m_obs:
|
297
|
self.like = ct.ctlike(self.m_obs)
|
298
|
else:
|
299
|
self.like = ct.ctlike()
|
300
|
if self.m_binned:
|
301
|
self.like["infile"] = self.m_cntfile
|
302
|
else:
|
303
|
self.like["infile"] = self.m_evtfile
|
304
|
|
305
|
self.like["stat"].string(self.m_stat)
|
306
|
self.like["caldb"].string(self.m_caldb)
|
307
|
self.like["irf"].string(self.m_irf)
|
308
|
self.like["srcmdl"] = self.m_xml
|
309
|
self.like["edisp"].boolean(self.m_edisp)
|
310
|
self.like["outmdl"] = self.m_xml_out
|
311
|
|
312
|
|
313
|
if log:
|
314
|
self.like.logFileOpen()
|
315
|
|
316
|
if debug:
|
317
|
self.like["debug"].boolean(True)
|
318
|
|
319
|
def fit(self,log=False,debug=False):
|
320
|
self.validate()
|
321
|
if not(self.like):
|
322
|
self.warning("ctlike object not created, creating now")
|
323
|
self.create_fit(log,debug)
|
324
|
|
325
|
|
326
|
self.like.run()
|
327
|
|
328
|
self.like.save()
|
329
|
self.success("Fit performed")
|
330
|
|
331
|
def PrintResults(self,srcname = ""):
|
332
|
self.info("Results of the Fit")
|
333
|
for m in self.like.obs().models():
|
334
|
if srcname == m.name() or srcname=="":
|
335
|
print "Model : "+m.name()
|
336
|
print m
|
337
|
|
338
|
def GetSrcResuls(self,Res,srcname,E0=None,factor = 1e6,tmin=None,tmax=None):
|
339
|
"""function to get and store the results of a fit"""
|
340
|
results = self.GetSrcParams(srcname)
|
341
|
|
342
|
if 1:
|
343
|
|
344
|
|
345
|
|
346
|
|
347
|
|
348
|
|
349
|
Res["time"]["tmin_value"].append(self.m_tmin)
|
350
|
Res["time"]["tmax_value"].append(self.m_tmax)
|
351
|
Res["time"]["unit"] = "sec"
|
352
|
|
353
|
|
354
|
if (E0 is not None):
|
355
|
Res["ener"]["value"].append(E0*factor)
|
356
|
Res["ener"]["ed_value"].append((E0-self.m_emin)*factor)
|
357
|
Res["ener"]["eu_value"].append((self.m_emax-E0)*factor)
|
358
|
Res["ener"]["unit"] = "MeV"
|
359
|
else:
|
360
|
Res["ener"]["value"].append(sqrt(self.m_emin*self.m_emax))
|
361
|
Res["ener"]["ed_value"].append(-self.m_emin+sqrt(self.m_emin*self.m_emax))
|
362
|
Res["ener"]["eu_value"].append(self.m_emax-sqrt(self.m_emin*self.m_emax))
|
363
|
Res["ener"]["unit"] = self.m_eunit
|
364
|
|
365
|
Res["Iflux"]["value"].append(results["flux"])
|
366
|
Res["Iflux"]["eu_value"].append(results["eflux"])
|
367
|
Res["Iflux"]["ed_value"].append(results["eflux"])
|
368
|
|
369
|
|
370
|
m = self.GetModel(srcname)
|
371
|
Res["dnde"]["value"].append(m.spectral()[0].value())
|
372
|
Res["dnde"]["eu_value"].append(m.spectral()[0].error())
|
373
|
Res["dnde"]["ed_value"].append(m.spectral()[0].error())
|
374
|
|
375
|
Res["TS"].append(100)
|
376
|
|
377
|
UL = UpperLimitComputer(self,srcname)
|
378
|
UL.run()
|
379
|
Res["ulim_dnde"].append(0)
|
380
|
|
381
|
return Res
|
382
|
|
383
|
def GetSrcParams(self,srcname):
|
384
|
results = {}
|
385
|
found = False
|
386
|
m = self.GetModel(srcname)
|
387
|
results["flux"] = m.spectral().flux(gl.GEnergy(self.m_emin,self.m_eunit),gl.GEnergy(self.m_emax,self.m_eunit))
|
388
|
results["eflux"] = m.spectral().eflux(gl.GEnergy(self.m_emin,self.m_eunit),gl.GEnergy(self.m_emax,self.m_eunit))
|
389
|
for par in m.spectral():
|
390
|
if par.is_free():
|
391
|
results[par.name()] = par.value()
|
392
|
results["e"+par.name()] = par.error()
|
393
|
|
394
|
return results
|
395
|
|
396
|
def GetModel(self,srcname):
|
397
|
found = False
|
398
|
for m in self.like.obs().models():
|
399
|
if m.name() == srcname:
|
400
|
return m
|
401
|
self.error("source "+srcname+" not in the source list")
|
402
|
|
403
|
|
404
|
def MakeEbin(analyse,nbins,srcname,binned = False):
|
405
|
|
406
|
Res = ResultsSorage()
|
407
|
|
408
|
|
409
|
analyse.GetModel(srcname)
|
410
|
|
411
|
|
412
|
Energy_list = {"MeV":1,"GeV":1e3,"TeV":1e6}
|
413
|
factor = Energy_list[analyse.m_eunit]
|
414
|
|
415
|
for i in xrange(nbins):
|
416
|
obs_copy = analyse.like.obs().copy()
|
417
|
emin = pow(10,log10(analyse.m_emin) + (log10(analyse.m_emax)-log10(analyse.m_emin))/(nbins)*i)
|
418
|
emax = pow(10,log10(analyse.m_emin) + (log10(analyse.m_emax)-log10(analyse.m_emin))/(nbins)*(i+1))
|
419
|
E0 = pow(10, (log10(emin) + log10(emax)) / 2)
|
420
|
print "Making energy bin between %2.1e"%(emin)+analyse.m_eunit+" and %2.1e"%(emax)+analyse.m_eunit
|
421
|
|
422
|
|
423
|
m = obs_copy.models()[srcname]
|
424
|
for par in m.spectral():
|
425
|
if par.name() == "PivotEnergy":
|
426
|
par.value(E0*factor)
|
427
|
|
428
|
ana_bin = Analyser()
|
429
|
|
430
|
|
431
|
ana_bin.set_obs(obs_copy)
|
432
|
|
433
|
|
434
|
ana_bin.copy(analyse)
|
435
|
|
436
|
|
437
|
ana_bin.set_energy_boundary(emin,emax)
|
438
|
ana_bin.ctselect()
|
439
|
|
440
|
|
441
|
|
442
|
|
443
|
|
444
|
ana_bin.create_fit(log=False,debug=False)
|
445
|
|
446
|
|
447
|
ana_bin.like.run()
|
448
|
|
449
|
|
450
|
Res = ana_bin.GetSrcResuls(Res,srcname,E0,factor)
|
451
|
|
452
|
del ana_bin
|
453
|
return Res
|
454
|
|
455
|
def MakeLC(analyse,nbins,srcname,binned = False):
|
456
|
|
457
|
Res = ResultsSorage()
|
458
|
|
459
|
|
460
|
analyse.GetModel(srcname)
|
461
|
|
462
|
for i in xrange(nbins):
|
463
|
obs_copy = analyse.like.obs().copy()
|
464
|
tmin = analyse.m_tmin + (analyse.m_tmax-analyse.m_tmin)/(nbins)*i
|
465
|
tmax = analyse.m_tmin + (analyse.m_tmax-analyse.m_tmin)/(nbins)*(i+1)
|
466
|
print "Making time bin between %2.1e"%(tmin)+" and %2.1e"%(tmax)
|
467
|
|
468
|
ana_bin = Analyser()
|
469
|
|
470
|
|
471
|
ana_bin.set_obs(obs_copy)
|
472
|
|
473
|
|
474
|
ana_bin.copy(analyse)
|
475
|
|
476
|
|
477
|
ana_bin.set_time_boundary(tmin,tmax)
|
478
|
ana_bin.ctselect()
|
479
|
|
480
|
|
481
|
|
482
|
|
483
|
|
484
|
ana_bin.create_fit(log=False,debug=False)
|
485
|
|
486
|
|
487
|
ana_bin.like.run()
|
488
|
|
489
|
|
490
|
Res = ana_bin.GetSrcResuls(Res,srcname,tmin=tmin,tmax=tmax)
|
491
|
|
492
|
del ana_bin
|
493
|
return Res
|
494
|
|
495
|
class UpperLimitComputer(base):
|
496
|
def __init__(self,analyser,srcname,parname = "Prefactor"):
|
497
|
super(UpperLimitComputer,self).__init__()
|
498
|
self.info("Creating "+self.classname+" object")
|
499
|
|
500
|
obs = analyser.like.obs().copy()
|
501
|
self.like = ct.ctlike(obs)
|
502
|
self.succes = False
|
503
|
|
504
|
|
505
|
self.model = self.like.obs().models()[srcname]
|
506
|
self.spec = self.model.spectral()
|
507
|
self.parname = parname
|
508
|
self.bestloglike = analyser.like.opt().value()
|
509
|
self.dloglike = 3.84/2.0
|
510
|
|
511
|
def _bisec(self,a,b,tol = 1e-12):
|
512
|
"""iterative function to find the roots"""
|
513
|
if (self._func(a)==0.):
|
514
|
return a
|
515
|
elif (self._func(b)==0):
|
516
|
return b
|
517
|
elif (self._func(b)*self._func(a)>0.):
|
518
|
self.warning("Finding root for upper limit calculation failed")
|
519
|
return 0
|
520
|
mid = (a+b)/2
|
521
|
err = abs(a-b)/2
|
522
|
fm = self._func(mid)
|
523
|
if (err<tol or self._func(mid)==0.):
|
524
|
self.succes = True
|
525
|
return mid
|
526
|
elif (fm*self._func(a)<0):
|
527
|
b = mid
|
528
|
else :
|
529
|
a = mid
|
530
|
return self._bisec(a,b)
|
531
|
|
532
|
def _func(self,value):
|
533
|
"""function to be minimized"""
|
534
|
|
535
|
val = value*self.spec[self.parname].scale()
|
536
|
|
537
|
|
538
|
if val <= self.spec[self.parname].min():
|
539
|
return 0.0-self.self.bestloglike-self.dloglike
|
540
|
|
541
|
|
542
|
self.spec[self.parname].value(val)
|
543
|
|
544
|
|
545
|
self.like.run()
|
546
|
logl = self.like.opt().value()
|
547
|
|
548
|
|
549
|
return (logl-self.bestloglike-self.dloglike)
|
550
|
|
551
|
def run(self):
|
552
|
|
553
|
|
554
|
value = self.spec[self.parname].value()
|
555
|
error = self.spec[self.parname].error()
|
556
|
scale = self.spec[self.parname].scale()
|
557
|
|
558
|
|
559
|
for model in self.like.obs().models():
|
560
|
for par in model:
|
561
|
par.fix()
|
562
|
|
563
|
|
564
|
|
565
|
self.startpar = value / scale + 2* error/scale
|
566
|
|
567
|
|
568
|
a= value / scale + (self.dloglike/4.)*error/scale
|
569
|
b=value / scale + (self.dloglike*2)* error/scale
|
570
|
ul = self._bisec(a,b)
|
571
|
|
572
|
self.ulimit = ul*self.spec[self.parname].scale()
|
573
|
|
574
|
if self.succes:
|
575
|
self.success("Upper limit of parameter \""+self.parname +"\" determined to "+str(self.ulimit))
|