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 * Version 1.5.0 released 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 Introduction 6 Introduction
@@ -9,10 +11,7 @@ ctools release with respect to the precedent release. @@ -9,10 +11,7 @@ ctools release with respect to the precedent release.
9 11
10 Configuration 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 Python interface 17 Python interface
@@ -27,8 +26,7 @@ None @@ -27,8 +26,7 @@ None
27 26
28 Base classes 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 ctbin - CTA event binning 32 ctbin - CTA event binning
@@ -43,9 +41,7 @@ None @@ -43,9 +41,7 @@ None
43 41
44 ctbutterfly - Generate butterfly diagrams 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 ctcubemask - CTA cube filtering 47 ctcubemask - CTA cube filtering
@@ -60,7 +56,7 @@ None @@ -60,7 +56,7 @@ None
60 56
61 cterror - Likelihood profile error computation 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 ctexpcube - CTA exposure cube generation 62 ctexpcube - CTA exposure cube generation
@@ -70,7 +66,7 @@ None @@ -70,7 +66,7 @@ None
70 66
71 ctlike - Maximum likelihood fitting 67 ctlike - Maximum likelihood fitting
72 ----------------------------------- 68 -----------------------------------
73 -Rename "stats" parameter to "statistic" (#2237). 69 +None
74 70
75 71
76 ctmapcube - CTA map cube generation 72 ctmapcube - CTA map cube generation
@@ -80,22 +76,12 @@ None @@ -80,22 +76,12 @@ None
80 76
81 ctmodel - CTA counts cube modelling 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 ctobssim - CTA observation simulation 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 ctphase - Event phase computation 87 ctphase - Event phase computation
@@ -110,51 +96,47 @@ None @@ -110,51 +96,47 @@ None
110 96
111 ctpsfcube - CTA point spread function cube generation 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 ctselect - CTA event selection 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 ctskymap - CTA sky mapping 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 cttsmap - Generation of Test Statistic map 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 ctulimit - Compute upper limit 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 support - Support classes 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 cscaldb - Inspect calibration database 142 cscaldb - Inspect calibration database
@@ -189,16 +171,7 @@ None @@ -189,16 +171,7 @@ None
189 171
190 cslightcrv - Generate light curve 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 csmodelinfo - Return model information 177 csmodelinfo - Return model information
@@ -218,8 +191,7 @@ None @@ -218,8 +191,7 @@ None
218 191
219 csmodelsois - Puts selected sources in diffuse model cube 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 csobs2caldb - Generate calibration database entry for an IACT observation 197 csobs2caldb - Generate calibration database entry for an IACT observation
@@ -234,47 +206,27 @@ None @@ -234,47 +206,27 @@ None
234 206
235 csobsinfo - Return observation definition information 207 csobsinfo - Return observation definition information
236 ----------------------------------------------------- 208 -----------------------------------------------------
237 -None 209 +Add ras() and decs() methods (#2313)
238 210
239 211
240 csobsselect - Select observations 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 csphagen - Generate On/Off observations 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 csphasecrv - Generate phase curve 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 cspull - Generate pull distributions 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 csresmap - Generate residual map 232 csresmap - Generate residual map
@@ -284,7 +236,7 @@ None @@ -284,7 +236,7 @@ None
284 236
285 csresspec - Generate residual spectrum 237 csresspec - Generate residual spectrum
286 -------------------------------------- 238 --------------------------------------
287 -Add script to generate a residual spectrum (#2289) 239 +None
288 240
289 241
290 csroot2caldb - Generates calibration database from ROOT file 242 csroot2caldb - Generates calibration database from ROOT file
@@ -294,25 +246,12 @@ None @@ -294,25 +246,12 @@ None
294 246
295 cssens - Computes sensitivity curve 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 csspec - Generates Spectral Energy Distribution 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 cssrcdetect - Detects sources in sky map 257 cssrcdetect - Detects sources in sky map
@@ -322,18 +261,7 @@ None @@ -322,18 +261,7 @@ None
322 261
323 cstsdist - Computes Test Statistic distribution 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 cstsmapmerge - Merge Test Statistic maps 267 cstsmapmerge - Merge Test Statistic maps
@@ -343,14 +271,12 @@ None @@ -343,14 +271,12 @@ None
343 271
344 cstsmapsplit - Prepare split Test Statistic computation 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 csviscube - Generate visibility cube 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 csworkflow - Execute workflow 282 csworkflow - Execute workflow
README.md
1 ctools information 1 ctools information
2 ================== 2 ==================
3 -* Version: 1.6.0.dev1 (1 August 2017) 3 +* Version: 1.6.0.dev1
4 * Author: Juergen Knoedlseder (jurgen.knodlseder@irap.omp.eu) 4 * Author: Juergen Knoedlseder (jurgen.knodlseder@irap.omp.eu)
5 * GammaLib dependency: 1.6.0.dev1 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,11 +59,11 @@ class csobsinfo(ctools.csobservation):
59 """ 59 """
60 Get parameters from parfile and setup the observation 60 Get parameters from parfile and setup the observation
61 """ 61 """
62 - # Get parameters 62 + # Get parameters
63 if self.obs().size() == 0: 63 if self.obs().size() == 0:
64 self._require_inobs('csobsinfo::get_parameters') 64 self._require_inobs('csobsinfo::get_parameters')
65 self.obs(self._get_observations(False)) 65 self.obs(self._get_observations(False))
66 - 66 +
67 # Initialise object position 67 # Initialise object position
68 self._obj_dir = gammalib.GSkyDir() 68 self._obj_dir = gammalib.GSkyDir()
69 69
@@ -83,7 +83,7 @@ class csobsinfo(ctools.csobservation): @@ -83,7 +83,7 @@ class csobsinfo(ctools.csobservation):
83 83
84 # Return 84 # Return
85 return 85 return
86 - 86 +
87 87
88 # Public methods 88 # Public methods
89 def run(self): 89 def run(self):
@@ -96,7 +96,7 @@ class csobsinfo(ctools.csobservation): @@ -96,7 +96,7 @@ class csobsinfo(ctools.csobservation):
96 96
97 # Get parameters 97 # Get parameters
98 self._get_parameters() 98 self._get_parameters()
99 - 99 +
100 # Initialise arrays to store certain values for reuse 100 # Initialise arrays to store certain values for reuse
101 # Todo, think about using a python dictionary 101 # Todo, think about using a python dictionary
102 self._offsets = [] 102 self._offsets = []
@@ -107,7 +107,7 @@ class csobsinfo(ctools.csobservation): @@ -107,7 +107,7 @@ class csobsinfo(ctools.csobservation):
107 self._ebounds = gammalib.GEbounds() 107 self._ebounds = gammalib.GEbounds()
108 self._gti = gammalib.GGti() 108 self._gti = gammalib.GGti()
109 obs_names = [] 109 obs_names = []
110 - 110 +
111 # Initialise output to be filled 111 # Initialise output to be filled
112 ontime = 0.0 112 ontime = 0.0
113 livetime = 0.0 113 livetime = 0.0
@@ -115,12 +115,12 @@ class csobsinfo(ctools.csobservation): @@ -115,12 +115,12 @@ class csobsinfo(ctools.csobservation):
115 n_eventbins = 0 115 n_eventbins = 0
116 n_obs_binned = 0 116 n_obs_binned = 0
117 n_obs_unbinned = 0 117 n_obs_unbinned = 0
118 - 118 +
119 # Write header 119 # Write header
120 if self._logTerse(): 120 if self._logTerse():
121 self._log('\n') 121 self._log('\n')
122 self._log.header1(gammalib.number('Observation', self.obs().size())) 122 self._log.header1(gammalib.number('Observation', self.obs().size()))
123 - 123 +
124 # Loop over observations 124 # Loop over observations
125 for obs in self.obs(): 125 for obs in self.obs():
126 126
@@ -128,44 +128,44 @@ class csobsinfo(ctools.csobservation): @@ -128,44 +128,44 @@ class csobsinfo(ctools.csobservation):
128 if not obs.classname() == 'GCTAObservation': 128 if not obs.classname() == 'GCTAObservation':
129 self._log('Skipping '+obs.instrument()+' observation\n') 129 self._log('Skipping '+obs.instrument()+' observation\n')
130 continue 130 continue
131 - 131 +
132 # Use observed object as observation name if name is not given 132 # Use observed object as observation name if name is not given
133 obs_name = obs.name() 133 obs_name = obs.name()
134 if obs_name == '': 134 if obs_name == '':
135 obs_name = obs.object() 135 obs_name = obs.object()
136 - 136 +
137 # Logging 137 # Logging
138 if self._logExplicit(): 138 if self._logExplicit():
139 self._log.header2(obs_name) 139 self._log.header2(obs_name)
140 - 140 +
141 # Retrieve observation name 141 # Retrieve observation name
142 obs_names.append(obs_name) 142 obs_names.append(obs_name)
143 - 143 +
144 # Retrieve energy boundaries 144 # Retrieve energy boundaries
145 obs_bounds = obs.events().ebounds() 145 obs_bounds = obs.events().ebounds()
146 - 146 +
147 # Retrieve time interval 147 # Retrieve time interval
148 obs_gti = obs.events().gti() 148 obs_gti = obs.events().gti()
149 - 149 +
150 # Compute mean time and dead time fraction in percent 150 # Compute mean time and dead time fraction in percent
151 deadfrac = (1.0-obs.deadc())*100.0 151 deadfrac = (1.0-obs.deadc())*100.0
152 - 152 +
153 # Retrieve pointing and store Ra,Dec 153 # Retrieve pointing and store Ra,Dec
154 - pnt_dir = obs.pointing().dir() 154 + pnt_dir = obs.pointing().dir()
155 self._pnt_ra.append(pnt_dir.ra_deg()) 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 # If avaliable append energy boundaries 158 # If avaliable append energy boundaries
159 if obs_bounds.size() > 0 : 159 if obs_bounds.size() > 0 :
160 self._ebounds.append(obs_bounds.emin(),obs_bounds.emax()) 160 self._ebounds.append(obs_bounds.emin(),obs_bounds.emax())
161 - 161 +
162 # Append time interval 162 # Append time interval
163 self._gti.append(obs_gti.tstart(), obs_gti.tstop()) 163 self._gti.append(obs_gti.tstart(), obs_gti.tstop())
164 - 164 +
165 # Increment global livetime and ontime 165 # Increment global livetime and ontime
166 ontime += obs.ontime() 166 ontime += obs.ontime()
167 livetime += obs.livetime() 167 livetime += obs.livetime()
168 - 168 +
169 # Bookkeeping 169 # Bookkeeping
170 if obs.eventtype() == 'CountsCube': 170 if obs.eventtype() == 'CountsCube':
171 n_eventbins += obs.events().size() 171 n_eventbins += obs.events().size()
@@ -190,10 +190,10 @@ class csobsinfo(ctools.csobservation): @@ -190,10 +190,10 @@ class csobsinfo(ctools.csobservation):
190 if self._compute_offset: 190 if self._compute_offset:
191 offset = pnt_dir.dist_deg(self._obj_dir) 191 offset = pnt_dir.dist_deg(self._obj_dir)
192 self._offsets.append(offset) 192 self._offsets.append(offset)
193 - 193 +
194 # Optionally log details 194 # Optionally log details
195 if self._logExplicit(): 195 if self._logExplicit():
196 - 196 +
197 # Log the observation energy range (if available) 197 # Log the observation energy range (if available)
198 self._log.parformat('Energy range') 198 self._log.parformat('Energy range')
199 if obs_bounds.size() == 0: 199 if obs_bounds.size() == 0:
@@ -203,7 +203,7 @@ class csobsinfo(ctools.csobservation): @@ -203,7 +203,7 @@ class csobsinfo(ctools.csobservation):
203 self._log(' - ') 203 self._log(' - ')
204 self._log(str(obs_bounds.emax())) 204 self._log(str(obs_bounds.emax()))
205 self._log('\n') 205 self._log('\n')
206 - 206 +
207 # Log observation time interval 207 # Log observation time interval
208 self._log.parformat('Time range (MJD)') 208 self._log.parformat('Time range (MJD)')
209 if obs_gti.size() == 0: 209 if obs_gti.size() == 0:
@@ -213,7 +213,7 @@ class csobsinfo(ctools.csobservation): @@ -213,7 +213,7 @@ class csobsinfo(ctools.csobservation):
213 self._log(' - ') 213 self._log(' - ')
214 self._log(str(obs_gti.tstop().mjd())) 214 self._log(str(obs_gti.tstop().mjd()))
215 self._log('\n') 215 self._log('\n')
216 - 216 +
217 # Log observation information 217 # Log observation information
218 self._log_value(gammalib.EXPLICIT, 'Ontime', '%.3f s' % 218 self._log_value(gammalib.EXPLICIT, 'Ontime', '%.3f s' %
219 obs.ontime()) 219 obs.ontime())
@@ -233,7 +233,7 @@ class csobsinfo(ctools.csobservation): @@ -233,7 +233,7 @@ class csobsinfo(ctools.csobservation):
233 zenith) 233 zenith)
234 self._log_value(gammalib.EXPLICIT, 'Azimuth angle', '%.2f deg' % 234 self._log_value(gammalib.EXPLICIT, 'Azimuth angle', '%.2f deg' %
235 azimuth) 235 azimuth)
236 - 236 +
237 # Write summary header 237 # Write summary header
238 self._log_header1(gammalib.NORMAL, 'Summary') 238 self._log_header1(gammalib.NORMAL, 'Summary')
239 239
@@ -258,7 +258,7 @@ class csobsinfo(ctools.csobservation): @@ -258,7 +258,7 @@ class csobsinfo(ctools.csobservation):
258 mean_azimuth = '%.2f deg' % (sum(self._azimuths) / len(self._azimuths)) 258 mean_azimuth = '%.2f deg' % (sum(self._azimuths) / len(self._azimuths))
259 else: 259 else:
260 mean_azimuth = 'Unknown' 260 mean_azimuth = 'Unknown'
261 - 261 +
262 # Log mean offset, azimuth and zenith angle 262 # Log mean offset, azimuth and zenith angle
263 self._log_header3(gammalib.NORMAL, 'Pointings') 263 self._log_header3(gammalib.NORMAL, 'Pointings')
264 self._log_value(gammalib.NORMAL, 'Mean offset angle', mean_offset) 264 self._log_value(gammalib.NORMAL, 'Mean offset angle', mean_offset)
@@ -323,48 +323,60 @@ class csobsinfo(ctools.csobservation): @@ -323,48 +323,60 @@ class csobsinfo(ctools.csobservation):
323 323
324 # Check if DS9 file is valid 324 # Check if DS9 file is valid
325 if ds9file.url() != 'NONE': 325 if ds9file.url() != 'NONE':
326 - 326 +
327 # Write header 327 # Write header
328 self._log_header1(gammalib.TERSE, 'Save pointings in DS9 file') 328 self._log_header1(gammalib.TERSE, 'Save pointings in DS9 file')
329 329
330 # Log filename 330 # Log filename
331 self._log_value(gammalib.NORMAL, 'DS9 filename', ds9file.url()) 331 self._log_value(gammalib.NORMAL, 'DS9 filename', ds9file.url())
332 -  
333 - # Open file 332 +
  333 + # Open file
334 f = open(ds9file.url(),'w') 334 f = open(ds9file.url(),'w')
335 - 335 +
336 # Write coordinate system 336 # Write coordinate system
337 f.write('fk5\n') 337 f.write('fk5\n')
338 - 338 +
339 # Loop over pointings 339 # Loop over pointings
340 for i in range(len(self._pnt_ra)): 340 for i in range(len(self._pnt_ra)):
341 - 341 +
342 # Create string 342 # Create string
343 line = 'point(' 343 line = 'point('
344 line += str(self._pnt_ra[i])+','+str(self._pnt_dec[i])+')' 344 line += str(self._pnt_ra[i])+','+str(self._pnt_dec[i])+')'
345 line += ' # point=cross 20 width=3\n' 345 line += ' # point=cross 20 width=3\n'
346 - 346 +
347 # Write to file 347 # Write to file
348 f.write(line) 348 f.write(line)
349 - 349 +
350 # Close file 350 # Close file
351 f.close() 351 f.close()
352 - 352 +
353 # Return 353 # Return
354 - return  
355 - 354 + return
  355 +
356 def zeniths(self): 356 def zeniths(self):
357 """ 357 """
358 Return zenith angles 358 Return zenith angles
359 """ 359 """
360 return self._zeniths 360 return self._zeniths
361 - 361 +
362 def azimuths(self): 362 def azimuths(self):
363 """ 363 """
364 Return azimuth angles 364 Return azimuth angles
365 """ 365 """
366 return self._azimuths 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 def offsets(self): 380 def offsets(self):
369 """ 381 """
370 Return offset angles 382 Return offset angles
@@ -376,13 +388,13 @@ class csobsinfo(ctools.csobservation): @@ -376,13 +388,13 @@ class csobsinfo(ctools.csobservation):
376 Return energy boundaries 388 Return energy boundaries
377 """ 389 """
378 return self._ebounds 390 return self._ebounds
379 - 391 +
380 def gti(self): 392 def gti(self):
381 """ 393 """
382 Return good time intervals 394 Return good time intervals
383 """ 395 """
384 return self._gti 396 return self._gti
385 - 397 +
386 398
387 # ======================== # 399 # ======================== #
388 # Main routine entry point # 400 # Main routine entry point #
@@ -391,6 +403,6 @@ if __name__ == '__main__': @@ -391,6 +403,6 @@ if __name__ == '__main__':
391 403
392 # Create instance of application 404 # Create instance of application
393 app = csobsinfo(sys.argv) 405 app = csobsinfo(sys.argv)
394 - 406 +
395 # Execute application 407 # Execute application
396 app.execute() 408 app.execute()
cscripts/csphagen.py
@@ -123,7 +123,8 @@ class csphagen(ctools.csobservation): @@ -123,7 +123,8 @@ class csphagen(ctools.csobservation):
123 # Off region region the parameter 'bkgregfile' for all CTA observations 123 # Off region region the parameter 'bkgregfile' for all CTA observations
124 # without Off region 124 # without Off region
125 for obs in self.obs(): 125 for obs in self.obs():
126 - if obs.instrument() == 'CTA': 126 + #if obs.instrument() == 'CTA':
  127 + if obs.classname() == 'GCTAObservation':
127 if obs.off_regions().is_empty(): 128 if obs.off_regions().is_empty():
128 filename = self['bkgregfile'].filename() 129 filename = self['bkgregfile'].filename()
129 regions = gammalib.GSkyRegions(filename) 130 regions = gammalib.GSkyRegions(filename)
@@ -263,8 +264,8 @@ class csphagen(ctools.csobservation): @@ -263,8 +264,8 @@ class csphagen(ctools.csobservation):
263 """ 264 """
264 Set models in observation container 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 Parameters 270 Parameters
270 ---------- 271 ----------
@@ -279,11 +280,15 @@ class csphagen(ctools.csobservation): @@ -279,11 +280,15 @@ class csphagen(ctools.csobservation):
279 # Initialise model container 280 # Initialise model container
280 models = gammalib.GModels() 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 for model in self.obs().models(): 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 models.append(model) 292 models.append(model)
288 293
289 # Append model to observation container 294 # Append model to observation container
@@ -399,7 +404,8 @@ class csphagen(ctools.csobservation): @@ -399,7 +404,8 @@ class csphagen(ctools.csobservation):
399 for obs in self.obs(): 404 for obs in self.obs():
400 405
401 # Skip non CTA observations 406 # Skip non CTA observations
402 - if obs.instrument() != 'CTA': 407 + #if obs.instrument() != 'CTA':
  408 + if obs.classname() != 'GCTAObservation':
403 self._log_string(gammalib.NORMAL, 'Skip %s observation "%s"' % \ 409 self._log_string(gammalib.NORMAL, 'Skip %s observation "%s"' % \
404 (obs.instrument(), obs.id())) 410 (obs.instrument(), obs.id()))
405 continue 411 continue
cscripts/csresspec.py
@@ -241,14 +241,14 @@ class csresspec(ctools.csobservation): @@ -241,14 +241,14 @@ class csresspec(ctools.csobservation):
241 241
242 # Bin events 242 # Bin events
243 cntcube = ctools.ctbin(obs) 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 cntcube['coordsys'] = 'CEL' 250 cntcube['coordsys'] = 'CEL'
251 - cntcube['ebinalg'] = self['ebinalg'].string() 251 + cntcube['ebinalg'] = self['ebinalg'].string()
252 if self['ebinalg'].string() == 'FILE': 252 if self['ebinalg'].string() == 'FILE':
253 cntcube['ebinfile'] = self['ebinfile'].filename() 253 cntcube['ebinfile'] = self['ebinfile'].filename()
254 else: 254 else:
@@ -356,7 +356,7 @@ class csresspec(ctools.csobservation): @@ -356,7 +356,7 @@ class csresspec(ctools.csobservation):
356 def _cube_to_spectrum(self, cube, evlist_info): 356 def _cube_to_spectrum(self, cube, evlist_info):
357 """ 357 """
358 Derive from event cube a count spectrum 358 Derive from event cube a count spectrum
359 - 359 +
360 If data come from event list use only the ROI and energy range of 360 If data come from event list use only the ROI and energy range of
361 the original data. Apply user defined mask if requested. 361 the original data. Apply user defined mask if requested.
362 362
@@ -550,29 +550,30 @@ class csresspec(ctools.csobservation): @@ -550,29 +550,30 @@ class csresspec(ctools.csobservation):
550 # if 3D observation 550 # if 3D observation
551 if obs[0].classname() == 'GCTAObservation': 551 if obs[0].classname() == 'GCTAObservation':
552 552
553 - # Prepare Observations  
554 -  
555 # If already binned set the evlist_info dictionary to have 553 # If already binned set the evlist_info dictionary to have
556 # attribute was_list False 554 # attribute was_list False
557 if obs[0].eventtype() == 'CountsCube': 555 if obs[0].eventtype() == 'CountsCube':
558 evlist_info = {'was_list': False} 556 evlist_info = {'was_list': False}
559 - # Otherwise bin now 557 +
  558 + # ... otherwise bin now
560 else: 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 msg = 'Setting up binned observation' 562 msg = 'Setting up binned observation'
564 self._log_string(gammalib.NORMAL, msg) 563 self._log_string(gammalib.NORMAL, msg)
565 obs, evlist_info = self._bin_evlist(obs) 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 if self._use_maps: 568 if self._use_maps:
570 modcube = gammalib.GCTAEventCube(self['inmodel'].filename()) 569 modcube = gammalib.GCTAEventCube(self['inmodel'].filename())
571 - # Otherwise calculate it now 570 +
  571 + # ... otherwise calculate it now
572 else: 572 else:
573 msg = 'Computing model cube' 573 msg = 'Computing model cube'
574 self._log_string(gammalib.NORMAL, msg) 574 self._log_string(gammalib.NORMAL, msg)
575 modelcube = ctools.ctmodel(obs) 575 modelcube = ctools.ctmodel(obs)
  576 + modelcube['edisp'] = self['edisp'].boolean()
576 modelcube.run() 577 modelcube.run()
577 modcube = modelcube.cube().copy() 578 modcube = modelcube.cube().copy()
578 579
cscripts/csroot2caldb.py
@@ -2,7 +2,7 @@ @@ -2,7 +2,7 @@
2 # ========================================================================== 2 # ==========================================================================
3 # Generate IRFs in CALDB format from a ROOT offaxis performance file 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 # This program is free software: you can redistribute it and/or modify 7 # This program is free software: you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by 8 # it under the terms of the GNU General Public License as published by
@@ -618,7 +618,7 @@ class csroot2caldb(ctools.cscript): @@ -618,7 +618,7 @@ class csroot2caldb(ctools.cscript):
618 ds['HDU_EA'].card('HI_THRES', emin, '[TeV] High energy threshold') 618 ds['HDU_EA'].card('HI_THRES', emin, '[TeV] High energy threshold')
619 619
620 # Create "EFFAREA" data column 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 # Return 623 # Return
624 return 624 return
@@ -745,7 +745,6 @@ class csroot2caldb(ctools.cscript): @@ -745,7 +745,6 @@ class csroot2caldb(ctools.cscript):
745 'Generate Gaussian point spread function extension') 745 'Generate Gaussian point spread function extension')
746 746
747 # Get relevant ROOT histograms 747 # Get relevant ROOT histograms
748 - #r68 = tfile.Get('AngRes_offaxis')  
749 r68, _ = self._root2psf_get_r68_and_r80(tfile) 748 r68, _ = self._root2psf_get_r68_and_r80(tfile)
750 749
751 # Extract number of bins in histogram 750 # Extract number of bins in histogram
@@ -826,8 +825,6 @@ class csroot2caldb(ctools.cscript): @@ -826,8 +825,6 @@ class csroot2caldb(ctools.cscript):
826 'Generate King point spread function extension') 825 'Generate King point spread function extension')
827 826
828 # Get relevant ROOT histograms 827 # Get relevant ROOT histograms
829 - #r68 = tfile.Get('AngRes_offaxis')  
830 - #r80 = tfile.Get('AngRes80_offaxis')  
831 r68, r80 = self._root2psf_get_r68_and_r80(tfile) 828 r68, r80 = self._root2psf_get_r68_and_r80(tfile)
832 829
833 # Extract number of bins in histogram 830 # Extract number of bins in histogram
@@ -1034,7 +1031,6 @@ class csroot2caldb(ctools.cscript): @@ -1034,7 +1031,6 @@ class csroot2caldb(ctools.cscript):
1034 1031
1035 # Get relevant ROOT histograms 1032 # Get relevant ROOT histograms
1036 matrix = self._root2edisp_get_migra(tfile) 1033 matrix = self._root2edisp_get_migra(tfile)
1037 - #matrix = tfile.Get('EestOverEtrue_offaxis')  
1038 1034
1039 # Write boundary keywords 1035 # Write boundary keywords
1040 self._set_cif_keywords(ds['HDU_EDISP'], irf['EDISP_NAME'], 1036 self._set_cif_keywords(ds['HDU_EDISP'], irf['EDISP_NAME'],
@@ -1087,7 +1083,7 @@ class csroot2caldb(ctools.cscript): @@ -1087,7 +1083,7 @@ class csroot2caldb(ctools.cscript):
1087 irf['BGD_BOUNDS'], irf['BGD_DESC'], irf) 1083 irf['BGD_BOUNDS'], irf['BGD_DESC'], irf)
1088 1084
1089 # Create "BGD" data column 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 # Return 1088 # Return
1093 return 1089 return
@@ -1327,7 +1323,7 @@ class csroot2caldb(ctools.cscript): @@ -1327,7 +1323,7 @@ class csroot2caldb(ctools.cscript):
1327 # Log power law replacement 1323 # Log power law replacement
1328 self._log_value(gammalib.NORMAL, 1324 self._log_value(gammalib.NORMAL,
1329 'Power law replacement', 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 'Off=%.2f deg (%d)' % 1327 'Off=%.2f deg (%d)' %
1332 (value,engs[ieng],ieng,offset,ioff)) 1328 (value,engs[ieng],ieng,offset,ioff))
1333 1329
@@ -1479,7 +1475,7 @@ class csroot2caldb(ctools.cscript): @@ -1479,7 +1475,7 @@ class csroot2caldb(ctools.cscript):
1479 # Log background infill 1475 # Log background infill
1480 self._log_value(gammalib.NORMAL, 1476 self._log_value(gammalib.NORMAL,
1481 'Background infill', 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 'Off=%.2f deg (%d)' % 1479 'Off=%.2f deg (%d)' %
1484 (value,engs[ieng],ieng,offset,ioff)) 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 +7,7 @@ The current development version of the code can be downloaded as source
7 tarballs, Mac OS X binaries, or directly from the `Git <https://git-scm.com/>`_ 7 tarballs, Mac OS X binaries, or directly from the `Git <https://git-scm.com/>`_
8 repository. 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 Source tarballs 13 Source tarballs
@@ -15,8 +15,8 @@ Source tarballs @@ -15,8 +15,8 @@ Source tarballs
15 15
16 Download the source tarballs from the following links 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 and follow the instructions on :ref:`sec_install_source`. 21 and follow the instructions on :ref:`sec_install_source`.
22 22
@@ -26,7 +26,7 @@ Binary packages @@ -26,7 +26,7 @@ Binary packages
26 26
27 Download the installer image from the following link 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 and follow the instructions on :ref:`sec_install_binary`. 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 +6,8 @@ Release History
6 .. toctree:: 6 .. toctree::
7 :maxdepth: 1 7 :maxdepth: 1
8 8
  9 + ctools 1.6 <1.6>
  10 + ctools 1.5.1 <1.5.1>
9 ctools 1.5 <1.5> 11 ctools 1.5 <1.5>
10 ctools 1.4.3 <1.4.3> 12 ctools 1.4.3 <1.4.3>
11 ctools 1.4.2 <1.4.2> 13 ctools 1.4.2 <1.4.2>
doc/source/users/reference_manual/ctskymap.rst
@@ -41,6 +41,11 @@ General parameters @@ -41,6 +41,11 @@ General parameters
41 Instrument response function (only required for IRF background subtraction 41 Instrument response function (only required for IRF background subtraction
42 if no response information is provided by ``inobs``). 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 ``outmap [file]`` 49 ``outmap [file]``
45 Output sky map file. 50 Output sky map file.
46 51
@@ -82,17 +87,37 @@ Background subtraction configuration parameters @@ -82,17 +87,37 @@ Background subtraction configuration parameters
82 Background subtraction method. 87 Background subtraction method.
83 88
84 ``roiradius [real]`` 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 ``inradius [real]`` 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 ``outradius [real]`` 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 ``(inexclusion = NONE) [file]`` 111 ``(inexclusion = NONE) [file]``
94 Exclusion region file as either a FITS map or DS9 region file. 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 Standard parameters 122 Standard parameters
98 ------------------- 123 -------------------
pyext/ctobservation.i
@@ -94,6 +94,7 @@ public: @@ -94,6 +94,7 @@ public:
94 // Make methods private in Python by prepending an underscore 94 // Make methods private in Python by prepending an underscore
95 %rename(_first_unbinned_observation) first_unbinned_observation; 95 %rename(_first_unbinned_observation) first_unbinned_observation;
96 %rename(_next_unbinned_observation) next_unbinned_observation; 96 %rename(_next_unbinned_observation) next_unbinned_observation;
  97 + %rename(_read_ogip_keywords) read_ogip_keywords;
97 %rename(_write_ogip_keywords) write_ogip_keywords; 98 %rename(_write_ogip_keywords) write_ogip_keywords;
98 %rename(_set_obs_statistic) set_obs_statistic; 99 %rename(_set_obs_statistic) set_obs_statistic;
99 %rename(_save_events_fits) save_events_fits; 100 %rename(_save_events_fits) save_events_fits;
src/ctskymap/ctskymap.cpp
1 /*************************************************************************** 1 /***************************************************************************
2 * ctskymap - Sky mapping tool * 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 * This program is free software: you can redistribute it and/or modify * 7 * This program is free software: you can redistribute it and/or modify *
@@ -30,14 +30,14 @@ @@ -30,14 +30,14 @@
30 #endif 30 #endif
31 #include <cstdio> 31 #include <cstdio>
32 #include "ctskymap.hpp" 32 #include "ctskymap.hpp"
33 -#include "GSkyRegions.hpp"  
34 33
35 /* __ Method name definitions ____________________________________________ */ 34 /* __ Method name definitions ____________________________________________ */
36 #define G_GET_PARAMETERS "ctskymap::get_parameter()" 35 #define G_GET_PARAMETERS "ctskymap::get_parameter()"
37 #define G_MAP_EVENTS "ctskymap::map_events(GCTAObservation*)" 36 #define G_MAP_EVENTS "ctskymap::map_events(GCTAObservation*)"
38 #define G_MAP_BACKGROUND_IRF "ctskymap::map_background_irf(GCTAObservation*)" 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 /* __ Debug definitions __________________________________________________ */ 42 /* __ Debug definitions __________________________________________________ */
43 43
@@ -222,37 +222,18 @@ void ctskymap::run(void) @@ -222,37 +222,18 @@ void ctskymap::run(void)
222 // Get parameters 222 // Get parameters
223 get_parameters(); 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 // Optionally publish sky map 237 // Optionally publish sky map
257 if (m_publish) { 238 if (m_publish) {
258 publish(); 239 publish();
@@ -266,8 +247,8 @@ void ctskymap::run(void) @@ -266,8 +247,8 @@ void ctskymap::run(void)
266 /***********************************************************************//** 247 /***********************************************************************//**
267 * @brief Save sky map 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 void ctskymap::save(void) 253 void ctskymap::save(void)
273 { 254 {
@@ -284,39 +265,31 @@ void ctskymap::save(void) @@ -284,39 +265,31 @@ void ctskymap::save(void)
284 GFits fits; 265 GFits fits;
285 266
286 // Write sky map into FITS file 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 if (m_bkgsubtract != "NONE") { 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 // Write significance map into FITS file 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 } // endif: background subtraction was requested 294 } // endif: background subtraction was requested
322 295
@@ -375,25 +348,36 @@ void ctskymap::publish(const std::string&amp; name) @@ -375,25 +348,36 @@ void ctskymap::publish(const std::string&amp; name)
375 ***************************************************************************/ 348 ***************************************************************************/
376 void ctskymap::init_members(void) 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 m_emin = 0.0; 354 m_emin = 0.0;
386 m_emax = 0.0; 355 m_emax = 0.0;
387 m_bkgsubtract = "NONE"; 356 m_bkgsubtract = "NONE";
388 m_roiradius = 0.0; 357 m_roiradius = 0.0;
389 m_inradius = 0.0; 358 m_inradius = 0.0;
390 m_outradius = 0.0; 359 m_outradius = 0.0;
  360 + m_iterations = 0;
  361 + m_threshold = 5.0;
  362 + m_usefft = true;
391 m_publish = false; 363 m_publish = false;
392 m_chatter = static_cast<GChatter>(2); 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 // Initialise cache 375 // Initialise cache
395 m_solidangle.clear(); 376 m_solidangle.clear();
396 m_dirs.clear(); 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 // Return 382 // Return
399 return; 383 return;
@@ -407,25 +391,36 @@ void ctskymap::init_members(void) @@ -407,25 +391,36 @@ void ctskymap::init_members(void)
407 ***************************************************************************/ 391 ***************************************************************************/
408 void ctskymap::copy_members(const ctskymap& app) 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 m_emin = app.m_emin; 397 m_emin = app.m_emin;
418 m_emax = app.m_emax; 398 m_emax = app.m_emax;
419 m_bkgsubtract = app.m_bkgsubtract; 399 m_bkgsubtract = app.m_bkgsubtract;
420 m_roiradius = app.m_roiradius; 400 m_roiradius = app.m_roiradius;
421 m_inradius = app.m_inradius; 401 m_inradius = app.m_inradius;
422 m_outradius = app.m_outradius; 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 m_publish = app.m_publish; 406 m_publish = app.m_publish;
424 m_chatter = app.m_chatter; 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 // Copy cache 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 // Return 425 // Return
431 return; 426 return;
@@ -453,41 +448,92 @@ void ctskymap::free_members(void) @@ -453,41 +448,92 @@ void ctskymap::free_members(void)
453 ***************************************************************************/ 448 ***************************************************************************/
454 void ctskymap::get_parameters(void) 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 m_bkgsubtract = (*this)["bkgsubtract"].string(); 506 m_bkgsubtract = (*this)["bkgsubtract"].string();
467 507
468 // Get RING background parameters 508 // Get RING background parameters
469 if (m_bkgsubtract == "RING") { 509 if (m_bkgsubtract == "RING") {
470 510
471 // Get parameters 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 // Make sure that (roiradius < inradius < outradius) 522 // Make sure that (roiradius < inradius < outradius)
477 if (m_roiradius > m_inradius) { 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 throw GException::invalid_value(G_GET_PARAMETERS, msg); 525 throw GException::invalid_value(G_GET_PARAMETERS, msg);
480 } 526 }
481 else if (m_inradius > m_outradius) { 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 throw GException::invalid_value(G_GET_PARAMETERS, msg); 529 throw GException::invalid_value(G_GET_PARAMETERS, msg);
484 } 530 }
485 531
486 } // endif: read RING background parameters 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 set_response(m_obs); 537 set_response(m_obs);
492 } 538 }
493 539
@@ -497,39 +543,209 @@ void ctskymap::get_parameters(void) @@ -497,39 +543,209 @@ void ctskymap::get_parameters(void)
497 543
498 // Read ahead parameters 544 // Read ahead parameters
499 if (read_ahead()) { 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 // Create background map and significance map if background subtraction 568 // Create background map and significance map if background subtraction
504 // is requested 569 // is requested
505 if (m_bkgsubtract != "NONE") { 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 // Setup the exclusions map 572 // Setup the exclusions map
512 - map_exclusions((*this)["inexclusion"].filename()); 573 + setup_exclusion_map(m_inexclusion);
513 574
514 // Cache the pixel solid angles and sky directions 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 // Return 750 // Return
535 return; 751 return;
@@ -537,16 +753,17 @@ void ctskymap::get_parameters(void) @@ -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 * @param[in] obs CTA observation. 758 * @param[in] obs CTA observation.
543 * 759 *
544 * @exception GException::no_list 760 * @exception GException::no_list
545 * No event list found in observation. 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 // Get non-const pointer on a CTA event list 768 // Get non-const pointer on a CTA event list
552 GCTAEventList* events = dynamic_cast<GCTAEventList*> 769 GCTAEventList* events = dynamic_cast<GCTAEventList*>
@@ -589,11 +806,11 @@ void ctskymap::map_events(GCTAObservation* obs) @@ -589,11 +806,11 @@ void ctskymap::map_events(GCTAObservation* obs)
589 // Determine sky pixel 806 // Determine sky pixel
590 GCTAInstDir* inst = (GCTAInstDir*)&(event->dir()); 807 GCTAInstDir* inst = (GCTAInstDir*)&(event->dir());
591 GSkyDir dir = inst->dir(); 808 GSkyDir dir = inst->dir();
592 - GSkyPixel pixel = m_skymap.dir2pix(dir); 809 + GSkyPixel pixel = m_counts.dir2pix(dir);
593 810
594 // Skip if pixel is out of range 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 num_outside_map++; 814 num_outside_map++;
598 continue; 815 continue;
599 } 816 }
@@ -605,49 +822,25 @@ void ctskymap::map_events(GCTAObservation* obs) @@ -605,49 +822,25 @@ void ctskymap::map_events(GCTAObservation* obs)
605 } 822 }
606 823
607 // Fill event in skymap 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 num_in_map++; 827 num_in_map++;
610 - 828 +
611 } // endfor: looped over all events 829 } // endfor: looped over all events
612 830
613 // Log binning results 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 // Return 846 // Return
@@ -656,7 +849,7 @@ void ctskymap::map_background(GCTAObservation* obs) @@ -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 * @param[in] obs CTA observation. 854 * @param[in] obs CTA observation.
662 * 855 *
@@ -664,14 +857,14 @@ void ctskymap::map_background(GCTAObservation* obs) @@ -664,14 +857,14 @@ void ctskymap::map_background(GCTAObservation* obs)
664 * No response information available for observation. 857 * No response information available for observation.
665 * No background template available in instrument response function. 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 // Get IRF response 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 // Throw an exception if observation has no instrument response function 869 // Throw an exception if observation has no instrument response function
677 if (rsp == NULL) { 870 if (rsp == NULL) {
@@ -694,60 +887,58 @@ void ctskymap::map_background_irf(GCTAObservation* obs) @@ -694,60 +887,58 @@ void ctskymap::map_background_irf(GCTAObservation* obs)
694 throw GException::invalid_value(G_MAP_BACKGROUND_IRF, msg); 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 // Initialise statistics 905 // Initialise statistics
705 - int calls = 0;  
706 double total = 0.0; 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 // Get sky direction of pixel 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 continue; 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 // Multiply background rate with livetime and solid angle 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 // Update total number of background events 932 // Update total number of background events
744 total += value; 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 // Return 943 // Return
753 return; 944 return;
@@ -755,97 +946,99 @@ void ctskymap::map_background_irf(GCTAObservation* obs) @@ -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 // Return 1043 // Return
851 return; 1044 return;
@@ -853,24 +1046,85 @@ void ctskymap::map_background_ring(GCTAObservation* obs) @@ -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 // Return 1129 // Return
876 return; 1130 return;
@@ -878,89 +1132,85 @@ void ctskymap::write_hdu_keywords(GFitsHDU* hdu) const @@ -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 // Return 1215 // Return
966 return; 1216 return;
@@ -970,66 +1220,107 @@ void ctskymap::map_significance(void) @@ -970,66 +1220,107 @@ void ctskymap::map_significance(void)
970 /***********************************************************************//** 1220 /***********************************************************************//**
971 * @brief Computes Non, Noff and alpha for a counts map and sensitivity map 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 double& non, 1244 double& non,
987 double& noff, 1245 double& noff,
988 double& alpha) 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 // Initialise On and Off alpha 1253 // Initialise On and Off alpha
995 double alpha_on = 0.0; 1254 double alpha_on = 0.0;
996 double alpha_off = 0.0; 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 // Compute alpha. If the off region does not have any sensitivity then 1325 // Compute alpha. If the off region does not have any sensitivity then
1035 // set Non = Noff = 0 1326 // set Non = Noff = 0
@@ -1048,34 +1339,100 @@ void ctskymap::compute_ring_values(const GSkyMap&amp; counts, @@ -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 // Return 1437 // Return
1081 return; 1438 return;
@@ -1083,64 +1440,166 @@ void ctskymap::map_exclusions(const GFilename&amp; filename) @@ -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 // Return 1604 // Return
1146 return; 1605 return;
@@ -1148,21 +1607,36 @@ void ctskymap::map_exclusions_reg(const GFilename&amp; filename) @@ -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 * ctskymap - Sky mapping tool * 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 * This program is free software: you can redistribute it and/or modify * 7 * This program is free software: you can redistribute it and/or modify *
@@ -64,59 +64,69 @@ public: @@ -64,59 +64,69 @@ public:
64 64
65 protected: 65 protected:
66 // Protected methods 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 // User parameters 100 // User parameters
99 GFilename m_outmap; //!< Output file name 101 GFilename m_outmap; //!< Output file name
  102 + GFilename m_inexclusion; //!< Exclusion map file name
100 double m_emin; //!< Minimum energy (TeV) 103 double m_emin; //!< Minimum energy (TeV)
101 double m_emax; //!< Maximum energy (TeV) 104 double m_emax; //!< Maximum energy (TeV)
102 std::string m_bkgsubtract; //!< Background subtraction method 105 std::string m_bkgsubtract; //!< Background subtraction method
103 double m_roiradius; //!< Region of interest radius for RING bkg. 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 bool m_publish; //!< Publish sky map? 112 bool m_publish; //!< Publish sky map?
107 GChatter m_chatter; //!< Chattiness 113 GChatter m_chatter; //!< Chattiness
108 114
109 // Protected members 115 // Protected members
  116 + bool m_has_inmap; //!< Has valid input map
110 GSkyMap m_skymap; //!< Sky map 117 GSkyMap m_skymap; //!< Sky map
111 GSkyMap m_bkgmap; //!< Background map 118 GSkyMap m_bkgmap; //!< Background map
112 GSkyMap m_sigmap; //!< Significance map 119 GSkyMap m_sigmap; //!< Significance map
  120 + GSkyMap m_counts; //!< Counts map
  121 + GSkyMap m_acceptance; //!< Acceptance map
113 GSkyMap m_exclmap; //!< Exclusion map for RING background 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 // Caching variables to prevent multiple computations of the same thing 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,6 +19,7 @@
19 inobs, f, a, events.fits,,, "Input event list or observation definition XML file" 19 inobs, f, a, events.fits,,, "Input event list or observation definition XML file"
20 caldb, s, a, prod2,,, "Calibration database" 20 caldb, s, a, prod2,,, "Calibration database"
21 irf, s, a, South_0.5h,,, "Instrument response function" 21 irf, s, a, South_0.5h,,, "Instrument response function"
  22 +inmap, f, h, NONE,,, "Input skymap file"
22 outmap, f, a, skymap.fits,,, "Output skymap file" 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,10 +44,13 @@ yref, r, a, 22.01,-90,90, &quot;Second coordinate of image center in degrees (DEC
43 # Background subtraction parameters 44 # Background subtraction parameters
44 #================================== 45 #==================================
45 bkgsubtract, s, a, NONE, NONE|IRF|RING,, "Background subtraction method" 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 inexclusion, f, h, NONE,,, "Exclusion map or DS9 region file name" 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 # Standard parameters 56 # Standard parameters
src/support/ctobservation.cpp
@@ -319,6 +319,48 @@ const GCTAObservation* ctobservation::next_unbinned_observation(void) const @@ -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 * @brief Write OGIP keywords in FITS HDU 364 * @brief Write OGIP keywords in FITS HDU
323 * 365 *
324 * @param[in,out] hdu Pointer to FITS HDU. 366 * @param[in,out] hdu Pointer to FITS HDU.
src/support/ctobservation.hpp
1 /*************************************************************************** 1 /***************************************************************************
2 * ctobservation - Base class for observation tools * 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 * This program is free software: you can redistribute it and/or modify * 7 * This program is free software: you can redistribute it and/or modify *
@@ -73,6 +73,7 @@ protected: @@ -73,6 +73,7 @@ protected:
73 GCTAObservation* next_unbinned_observation(void); 73 GCTAObservation* next_unbinned_observation(void);
74 const GCTAObservation* first_unbinned_observation(void) const; 74 const GCTAObservation* first_unbinned_observation(void) const;
75 const GCTAObservation* next_unbinned_observation(void) const; 75 const GCTAObservation* next_unbinned_observation(void) const;
  76 + void read_ogip_keywords(GFitsHDU* hdu) const;
76 void write_ogip_keywords(GFitsHDU* hdu) const; 77 void write_ogip_keywords(GFitsHDU* hdu) const;
77 void set_obs_statistic(const std::string& statistic); 78 void set_obs_statistic(const std::string& statistic);
78 void save_events_fits(void); 79 void save_events_fits(void);
src/support/ctool.cpp
@@ -650,7 +650,11 @@ GEbounds ctool::create_ebounds(void) @@ -650,7 +650,11 @@ GEbounds ctool::create_ebounds(void)
650 ***************************************************************************/ 650 ***************************************************************************/
651 GSkyMap ctool::create_map(const GObservations& obs) 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 double xref = 0.0; 658 double xref = 0.0;
655 double yref = 0.0; 659 double yref = 0.0;
656 bool usepnt = (*this)["usepnt"].boolean(); 660 bool usepnt = (*this)["usepnt"].boolean();
@@ -658,11 +662,11 @@ GSkyMap ctool::create_map(const GObservations&amp; obs) @@ -658,11 +662,11 @@ GSkyMap ctool::create_map(const GObservations&amp; obs)
658 xref = (*this)["xref"].real(); 662 xref = (*this)["xref"].real();
659 yref = (*this)["yref"].real(); 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 // If requested, get pointing from observations 671 // If requested, get pointing from observations
668 if (usepnt) { 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,7 +112,7 @@ class Test(test):
112 obsinfo['ra'] = 83.63 112 obsinfo['ra'] = 83.63
113 obsinfo['dec'] = 22.01 113 obsinfo['dec'] = 22.01
114 obsinfo['logfile'] = 'csobsinfo_py1.log' 114 obsinfo['logfile'] = 'csobsinfo_py1.log'
115 - obsinfo['chatter'] = 2 115 + obsinfo['chatter'] = 3
116 116
117 # Run csobsinfo script and save XML file 117 # Run csobsinfo script and save XML file
118 obsinfo.logFileOpen() # Make sure we get a log file 118 obsinfo.logFileOpen() # Make sure we get a log file
@@ -122,21 +122,20 @@ class Test(test): @@ -122,21 +122,20 @@ class Test(test):
122 # Check DS9 file 122 # Check DS9 file
123 self._check_ds9_file('csobsinfo_py1.reg') 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 # Return 140 # Return
142 return 141 return
@@ -151,13 +150,12 @@ class Test(test): @@ -151,13 +150,12 @@ class Test(test):
151 150
152 # Expect "fk5" in first line 151 # Expect "fk5" in first line
153 line = f.readline().strip('\n') 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 # Expect "fk5" in first line 156 # Expect "fk5" in first line
158 line = f.readline().strip('\n') 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 # Close file 160 # Close file
163 f.close() 161 f.close()
test/test_ctskymap.py
@@ -248,6 +248,7 @@ class Test(test): @@ -248,6 +248,7 @@ class Test(test):
248 skymap['roiradius'] = 0.1 248 skymap['roiradius'] = 0.1
249 skymap['inradius'] = 0.6 249 skymap['inradius'] = 0.6
250 skymap['outradius'] = 0.8 250 skymap['outradius'] = 0.8
  251 + skymap['iterations'] = 0
251 skymap['inexclusion'] = 'NONE' 252 skymap['inexclusion'] = 'NONE'
252 skymap['caldb'] = self._caldb 253 skymap['caldb'] = self._caldb
253 skymap['irf'] = self._irf 254 skymap['irf'] = self._irf
test/test_obsutils.py
@@ -2,7 +2,7 @@ @@ -2,7 +2,7 @@
2 # ========================================================================== 2 # ==========================================================================
3 # This scripts performs unit tests for the obsutils module 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 # This program is free software: you can redistribute it and/or modify 7 # This program is free software: you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by 8 # it under the terms of the GNU General Public License as published by
@@ -214,7 +214,7 @@ class Test(test): @@ -214,7 +214,7 @@ class Test(test):
214 # Check simulation results 214 # Check simulation results
215 self.test_value(res.size(), 1, 'Check number of observations') 215 self.test_value(res.size(), 1, 'Check number of observations')
216 self.test_value(res.models().size(), 2, 'Check number of models') 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 self.test_value(res.npred(), 0.0, 'Check number of predicted events') 218 self.test_value(res.npred(), 0.0, 'Check number of predicted events')
219 self.test_value(res[0].eventtype(), 'CountsCube', 'Check event type') 219 self.test_value(res[0].eventtype(), 'CountsCube', 'Check event type')
220 self.test_value(res[0].events().ebounds().emin().TeV(), 1.0, 220 self.test_value(res[0].events().ebounds().emin().TeV(), 1.0,
@@ -223,7 +223,7 @@ class Test(test): @@ -223,7 +223,7 @@ class Test(test):
223 'Check minimum energy') 223 'Check minimum energy')
224 self.test_value(res[0].events().ebounds().size(), 5, 224 self.test_value(res[0].events().ebounds().size(), 5,
225 'Check number of energy bins') 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 'Check number of events in cube') 227 'Check number of events in cube')
228 228
229 # Check energy dispersion flag 229 # Check energy dispersion flag