pull_OnOff.py
1 |
#! /Applications/Anaconda/bin/python
|
---|---|
2 |
# ====================================================================
|
3 |
# This script tests the ON-OFF analysis functionality of the gammalib
|
4 |
# ====================================================================
|
5 |
|
6 |
from ctools import * |
7 |
from gammalib import * |
8 |
from math import * |
9 |
|
10 |
import os |
11 |
import glob |
12 |
import sys |
13 |
|
14 |
import numpy as np |
15 |
|
16 |
import matplotlib.pyplot as plt |
17 |
import matplotlib.cm as cm |
18 |
import matplotlib.patches as ptch |
19 |
|
20 |
# ======================== #
|
21 |
# Setup single observation #
|
22 |
# ======================== #
|
23 |
def single_obs(pntdir, tstart=0.0, duration=1800.0, deadc=0.95, \ |
24 |
emin=0.1, emax=100.0, rad=5.0, \ |
25 |
irf="South_50h", caldb="prod2", id="000000", instrument="CTA"): |
26 |
"""
|
27 |
Returns a single CTA observation.
|
28 |
|
29 |
Parameters:
|
30 |
pntdir - Pointing direction [GSkyDir]
|
31 |
Keywords:
|
32 |
tstart - Start time [seconds] (default: 0.0)
|
33 |
duration - Duration of observation [seconds] (default: 1800.0)
|
34 |
deadc - Deadtime correction factor (default: 0.95)
|
35 |
emin - Minimum event energy [TeV] (default: 0.1)
|
36 |
emax - Maximum event energy [TeV] (default: 100.0)
|
37 |
rad - ROI radius used for analysis [deg] (default: 5.0)
|
38 |
irf - Instrument response function (default: cta_dummy_irf)
|
39 |
caldb - Calibration database path (default: "dummy")
|
40 |
id - Run identifier (default: "000000")
|
41 |
instrument - Intrument (default: "CTA")
|
42 |
"""
|
43 |
# Allocate CTA observation
|
44 |
obs_cta = GCTAObservation(instrument) |
45 |
|
46 |
# Set calibration database
|
47 |
db = GCaldb() |
48 |
if (os.path.isdir(caldb)):
|
49 |
db.rootdir(caldb) |
50 |
else:
|
51 |
db.open("cta", caldb)
|
52 |
|
53 |
# Set pointing direction
|
54 |
pnt = GCTAPointing() |
55 |
pnt.dir(pntdir) |
56 |
obs_cta.pointing(pnt) |
57 |
|
58 |
# Set ROI
|
59 |
roi = GCTARoi() |
60 |
instdir = GCTAInstDir() |
61 |
instdir.dir(pntdir) |
62 |
roi.centre(instdir) |
63 |
roi.radius(rad) |
64 |
|
65 |
# Set GTI
|
66 |
gti = GGti() |
67 |
gti.append(GTime(tstart), GTime(tstart+duration)) |
68 |
|
69 |
# Set energy boundaries
|
70 |
ebounds = GEbounds(GEnergy(emin, "TeV"), \
|
71 |
GEnergy(emax, "TeV"))
|
72 |
|
73 |
# Allocate event list
|
74 |
events = GCTAEventList() |
75 |
events.roi(roi) |
76 |
events.gti(gti) |
77 |
events.ebounds(ebounds) |
78 |
obs_cta.events(events) |
79 |
|
80 |
# Set instrument response
|
81 |
obs_cta.response(irf, db) |
82 |
|
83 |
# Set ontime, livetime, and deadtime correction factor
|
84 |
obs_cta.ontime(duration) |
85 |
obs_cta.livetime(duration*deadc) |
86 |
obs_cta.deadc(deadc) |
87 |
obs_cta.id(id)
|
88 |
|
89 |
# Return CTA observation
|
90 |
return obs_cta
|
91 |
|
92 |
|
93 |
# ======================================== #
|
94 |
# Add CTA background model to observations #
|
95 |
# ======================================== #
|
96 |
def add_background_model(models,name="Background",instru="CTA",id="",bgd_file="",bgd_sigma=0.0): |
97 |
|
98 |
"""
|
99 |
Add CTA background model to a model container.
|
100 |
|
101 |
Parameters:
|
102 |
models - a model container
|
103 |
bgd_file - file function for background spectral dependence
|
104 |
bgd_sigma - sigma parameter for the gaussian offset angle dependence of the background rate
|
105 |
"""
|
106 |
|
107 |
# Gaussian radial dependence model
|
108 |
if bgd_sigma > 0.0: |
109 |
|
110 |
# Spatial part
|
111 |
bgd_radial = GCTAModelRadialGauss(bgd_sigma) |
112 |
|
113 |
# Spectral part
|
114 |
if len(bgd_file) > 0: |
115 |
bgd_spectrum = GModelSpectralFunc(bgd_file,1.0)
|
116 |
else:
|
117 |
pivot_nrj=GEnergy(1.0e6, "MeV") |
118 |
bgd_spectrum = GModelSpectralPlaw(1.0e-6, -2.0, pivot_nrj) |
119 |
bgd_spectrum["Prefactor"].value(61.8e-6) |
120 |
bgd_spectrum["Prefactor"].scale(1.0e-6) |
121 |
bgd_spectrum["PivotEnergy"].value(1.0) |
122 |
bgd_spectrum["PivotEnergy"].scale(1.0e6) |
123 |
bgd_spectrum["Index"].value(-1.85) |
124 |
bgd_spectrum["Index"].scale(1.0) |
125 |
|
126 |
# Full model
|
127 |
bgd_model = GCTAModelRadialAcceptance(bgd_radial, bgd_spectrum) |
128 |
|
129 |
# IRF-defined model
|
130 |
else:
|
131 |
|
132 |
# Spectral model
|
133 |
pivot_nrj=GEnergy(1.0e6, "MeV") |
134 |
bgd_spectrum = GModelSpectralPlaw(1.0e-6, -2.0, pivot_nrj) |
135 |
bgd_spectrum["Prefactor"].value(1.0) |
136 |
bgd_spectrum["Prefactor"].scale(1.0) |
137 |
bgd_spectrum["PivotEnergy"].value(1.0) |
138 |
bgd_spectrum["PivotEnergy"].scale(1.0e6) |
139 |
bgd_spectrum["Index"].value(0.0) |
140 |
bgd_spectrum["Index"].scale(1.0) |
141 |
|
142 |
# Full model
|
143 |
bgd_model = GCTAModelIrfBackground(bgd_spectrum) |
144 |
|
145 |
# Set other background properties
|
146 |
if (len(name) > 0): |
147 |
bgd_model.name(name) |
148 |
else:
|
149 |
bgd_model.name("Background")
|
150 |
if (len(instru) > 0): |
151 |
bgd_model.instruments(instru) |
152 |
else:
|
153 |
bgd_model.instruments("CTA")
|
154 |
if (len(id) > 0): |
155 |
bgd_model.ids(id)
|
156 |
|
157 |
# By default, set model to be fitted
|
158 |
if (len(bgd_file) > 0): |
159 |
bgd_model['Normalization'].free()
|
160 |
else:
|
161 |
bgd_model['Prefactor'].free()
|
162 |
bgd_model['Index'].free()
|
163 |
|
164 |
# Add background model to container
|
165 |
models.append(bgd_model) |
166 |
|
167 |
# Return model container
|
168 |
return models
|
169 |
|
170 |
|
171 |
# =========================== #
|
172 |
# Set any general type source #
|
173 |
# =========================== #
|
174 |
def add_source_model(models, name, ra, dec, coord='CEL', \ |
175 |
skymap='', specfile='', \ |
176 |
flux=5.7e-16, index=-2.5, pivot=3.0e5, \ |
177 |
type="point", sigma=1.0, radius=1.0, width=0.1, \ |
178 |
nnode=0, emin=1.0, emax=10.0, \ |
179 |
pivot_scale=1e6, flux_scale=1.0e-16): |
180 |
"""
|
181 |
Adds a single point-source with power-law spectrum to a model container.
|
182 |
The default is crab-like.
|
183 |
|
184 |
Parameters:
|
185 |
models - a model container
|
186 |
name - Unique source name
|
187 |
ra - RA of source location [deg]
|
188 |
dec - Declination of source location [deg]
|
189 |
Keywords:
|
190 |
flux - Source flux density in 1.0e-16 ph/cm2/s/MeV
|
191 |
index - Source spectral index
|
192 |
pivot - Pivot energy in TeV
|
193 |
type - Source spatial model
|
194 |
sigma/radius/width - Spatial model parameters
|
195 |
nnode - Number of nodes (if > 0, spectral model is a node function)
|
196 |
"""
|
197 |
|
198 |
# Set source spectral model
|
199 |
if (nnode > 0) and (emin != emax): |
200 |
spectrum = GModelSpectralNodes() |
201 |
dloge=log10(emax/emin)/nnode |
202 |
logemin=math.log10(emin) |
203 |
spectrum.reserve(nnode) |
204 |
for i in range(nnode): |
205 |
enode = GEnergy() |
206 |
enode.TeV(10.0**(logemin+(i+0.5)*dloge)) |
207 |
inode=flux*(enode.MeV()/pivot)**(index) |
208 |
spectrum.append(enode, inode) |
209 |
spectrum[i*2].scale(pivot_scale)
|
210 |
spectrum[i*2].fix()
|
211 |
spectrum[i*2+1].scale(flux_scale) |
212 |
spectrum[i*2+1].free() |
213 |
# Set source spectral model
|
214 |
elif (len(specfile) > 0): |
215 |
spectrum = GModelSpectralFunc(specfile) |
216 |
spectrum["Normalization"].value(1.0) |
217 |
spectrum["Normalization"].free()
|
218 |
else:
|
219 |
pivot_nrj=GEnergy() |
220 |
pivot_nrj.TeV(pivot*pivot_scale/1e6)
|
221 |
spectrum = GModelSpectralPlaw(flux, index,pivot_nrj) |
222 |
spectrum["Prefactor"].scale(flux_scale)
|
223 |
spectrum["Prefactor"].value(flux)
|
224 |
spectrum["Prefactor"].free()
|
225 |
spectrum["PivotEnergy"].scale(pivot_scale)
|
226 |
spectrum["PivotEnergy"].value(pivot)
|
227 |
spectrum["Index"].value(index)
|
228 |
spectrum["Index"].scale(1.0) |
229 |
spectrum["Index"].free()
|
230 |
|
231 |
# Set source spatial model
|
232 |
location = GSkyDir() |
233 |
if (coord == 'CEL'): |
234 |
location.radec_deg(ra, dec) |
235 |
else:
|
236 |
location.lb_deg(ra, dec) |
237 |
if (len(skymap) > 0): |
238 |
spatial = GModelSpatialDiffuseMap(skymap,1.0)
|
239 |
source = GModelSky(spatial, spectrum) |
240 |
spatial[0].free()
|
241 |
elif type == "point": |
242 |
spatial = GModelSpatialPointSource(location) |
243 |
spatial[0].free()
|
244 |
spatial[1].free()
|
245 |
source = GModelSky(spatial, spectrum) |
246 |
elif type == "gauss": |
247 |
radial = GModelSpatialRadialGauss(location, sigma) |
248 |
radial[0].free()
|
249 |
radial[1].free()
|
250 |
radial[2].free()
|
251 |
source = GModelSky(radial, spectrum) |
252 |
elif type == "disk": |
253 |
radial = GModelSpatialRadialDisk(location, radius) |
254 |
radial[0].free()
|
255 |
radial[1].free()
|
256 |
radial[2].free()
|
257 |
source = GModelSky(radial, spectrum) |
258 |
elif type == "shell": |
259 |
radial = GModelSpatialRadialShell(location, radius, width) |
260 |
radial[0].free()
|
261 |
radial[1].free()
|
262 |
radial[2].free()
|
263 |
radial[3].free()
|
264 |
source = GModelSky(radial, spectrum) |
265 |
else:
|
266 |
print "ERROR: Unknown source type '"+type+"'." |
267 |
return None |
268 |
|
269 |
# Set source name
|
270 |
source.name(name) |
271 |
|
272 |
# Append source model to model container
|
273 |
models.append(source) |
274 |
|
275 |
# Return source
|
276 |
return models
|
277 |
|
278 |
# ======================================== #
|
279 |
# Set the fit parameters for a given model #
|
280 |
# ======================================== #
|
281 |
def set_fit_param(model, parname=[], parfit=[], parval=[]): |
282 |
"""
|
283 |
Set the fit parameters for a given model
|
284 |
|
285 |
Arguments
|
286 |
- model: a model container
|
287 |
- parname: array of parameters names
|
288 |
- parfit: array of fit/fix flags (True=fit,False=fix)
|
289 |
- parval: array of initial/fix parameters values
|
290 |
|
291 |
Notes
|
292 |
- Arrays should have same numbers of elements
|
293 |
- A way to not set a value but just the fit/fix flag is to pass/leave parval empty
|
294 |
- If among several parameters some values must be set and others not, separate these and call the function two times
|
295 |
"""
|
296 |
|
297 |
# Count/check number of parameters to be set
|
298 |
if (len(parname) != len(parfit)): |
299 |
print "Incorrect input. Fit parameters not set or modified." |
300 |
return 0 |
301 |
else:
|
302 |
num_toset=len(parname)
|
303 |
|
304 |
# Loop over parameters
|
305 |
for i in range(model.size()): |
306 |
name=model[i].name() |
307 |
if (name in parname): |
308 |
# Get index of matching name
|
309 |
i_par=parname.index(name) |
310 |
# Set the parameter to be fix/free
|
311 |
if (parfit[i_par]):
|
312 |
model[name].free() |
313 |
else:
|
314 |
model[name].fix() |
315 |
# Optionally set the value
|
316 |
if (len(parval) > 0): |
317 |
model[name].value(parval[i_par]) |
318 |
|
319 |
# Exit point
|
320 |
return model
|
321 |
|
322 |
|
323 |
# ===================== #
|
324 |
# Simulate observations #
|
325 |
# ===================== #
|
326 |
def sim(obs, log=False, debug=False, chatter=2, edisp=False, seed=0, nbins=0, |
327 |
binsz=0.05, npix=200, proj="TAN", coord="GAL", outfile=""): |
328 |
"""
|
329 |
Simulate events for all observations in the container.
|
330 |
|
331 |
Parameters:
|
332 |
obs - Observation container
|
333 |
Keywords:
|
334 |
log - Create log file(s)
|
335 |
debug - Create console dump?
|
336 |
edisp - Apply energy dispersion?
|
337 |
seed - Seed value for simulations (default: 0)
|
338 |
nbins - Number of energy bins (default: 0=unbinned)
|
339 |
binsz - Pixel size for binned simulation (deg/pixel)
|
340 |
npix - Number of pixels in X and Y for binned simulation
|
341 |
"""
|
342 |
|
343 |
# Allocate ctobssim application and set parameters
|
344 |
sim = ctobssim(obs) |
345 |
sim["seed"] = seed
|
346 |
sim["edisp"] = edisp
|
347 |
sim["outevents"] = outfile
|
348 |
|
349 |
# Optionally open the log file
|
350 |
if log:
|
351 |
sim.logFileOpen() |
352 |
|
353 |
# Optionally switch-on debugging model
|
354 |
if debug:
|
355 |
sim["debug"] = True |
356 |
|
357 |
# Set chatter level
|
358 |
sim["chatter"] = chatter
|
359 |
|
360 |
# Run ctobssim application. This will loop over all observations in the
|
361 |
# container and simulation the events for each observation. Note that
|
362 |
# events are not added together, they still apply to each observation
|
363 |
# separately.
|
364 |
sim.run() |
365 |
|
366 |
# Binned option?
|
367 |
if nbins > 0: |
368 |
|
369 |
# Determine common energy boundaries for observations
|
370 |
emin = None
|
371 |
emax = None
|
372 |
for run in sim.obs(): |
373 |
run_emin = run.events().ebounds().emin().TeV() |
374 |
run_emax = run.events().ebounds().emax().TeV() |
375 |
if emin == None: |
376 |
emin = run_emin |
377 |
elif run_emin > emin:
|
378 |
emin = run_emin |
379 |
if emax == None: |
380 |
emax = run_emax |
381 |
elif run_emax > emax:
|
382 |
emax = run_emax |
383 |
|
384 |
# Allocate ctbin application and set parameters
|
385 |
bin = ctbin(sim.obs()) |
386 |
bin["ebinalg"] = "LOG" |
387 |
bin["emin"] = emin |
388 |
bin["emax"] = emax |
389 |
bin["enumbins"] = nbins |
390 |
bin["usepnt"] = True # Use pointing for map centre |
391 |
bin["nxpix"] = npix |
392 |
bin["nypix"] = npix |
393 |
bin["binsz"] = binsz |
394 |
bin["coordsys"] = coord |
395 |
bin["proj"] = proj |
396 |
|
397 |
# Optionally open the log file
|
398 |
if log:
|
399 |
bin.logFileOpen()
|
400 |
|
401 |
# Optionally switch-on debugging model
|
402 |
if debug:
|
403 |
bin["debug"] = True |
404 |
|
405 |
# Set chatter level
|
406 |
bin["chatter"] = chatter |
407 |
|
408 |
# Run ctbin application. This will loop over all observations in
|
409 |
# the container and bin the events in counts maps
|
410 |
bin.run()
|
411 |
|
412 |
# Make a deep copy of the observation that will be returned
|
413 |
# (the ctbin object will go out of scope one the function is
|
414 |
# left)
|
415 |
obs = bin.obs().copy()
|
416 |
|
417 |
else:
|
418 |
|
419 |
# Make a deep copy of the observation that will be returned
|
420 |
# (the ctobssim object will go out of scope one the function is
|
421 |
# left)
|
422 |
obs = sim.obs().copy() |
423 |
|
424 |
# Optionally save file
|
425 |
if (len(outfile) > 0): |
426 |
sim.save() |
427 |
|
428 |
# Delete the simulation
|
429 |
del sim
|
430 |
|
431 |
# Return observation container
|
432 |
return obs
|
433 |
|
434 |
|
435 |
# =================== #
|
436 |
# Make a simple model #
|
437 |
# =================== #
|
438 |
def make_simple_model(models,name,ra,dec,flux,idx): |
439 |
|
440 |
# Add background model (one for each pointing)
|
441 |
models = add_background_model(models,bgd_sigma=0.0,instru='CTA',id="0001",name="Background left") |
442 |
set_fit_param(models["Background left"],parname=['Sigma','Normalization','Prefactor','Index'],parfit=[False,True,True,False]) |
443 |
models = add_background_model(models,bgd_sigma=0.0,instru='CTA',id="0002",name="Background right") |
444 |
set_fit_param(models["Background right"],parname=['Sigma','Normalization','Prefactor','Index'],parfit=[False,True,True,False]) |
445 |
|
446 |
# Add source model
|
447 |
models = add_source_model(models, name, ra, dec, flux=flux, index=idx, pivot=1.0e6, type="point", pivot_scale=1e6, flux_scale=1.0e-16) |
448 |
set_fit_param(models["Test source"],parname=['Prefactor','Index','RA','DEC'],parfit=[True,True,False,False]) |
449 |
|
450 |
# Exit
|
451 |
return models
|
452 |
|
453 |
|
454 |
# =================== #
|
455 |
# Make a simple model #
|
456 |
# =================== #
|
457 |
def reset_simple_model(models,ra,dec,flux,idx): |
458 |
|
459 |
# Add background model (one for each pointing)
|
460 |
set_fit_param(models["Background left"],parname=['Sigma','Normalization','Prefactor','Index'],parfit=[False,True,True,False],parval=[0.0,1.0,1.0,0.0]) |
461 |
set_fit_param(models["Background right"],parname=['Sigma','Normalization','Prefactor','Index'],parfit=[False,True,True,False],parval=[0.0,1.0,1.0,0.0]) |
462 |
set_fit_param(models["Test source"],parname=['Prefactor','Index','RA','DEC'],parfit=[True,True,False,False],parval=[flux,idx,ra,dec]) |
463 |
|
464 |
# Exit
|
465 |
return models
|
466 |
|
467 |
|
468 |
# ================ #
|
469 |
# Make a histogram #
|
470 |
# ================ #
|
471 |
def make_histogram(arr,n): |
472 |
|
473 |
# Make histogram
|
474 |
dbin=(max(arr)-min(arr))/n |
475 |
hist=np.histogram(np.array(arr), bins=n) |
476 |
yhist=hist[0]
|
477 |
xhist=np.linspace(min(arr)+0.5*dbin, max(arr)-0.5*dbin, num=n, endpoint=True) |
478 |
|
479 |
# Exit
|
480 |
return xhist,yhist
|
481 |
|
482 |
|
483 |
# =========== #
|
484 |
# Make a plot #
|
485 |
# =========== #
|
486 |
def plot_histogram(x,n): |
487 |
|
488 |
# Plot
|
489 |
plt.figure() |
490 |
plt.hist(x,n) |
491 |
plt.xscale('linear')
|
492 |
plt.yscale('linear')
|
493 |
plt.xlabel('Parameter',labelpad=10) |
494 |
plt.ylabel('Number',labelpad=10) |
495 |
|
496 |
|
497 |
# ======================== #
|
498 |
# Set plotting environment #
|
499 |
# ======================== #
|
500 |
def prepare_plots(): |
501 |
|
502 |
# Set
|
503 |
params = {'legend.fontsize': 14, |
504 |
'legend.labelspacing': 0.2, |
505 |
'figure.figsize': (8,8), |
506 |
'figure.facecolor': 'white', |
507 |
'lines.linewidth': 2, |
508 |
'axes.titlesize': 'large', |
509 |
'axes.labelsize': 'large'} |
510 |
# Assign
|
511 |
plt.rcParams.update(params) |
512 |
return
|
513 |
|
514 |
|
515 |
# =========== #
|
516 |
# Make a plot #
|
517 |
# =========== #
|
518 |
def plot_results(x,y): |
519 |
|
520 |
# Plot
|
521 |
plt.figure() |
522 |
plt.plot(x,y) |
523 |
plt.xscale('linear')
|
524 |
plt.yscale('linear')
|
525 |
plt.xlabel('Parameter',labelpad=10) |
526 |
plt.ylabel('Number',labelpad=10) |
527 |
xmin=0.95*min(x) |
528 |
xmax=1.05*max(x) |
529 |
ymin=0.95*min(y) |
530 |
ymax=1.05*max(y) |
531 |
plt.xlim([xmin,xmax]) |
532 |
plt.ylim([ymin,ymax]) |
533 |
|
534 |
|
535 |
# ===================== #
|
536 |
# Print out fit results #
|
537 |
# ===================== #
|
538 |
def print_fit_results(obslist): |
539 |
|
540 |
# Print
|
541 |
print "Background left prefactor: %.3e +/- %.3e " % (obslist.models()["Background left"]['Prefactor'].value(),obslist.models()["Background left"]['Prefactor'].error()) |
542 |
print "Background left index: %.3e +/- %.3e " % (obslist.models()["Background left"]['Index'].value(),obslist.models()["Background left"]['Index'].error()) |
543 |
print "Background right prefactor: %.3e +/- %.3e " % (obslist.models()["Background right"]['Prefactor'].value(),obslist.models()["Background right"]['Prefactor'].error()) |
544 |
print "Background right index: %.3e +/- %.3e " % (obslist.models()["Background right"]['Index'].value(),obslist.models()["Background right"]['Index'].error()) |
545 |
print "Source prefactor: %.3e +/- %.3e " % (obslist.models()["Test source"]['Prefactor'].value(),obslist.models()["Test source"]['Prefactor'].error()) |
546 |
print "Source index: %.3e +/- %.3e " % (obslist.models()["Test source"]['Index'].value(),obslist.models()["Test source"]['Index'].error()) |
547 |
print "Fit ended with value %.3e and status %d after %d iterations." % (opt.value(),opt.status(),opt.iter()) |
548 |
|
549 |
# Exit
|
550 |
return
|
551 |
|
552 |
|
553 |
#=====================#
|
554 |
# Routine entry point #
|
555 |
#=====================#
|
556 |
if __name__ == '__main__': |
557 |
|
558 |
"""
|
559 |
Aims:
|
560 |
This script tests the ON-OFF analysis functionality of the gammalib
|
561 |
|
562 |
Arguments:
|
563 |
- None
|
564 |
|
565 |
Notes:
|
566 |
- Hard-coded source, observations, and regions (more flexible script later...)
|
567 |
- Approximate method to set OFF regions (works only on equator)
|
568 |
- Have to set up path to data base (db and irf)
|
569 |
- Source flux set as flux density at 1TeV
|
570 |
"""
|
571 |
|
572 |
# Job parameters
|
573 |
# CTA
|
574 |
db="prod2"
|
575 |
irf="South_50h"
|
576 |
bgd=""
|
577 |
rsp_sigma=4.0
|
578 |
bgd_sigma=4.0
|
579 |
duration=1800.0
|
580 |
rad=5.0
|
581 |
# Energy
|
582 |
emin=0.1
|
583 |
emax=100.0
|
584 |
nbins=9
|
585 |
nnodes=5
|
586 |
# Source
|
587 |
name="Test source"
|
588 |
ra=0.0
|
589 |
dec=0.0
|
590 |
flux=1.0e-16
|
591 |
idx=-2.3
|
592 |
dflux=1.5 # Offset factor by which true value is rescaled before fit |
593 |
didx=0.3 # Offset factor by which true value is rescaled before fit |
594 |
# ON-OFF
|
595 |
onshift=1.0
|
596 |
onsize=0.3
|
597 |
noff=3
|
598 |
# Others
|
599 |
npull=100
|
600 |
nhist=10
|
601 |
chatter=4
|
602 |
seed=5
|
603 |
|
604 |
# Create observation container
|
605 |
obslist = GObservations() |
606 |
|
607 |
# Add two offset observations
|
608 |
leftdir = GSkyDir() |
609 |
leftdir.radec_deg(ra+onshift, dec) |
610 |
obs=single_obs(leftdir, tstart=0.0, duration=duration, deadc=0.95, \ |
611 |
emin=emin, emax=emax, rad=rad, \ |
612 |
irf=irf, caldb=db, id="000000", instrument="CTA") |
613 |
obs.name("Left")
|
614 |
obs.id("0001")
|
615 |
obslist.append(obs) |
616 |
rightdir = GSkyDir() |
617 |
rightdir.radec_deg(ra-onshift, dec) |
618 |
obs=single_obs(rightdir, tstart=0.0, duration=duration, deadc=0.95, \ |
619 |
emin=emin, emax=emax, rad=rad, \ |
620 |
irf=irf, caldb=db, id="000000", instrument="CTA") |
621 |
obs.name("Right")
|
622 |
obs.id("0002")
|
623 |
obslist.append(obs) |
624 |
|
625 |
# Create a model container
|
626 |
models = GModels() |
627 |
# Make a simple model for two observations
|
628 |
models = make_simple_model(models,name,ra,dec,flux,idx) |
629 |
# Add model to the container
|
630 |
obslist.models(models) |
631 |
|
632 |
# Define ON region
|
633 |
ondir = GSkyDir() |
634 |
ondir.radec_deg(0.0,0.0) |
635 |
on = GSkyRegions() |
636 |
onreg=GSkyRegionCircle(ondir,onsize) |
637 |
on.append(onreg) |
638 |
|
639 |
# Define OFF regions
|
640 |
offleft = GSkyRegions() |
641 |
offright = GSkyRegions() |
642 |
for i in range(noff): |
643 |
phi=(i+1)*360./(noff+1.0) |
644 |
# Left pointing
|
645 |
offdir = GSkyDir(leftdir) |
646 |
offdir.rotate_deg(phi-90., onshift)
|
647 |
offreg=GSkyRegionCircle(offdir, onsize) |
648 |
offleft.append(offreg) |
649 |
# Right pointing
|
650 |
offdir = GSkyDir(rightdir) |
651 |
offdir.rotate_deg(phi+90.0, onshift)
|
652 |
offreg=GSkyRegionCircle(offdir, onsize) |
653 |
offright.append(offreg) |
654 |
|
655 |
# Create ON-OFF analysis object
|
656 |
onofflist = GCTAOnOffObservations() |
657 |
|
658 |
# Define energy binning
|
659 |
etrue = GEbounds(nbins, GEnergy(emin, "TeV"), GEnergy(emax, "TeV")) |
660 |
ereco = GEbounds(nbins, GEnergy(emin, "TeV"), GEnergy(emax, "TeV")) |
661 |
|
662 |
# Append ON-OFF observations
|
663 |
obs=GCTAOnOffObservation(ereco, on, offleft) |
664 |
obs.name("Left")
|
665 |
obs.id("0001")
|
666 |
onofflist.append(obs) |
667 |
obs=GCTAOnOffObservation(ereco, on, offright) |
668 |
obs.name("Right")
|
669 |
obs.id("0002")
|
670 |
onofflist.append(obs) |
671 |
|
672 |
# Create arrays to store pull results
|
673 |
onoff_flux=[] |
674 |
onoff_idx=[] |
675 |
unbinned_flux=[] |
676 |
unbinned_idx=[] |
677 |
|
678 |
# Repeat the run...
|
679 |
# (make each time new copies of obslist and onofflist otherwise something accumulates and goes wrong)
|
680 |
for i in range(npull): |
681 |
|
682 |
# Print
|
683 |
print '\nRun ',i |
684 |
|
685 |
# Make event list
|
686 |
theobslist=GObservations(obslist) |
687 |
theobslist = sim(theobslist, log=False, debug=False, chatter=chatter, edisp=False, seed=seed+i, nbins=0) |
688 |
|
689 |
# Load data and save (fills ON and OFF spectra, ON and OFF observing time, and alpha parameter)
|
690 |
theonofflist=GCTAOnOffObservations(onofflist) |
691 |
theonofflist[0].fill(theobslist[0]) |
692 |
theonofflist[1].fill(theobslist[1]) |
693 |
|
694 |
# Compute responses and save
|
695 |
theonofflist[0].compute_response(theobslist[0],models,etrue) |
696 |
theonofflist[1].compute_response(theobslist[1],models,etrue) |
697 |
|
698 |
# Reset model container and append it to observation list
|
699 |
models=reset_simple_model(models,ra,dec,dflux*flux,idx-didx) |
700 |
theonofflist.models(models) |
701 |
|
702 |
# Set optimizer and logger
|
703 |
log=GLog('fit_onoff_%d.log' % (i),True) |
704 |
log.chatter(chatter) |
705 |
opt=GOptimizerLM(log) |
706 |
|
707 |
# Optimize for ON-OFF analysis
|
708 |
print '\nON-OFF fitting...' |
709 |
theonofflist.optimize(opt) |
710 |
theonofflist.errors(opt) |
711 |
print_fit_results(theonofflist) |
712 |
|
713 |
# Append results
|
714 |
onoff_flux.append(theonofflist.models()["Test source"]['Prefactor'].value()) |
715 |
onoff_idx.append(theonofflist.models()["Test source"]['Index'].value()) |
716 |
|
717 |
# Free memory
|
718 |
del opt
|
719 |
del log
|
720 |
del theonofflist
|
721 |
|
722 |
# Reset model container and append it to observation list
|
723 |
models=reset_simple_model(models,ra,dec,dflux*flux,idx-didx) |
724 |
theobslist.models(models) |
725 |
|
726 |
# Set optimizer and logger
|
727 |
log=GLog('fit_unbinned_%d.log' % (i),True) |
728 |
log.chatter(chatter) |
729 |
opt=GOptimizerLM(log) |
730 |
|
731 |
# Optimize for Fermi-style analysis
|
732 |
print '\nUnbinned fitting...' |
733 |
theobslist.optimize(opt) |
734 |
theobslist.errors(opt) |
735 |
print_fit_results(theobslist) |
736 |
|
737 |
# Append results
|
738 |
unbinned_flux.append(theobslist.models()["Test source"]['Prefactor'].value()) |
739 |
unbinned_idx.append(theobslist.models()["Test source"]['Index'].value()) |
740 |
|
741 |
# Free memory
|
742 |
del opt
|
743 |
del log
|
744 |
del theobslist
|
745 |
|
746 |
# Print out statistics
|
747 |
print '\nTrue values: prefactor=%.3e and index=%.3f \n' % (flux,idx) |
748 |
print 'ON-OFF analysis' |
749 |
print 'Prefactor: mean= %.3e +/- %.3e' % (np.mean(np.array(onoff_flux)),np.std(np.array(onoff_flux))) |
750 |
print 'Index= %.3f +/- %.3f \n' % (np.mean(np.array(onoff_idx)),np.std(np.array(onoff_idx))) |
751 |
print 'Unbinned analysis' |
752 |
print 'Prefactor: mean= %.3e +/- %.3e' % (np.mean(np.array(unbinned_flux)),np.std(np.array(unbinned_flux))) |
753 |
print 'Index= %.3f +/- %.3f \n' % (np.mean(np.array(unbinned_idx)),np.std(np.array(unbinned_idx))) |
754 |
|
755 |
# Plot results
|
756 |
prepare_plots() |
757 |
plot_histogram(unbinned_flux,nhist) |
758 |
plot_histogram(unbinned_idx,nhist) |
759 |
plot_histogram(onoff_flux,nhist) |
760 |
plot_histogram(onoff_idx,nhist) |
761 |
plt.show() |
762 |
|
763 |
|