pull_OnOff_latest.py

Brun Francois, 10/06/2016 05:14 PM

Download (24.8 KB)

 
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()
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 for the On-Off fitting #
456
# ========================================== #
457
def make_simple_model_onoff_fit(models,name,ra,dec,flux,idx):
458
    
459
    # Add background model (one for each pointing)
460
    models = add_background_model(models,bgd_sigma=0.0,instru='CTAOnOff',id="0001",name="Background left")
461
    set_fit_param(models["Background left"],parname=['Sigma','Normalization','Prefactor','Index'],parfit=[False,True,True,False])
462
    models = add_background_model(models,bgd_sigma=0.0,instru='CTAOnOff',id="0002",name="Background right")
463
    set_fit_param(models["Background right"],parname=['Sigma','Normalization','Prefactor','Index'],parfit=[False,True,True,False])
464
    
465
    # Add source model
466
    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)
467
    set_fit_param(models["Test source"],parname=['Prefactor','Index','RA','DEC'],parfit=[True,True,False,False])
468
    
469
    # Exit
470
    return models
471

    
472

    
473
# =================== #
474
# Make a simple model #
475
# =================== #
476
def reset_simple_model(models,ra,dec,flux,idx):
477
    
478
    # Add background model (one for each pointing)
479
    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])
480
    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])
481
    set_fit_param(models["Test source"],parname=['Prefactor','Index','RA','DEC'],parfit=[True,True,False,False],parval=[flux,idx,ra,dec])
482
    
483
    # Exit
484
    return models
485

    
486

    
487
# ================ #
488
# Make a histogram #
489
# ================ #
490
def make_histogram(arr,n):
491
        
492
    # Make histogram
493
    dbin=(max(arr)-min(arr))/n
494
    hist=np.histogram(np.array(arr), bins=n)
495
    yhist=hist[0]
496
    xhist=np.linspace(min(arr)+0.5*dbin, max(arr)-0.5*dbin, num=n, endpoint=True)
497

    
498
    # Exit
499
    return xhist,yhist
500

    
501

    
502
# =========== #
503
# Make a plot #
504
# =========== #
505
def plot_histogram(x,n):
506

    
507
    # Plot
508
    plt.figure()
509
    plt.hist(x,n)
510
    plt.xscale('linear')
511
    plt.yscale('linear')
512
    plt.xlabel('Parameter',labelpad=10)
513
    plt.ylabel('Number',labelpad=10)
514

    
515

    
516
# ======================== #
517
# Set plotting environment #
518
# ======================== #
519
def prepare_plots():
520
    
521
    # Set
522
    params = {'legend.fontsize': 14,
523
              'legend.labelspacing': 0.2,
524
              'figure.figsize': (8,8),
525
              'figure.facecolor': 'white',
526
              'lines.linewidth': 2,
527
              'axes.titlesize': 'large',
528
              'axes.labelsize': 'large'}
529
    # Assign
530
    plt.rcParams.update(params)
531
    return    
532

    
533

    
534
# =========== #
535
# Make a plot #
536
# =========== #
537
def plot_results(x,y):
538

    
539
    # Plot
540
    plt.figure()
541
    plt.plot(x,y)
542
    plt.xscale('linear')
543
    plt.yscale('linear')
544
    plt.xlabel('Parameter',labelpad=10)
545
    plt.ylabel('Number',labelpad=10)
546
    xmin=0.95*min(x)
547
    xmax=1.05*max(x)
548
    ymin=0.95*min(y)
549
    ymax=1.05*max(y)
550
    plt.xlim([xmin,xmax])
551
    plt.ylim([ymin,ymax])
552

    
553

    
554
# ===================== #
555
# Print out fit results #
556
# ===================== #
557
def print_fit_results(obslist):
558
    
559
    # Print
560
    print "Background left prefactor: %.3e +/- %.3e " % (obslist.models()["Background left"]['Prefactor'].value(),obslist.models()["Background left"]['Prefactor'].error())
561
    print "Background left index: %.3e +/- %.3e " % (obslist.models()["Background left"]['Index'].value(),obslist.models()["Background left"]['Index'].error())
562
    print "Background right prefactor: %.3e +/- %.3e " % (obslist.models()["Background right"]['Prefactor'].value(),obslist.models()["Background right"]['Prefactor'].error())
563
    print "Background right index: %.3e +/- %.3e " % (obslist.models()["Background right"]['Index'].value(),obslist.models()["Background right"]['Index'].error())
564
    print "Source prefactor: %.3e +/- %.3e " % (obslist.models()["Test source"]['Prefactor'].value(),obslist.models()["Test source"]['Prefactor'].error())
565
    print "Source index: %.3e +/- %.3e " % (obslist.models()["Test source"]['Index'].value(),obslist.models()["Test source"]['Index'].error())
566
    print "Fit ended with value %.3e and status %d after %d iterations." % (opt.value(),opt.status(),opt.iter())
567
    
568
    # Exit
569
    return
570
    
571

    
572
#=====================#
573
# Routine entry point #
574
#=====================#
575
if __name__ == '__main__':
576

    
577
    """
578
    Aims:
579
    This script tests the ON-OFF analysis functionality of the gammalib
580
    
581
    Arguments:
582
    - None
583
    
584
    Notes:
585
    - Hard-coded source, observations, and regions (more flexible script later...)
586
    - Approximate method to set OFF regions (works only on equator)
587
    - Have to set up path to data base (db and irf)
588
    - Source flux set as flux density at 1TeV
589
    """
590
    
591
    # Job parameters
592
    # CTA
593
    db="prod2"
594
    irf="South_50h"
595
    bgd=""
596
    rsp_sigma=4.0
597
    bgd_sigma=4.0
598
    duration=1800.0
599
    rad=5.0
600
    # Energy
601
    emin=0.1
602
    emax=100.0
603
    nbins=9
604
    nnodes=5
605
    # Source
606
    name="Test source"
607
    ra=0.0
608
    dec=0.0
609
    flux=1.0e-16
610
    idx=-2.3
611
    dflux=1.5  # Offset factor by which true value is rescaled before fit
612
    didx=0.3   # Offset factor by which true value is rescaled before fit
613
    # ON-OFF
614
    onshift=1.0
615
    onsize=0.3
616
    noff=3
617
    # Others
618
    npull=100
619
    nhist=10
620
    chatter=4
621
    seed=5
622
    
623
    # Create observation container
624
    obslist = GObservations()
625
    
626
    # Add two offset observations
627
    leftdir = GSkyDir()
628
    leftdir.radec_deg(ra+onshift, dec)
629
    obs=single_obs(leftdir, tstart=0.0, duration=duration, deadc=0.95, \
630
                   emin=emin, emax=emax, rad=rad, \
631
                   irf=irf, caldb=db, id="000000", instrument="CTA")
632
    obs.name("Left")
633
    obs.id("0001")
634
    obslist.append(obs)
635
    rightdir = GSkyDir()
636
    rightdir.radec_deg(ra-onshift, dec)
637
    obs=single_obs(rightdir, tstart=0.0, duration=duration, deadc=0.95, \
638
                   emin=emin, emax=emax, rad=rad, \
639
                   irf=irf, caldb=db, id="000000", instrument="CTA")
640
    obs.name("Right")
641
    obs.id("0002")
642
    obslist.append(obs)    
643
    
644
    # Create a model container
645
    models = GModels()   
646
    models_onoff_fit = GModels()   
647

    
648
    # Make a simple model for two observations
649
    models = make_simple_model(models,name,ra,dec,flux,idx)
650
    models_onoff_fit = make_simple_model_onoff_fit(models_onoff_fit,name,ra,dec,flux,idx)
651

    
652
    # Add model to the container
653
    obslist.models(models)
654
    
655
    # Define ON region
656
    ondir = GSkyDir()
657
    ondir.radec_deg(0.0,0.0)
658
    on = GSkyRegions()
659
    onreg=GSkyRegionCircle(ondir,onsize)
660
    on.append(onreg)
661
    
662
    # Define OFF regions
663
    offleft = GSkyRegions()
664
    offright = GSkyRegions()
665
    for i in range(noff):
666
        phi=(i+1)*360./(noff+1.0)
667
        # Left pointing  
668
        offdir = GSkyDir(leftdir)       
669
        offdir.rotate_deg(phi-90., onshift)
670
        offreg=GSkyRegionCircle(offdir, onsize)
671
        offleft.append(offreg)
672
        # Right pointing
673
        offdir = GSkyDir(rightdir)       
674
        offdir.rotate_deg(phi+90.0, onshift)       
675
        offreg=GSkyRegionCircle(offdir, onsize)
676
        offright.append(offreg)
677
            
678
    
679
    # Create arrays to store pull results
680
    onoff_flux=[]
681
    onoff_idx=[]
682
    unbinned_flux=[]
683
    unbinned_idx=[]
684
    
685
    # Repeat the run...
686
    # (make each time new copies of obslist and onofflist otherwise something accumulates and goes wrong)
687
    for i in range(npull):
688
    
689
        # Print
690
        print '\nRun ',i
691
        
692
        # Make event list
693
        theobslist = sim(obslist, log=False, debug=False, chatter=chatter, edisp=False, seed=seed+i, nbins=0)
694
       
695
        # Load data and save (fills ON and OFF spectra, ON and OFF observing time, and alpha parameter)
696
        # Create ON-OFF analysis object
697
        theonofflist = GObservations()
698
        
699
        # Define energy binning
700
        etrue = GEbounds(nbins, GEnergy(emin, "TeV"), GEnergy(emax, "TeV"))
701
        ereco = GEbounds(nbins, GEnergy(emin, "TeV"), GEnergy(emax, "TeV"))
702
        
703
        # Append ON-OFF observations
704
        obs=GCTAOnOffObservation(theobslist[0], etrue, ereco, on, offleft)
705
        obs.name("Left")
706
        obs.id("0001")
707
        theonofflist.append(obs)
708
        obs=GCTAOnOffObservation(theobslist[1], etrue, ereco, on, offright)
709
        obs.name("Right")
710
        obs.id("0002")
711
        theonofflist.append(obs)
712

    
713
        # Reset model container and append it to observation list
714
        models_onoff_fit=reset_simple_model(models_onoff_fit,ra,dec,dflux*flux,idx-didx)
715
        theonofflist.models(models_onoff_fit)
716
    
717
        # Set optimizer and logger
718
        log=GLog('fit_onoff_%d.log' % (i),True)
719
        log.chatter(chatter)
720
        opt=GOptimizerLM(log)
721
    
722
        # Optimize for ON-OFF analysis
723
        print '\nON-OFF fitting...'
724
        theonofflist.optimize(opt)
725
        theonofflist.errors(opt)
726
        print_fit_results(theonofflist)
727
        
728
        # Append results
729
        onoff_flux.append(theonofflist.models()["Test source"]['Prefactor'].value())
730
        onoff_idx.append(theonofflist.models()["Test source"]['Index'].value())
731
    
732
        # Free memory
733
        del opt
734
        del log
735
        del theonofflist
736
         
737
        # Reset model container and append it to observation list
738
        models=reset_simple_model(models,ra,dec,dflux*flux,idx-didx)
739
        theobslist.models(models)
740
    
741
        # Set optimizer and logger
742
        log=GLog('fit_unbinned_%d.log' % (i),True)
743
        log.chatter(chatter)
744
        opt=GOptimizerLM(log)
745
    
746
        # Optimize for Fermi-style analysis
747
        print '\nUnbinned fitting...'
748
        theobslist.optimize(opt)
749
        theobslist.errors(opt)
750
        print_fit_results(theobslist)
751
        
752
        # Append results
753
        unbinned_flux.append(theobslist.models()["Test source"]['Prefactor'].value())
754
        unbinned_idx.append(theobslist.models()["Test source"]['Index'].value())
755
    
756
        # Free memory
757
        del opt
758
        del log
759
        del theobslist
760
        
761
    # Print out statistics
762
    print '\nTrue values: prefactor=%.3e and index=%.3f \n' % (flux,idx)
763
    print 'ON-OFF analysis'
764
    print 'Prefactor: mean= %.3e +/- %.3e' % (np.mean(np.array(onoff_flux)),np.std(np.array(onoff_flux)))
765
    print 'Index= %.3f +/- %.3f \n' % (np.mean(np.array(onoff_idx)),np.std(np.array(onoff_idx)))
766
    print 'Unbinned analysis'
767
    print 'Prefactor: mean= %.3e +/- %.3e' % (np.mean(np.array(unbinned_flux)),np.std(np.array(unbinned_flux)))
768
    print 'Index= %.3f +/- %.3f \n' % (np.mean(np.array(unbinned_idx)),np.std(np.array(unbinned_idx)))
769
    
770
    # Plot results
771
    prepare_plots()
772
    plot_histogram(unbinned_flux,nhist)
773
    plot_histogram(unbinned_idx,nhist)
774
    plot_histogram(onoff_flux,nhist)
775
    plot_histogram(onoff_idx,nhist)
776
    plt.show()
777
    
778