Commit 41c397c89510d1e413bbea3b148224ff1c67b9ca

Authored by Jürgen Knödlseder
1 parent b0341d4a

Handle On/Off observations in cspull (#2260)

ChangeLog
1 -2017-10-22 1 +2017-10-26
2 2
3 * Version 1.5.0 released 3 * Version 1.5.0 released
4 ======================== 4 ========================
5 5
  6 + Handle On/Off observations by cspull (#2260)
6 Add "statistic" parameter to all likelihood tools and scripts (#2237) 7 Add "statistic" parameter to all likelihood tools and scripts (#2237)
7 Rename ctlike "stat" parameter to "statistic" (#2237) 8 Rename ctlike "stat" parameter to "statistic" (#2237)
8 Enable handling of On/Off observations in csspec (#2231) 9 Enable handling of On/Off observations in csspec (#2231)
1 New Features and Important Changes in ctools 1.5.0 1 New Features and Important Changes in ctools 1.5.0
2 2
3 -21 October 2017 3 +26 October 2017
4 4
5 5
6 Introduction 6 Introduction
@@ -227,6 +227,10 @@ curve generation (#2237). @@ -227,6 +227,10 @@ curve generation (#2237).
227 227
228 cspull - Generate pull distributions 228 cspull - Generate pull distributions
229 ------------------------------------ 229 ------------------------------------
  230 +The script now handles On/Off observation. The parameters "onsrc" and "onrad"
  231 +were added to allow specifying the On region test source and On region radius
  232 +(#2260).
  233 +
230 The script now derived from the csobservation base class. A "statistic" 234 The script now derived from the csobservation base class. A "statistic"
231 parameter was added to allow setting the optimizer statistic for the pull 235 parameter was added to allow setting the optimizer statistic for the pull
232 distribution generation (#2237). 236 distribution generation (#2237).
cscripts/cspull.par
@@ -18,6 +18,8 @@ @@ -18,6 +18,8 @@
18 #============================ 18 #============================
19 inobs, f, h, NONE,,, "Input event list, counts cube, or observation definition XML file" 19 inobs, f, h, NONE,,, "Input event list, counts cube, or observation definition XML file"
20 inmodel, f, a, $CTOOLS/share/models/crab.xml,,, "Input model definition XML file" 20 inmodel, f, a, $CTOOLS/share/models/crab.xml,,, "Input model definition XML file"
  21 +onsrc, s, a, ,,, "Name of On source (only for On/Off analysis)"
  22 +onrad, r, a, 0.2,,, "Radius of On region (deg)"
21 caldb, s, a, prod2,,, "Calibration database" 23 caldb, s, a, prod2,,, "Calibration database"
22 irf, s, a, South_0.5h,,, "Instrument response function" 24 irf, s, a, South_0.5h,,, "Instrument response function"
23 edisp, b, h, no,,, "Apply energy dispersion?" 25 edisp, b, h, no,,, "Apply energy dispersion?"
cscripts/cspull.py
@@ -40,18 +40,6 @@ class cspull(ctools.csobservation): @@ -40,18 +40,6 @@ class cspull(ctools.csobservation):
40 # Initialise application by calling the appropriate class constructor 40 # Initialise application by calling the appropriate class constructor
41 self._init_csobservation(self.__class__.__name__, ctools.__version__, argv) 41 self._init_csobservation(self.__class__.__name__, ctools.__version__, argv)
42 42
43 - # Initialise some members  
44 - self._edisp = False  
45 - self._coordsys = 'CEL'  
46 - self._proj = 'TAN'  
47 - self._log_clients = False # Static parameter  
48 - self._ntrials = 0  
49 - self._npix = 0  
50 - self._binsz = 0.0  
51 - self._enumbins = 0  
52 - self._seed = 1  
53 - self._chatter = 2  
54 -  
55 # Return 43 # Return
56 return 44 return
57 45
@@ -62,7 +50,7 @@ class cspull(ctools.csobservation): @@ -62,7 +50,7 @@ class cspull(ctools.csobservation):
62 Get parameters from parfile 50 Get parameters from parfile
63 """ 51 """
64 # If there are no observations in container then get some ... 52 # If there are no observations in container then get some ...
65 - if self.obs().size() == 0: 53 + if self.obs().is_empty():
66 self.obs(self._get_observations()) 54 self.obs(self._get_observations())
67 55
68 # ... otherwise add response information and energy boundaries 56 # ... otherwise add response information and energy boundaries
@@ -73,25 +61,26 @@ class cspull(ctools.csobservation): @@ -73,25 +61,26 @@ class cspull(ctools.csobservation):
73 # Set observation statistic 61 # Set observation statistic
74 self._set_obs_statistic(gammalib.toupper(self['statistic'].string())) 62 self._set_obs_statistic(gammalib.toupper(self['statistic'].string()))
75 63
76 - # Get number of energy bins  
77 - self._enumbins = self['enumbins'].integer() 64 + # Query parameters for On/Off observation
  65 + if len(self['onsrc'].string()) > 0:
  66 + self['onrad'].real()
78 67
79 - # Read parameters for binned if requested  
80 - if self._enumbins != 0:  
81 - self._npix = self['npix'].integer()  
82 - self._binsz = self['binsz'].real()  
83 - self._coordsys = self['coordsys'].string()  
84 - self._proj = self['proj'].string() 68 + # Query parameters for binned if requested
  69 + elif self['enumbins'].integer() > 0:
  70 + self['npix'].integer()
  71 + self['binsz'].real()
  72 + self['coordsys'].string()
  73 + self['proj'].string()
85 74
86 # Set models if we have none 75 # Set models if we have none
87 - if self.obs().models().size() == 0: 76 + if self.obs().models().is_empty():
88 self.obs().models(self['inmodel'].filename()) 77 self.obs().models(self['inmodel'].filename())
89 78
90 - # Read other parameters  
91 - self._ntrials = self['ntrials'].integer()  
92 - self._edisp = self['edisp'].boolean()  
93 - self._seed = self['seed'].integer()  
94 - self._chatter = self['chatter'].integer() 79 + # Query other parameters
  80 + self['ntrials'].integer()
  81 + self['edisp'].boolean()
  82 + self['seed'].integer()
  83 + self['chatter'].integer()
95 84
96 # Query some parameters 85 # Query some parameters
97 self['outfile'].filename() 86 self['outfile'].filename()
@@ -118,14 +107,19 @@ class cspull(ctools.csobservation): @@ -118,14 +107,19 @@ class cspull(ctools.csobservation):
118 Summary string 107 Summary string
119 """ 108 """
120 # Extract information from observation 109 # Extract information from observation
121 - emin = obs.events().ebounds().emin().TeV()  
122 - emax = obs.events().ebounds().emax().TeV()  
123 - events = obs.events().number()  
124 - binned = (obs.events().classname() == 'GCTAEventCube')  
125 - if binned:  
126 - mode = 'binned' 110 + if obs.classname() == 'GCTAOnOffObservation':
  111 + emin = obs.on_spec().ebounds().emin().TeV()
  112 + emax = obs.on_spec().ebounds().emin().TeV()
  113 + mode = 'On/Off'
127 else: 114 else:
128 - mode = 'unbinned' 115 + emin = obs.events().ebounds().emin().TeV()
  116 + emax = obs.events().ebounds().emax().TeV()
  117 + binned = (obs.events().classname() == 'GCTAEventCube')
  118 + if binned:
  119 + mode = 'binned'
  120 + else:
  121 + mode = 'unbinned'
  122 + events = obs.nobserved()
129 123
130 # Compose summary string 124 # Compose summary string
131 if events > 0: 125 if events > 0:
@@ -151,38 +145,55 @@ class cspull(ctools.csobservation): @@ -151,38 +145,55 @@ class cspull(ctools.csobservation):
151 Dictionary of results 145 Dictionary of results
152 """ 146 """
153 # Write header 147 # Write header
154 - if self._logNormal():  
155 - self._log.header2('Trial '+str(seed-self._seed+1))  
156 -  
157 - # If we have a binned obeservation then specify the lower and  
158 - # upper energy limit in TeV  
159 - if self._enumbins > 0:  
160 - emin = self['emin'].real()  
161 - emax = self['emax'].real() 148 + self._log_header2(gammalib.NORMAL, 'Trial %d' %
  149 + (seed-self['seed'].integer()+1))
  150 +
  151 + # Get number of energy bins and On source name and initialise
  152 + # some parameters
  153 + nbins = self['enumbins'].integer()
  154 + onsrc = self['onsrc'].string()
  155 + emin = None
  156 + emax = None
  157 + binsz = 0.0
  158 + npix = 0
  159 + proj = 'TAN'
  160 + coordsys = 'CEL'
  161 +
  162 + # If we have a On source name then set On region radius
  163 + if len(onsrc) > 0:
  164 + onrad = self['onrad'].real()
  165 + emin = self['emin'].real()
  166 + emax = self['emax'].real()
162 else: 167 else:
163 - emin = None  
164 - emax = None 168 +
  169 + # Reset On region source name and radius
  170 + onrad = 0.0
  171 + onsrc = None
  172 +
  173 + # If we have a binned obeservation then specify the lower and
  174 + # upper energy limit in TeV
  175 + if nbins > 0:
  176 + emin = self['emin'].real()
  177 + emax = self['emax'].real()
  178 + binsz = self['binsz'].real()
  179 + npix = self['npix'].integer()
  180 + proj = self['proj'].string()
  181 + coordsys = self['coordsys'].string()
165 182
166 # Simulate events 183 # Simulate events
167 obs = obsutils.sim(self.obs(), 184 obs = obsutils.sim(self.obs(),
168 - emin=emin,  
169 - emax=emax,  
170 - nbins=self._enumbins,  
171 - addbounds=True,  
172 - seed=seed,  
173 - binsz=self._binsz,  
174 - npix=self._npix,  
175 - proj=self._proj,  
176 - coord=self._coordsys,  
177 - edisp=self._edisp,  
178 - log=self._log_clients,  
179 - debug=self._logDebug(),  
180 - chatter=self._chatter) 185 + emin=emin, emax=emax, nbins=nbins,
  186 + onsrc=onsrc, onrad=onrad,
  187 + addbounds=True, seed=seed,
  188 + binsz=binsz, npix=npix, proj=proj, coord=coordsys,
  189 + edisp=self['edisp'].boolean(),
  190 + log=False, debug=self._logDebug(),
  191 + chatter=self['chatter'].integer())
181 192
182 # Determine number of events in simulation 193 # Determine number of events in simulation
183 nevents = 0.0 194 nevents = 0.0
184 for run in obs: 195 for run in obs:
185 - nevents += run.events().number() 196 + nevents += run.nobserved()
186 197
187 # Write simulation results 198 # Write simulation results
188 self._log_header3(gammalib.NORMAL, 'Simulation') 199 self._log_header3(gammalib.NORMAL, 'Simulation')
@@ -200,15 +211,15 @@ class cspull(ctools.csobservation): @@ -200,15 +211,15 @@ class cspull(ctools.csobservation):
200 for model in models: 211 for model in models:
201 like = ctools.cterror(obs) 212 like = ctools.cterror(obs)
202 like['srcname'] = model.name() 213 like['srcname'] = model.name()
203 - like['edisp'] = self._edisp 214 + like['edisp'] = self['edisp'].boolean()
204 like['debug'] = self._logDebug() 215 like['debug'] = self._logDebug()
205 - like['chatter'] = self._chatter 216 + like['chatter'] = self['chatter'].integer()
206 like.run() 217 like.run()
207 else: 218 else:
208 like = ctools.ctlike(obs) 219 like = ctools.ctlike(obs)
209 - like['edisp'] = self._edisp 220 + like['edisp'] = self['edisp'].boolean()
210 like['debug'] = self._logDebug() 221 like['debug'] = self._logDebug()
211 - like['chatter'] = self._chatter 222 + like['chatter'] = self['chatter'].integer()
212 like.run() 223 like.run()
213 224
214 # Store results 225 # Store results
@@ -217,8 +228,7 @@ class cspull(ctools.csobservation): @@ -217,8 +228,7 @@ class cspull(ctools.csobservation):
217 models = like.obs().models() 228 models = like.obs().models()
218 229
219 # Write result header 230 # Write result header
220 - if self._logNormal():  
221 - self._log.header3('Pulls') 231 + self._log_header3(gammalib.NORMAL, 'Pulls')
222 232
223 # Gather results in form of a list of result columns and a 233 # Gather results in form of a list of result columns and a
224 # dictionary containing the results. The result contains the 234 # dictionary containing the results. The result contains the
@@ -294,10 +304,10 @@ class cspull(ctools.csobservation): @@ -294,10 +304,10 @@ class cspull(ctools.csobservation):
294 self._log_header1(gammalib.TERSE, 'Generate pull distribution') 304 self._log_header1(gammalib.TERSE, 'Generate pull distribution')
295 305
296 # Loop over trials 306 # Loop over trials
297 - for seed in range(self._ntrials): 307 + for seed in range(self['ntrials'].integer()):
298 308
299 # Make a trial and add initial seed 309 # Make a trial and add initial seed
300 - result = self._trial(seed + self._seed) 310 + result = self._trial(seed + self['seed'].integer())
301 311
302 # Write out trial result 312 # Write out trial result
303 ioutils.write_csv_row(self['outfile'].filename().url(), seed, 313 ioutils.write_csv_row(self['outfile'].filename().url(), seed,
cscripts/obsutils.py
@@ -20,13 +20,14 @@ @@ -20,13 +20,14 @@
20 import math 20 import math
21 import gammalib 21 import gammalib
22 import ctools 22 import ctools
  23 +import cscripts
23 24
24 25
25 # ===================== # 26 # ===================== #
26 # Simulate observations # 27 # Simulate observations #
27 # ===================== # 28 # ===================== #
28 def sim(obs, log=False, debug=False, chatter=2, edisp=False, seed=0, 29 def sim(obs, log=False, debug=False, chatter=2, edisp=False, seed=0,
29 - emin=None, emax=None, nbins=0, addbounds=False, 30 + emin=None, emax=None, nbins=0, onsrc=None, onrad=0.2, addbounds=False,
30 binsz=0.05, npix=200, proj='TAN', coord='GAL'): 31 binsz=0.05, npix=200, proj='TAN', coord='GAL'):
31 """ 32 """
32 Simulate events for all observations in the container 33 Simulate events for all observations in the container
@@ -59,6 +60,10 @@ def sim(obs, log=False, debug=False, chatter=2, edisp=False, seed=0, @@ -59,6 +60,10 @@ def sim(obs, log=False, debug=False, chatter=2, edisp=False, seed=0,
59 Maximum energy of counts cube for binned (TeV) 60 Maximum energy of counts cube for binned (TeV)
60 nbins : int, optional 61 nbins : int, optional
61 Number of energy bins (0=unbinned) 62 Number of energy bins (0=unbinned)
  63 + onsrc : str, optional
  64 + Name of source for On region (None if no On/Off obs. is used)
  65 + onrad : float, optional
  66 + Radius for On region (deg)
62 addbounds : bool, optional 67 addbounds : bool, optional
63 Add boundaries at observation energies 68 Add boundaries at observation energies
64 binsz : float, optional 69 binsz : float, optional
@@ -105,61 +110,97 @@ def sim(obs, log=False, debug=False, chatter=2, edisp=False, seed=0, @@ -105,61 +110,97 @@ def sim(obs, log=False, debug=False, chatter=2, edisp=False, seed=0,
105 emin = min(run.events().ebounds().emin().TeV(), emin) 110 emin = min(run.events().ebounds().emin().TeV(), emin)
106 emax = max(run.events().ebounds().emax().TeV(), emax) 111 emax = max(run.events().ebounds().emax().TeV(), emax)
107 112
108 - # Allocate ctbin application and set parameters  
109 - binning = ctools.ctbin(obssim.obs())  
110 - binning['ebinalg'] = 'LOG'  
111 - binning['emin'] = emin  
112 - binning['emax'] = emax  
113 - binning['enumbins'] = nbins  
114 - binning['usepnt'] = True # Use pointing for map centre  
115 - binning['nxpix'] = npix  
116 - binning['nypix'] = npix  
117 - binning['binsz'] = binsz  
118 - binning['coordsys'] = coord  
119 - binning['proj'] = proj  
120 - binning['chatter'] = chatter  
121 - binning['debug'] = debug  
122 -  
123 - # Optionally open the log file  
124 - if log:  
125 - binning.logFileOpen()  
126 -  
127 - # Run ctbin application. This will loop over all observations in  
128 - # the container and bin the events in counts maps  
129 - binning.run()  
130 -  
131 - # If we have multiple input observations then create stacked response  
132 - # cubes and append them to the observation  
133 - if len(obssim.obs()) > 1:  
134 -  
135 - # Get stacked response (use pointing for map centre)  
136 - response = get_stacked_response(obssim.obs(), None, None,  
137 - binsz=binsz, nxpix=npix, nypix=npix,  
138 - emin=emin, emax=emax, enumbins=nbins,  
139 - edisp=edisp,  
140 - coordsys=coord, proj=proj,  
141 - addbounds=addbounds,  
142 - log=log, debug=debug,  
143 - chatter=chatter)  
144 -  
145 - # Set stacked response  
146 - if edisp:  
147 - binning.obs()[0].response(response['expcube'],  
148 - response['psfcube'],  
149 - response['edispcube'],  
150 - response['bkgcube'])  
151 - else:  
152 - binning.obs()[0].response(response['expcube'],  
153 - response['psfcube'],  
154 - response['bkgcube'])  
155 -  
156 - # Set new models  
157 - binning.obs().models(response['models'])  
158 -  
159 - # Make a deep copy of the observation that will be returned  
160 - # (the ctbin object will go out of scope one the function is  
161 - # left)  
162 - obs = binning.obs().copy() 113 + # If a On source is specified then create On/Off observations
  114 + if onsrc != None:
  115 +
  116 + # Extract source position from model
  117 + model = obssim.obs().models()[onsrc]
  118 + ra = model['RA'].value()
  119 + dec = model['DEC'].value()
  120 +
  121 + # Allocate csphagen application and set parameters
  122 + phagen = cscripts.csphagen(obssim.obs())
  123 + phagen['ebinalg'] = 'LOG'
  124 + phagen['emin'] = emin
  125 + phagen['emax'] = emax
  126 + phagen['enumbins'] = nbins
  127 + phagen['coordsys'] = 'CEL'
  128 + phagen['ra'] = ra
  129 + phagen['dec'] = dec
  130 + phagen['rad'] = onrad
  131 + phagen['stack'] = False
  132 + phagen['inexclusion'] = 'NONE'
  133 +
  134 + # Optionally open the log file
  135 + if log:
  136 + phagen.logFileOpen()
  137 +
  138 + # Run csphagen application
  139 + phagen.run()
  140 +
  141 + # Make a deep copy of the observation that will be returned
  142 + # (the csphagen object will go out of scope one the function is
  143 + # left)
  144 + obs = phagen.obs().copy()
  145 +
  146 + # ... otherwise use binned observations
  147 + else:
  148 +
  149 + # Allocate ctbin application and set parameters
  150 + binning = ctools.ctbin(obssim.obs())
  151 + binning['ebinalg'] = 'LOG'
  152 + binning['emin'] = emin
  153 + binning['emax'] = emax
  154 + binning['enumbins'] = nbins
  155 + binning['usepnt'] = True # Use pointing for map centre
  156 + binning['nxpix'] = npix
  157 + binning['nypix'] = npix
  158 + binning['binsz'] = binsz
  159 + binning['coordsys'] = coord
  160 + binning['proj'] = proj
  161 + binning['chatter'] = chatter
  162 + binning['debug'] = debug
  163 +
  164 + # Optionally open the log file
  165 + if log:
  166 + binning.logFileOpen()
  167 +
  168 + # Run ctbin application. This will loop over all observations in
  169 + # the container and bin the events in counts maps
  170 + binning.run()
  171 +
  172 + # If we have multiple input observations then create stacked response
  173 + # cubes and append them to the observation
  174 + if len(obssim.obs()) > 1:
  175 +
  176 + # Get stacked response (use pointing for map centre)
  177 + response = get_stacked_response(obssim.obs(), None, None,
  178 + binsz=binsz, nxpix=npix, nypix=npix,
  179 + emin=emin, emax=emax, enumbins=nbins,
  180 + edisp=edisp,
  181 + coordsys=coord, proj=proj,
  182 + addbounds=addbounds,
  183 + log=log, debug=debug,
  184 + chatter=chatter)
  185 +
  186 + # Set stacked response
  187 + if edisp:
  188 + binning.obs()[0].response(response['expcube'],
  189 + response['psfcube'],
  190 + response['edispcube'],
  191 + response['bkgcube'])
  192 + else:
  193 + binning.obs()[0].response(response['expcube'],
  194 + response['psfcube'],
  195 + response['bkgcube'])
  196 +
  197 + # Set new models
  198 + binning.obs().models(response['models'])
  199 +
  200 + # Make a deep copy of the observation that will be returned
  201 + # (the ctbin object will go out of scope one the function is
  202 + # left)
  203 + obs = binning.obs().copy()
163 204
164 else: 205 else:
165 206
doc/source/admin/release_history/1.5.rst
@@ -23,6 +23,8 @@ Bug fixes @@ -23,6 +23,8 @@ Bug fixes
23 Improvements 23 Improvements
24 ------------ 24 ------------
25 25
  26 +* [`2260 <https://cta-redmine.irap.omp.eu/issues/2260>`_] -
  27 + Handle On/Off observations by :ref:`cspull`
26 * [`2237 <https://cta-redmine.irap.omp.eu/issues/2237>`_] - 28 * [`2237 <https://cta-redmine.irap.omp.eu/issues/2237>`_] -
27 Add ``statistic`` parameters to :ref:`ctbutterfly`, :ref:`cterror`, 29 Add ``statistic`` parameters to :ref:`ctbutterfly`, :ref:`cterror`,
28 :ref:`cttsmap`, :ref:`ctulimit`, :ref:`cslightcrv, :ref:`csphasecrv`, 30 :ref:`cttsmap`, :ref:`ctulimit`, :ref:`cslightcrv, :ref:`csphasecrv`,