Commit 92c018a72107f75eef8082c9df29a93ae1b711e5

Authored by ldivenere
2 parents 6acd8596 03b31792

Merge branch 'devel' of https://cta-gitlab.irap.omp.eu/ctools/ctools into devel

ChangeLog
1   -2018-01-25
  1 +2018-02-14
  2 +
  3 + * Version 1.6.0 released
  4 + ========================
  5 +
  6 + Allow specifying of input sky map in ctskymap (#2311)
  7 + Add ras() and decs() methods to csobsinfo (#2313)
  8 + Accelerate IRF and RING background methods in ctskymap (#2309)
  9 +
  10 +
  11 +2018-02-16
  12 +
  13 + * Bug fix version 1.5.1 released
  14 + ================================
  15 +
  16 + Take energy dispersion in csresspec modelling into account (#2341)
  17 + Enable using csphagen for HESS, VERITAS and MAGIC observations (#2307)
  18 +
2 19  
3 20 * Version 1.5.0 released
4 21 ========================
... ...
1   -25 January 2018
  1 +New Features and Important Changes in ctools 1.6.0
  2 +
  3 +14 February 2018
2 4  
3 5  
4 6 Introduction
... ... @@ -9,10 +11,7 @@ ctools release with respect to the precedent release.
9 11  
10 12 Configuration
11 13 -------------
12   -In case of an error, test output is now written into log file (#2264).
13   -
14   -The cscripts::execute() method no longer creates log files, making the
15   -cscripts behaviour compliant with that of ctools (#2182).
  14 +None
16 15  
17 16  
18 17 Python interface
... ... @@ -27,8 +26,7 @@ None
27 26  
28 27 Base classes
29 28 ------------
30   -Add ctools::time_reference constant and remove ctools::time_reference() method
31   -(#2295).
  29 +None
32 30  
33 31  
34 32 ctbin - CTA event binning
... ... @@ -43,9 +41,7 @@ None
43 41  
44 42 ctbutterfly - Generate butterfly diagrams
45 43 -----------------------------------------
46   -Add "statistic" parameter to allow setting of optimizer statistic (#2237).
47   -
48   -Add butterfly() method to return the butterfly diagram as CSV object (#1512).
  44 +None
49 45  
50 46  
51 47 ctcubemask - CTA cube filtering
... ... @@ -60,7 +56,7 @@ None
60 56  
61 57 cterror - Likelihood profile error computation
62 58 ----------------------------------------------
63   -Add "statistic" parameter to allow setting of optimizer statistic (#2237).
  59 +None
64 60  
65 61  
66 62 ctexpcube - CTA exposure cube generation
... ... @@ -70,7 +66,7 @@ None
70 66  
71 67 ctlike - Maximum likelihood fitting
72 68 -----------------------------------
73   -Rename "stats" parameter to "statistic" (#2237).
  69 +None
74 70  
75 71  
76 72 ctmapcube - CTA map cube generation
... ... @@ -80,22 +76,12 @@ None
80 76  
81 77 ctmodel - CTA counts cube modelling
82 78 -----------------------------------
83   -Use t-type parameters for "tmin" and "tmax" to all specifying times in various
84   -formats (#1864).
85   -
86   -The tool now also checks which models actually overlap with the RoI of a given
87   -observation and then only use these models to compute the model cube. The
88   -parallelization of the tool has also been optimised so that diffuse models are
89   -not loaded over and over again (#2189).
  79 +None
90 80  
91 81  
92 82 ctobssim - CTA observation simulation
93 83 -------------------------------------
94   -Use t-type parameters for "tmin" and "tmax" to all specifying times in various
95   -formats (#1864).
96   -
97   -Set the OBS_ID keyword to the number that is used for creating the FITS file
98   -(#1836).
  84 +None
99 85  
100 86  
101 87 ctphase - Event phase computation
... ... @@ -110,51 +96,47 @@ None
110 96  
111 97 ctpsfcube - CTA point spread function cube generation
112 98 -----------------------------------------------------
113   -The chatter level is now correctly forwarded to the PSF cube fill method (#2262).
  99 +None
114 100  
115 101  
116 102 ctselect - CTA event selection
117 103 ------------------------------
118   -Any .gz suffix is now correctly stripped from the event filenames when creating
119   -the output filenames (#2287).
120   -
121   -ctselect now informs about non-overlapping time interval; before the time interval
122   -was set to [0,0] which was disturbing and eventually incorrect (#2272).
123   -
124   -Use t-type parameters for "tmin" and "tmax" to all specifying times in various
125   -formats (#1864).
  104 +None
126 105  
127 106  
128 107 ctskymap - CTA sky mapping
129 108 --------------------------
130   -Header keywords informing about the dates and times covered by the observations
131   -used to generate the skymap, as well as the event energy range, are now appended
132   -as header keywords to the FITS file (#2194).
133   -
134   -Add the RING background method, which is a classical analysis methods for IACT
135   -data analysis, for background subtraction (#2207).
  109 +Add the "inmap", "iterations" and "threshold" parameters. The hidden
  110 +"inmap" parameter allows specifying a sky map file containing a "COUNTS"
  111 +and "ACCEPTANCE" extension which avoids looping over event lists in an
  112 +observation container. The "iterations" parameter allows the iterative
  113 +determination of an exclusion map from the resulting significance map for
  114 +the RING background method. The exclusion map will flag all pixels with
  115 +significances above the "threshold" value so that they will be excluded
  116 +in the subsequent RING background determination. Since this will impact
  117 +the pixel significances, the procedure needs to be repeated a couple of
  118 +times to stabilise (#2311).
  119 +
  120 +Accelerate the IRF and RING background methods. For the RING background
  121 +method the computation is either performed using a FFT or using a direct
  122 +computation. The FFT computation is faster, but assumed Euclidian
  123 +distances for the sky map grid, the direct computation is slower, but
  124 +accurately computes the distances between sky map pixels (#2309).
136 125  
137 126  
138 127 cttsmap - Generation of Test Statistic map
139 128 ------------------------------------------
140   -Add "statistic" parameter to allow setting of optimizer statistic (#2237).
  129 +None
141 130  
142 131  
143 132 ctulimit - Compute upper limit
144 133 ------------------------------
145   -The tool now also computes correct upper flux limits for diffuse map cubes
146   -for which the spectral component is a normalization of the spectral information
147   -available in the map cube (#2298).
148   -
149   -Add "statistic" parameter to allow setting of optimizer statistic (#2237).
  134 +None
150 135  
151 136  
152 137 support - Support classes
153 138 -------------------------
154   -Change DATE_OBS, DATE_END, TIME_OBS and TIME_END FITS keywords into DATA-OBS,
155   -DATE-END, TIME-OBS and TIME-END.
156   -
157   -Add ctobservation.obs() method to set an observation container.
  139 +None
158 140  
159 141  
160 142 cscaldb - Inspect calibration database
... ... @@ -189,16 +171,7 @@ None
189 171  
190 172 cslightcrv - Generate light curve
191 173 ---------------------------------
192   -The script now also supports the handling of On/Off observations. For that
193   -purpose parameters were added that are needed for the execution of the
194   -csphagen script (#2273).
195   -
196   -The script now derives from the csobservation base class. A "statistic"
197   -parameter was added to allow setting the optimizer statistic for the light
198   -curve generation (#2237).
199   -
200   -Use t-type parameters for "tmin" and "tmax" to all specifying times in various
201   -formats (#1864).
  174 +None
202 175  
203 176  
204 177 csmodelinfo - Return model information
... ... @@ -218,8 +191,7 @@ None
218 191  
219 192 csmodelsois - Puts selected sources in diffuse model cube
220 193 ---------------------------------------------------------
221   -Add script that replaces selected models in a model container by a diffuse
222   -map cube (#2180).
  194 +None
223 195  
224 196  
225 197 csobs2caldb - Generate calibration database entry for an IACT observation
... ... @@ -234,47 +206,27 @@ None
234 206  
235 207 csobsinfo - Return observation definition information
236 208 -----------------------------------------------------
237   -None
  209 +Add ras() and decs() methods (#2313)
238 210  
239 211  
240 212 csobsselect - Select observations
241 213 ---------------------------------
242   -Add tmin and tmax parameters to allow selection of observation by time (#2297).
  214 +None
243 215  
244 216  
245 217 csphagen - Generate On/Off observations
246 218 ---------------------------------------
247   -The CUSTOM background method was added to allow specifying On and Off regions
248   -from a region file or FITS WCS map. For multiple observations, a custom Off
249   -region per observation can be specified in the observation definition XML file
250   -using the OffRegions parameter (#2284).
251   -
252   -The script was added to allow the generation of PHA, ARF and RMF datasets for classical On/Off IACT analysis (#2208).
  219 +None
253 220  
254 221  
255 222 csphasecrv - Generate phase curve
256 223 ---------------------------------
257   -The script now also supports the handling of On/Off observations. For that
258   -purpose parameters were added that are needed for the execution of the
259   -csphagen script (#2273).
260   -
261   -The script now derives from the csobservation base class. A "statistic"
262   -parameter was added to allow setting the optimizer statistic for the light
263   -curve generation (#2237).
  224 +None
264 225  
265 226  
266 227 cspull - Generate pull distributions
267 228 ------------------------------------
268   -The script now handles On/Off observation. The parameters "onsrc" and "onrad"
269   -were added to allow specifying the On region test source and On region radius
270   -(#2260).
271   -
272   -The script now derives from the csobservation base class. A "statistic"
273   -parameter was added to allow setting the optimizer statistic for the pull
274   -distribution generation (#2237).
275   -
276   -Use t-type parameters for "tmin" and "tmax" to all specifying times in various
277   -formats (#1864).
  229 +None
278 230  
279 231  
280 232 csresmap - Generate residual map
... ... @@ -284,7 +236,7 @@ None
284 236  
285 237 csresspec - Generate residual spectrum
286 238 --------------------------------------
287   -Add script to generate a residual spectrum (#2289)
  239 +None
288 240  
289 241  
290 242 csroot2caldb - Generates calibration database from ROOT file
... ... @@ -294,25 +246,12 @@ None
294 246  
295 247 cssens - Computes sensitivity curve
296 248 -----------------------------------
297   -The script now derived from the csobservation base class. A "statistic"
298   -parameter was added to allow setting the optimizer statistic for the sensitivity
299   -computation (#2237).
  249 +None
300 250  
301 251  
302 252 csspec - Generates Spectral Energy Distribution
303 253 -----------------------------------------------
304   -The script now derives from the csobservation base class. A "statistic"
305   -parameter was added to allow setting the optimizer statistic for the spectrum
306   -generation (#2237).
307   -
308   -The script now can also handle On/Off spectra for SED generation (#2231).
309   -
310   -The script now can also handle non-CTA data. For that purpose a "method"
311   -parameter has been introduced that allows to selected between slicing of the data
312   -(method="SLICE"), which is only supported for CTA observations, and replacing of
313   -the spectral model by a node function (method="NODES"). If method="AUTO" the tool
314   -will use "SLICE" for observations containing only CTA data and "NODES" for observations
315   -containing other data (#1927).
  254 +None
316 255  
317 256  
318 257 cssrcdetect - Detects sources in sky map
... ... @@ -322,18 +261,7 @@ None
322 261  
323 262 cstsdist - Computes Test Statistic distribution
324 263 -----------------------------------------------
325   -The script now derives from the csobservation base class. A "statistic"
326   -parameter was added to allow setting the optimizer statistic for the Test
327   -Statistic distribution generation (#2237).
328   -
329   -The parameter interface was considerably revised, in particular the following
330   -parameters were removed: "ra", "dec", "rad", "coordsys", "proj", "emin",
331   -"emax", "enumbins", "tmin", "tmax", "npix" and "binsz". The "inobs" parameter
332   -is now always queried. The script now reads in an observation or an observation
333   -container and derives the TS distribution for a source based on that observation
334   -container. So far the script supports unbinned, binned and stacked observations.
335   -If an observation definition XML file is specified the script assumes that all
336   -observations are event lists (#1567).
  264 +None
337 265  
338 266  
339 267 cstsmapmerge - Merge Test Statistic maps
... ... @@ -343,14 +271,12 @@ None
343 271  
344 272 cstsmapsplit - Prepare split Test Statistic computation
345 273 -------------------------------------------------------
346   -A "statistic" parameter was added to allow setting the optimizer statistic for
347   -the TS map generation (#2237).
  274 +None
348 275  
349 276  
350 277 csviscube - Generate visibility cube
351 278 ------------------------------------
352   -Use t-type parameters for "tmin" and "tmax" to all specifying times in various
353   -formats (#1864).
  279 +None
354 280  
355 281  
356 282 csworkflow - Execute workflow
... ...
README.md
1 1 ctools information
2 2 ==================
3   -* Version: 1.6.0.dev1 (1 August 2017)
  3 +* Version: 1.6.0.dev1
4 4 * Author: Juergen Knoedlseder (jurgen.knodlseder@irap.omp.eu)
5 5 * GammaLib dependency: 1.6.0.dev1
6 6  
... ...
caldb/data/cta/prod2/bcf/North_0.5h/irf_file.fits.gz
No preview for this file type
caldb/data/cta/prod2/bcf/North_50h/irf_file.fits.gz
No preview for this file type
caldb/data/cta/prod2/bcf/North_5h/irf_file.fits.gz
No preview for this file type
caldb/data/cta/prod2/bcf/South_0.5h/irf_file.fits.gz
No preview for this file type
caldb/data/cta/prod2/bcf/South_50h/irf_file.fits.gz
No preview for this file type
caldb/data/cta/prod2/bcf/South_5h/irf_file.fits.gz
No preview for this file type
cscripts/csobsinfo.py
... ... @@ -59,11 +59,11 @@ class csobsinfo(ctools.csobservation):
59 59 """
60 60 Get parameters from parfile and setup the observation
61 61 """
62   - # Get parameters
  62 + # Get parameters
63 63 if self.obs().size() == 0:
64 64 self._require_inobs('csobsinfo::get_parameters')
65 65 self.obs(self._get_observations(False))
66   -
  66 +
67 67 # Initialise object position
68 68 self._obj_dir = gammalib.GSkyDir()
69 69  
... ... @@ -83,7 +83,7 @@ class csobsinfo(ctools.csobservation):
83 83  
84 84 # Return
85 85 return
86   -
  86 +
87 87  
88 88 # Public methods
89 89 def run(self):
... ... @@ -96,7 +96,7 @@ class csobsinfo(ctools.csobservation):
96 96  
97 97 # Get parameters
98 98 self._get_parameters()
99   -
  99 +
100 100 # Initialise arrays to store certain values for reuse
101 101 # Todo, think about using a python dictionary
102 102 self._offsets = []
... ... @@ -107,7 +107,7 @@ class csobsinfo(ctools.csobservation):
107 107 self._ebounds = gammalib.GEbounds()
108 108 self._gti = gammalib.GGti()
109 109 obs_names = []
110   -
  110 +
111 111 # Initialise output to be filled
112 112 ontime = 0.0
113 113 livetime = 0.0
... ... @@ -115,12 +115,12 @@ class csobsinfo(ctools.csobservation):
115 115 n_eventbins = 0
116 116 n_obs_binned = 0
117 117 n_obs_unbinned = 0
118   -
  118 +
119 119 # Write header
120 120 if self._logTerse():
121 121 self._log('\n')
122 122 self._log.header1(gammalib.number('Observation', self.obs().size()))
123   -
  123 +
124 124 # Loop over observations
125 125 for obs in self.obs():
126 126  
... ... @@ -128,44 +128,44 @@ class csobsinfo(ctools.csobservation):
128 128 if not obs.classname() == 'GCTAObservation':
129 129 self._log('Skipping '+obs.instrument()+' observation\n')
130 130 continue
131   -
  131 +
132 132 # Use observed object as observation name if name is not given
133 133 obs_name = obs.name()
134 134 if obs_name == '':
135 135 obs_name = obs.object()
136   -
  136 +
137 137 # Logging
138 138 if self._logExplicit():
139 139 self._log.header2(obs_name)
140   -
  140 +
141 141 # Retrieve observation name
142 142 obs_names.append(obs_name)
143   -
  143 +
144 144 # Retrieve energy boundaries
145 145 obs_bounds = obs.events().ebounds()
146   -
  146 +
147 147 # Retrieve time interval
148 148 obs_gti = obs.events().gti()
149   -
  149 +
150 150 # Compute mean time and dead time fraction in percent
151 151 deadfrac = (1.0-obs.deadc())*100.0
152   -
  152 +
153 153 # Retrieve pointing and store Ra,Dec
154   - pnt_dir = obs.pointing().dir()
  154 + pnt_dir = obs.pointing().dir()
155 155 self._pnt_ra.append(pnt_dir.ra_deg())
156   - self._pnt_dec.append(pnt_dir.dec_deg())
157   -
  156 + self._pnt_dec.append(pnt_dir.dec_deg())
  157 +
158 158 # If avaliable append energy boundaries
159 159 if obs_bounds.size() > 0 :
160 160 self._ebounds.append(obs_bounds.emin(),obs_bounds.emax())
161   -
  161 +
162 162 # Append time interval
163 163 self._gti.append(obs_gti.tstart(), obs_gti.tstop())
164   -
  164 +
165 165 # Increment global livetime and ontime
166 166 ontime += obs.ontime()
167 167 livetime += obs.livetime()
168   -
  168 +
169 169 # Bookkeeping
170 170 if obs.eventtype() == 'CountsCube':
171 171 n_eventbins += obs.events().size()
... ... @@ -190,10 +190,10 @@ class csobsinfo(ctools.csobservation):
190 190 if self._compute_offset:
191 191 offset = pnt_dir.dist_deg(self._obj_dir)
192 192 self._offsets.append(offset)
193   -
  193 +
194 194 # Optionally log details
195 195 if self._logExplicit():
196   -
  196 +
197 197 # Log the observation energy range (if available)
198 198 self._log.parformat('Energy range')
199 199 if obs_bounds.size() == 0:
... ... @@ -203,7 +203,7 @@ class csobsinfo(ctools.csobservation):
203 203 self._log(' - ')
204 204 self._log(str(obs_bounds.emax()))
205 205 self._log('\n')
206   -
  206 +
207 207 # Log observation time interval
208 208 self._log.parformat('Time range (MJD)')
209 209 if obs_gti.size() == 0:
... ... @@ -213,7 +213,7 @@ class csobsinfo(ctools.csobservation):
213 213 self._log(' - ')
214 214 self._log(str(obs_gti.tstop().mjd()))
215 215 self._log('\n')
216   -
  216 +
217 217 # Log observation information
218 218 self._log_value(gammalib.EXPLICIT, 'Ontime', '%.3f s' %
219 219 obs.ontime())
... ... @@ -233,7 +233,7 @@ class csobsinfo(ctools.csobservation):
233 233 zenith)
234 234 self._log_value(gammalib.EXPLICIT, 'Azimuth angle', '%.2f deg' %
235 235 azimuth)
236   -
  236 +
237 237 # Write summary header
238 238 self._log_header1(gammalib.NORMAL, 'Summary')
239 239  
... ... @@ -258,7 +258,7 @@ class csobsinfo(ctools.csobservation):
258 258 mean_azimuth = '%.2f deg' % (sum(self._azimuths) / len(self._azimuths))
259 259 else:
260 260 mean_azimuth = 'Unknown'
261   -
  261 +
262 262 # Log mean offset, azimuth and zenith angle
263 263 self._log_header3(gammalib.NORMAL, 'Pointings')
264 264 self._log_value(gammalib.NORMAL, 'Mean offset angle', mean_offset)
... ... @@ -323,48 +323,60 @@ class csobsinfo(ctools.csobservation):
323 323  
324 324 # Check if DS9 file is valid
325 325 if ds9file.url() != 'NONE':
326   -
  326 +
327 327 # Write header
328 328 self._log_header1(gammalib.TERSE, 'Save pointings in DS9 file')
329 329  
330 330 # Log filename
331 331 self._log_value(gammalib.NORMAL, 'DS9 filename', ds9file.url())
332   -
333   - # Open file
  332 +
  333 + # Open file
334 334 f = open(ds9file.url(),'w')
335   -
  335 +
336 336 # Write coordinate system
337 337 f.write('fk5\n')
338   -
  338 +
339 339 # Loop over pointings
340 340 for i in range(len(self._pnt_ra)):
341   -
  341 +
342 342 # Create string
343 343 line = 'point('
344 344 line += str(self._pnt_ra[i])+','+str(self._pnt_dec[i])+')'
345 345 line += ' # point=cross 20 width=3\n'
346   -
  346 +
347 347 # Write to file
348 348 f.write(line)
349   -
  349 +
350 350 # Close file
351 351 f.close()
352   -
  352 +
353 353 # Return
354   - return
355   -
  354 + return
  355 +
356 356 def zeniths(self):
357 357 """
358 358 Return zenith angles
359 359 """
360 360 return self._zeniths
361   -
  361 +
362 362 def azimuths(self):
363 363 """
364 364 Return azimuth angles
365 365 """
366 366 return self._azimuths
367   -
  367 +
  368 + def ras(self):
  369 + """
  370 + Return pointings right ascension
  371 + """
  372 + return self._pnt_ra
  373 +
  374 + def decs(self):
  375 + """
  376 + Return pointings declination
  377 + """
  378 + return self._pnt_dec
  379 +
368 380 def offsets(self):
369 381 """
370 382 Return offset angles
... ... @@ -376,13 +388,13 @@ class csobsinfo(ctools.csobservation):
376 388 Return energy boundaries
377 389 """
378 390 return self._ebounds
379   -
  391 +
380 392 def gti(self):
381 393 """
382 394 Return good time intervals
383 395 """
384 396 return self._gti
385   -
  397 +
386 398  
387 399 # ======================== #
388 400 # Main routine entry point #
... ... @@ -391,6 +403,6 @@ if __name__ == '__main__':
391 403  
392 404 # Create instance of application
393 405 app = csobsinfo(sys.argv)
394   -
  406 +
395 407 # Execute application
396 408 app.execute()
... ...
cscripts/csphagen.py
... ... @@ -123,7 +123,8 @@ class csphagen(ctools.csobservation):
123 123 # Off region region the parameter 'bkgregfile' for all CTA observations
124 124 # without Off region
125 125 for obs in self.obs():
126   - if obs.instrument() == 'CTA':
  126 + #if obs.instrument() == 'CTA':
  127 + if obs.classname() == 'GCTAObservation':
127 128 if obs.off_regions().is_empty():
128 129 filename = self['bkgregfile'].filename()
129 130 regions = gammalib.GSkyRegions(filename)
... ... @@ -263,8 +264,8 @@ class csphagen(ctools.csobservation):
263 264 """
264 265 Set models in observation container
265 266  
266   - The method replaces all "CTA" background models by "CTAOnOff"
267   - background models.
  267 + The method replaces all "CTA", "HESS", "VERITAS", and "MAGIC"
  268 + background models by "CTAOnOff" background models.
268 269  
269 270 Parameters
270 271 ----------
... ... @@ -279,11 +280,15 @@ class csphagen(ctools.csobservation):
279 280 # Initialise model container
280 281 models = gammalib.GModels()
281 282  
282   - # Loop over all models and replace CTA background model by CTAOnOff
283   - # background model
  283 + # Loop over all models and replace CTA/HESS/VERITAS/MAGIC background
  284 + # models by CTAOnOff background model
284 285 for model in self.obs().models():
285   - if 'GCTA' in model.classname() and 'CTA' in model.instruments():
286   - model.instruments('CTAOnOff')
  286 + if 'GCTA' in model.classname():
  287 + if 'CTA' in model.instruments() or \
  288 + 'HESS' in model.instruments() or \
  289 + 'VERITAS' in model.instruments() or \
  290 + 'MAGIC' in model.instruments():
  291 + model.instruments('CTAOnOff')
287 292 models.append(model)
288 293  
289 294 # Append model to observation container
... ... @@ -399,7 +404,8 @@ class csphagen(ctools.csobservation):
399 404 for obs in self.obs():
400 405  
401 406 # Skip non CTA observations
402   - if obs.instrument() != 'CTA':
  407 + #if obs.instrument() != 'CTA':
  408 + if obs.classname() != 'GCTAObservation':
403 409 self._log_string(gammalib.NORMAL, 'Skip %s observation "%s"' % \
404 410 (obs.instrument(), obs.id()))
405 411 continue
... ...
cscripts/csresspec.py
... ... @@ -241,14 +241,14 @@ class csresspec(ctools.csobservation):
241 241  
242 242 # Bin events
243 243 cntcube = ctools.ctbin(obs)
244   - cntcube['xref'] = ra
245   - cntcube['yref'] = dec
246   - cntcube['binsz'] = 0.02
247   - cntcube['nxpix'] = npix
248   - cntcube['nypix'] = npix
249   - cntcube['proj'] = 'TAN'
  244 + cntcube['xref'] = ra
  245 + cntcube['yref'] = dec
  246 + cntcube['binsz'] = 0.02
  247 + cntcube['nxpix'] = npix
  248 + cntcube['nypix'] = npix
  249 + cntcube['proj'] = 'TAN'
250 250 cntcube['coordsys'] = 'CEL'
251   - cntcube['ebinalg'] = self['ebinalg'].string()
  251 + cntcube['ebinalg'] = self['ebinalg'].string()
252 252 if self['ebinalg'].string() == 'FILE':
253 253 cntcube['ebinfile'] = self['ebinfile'].filename()
254 254 else:
... ... @@ -356,7 +356,7 @@ class csresspec(ctools.csobservation):
356 356 def _cube_to_spectrum(self, cube, evlist_info):
357 357 """
358 358 Derive from event cube a count spectrum
359   -
  359 +
360 360 If data come from event list use only the ROI and energy range of
361 361 the original data. Apply user defined mask if requested.
362 362  
... ... @@ -550,29 +550,30 @@ class csresspec(ctools.csobservation):
550 550 # if 3D observation
551 551 if obs[0].classname() == 'GCTAObservation':
552 552  
553   - # Prepare Observations
554   -
555 553 # If already binned set the evlist_info dictionary to have
556 554 # attribute was_list False
557 555 if obs[0].eventtype() == 'CountsCube':
558 556 evlist_info = {'was_list': False}
559   - # Otherwise bin now
  557 +
  558 + # ... otherwise bin now
560 559 else:
561   - # we remember if we binned an event list
562   - # so that we can mask only the ROI for residual calculation
  560 + # we remember if we binned an event list so that we can
  561 + # mask only the ROI for residual calculation
563 562 msg = 'Setting up binned observation'
564 563 self._log_string(gammalib.NORMAL, msg)
565 564 obs, evlist_info = self._bin_evlist(obs)
566 565  
567   - # Calculate Model and residuals
568   - # If model cube is provided load it
  566 + # Calculate Model and residuals. If model cube is provided load
  567 + # it
569 568 if self._use_maps:
570 569 modcube = gammalib.GCTAEventCube(self['inmodel'].filename())
571   - # Otherwise calculate it now
  570 +
  571 + # ... otherwise calculate it now
572 572 else:
573 573 msg = 'Computing model cube'
574 574 self._log_string(gammalib.NORMAL, msg)
575 575 modelcube = ctools.ctmodel(obs)
  576 + modelcube['edisp'] = self['edisp'].boolean()
576 577 modelcube.run()
577 578 modcube = modelcube.cube().copy()
578 579  
... ...
cscripts/csroot2caldb.py
... ... @@ -2,7 +2,7 @@
2 2 # ==========================================================================
3 3 # Generate IRFs in CALDB format from a ROOT offaxis performance file
4 4 #
5   -# Copyright (C) 2016-2017 Juergen Knoedlseder
  5 +# Copyright (C) 2016-2018 Juergen Knoedlseder
6 6 #
7 7 # This program is free software: you can redistribute it and/or modify
8 8 # it under the terms of the GNU General Public License as published by
... ... @@ -618,7 +618,7 @@ class csroot2caldb(ctools.cscript):
618 618 ds['HDU_EA'].card('HI_THRES', emin, '[TeV] High energy threshold')
619 619  
620 620 # Create "EFFAREA" data column
621   - self._make_2D(etrue, ds['HDU_EA'], 'EFFAREA', 'm2')
  621 + self._make_2D(etrue, ds['HDU_EA'], 'EFFAREA', 'm**2')
622 622  
623 623 # Return
624 624 return
... ... @@ -745,7 +745,6 @@ class csroot2caldb(ctools.cscript):
745 745 'Generate Gaussian point spread function extension')
746 746  
747 747 # Get relevant ROOT histograms
748   - #r68 = tfile.Get('AngRes_offaxis')
749 748 r68, _ = self._root2psf_get_r68_and_r80(tfile)
750 749  
751 750 # Extract number of bins in histogram
... ... @@ -826,8 +825,6 @@ class csroot2caldb(ctools.cscript):
826 825 'Generate King point spread function extension')
827 826  
828 827 # Get relevant ROOT histograms
829   - #r68 = tfile.Get('AngRes_offaxis')
830   - #r80 = tfile.Get('AngRes80_offaxis')
831 828 r68, r80 = self._root2psf_get_r68_and_r80(tfile)
832 829  
833 830 # Extract number of bins in histogram
... ... @@ -1034,7 +1031,6 @@ class csroot2caldb(ctools.cscript):
1034 1031  
1035 1032 # Get relevant ROOT histograms
1036 1033 matrix = self._root2edisp_get_migra(tfile)
1037   - #matrix = tfile.Get('EestOverEtrue_offaxis')
1038 1034  
1039 1035 # Write boundary keywords
1040 1036 self._set_cif_keywords(ds['HDU_EDISP'], irf['EDISP_NAME'],
... ... @@ -1087,7 +1083,7 @@ class csroot2caldb(ctools.cscript):
1087 1083 irf['BGD_BOUNDS'], irf['BGD_DESC'], irf)
1088 1084  
1089 1085 # Create "BGD" data column
1090   - self._make_3D(array, ds['HDU_BGD'], 'BGD', '1/s/MeV/sr')
  1086 + self._make_3D(array, ds['HDU_BGD'], 'BGD', '1/(MeV s sr)')
1091 1087  
1092 1088 # Return
1093 1089 return
... ... @@ -1327,7 +1323,7 @@ class csroot2caldb(ctools.cscript):
1327 1323 # Log power law replacement
1328 1324 self._log_value(gammalib.NORMAL,
1329 1325 'Power law replacement',
1330   - '%e cts/s/deg2 (E=%8.4f TeV (%d) '
  1326 + '%e cts/(s deg**2) (E=%8.4f TeV (%d) '
1331 1327 'Off=%.2f deg (%d)' %
1332 1328 (value,engs[ieng],ieng,offset,ioff))
1333 1329  
... ... @@ -1479,7 +1475,7 @@ class csroot2caldb(ctools.cscript):
1479 1475 # Log background infill
1480 1476 self._log_value(gammalib.NORMAL,
1481 1477 'Background infill',
1482   - '%e cts/s/deg2 (E=%8.4f TeV (%d) '
  1478 + '%e cts/(s deg**2) (E=%8.4f TeV (%d) '
1483 1479 'Off=%.2f deg (%d)' %
1484 1480 (value,engs[ieng],ieng,offset,ioff))
1485 1481  
... ...
doc/source/admin/install_devel.rst
... ... @@ -7,7 +7,7 @@ The current development version of the code can be downloaded as source
7 7 tarballs, Mac OS X binaries, or directly from the `Git <https://git-scm.com/>`_
8 8 repository.
9 9  
10   -The current ctools development release is ``ctools-1.5.0.dev1``.
  10 +The current ctools development release is ``ctools-1.6.0.dev1``.
11 11  
12 12  
13 13 Source tarballs
... ... @@ -15,8 +15,8 @@ Source tarballs
15 15  
16 16 Download the source tarballs from the following links
17 17  
18   -- `ctools <http://cta.irap.omp.eu/ctools/releases/ctools/ctools-1.5.0.dev1.tar.gz>`_
19   -- `GammaLib <http://cta.irap.omp.eu/ctools/releases/gammalib/gammalib-1.5.0.dev1.tar.gz>`_
  18 +- `ctools <http://cta.irap.omp.eu/ctools/releases/ctools/ctools-1.6.0.dev1.tar.gz>`_
  19 +- `GammaLib <http://cta.irap.omp.eu/ctools/releases/gammalib/gammalib-1.6.0.dev1.tar.gz>`_
20 20  
21 21 and follow the instructions on :ref:`sec_install_source`.
22 22  
... ... @@ -26,7 +26,7 @@ Binary packages
26 26  
27 27 Download the installer image from the following link
28 28  
29   -- `Mac OS X (10.7+) <http://cta.irap.omp.eu/ctools/releases/ctools/ctools-1.5.0.dev1-macosx10.7.dmg>`_
  29 +- `Mac OS X (10.7+) <http://cta.irap.omp.eu/ctools/releases/ctools/ctools-1.6.0.dev1-macosx10.7.dmg>`_
30 30  
31 31 and follow the instructions on :ref:`sec_install_binary`.
32 32  
... ...
doc/source/admin/release_history/1.5.1.rst 0 โ†’ 100644
  1 +.. _1.5.1:
  2 +
  3 +ctools 1.5.1 release
  4 +====================
  5 +
  6 +Overview
  7 +--------
  8 +
  9 +ctools 1.5.1 is a bug fix release for ctools release 1.5.
  10 +
  11 +The following bugs were fixed:
  12 +
  13 +* [`2341 <https://cta-redmine.irap.omp.eu/issues/2341>`_] -
  14 + Take energy dispersion in :ref:`csresspec` modelling into account
  15 +
  16 +
  17 +The following improvements were made:
  18 +
  19 +* [`2307 <https://cta-redmine.irap.omp.eu/issues/2307>`_] -
  20 + Enable using :ref:`csphagen` for HESS, VERITAS and MAGIC observations
... ...
doc/source/admin/release_history/1.6.rst 0 โ†’ 100644
  1 +.. _1.6:
  2 +
  3 +ctools 1.6 release
  4 +==================
  5 +
  6 +Overview
  7 +--------
  8 +
  9 +ctools 1.6 is a major release that adds significant functionality.
  10 +
  11 +In particular, this release provides:
  12 +
  13 +* ...
  14 +
  15 +
  16 +Bug fixes
  17 +---------
  18 +
  19 +* ...
  20 +
  21 +
  22 +Improvements
  23 +------------
  24 +
  25 +* [`2313 <https://cta-redmine.irap.omp.eu/issues/2313>`_] -
  26 + Add `ras()` and `decs()` methods to :ref:`csobsinfo`
  27 +* [`2311 <https://cta-redmine.irap.omp.eu/issues/2311>`_] -
  28 + Allow specifying of input skymap to :ref:`ctskymap` tool
  29 +* [`2309 <https://cta-redmine.irap.omp.eu/issues/2309>`_] -
  30 + Accelerate `IRF` and `RING` background methods in :ref:`ctskymap`
  31 +
  32 +
  33 +New features
  34 +------------
  35 +
  36 +* ...
  37 +
  38 +
  39 +Clean up
  40 +--------
  41 +
  42 +* ...
... ...
doc/source/admin/release_history/index.rst
... ... @@ -6,6 +6,8 @@ Release History
6 6 .. toctree::
7 7 :maxdepth: 1
8 8  
  9 + ctools 1.6 <1.6>
  10 + ctools 1.5.1 <1.5.1>
9 11 ctools 1.5 <1.5>
10 12 ctools 1.4.3 <1.4.3>
11 13 ctools 1.4.2 <1.4.2>
... ...
doc/source/users/reference_manual/ctskymap.rst
... ... @@ -41,6 +41,11 @@ General parameters
41 41 Instrument response function (only required for IRF background subtraction
42 42 if no response information is provided by ``inobs``).
43 43  
  44 +``(inmap = NONE) [file]``
  45 + Input sky map file containing a ``COUNTS`` and an ``ACCEPTANCE`` extension.
  46 + Such extensions are produce by a previous run of the :ref:`ctskymap` tool with
  47 + the ``IRF`` or ``RING`` background method.
  48 +
44 49 ``outmap [file]``
45 50 Output sky map file.
46 51  
... ... @@ -82,17 +87,37 @@ Background subtraction configuration parameters
82 87 Background subtraction method.
83 88  
84 89 ``roiradius [real]``
85   - Source region radius for estimating on-counts (in degrees).
  90 + Source region radius for ``RING`` subtraction (in degrees).
86 91  
87 92 ``inradius [real]``
88   - Inner ring radius for RING subtraction (in degrees).
  93 + Inner background ring radius for ``RING`` subtraction (in degrees).
89 94  
90 95 ``outradius [real]``
91   - Outer ring radius for RING subtraction (in degrees).
  96 + Outer background ring radius for ``RING`` subtraction (in degrees).
  97 +
  98 +``iterations [integer]``
  99 + Number of iterations for the automatic computation of exclusion regions.
  100 + Sky map pixels with detection significance above the value specified by the
  101 + ``threshold`` parameter will be defined as exclusion regions, and the ``RING``
  102 + background will be recomputed based on these exclusion regions. Since this
  103 + will likely change the significance of the pixels, the procedure can be
  104 + iteratively repeated, and the ``iterations`` parameter specifies how often
  105 + the procedure should be repeated.
  106 +
  107 +``threshold [real]``
  108 + Significance threshold above which pixels should be excluded for the
  109 + background computation in the ``RING`` background method.
92 110  
93 111 ``(inexclusion = NONE) [file]``
94 112 Exclusion region file as either a FITS map or DS9 region file.
95 113  
  114 +``(usefft = yes) [boolean]``
  115 + Specifies whether a FFT should be used for ring computation. If set to
  116 + ``no`` the ring background will be computed directly. Direct computation is
  117 + more accurate since the precise angular distances between pixels are taken
  118 + into account, but FFT is faster, but less accurate since it assumes
  119 + Euclidean distances between pixels.
  120 +
96 121  
97 122 Standard parameters
98 123 -------------------
... ...
pyext/ctobservation.i
... ... @@ -94,6 +94,7 @@ public:
94 94 // Make methods private in Python by prepending an underscore
95 95 %rename(_first_unbinned_observation) first_unbinned_observation;
96 96 %rename(_next_unbinned_observation) next_unbinned_observation;
  97 + %rename(_read_ogip_keywords) read_ogip_keywords;
97 98 %rename(_write_ogip_keywords) write_ogip_keywords;
98 99 %rename(_set_obs_statistic) set_obs_statistic;
99 100 %rename(_save_events_fits) save_events_fits;
... ...
src/ctskymap/ctskymap.cpp
1 1 /***************************************************************************
2 2 * ctskymap - Sky mapping tool *
3 3 * ----------------------------------------------------------------------- *
4   - * copyright (C) 2011-2017 by Juergen Knoedlseder *
  4 + * copyright (C) 2011-2018 by Juergen Knoedlseder *
5 5 * ----------------------------------------------------------------------- *
6 6 * *
7 7 * This program is free software: you can redistribute it and/or modify *
... ... @@ -30,14 +30,14 @@
30 30 #endif
31 31 #include <cstdio>
32 32 #include "ctskymap.hpp"
33   -#include "GSkyRegions.hpp"
34 33  
35 34 /* __ Method name definitions ____________________________________________ */
36 35 #define G_GET_PARAMETERS "ctskymap::get_parameter()"
37 36 #define G_MAP_EVENTS "ctskymap::map_events(GCTAObservation*)"
38 37 #define G_MAP_BACKGROUND_IRF "ctskymap::map_background_irf(GCTAObservation*)"
39   -#define G_MAP_BACKGROUND_RING "ctskymap::map_background_ring("\
40   - "GCTAObservation*)"
  38 +#define G_RING_BOUNDING_BOX "ctskymap::ring_bounding_box(int&, int&, int&, "\
  39 + "int&, int&)"
  40 +#define G_RING_KERNEL "ctskymap::ring_kernel(double&, double&)"
41 41  
42 42 /* __ Debug definitions __________________________________________________ */
43 43  
... ... @@ -222,37 +222,18 @@ void ctskymap::run(void)
222 222 // Get parameters
223 223 get_parameters();
224 224  
225   - // Write input observation container into logger
226   - log_observations(NORMAL, m_obs, "Input observation");
227   -
228   - // Write header into logger
229   - log_header1(TERSE, gammalib::number("Map observation", m_obs.size()));
230   -
231   - // Loop over all unbinned CTA observations in the container
232   - for (GCTAObservation* obs = first_unbinned_observation(); obs != NULL;
233   - obs = next_unbinned_observation()) {
234   -
235   - // Map events into sky map
236   - map_events(obs);
  225 + // Setup maps
  226 + setup_maps();
237 227  
238   - // Compute background sky map
239   - map_background(obs);
240   -
241   - // Dispose events to free memory
242   - obs->dispose_events();
243   -
244   - } // endfor: looped over observations
245   -
246   - // If background subtraction is selected then compute significance map
247   - if (m_bkgsubtract != "NONE") {
248   -
249   - // Compute the significance map
250   - map_significance();
251   -
252   - // Subtract background map from counts map
253   - m_skymap -= m_bkgmap;
  228 + // If no input map exists then fill counts and acceptance maps from
  229 + // observation container
  230 + if (!m_has_inmap) {
  231 + fill_maps();
254 232 }
255 233  
  234 + // Compute maps
  235 + compute_maps();
  236 +
256 237 // Optionally publish sky map
257 238 if (m_publish) {
258 239 publish();
... ... @@ -266,8 +247,8 @@ void ctskymap::run(void)
266 247 /***********************************************************************//**
267 248 * @brief Save sky map
268 249 *
269   - * Saves the sky map into a FITS file. The FITS file name is specified by the
270   - * @p outname parameter.
  250 + * Saves the sky map into a FITS file. The FITS file name is specified by
  251 + * the @p outname parameter.
271 252 ***************************************************************************/
272 253 void ctskymap::save(void)
273 254 {
... ... @@ -284,39 +265,31 @@ void ctskymap::save(void)
284 265 GFits fits;
285 266  
286 267 // Write sky map into FITS file
287   - GFitsHDU* hdu = m_skymap.write(fits);
288   -
289   - // Write keywords into sky map extension
290   - write_ogip_keywords(hdu);
291   - write_hdu_keywords(hdu);
  268 + write_map(fits, m_skymap, "SKYMAP");
292 269  
293   - // If background subtraction is requested then write background map
294   - // and significance map to FITS file
  270 + // If background subtraction is requested then write the background map,
  271 + // the significance map, the counts map and the acceptance map to the
  272 + // FITS file
295 273 if (m_bkgsubtract != "NONE") {
296   -
297   - // Write background map into FITS file
298   - hdu = m_bkgmap.write(fits);
299 274  
300   - // Set background map extension name
301   - if (hdu != NULL) {
302   - hdu->extname("BACKGROUND");
303   - }
  275 + // Write background map into FITS file
  276 + write_map(fits, m_bkgmap, "BACKGROUND");
304 277  
305   - // Write keywords into background extension
306   - write_ogip_keywords(hdu);
307   - write_hdu_keywords(hdu);
308 278  
309 279 // Write significance map into FITS file
310   - hdu = m_sigmap.write(fits);
  280 + write_map(fits, m_sigmap, "SIGNIFICANCE");
311 281  
312   - // Set significance map extension name
313   - if (hdu != NULL) {
314   - hdu->extname("SIGNIFICANCE");
315   - }
  282 + // Write counts map into FITS file
  283 + write_map(fits, m_counts, "COUNTS");
  284 +
  285 + // Write acceptance map into FITS file
  286 + write_map(fits, m_acceptance, "ACCEPTANCE");
316 287  
317   - // Write keywords into significance extension
318   - write_ogip_keywords(hdu);
319   - write_hdu_keywords(hdu);
  288 + // If RING background subtraction is requested then write also
  289 + // the exclusion map to the FITS file
  290 + if (m_bkgsubtract == "RING") {
  291 + write_map(fits, m_exclmap, "EXCLUSION");
  292 + }
320 293  
321 294 } // endif: background subtraction was requested
322 295  
... ... @@ -375,25 +348,36 @@ void ctskymap::publish(const std::string&amp; name)
375 348 ***************************************************************************/
376 349 void ctskymap::init_members(void)
377 350 {
378   - // Initialise members
379   - m_skymap.clear();
380   - m_bkgmap.clear();
381   - m_sigmap.clear();
382   - m_exclmap.clear();
383   - m_alphamap.clear();
384   - m_onmap.clear();
  351 + // Initialise User parameters
  352 + m_outmap.clear();
  353 + m_inexclusion.clear();
385 354 m_emin = 0.0;
386 355 m_emax = 0.0;
387 356 m_bkgsubtract = "NONE";
388 357 m_roiradius = 0.0;
389 358 m_inradius = 0.0;
390 359 m_outradius = 0.0;
  360 + m_iterations = 0;
  361 + m_threshold = 5.0;
  362 + m_usefft = true;
391 363 m_publish = false;
392 364 m_chatter = static_cast<GChatter>(2);
393 365  
  366 + // Initialise members
  367 + m_has_inmap = false;
  368 + m_skymap.clear();
  369 + m_bkgmap.clear();
  370 + m_sigmap.clear();
  371 + m_counts.clear();
  372 + m_acceptance.clear();
  373 + m_exclmap.clear();
  374 +
394 375 // Initialise cache
395 376 m_solidangle.clear();
396 377 m_dirs.clear();
  378 + m_cos_roiradius = 1.0;
  379 + m_cos_inradius = 1.0;
  380 + m_cos_outradius = 1.0;
397 381  
398 382 // Return
399 383 return;
... ... @@ -407,25 +391,36 @@ void ctskymap::init_members(void)
407 391 ***************************************************************************/
408 392 void ctskymap::copy_members(const ctskymap& app)
409 393 {
410   - // Copy attributes
411   - m_skymap = app.m_skymap;
412   - m_bkgmap = app.m_bkgmap;
413   - m_sigmap = app.m_sigmap;
414   - m_exclmap = app.m_exclmap;
415   - m_alphamap = app.m_alphamap;
416   - m_onmap = app.m_onmap;
  394 + // Copy User parameters
  395 + m_outmap = app.m_outmap;
  396 + m_inexclusion = app.m_inexclusion;
417 397 m_emin = app.m_emin;
418 398 m_emax = app.m_emax;
419 399 m_bkgsubtract = app.m_bkgsubtract;
420 400 m_roiradius = app.m_roiradius;
421 401 m_inradius = app.m_inradius;
422 402 m_outradius = app.m_outradius;
  403 + m_iterations = app.m_iterations;
  404 + m_threshold = app.m_threshold;
  405 + m_usefft = app.m_usefft;
423 406 m_publish = app.m_publish;
424 407 m_chatter = app.m_chatter;
425 408  
  409 + // Copy members
  410 + m_has_inmap = app.m_has_inmap;
  411 + m_skymap = app.m_skymap;
  412 + m_bkgmap = app.m_bkgmap;
  413 + m_sigmap = app.m_sigmap;
  414 + m_counts = app.m_counts;
  415 + m_acceptance = app.m_acceptance;
  416 + m_exclmap = app.m_exclmap;
  417 +
426 418 // Copy cache
427   - m_solidangle = app.m_solidangle;
428   - m_dirs = app.m_dirs;
  419 + m_solidangle = app.m_solidangle;
  420 + m_dirs = app.m_dirs;
  421 + m_cos_roiradius = app.m_cos_roiradius;
  422 + m_cos_inradius = app.m_cos_inradius;
  423 + m_cos_outradius = app.m_cos_outradius;
429 424  
430 425 // Return
431 426 return;
... ... @@ -453,41 +448,92 @@ void ctskymap::free_members(void)
453 448 ***************************************************************************/
454 449 void ctskymap::get_parameters(void)
455 450 {
456   - // Setup observations from "inobs" parameter. Do not request response
457   - // information and do not accept counts cubes.
458   - setup_observations(m_obs, false, true, false);
  451 + // So far we have no valid input map
  452 + m_has_inmap = false;
  453 +
  454 + // If an input map was specified then extract try loading the COUNTS
  455 + // and ACCEPTANCE extensions from that file
  456 + GFilename inmap = (*this)["inmap"].filename();
  457 + if (inmap != "NONE") {
  458 + GFits fits(inmap);
  459 + if (fits.contains("COUNTS") && fits.contains("ACCEPTANCE")) {
  460 +
  461 + // Get HDUs
  462 + GFitsHDU &counts = *fits["COUNTS"];
  463 + GFitsHDU &acceptance = *fits["ACCEPTANCE"];
  464 +
  465 + // Read counts and acceptance map
  466 + m_counts.read(counts);
  467 + m_acceptance.read(acceptance);
459 468  
460   - // Create sky map based on task parameters
461   - m_skymap = create_map(m_obs);
  469 + // Extract OGIP parameters
  470 + read_ogip_keywords(&counts);
462 471  
463   - // Get further parameters
464   - m_emin = (*this)["emin"].real();
465   - m_emax = (*this)["emax"].real();
  472 + // Extract energy range
  473 + m_emin = (counts.has_card("E_MIN")) ? counts.real("E_MIN") : 0.0;
  474 + m_emax = (counts.has_card("E_MAX")) ? counts.real("E_MAX") : 0.0;
  475 +
  476 + // Signal availability of sky map
  477 + m_has_inmap = true;
  478 + }
  479 + else {
  480 + std::string msg = "Input sky map \""+inmap.url()+"\" does not "
  481 + "contain \"COUNTS\" and \"ACCEPTANCE\" "
  482 + "extensions.";
  483 + throw GException::invalid_value(G_GET_PARAMETERS, msg);
  484 + }
  485 + fits.close();
  486 + }
  487 +
  488 + // If no valid input map exists then request observations
  489 + if (!m_has_inmap) {
  490 +
  491 + // Setup observations from "inobs" parameter. Do not request response
  492 + // information and do not accept counts cubes.
  493 + setup_observations(m_obs, false, true, false);
  494 +
  495 + // Create counts and acceptance maps from the User parameters
  496 + m_counts = create_map(m_obs);
  497 + m_acceptance = m_counts;
  498 +
  499 + // Get further parameters
  500 + m_emin = (*this)["emin"].real();
  501 + m_emax = (*this)["emax"].real();
  502 +
  503 + } // endif: no valid input map existed
  504 +
  505 + // Get background method
466 506 m_bkgsubtract = (*this)["bkgsubtract"].string();
467 507  
468 508 // Get RING background parameters
469 509 if (m_bkgsubtract == "RING") {
470 510  
471 511 // Get parameters
472   - m_roiradius = (*this)["roiradius"].real();
473   - m_inradius = (*this)["inradius"].real();
474   - m_outradius = (*this)["outradius"].real();
  512 + m_roiradius = (*this)["roiradius"].real();
  513 + m_inradius = (*this)["inradius"].real();
  514 + m_outradius = (*this)["outradius"].real();
  515 + m_iterations = (*this)["iterations"].integer();
  516 + if (m_iterations > 0) {
  517 + m_threshold = (*this)["threshold"].real();
  518 + }
  519 + m_inexclusion = (*this)["inexclusion"].filename();
  520 + m_usefft = (*this)["usefft"].boolean();
475 521  
476 522 // Make sure that (roiradius < inradius < outradius)
477 523 if (m_roiradius > m_inradius) {
478   - std::string msg("'roiradius' must be smaller than 'inradius'");
  524 + std::string msg("\"roiradius\" must be smaller than \"inradius\"");
479 525 throw GException::invalid_value(G_GET_PARAMETERS, msg);
480 526 }
481 527 else if (m_inradius > m_outradius) {
482   - std::string msg("'inradius' must be smaller than 'outradius'");
  528 + std::string msg("\"inradius\" must be smaller than \"outradius\"");
483 529 throw GException::invalid_value(G_GET_PARAMETERS, msg);
484 530 }
485 531  
486 532 } // endif: read RING background parameters
487 533  
488   - // If IRF background subtraction is requested then make sure that the
489   - // CTA observations in the observation container have response information
490   - if (m_bkgsubtract != "NONE") {
  534 + // If background subtraction is requested then make sure that the CTA
  535 + // observations in the observation container have response information
  536 + if (m_bkgsubtract != "NONE" && !m_has_inmap) {
491 537 set_response(m_obs);
492 538 }
493 539  
... ... @@ -497,39 +543,209 @@ void ctskymap::get_parameters(void)
497 543  
498 544 // Read ahead parameters
499 545 if (read_ahead()) {
500   - m_outmap = (*this)["outmap"].filename();
  546 + m_outmap = (*this)["outmap"].filename();
501 547 }
502 548  
  549 + // Write parameters into logger
  550 + log_parameters(TERSE);
  551 +
  552 + // Return
  553 + return;
  554 +}
  555 +
  556 +
  557 +/***********************************************************************//**
  558 + * @brief Setup maps
  559 + *
  560 + * Setup maps for sky map generation.
  561 + ***************************************************************************/
  562 +void ctskymap::setup_maps(void)
  563 +{
  564 + // Clear cache
  565 + m_solidangle.clear();
  566 + m_dirs.clear();
  567 +
503 568 // Create background map and significance map if background subtraction
504 569 // is requested
505 570 if (m_bkgsubtract != "NONE") {
506 571  
507   - // Create backgrond and significance maps
508   - m_bkgmap = create_map(m_obs);
509   - m_sigmap = create_map(m_obs);
510   -
511 572 // Setup the exclusions map
512   - map_exclusions((*this)["inexclusion"].filename());
  573 + setup_exclusion_map(m_inexclusion);
513 574  
514 575 // Cache the pixel solid angles and sky directions
515   - m_solidangle.reserve(m_bkgmap.npix());
516   - m_dirs.reserve(m_bkgmap.npix());
517   - for (int i = 0; i < m_bkgmap.npix(); ++i) {
518   - m_solidangle.push_back(m_bkgmap.solidangle(i));
519   - m_dirs.push_back(m_bkgmap.inx2dir(i));
  576 + m_solidangle.reserve(m_counts.npix());
  577 + m_dirs.reserve(m_counts.npix());
  578 + for (int i = 0; i < m_counts.npix(); ++i) {
  579 + m_solidangle.push_back(m_counts.solidangle(i));
  580 + m_dirs.push_back(m_counts.inx2dir(i));
520 581 }
521 582  
522   - // If doing a ring background subtraction, generate an alpha map and
523   - // make sure to cache the counts for each observation
524   - if (m_bkgsubtract == "RING") {
525   - m_alphamap = create_map(m_obs);
526   - m_onmap = create_map(m_obs);
  583 + } // endif: background subtraction selected
  584 +
  585 + // Compute cosine of ring radii for RING background method
  586 + if (m_bkgsubtract == "RING") {
  587 + m_cos_roiradius = std::cos(m_roiradius * gammalib::deg2rad);
  588 + m_cos_inradius = std::cos(m_inradius * gammalib::deg2rad);
  589 + m_cos_outradius = std::cos(m_outradius * gammalib::deg2rad);
  590 + }
  591 +
  592 + // Return
  593 + return;
  594 +}
  595 +
  596 +
  597 +/***********************************************************************//**
  598 + * @brief Generates map of pixel exclusions
  599 + *
  600 + * @param[in] filename Exclusion file name.
  601 + *
  602 + * Generates a sky map of the pixels that are to be excluded from the
  603 + * background estimation. Pixels with values different from 0 will be
  604 + * excluded.
  605 + ***************************************************************************/
  606 +void ctskymap::setup_exclusion_map(const GFilename& filename)
  607 +{
  608 + // Create exlusion map
  609 + m_exclmap = m_counts;
  610 +
  611 + // Set all pixels to 0 (no pixel excluded)
  612 + m_exclmap = 0.0;
  613 +
  614 + // Make sure the exclusions filename is valid
  615 + if (is_valid_filename(filename)) {
  616 +
  617 + // Fill the exclusions based on the regions supplied
  618 + if (filename.is_fits()) {
  619 + setup_exclusion_map_fits(filename);
  620 + }
  621 + else {
  622 + setup_exclusion_map_region(filename);
527 623 }
528 624  
529   - } // endif: background subtraction selected
  625 + } // endif: filename was valid
530 626  
531   - // Write parameters into logger
532   - log_parameters(TERSE);
  627 + // Return
  628 + return;
  629 +}
  630 +
  631 +
  632 +/***********************************************************************//**
  633 + * @brief Fills exclusions map from FITS image
  634 + *
  635 + * @param[in] filename FITS image file name.
  636 + *
  637 + * Sets all exclusion map pixels to 1 that correspond to non-zero pixels in
  638 + * the exclusion sky map FITS file.
  639 + ***************************************************************************/
  640 +void ctskymap::setup_exclusion_map_fits(const GFilename& filename)
  641 +{
  642 + // Load the fits image
  643 + GSkyMap inmap(filename);
  644 +
  645 + // Loop through the individual pixels in the exclusion map
  646 + for (int i = 0; i < m_exclmap.npix(); ++i) {
  647 +
  648 + // Get the pixel direction
  649 + GSkyDir dir = m_exclmap.pix2dir(i);
  650 +
  651 + // Check this sky position in the fits map
  652 + if (inmap.contains(dir) && (inmap(inmap.dir2inx(dir)) != 0.0)) {
  653 +
  654 + // Set the pixel to 1
  655 + m_exclmap(i) = 1.0;
  656 +
  657 + } // endif: pixel,region overlap check
  658 +
  659 + } // endfor: looped over exclusion map pixels
  660 +
  661 + // Return
  662 + return;
  663 +}
  664 +
  665 +
  666 +/***********************************************************************//**
  667 + * @brief Fills exclusions map from DS9 region file
  668 + *
  669 + * @param[in] filename DS9 region file name.
  670 + *
  671 + * Sets all exclusion map pixels to 1 that are contained in any of the DS9
  672 + * regions.
  673 + ***************************************************************************/
  674 +void ctskymap::setup_exclusion_map_region(const GFilename& filename)
  675 +{
  676 + // Load the exclusion regions
  677 + GSkyRegions regions(filename);
  678 +
  679 + // Loop through the individual pixels in the exclusion map
  680 + for (int i = 0; i < m_exclmap.npix(); ++i) {
  681 +
  682 + // Get the pixel position
  683 + GSkyDir dir = m_exclmap.pix2dir(i);
  684 +
  685 + // If pixel position overlaps with the regions
  686 + if (regions.contains(dir)) {
  687 + m_exclmap(i) = 1.0;
  688 + }
  689 +
  690 + } // endfor: looped over exclusion map pixels
  691 +
  692 + // Return
  693 + return;
  694 +}
  695 +
  696 +
  697 +/***********************************************************************//**
  698 + * @brief Fill maps from observation container
  699 + *
  700 + * Fill counts and optionally acceptance maps from data in the observation
  701 + * container.
  702 + ***************************************************************************/
  703 +void ctskymap::fill_maps(void)
  704 +{
  705 + // Write input observation container into logger
  706 + log_observations(NORMAL, m_obs, "Input observation");
  707 +
  708 + // Write header into logger
  709 + log_header1(TERSE, gammalib::number("Find unbinned observation",
  710 + m_obs.size()));
  711 +
  712 + // Find all unbinned CTA observations in m_obs
  713 + std::vector<GCTAObservation*> obs_list(0);
  714 + for (GCTAObservation* obs = first_unbinned_observation(); obs != NULL;
  715 + obs = next_unbinned_observation()) {
  716 +
  717 + // Push observation into list
  718 + obs_list.push_back(obs);
  719 +
  720 + // Write message
  721 + std::string msg = " Including unbinned "+obs->instrument()+
  722 + " observation";
  723 + log_string(NORMAL, msg);
  724 + }
  725 +
  726 + // Write header into logger
  727 + log_header1(TERSE, gammalib::number("Fill map from observation",
  728 + m_obs.size()));
  729 +
  730 + // Loop over all unbinned CTA observations in the container
  731 + #pragma omp parallel for
  732 + for (int i = 0; i < obs_list.size(); ++i) {
  733 +
  734 + // Get pointer to observation
  735 + GCTAObservation* obs = obs_list[i];
  736 +
  737 + // Fill events into counts map
  738 + fill_maps_counts(obs);
  739 +
  740 + // Optionally compute acceptance sky map
  741 + if (m_bkgsubtract != "NONE") {
  742 + fill_maps_acceptance(obs);
  743 + }
  744 +
  745 + // Dispose events to free memory
  746 + obs->dispose_events();
  747 +
  748 + } // endfor: looped over observations
533 749  
534 750 // Return
535 751 return;
... ... @@ -537,16 +753,17 @@ void ctskymap::get_parameters(void)
537 753  
538 754  
539 755 /***********************************************************************//**
540   - * @brief Map events into a sky map
  756 + * @brief Fill events into counts map
541 757 *
542 758 * @param[in] obs CTA observation.
543 759 *
544 760 * @exception GException::no_list
545 761 * No event list found in observation.
546 762 *
547   - * This method maps the events found in a CTA events list into a sky map.
  763 + * Fills the events found in a CTA events list into a sky map. The method
  764 + * adds the events to the m_counts member.
548 765 ***************************************************************************/
549   -void ctskymap::map_events(GCTAObservation* obs)
  766 +void ctskymap::fill_maps_counts(GCTAObservation* obs)
550 767 {
551 768 // Get non-const pointer on a CTA event list
552 769 GCTAEventList* events = dynamic_cast<GCTAEventList*>
... ... @@ -589,11 +806,11 @@ void ctskymap::map_events(GCTAObservation* obs)
589 806 // Determine sky pixel
590 807 GCTAInstDir* inst = (GCTAInstDir*)&(event->dir());
591 808 GSkyDir dir = inst->dir();
592   - GSkyPixel pixel = m_skymap.dir2pix(dir);
  809 + GSkyPixel pixel = m_counts.dir2pix(dir);
593 810  
594 811 // Skip if pixel is out of range
595   - if (pixel.x() < -0.5 || pixel.x() > (m_skymap.nx() - 0.5) ||
596   - pixel.y() < -0.5 || pixel.y() > (m_skymap.ny() - 0.5)) {
  812 + if (pixel.x() < -0.5 || pixel.x() > (m_counts.nx() - 0.5) ||
  813 + pixel.y() < -0.5 || pixel.y() > (m_counts.ny() - 0.5)) {
597 814 num_outside_map++;
598 815 continue;
599 816 }
... ... @@ -605,49 +822,25 @@ void ctskymap::map_events(GCTAObservation* obs)
605 822 }
606 823  
607 824 // Fill event in skymap
608   - m_skymap(pixel, 0) += 1.0;
  825 + #pragma omp critical(ctskymap_map_events)
  826 + m_counts(pixel) += 1.0;
609 827 num_in_map++;
610   -
  828 +
611 829 } // endfor: looped over all events
612 830  
613 831 // Log binning results
614   - log_value(NORMAL, "Events in list", obs->events()->size());
615   - log_value(NORMAL, "Events in map", num_in_map);
616   - log_value(NORMAL, "Events outside RoI", num_outside_roi);
617   - log_value(NORMAL, "Events outside map area", num_outside_map);
618   - log_value(NORMAL, "Events outside energies", num_outside_erange);
619   -
620   - // Write sky map into header
621   - log_header1(EXPLICIT, "Sky map");
622   - log_string(EXPLICIT, m_skymap.print(m_chatter));
623   -
624   - // Return
625   - return;
626   -}
627   -
628   -
629   -/***********************************************************************//**
630   - * @brief Estimates the background in sky map for observation
631   - *
632   - * @param[in] obs CTA observation.
633   - *
634   - * Estimates the background in the sky map for a given observation and adds
635   - * this estimate to the background sky map. The background estimation method
636   - * is specified by the "bkgsubtract" parameter which can take the following
637   - * values:
638   - *
639   - * NONE - No background estimation
640   - * IRF - Background estimation based on IRF template
641   - * RING - Ring background estimation
642   - ***************************************************************************/
643   -void ctskymap::map_background(GCTAObservation* obs)
644   -{
645   - // Dispatch to appropriate background estimation method
646   - if (m_bkgsubtract == "IRF") {
647   - map_background_irf(obs);
648   - }
649   - else if (m_bkgsubtract == "RING") {
650   - map_background_ring(obs);
  832 + #pragma omp critical(ctskymap_map_events)
  833 + {
  834 + log_header3(TERSE, get_obs_header(obs));
  835 + log_value(NORMAL, "Events in list", obs->events()->size());
  836 + log_value(NORMAL, "Events in map", num_in_map);
  837 + log_value(NORMAL, "Events outside RoI", num_outside_roi);
  838 + log_value(NORMAL, "Events outside map area", num_outside_map);
  839 + log_value(NORMAL, "Events outside energies", num_outside_erange);
  840 +
  841 + // Write sky map into header
  842 + log_header1(EXPLICIT, "Sky map");
  843 + log_string(EXPLICIT, m_counts.print(m_chatter));
651 844 }
652 845  
653 846 // Return
... ... @@ -656,7 +849,7 @@ void ctskymap::map_background(GCTAObservation* obs)
656 849  
657 850  
658 851 /***********************************************************************//**
659   - * @brief Estimates the background in sky map based on IRF template
  852 + * @brief Compute background acceptance sky map based on IRF template
660 853 *
661 854 * @param[in] obs CTA observation.
662 855 *
... ... @@ -664,14 +857,14 @@ void ctskymap::map_background(GCTAObservation* obs)
664 857 * No response information available for observation.
665 858 * No background template available in instrument response function.
666 859 *
667   - * Estimates the background in the sky map using the IRF template for a given
668   - * observation and adds this estimate to the background sky map. The IRF
669   - * template is integrated numerically in energy.
  860 + * Computes a background acceptance sky map using the IRF template for a
  861 + * given observation and adds the estimate to the acceptance sky map.
670 862 ***************************************************************************/
671   -void ctskymap::map_background_irf(GCTAObservation* obs)
  863 +void ctskymap::fill_maps_acceptance(GCTAObservation* obs)
672 864 {
673 865 // Get IRF response
674   - const GCTAResponseIrf* rsp = dynamic_cast<const GCTAResponseIrf*>(obs->response());
  866 + const GCTAResponseIrf* rsp = dynamic_cast<const GCTAResponseIrf*>
  867 + (obs->response());
675 868  
676 869 // Throw an exception if observation has no instrument response function
677 870 if (rsp == NULL) {
... ... @@ -694,60 +887,58 @@ void ctskymap::map_background_irf(GCTAObservation* obs)
694 887 throw GException::invalid_value(G_MAP_BACKGROUND_IRF, msg);
695 888 }
696 889  
697   - // Compute natural logarithm of energy range in MeV
698   - double lnEmin = std::log(m_emin * 1.0e6);
699   - double lnEmax = std::log(m_emax * 1.0e6);
  890 + // Set minimum and maximum energy as GEnergy instances
  891 + GEnergy emin(m_emin, "TeV");
  892 + GEnergy emax(m_emax, "TeV");
700 893  
701   - // Extract region of interest from observation
702   - GCTARoi roi = obs->roi();
  894 + // Extract region of interest center and radius from observation and
  895 + // compute the cosine of the RoI radius. This allows to mimimize as
  896 + // much as possible the trigonometric computations. Note that the cosine
  897 + // of an angle is maximal for an angle of zero. This explains later why
  898 + // we chose ">=" to test whether an angle is smaller than a given radius.
  899 + GSkyDir roi_centre = obs->roi().centre().dir();
  900 + double cos_roi_radius = std::cos(obs->roi().radius() * gammalib::deg2rad);
  901 +
  902 + // Extract exposure
  903 + double exposure = obs->livetime();
703 904  
704 905 // Initialise statistics
705   - int calls = 0;
706 906 double total = 0.0;
707 907  
708   - // Loop over all background map pixels
709   - for (int i = 0; i < m_bkgmap.npix(); ++i) {
  908 + // Loop over all acceptance map pixels
  909 + for (int i = 0; i < m_acceptance.npix(); ++i) {
710 910  
711 911 // Get sky direction of pixel
712   - GSkyDir skydir = m_bkgmap.inx2dir(i);
713   -
714   - // Convert sky direction in instrument direction
715   - GCTAInstDir instdir = obs->pointing().instdir(skydir);
  912 + GSkyDir& skydir = m_dirs[i];
716 913  
717   - // If RoI is valid and instrument direction is not within RoI then
718   - // skip pixel
719   - if (roi.is_valid() && !roi.contains(instdir)) {
  914 + // Skip pixel if it is not within the RoI
  915 + if (roi_centre.cos_dist(skydir) < cos_roi_radius) {
720 916 continue;
721 917 }
722 918  
723   - // Setup integration function
724   - ctskymap::irf_kern integrand(bkg, &instdir);
725   - GIntegral integral(&integrand);
726   -
727   - // Set precision (has been carefully adjusted using a test simulation
728   - // over the energy range 20 GeV - 120 TeV)
729   - integral.eps(1.0e-6);
  919 + // Convert sky direction to instrument direction
  920 + GCTAInstDir instdir = obs->pointing().instdir(skydir);
730 921  
731   - // Do Romberg integration
732   - double value = integral.romberg(lnEmin, lnEmax);
  922 + // Compute background value
  923 + double value = bkg->rate_ebin(instdir, emin, emax);
733 924  
734   - // Update number of background function calls
735   - calls += integral.calls();
736   -
737 925 // Multiply background rate with livetime and solid angle
738   - value *= obs->livetime() * m_solidangle[i];
  926 + value *= exposure * m_solidangle[i];
739 927  
740   - // Add number of background events to background map
741   - m_bkgmap(i,0) += value;
  928 + // Add number of background events to acceptance map
  929 + #pragma omp critical(ctskymap_map_acceptance)
  930 + m_acceptance(i) += value;
742 931  
743 932 // Update total number of background events
744 933 total += value;
745 934  
746   - } // endfor: looped over background map pixels
  935 + } // endfor: looped over acceptance map pixels
747 936  
748   - // Log background subtraction results
749   - log_value(NORMAL, "Events in background", int(total+0.5));
750   - log_value(NORMAL, "Background evaluations", calls);
  937 + // Log acceptance results
  938 + #pragma omp critical(ctskymap_map_events)
  939 + {
  940 + log_value(NORMAL, "Events in background", int(total+0.5));
  941 + }
751 942  
752 943 // Return
753 944 return;
... ... @@ -755,97 +946,99 @@ void ctskymap::map_background_irf(GCTAObservation* obs)
755 946  
756 947  
757 948 /***********************************************************************//**
758   - * @brief Estimates the background in sky map based on the ring background
759   - * method
  949 + * @brief Compute sky map, background map and significance map
760 950 *
761   - * @param[in] obs CTA observation.
  951 + * Computes the sky map, background map and significance map. The following
  952 + * background subtraction methods are supported:
762 953 *
763   - * @exception GException::invalid_value
764   - * No response information available for observation.
765   - * No background template available in instrument response function.
  954 + * NONE
  955 + * ====
  956 + *
  957 + * The sky map is simply the binned counts map, no background and
  958 + * significance maps are computed.
  959 + *
  960 + * IRF
  961 + * ===
  962 + *
  963 + * The background map is the acceptance map, and this background map is
  964 + * subtracted from the counts map and assigned to the sky map. The
  965 + * significance \f$\sigma_i\f$ is computed assuming Poisson statistic in
  966 + * the Gaussian limit, using
  967 + *
  968 + * \f[\sigma_i = \frac{N_i - B_i}{\sqrt{N_i}}\f]
766 969 *
767   - * Estimates the background in the sky map by summing the events within a
768   - * ring centered at a given pixel's position. The pixels in the ring are
769   - * weighted also by the background IRF value.
  970 + * where
  971 + * \f$N_i\f$ is the number of observed counts and
  972 + * \f$B_i\f$ is the estimated number of background counts.
  973 + *
  974 + * RING
  975 + * ====
  976 + *
  977 + * For the @c RING method the Li & Ma significance is computed for each
  978 + * pixel, using the @c roiradius, @c inradius and @c outradius parameters
  979 + * to specify the radius of the On region and the Off background ring,
  980 + * respectively. Depending on the @c usefft parameter, either an FFT is used
  981 + * to compute the number of events and the acceptance of the On and Off
  982 + * regions, or a direct computation is performed. While the former is faster
  983 + * it is less accurate since it assumes Euclidean distances in the sky map.
  984 + * The latter is slower but compute exact distance between pixels of the sky
  985 + * map. Use FFT if the sky map is close to a cartesian grid, and use the
  986 + * direct method if the sky map shows important distortions.
770 987 ***************************************************************************/
771   -void ctskymap::map_background_ring(GCTAObservation* obs)
  988 +void ctskymap::compute_maps(void)
772 989 {
773   - // Get IRF response (to scale background counts)
774   - const GCTAResponseIrf* rsp = dynamic_cast<const GCTAResponseIrf*>(obs->response());
  990 + // Write header into logger
  991 + log_header1(TERSE, "Compute maps");
775 992  
776   - // Throw an exception if observation has no instrument response function
777   - if (rsp == NULL) {
778   - std::string msg = "No response information available for "+
779   - get_obs_header(obs)+" to compute IRF background. "
780   - "Please specify response information or use "
781   - "another background subtraction method.";
782   - throw GException::invalid_value(G_MAP_BACKGROUND_RING, msg);
  993 + // If no background subtraction is requested then simply assign the
  994 + // counts map to the skymap
  995 + if (m_bkgsubtract == "NONE") {
  996 + m_skymap = m_counts;
783 997 }
784 998  
785   - // Get IRF background template
786   - const GCTABackground* bkg = rsp->background();
787   -
788   - // Throw an exception if observation has no IRF background template
789   - if (bkg == NULL) {
790   - std::string msg = "No IRF background template found in instrument "
791   - "response function for "+
792   - get_obs_header(obs)+". Please specify an instrument "
793   - "response function containing a background template.";
794   - throw GException::invalid_value(G_MAP_BACKGROUND_RING, msg);
  999 + // else if IRF background is selected then use the acceptance as background
  1000 + else if (m_bkgsubtract == "IRF") {
  1001 + m_skymap = m_counts - m_acceptance;
  1002 + m_bkgmap = m_acceptance;
  1003 + m_sigmap = (m_counts - m_acceptance) / sqrt(m_counts);
795 1004 }
796 1005  
797   - // Compute natural logarithm of energy range in MeV
798   - double lnEmin = std::log(m_emin * 1.0e6);
799   - double lnEmax = std::log(m_emax * 1.0e6);
800   -
801   - // Extract region of interest from observation
802   - GCTARoi roi = obs->roi();
803   -
804   - // Initialise statistics
805   - int calls = 0;
806   - double exposure = obs->livetime();
807   -
808   - // Loop over all map pixels
809   - for (int i = 0; i < m_bkgmap.npix(); ++i) {
810   -
811   - // Get sky direction of pixel
812   - GSkyDir& skydir = m_dirs[i];
  1006 + // else if RING background is selected then compute the On
  1007 + else if (m_bkgsubtract == "RING") {
813 1008  
814   - // Convert sky direction in instrument direction
815   - GCTAInstDir instdir = obs->pointing().instdir(skydir);
816   -
817   - // If RoI is valid and instrument direction is not within RoI then
818   - // skip pixel
819   - if (roi.is_valid() && !roi.contains(instdir)) {
820   - continue;
  1009 + // Compute initial RING background
  1010 + if (m_usefft) {
  1011 + compute_maps_ring_fft();
  1012 + }
  1013 + else {
  1014 + compute_maps_ring_direct();
821 1015 }
822 1016  
823   - // Setup integration function
824   - ctskymap::irf_kern integrand(bkg, &instdir);
825   - GIntegral integral(&integrand);
826   -
827   - // Set precision
828   - integral.eps(1.0e-6);
829   -
830   - // Do Romberg integration to get the sensitivity (note: this assumes
831   - // the background IRF is a good approximation for the radial
832   - // sensitivity)
833   - m_alphamap(i,0) += integral.romberg(lnEmin, lnEmax) *
834   - m_solidangle[i] * exposure;
835   -
836   - // Store the counts in this bin
837   - m_onmap(i,0) += m_skymap(i,0);
  1017 + // Store copy of exclusion regions
  1018 + GSkyMap exclmap = m_exclmap;
838 1019  
839   - // Update number of background function calls
840   - calls += integral.calls();
  1020 + // Iterative computation of exclusion regions
  1021 + for (int iter = 0; iter < m_iterations; ++iter) {
841 1022  
842   - } // endfor: Loop for caching bkg IRF sensitivity
  1023 + // Set exclusion region from significance map
  1024 + m_exclmap = exclmap;
  1025 + for (int i = 0; i < m_exclmap.npix(); ++i) {
  1026 + if (m_sigmap(i) > m_threshold) {
  1027 + m_exclmap(i) = 1.0;
  1028 + }
  1029 + }
  1030 +
  1031 + // Re-compute RING background
  1032 + if (m_usefft) {
  1033 + compute_maps_ring_fft();
  1034 + }
  1035 + else {
  1036 + compute_maps_ring_direct();
  1037 + }
843 1038  
844   - // Log background subtraction results
845   - log_value(NORMAL, "Alpha map evaluations", calls);
  1039 + } // endfor: looped over iterations
846 1040  
847   - // Zero out the counts map to prepare it for the next observation
848   - m_skymap = 0.0;
  1041 + }
849 1042  
850 1043 // Return
851 1044 return;
... ... @@ -853,24 +1046,85 @@ void ctskymap::map_background_ring(GCTAObservation* obs)
853 1046  
854 1047  
855 1048 /***********************************************************************//**
856   - * @brief Write keywords in FITS HDU
857   - *
858   - * @param[in,out] hdu Pointer to FITS HDU.
  1049 + * @brief Compute the maps for RING background using a FFT
859 1050 *
860   - * Writes keywords in FITS HDU.
  1051 + * Computes the Li & Ma significance for each sky map pixel and replaces
  1052 + * the sky and background maps by the On- and Off-count maps. The computation
  1053 + * is done using a FFT.
861 1054 ***************************************************************************/
862   -void ctskymap::write_hdu_keywords(GFitsHDU* hdu) const
  1055 +void ctskymap::compute_maps_ring_fft(void)
863 1056 {
864   - // Continue only if pointer is valid
865   - if (hdu != NULL) {
  1057 + // Log message about what is being done
  1058 + log_header3(NORMAL, "Computing ring background map (FFT method)");
866 1059  
867   - // Set keywords
868   - hdu->card("BKGSUB", m_bkgsubtract, "Background substraction method");
869   - hdu->card("E_MIN", m_emin, "[TeV] Lower energy boundary");
870   - hdu->card("E_MAX", m_emax, "[TeV] Upper energy boundary");
871   - hdu->card("EUNIT", "TeV", "Units for E_MIN and E_MAX");
  1060 + // Initialise maps
  1061 + m_skymap = m_counts;
  1062 + m_bkgmap = m_counts;
  1063 + m_sigmap = m_counts;
  1064 + m_skymap = 0.0;
  1065 + m_bkgmap = 0.0;
  1066 + m_sigmap = 0.0;
872 1067  
873   - } // endif: pointer was valid
  1068 + // Initialise statistics
  1069 + int num_bad_alpha = 0;
  1070 +
  1071 + // Set-up sky and acceptance maps with excluded pixels according to the
  1072 + // exclusion map
  1073 + GSkyMap excl_counts = m_counts;
  1074 + GSkyMap excl_acceptance = m_acceptance;
  1075 + for (int i = 0; i < m_counts.npix(); ++i) {
  1076 + if (m_exclmap(i) != 0.0) {
  1077 + excl_counts(i) = 0.0;
  1078 + excl_acceptance(i) = 0.0;
  1079 + }
  1080 + }
  1081 +
  1082 + // Convolve maps for On-region and Off-region
  1083 + GSkyMap on_counts = ring_convolve(m_counts, 0.0, m_roiradius);
  1084 + GSkyMap on_alpha = ring_convolve(m_acceptance, 0.0, m_roiradius);
  1085 + GSkyMap off_counts = ring_convolve(excl_counts, m_inradius, m_outradius);
  1086 + GSkyMap off_alpha = ring_convolve(excl_acceptance, m_inradius, m_outradius);
  1087 +
  1088 + // Compute maps by loop over sky map pixels
  1089 + for (int i = 0; i < m_sigmap.npix(); ++i) {
  1090 +
  1091 + // Get On-counts, Off-counts and alpha value for this bin
  1092 + double n_on = on_counts(i);
  1093 + double n_off = off_counts(i);
  1094 + double alpha;
  1095 + if (off_alpha(i) == 0.0) {
  1096 + alpha = 0.0;
  1097 + n_on = 0.0;
  1098 + n_off = 0.0;
  1099 + }
  1100 + else {
  1101 + alpha = on_alpha(i) / off_alpha(i);
  1102 + }
  1103 +
  1104 + // Store the On and alpha-weighted Off-counts
  1105 + m_bkgmap(i) = alpha * n_off;
  1106 + m_skymap(i) = n_on - m_bkgmap(i);
  1107 +
  1108 + // If alpha is zero then increment the bad alpha counter
  1109 + if (alpha == 0.0) {
  1110 + num_bad_alpha++;
  1111 + }
  1112 +
  1113 + // ... otherwise compute and store the results
  1114 + else {
  1115 + m_sigmap(i) = sigma_li_ma(n_on, n_off, alpha);
  1116 + }
  1117 +
  1118 + } // endfor: looped over all pixels
  1119 +
  1120 + // Log the number of bad-alpha bins
  1121 + log_value(NORMAL, "Total number of pixels", m_sigmap.npix());
  1122 + log_value(NORMAL, "Pixels with alpha=0", num_bad_alpha);
  1123 +
  1124 + // Now take the square root. Since some bins can be negative, first
  1125 + // take the absolute value of the map, square root that, then multiply
  1126 + // each bin by its sign to preserve the +/- significance.
  1127 + m_sigmap = sign(m_sigmap) * sqrt(abs(m_sigmap));
874 1128  
875 1129 // Return
876 1130 return;
... ... @@ -878,89 +1132,85 @@ void ctskymap::write_hdu_keywords(GFitsHDU* hdu) const
878 1132  
879 1133  
880 1134 /***********************************************************************//**
881   - * @brief Computes the bin-by-bin significance if background subtraction
882   - * requested
  1135 + * @brief Compute the pixel significance for RING background
883 1136 *
884   - * Method computes the bin-by bin significance if a background subtraction
885   - * method is specified. If method is "IRF", Poisson statistics (in the
886   - * Gaussian limit) are assumed. If method is "RING" a Li & Ma significance
887   - * is computed for each bin (as well as a map of the computed alpha values).
  1137 + * Computes the Li & Ma significance for each sky map pixel and replaces
  1138 + * the sky and background maps by the On- and Off-count maps. The computation
  1139 + * is done by computing the exact distances between pixels in the sky map.
888 1140 ***************************************************************************/
889   -void ctskymap::map_significance(void)
  1141 +void ctskymap::compute_maps_ring_direct(void)
890 1142 {
891   - // Compute significance from "RING" method (Li & Ma eq. 17)
892   - if (m_bkgsubtract == "RING") {
893   -
894   - // Log message about what is being done
895   - log_header1(NORMAL, "Computing Ring background map");
896   - log_value(NORMAL, "Total pixels to process", m_onmap.npix());
897   -
898   - // Store the number of bins with an inappropriate alpha parameter
899   - int num_bad_alpha = 0;
900   -
901   - // Loop through each bin in the on-counts map
902   - for (int i = 0; i < m_onmap.npix(); ++i) {
903   -
904   - // Initialise the on/off counts and alpha for this bin
905   - double n_on = 0.0;
906   - double n_off = 0.0;
907   - double alpha = 0.0;
  1143 + // Log message about what is being done
  1144 + log_header3(NORMAL, "Computing ring background map (direct method)");
  1145 + log_value(NORMAL, "Total pixels to process", m_counts.npix());
  1146 +
  1147 + // Initialise maps
  1148 + m_skymap = m_counts;
  1149 + m_bkgmap = m_counts;
  1150 + m_sigmap = m_counts;
  1151 + m_skymap = 0.0;
  1152 + m_bkgmap = 0.0;
  1153 + m_sigmap = 0.0;
908 1154  
909   - // Since this can take a long time, keep the user updated on
910   - // the progress when another 10% of pixels is processed
911   - if (i % (m_onmap.npix()/10) == 0) {
912   - log_value(NORMAL, "Pixels remaining", m_onmap.npix()-i);
913   - }
  1155 + // Initialise statistics
  1156 + int num_bad_alpha = 0;
  1157 +
  1158 + // Set progress logging frequency. By default the user is updated on the
  1159 + // progress when another 10% of pixels is processed, however, if there
  1160 + // are many pixels the user is updated each 100000 pixels.
  1161 + int n_logpix = m_counts.npix() / 10;
  1162 + if (n_logpix > 100000) {
  1163 + n_logpix = 100000;
  1164 + }
914 1165  
915   - // Get bin coordinates
916   - GSkyDir& skydir = m_dirs[i];
  1166 + // Compute maps by loop over sky map pixels
  1167 + #pragma omp parallel for
  1168 + for (int i = 0; i < m_sigmap.npix(); ++i) {
917 1169  
918   - // Compute the alpha and counts for this bin
919   - compute_ring_values(m_onmap, m_alphamap, skydir,
920   - n_on, n_off, alpha);
  1170 + // Initialise the on/off counts and alpha for this bin
  1171 + double n_on = 0.0;
  1172 + double n_off = 0.0;
  1173 + double alpha = 0.0;
921 1174  
922   - // If alpha is zero then increment the bad alpha counter
923   - if (alpha == 0.0) {
924   - num_bad_alpha++;
925   - }
  1175 + // Update user about progress (only if there are more than 10000 pixels)
  1176 + if (i % n_logpix == 0 && n_logpix > 10000) {
  1177 + #pragma omp critical(ctskymap_map_significance_ring)
  1178 + log_value(NORMAL, "Pixels remaining", m_sigmap.npix()-i);
  1179 + }
926 1180  
927   - // ... otherwise store the results
928   - else {
  1181 + // Compute the alpha and counts for this bin
  1182 + compute_ring_values(i, m_counts, m_acceptance, n_on, n_off, alpha);
929 1183  
930   - // Store the on-counts and alpha-weighted off-counts
931   - m_skymap(i,0) = n_on;
932   - m_bkgmap(i,0) = alpha * n_off;
  1184 + // Store the On and alpha-weighted Off-counts
  1185 + m_bkgmap(i) = alpha * n_off;
  1186 + m_skymap(i) = n_on - m_bkgmap(i);
933 1187  
934   - // Compute and store significance (Li & Ma eq. 17)
935   - if (n_on == 0.0) {
936   - m_sigmap(i,0) = -2.0 * n_off * std::log(1.0+alpha);
937   - }
938   - else {
939   - m_sigmap(i,0) = (n_on < (alpha*n_off) ? -2.0 : 2.0) *
940   - (n_on * std::log((1.0+alpha) *
941   - n_on / (alpha * (n_on+n_off))) +
942   - n_off * std::log((1.0+alpha) *
943   - n_off / (n_on+n_off)));
944   - }
  1188 + // If alpha is zero then increment the bad alpha counter
  1189 + if (alpha == 0.0) {
  1190 + num_bad_alpha++;
  1191 + #pragma omp critical(ctskymap_compute_maps_ring_direct)
  1192 + m_sigmap(i) = 0.0;
  1193 + }
945 1194  
946   - } // endelse: alpha was non-zero
  1195 + // ... otherwise store the results
  1196 + else {
947 1197  
948   - } // endfor: looped over all pixels
  1198 + // Compute and store significance (Li & Ma eq. 17)
  1199 + double sigma = sigma_li_ma(n_on, n_off, alpha);
  1200 + #pragma omp critical(ctskymap_compute_maps_ring_direct)
  1201 + m_sigmap(i) = sigma;
949 1202  
950   - // Log the number of bad-alpha bins
951   - log_value(NORMAL, "Bins with alpha=0", num_bad_alpha);
  1203 + } // endelse: alpha was non-zero
952 1204  
953   - // Now take the square root. Since some bins can be negative, first
954   - // take the absolute value of the map, square root that, then multiply
955   - // each bin by its sign to preserve the +/- significance.
956   - m_sigmap = sign(m_sigmap) * sqrt(abs(m_sigmap));
  1205 + } // endfor: looped over all pixels
957 1206  
958   - } // endif: ring background selected
  1207 + // Log the number of bad-alpha bins
  1208 + log_value(NORMAL, "Pixels with alpha=0", num_bad_alpha);
959 1209  
960   - // Compute significance using Poisson statistics in the Gaussian limit
961   - else {
962   - m_sigmap = (m_skymap - m_bkgmap) / sqrt(m_skymap);
963   - }
  1210 + // Now take the square root. Since some bins can be negative, first
  1211 + // take the absolute value of the map, square root that, then multiply
  1212 + // each bin by its sign to preserve the +/- significance.
  1213 + m_sigmap = sign(m_sigmap) * sqrt(abs(m_sigmap));
964 1214  
965 1215 // Return
966 1216 return;
... ... @@ -970,66 +1220,107 @@ void ctskymap::map_significance(void)
970 1220 /***********************************************************************//**
971 1221 * @brief Computes Non, Noff and alpha for a counts map and sensitivity map
972 1222 *
973   - * @param[in] counts Counts map
974   - * @param[in] sensitivity Sensitivity map
975   - * @param[in] position Position of the ring & roi centers
976   - * @param[out] non Returned estimate of ON counts
977   - * @param[out] noff Returned estimate of OFF counts
978   - * @param[out] alpha Returned estimate of alpha
  1223 + * @param[in] ipixel Sky map pixel to consider.
  1224 + * @param[in] counts Counts map.
  1225 + * @param[in] background Background map.
  1226 + * @param[out] non Returned estimate of On-counts.
  1227 + * @param[out] noff Returned estimate of Off-counts.
  1228 + * @param[out] alpha Returned estimate of alpha.
979 1229 *
980   - * This method computes the Non counts Noff values at a given position. It
981   - * also computes the alpha parameter from the passed sensitivity map.
  1230 + * Computes the On- and Off-counts values at a given position from in counts
  1231 + * map and the alpha parameter from the background map.
  1232 + *
  1233 + * To speed-up the computations the method considers only the pixels within
  1234 + * a bounding box that comprises the outer background ring radius. The method
  1235 + * considers wrapping around of pixel indices in the Right Ascension /
  1236 + * Galactic longitude direction.
  1237 + *
  1238 + * If the alpha of the ring region is zero the values of @p non, @p noff,
  1239 + * and @p alpha will be set to zero.
982 1240 ***************************************************************************/
983   -void ctskymap::compute_ring_values(const GSkyMap& counts,
984   - const GSkyMap& sensitivity,
985   - const GSkyDir& position,
  1241 +void ctskymap::compute_ring_values(const int& ipixel,
  1242 + const GSkyMap& counts,
  1243 + const GSkyMap& background,
986 1244 double& non,
987 1245 double& noff,
988 1246 double& alpha)
989 1247 {
990   - // Reset Non and Noff
991   - non = 0.0;
992   - noff = 0.0;
  1248 + // Initialise return values (non, noff and alpha)
  1249 + non = 0.0;
  1250 + noff = 0.0;
  1251 + alpha = 0.0;
993 1252  
994 1253 // Initialise On and Off alpha
995 1254 double alpha_on = 0.0;
996 1255 double alpha_off = 0.0;
997 1256  
998   - // Define the regions necessary to do the pixel checks
999   - GSkyRegionCircle roi_reg(position, m_roiradius);
1000   - GSkyRegionCircle inner_reg(position, m_inradius);
1001   - GSkyRegionCircle outer_reg(position, m_outradius);
  1257 + // Get sky direction of pixel
  1258 + GSkyDir& position = m_dirs[ipixel];
1002 1259  
1003   - // Loop over every pixel in the observation to compute Non, Noff
1004   - for (int j = 0; j < counts.npix(); ++j) {
  1260 + // Get pixel bounding box
  1261 + int ix_start;
  1262 + int ix_stop;
  1263 + int iy_start;
  1264 + int iy_stop;
  1265 + ring_bounding_box(ipixel, ix_start, ix_stop, iy_start, iy_stop);
1005 1266  
1006   - // Get the index and sky direction of this pixel
1007   - GSkyDir& skydir = m_dirs[j];
  1267 + // Get number of x pixels in sky map
  1268 + int nx = counts.nx();
1008 1269  
1009   - // Check if pixel is inside the background region
1010   - if ((m_exclmap(j,0) == 0.0) && outer_reg.contains(skydir) &&
1011   - !inner_reg.contains(skydir)) {
  1270 + // Loop over x pixels of bounding box
  1271 + for (int ix_nowrap = ix_start; ix_nowrap < ix_stop; ++ix_nowrap) {
1012 1272  
1013   - // Update n_off
1014   - noff += counts(j,0);
  1273 + // Compute x pixel index by wrapping the pixel index into the
  1274 + // interval [0,nx[
  1275 + int ix = ix_nowrap;
  1276 + if (ix < 0) {
  1277 + ix += nx;
  1278 + }
  1279 + else if (ix >= nx) {
  1280 + ix -= nx;
  1281 + }
1015 1282  
1016   - // Update alpha_off
1017   - alpha_off += sensitivity(j,0);
  1283 + // Initialise pixel index
  1284 + int i = ix + iy_start * nx;
1018 1285  
1019   - }
  1286 + // Loop over y pixels of bounding box
  1287 + for (int iy = iy_start; iy < iy_stop; ++iy, i += nx) {
  1288 +
  1289 + // Get the index and sky direction of this pixel
  1290 + GSkyDir& skydir = m_dirs[i];
  1291 +
  1292 + // Only consider pixels within the outer radius of the background
  1293 + // region
  1294 + if (position.cos_dist(skydir) >= m_cos_outradius) {
  1295 +
  1296 + // Check if pixel is inside the background region
  1297 + if ((m_exclmap(i) == 0.0) &&
  1298 + (position.cos_dist(skydir) < m_cos_inradius)) {
  1299 +
  1300 + // Update n_off
  1301 + noff += counts(i);
  1302 +
  1303 + // Update alpha_off
  1304 + alpha_off += background(i);
  1305 +
  1306 + }
  1307 +
  1308 + // ... otherwise check if pixel is inside source region
  1309 + else if (position.cos_dist(skydir) >= m_cos_roiradius) {
  1310 +
  1311 + // Update n_on
  1312 + non += counts(i);
1020 1313  
1021   - // ... otherwise check if pixel is inside source region
1022   - else if (roi_reg.contains(skydir)) {
  1314 + // Update alpha_on
  1315 + alpha_on += background(i);
1023 1316  
1024   - // Update n_on for significance computation
1025   - non += counts(j,0);
  1317 + } // endif: source and background region check
1026 1318  
1027   - // Update alpha_on
1028   - alpha_on += sensitivity(j,0);
  1319 + } // endif: pixel is within outer radius of background region
1029 1320  
1030   - } // endif: source and background region check
  1321 + } // endfor: looped over y pixels
1031 1322  
1032   - } // endfor: looped over pixels
  1323 + } // endfor: looped over x pixels
1033 1324  
1034 1325 // Compute alpha. If the off region does not have any sensitivity then
1035 1326 // set Non = Noff = 0
... ... @@ -1048,34 +1339,100 @@ void ctskymap::compute_ring_values(const GSkyMap&amp; counts,
1048 1339  
1049 1340  
1050 1341 /***********************************************************************//**
1051   - * @brief Generates map of pixel exclusions
  1342 + * @brief Computes bounding box for RING background computation
1052 1343 *
1053   - * @param[in] filename Exclusion file name.
  1344 + * @param[in] ipixel Sky map pixel to consider.
  1345 + * @param[out] ix1 Index of first pixel in x.
  1346 + * @param[out] ix2 Index after last pixel in x.
  1347 + * @param[out] iy1 Index of first pixel in y.
  1348 + * @param[out] iy2 Index after last pixel in y.
1054 1349 *
1055   - * Generates a sky map of the pixels that are to be excluded from the
1056   - * background estimation. Pixels with values different from 0 will be
1057   - * excluded.
  1350 + * Computes the bounding box that contained the background ring for a
  1351 + * specific pixel for the RING background method.
  1352 + *
  1353 + * The method determines the local pixel scale for the requested pixel and
  1354 + * draws a bounding box with 1.5 times the outer background ring radius
  1355 + * around the pixel. In the x direction the pixel indices are unconstrained,
  1356 + * and the client has to assure that the pixel value is comprised within
  1357 + * the validity range (this is required to handle the longitude wrap around).
  1358 + * In the y direction the pixel indices are constrained to [0,ny], where
  1359 + * ny is the number of y pixels in the sky map.
1058 1360 ***************************************************************************/
1059   -void ctskymap::map_exclusions(const GFilename& filename)
  1361 +void ctskymap::ring_bounding_box(const int& ipixel, int& ix1, int& ix2,
  1362 + int& iy1, int& iy2) const
1060 1363 {
1061   - // Create exlusion map
1062   - m_exclmap = create_map(m_obs);
  1364 + // Get number of pixels in x and y direction
  1365 + int nx = m_counts.nx();
  1366 + int ny = m_counts.ny();
  1367 +
  1368 + // Get x and y index of pixel to consider
  1369 + int ix0 = ipixel % nx;
  1370 + int iy0 = ipixel / nx;
  1371 +
  1372 + // Get pointer on WCS projection
  1373 + const GWcs* wcs = dynamic_cast<const GWcs*>(m_counts.projection());
  1374 + if (wcs == NULL) {
  1375 + std::string msg = "Sky map is not a WCS projection. Method is only "
  1376 + "valid for WCS projections.";
  1377 + throw GException::invalid_value(G_RING_BOUNDING_BOX, msg);
  1378 + }
1063 1379  
1064   - // Set all pixels to 0 (no pixel excluded)
1065   - m_exclmap = 0.0;
  1380 + // Get X and Y step size
  1381 + double dx_binsz = wcs->cdelt(0);
  1382 + double dy_binsz = wcs->cdelt(1);
1066 1383  
1067   - // Make sure the exclusions filename is valid
1068   - if (is_valid_filename(filename)) {
  1384 + // Compute pixel increment in x-direction
  1385 + double dx = 0.0;
  1386 + int ix = (ix0 > 0) ? ix0 - 1 : ix0 + 1;
  1387 + if (ix < nx) {
  1388 + dx = m_dirs[ipixel].dist_deg(m_dirs[ix+iy0*nx]);
  1389 + }
  1390 + if (dx < dx_binsz) {
  1391 + dx = dx_binsz;
  1392 + }
1069 1393  
1070   - // Fill the exclusions based on the regions supplied
1071   - if (filename.is_fits()) {
1072   - map_exclusions_fits(filename);
  1394 + // Compute pixel increment in y-direction
  1395 + double dy = 0.0;
  1396 + int iy = (iy0 > 0) ? iy0 - 1 : iy0 + 1;
  1397 + if (iy < ny) {
  1398 + dx = m_dirs[ipixel].dist_deg(m_dirs[ix0+iy*nx]);
  1399 + }
  1400 + if (dy < dy_binsz) {
  1401 + dy = dy_binsz;
  1402 + }
  1403 +
  1404 + // Compute bounding box half size in x and y. The outer radius is
  1405 + // multiplied by 1.5 to have some margin in case of map distortions
  1406 + int nx_bbox = int(1.5 * m_outradius / dx);
  1407 + int ny_bbox = int(1.5 * m_outradius / dy);
  1408 +
  1409 + // Compute index range for x. We allow that indices are smaller than 0
  1410 + // or equal or larger than nx to handle wrap around, which needs to be
  1411 + // done in the client method.
  1412 + if (2*nx_bbox < nx) {
  1413 + ix1 = ix0 - nx_bbox;
  1414 + ix2 = ix0 + nx_bbox;
  1415 + }
  1416 + else {
  1417 + ix1 = 0;
  1418 + ix2 = nx;
  1419 + }
  1420 +
  1421 + // Compute index range for y. The index range is restricted to [0,ny].
  1422 + if (2*ny_bbox < ny) {
  1423 + iy1 = iy0 - ny_bbox;
  1424 + iy2 = iy0 + ny_bbox;
  1425 + if (iy1 < 0) {
  1426 + iy1 = 0;
1073 1427 }
1074   - else {
1075   - map_exclusions_reg(filename);
  1428 + if (iy2 > ny) {
  1429 + iy2 = ny;
1076 1430 }
1077   -
1078   - } // endif: filename was valid
  1431 + }
  1432 + else {
  1433 + iy1 = 0;
  1434 + iy2 = ny;
  1435 + }
1079 1436  
1080 1437 // Return
1081 1438 return;
... ... @@ -1083,64 +1440,166 @@ void ctskymap::map_exclusions(const GFilename&amp; filename)
1083 1440  
1084 1441  
1085 1442 /***********************************************************************//**
1086   - * @brief Fills exclusions map from FITS image
  1443 + * @brief Return FFT kernel for background ring
1087 1444 *
1088   - * @param[in] filename FITS image file name.
  1445 + * @param[in] map Sky map to be convolved with a background ring.
  1446 + * @param[in] rmin Minimum ring radius (degrees).
  1447 + * @param[in] rmax Maximum ring radius (degrees).
  1448 + * @return FFT kernel.
1089 1449 *
1090   - * Sets all exclusion map pixels to 1 that correspond to non-zero pixels in
1091   - * the exclusion sky map FITS file.
  1450 + * Computes the FFT kernel for a backgrund ring.
1092 1451 ***************************************************************************/
1093   -void ctskymap::map_exclusions_fits(const GFilename& filename)
  1452 +GSkyMap ctskymap::ring_convolve(const GSkyMap& map, const double& rmin,
  1453 + const double& rmax) const
1094 1454 {
1095   - // Load the fits image
1096   - GSkyMap inmap(filename);
  1455 + // Copy input map
  1456 + GSkyMap conv_map = map;
1097 1457  
1098   - // Loop through the individual pixels in the exclusion map
1099   - for (int i = 0; i < m_exclmap.npix(); ++i) {
  1458 + // Get FFT of ring kernel
  1459 + GFft fft_kernel(ring_kernel(rmin, rmax));
1100 1460  
1101   - // Get the pixel direction
1102   - GSkyDir dir = m_exclmap.pix2dir(i);
  1461 + // Extract sky map into Ndarray
  1462 + GNdarray array(conv_map.nx(), conv_map.ny());
  1463 + double *dst = array.data();
  1464 + for (int i = 0; i < conv_map.npix(); ++i) {
  1465 + *dst++ = conv_map(i);
  1466 + }
1103 1467  
1104   - // Check this sky position in the fits map
1105   - if (inmap.contains(dir) && (inmap(inmap.dir2inx(dir)) != 0.0)) {
  1468 + // FFT of sky map
  1469 + GFft fft_array = GFft(array);
1106 1470  
1107   - // Set the pixel to 1
1108   - m_exclmap(i) = 1.0;
  1471 + // Multiply FFT of sky map with FFT of kernel
  1472 + GFft fft_smooth = fft_array * fft_kernel;
1109 1473  
1110   - } // endif: pixel,region overlap check
  1474 + // Backward transform sky map
  1475 + GNdarray smooth = fft_smooth.backward();
1111 1476  
1112   - } // endfor: looped over exclusion map pixels
  1477 + // Insert sky map
  1478 + double *src = smooth.data();
  1479 + for (int i = 0; i < conv_map.npix(); ++i) {
  1480 + conv_map(i) = *src++;
  1481 + }
1113 1482  
1114   - // Return
1115   - return;
  1483 + // Return convolved map
  1484 + return conv_map;
1116 1485 }
1117 1486  
1118 1487  
1119 1488 /***********************************************************************//**
1120   - * @brief Fills exclusions map from DS9 region file
  1489 + * @brief Return FFT kernel for background ring
1121 1490 *
1122   - * @param[in] filename DS9 region file name.
  1491 + * @param[in] rmin Minimum ring radius (degrees).
  1492 + * @param[in] rmax Maximum ring radius (degrees).
  1493 + * @return FFT kernel.
1123 1494 *
1124   - * Sets all exclusion map pixels to 1 that are contained in any of the DS9
1125   - * regions.
  1495 + * @exception GException::invalid_value
  1496 + * Sky map is not a WCS projection.
  1497 + *
  1498 + * Computes the FFT kernel for a background ring. The computation is done
  1499 + * assuming that the sky map represents a cartesian grid, and distances are
  1500 + * computed in that grid using Euclidean distances. The @p rmin and @p rmax
  1501 + * arguments refer to a ring radius in these Euclidean distances.
1126 1502 ***************************************************************************/
1127   -void ctskymap::map_exclusions_reg(const GFilename& filename)
  1503 +GNdarray ctskymap::ring_kernel(const double& rmin, const double& rmax) const
1128 1504 {
1129   - // Load the exclusion regions
1130   - GSkyRegions regions(filename);
  1505 + // Get pointer on WCS projection
  1506 + const GWcs* wcs = dynamic_cast<const GWcs*>(m_counts.projection());
  1507 + if (wcs == NULL) {
  1508 + std::string msg = "Sky map is not a WCS projection. Method is only "
  1509 + "valid for WCS projections.";
  1510 + throw GException::invalid_value(G_RING_KERNEL, msg);
  1511 + }
1131 1512  
1132   - // Loop through the individual pixels in the exclusion map
1133   - for (int i = 0; i < m_exclmap.npix(); ++i) {
  1513 + // Get X and Y step size
  1514 + double dx = wcs->cdelt(0);
  1515 + double dy = wcs->cdelt(1);
  1516 +
  1517 + // Initialise kernel
  1518 + GNdarray kern(m_counts.nx(), m_counts.ny());
  1519 +
  1520 + // Fill kernel
  1521 + for (int ix1 = 0, ix2 = m_counts.nx(); ix1 < m_counts.nx(); ++ix1, --ix2) {
  1522 + double x = ix1 * dx;
  1523 + double xqs = x * x;
  1524 + for (int iy1 = 0, iy2 = m_counts.ny(); iy1 < m_counts.ny(); ++iy1, --iy2) {
  1525 + double y = iy1 * dy;
  1526 + double r = std::sqrt(xqs + y*y);
  1527 + if ((r >= rmin) && (r <= rmax)) {
  1528 + kern(ix1,iy1) += 1.0;
  1529 + if (ix2 < m_counts.nx()) {
  1530 + kern(ix2,iy1) += 1.0;
  1531 + }
  1532 + if (iy2 < m_counts.ny()) {
  1533 + kern(ix1,iy2) += 1.0;
  1534 + }
  1535 + if ((ix2 < m_counts.nx()) && (iy2 < m_counts.ny())) {
  1536 + kern(ix2,iy2) += 1.0;
  1537 + }
  1538 + }
  1539 + }
  1540 + }
1134 1541  
1135   - // Get the pixel position
1136   - GSkyDir dir = m_exclmap.pix2dir(i);
  1542 + // Return kernel
  1543 + return kern;
  1544 +}
1137 1545  
1138   - // If pixel position overlaps with the regions
1139   - if (regions.contains(dir)) {
1140   - m_exclmap(i) = 1.0;
1141   - }
1142 1546  
1143   - } // endfor: looped over exclusion map pixels
  1547 +/***********************************************************************//**
  1548 + * @brief Compute significance following Li & Ma
  1549 + *
  1550 + * @param[in] n_on Number of On-counts.
  1551 + * @param[in] n_off Number of Off-counts.
  1552 + * @param[in] alpha Alpha parameters.
  1553 + * @return Significance.
  1554 + *
  1555 + * Computes the significance following Li & Ma, Equation (17).
  1556 + ***************************************************************************/
  1557 +double ctskymap::sigma_li_ma(const double& n_on,
  1558 + const double& n_off,
  1559 + const double& alpha) const
  1560 +{
  1561 + // Allocate result
  1562 + double sigma;
  1563 +
  1564 + // Handle special case of no On-counts
  1565 + if (n_on == 0.0) {
  1566 + sigma = -2.0 * n_off * std::log(1.0+alpha);
  1567 + }
  1568 +
  1569 + // ... otherwise handle general case
  1570 + else {
  1571 + sigma = (n_on < (alpha*n_off) ? -2.0 : 2.0) *
  1572 + (n_on * std::log((1.0+alpha) * n_on / (alpha * (n_on+n_off))) +
  1573 + n_off * std::log((1.0+alpha) * n_off / (n_on+n_off)));
  1574 + }
  1575 +
  1576 + // Return sigma
  1577 + return sigma;
  1578 +}
  1579 +
  1580 +
  1581 +/***********************************************************************//**
  1582 + * @brief Write sky map into FITS file
  1583 + *
  1584 + * @param[in,out] fits FITS file.
  1585 + * @param[in] map Sky map.
  1586 + * @param[in] extname Extension name.
  1587 + *
  1588 + * Write one sky map with the extension name and keywords into the FITS file.
  1589 + ***************************************************************************/
  1590 +void ctskymap::write_map(GFits& fits, const GSkyMap& map, const std::string& extname) const
  1591 +{
  1592 + // Write map into FITS file
  1593 + GFitsHDU* hdu = map.write(fits);
  1594 +
  1595 + // Set map extension name
  1596 + if (hdu != NULL) {
  1597 + hdu->extname(extname);
  1598 + }
  1599 +
  1600 + // Write keywords into map extension
  1601 + write_ogip_keywords(hdu);
  1602 + write_hdu_keywords(hdu);
1144 1603  
1145 1604 // Return
1146 1605 return;
... ... @@ -1148,21 +1607,36 @@ void ctskymap::map_exclusions_reg(const GFilename&amp; filename)
1148 1607  
1149 1608  
1150 1609 /***********************************************************************//**
1151   - * @brief Estimates the background in sky map based on IRF template
  1610 + * @brief Write keywords in FITS HDU
1152 1611 *
1153   - * @param[in] lnE Natural logarithm of energy in MeV.
  1612 + * @param[in,out] hdu Pointer to FITS HDU.
  1613 + *
  1614 + * Writes keywords in FITS HDU.
1154 1615 ***************************************************************************/
1155   -double ctskymap::irf_kern::eval(const double& lnE)
  1616 +void ctskymap::write_hdu_keywords(GFitsHDU* hdu) const
1156 1617 {
1157   - // Get log10 of energy in TeV
1158   - double logE = lnE * gammalib::inv_ln10 - 6;
  1618 + // Continue only if pointer is valid
  1619 + if (hdu != NULL) {
  1620 +
  1621 + // Set keywords
  1622 + hdu->card("BKGSUB", m_bkgsubtract, "Background substraction method");
  1623 + hdu->card("E_MIN", m_emin, "[TeV] Lower energy boundary");
  1624 + hdu->card("E_MAX", m_emax, "[TeV] Upper energy boundary");
  1625 + hdu->card("EUNIT", "TeV", "Units for E_MIN and E_MAX");
1159 1626  
1160   - // Get function value
1161   - double value = (*m_bgd)(logE, m_dir->detx(), m_dir->dety());
  1627 + // Write RING background keywords
  1628 + if (m_bkgsubtract == "RING") {
  1629 + hdu->card("USEFFT", m_usefft, "Use FFT for RING background");
  1630 + hdu->card("ROIRAD", m_roiradius, "[deg] Source region radius");
  1631 + hdu->card("INRAD", m_inradius, "[deg] Inner background ring radius");
  1632 + hdu->card("OUTRAD", m_outradius, "[deg] Outer background ring radius");
  1633 + hdu->card("ITER", m_iterations, "Exclusion map iterations");
  1634 + hdu->card("THRES", m_threshold, "Exclusion map threshold");
  1635 + hdu->card("EXCLMAP", m_inexclusion.url(), "Exclusion map name");
  1636 + }
1162 1637  
1163   - // Correct for variable substitution
1164   - value *= std::exp(lnE);
  1638 + } // endif: pointer was valid
1165 1639  
1166   - // Return value
1167   - return value;
  1640 + // Return
  1641 + return;
1168 1642 }
... ...
src/ctskymap/ctskymap.hpp
1 1 /***************************************************************************
2 2 * ctskymap - Sky mapping tool *
3 3 * ----------------------------------------------------------------------- *
4   - * copyright (C) 2010-2017 by Juergen Knoedlseder *
  4 + * copyright (C) 2010-2018 by Juergen Knoedlseder *
5 5 * ----------------------------------------------------------------------- *
6 6 * *
7 7 * This program is free software: you can redistribute it and/or modify *
... ... @@ -64,59 +64,69 @@ public:
64 64  
65 65 protected:
66 66 // Protected methods
67   - void init_members(void);
68   - void copy_members(const ctskymap& app);
69   - void free_members(void);
70   - void get_parameters(void);
71   - void map_events(GCTAObservation* obs);
72   - void map_background(GCTAObservation* obs);
73   - void map_background_irf(GCTAObservation* obs);
74   - void map_background_ring(GCTAObservation* obs);
75   - void map_significance(void);
76   - void map_exclusions(const GFilename& filename);
77   - void map_exclusions_fits(const GFilename& filename);
78   - void map_exclusions_reg(const GFilename& filename);
79   - void compute_ring_values(const GSkyMap& counts,
80   - const GSkyMap& sensitivity,
81   - const GSkyDir& position,
82   - double& non, double& noff, double& alpha);
83   - void write_hdu_keywords(GFitsHDU* hdu) const;
84   -
85   - // Background integration kernel
86   - class irf_kern : public GFunction {
87   - public:
88   - irf_kern(const GCTABackground* bgd,
89   - const GCTAInstDir* dir) :
90   - m_bgd(bgd),
91   - m_dir(dir) { }
92   - double eval(const double& lnE);
93   - protected:
94   - const GCTABackground* m_bgd; //!< Pointer to background
95   - const GCTAInstDir* m_dir; //!< Pointer to instrument direction
96   - };
  67 + void init_members(void);
  68 + void copy_members(const ctskymap& app);
  69 + void free_members(void);
  70 + void get_parameters(void);
  71 + void setup_maps(void);
  72 + void setup_exclusion_map(const GFilename& filename);
  73 + void setup_exclusion_map_fits(const GFilename& filename);
  74 + void setup_exclusion_map_region(const GFilename& filename);
  75 + void fill_maps(void);
  76 + void fill_maps_counts(GCTAObservation* obs);
  77 + void fill_maps_acceptance(GCTAObservation* obs);
  78 + void compute_maps(void);
  79 + void compute_maps_ring_fft(void);
  80 + void compute_maps_ring_direct(void);
  81 + void compute_ring_values(const int& ipixel,
  82 + const GSkyMap& counts,
  83 + const GSkyMap& background,
  84 + double& non,
  85 + double& noff,
  86 + double& alpha);
  87 + void ring_bounding_box(const int& ipixel, int& ix1, int& ix2,
  88 + int& iy1, int& iy2) const;
  89 + GSkyMap ring_convolve(const GSkyMap& map, const double& rmin,
  90 + const double& rmax) const;
  91 + GNdarray ring_kernel(const double& rmin, const double& rmax) const;
  92 + double sigma_li_ma(const double& n_on,
  93 + const double& n_off,
  94 + const double& alpha) const;
  95 + void write_map(GFits& fits,
  96 + const GSkyMap& map,
  97 + const std::string& extname) const;
  98 + void write_hdu_keywords(GFitsHDU* hdu) const;
97 99  
98 100 // User parameters
99 101 GFilename m_outmap; //!< Output file name
  102 + GFilename m_inexclusion; //!< Exclusion map file name
100 103 double m_emin; //!< Minimum energy (TeV)
101 104 double m_emax; //!< Maximum energy (TeV)
102 105 std::string m_bkgsubtract; //!< Background subtraction method
103 106 double m_roiradius; //!< Region of interest radius for RING bkg.
104   - double m_inradius; //!< Inner ring radius for RING bkg.
105   - double m_outradius; //!< Outer ring radius for RING bkg.
  107 + double m_inradius; //!< Inner ring radius for RING background
  108 + double m_outradius; //!< Outer ring radius for RING background
  109 + int m_iterations; //!< Number of iterations for RING background
  110 + double m_threshold; //!< Threshold for RING background
  111 + bool m_usefft; //!< Use FFT for RING background
106 112 bool m_publish; //!< Publish sky map?
107 113 GChatter m_chatter; //!< Chattiness
108 114  
109 115 // Protected members
  116 + bool m_has_inmap; //!< Has valid input map
110 117 GSkyMap m_skymap; //!< Sky map
111 118 GSkyMap m_bkgmap; //!< Background map
112 119 GSkyMap m_sigmap; //!< Significance map
  120 + GSkyMap m_counts; //!< Counts map
  121 + GSkyMap m_acceptance; //!< Acceptance map
113 122 GSkyMap m_exclmap; //!< Exclusion map for RING background
114   - GSkyMap m_alphamap; //!< Alpha map values for RING background
115   - GSkyMap m_onmap; //!< On counts map for RING background
116 123  
117 124 // Caching variables to prevent multiple computations of the same thing
118   - std::vector<double> m_solidangle; //!< Cached pixel solid angles
119   - std::vector<GSkyDir> m_dirs; //!< Cached pixel directions
  125 + std::vector<double> m_solidangle; //!< Cached pixel solid angles
  126 + std::vector<GSkyDir> m_dirs; //!< Cached pixel directions
  127 + double m_cos_roiradius; //!< Cosine of RoI radius
  128 + double m_cos_inradius; //!< Cosine of inner ring radius
  129 + double m_cos_outradius; //!< Cosine of outer ring radius
120 130 };
121 131  
122 132  
... ...
src/ctskymap/ctskymap.par
... ... @@ -19,6 +19,7 @@
19 19 inobs, f, a, events.fits,,, "Input event list or observation definition XML file"
20 20 caldb, s, a, prod2,,, "Calibration database"
21 21 irf, s, a, South_0.5h,,, "Instrument response function"
  22 +inmap, f, h, NONE,,, "Input skymap file"
22 23 outmap, f, a, skymap.fits,,, "Output skymap file"
23 24  
24 25 #
... ... @@ -43,10 +44,13 @@ yref, r, a, 22.01,-90,90, &quot;Second coordinate of image center in degrees (DEC
43 44 # Background subtraction parameters
44 45 #==================================
45 46 bkgsubtract, s, a, NONE, NONE|IRF|RING,, "Background subtraction method"
46   -roiradius, r, a, 0.2,,, "Source region radius for estimating on-counts (degrees)"
47   -inradius, r, a, 0.6,,, "Inner ring radius (degrees)"
48   -outradius, r, a, 0.8,,, "Outer ring radius (degrees)"
  47 +roiradius, r, a, 0.1,,, "Source region radius for estimating on-counts (degrees)"
  48 +inradius, r, a, 0.6,,, "Inner background ring radius (degrees)"
  49 +outradius, r, a, 0.8,,, "Outer background ring radius (degrees)"
  50 +iterations, i, a, 0,0,100, "Number of iterations for exclusion regions computation"
  51 +threshold, r, a, 5.0,,, "Significance threshold for exclusion regions computation"
49 52 inexclusion, f, h, NONE,,, "Exclusion map or DS9 region file name"
  53 +usefft, b, h, yes,,, "Use FFT for ring computation?"
50 54  
51 55 #
52 56 # Standard parameters
... ...
src/support/ctobservation.cpp
... ... @@ -319,6 +319,48 @@ const GCTAObservation* ctobservation::next_unbinned_observation(void) const
319 319  
320 320  
321 321 /***********************************************************************//**
  322 + * @brief Read OGIP keywords from FITS HDU
  323 + *
  324 + * @param[in,out] hdu Pointer to FITS HDU.
  325 + *
  326 + * Read OGIP keywords from FITS HDU.
  327 + ***************************************************************************/
  328 +void ctobservation::read_ogip_keywords(GFitsHDU* hdu) const
  329 +{
  330 + // Continue only if pointer is valid
  331 + if (hdu != NULL) {
  332 +
  333 + // Get observation information
  334 + m_ogip_telescope = (hdu->has_card("TELESCOP")) ? hdu->string("TELESCOP") : "";
  335 +
  336 + // Get observation time information
  337 + std::string date_obs = (hdu->has_card("DATE-OBS")) ? hdu->string("DATE-OBS") : "";
  338 + std::string time_obs = (hdu->has_card("TIME-OBS")) ? hdu->string("TIME-OBS") : "";
  339 + std::string date_end = (hdu->has_card("DATE-END")) ? hdu->string("DATE-END") : "";
  340 + std::string time_end = (hdu->has_card("TIME-END")) ? hdu->string("TIME-END") : "";
  341 + m_ogip_telapse = (hdu->has_card("TELAPSE")) ? hdu->real("TELAPSE") : 0.0;
  342 + m_ogip_ontime = (hdu->has_card("ONTIME")) ? hdu->real("ONTIME") : 0.0;
  343 + m_ogip_livetime = (hdu->has_card("LIVETIME")) ? hdu->real("LIVETIME") : 0.0;
  344 + m_ogip_exposure = (hdu->has_card("EXPOSURE")) ? hdu->real("EXPOSURE") : 0.0;
  345 +
  346 + // Get OGIP start and stop time
  347 + std::string tstart = date_obs + "T" + time_obs;
  348 + std::string tstop = date_end + "T" + time_end;
  349 + if (tstart != "T") {
  350 + m_ogip_tstart.utc(tstart);
  351 + }
  352 + if (tstop != "T") {
  353 + m_ogip_tstop.utc(tstop);
  354 + }
  355 +
  356 + } // endif: pointer was valid
  357 +
  358 + // Return
  359 + return;
  360 +}
  361 +
  362 +
  363 +/***********************************************************************//**
322 364 * @brief Write OGIP keywords in FITS HDU
323 365 *
324 366 * @param[in,out] hdu Pointer to FITS HDU.
... ...
src/support/ctobservation.hpp
1 1 /***************************************************************************
2 2 * ctobservation - Base class for observation tools *
3 3 * ----------------------------------------------------------------------- *
4   - * copyright (C) 2016-2017 by Juergen Knoedlseder *
  4 + * copyright (C) 2016-2018 by Juergen Knoedlseder *
5 5 * ----------------------------------------------------------------------- *
6 6 * *
7 7 * This program is free software: you can redistribute it and/or modify *
... ... @@ -73,6 +73,7 @@ protected:
73 73 GCTAObservation* next_unbinned_observation(void);
74 74 const GCTAObservation* first_unbinned_observation(void) const;
75 75 const GCTAObservation* next_unbinned_observation(void) const;
  76 + void read_ogip_keywords(GFitsHDU* hdu) const;
76 77 void write_ogip_keywords(GFitsHDU* hdu) const;
77 78 void set_obs_statistic(const std::string& statistic);
78 79 void save_events_fits(void);
... ...
src/support/ctool.cpp
... ... @@ -650,7 +650,11 @@ GEbounds ctool::create_ebounds(void)
650 650 ***************************************************************************/
651 651 GSkyMap ctool::create_map(const GObservations& obs)
652 652 {
653   - // Read task parameters
  653 + // Read coordinate system and projection
  654 + std::string coordsys = (*this)["coordsys"].string();
  655 + std::string proj = (*this)["proj"].string();
  656 +
  657 + // Read sky map reference
654 658 double xref = 0.0;
655 659 double yref = 0.0;
656 660 bool usepnt = (*this)["usepnt"].boolean();
... ... @@ -658,11 +662,11 @@ GSkyMap ctool::create_map(const GObservations&amp; obs)
658 662 xref = (*this)["xref"].real();
659 663 yref = (*this)["yref"].real();
660 664 }
661   - std::string proj = (*this)["proj"].string();
662   - std::string coordsys = (*this)["coordsys"].string();
663   - double binsz = (*this)["binsz"].real();
664   - int nxpix = (*this)["nxpix"].integer();
665   - int nypix = (*this)["nypix"].integer();
  665 +
  666 + // Read sky map pixel size and number of bins
  667 + double binsz = (*this)["binsz"].real();
  668 + int nxpix = (*this)["nxpix"].integer();
  669 + int nypix = (*this)["nypix"].integer();
666 670  
667 671 // If requested, get pointing from observations
668 672 if (usepnt) {
... ...
test/data/irf_file.fits.gz
No preview for this file type
test/test_csobsinfo.py
... ... @@ -112,7 +112,7 @@ class Test(test):
112 112 obsinfo['ra'] = 83.63
113 113 obsinfo['dec'] = 22.01
114 114 obsinfo['logfile'] = 'csobsinfo_py1.log'
115   - obsinfo['chatter'] = 2
  115 + obsinfo['chatter'] = 3
116 116  
117 117 # Run csobsinfo script and save XML file
118 118 obsinfo.logFileOpen() # Make sure we get a log file
... ... @@ -122,21 +122,20 @@ class Test(test):
122 122 # Check DS9 file
123 123 self._check_ds9_file('csobsinfo_py1.reg')
124 124  
125   - # Set-up csobsinfo
126   - obsinfo = cscripts.csobsinfo()
127   - obsinfo['inobs'] = self._inobs
128   - obsinfo['outds9file'] = 'csobsinfo_py2.reg'
129   - obsinfo['offset'] = True
130   - obsinfo['ra'] = 83.63
131   - obsinfo['dec'] = 22.01
132   - obsinfo['logfile'] = 'csobsinfo_py2.log'
133   - obsinfo['chatter'] = 3
134   -
135   - # Execute csobsinfo script
136   - obsinfo.execute()
137   -
138   - # Check DS9 file
139   - self._check_ds9_file('csobsinfo_py2.reg')
  125 + # Check result access methods
  126 + self.test_value(len(obsinfo.zeniths()), 1, 'Test number of zenith angles')
  127 + self.test_value(obsinfo.zeniths()[0], 0.0, 'Test zenith angle')
  128 + self.test_value(len(obsinfo.azimuths()), 1, 'Test number of azimuth angles')
  129 + self.test_value(obsinfo.azimuths()[0], 0.0, 'Test azimuth angle')
  130 + self.test_value(len(obsinfo.ras()), 1, 'Test number of Right Ascensions')
  131 + self.test_value(obsinfo.ras()[0], 83.63, 'Test Right Ascension')
  132 + self.test_value(len(obsinfo.decs()), 1, 'Test number of Declinations')
  133 + self.test_value(obsinfo.decs()[0], 22.51, 'Test Declination')
  134 + self.test_value(len(obsinfo.offsets()), 1, 'Test number of offsets')
  135 + self.test_value(obsinfo.offsets()[0], 0.5, 'Test offset angle')
  136 + self.test_value(obsinfo.ebounds().size(), 1, 'Test number of energy boundaries')
  137 + self.test_value(obsinfo.ebounds().emin().GeV(), 100.0, 'Test minimum energy')
  138 + self.test_value(obsinfo.ebounds().emax().TeV(), 100.0, 'Test maximum energy')
140 139  
141 140 # Return
142 141 return
... ... @@ -151,13 +150,12 @@ class Test(test):
151 150  
152 151 # Expect "fk5" in first line
153 152 line = f.readline().strip('\n')
154   - self.test_assert(line == 'fk5',
155   - 'Check for "fk5" in first line')
  153 + self.test_value(line, 'fk5',
  154 + 'Test for "fk5" in first line of DS9 file')
156 155  
157 156 # Expect "fk5" in first line
158 157 line = f.readline().strip('\n')
159   - self.test_assert(line[0:6] == 'point(',
160   - 'Check for "point(" in second line')
  158 + self.test_value(line[0:6], 'point(', 'Test for "point(" in second line of DS9 file')
161 159  
162 160 # Close file
163 161 f.close()
... ...
test/test_ctskymap.py
... ... @@ -248,6 +248,7 @@ class Test(test):
248 248 skymap['roiradius'] = 0.1
249 249 skymap['inradius'] = 0.6
250 250 skymap['outradius'] = 0.8
  251 + skymap['iterations'] = 0
251 252 skymap['inexclusion'] = 'NONE'
252 253 skymap['caldb'] = self._caldb
253 254 skymap['irf'] = self._irf
... ...
test/test_obsutils.py
... ... @@ -2,7 +2,7 @@
2 2 # ==========================================================================
3 3 # This scripts performs unit tests for the obsutils module
4 4 #
5   -# Copyright (C) 2017 Juergen Knoedlseder
  5 +# Copyright (C) 2017-2018 Juergen Knoedlseder
6 6 #
7 7 # This program is free software: you can redistribute it and/or modify
8 8 # it under the terms of the GNU General Public License as published by
... ... @@ -214,7 +214,7 @@ class Test(test):
214 214 # Check simulation results
215 215 self.test_value(res.size(), 1, 'Check number of observations')
216 216 self.test_value(res.models().size(), 2, 'Check number of models')
217   - self.test_value(res.nobserved(), 24, 'Check number of observed events')
  217 + self.test_value(res.nobserved(), 23, 'Check number of observed events')
218 218 self.test_value(res.npred(), 0.0, 'Check number of predicted events')
219 219 self.test_value(res[0].eventtype(), 'CountsCube', 'Check event type')
220 220 self.test_value(res[0].events().ebounds().emin().TeV(), 1.0,
... ... @@ -223,7 +223,7 @@ class Test(test):
223 223 'Check minimum energy')
224 224 self.test_value(res[0].events().ebounds().size(), 5,
225 225 'Check number of energy bins')
226   - self.test_value(res[0].events().number(), 24,
  226 + self.test_value(res[0].events().number(), 23,
227 227 'Check number of events in cube')
228 228  
229 229 # Check energy dispersion flag
... ...