Commit 2f2afb6330b79c6e70b3c264e848fef177bceb32

Authored by Jürgen Knödlseder
2 parents a0d4125d d5430cfa

Merge branch 'integration' into devel

cscripts/Makefile.am
@@ -164,6 +164,7 @@ testdata = $(top_srcdir)/test/data/crab.xml \ @@ -164,6 +164,7 @@ testdata = $(top_srcdir)/test/data/crab.xml \
164 $(top_srcdir)/test/data/crab_psfcube.fits.gz \ 164 $(top_srcdir)/test/data/crab_psfcube.fits.gz \
165 $(top_srcdir)/test/data/crab_edispcube.fits.gz \ 165 $(top_srcdir)/test/data/crab_edispcube.fits.gz \
166 $(top_srcdir)/test/data/crab_bkgcube.fits.gz \ 166 $(top_srcdir)/test/data/crab_bkgcube.fits.gz \
  167 + $(top_srcdir)/test/data/crab_onoff.xml \
167 $(top_srcdir)/test/data/crab_offaxis1.fits.gz \ 168 $(top_srcdir)/test/data/crab_offaxis1.fits.gz \
168 $(top_srcdir)/test/data/crab_offaxis2.fits.gz \ 169 $(top_srcdir)/test/data/crab_offaxis2.fits.gz \
169 $(top_srcdir)/test/data/crab_exclusion.fits.gz \ 170 $(top_srcdir)/test/data/crab_exclusion.fits.gz \
cscripts/cslightcrv.par
@@ -16,12 +16,13 @@ @@ -16,12 +16,13 @@
16 # 16 #
17 # Input and output parameters 17 # Input and output parameters
18 #============================ 18 #============================
19 -inobs, f, a, events.fits,,, "Input event list or observation definition XML file"  
20 -inmodel, f, a, $CTOOLS/share/models/crab.xml,,, "Input model definition XML file"  
21 -srcname, s, a, Crab,,, "Source name"  
22 -caldb, s, a, prod2,,, "Calibration database"  
23 -irf, s, a, South_0.5h,,, "Instrument response function"  
24 -edisp, b, h, no,,, "Apply energy dispersion?" 19 +inobs, f, a, events.fits,,, "Input event list or observation definition XML file"
  20 +inmodel, f, a, $CTOOLS/share/models/crab.xml,,, "Input model definition XML file"
  21 +srcname, s, a, Crab,,, "Source name"
  22 +caldb, s, a, prod2,,, "Calibration database"
  23 +irf, s, a, South_0.5h,,, "Instrument response function"
  24 +inexclusion, f, h, NONE,,, "Sky map of exclusion regions for On/Off analysis"
  25 +edisp, b, h, no,,, "Apply energy dispersion in Cube analysis?"
25 outfile, f, a, lightcurve.fits,,, "Output light curve file" 26 outfile, f, a, lightcurve.fits,,, "Output light curve file"
26 27
27 # 28 #
@@ -32,16 +33,25 @@ tmin, t, a, MJD 51544.5,,,"Lightcurve start time (UTC string, JD, MJD or ME @@ -32,16 +33,25 @@ tmin, t, a, MJD 51544.5,,,"Lightcurve start time (UTC string, JD, MJD or ME
32 tmax, t, a, MJD 51544.6,,,"Lightcurve stop time (UTC string, JD, MJD or MET in seconds)" 33 tmax, t, a, MJD 51544.6,,,"Lightcurve stop time (UTC string, JD, MJD or MET in seconds)"
33 tbins, i, a, 5,1,10000, "Number of time bins" 34 tbins, i, a, 5,1,10000, "Number of time bins"
34 tbinfile, f, a, NONE,,, "File defining the time binning" 35 tbinfile, f, a, NONE,,, "File defining the time binning"
  36 +method, s, a, 3D, 3D|ONOFF,, "Analysis method"
35 emin, r, a, 0.1,,, "Lower energy limit of events (TeV)" 37 emin, r, a, 0.1,,, "Lower energy limit of events (TeV)"
36 emax, r, a, 100.0,,, "Upper energy limit of events (TeV)" 38 emax, r, a, 100.0,,, "Upper energy limit of events (TeV)"
37 -enumbins, i, a, 0,0,100, "Number of energy bins for binned (0=unbinned)" 39 +enumbins, i, a, 0,0,100, "Number of energy bins for binned (0=unbinned for 3D analysis only)"
38 coordsys, s, a, CEL,CEL|GAL,, "Coordinate System" 40 coordsys, s, a, CEL,CEL|GAL,, "Coordinate System"
39 proj, s, a, CAR,AIT|AZP|CAR|GLS|MER|MOL|SFL|SIN|STG|TAN,, "Projection method" 41 proj, s, a, CAR,AIT|AZP|CAR|GLS|MER|MOL|SFL|SIN|STG|TAN,, "Projection method"
40 -xref, r, a, 83.63,0,360, "First coordinate of image center in degrees (RA or galactic l)"  
41 -yref, r, a, 22.01,-90,90, "Second coordinate of image center in degrees (DEC or galactic b)" 42 +xref, r, a, 83.63,0,360, "First coordinate of image/source region center in degrees (RA or galactic l)"
  43 +yref, r, a, 22.01,-90,90, "Second coordinate of image/source region center in degrees (DEC or galactic b)"
42 nxpix, i, a, 200,,, "Size of the X axis in pixels" 44 nxpix, i, a, 200,,, "Size of the X axis in pixels"
43 nypix, i, a, 200,,, "Size of the Y axis in pixels" 45 nypix, i, a, 200,,, "Size of the Y axis in pixels"
44 binsz, r, a, 0.02,,, "Pixel size (deg/pixel)" 46 binsz, r, a, 0.02,,, "Pixel size (deg/pixel)"
  47 +srcshape, s, h, CIRCLE,CIRCLE,, "Shape of the source region"
  48 +rad, r, a, 0.2,0,180, "Radius of source region circle (deg)"
  49 +bkgmethod, s, h, REFLECTED,REFLECTED,, "Method for background estimation"
  50 +bkgregmin, i, h, 2,1,1000, "Minimum number of background regions"
  51 +maxoffset, r, h, 4.0,,, "Maximum offset of source from camera center to accept observation"
  52 +etruemin, r, h, 0.01,,, "Minimum true energy (TeV)"
  53 +etruemax, r, h, 300.0,,, "Maximum true energy (TeV)"
  54 +etruebins, i, h, 30,,, "Number of bins per decade for true energy bins"
45 statistic, s, h, DEFAULT,DEFAULT|CSTAT|WSTAT|CHI2,, "Optimization statistic" 55 statistic, s, h, DEFAULT,DEFAULT|CSTAT|WSTAT|CHI2,, "Optimization statistic"
46 calc_ts, b, h, yes,,, "Compute TS value for each bin?" 56 calc_ts, b, h, yes,,, "Compute TS value for each bin?"
47 calc_ulim, b, h, yes,,, "Compute upper limit for each bin?" 57 calc_ulim, b, h, yes,,, "Compute upper limit for each bin?"
cscripts/cslightcrv.py
@@ -74,6 +74,7 @@ class cslightcrv(ctools.csobservation): @@ -74,6 +74,7 @@ class cslightcrv(ctools.csobservation):
74 # Initialise some members 74 # Initialise some members
75 self._srcname = '' 75 self._srcname = ''
76 self._tbins = gammalib.GGti() 76 self._tbins = gammalib.GGti()
  77 + self._onoff = False
77 self._stacked = False 78 self._stacked = False
78 self._fits = gammalib.GFits() 79 self._fits = gammalib.GFits()
79 80
@@ -103,8 +104,13 @@ class cslightcrv(ctools.csobservation): @@ -103,8 +104,13 @@ class cslightcrv(ctools.csobservation):
103 # Get time boundaries 104 # Get time boundaries
104 self._tbins = self._create_tbounds() 105 self._tbins = self._create_tbounds()
105 106
  107 + # Set On/Off analysis flag and query relevant user parameters
  108 + self._onoff = self._is_onoff()
  109 +
  110 + # If cube analysis is selected
106 # Set stacked analysis flag and query relevant user parameters 111 # Set stacked analysis flag and query relevant user parameters
107 - self._stacked = self._is_stacked() 112 + if not self._onoff:
  113 + self._stacked = self._is_stacked()
108 114
109 # Query the hidden parameters, just in case 115 # Query the hidden parameters, just in case
110 self['edisp'].boolean() 116 self['edisp'].boolean()
@@ -357,40 +363,6 @@ class cslightcrv(ctools.csobservation): @@ -357,40 +363,6 @@ class cslightcrv(ctools.csobservation):
357 # Return upper limit tuple 363 # Return upper limit tuple
358 return ul_diff, ul_flux, ul_eflux 364 return ul_diff, ul_flux, ul_eflux
359 365
360 - def _bin_observation(self, obs):  
361 - """  
362 - Bin an observation if a binned analysis was requested  
363 -  
364 - Parameters  
365 - ----------  
366 - obs : `~gammalib.GObservations`  
367 - Observation container  
368 -  
369 - Returns  
370 - -------  
371 - obs : `~gammalib.GObservations`  
372 - Observation container where the first observation is a binned observation  
373 - """  
374 - # Get new observation container with binned observation  
375 - new_obs = obsutils.get_stacked_obs(self, obs)  
376 -  
377 - # Extract models  
378 - models = new_obs.models()  
379 -  
380 - # Fix background models if required  
381 - if self['fix_bkg'].boolean():  
382 - for model in models:  
383 - if model.classname() != 'GModelSky':  
384 - for par in model:  
385 - par.fix()  
386 -  
387 - # Put back models  
388 - new_obs.models(models)  
389 -  
390 - # Return new oberservation container  
391 - return new_obs  
392 -  
393 -  
394 # Public methods 366 # Public methods
395 def run(self): 367 def run(self):
396 """ 368 """
@@ -483,31 +455,116 @@ class cslightcrv(ctools.csobservation): @@ -483,31 +455,116 @@ class cslightcrv(ctools.csobservation):
483 # Retrieve observation 455 # Retrieve observation
484 obs = select.obs() 456 obs = select.obs()
485 457
486 - # If a stacked analysis is requested then bin the events  
487 - # and compute the stacked response functions and setup  
488 - # an observation container with a single stacked observation.  
489 - if self._stacked:  
490 - obs = self._bin_observation(obs) 458 + # Deal with stacked and On/Off Observations
  459 + if self._stacked or self._onoff:
  460 +
  461 + # If a stacked analysis is requested bin the events
  462 + # and compute the stacked response functions and setup
  463 + # an observation container with a single stacked observation.
  464 + if self._stacked:
  465 + new_obs = obsutils.get_stacked_obs(self, obs)
  466 +
  467 + # ... otherwise if On/Off analysis is requested generate
  468 + # the On/Off observations and response
  469 + elif self._onoff:
  470 + new_obs = obsutils.get_onoff_obs(self, obs)
  471 +
  472 + # Extract models
  473 + models = new_obs.models()
  474 +
  475 + # Fix background models if required
  476 + if self['fix_bkg'].boolean():
  477 + for model in models:
  478 + if model.classname() != 'GModelSky':
  479 + for par in model:
  480 + par.fix()
  481 +
  482 + # Put back models
  483 + new_obs.models(models)
  484 +
  485 + # Continue with new oberservation container
  486 + obs = new_obs
491 487
492 # Header 488 # Header
493 self._log_header3(gammalib.EXPLICIT, 'Fitting the data') 489 self._log_header3(gammalib.EXPLICIT, 'Fitting the data')
494 490
495 # Do maximum likelihood model fitting 491 # Do maximum likelihood model fitting
496 - like = ctools.ctlike(obs)  
497 - like['edisp'] = self['edisp'].boolean()  
498 - like.run() 492 + if obs.size() > 0:
  493 + like = ctools.ctlike(obs)
  494 + like['edisp'] = self['edisp'].boolean()
  495 + like.run()
  496 +
  497 + # Skip bin if no event was present
  498 + if like.obs().logL() == 0.0:
499 499
500 - # Skip bin if no event was present  
501 - if like.obs().logL() == 0.0: 500 + # Signal skipping of bin
  501 + self._log_value(gammalib.TERSE, 'Warning',
  502 + 'No event in this time bin, skip bin.')
502 503
503 - # Signal skipping of bin 504 + # Set all results to 0
  505 + for par in pars:
  506 + result['values'][par] = 0.0
  507 + result['values']['e_'+par] = 0.0
  508 +
  509 + # Append result
  510 + results.append(result)
  511 +
  512 + # Continue with next time bin
  513 + continue
  514 +
  515 + # Retrieve model fitting results for source of interest
  516 + source = like.obs().models()[self._srcname]
  517 +
  518 + # Extract parameter values
  519 + for par in pars:
  520 + result['values'][par] = source[par].value()
  521 + result['values']['e_'+par] = source[par].error()
  522 +
  523 + # Calculate upper limit (-1 if not computed)
  524 + #ul_diff, ul_flux, ul_eflux = self._compute_ulimit(like.obs())
  525 + ul_diff, ul_flux, ul_eflux = self._compute_ulimit(obs)
  526 + if ul_diff > 0.0:
  527 + result['ul_diff'] = ul_diff
  528 + result['ul_flux'] = ul_flux
  529 + result['ul_eflux'] = ul_eflux
  530 +
  531 + # Extract Test Statistic value
  532 + if self['calc_ts'].boolean():
  533 + result['ts'] = source.ts()
  534 +
  535 + # Append result to list of dictionaries
  536 + results.append(result)
  537 +
  538 + # Log results for this time bin
  539 + self._log.header3('Results')
  540 + pars = self._get_free_par_names()
  541 + for par in pars:
  542 + value = source[par].value()
  543 + error = source[par].error()
  544 + unit = source[par].unit()
  545 + self._log_value(gammalib.NORMAL, par,
  546 + str(value)+' +/- '+str(error)+' '+unit)
  547 + if ul_diff > 0.0:
  548 + self._log_value(gammalib.NORMAL, 'Upper flux limit',
  549 + str(result['ul_diff'])+' ph/cm2/s/MeV')
  550 + self._log_value(gammalib.NORMAL, 'Upper flux limit',
  551 + str(result['ul_flux'])+' ph/cm2/s')
  552 + self._log_value(gammalib.NORMAL, 'Upper flux limit',
  553 + str(result['ul_eflux'])+' erg/cm2/s')
  554 + if self['calc_ts'].boolean():
  555 + self._log_value(gammalib.NORMAL, 'Test Statistic', result['ts'])
  556 +
  557 + # Otherwise, if observations size is 0, signal bin is skipped and
  558 + # fill results table with zeros
  559 + else:
504 self._log_value(gammalib.TERSE, 'Warning', 560 self._log_value(gammalib.TERSE, 'Warning',
505 - 'No event in this time bin, skip bin.') 561 + 'No observations available in this time bin',
  562 + 'Skip bin.')
506 563
507 # Set all results to 0 564 # Set all results to 0
508 for par in pars: 565 for par in pars:
509 - result['values'][par] = 0.0  
510 - result['values']['e_'+par] = 0.0 566 + result['values'][par] = 0.0
  567 + result['values']['e_' + par] = 0.0
511 568
512 # Append result 569 # Append result
513 results.append(result) 570 results.append(result)
@@ -515,48 +572,6 @@ class cslightcrv(ctools.csobservation): @@ -515,48 +572,6 @@ class cslightcrv(ctools.csobservation):
515 # Continue with next time bin 572 # Continue with next time bin
516 continue 573 continue
517 574
518 - # Retrieve model fitting results for source of interest  
519 - source = like.obs().models()[self._srcname]  
520 -  
521 - # Extract parameter values  
522 - for par in pars:  
523 - result['values'][par] = source[par].value()  
524 - result['values']['e_'+par] = source[par].error()  
525 -  
526 - # Calculate upper limit (-1 if not computed)  
527 - #ul_diff, ul_flux, ul_eflux = self._compute_ulimit(like.obs())  
528 - ul_diff, ul_flux, ul_eflux = self._compute_ulimit(obs)  
529 - if ul_diff > 0.0:  
530 - result['ul_diff'] = ul_diff  
531 - result['ul_flux'] = ul_flux  
532 - result['ul_eflux'] = ul_eflux  
533 -  
534 - # Extract Test Statistic value  
535 - if self['calc_ts'].boolean():  
536 - result['ts'] = source.ts()  
537 -  
538 - # Append result to list of dictionaries  
539 - results.append(result)  
540 -  
541 - # Log results for this time bin  
542 - self._log.header3('Results')  
543 - pars = self._get_free_par_names()  
544 - for par in pars:  
545 - value = source[par].value()  
546 - error = source[par].error()  
547 - unit = source[par].unit()  
548 - self._log_value(gammalib.NORMAL, par,  
549 - str(value)+' +/- '+str(error)+' '+unit)  
550 - if ul_diff > 0.0:  
551 - self._log_value(gammalib.NORMAL, 'Upper flux limit',  
552 - str(result['ul_diff'])+' ph/cm2/s/MeV')  
553 - self._log_value(gammalib.NORMAL, 'Upper flux limit',  
554 - str(result['ul_flux'])+' ph/cm2/s')  
555 - self._log_value(gammalib.NORMAL, 'Upper flux limit',  
556 - str(result['ul_eflux'])+' erg/cm2/s')  
557 - if self['calc_ts'].boolean():  
558 - self._log_value(gammalib.NORMAL, 'Test Statistic', result['ts'])  
559 -  
560 # Create FITS table from results 575 # Create FITS table from results
561 table = self._create_fits_table(results) 576 table = self._create_fits_table(results)
562 577
cscripts/csphasecrv.par
@@ -21,7 +21,8 @@ inmodel, f, a, $CTOOLS/share/models/crab.xml,,, "Input model definition XML fi @@ -21,7 +21,8 @@ inmodel, f, a, $CTOOLS/share/models/crab.xml,,, "Input model definition XML fi
21 srcname, s, a, Crab,,, "Source name" 21 srcname, s, a, Crab,,, "Source name"
22 caldb, s, a, prod2,,, "Calibration database" 22 caldb, s, a, prod2,,, "Calibration database"
23 irf, s, a, South_0.5h,,, "Instrument response function" 23 irf, s, a, South_0.5h,,, "Instrument response function"
24 -edisp, b, h, no,,, "Apply energy dispersion?" 24 +inexclusion, f, h, NONE,,, "Sky map of exclusion regions for On/Off analysis"
  25 +edisp, b, h, no,,, "Apply energy dispersion in Cube analysis?"
25 outfile, f, a, phasecrv.fits,,, "Output fits file name." 26 outfile, f, a, phasecrv.fits,,, "Output fits file name."
26 27
27 # 28 #
@@ -30,16 +31,25 @@ outfile, f, a, phasecrv.fits,,, "Output fits file name." @@ -30,16 +31,25 @@ outfile, f, a, phasecrv.fits,,, "Output fits file name."
30 phbinalg, s, a, LIN,LIN|FILE,, "Algorithm for defining phase bins" 31 phbinalg, s, a, LIN,LIN|FILE,, "Algorithm for defining phase bins"
31 phbins, i, a, 5,1,10000, "Number of phase bins" 32 phbins, i, a, 5,1,10000, "Number of phase bins"
32 phbinfile, f, a, NONE,,, "File defining the phase binning" 33 phbinfile, f, a, NONE,,, "File defining the phase binning"
  34 +method, s, a, 3D, 3D|ONOFF,, "Analysis method"
33 emin, r, a, 0.1,,, "Lower energy limit of events (TeV)" 35 emin, r, a, 0.1,,, "Lower energy limit of events (TeV)"
34 emax, r, a, 100.0,,, "Upper energy limit of events (TeV)" 36 emax, r, a, 100.0,,, "Upper energy limit of events (TeV)"
35 -enumbins, i, a, 0,0,100, "Number of energy bins for binned (0=unbinned)" 37 +enumbins, i, a, 0,0,100, "Number of energy bins for binned (0=unbinned for 3D analysis only))"
36 coordsys, s, a, CEL,CEL|GAL,, "Coordinate System" 38 coordsys, s, a, CEL,CEL|GAL,, "Coordinate System"
37 proj, s, a, CAR,AIT|AZP|CAR|GLS|MER|MOL|SFL|SIN|STG|TAN,, "Projection method" 39 proj, s, a, CAR,AIT|AZP|CAR|GLS|MER|MOL|SFL|SIN|STG|TAN,, "Projection method"
38 -xref, r, a, 83.63,0,360, "First coordinate of image center in degrees (RA or galactic l)"  
39 -yref, r, a, 22.01,-90,90, "Second coordinate of image center in degrees (DEC or galactic b)" 40 +xref, r, a, 83.63,0,360, "First coordinate of image/source region center in degrees (RA or galactic l)"
  41 +yref, r, a, 22.01,-90,90, "Second coordinate of image/source region center in degrees (DEC or galactic b)"
40 nxpix, i, a, 200,,, "Size of the X axis in pixels" 42 nxpix, i, a, 200,,, "Size of the X axis in pixels"
41 nypix, i, a, 200,,, "Size of the Y axis in pixels" 43 nypix, i, a, 200,,, "Size of the Y axis in pixels"
42 binsz, r, a, 0.02,,, "Pixel size (deg/pixel)" 44 binsz, r, a, 0.02,,, "Pixel size (deg/pixel)"
  45 +srcshape, s, h, CIRCLE,CIRCLE,, "Shape of the source region"
  46 +rad, r, a, 0.2,0,180, "Radius of source region circle (deg)"
  47 +bkgmethod, s, h, REFLECTED,REFLECTED,, "Method for background estimation"
  48 +bkgregmin, i, h, 2,1,1000, "Minimum number of background regions"
  49 +maxoffset, r, h, 4.0,,, "Maximum offset of source from camera center to accept observation"
  50 +etruemin, r, h, 0.01,,, "Minimum true energy (TeV)"
  51 +etruemax, r, h, 300.0,,, "Maximum true energy (TeV)"
  52 +etruebins, i, h, 30,,, "Number of bins per decade for true energy bins"
43 statistic, s, h, DEFAULT,DEFAULT|CSTAT|WSTAT|CHI2,, "Optimization statistic" 53 statistic, s, h, DEFAULT,DEFAULT|CSTAT|WSTAT|CHI2,, "Optimization statistic"
44 54
45 # 55 #
cscripts/csphasecrv.py
@@ -63,6 +63,7 @@ class csphasecrv(ctools.csobservation): @@ -63,6 +63,7 @@ class csphasecrv(ctools.csobservation):
63 # [[ph1min,ph1max], [ph2min,ph2max],..] 63 # [[ph1min,ph1max], [ph2min,ph2max],..]
64 self._srcname = '' 64 self._srcname = ''
65 self._phbins = [[0.0,1.0]] 65 self._phbins = [[0.0,1.0]]
  66 + self._onoff = False
66 self._stacked = False 67 self._stacked = False
67 self._fits = gammalib.GFits() 68 self._fits = gammalib.GFits()
68 self._fitmodels = {} 69 self._fitmodels = {}
@@ -92,8 +93,13 @@ class csphasecrv(ctools.csobservation): @@ -92,8 +93,13 @@ class csphasecrv(ctools.csobservation):
92 # Get phase boundaries 93 # Get phase boundaries
93 self._phbins = self._create_tbounds() 94 self._phbins = self._create_tbounds()
94 95
  96 + # Set On/Off analysis flag and query relevant user parameters
  97 + self._onoff = self._is_onoff()
  98 +
  99 + # If cube analysis is selected
95 # Set stacked analysis flag and query relevant user parameters 100 # Set stacked analysis flag and query relevant user parameters
96 - self._stacked = self._is_stacked() 101 + if not self._onoff:
  102 + self._stacked = self._is_stacked()
97 103
98 # Query the hidden parameters, just in case 104 # Query the hidden parameters, just in case
99 self['edisp'].boolean() 105 self['edisp'].boolean()
@@ -236,11 +242,20 @@ class csphasecrv(ctools.csobservation): @@ -236,11 +242,20 @@ class csphasecrv(ctools.csobservation):
236 242
237 # Store fit results 243 # Store fit results
238 phname = str(phmin)+'-'+str(phmax) 244 phname = str(phmin)+'-'+str(phmax)
239 - source = self._fitmodels[phname][self._srcname]  
240 - for par in pars:  
241 - result['values'][par] = source[par].value()  
242 - result['values']['e_'+par] = source[par].error()  
243 - 245 +
  246 + # If the model contains the source of interest fill results
  247 + try:
  248 + source = self._fitmodels[phname][self._srcname]
  249 + for par in pars:
  250 + result['values'][par] = source[par].value()
  251 + result['values']['e_'+par] = source[par].error()
  252 +
  253 + # ... otherwise fills with zeros
  254 + except:
  255 + for par in pars:
  256 + result['values'][par] = 0.
  257 + result['values']['e_'+par] = 0.
  258 +
244 # Append result to list of dictionaries 259 # Append result to list of dictionaries
245 results.append(result) 260 results.append(result)
246 261
@@ -348,36 +363,57 @@ class csphasecrv(ctools.csobservation): @@ -348,36 +363,57 @@ class csphasecrv(ctools.csobservation):
348 select['dec'] = 'UNDEFINED' 363 select['dec'] = 'UNDEFINED'
349 select['expr'] = 'PHASE>'+str(phbin[0])+' && PHASE<'+str(phbin[1]) 364 select['expr'] = 'PHASE>'+str(phbin[0])+' && PHASE<'+str(phbin[1])
350 select.run() 365 select.run()
351 - 366 +
  367 + # Set phase string
  368 + phstr = str(phbin[0]) + '-' + str(phbin[1])
  369 +
352 # Add phase to observation id 370 # Add phase to observation id
353 for i in range(0,select.obs().size()): 371 for i in range(0,select.obs().size()):
354 oldid = select.obs()[i].id() 372 oldid = select.obs()[i].id()
355 - select.obs()[i].id(oldid+'_'+str(phbin[0])+'-'+str(phbin[1])) 373 + select.obs()[i].id(oldid+'_'+phstr)
356 obs = select.obs() 374 obs = select.obs()
357 375
358 - # If a stacked analysis is requested then bin the events  
359 - # and compute the stacked response functions and setup 376 + # If an On/Off analysis is requested generate the On/Off observations
  377 + if self._onoff:
  378 + obs = obsutils.get_onoff_obs(self, select.obs())
  379 +
  380 + # ... otherwise, if stacked analysis is requested then bin the
  381 + # events and compute the stacked response functions and setup
360 # an observation container with a single stacked observation. 382 # an observation container with a single stacked observation.
361 - if self._stacked: 383 + elif self._stacked:
362 obs = obsutils.get_stacked_obs(self, select.obs()) 384 obs = obsutils.get_stacked_obs(self, select.obs())
363 - 385 +
364 # Header 386 # Header
365 self._log_header3(gammalib.EXPLICIT, 'Fitting the data') 387 self._log_header3(gammalib.EXPLICIT, 'Fitting the data')
366 -  
367 - # Do maximum likelihood model fitting  
368 - like = ctools.ctlike(obs)  
369 - like['edisp'] = self['edisp'].boolean()  
370 - like.run()  
371 -  
372 - # Renormalize models to phase selection  
373 - # TODO move the scaling from the temporal to the spectral component  
374 - for model in like.obs().models():  
375 - scaled_norm = model['Normalization'].value()/(phbin[1]-phbin[0])  
376 - model['Normalization'].value(scaled_norm)  
377 -  
378 - # Store fit model  
379 - self._fitmodels[str(phbin[0])+'-'+str(phbin[1])] = \  
380 - like.obs().models().copy() 388 +
  389 + # The On/Off analysis can produce empty observation containers,
  390 + # e.g., when on-axis observations are used. To avoid ctlike asking
  391 + # for a new observation container (or hang, if in interactive mode)
  392 + # we'll run ctlike only if the size is >0
  393 + if obs.size() > 0:
  394 +
  395 + # Do maximum likelihood model fitting
  396 + like = ctools.ctlike(obs)
  397 + like['edisp'] = self['edisp'].boolean()
  398 + like.run()
  399 +
  400 + # Renormalize models to phase selection
  401 + # TODO move the scaling from the temporal to the spectral component
  402 + for model in like.obs().models():
  403 + scaled_norm = model['Normalization'].value()/(phbin[1]-phbin[0])
  404 + model['Normalization'].value(scaled_norm)
  405 +
  406 + # Store fit model
  407 + self._fitmodels[phstr] = like.obs().models().copy()
  408 +
  409 + # ... otherwise we set an empty model container
  410 + else:
  411 + self._log_string(gammalib.TERSE,
  412 + 'PHASE %f - %f: no observations available'
  413 + ' for fitting' %(phbin[0], phbin[1]))
  414 +
  415 + # Set empty models container
  416 + self._fitmodels[phstr] = gammalib.GModels()
381 417
382 # Create FITS file 418 # Create FITS file
383 self._create_fits() 419 self._create_fits()
cscripts/obsutils.py
@@ -760,3 +760,71 @@ def get_stacked_obs(cls, obs): @@ -760,3 +760,71 @@ def get_stacked_obs(cls, obs):
760 760
761 # Return new oberservation container 761 # Return new oberservation container
762 return new_obs 762 return new_obs
  763 +
  764 +
  765 +# ================================ #
  766 +# Get On/Off observation container #
  767 +# ================================ #
  768 +def get_onoff_obs(cls, obs):
  769 + """
  770 + Create On/Off observations container from given observations
  771 +
  772 + Parameters
  773 + ----------
  774 + cls : `~ctools.cscript`
  775 + cscript class
  776 + obs : `~gammalib.GObservations`
  777 + Observation container
  778 +
  779 + Returns
  780 + -------
  781 + onoff_obs : `~gammalib.GObservations`
  782 + Observation container with On/Off observations
  783 + """
  784 + # Write header
  785 + if cls._logExplicit():
  786 + cls._log.header3('Creating On/Off observations')
  787 +
  788 + # Create On/Off observations
  789 + phagen = cscripts.csphagen(obs)
  790 + phagen['inexclusion'] = cls['inexclusion'].value()
  791 + phagen['emin'] = cls['emin'].real()
  792 + phagen['emax'] = cls['emax'].real()
  793 + phagen['enumbins'] = cls['enumbins'].integer()
  794 + phagen['ebinalg'] = 'LOG'
  795 + phagen['srcshape'] = cls['srcshape'].string()
  796 + phagen['coordsys'] = cls['coordsys'].string()
  797 + if cls['coordsys'].string() == 'CEL':
  798 + phagen['ra'] = cls['xref'].real()
  799 + phagen['dec'] = cls['yref'].real()
  800 + elif cls['coordsys'].string() == 'GAL':
  801 + phagen['glon'] = cls['xref'].real()
  802 + phagen['glat'] = cls['yref'].real()
  803 + if cls['srcshape'].string() == 'CIRCLE':
  804 + phagen['rad'] = cls['rad'].real()
  805 + phagen['bkgmethod'] = cls['bkgmethod'].string()
  806 + if cls['bkgmethod'].string() == 'REFLECTED':
  807 + phagen['bkgregmin'] = cls['bkgregmin'].integer()
  808 + phagen['maxoffset'] = cls['maxoffset'].real()
  809 + phagen['stack'] = True
  810 + phagen['etruemin'] = cls['etruemin'].real()
  811 + phagen['etruemax'] = cls['etruemax'].real()
  812 + phagen['etruebins'] = cls['etruebins'].integer()
  813 + phagen['chatter'] = cls['chatter'].integer()
  814 + phagen['clobber'] = cls['clobber'].boolean()
  815 + phagen['debug'] = cls['debug'].boolean()
  816 + phagen.run()
  817 +
  818 + # Clone resulting observation container
  819 + onoff_obs = phagen.obs().copy()
  820 +
  821 + # On/Off observations are created with CSTAT as default statistic
  822 + # We will change this to the user choice
  823 + if cls['statistic'].string() == 'DEFAULT':
  824 + pass
  825 + else:
  826 + for observation in onoff_obs:
  827 + observation.statistic(cls['statistic'].string())
  828 +
  829 + # Return On/Off oberservation container
  830 + return onoff_obs
doc/source/users/reference_manual/cslightcrv.rst
@@ -42,13 +42,19 @@ General parameters @@ -42,13 +42,19 @@ General parameters
42 ``irf [string]`` 42 ``irf [string]``
43 Instrumental response function. 43 Instrumental response function.
44 44
  45 +``(inexclusion = NONE) [file]``
  46 + Optional FITS file containing a WCS map in the first hdu that defines sky
  47 + regions not to be used for background estimation in ``ONOFF`` analysis (where
  48 + map value != 0).
  49 +
45 ``(edisp = no) [boolean]`` 50 ``(edisp = no) [boolean]``
46 - Applies energy dispersion to response computation. 51 + Applies energy dispersion to response computation (for ``3D`` analysis only,
  52 + energy dispersion is always taken into account in ``ONOFF`` analysis).
47 53
48 ``outfile [file]`` 54 ``outfile [file]``
49 Name of the light curve output file. 55 Name of the light curve output file.
50 56
51 -``(tbinalg = GTI) <FILE|LIN|GTI> [string]`` 57 +``tbinalg <FILE|LIN|GTI> [string]``
52 Algorithm for defining time bins. 58 Algorithm for defining time bins.
53 59
54 ``tmin [time]`` 60 ``tmin [time]``
@@ -63,6 +69,10 @@ General parameters @@ -63,6 +69,10 @@ General parameters
63 ``tbinfile [file]`` 69 ``tbinfile [file]``
64 File defining the time binning. 70 File defining the time binning.
65 71
  72 +``method <3D|ONOFF> [string]``
  73 + Selects between ``3D`` analysis (3D spatial/energy likelihood) and ``ONOFF``
  74 + analysis (1D likelihood with background from Off regions).
  75 +
66 ``emin [real]`` 76 ``emin [real]``
67 Lower energy limit of events (in TeV). 77 Lower energy limit of events (in TeV).
68 78
@@ -70,7 +80,7 @@ General parameters @@ -70,7 +80,7 @@ General parameters
70 Upper energy limit of events (in TeV). 80 Upper energy limit of events (in TeV).
71 81
72 ``enumbins [integer]`` 82 ``enumbins [integer]``
73 - Number of energy bins per light curve bin (0=unbinned). 83 + Number of energy bins per light curve bin (0=unbinned for ``3D`` analysis only).
74 84
75 ``coordsys <CEL|GAL> [string]`` 85 ``coordsys <CEL|GAL> [string]``
76 Coordinate system (CEL - celestial, GAL - galactic). 86 Coordinate system (CEL - celestial, GAL - galactic).
@@ -79,19 +89,52 @@ General parameters @@ -79,19 +89,52 @@ General parameters
79 Projection method. 89 Projection method.
80 90
81 ``xref [real]`` 91 ``xref [real]``
82 - Right Ascension / Galactic longitude of image centre (J2000, in degrees). 92 + Right Ascension / Galactic longitude of image centre for ``3D`` analysis or
  93 + source region centre for ``ONOFF`` analysis (J2000, in degrees).
83 94
84 ``yref [real]`` 95 ``yref [real]``
85 - Declination / Galactic latitude of image centre (J2000, in degrees). 96 + Declination / Galactic latitude of image centre for ``3D`` analysis or
  97 + source region centre for ``ONOFF`` analysis (J2000, in degrees).
86 98
87 ``nxpix [integer]`` 99 ``nxpix [integer]``
88 - Size of the Right Ascension / Galactic longitude axis (in pixels). 100 + Size of the Right Ascension / Galactic longitude axis for ``3D`` analysis (in pixels).
89 101
90 ``nypix [integer]`` 102 ``nypix [integer]``
91 - Size of the Declination / Galactic latitude axis (in pixels). 103 + Size of the Declination / Galactic latitude axis for ``3D`` analysis (in pixels).
92 104
93 ``binsz [real]`` 105 ``binsz [real]``
94 - Pixel size (in degrees/pixel). 106 + Pixel size for ``3D`` analysis (in degrees/pixel).
  107 +
  108 +``(srcshape = CIRCLE) [string]``
  109 + Shape of the source region for ``ONOFF`` analysis.
  110 + ``CIRCLE``: circular region around given position.
  111 +
  112 +``rad [real]``
  113 + Radius of source region circle for ``ONOFF`` analysis (deg)
  114 +
  115 +``(bkgmethod = REFLECTED) [string]``
  116 + Method for background estimation in ``ONOFF`` analysis.
  117 + ``REFLECTED:`` background evaluated in regions with the same shape as
  118 + source region reflected w.r.t. pointing direction for each observation.
  119 +
  120 +``(bkgregmin = 2) [integer]``
  121 + Minimum number of background regions that are required for an observation in
  122 + ``ONOFF`` analysis. If this number of background regions is not available the
  123 + observation is skipped.
  124 +
  125 +``(maxoffset = 4.0) [real]``
  126 + Maximum offset in degrees of source from camera center to accept the
  127 + observation for ``ONOFF`` analysis.
  128 +
  129 +``(etruemin = 0.01) [real]``
  130 + Minimum true energy to evaluate instrumental response in ``ONOFF`` analysis (TeV).
  131 +
  132 +``(etruemax = 0.01) [real]``
  133 + Maximum true energy to evaluate instrumental response in ``ONOFF`` analysis (TeV).
  134 +
  135 +``(etruebins = 30) [integer]``
  136 + Number of bins per decade for true energy bins to evaluate instrumental
  137 + response in ``ONOFF`` analysis.
95 138
96 ``(statistic = DEFAULT) <DEFAULT|CSTAT|WSTAT|CHI2> [string]`` 139 ``(statistic = DEFAULT) <DEFAULT|CSTAT|WSTAT|CHI2> [string]``
97 Optimization statistic. ``DEFAULT`` uses the default statistic for all 140 Optimization statistic. ``DEFAULT`` uses the default statistic for all
doc/source/users/reference_manual/csphasecrv.rst
@@ -10,14 +10,16 @@ Synopsis @@ -10,14 +10,16 @@ Synopsis
10 -------- 10 --------
11 11
12 This script computes spectra by performing a maximum likelihood fit using 12 This script computes spectra by performing a maximum likelihood fit using
13 -:doc:`ctlike` in a series of phase bins for pulsars. The phase bins can be 13 +:doc:`ctlike` in a series of phase bins for periodic sources. The input event
  14 +list must have a ``PHASE`` column containing the phase values, that can be
  15 +assigned using :doc:`ctphase`. The phase bins can be
14 either specified in an ASCII file or as an interval divided into equally sized 16 either specified in an ASCII file or as an interval divided into equally sized
15 phase bins. The format of the ASCII file is one row per phase bin, each 17 phase bins. The format of the ASCII file is one row per phase bin, each
16 specifying the start of stop value of the phase bin, separated by a whitespace. 18 specifying the start of stop value of the phase bin, separated by a whitespace.
17 The phase goes from 0.0 to 1.0. 19 The phase goes from 0.0 to 1.0.
18 20
19 -On output the script writes the fitting results into a FITS file. The script  
20 -also produces one XML file per phase bin that contains the fitting results for 21 +The script writes the fit results into a FITS file. The script
  22 +also produces one XML file per phase bin that contains the best-fit model for
21 that bin. 23 that bin.
22 24
23 25
@@ -39,8 +41,14 @@ General parameters @@ -39,8 +41,14 @@ General parameters
39 ``irf [string]`` 41 ``irf [string]``
40 Instrumental response function. 42 Instrumental response function.
41 43
  44 +``(inexclusion = NONE) [file]``
  45 + Optional FITS file containing a WCS map in the first hdu that defines sky
  46 + regions not to be used for background estimation in ``ONOFF`` analysis (where
  47 + map value != 0).
  48 +
42 ``(edisp = no) [boolean]`` 49 ``(edisp = no) [boolean]``
43 - Applies energy dispersion to response computation. 50 + Applies energy dispersion to response computation (for ``3D`` analysis only,
  51 + energy dispersion is always taken into account in ``ONOFF`` analysis).
44 52
45 ``outfile [file]`` 53 ``outfile [file]``
46 Name of the XML output file. The phase interval will be automatically 54 Name of the XML output file. The phase interval will be automatically
@@ -55,6 +63,10 @@ General parameters @@ -55,6 +63,10 @@ General parameters
55 ``phbinfile [file]`` 63 ``phbinfile [file]``
56 File defining the phase binning. 64 File defining the phase binning.
57 65
  66 +``method <3D|ONOFF> [string]``
  67 + Selects between 3D analysis (3D spatial/energy likelihood) and ONOFF
  68 + analysis (1D likelihood with background from Off regions).
  69 +
58 ``emin [real]`` 70 ``emin [real]``
59 Lower energy limit of events (in TeV). 71 Lower energy limit of events (in TeV).
60 72
@@ -71,19 +83,52 @@ General parameters @@ -71,19 +83,52 @@ General parameters
71 Projection method. 83 Projection method.
72 84
73 ``xref [real]`` 85 ``xref [real]``
74 - Right Ascension / Galactic longitude of image centre (J2000, in degrees). 86 + Right Ascension / Galactic longitude of image centre for ``3D`` analysis or
  87 + source region centre for ``ONOFF`` analysis (J2000, in degrees).
75 88
76 ``yref [real]`` 89 ``yref [real]``
77 - Declination / Galactic latitude of image centre (J2000, in degrees). 90 + Declination / Galactic latitude of image centre for ``3D`` analysis or
  91 + source region centre for ``ONOFF`` analysis (J2000, in degrees).
78 92
79 ``nxpix [integer]`` 93 ``nxpix [integer]``
80 - Size of the Right Ascension / Galactic longitude axis (in pixels). 94 + Size of the Right Ascension / Galactic longitude axis for ``3D`` analysis (in pixels).
81 95
82 ``nypix [integer]`` 96 ``nypix [integer]``
83 - Size of the Declination / Galactic latitude axis (in pixels). 97 + Size of the Declination / Galactic latitude axis for ``3D`` analysis (in pixels).
84 98
85 ``binsz [real]`` 99 ``binsz [real]``
86 - Pixel size (in degrees/pixel). 100 + Pixel size for ``3D`` analysis (in degrees/pixel).
  101 +
  102 +``(srcshape = CIRCLE) [string]``
  103 + Shape of the source region for ``ONOFF`` analysis.
  104 + ``CIRCLE``: circular region around given position.
  105 +
  106 +``rad [real]``
  107 + Radius of source region circle for ``ONOFF`` analysis (deg)
  108 +
  109 +``(bkgmethod = REFLECTED) [string]``
  110 + Method for background estimation in ``ONOFF`` analysis.
  111 + ``REFLECTED:`` background evaluated in regions with the same shape as
  112 + source region reflected w.r.t. pointing direction for each observation.
  113 +
  114 +``(bkgregmin = 2) [integer]``
  115 + Minimum number of background regions that are required for an observation in
  116 + ``ONOFF`` analysis. If this number of background regions is not available the
  117 + observation is skipped.
  118 +
  119 +``(maxoffset = 4.0) [real]``
  120 + Maximum offset in degrees of source from camera center to accept the
  121 + observation for ``ONOFF`` analysis.
  122 +
  123 +``(etruemin = 0.01) [real]``
  124 + Minimum true energy to evaluate instrumental response in ``ONOFF`` analysis (TeV).
  125 +
  126 +``(etruemax = 0.01) [real]``
  127 + Maximum true energy to evaluate instrumental response in ``ONOFF`` analysis (TeV).
  128 +
  129 +``(etruebins = 30) [integer]``
  130 + Number of bins per decade for true energy bins to evaluate instrumental
  131 + response in ``ONOFF`` analysis.
87 132
88 ``(statistic = DEFAULT) <DEFAULT|CSTAT|WSTAT|CHI2> [string]`` 133 ``(statistic = DEFAULT) <DEFAULT|CSTAT|WSTAT|CHI2> [string]``
89 Optimization statistic. ``DEFAULT`` uses the default statistic for all 134 Optimization statistic. ``DEFAULT`` uses the default statistic for all
@@ -129,4 +174,5 @@ Standard parameters @@ -129,4 +174,5 @@ Standard parameters
129 Related tools or scripts 174 Related tools or scripts
130 ------------------------ 175 ------------------------
131 176
  177 +:doc:`ctphase`
132 :doc:`ctlike` 178 :doc:`ctlike`
doc/source/users/tutorials/howto/howto_lightcurve.rst
@@ -34,12 +34,13 @@ The time interval is linearly divided into 20 time bins. @@ -34,12 +34,13 @@ The time interval is linearly divided into 20 time bins.
34 Input model definition XML file [$CTOOLS/share/models/crab.xml] models.xml 34 Input model definition XML file [$CTOOLS/share/models/crab.xml] models.xml
35 Source name [Crab] Src001 35 Source name [Crab] Src001
36 Algorithm for defining time bins (FILE|LIN|GTI) [GTI] LIN 36 Algorithm for defining time bins (FILE|LIN|GTI) [GTI] LIN
37 - Lightcurve start time (MJD) [51544.5] 59235.5  
38 - Lightcurve stop time (MJD) [51544.6] 59236.0 37 + Lightcurve start time (UTC string, JD, MJD or MET in seconds) [MJD 51544.5] MJD 59235.5
  38 + Lightcurve stop time (UTC string, JD, MJD or MET in seconds) [MJD 51544.6] MJD 59236.0
39 Number of time bins (1-10000) [5] 20 39 Number of time bins (1-10000) [5] 20
40 - Number of energy bins for binned (0=unbinned) (0-100) [0] 40 + Analysis method (3D|ONOFF) [3D]
41 Lower energy limit of events (TeV) [0.1] 41 Lower energy limit of events (TeV) [0.1]
42 Upper energy limit of events (TeV) [100.0] 42 Upper energy limit of events (TeV) [100.0]
  43 + Number of energy bins for binned (0=unbinned for Cube analysis only) (0-100) [0]
43 Output light curve file [lightcurve.fits] 44 Output light curve file [lightcurve.fits]
44 45
45 The resulting light curve is shown in the figure below. 46 The resulting light curve is shown in the figure below.
pyext/ctool.i
@@ -100,6 +100,7 @@ public: @@ -100,6 +100,7 @@ public:
100 %rename(_get_skydir) get_skydir; 100 %rename(_get_skydir) get_skydir;
101 %rename(_set_outfile_name) set_outfile_name; 101 %rename(_set_outfile_name) set_outfile_name;
102 %rename(_is_stacked) is_stacked; 102 %rename(_is_stacked) is_stacked;
  103 + %rename(_is_onoff) is_onoff;
103 %rename(_set_response) set_response; 104 %rename(_set_response) set_response;
104 %rename(_set_edisp) set_edisp; 105 %rename(_set_edisp) set_edisp;
105 %rename(_restore_edisp) restore_edisp; 106 %rename(_restore_edisp) restore_edisp;
@@ -146,6 +147,7 @@ public: @@ -146,6 +147,7 @@ public:
146 147
147 // Protected methods that query user parameters 148 // Protected methods that query user parameters
148 bool is_stacked(void); 149 bool is_stacked(void);
  150 + bool is_onoff(void);
149 151
150 // Protected support methods 152 // Protected support methods
151 void set_response(GObservations& obs); 153 void set_response(GObservations& obs);
src/support/ctool.cpp
@@ -1195,6 +1195,54 @@ bool ctool::is_stacked(void) @@ -1195,6 +1195,54 @@ bool ctool::is_stacked(void)
1195 1195
1196 1196
1197 /***********************************************************************//** 1197 /***********************************************************************//**
  1198 + * @brief Query user parameters for On/Off analysis
  1199 + *
  1200 + * @return True if an On/Off analysis should be done.
  1201 + *
  1202 + * Queries the parameter "method", and if this is "ONOFF" signal that an On/
  1203 + * Off analysis is requested. In that case, all necessary input parameters of
  1204 + * csphagen are queried: "inexclusion", "emin", "emax", "enumbins",
  1205 + * "coordsys", "xref", "yref", "srcshape", "rad", "bkgmethod", "bkgregmin",
  1206 + * "maxoffset", "etruemin", "etruemax", and "etruebins".
  1207 + *
  1208 + * Using this method assures that the parameters are always queried in the
  1209 + * same order.
  1210 + ***************************************************************************/
  1211 +bool ctool::is_onoff(void)
  1212 +{
  1213 + // Set onoff flag
  1214 + bool onoff = ((*this)["method"].string() == "ONOFF");
  1215 +
  1216 + // Query analysis method chosen
  1217 + if (onoff) {
  1218 +
  1219 + // Query csphagen parameters
  1220 + (*this)["inexclusion"].filename();
  1221 + (*this)["emin"].real();
  1222 + (*this)["emax"].real();
  1223 + (*this)["enumbins"].integer();
  1224 + (*this)["coordsys"].string();
  1225 + (*this)["xref"].real();
  1226 + (*this)["yref"].real();
  1227 + if ((*this)["srcshape"].string() == "CIRCLE"){
  1228 + (*this)["rad"].real();
  1229 + }
  1230 + if ((*this)["bkgmethod"].string() == "REFLECTED"){
  1231 + (*this)["bkgregmin"].integer();
  1232 + }
  1233 + (*this)["maxoffset"].real();
  1234 + (*this)["etruemin"].real();
  1235 + (*this)["etruemax"].real();
  1236 + (*this)["etruebins"].integer();
  1237 +
  1238 + } // endif: method was ONOFF
  1239 +
  1240 + // Return onoff flag
  1241 + return onoff;
  1242 +}
  1243 +
  1244 +
  1245 +/***********************************************************************//**
1198 * @brief Set response for all CTA observations in container 1246 * @brief Set response for all CTA observations in container
1199 * 1247 *
1200 * @param[in,out] obs Observation container 1248 * @param[in,out] obs Observation container
src/support/ctool.hpp
@@ -104,6 +104,7 @@ protected: @@ -104,6 +104,7 @@ protected:
104 104
105 // Protected methods that query user parameters 105 // Protected methods that query user parameters
106 bool is_stacked(void); 106 bool is_stacked(void);
  107 + bool is_onoff(void);
107 108
108 // Protected methods for logging 109 // Protected methods for logging
109 void log_observations(const GChatter& chatter, 110 void log_observations(const GChatter& chatter,
test/data/crab_offaxis1.fits.gz
No preview for this file type
test/data/crab_onoff.xml 0 → 100644
  1 +<?xml version="1.0" standalone="no"?>
  2 +<source_library title="source library">
  3 + <source name="Crab" type="PointSource">
  4 + <spectrum type="PowerLaw">
  5 + <parameter name="Prefactor" scale="1e-16" value="5.7" min="1e-07" max="1000.0" free="1"/>
  6 + <parameter name="Index" scale="-1" value="2.48" min="0.0" max="+5.0" free="1"/>
  7 + <parameter name="PivotEnergy" scale="1e6" value="0.3" min="0.01" max="1000.0" free="0"/>
  8 + </spectrum>
  9 + <spatialModel type="PointSource">
  10 + <parameter name="RA" scale="1.0" value="83.6331" min="-360" max="360" free="0"/>
  11 + <parameter name="DEC" scale="1.0" value="22.0145" min="-90" max="90" free="0"/>
  12 + </spatialModel>
  13 + </source>
  14 + <source name="Background" type="RadialAcceptance" instrument="CTAOnOff">
  15 + <spectrum type="PowerLaw">
  16 + <parameter name="Prefactor" scale="1e-6" value="61.8" min="0.0" max="1000.0" free="1"/>
  17 + <parameter name="Index" scale="-1" value="1.85" min="0.0" max="+5.0" free="1"/>
  18 + <parameter name="PivotEnergy" scale="1e6" value="1.0" min="0.01" max="1000.0" free="0"/>
  19 + </spectrum>
  20 + <radialModel type="Gaussian">
  21 + <parameter name="Sigma" scale="1.0" value="3.0" min="0.01" max="10.0" free="1"/>
  22 + </radialModel>
  23 + </source>
  24 +</source_library>
test/test_cscript.py
@@ -455,6 +455,9 @@ class Test(test): @@ -455,6 +455,9 @@ class Test(test):
455 # Test is_stacked() method 455 # Test is_stacked() method
456 # TODO: implement 456 # TODO: implement
457 457
  458 + # Test is_onoff() method
  459 + # TODO: implement
  460 +
458 # Test set_response() method 461 # Test set_response() method
459 # TODO: implement 462 # TODO: implement
460 463
test/test_cslightcrv.py
@@ -42,6 +42,10 @@ class Test(test): @@ -42,6 +42,10 @@ class Test(test):
42 # Call base class constructor 42 # Call base class constructor
43 test.__init__(self) 43 test.__init__(self)
44 44
  45 + # Add off-axis events for classical analysis
  46 + self._offaxis_events = self._datadir + '/crab_offaxis1.fits'
  47 + self._model_onoff = self._datadir + '/crab_onoff.xml'
  48 +
45 # Return 49 # Return
46 return 50 return
47 51
@@ -73,7 +77,7 @@ class Test(test): @@ -73,7 +77,7 @@ class Test(test):
73 ' inmodel="'+self._model+'" srcname="Crab"'+ \ 77 ' inmodel="'+self._model+'" srcname="Crab"'+ \
74 ' caldb="'+self._caldb+'" irf="'+self._irf+'"'+ \ 78 ' caldb="'+self._caldb+'" irf="'+self._irf+'"'+ \
75 ' tbinalg="LIN" tmin="MJD 51544.50" tmax="MJD 51544.53"'+ \ 79 ' tbinalg="LIN" tmin="MJD 51544.50" tmax="MJD 51544.53"'+ \
76 - ' tbins=3 enumbins=0 emin=0.1 emax=100.0'+ \ 80 + ' tbins=3 method="3D" enumbins=0 emin=0.1 emax=100.0'+ \
77 ' outfile="lightcurve_cmd1.fits"'+ \ 81 ' outfile="lightcurve_cmd1.fits"'+ \
78 ' logfile="cslightcrv_cmd1.log" chatter=1' 82 ' logfile="cslightcrv_cmd1.log" chatter=1'
79 83
@@ -86,14 +90,14 @@ class Test(test): @@ -86,14 +90,14 @@ class Test(test):
86 'Check successful execution from command line') 90 'Check successful execution from command line')
87 91
88 # Check light curve 92 # Check light curve
89 - self._check_light_curve('lightcurve_cmd1.fits', 3) 93 + self._check_light_curve('lightcurve_cmd1.fits', 3, 1.6e-16)
90 94
91 # Setup cslightcrv command 95 # Setup cslightcrv command
92 cmd = cslightcrv+' inobs="events_that_do_not_exist.fits"'+ \ 96 cmd = cslightcrv+' inobs="events_that_do_not_exist.fits"'+ \
93 ' inmodel="'+self._model+'" srcname="Crab"'+ \ 97 ' inmodel="'+self._model+'" srcname="Crab"'+ \
94 ' caldb="'+self._caldb+'" irf="'+self._irf+'"'+ \ 98 ' caldb="'+self._caldb+'" irf="'+self._irf+'"'+ \
95 ' tbinalg="LIN" tmin="MJD 51544.50" tmax="MJD 51544.53"'+ \ 99 ' tbinalg="LIN" tmin="MJD 51544.50" tmax="MJD 51544.53"'+ \
96 - ' tbins=3 enumbins=0 emin=0.1 emax=100.0'+ \ 100 + ' tbins=3 method="3D" enumbins=0 emin=0.1 emax=100.0'+ \
97 ' outfile="lightcurve_cmd1.fits"'+ \ 101 ' outfile="lightcurve_cmd1.fits"'+ \
98 ' logfile="cslightcrv_cmd2.log" debug=yes' 102 ' logfile="cslightcrv_cmd2.log" debug=yes'
99 103
@@ -123,6 +127,7 @@ class Test(test): @@ -123,6 +127,7 @@ class Test(test):
123 lcrv['tmin'] = 'MJD 51544.50' 127 lcrv['tmin'] = 'MJD 51544.50'
124 lcrv['tmax'] = 'MJD 51544.53' 128 lcrv['tmax'] = 'MJD 51544.53'
125 lcrv['tbins'] = 3 129 lcrv['tbins'] = 3
  130 + lcrv['method'] = '3D'
126 lcrv['enumbins'] = 0 131 lcrv['enumbins'] = 0
127 lcrv['emin'] = 0.1 132 lcrv['emin'] = 0.1
128 lcrv['emax'] = 100.0 133 lcrv['emax'] = 100.0
@@ -137,7 +142,7 @@ class Test(test): @@ -137,7 +142,7 @@ class Test(test):
137 lcrv.save() 142 lcrv.save()
138 143
139 # Check light curve 144 # Check light curve
140 - self._check_light_curve('cslightcrv_py1.fits', 3) 145 + self._check_light_curve('cslightcrv_py1.fits', 3, 1.6e-16)
141 146
142 # Now use FILE as time bin algorithm. For this we need first to 147 # Now use FILE as time bin algorithm. For this we need first to
143 # create an ASCII file. We use now 6 time bins. The ASCII file 148 # create an ASCII file. We use now 6 time bins. The ASCII file
@@ -159,6 +164,7 @@ class Test(test): @@ -159,6 +164,7 @@ class Test(test):
159 lcrv['irf'] = self._irf 164 lcrv['irf'] = self._irf
160 lcrv['tbinalg'] = 'FILE' 165 lcrv['tbinalg'] = 'FILE'
161 lcrv['tbinfile'] = 'cslightcrv_py2.dat' 166 lcrv['tbinfile'] = 'cslightcrv_py2.dat'
  167 + lcrv['method'] = '3D'
162 lcrv['enumbins'] = 0 168 lcrv['enumbins'] = 0
163 lcrv['emin'] = 0.1 169 lcrv['emin'] = 0.1
164 lcrv['emax'] = 100.0 170 lcrv['emax'] = 100.0
@@ -171,7 +177,7 @@ class Test(test): @@ -171,7 +177,7 @@ class Test(test):
171 lcrv.execute() 177 lcrv.execute()
172 178
173 # Check light curve 179 # Check light curve
174 - self._check_light_curve('cslightcrv_py2.fits', 2) 180 + self._check_light_curve('cslightcrv_py2.fits', 2, 1.6e-16)
175 181
176 # Now we setup an observation container on input. We attached the 182 # Now we setup an observation container on input. We attached the
177 # model to the observation container so that cslightcrv should 183 # model to the observation container so that cslightcrv should
@@ -188,6 +194,7 @@ class Test(test): @@ -188,6 +194,7 @@ class Test(test):
188 lcrv['caldb'] = self._caldb 194 lcrv['caldb'] = self._caldb
189 lcrv['irf'] = self._irf 195 lcrv['irf'] = self._irf
190 lcrv['tbinalg'] = 'GTI' 196 lcrv['tbinalg'] = 'GTI'
  197 + lcrv['method'] = '3D'
191 lcrv['enumbins'] = 0 198 lcrv['enumbins'] = 0
192 lcrv['emin'] = 0.1 199 lcrv['emin'] = 0.1
193 lcrv['emax'] = 100.0 200 lcrv['emax'] = 100.0
@@ -199,9 +206,9 @@ class Test(test): @@ -199,9 +206,9 @@ class Test(test):
199 lcrv.execute() 206 lcrv.execute()
200 207
201 # Check light curve 208 # Check light curve
202 - self._check_light_curve('cslightcrv_py3.fits', 1) 209 + self._check_light_curve('cslightcrv_py3.fits', 1, 1.6e-16)
203 210
204 - # Finally we set-up a binned cslightcrv 211 + # Binned cslightcrv
205 lcrv = cscripts.cslightcrv() 212 lcrv = cscripts.cslightcrv()
206 lcrv['inobs'] = self._events 213 lcrv['inobs'] = self._events
207 lcrv['inmodel'] = self._model 214 lcrv['inmodel'] = self._model
@@ -212,6 +219,7 @@ class Test(test): @@ -212,6 +219,7 @@ class Test(test):
212 lcrv['tmin'] = 'MJD 51544.50' 219 lcrv['tmin'] = 'MJD 51544.50'
213 lcrv['tmax'] = 'MJD 51544.53' 220 lcrv['tmax'] = 'MJD 51544.53'
214 lcrv['tbins'] = 2 221 lcrv['tbins'] = 2
  222 + lcrv['method'] = '3D'
215 lcrv['emin'] = 0.1 223 lcrv['emin'] = 0.1
216 lcrv['emax'] = 100.0 224 lcrv['emax'] = 100.0
217 lcrv['enumbins'] = 10 225 lcrv['enumbins'] = 10
@@ -230,13 +238,43 @@ class Test(test): @@ -230,13 +238,43 @@ class Test(test):
230 lcrv.execute() 238 lcrv.execute()
231 239
232 # Check light curve 240 # Check light curve
233 - self._check_light_curve('cslightcrv_py4.fits', 2) 241 + self._check_light_curve('cslightcrv_py4.fits', 2, 1.6e-16)
  242 +
  243 + # cslightcrv with classical analysis
  244 + lcrv = cscripts.cslightcrv()
  245 + lcrv['inobs'] = self._offaxis_events
  246 + lcrv['inmodel'] = self._model_onoff
  247 + lcrv['srcname'] = 'Crab'
  248 + lcrv['caldb'] = self._caldb
  249 + lcrv['irf'] = self._irf
  250 + lcrv['tbinalg'] = 'LIN'
  251 + lcrv['tmin'] = 'MJD 58849.0008'
  252 + lcrv['tmax'] = 'MJD 58849.0112'
  253 + lcrv['tbins'] = 2
  254 + lcrv['method'] = 'ONOFF'
  255 + lcrv['emin'] = 0.1
  256 + lcrv['emax'] = 100.0
  257 + lcrv['enumbins'] = 10
  258 + lcrv['coordsys'] = 'CEL'
  259 + lcrv['xref'] = 83.63
  260 + lcrv['yref'] = 22.01
  261 + lcrv['rad'] = 0.2
  262 + lcrv['statistic'] = 'WSTAT'
  263 + lcrv['outfile'] = 'cslightcrv_py5.fits'
  264 + lcrv['logfile'] = 'cslightcrv_py5.log'
  265 + lcrv['chatter'] = 4
  266 +
  267 + # Execute cslightcrv script
  268 + lcrv.execute()
  269 +
  270 + # Check light curve
  271 + self._check_light_curve('cslightcrv_py5.fits', 2, 6.e-16)
234 272
235 # Return 273 # Return
236 return 274 return
237 275
238 # Check light curve result file 276 # Check light curve result file
239 - def _check_light_curve(self, filename, bins): 277 + def _check_light_curve(self, filename, bins,pref):
240 """ 278 """
241 Check light curve file 279 Check light curve file
242 """ 280 """
@@ -266,6 +304,13 @@ class Test(test): @@ -266,6 +304,13 @@ class Test(test):
266 self.test_assert(table.contains(col), 304 self.test_assert(table.contains(col),
267 'FITS file contains "'+col+'" column') 305 'FITS file contains "'+col+'" column')
268 306
  307 + # Check that table has been filled
  308 + # Prefactor has right order of magnitude
  309 + for s in range(table.nrows()):
  310 + self.test_value(table['Prefactor'][s], pref, 0.2*pref,
  311 + 'Check prefactor value')
  312 +
  313 +
269 # Close FITS file 314 # Close FITS file
270 fits.close() 315 fits.close()
271 316
test/test_csphasecrv.py
@@ -44,6 +44,8 @@ class Test(test): @@ -44,6 +44,8 @@ class Test(test):
44 44
45 # Set test data 45 # Set test data
46 self._phased_events = self._datadir + '/phased_events.fits' 46 self._phased_events = self._datadir + '/phased_events.fits'
  47 + self._offaxis_events = self._datadir + '/crab_offaxis1.fits'
  48 + self._model_onoff = self._datadir + '/crab_onoff.xml'
47 49
48 # Return 50 # Return
49 return 51 return
@@ -75,7 +77,7 @@ class Test(test): @@ -75,7 +77,7 @@ class Test(test):
75 cmd = csphasecrv+' inobs="'+self._phased_events+'"'+ \ 77 cmd = csphasecrv+' inobs="'+self._phased_events+'"'+ \
76 ' inmodel="'+self._model+'" srcname="Crab"'+ \ 78 ' inmodel="'+self._model+'" srcname="Crab"'+ \
77 ' caldb="'+self._caldb+'" irf="'+self._irf+'"'+ \ 79 ' caldb="'+self._caldb+'" irf="'+self._irf+'"'+ \
78 - ' phbinalg="LIN" phbins=5'+ \ 80 + ' phbinalg="LIN" phbins=5 method="3D"'+ \
79 ' enumbins=0 emin=0.1 emax=100.0'+ \ 81 ' enumbins=0 emin=0.1 emax=100.0'+ \
80 ' outfile="csphasecrv_cmd1.fits"'+ \ 82 ' outfile="csphasecrv_cmd1.fits"'+ \
81 ' logfile="csphasecrv_cmd1.log" chatter=1' 83 ' logfile="csphasecrv_cmd1.log" chatter=1'
@@ -95,7 +97,7 @@ class Test(test): @@ -95,7 +97,7 @@ class Test(test):
95 cmd = csphasecrv+' inobs="events_that_do_not_exist.fits"'+ \ 97 cmd = csphasecrv+' inobs="events_that_do_not_exist.fits"'+ \
96 ' inmodel="'+self._model+'" srcname="Crab"'+ \ 98 ' inmodel="'+self._model+'" srcname="Crab"'+ \
97 ' caldb="'+self._caldb+'" irf="'+self._irf+'"'+ \ 99 ' caldb="'+self._caldb+'" irf="'+self._irf+'"'+ \
98 - ' phbinalg="LIN" phbins=5'+ \ 100 + ' phbinalg="LIN" phbins=5 method="3D"'+ \
99 ' enumbins=0 emin=0.1 emax=100.0'+ \ 101 ' enumbins=0 emin=0.1 emax=100.0'+ \
100 ' outfile="csphasecrv_cmd2.fits"'+ \ 102 ' outfile="csphasecrv_cmd2.fits"'+ \
101 ' logfile="csphasecrv_cmd2.log" debug=yes chatter=1' 103 ' logfile="csphasecrv_cmd2.log" debug=yes chatter=1'
@@ -124,6 +126,7 @@ class Test(test): @@ -124,6 +126,7 @@ class Test(test):
124 pcrv['irf'] = self._irf 126 pcrv['irf'] = self._irf
125 pcrv['phbinalg'] = 'LIN' 127 pcrv['phbinalg'] = 'LIN'
126 pcrv['phbins'] = 5 128 pcrv['phbins'] = 5
  129 + pcrv['method'] = '3D'
127 pcrv['enumbins'] = 0 130 pcrv['enumbins'] = 0
128 pcrv['emin'] = 0.1 131 pcrv['emin'] = 0.1
129 pcrv['emax'] = 100.0 132 pcrv['emax'] = 100.0
@@ -160,6 +163,7 @@ class Test(test): @@ -160,6 +163,7 @@ class Test(test):
160 pcrv['irf'] = self._irf 163 pcrv['irf'] = self._irf
161 pcrv['phbinalg'] = 'FILE' 164 pcrv['phbinalg'] = 'FILE'
162 pcrv['phbinfile'] = 'csphasecrv_py2.dat' 165 pcrv['phbinfile'] = 'csphasecrv_py2.dat'
  166 + pcrv['method'] = '3D'
163 pcrv['enumbins'] = 0 167 pcrv['enumbins'] = 0
164 pcrv['emin'] = 0.1 168 pcrv['emin'] = 0.1
165 pcrv['emax'] = 100.0 169 pcrv['emax'] = 100.0
@@ -190,6 +194,7 @@ class Test(test): @@ -190,6 +194,7 @@ class Test(test):
190 pcrv['irf'] = self._irf 194 pcrv['irf'] = self._irf
191 pcrv['phbinalg'] = 'LIN' 195 pcrv['phbinalg'] = 'LIN'
192 pcrv['phbins'] = 5 196 pcrv['phbins'] = 5
  197 + pcrv['method'] = '3D'
193 pcrv['enumbins'] = 0 198 pcrv['enumbins'] = 0
194 pcrv['emin'] = 0.1 199 pcrv['emin'] = 0.1
195 pcrv['emax'] = 100.0 200 pcrv['emax'] = 100.0
@@ -204,7 +209,7 @@ class Test(test): @@ -204,7 +209,7 @@ class Test(test):
204 # Check phase curve 209 # Check phase curve
205 self._check_phase_curve('csphasecrv_py3.fits', 5) 210 self._check_phase_curve('csphasecrv_py3.fits', 5)
206 211
207 - # Finally we set-up a binned csphasecrv 212 + # Binned csphasecrv
208 pcrv = cscripts.csphasecrv() 213 pcrv = cscripts.csphasecrv()
209 pcrv['inobs'] = self._phased_events 214 pcrv['inobs'] = self._phased_events
210 pcrv['inmodel'] = self._model 215 pcrv['inmodel'] = self._model
@@ -213,6 +218,7 @@ class Test(test): @@ -213,6 +218,7 @@ class Test(test):
213 pcrv['irf'] = self._irf 218 pcrv['irf'] = self._irf
214 pcrv['phbinalg'] = 'LIN' 219 pcrv['phbinalg'] = 'LIN'
215 pcrv['phbins'] = 2 220 pcrv['phbins'] = 2
  221 + pcrv['method'] = '3D'
216 pcrv['emin'] = 0.1 222 pcrv['emin'] = 0.1
217 pcrv['emax'] = 100.0 223 pcrv['emax'] = 100.0
218 pcrv['enumbins'] = 10 224 pcrv['enumbins'] = 10
@@ -234,6 +240,35 @@ class Test(test): @@ -234,6 +240,35 @@ class Test(test):
234 # Check phase curve 240 # Check phase curve
235 self._check_phase_curve('csphasecrv_py4.fits', 2) 241 self._check_phase_curve('csphasecrv_py4.fits', 2)
236 242
  243 + # On/Off csphasecrv
  244 + pcrv = cscripts.csphasecrv()
  245 + pcrv['inobs'] = self._offaxis_events
  246 + pcrv['inmodel'] = self._model_onoff
  247 + pcrv['srcname'] = 'Crab'
  248 + pcrv['caldb'] = self._caldb
  249 + pcrv['irf'] = self._irf
  250 + pcrv['phbinalg'] = 'LIN'
  251 + pcrv['phbins'] = 2
  252 + pcrv['method'] = 'ONOFF'
  253 + pcrv['statistic'] = 'WSTAT'
  254 + pcrv['emin'] = 0.1
  255 + pcrv['emax'] = 100.0
  256 + pcrv['enumbins'] = 10
  257 + pcrv['coordsys'] = 'CEL'
  258 + pcrv['xref'] = 83.63
  259 + pcrv['yref'] = 22.01
  260 + pcrv['rad'] = 0.2
  261 + pcrv['outfile'] = 'csphasecrv_py5.fits'
  262 + pcrv['logfile'] = 'csphasecrv_py5.log'
  263 + pcrv['chatter'] = 4
  264 +
  265 + # Execute csphasecrv script
  266 + pcrv.logFileOpen() # Make sure we get a log file
  267 + pcrv.execute()
  268 +
  269 + # Check phase curve
  270 + self._check_phase_curve('csphasecrv_py5.fits', 2)
  271 +
237 # Return 272 # Return
238 return 273 return
239 274
@@ -267,6 +302,12 @@ class Test(test): @@ -267,6 +302,12 @@ class Test(test):
267 self.test_assert(table.contains(col), 302 self.test_assert(table.contains(col),
268 'FITS file contains "'+col+'" column') 303 'FITS file contains "'+col+'" column')
269 304
  305 + # Check that table has been filled
  306 + # Prefactor has right order of magnitude
  307 + for s in range(table.nrows()):
  308 + self.test_value(table['Prefactor'][s], 4.e-16, 3.e-16,
  309 + 'Check prefactor value')
  310 +
270 # Close FITS file 311 # Close FITS file
271 fits.close() 312 fits.close()
272 313