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 3 * Version 1.5.0 released
4 4 ========================
5 5  
  6 + Handle On/Off observations by cspull (#2260)
6 7 Add "statistic" parameter to all likelihood tools and scripts (#2237)
7 8 Rename ctlike "stat" parameter to "statistic" (#2237)
8 9 Enable handling of On/Off observations in csspec (#2231)
... ...
1 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 6 Introduction
... ... @@ -227,6 +227,10 @@ curve generation (#2237).
227 227  
228 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 234 The script now derived from the csobservation base class. A "statistic"
231 235 parameter was added to allow setting the optimizer statistic for the pull
232 236 distribution generation (#2237).
... ...
cscripts/cspull.par
... ... @@ -18,6 +18,8 @@
18 18 #============================
19 19 inobs, f, h, NONE,,, "Input event list, counts cube, or observation definition XML file"
20 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 23 caldb, s, a, prod2,,, "Calibration database"
22 24 irf, s, a, South_0.5h,,, "Instrument response function"
23 25 edisp, b, h, no,,, "Apply energy dispersion?"
... ...
cscripts/cspull.py
... ... @@ -40,18 +40,6 @@ class cspull(ctools.csobservation):
40 40 # Initialise application by calling the appropriate class constructor
41 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 43 # Return
56 44 return
57 45  
... ... @@ -62,7 +50,7 @@ class cspull(ctools.csobservation):
62 50 Get parameters from parfile
63 51 """
64 52 # If there are no observations in container then get some ...
65   - if self.obs().size() == 0:
  53 + if self.obs().is_empty():
66 54 self.obs(self._get_observations())
67 55  
68 56 # ... otherwise add response information and energy boundaries
... ... @@ -73,25 +61,26 @@ class cspull(ctools.csobservation):
73 61 # Set observation statistic
74 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 75 # Set models if we have none
87   - if self.obs().models().size() == 0:
  76 + if self.obs().models().is_empty():
88 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 85 # Query some parameters
97 86 self['outfile'].filename()
... ... @@ -118,14 +107,19 @@ class cspull(ctools.csobservation):
118 107 Summary string
119 108 """
120 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 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 124 # Compose summary string
131 125 if events > 0:
... ... @@ -151,38 +145,55 @@ class cspull(ctools.csobservation):
151 145 Dictionary of results
152 146 """
153 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 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 183 # Simulate events
167 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 193 # Determine number of events in simulation
183 194 nevents = 0.0
184 195 for run in obs:
185   - nevents += run.events().number()
  196 + nevents += run.nobserved()
186 197  
187 198 # Write simulation results
188 199 self._log_header3(gammalib.NORMAL, 'Simulation')
... ... @@ -200,15 +211,15 @@ class cspull(ctools.csobservation):
200 211 for model in models:
201 212 like = ctools.cterror(obs)
202 213 like['srcname'] = model.name()
203   - like['edisp'] = self._edisp
  214 + like['edisp'] = self['edisp'].boolean()
204 215 like['debug'] = self._logDebug()
205   - like['chatter'] = self._chatter
  216 + like['chatter'] = self['chatter'].integer()
206 217 like.run()
207 218 else:
208 219 like = ctools.ctlike(obs)
209   - like['edisp'] = self._edisp
  220 + like['edisp'] = self['edisp'].boolean()
210 221 like['debug'] = self._logDebug()
211   - like['chatter'] = self._chatter
  222 + like['chatter'] = self['chatter'].integer()
212 223 like.run()
213 224  
214 225 # Store results
... ... @@ -217,8 +228,7 @@ class cspull(ctools.csobservation):
217 228 models = like.obs().models()
218 229  
219 230 # Write result header
220   - if self._logNormal():
221   - self._log.header3('Pulls')
  231 + self._log_header3(gammalib.NORMAL, 'Pulls')
222 232  
223 233 # Gather results in form of a list of result columns and a
224 234 # dictionary containing the results. The result contains the
... ... @@ -294,10 +304,10 @@ class cspull(ctools.csobservation):
294 304 self._log_header1(gammalib.TERSE, 'Generate pull distribution')
295 305  
296 306 # Loop over trials
297   - for seed in range(self._ntrials):
  307 + for seed in range(self['ntrials'].integer()):
298 308  
299 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 312 # Write out trial result
303 313 ioutils.write_csv_row(self['outfile'].filename().url(), seed,
... ...
cscripts/obsutils.py
... ... @@ -20,13 +20,14 @@
20 20 import math
21 21 import gammalib
22 22 import ctools
  23 +import cscripts
23 24  
24 25  
25 26 # ===================== #
26 27 # Simulate observations #
27 28 # ===================== #
28 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 31 binsz=0.05, npix=200, proj='TAN', coord='GAL'):
31 32 """
32 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 60 Maximum energy of counts cube for binned (TeV)
60 61 nbins : int, optional
61 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 67 addbounds : bool, optional
63 68 Add boundaries at observation energies
64 69 binsz : float, optional
... ... @@ -105,61 +110,97 @@ def sim(obs, log=False, debug=False, chatter=2, edisp=False, seed=0,
105 110 emin = min(run.events().ebounds().emin().TeV(), emin)
106 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 205 else:
165 206  
... ...
doc/source/admin/release_history/1.5.rst
... ... @@ -23,6 +23,8 @@ Bug fixes
23 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 28 * [`2237 <https://cta-redmine.irap.omp.eu/issues/2237>`_] -
27 29 Add ``statistic`` parameters to :ref:`ctbutterfly`, :ref:`cterror`,
28 30 :ref:`cttsmap`, :ref:`ctulimit`, :ref:`cslightcrv, :ref:`csphasecrv`,
... ...