Commit cfa7379fa139515e72e707a703b2d574cc4794b7

Authored by Jürgen Knödlseder
1 parent 1e00ea3d

Increase ctool branch coverage (#1828)

src/cterror/cterror.cpp
@@ -250,85 +250,80 @@ void cterror::run(void) @@ -250,85 +250,80 @@ void cterror::run(void)
250 log_value(NORMAL, "Maximum log likelihood", gammalib::str(m_best_logL,3)); 250 log_value(NORMAL, "Maximum log likelihood", gammalib::str(m_best_logL,3));
251 log_string(NORMAL, m_obs.models().print(m_chatter)); 251 log_string(NORMAL, m_obs.models().print(m_chatter));
252 252
253 - // Continue only if source model exists  
254 - if (m_obs.models().contains(m_srcname)) { 253 + // Save best fitting models
  254 + GModels models_best = m_obs.models();
255 255
256 - // Save best fitting models  
257 - GModels models_best = m_obs.models(); 256 + // Get pointer on model
  257 + GModel* model = models_best[m_srcname];
258 258
259 - // Get pointer on model  
260 - GModel* model = models_best[m_srcname]; 259 + // Get number of parameters
  260 + int npars = model->size();
261 261
262 - // Get number of parameters  
263 - int npars = model->size(); 262 + // Loop over parameters of sky model
  263 + for (int i = 0; i < npars; ++i) {
264 264
265 - // Loop over parameters of sky model  
266 - for (int i = 0; i < npars; ++i) {  
267 -  
268 - // Skip parameter if it is fixed  
269 - if (model->at(i).is_fixed()) {  
270 - continue;  
271 - } 265 + // Skip parameter if it is fixed
  266 + if (model->at(i).is_fixed()) {
  267 + continue;
  268 + }
272 269
273 - // Initialise with best fitting models  
274 - m_obs.models(models_best);  
275 -  
276 - // Get pointer on model parameter  
277 - GModels& current_models = const_cast<GModels&>(m_obs.models());  
278 - m_model_par = &(current_models[m_srcname]->at(i));  
279 -  
280 - // Extract current value  
281 - m_value = m_model_par->factor_value();  
282 -  
283 - // Compute parameter bracketing  
284 - double parmin = std::max(m_model_par->factor_min(),  
285 - m_value - 10.0*m_model_par->factor_error());  
286 - double parmax = std::min(m_model_par->factor_max(),  
287 - m_value + 10.0*m_model_par->factor_error());  
288 -  
289 - // Write header and initial parameters into logger  
290 - log_header1(TERSE, "Compute error for source \""+m_srcname+"\""  
291 - " parameter \""+m_model_par->name()+"\"");  
292 - log_value(NORMAL, "Confidence level",  
293 - gammalib::str(m_confidence*100.0)+" %");  
294 - log_value(NORMAL, "Log-likelihood difference", m_dlogL);  
295 - log_value(NORMAL, "Initial factor range",  
296 - "["+gammalib::str(parmin)+", "+gammalib::str(parmax)+"]");  
297 -  
298 - // Compute lower and upper boundaries  
299 - double value_lo = error_bisection(parmin, m_value);  
300 - double value_hi = error_bisection(m_value, parmax);  
301 -  
302 - // Compute errors  
303 - double error = 0.5 * (value_hi - value_lo);  
304 - double error_neg = m_value - value_lo;  
305 - double error_pos = value_hi - m_value;  
306 - double error_value = std::abs(error*m_model_par->scale());  
307 - double error_value_neg = std::abs(error_neg*m_model_par->scale());  
308 - double error_value_pos = std::abs(error_pos*m_model_par->scale());  
309 -  
310 - // Write results into logger  
311 - std::string unit = " " + m_model_par->unit();  
312 - log_value(NORMAL, "Lower parameter factor", value_lo);  
313 - log_value(NORMAL, "Upper parameter factor", value_hi);  
314 - log_value(NORMAL, "Error from curvature",  
315 - gammalib::str(m_model_par->error()) + unit);  
316 - log_value(NORMAL, "Error from profile",  
317 - gammalib::str(error_value) + unit);  
318 - log_value(NORMAL, "Negative profile error",  
319 - gammalib::str(error_value_neg) + unit);  
320 - log_value(NORMAL, "Positive profile error",  
321 - gammalib::str(error_value_pos) + unit);  
322 -  
323 - // Save error result  
324 - model->at(i).factor_error(error);  
325 -  
326 - } // endfor: looped over spectral parameters  
327 -  
328 - // Restore best fitting models (now with new errors computed) 270 + // Initialise with best fitting models
329 m_obs.models(models_best); 271 m_obs.models(models_best);
330 272
331 - } // endif: source model exists 273 + // Get pointer on model parameter
  274 + GModels& current_models = const_cast<GModels&>(m_obs.models());
  275 + m_model_par = &(current_models[m_srcname]->at(i));
  276 +
  277 + // Extract current value
  278 + m_value = m_model_par->factor_value();
  279 +
  280 + // Compute parameter bracketing
  281 + double parmin = std::max(m_model_par->factor_min(),
  282 + m_value - 10.0*m_model_par->factor_error());
  283 + double parmax = std::min(m_model_par->factor_max(),
  284 + m_value + 10.0*m_model_par->factor_error());
  285 +
  286 + // Write header and initial parameters into logger
  287 + log_header1(TERSE, "Compute error for source \""+m_srcname+"\""
  288 + " parameter \""+m_model_par->name()+"\"");
  289 + log_value(NORMAL, "Confidence level",
  290 + gammalib::str(m_confidence*100.0)+" %");
  291 + log_value(NORMAL, "Log-likelihood difference", m_dlogL);
  292 + log_value(NORMAL, "Initial factor range",
  293 + "["+gammalib::str(parmin)+", "+gammalib::str(parmax)+"]");
  294 +
  295 + // Compute lower and upper boundaries
  296 + double value_lo = error_bisection(parmin, m_value);
  297 + double value_hi = error_bisection(m_value, parmax);
  298 +
  299 + // Compute errors
  300 + double error = 0.5 * (value_hi - value_lo);
  301 + double error_neg = m_value - value_lo;
  302 + double error_pos = value_hi - m_value;
  303 + double error_value = std::abs(error*m_model_par->scale());
  304 + double error_value_neg = std::abs(error_neg*m_model_par->scale());
  305 + double error_value_pos = std::abs(error_pos*m_model_par->scale());
  306 +
  307 + // Write results into logger
  308 + std::string unit = " " + m_model_par->unit();
  309 + log_value(NORMAL, "Lower parameter factor", value_lo);
  310 + log_value(NORMAL, "Upper parameter factor", value_hi);
  311 + log_value(NORMAL, "Error from curvature",
  312 + gammalib::str(m_model_par->error()) + unit);
  313 + log_value(NORMAL, "Error from profile",
  314 + gammalib::str(error_value) + unit);
  315 + log_value(NORMAL, "Negative profile error",
  316 + gammalib::str(error_value_neg) + unit);
  317 + log_value(NORMAL, "Positive profile error",
  318 + gammalib::str(error_value_pos) + unit);
  319 +
  320 + // Save error result
  321 + model->at(i).factor_error(error);
  322 +
  323 + } // endfor: looped over spectral parameters
  324 +
  325 + // Restore best fitting models (now with new errors computed)
  326 + m_obs.models(models_best);
332 327
333 // Recover optimizer 328 // Recover optimizer
334 m_opt = best_opt; 329 m_opt = best_opt;
src/cterror/cterror.par
@@ -35,7 +35,7 @@ outmodel, f, a, &quot;results.xml&quot;,,, &quot;Output model XML file&quot; @@ -35,7 +35,7 @@ outmodel, f, a, &quot;results.xml&quot;,,, &quot;Output model XML file&quot;
35 #============================= 35 #=============================
36 confidence, r, h, 0.68,0.0,1.0, "Confidence level" 36 confidence, r, h, 0.68,0.0,1.0, "Confidence level"
37 tol, r, h, 1e-3,,, "Computation tolerance" 37 tol, r, h, 1e-3,,, "Computation tolerance"
38 -max_iter, i, h, 50,,, "Maximum number of iterations" 38 +max_iter, i, h, 50,1,1000, "Maximum number of iterations"
39 39
40 # 40 #
41 # Standard parameters 41 # Standard parameters
src/ctulimit/ctulimit.cpp
@@ -228,17 +228,9 @@ void ctulimit::run(void) @@ -228,17 +228,9 @@ void ctulimit::run(void)
228 // Get task parameters 228 // Get task parameters
229 get_parameters(); 229 get_parameters();
230 230
231 - // Set energy dispersion flag for all CTA observations and save old 231 + // Set energy dispersion flags of all CTA observations and save old
232 // values in save_edisp vector 232 // values in save_edisp vector
233 - std::vector<bool> save_edisp;  
234 - save_edisp.assign(m_obs.size(), false);  
235 - for (int i = 0; i < m_obs.size(); ++i) {  
236 - GCTAObservation* obs = dynamic_cast<GCTAObservation*>(m_obs[i]);  
237 - if (obs != NULL) {  
238 - save_edisp[i] = obs->response()->apply_edisp();  
239 - obs->response()->apply_edisp(m_apply_edisp);  
240 - }  
241 - } 233 + std::vector<bool> save_edisp = set_edisp(m_obs, m_apply_edisp);
242 234
243 // Write input observation container into logger 235 // Write input observation container into logger
244 log_observations(NORMAL, m_obs, "Input observation"); 236 log_observations(NORMAL, m_obs, "Input observation");
@@ -338,13 +330,8 @@ void ctulimit::run(void) @@ -338,13 +330,8 @@ void ctulimit::run(void)
338 // Recover optimizer 330 // Recover optimizer
339 m_opt = best_opt; 331 m_opt = best_opt;
340 332
341 - // Restore energy dispersion flag for all CTA observations  
342 - for (int i = 0; i < m_obs.size(); ++i) {  
343 - GCTAObservation* obs = dynamic_cast<GCTAObservation*>(m_obs[i]);  
344 - if (obs != NULL) {  
345 - obs->response()->apply_edisp(save_edisp[i]);  
346 - }  
347 - } 333 + // Restore energy dispersion flags of all CTA observations
  334 + restore_edisp(m_obs, save_edisp);
348 335
349 // Return 336 // Return
350 return; 337 return;
test/test_ctedispcube.py
@@ -206,6 +206,7 @@ class Test(test): @@ -206,6 +206,7 @@ class Test(test):
206 edispcube['yref'] = 22.01 206 edispcube['yref'] = 22.01
207 edispcube['migramax'] = 2.0 207 edispcube['migramax'] = 2.0
208 edispcube['migrabins'] = 10 208 edispcube['migrabins'] = 10
  209 + edispcube['addbounds'] = True
209 edispcube['outcube'] = 'ctedispcube_py3.fits' 210 edispcube['outcube'] = 'ctedispcube_py3.fits'
210 edispcube['logfile'] = 'ctedispcube_py3.log' 211 edispcube['logfile'] = 'ctedispcube_py3.log'
211 edispcube['chatter'] = 4 212 edispcube['chatter'] = 4
test/test_cterror.py
@@ -161,6 +161,41 @@ class Test(test): @@ -161,6 +161,41 @@ class Test(test):
161 # Check that the cleared copy has also cleared the observations 161 # Check that the cleared copy has also cleared the observations
162 self._check_obs(cpy_error.obs(), nobs=0, nmodels=0) 162 self._check_obs(cpy_error.obs(), nobs=0, nmodels=0)
163 163
  164 + # Prepare observation container with a single event list
  165 + obs = gammalib.GObservations()
  166 + obs.append(gammalib.GCTAObservation(self._events))
  167 + obs.models(gammalib.GModels(self._model))
  168 +
  169 + # Setup cterror tool from observation container
  170 + error = ctools.cterror(obs)
  171 + error['srcname'] = 'Crab'
  172 + error['caldb'] = self._caldb
  173 + error['irf'] = self._irf
  174 + error['outmodel'] = 'cterror_py3.xml'
  175 + error['logfile'] = 'cterror_py3.log'
  176 + error['chatter'] = 4
  177 +
  178 + # Execute cterror tool
  179 + error.logFileOpen() # Make sure we get a log file
  180 + error.execute()
  181 +
  182 + # Check result file
  183 + self._check_result_file('cterror_py3.xml')
  184 +
  185 + # And now a run with not enough iterations
  186 + error['max_iter'] = 1
  187 + error['outmodel'] = 'cterror_py4.xml'
  188 + error['logfile'] = 'cterror_py4.log'
  189 +
  190 + # Execute cterror tool and catch the exception
  191 + error.logFileOpen() # Make sure we get a log file
  192 + self.test_try('Test cterror with not enough iterations')
  193 + try:
  194 + error.execute()
  195 + self.test_try_failure('Exception not thrown')
  196 + except ValueError:
  197 + self.test_try_success()
  198 +
164 # Return 199 # Return
165 return 200 return
166 201
test/test_ctlike.py
@@ -160,6 +160,32 @@ class Test(test): @@ -160,6 +160,32 @@ class Test(test):
160 # Check that the cleared copy has also cleared the observations 160 # Check that the cleared copy has also cleared the observations
161 self._check_obs(cpy_like.obs(), nobs=0, nmodels=0) 161 self._check_obs(cpy_like.obs(), nobs=0, nmodels=0)
162 162
  163 + # Prepare observation container with a single event list
  164 + obs = gammalib.GObservations()
  165 + obs.append(gammalib.GCTAObservation(self._events))
  166 +
  167 + # Prepare a special model to enforce TS computation
  168 + models = gammalib.GModels(self._model)
  169 + models['Crab'].tscalc(True)
  170 + obs.models(models)
  171 +
  172 + # Allocate ctlike tool from observation container
  173 + like = ctools.ctlike(obs)
  174 + like['caldb'] = self._caldb
  175 + like['irf'] = self._irf
  176 + like['refit'] = True
  177 + like['fix_spat_for_ts'] = True
  178 + like['outmodel'] = 'ctlike_py3.xml'
  179 + like['logfile'] = 'ctlike_py3.log'
  180 + like['chatter'] = 4
  181 +
  182 + # Execute ctlike
  183 + like.logFileOpen() # Needed to get a new log file
  184 + like.execute()
  185 +
  186 + # Check result file
  187 + self._check_result_file('ctlike_py3.xml')
  188 +
163 # Return 189 # Return
164 return 190 return
165 191
test/test_ctmapcube.py
@@ -177,6 +177,36 @@ class Test(test): @@ -177,6 +177,36 @@ class Test(test):
177 # Check that the cleared copy has also cleared the map cube 177 # Check that the cleared copy has also cleared the map cube
178 self._check_cube(cpy_mapcube.mapcube(), nmaps=0, npixels=0) 178 self._check_cube(cpy_mapcube.mapcube(), nmaps=0, npixels=0)
179 179
  180 + # Allocate ctmapcube tools, set models and do now a linear
  181 + # binning; also do not use Gaussian point sources
  182 + mapcube = ctools.ctmapcube()
  183 + mapcube.models(gammalib.GModels(self._model))
  184 + mapcube['ebinalg'] = 'LIN'
  185 + mapcube['emin'] = 0.1
  186 + mapcube['emax'] = 100.0
  187 + mapcube['enumbins'] = 10
  188 + mapcube['nxpix'] = 100
  189 + mapcube['nypix'] = 100
  190 + mapcube['binsz'] = 0.04
  191 + mapcube['coordsys'] = 'CEL'
  192 + mapcube['proj'] = 'CAR'
  193 + mapcube['xref'] = 83.63
  194 + mapcube['yref'] = 22.01
  195 + mapcube['ptsrcsig'] = 0.0
  196 + mapcube['outcube'] = 'ctmapcube_py3.fits'
  197 + mapcube['logfile'] = 'ctmapcube_py3.log'
  198 + mapcube['chatter'] = 4
  199 +
  200 + # Execute mapcube
  201 + mapcube.logFileOpen() # Needed to get a new log file
  202 + mapcube.execute()
  203 +
  204 + # Check result file
  205 + self._check_result_file('ctmapcube_py3.fits')
  206 +
  207 + # Publish with a name
  208 + mapcube.publish('My map cube')
  209 +
180 # Return 210 # Return
181 return 211 return
182 212
test/test_ctmodel.py
@@ -189,11 +189,38 @@ class Test(test): @@ -189,11 +189,38 @@ class Test(test):
189 # Check that the cleared copy has also cleared the model cube 189 # Check that the cleared copy has also cleared the model cube
190 self._check_cube(cpy_model.cube(), nx=0, ny=0, nebins=0) 190 self._check_cube(cpy_model.cube(), nx=0, ny=0, nebins=0)
191 191
  192 + # Get mixed observation container
  193 + obs = self._obs_mixed()
  194 +
  195 + # Setup ctmodel tool from observation container and get model cube
  196 + # definition from counts cube
  197 + model = ctools.ctmodel(obs)
  198 + model.models(gammalib.GModels(self._model))
  199 + model['incube'] = self._cntcube
  200 + model['expcube'] = 'NONE'
  201 + model['psfcube'] = 'NONE'
  202 + model['bkgcube'] = 'NONE'
  203 + model['caldb'] = self._caldb
  204 + model['irf'] = self._irf
  205 + model['outcube'] = 'ctmodel_py3.fits'
  206 + model['logfile'] = 'ctmodel_py3.log'
  207 + model['chatter'] = 4
  208 +
  209 + # Execute ctmodel tool
  210 + model.logFileOpen() # Needed to get a new log file
  211 + model.execute()
  212 +
  213 + # Check result file
  214 + self._check_result_file('ctmodel_py3.fits', nx=200, ny=200, nebins=20)
  215 +
  216 + # Publish with name
  217 + model.publish('My model')
  218 +
192 # Return 219 # Return
193 return 220 return
194 221
195 # Check result file 222 # Check result file
196 - def _check_result_file(self, filename): 223 + def _check_result_file(self, filename, nx=40, ny=40, nebins=10):
197 """ 224 """
198 Check result file 225 Check result file
199 226
@@ -201,12 +228,18 @@ class Test(test): @@ -201,12 +228,18 @@ class Test(test):
201 ---------- 228 ----------
202 filename : str 229 filename : str
203 Model cube file name 230 Model cube file name
  231 + nx : int, optional
  232 + Number of pixels in X direction
  233 + ny : int, optional
  234 + Number of pixels in Y direction
  235 + nebins : int, optional
  236 + Number of energy bins
204 """ 237 """
205 # Open result file 238 # Open result file
206 cube = gammalib.GCTAEventCube(filename) 239 cube = gammalib.GCTAEventCube(filename)
207 240
208 # Check cube 241 # Check cube
209 - self._check_cube(cube) 242 + self._check_cube(cube, nx=nx, ny=ny, nebins=nebins)
210 243
211 # Return 244 # Return
212 return 245 return
test/test_ctobssim.py
@@ -154,6 +154,12 @@ class Test(test): @@ -154,6 +154,12 @@ class Test(test):
154 sim['logfile'] = 'ctobssim_py2.log' 154 sim['logfile'] = 'ctobssim_py2.log'
155 sim['chatter'] = 3 155 sim['chatter'] = 3
156 156
  157 + # Double maximum event range
  158 + max_rate = sim.max_rate()
  159 + sim.max_rate(2*max_rate)
  160 + self.test_value(sim.max_rate(), 2*max_rate, 1.0e-3,
  161 + 'Check setting of maximum event rate')
  162 +
157 # Run tool 163 # Run tool
158 sim.logFileOpen() 164 sim.logFileOpen()
159 sim.run() 165 sim.run()
test/test_ctpsfcube.py
@@ -187,27 +187,29 @@ class Test(test): @@ -187,27 +187,29 @@ class Test(test):
187 # Get mixed observation container 187 # Get mixed observation container
188 obs = self._obs_mixed() 188 obs = self._obs_mixed()
189 189
190 - # Set-up ctpsfcube from observation container 190 + # Set-up ctpsfcube from observation container and input counts
  191 + # cube
191 psfcube = ctools.ctpsfcube(obs) 192 psfcube = ctools.ctpsfcube(obs)
192 - psfcube['incube'] = 'NONE'  
193 - psfcube['caldb'] = self._caldb  
194 - psfcube['irf'] = self._irf  
195 - psfcube['ebinalg'] = 'LOG'  
196 - psfcube['emin'] = 0.1  
197 - psfcube['emax'] = 100  
198 - psfcube['enumbins'] = 20  
199 - psfcube['nxpix'] = 10  
200 - psfcube['nypix'] = 10  
201 - psfcube['binsz'] = 0.4  
202 - psfcube['coordsys'] = 'CEL'  
203 - psfcube['proj'] = 'CAR'  
204 - psfcube['xref'] = 83.63  
205 - psfcube['yref'] = 22.01  
206 - psfcube['amax'] = 0.3  
207 - psfcube['anumbins'] = 10  
208 - psfcube['outcube'] = 'ctpsfcube_py3.fits'  
209 - psfcube['logfile'] = 'ctpsfcube_py3.log'  
210 - psfcube['chatter'] = 4 193 + psfcube['incube'] = self._cntcube
  194 + psfcube['caldb'] = self._caldb
  195 + psfcube['irf'] = self._irf
  196 + #psfcube['ebinalg'] = 'LOG'
  197 + #psfcube['emin'] = 0.1
  198 + #psfcube['emax'] = 100
  199 + #psfcube['enumbins'] = 20
  200 + #psfcube['nxpix'] = 10
  201 + #psfcube['nypix'] = 10
  202 + #psfcube['binsz'] = 0.4
  203 + #psfcube['coordsys'] = 'CEL'
  204 + #psfcube['proj'] = 'CAR'
  205 + #psfcube['xref'] = 83.63
  206 + #psfcube['yref'] = 22.01
  207 + psfcube['amax'] = 0.3
  208 + psfcube['anumbins'] = 10
  209 + psfcube['addbounds'] = True
  210 + psfcube['outcube'] = 'ctpsfcube_py3.fits'
  211 + psfcube['logfile'] = 'ctpsfcube_py3.log'
  212 + psfcube['chatter'] = 4
211 213
212 # Execute ctpsfcube tool 214 # Execute ctpsfcube tool
213 psfcube.logFileOpen() # Make sure we get a log file 215 psfcube.logFileOpen() # Make sure we get a log file
test/test_ctskymap.py
@@ -18,6 +18,7 @@ @@ -18,6 +18,7 @@
18 # along with this program. If not, see <http://www.gnu.org/licenses/>. 18 # along with this program. If not, see <http://www.gnu.org/licenses/>.
19 # 19 #
20 # ========================================================================== 20 # ==========================================================================
  21 +import os
21 import gammalib 22 import gammalib
22 import ctools 23 import ctools
23 from testing import test 24 from testing import test
@@ -103,10 +104,25 @@ class Test(test): @@ -103,10 +104,25 @@ class Test(test):
103 """ 104 """
104 Test ctskymap from Python 105 Test ctskymap from Python
105 """ 106 """
106 - # Set-up ctskymap 107 + # Allocate ctskymap
107 skymap = ctools.ctskymap() 108 skymap = ctools.ctskymap()
  109 +
  110 + # Check that empty ctskymap tool holds a map that has no pixels
  111 + self._check_map(skymap.map(), nx=0, ny=0)
  112 +
  113 + # Check that saving does not nothing
  114 + skymap['outmap'] = 'ctskymap_py0.fits'
  115 + skymap['logfile'] = 'ctskymap_py0.log'
  116 + skymap.logFileOpen()
  117 + skymap.save()
  118 + self.test_assert(not os.path.isfile('ctskymap_py0.fits'),
  119 + 'Check that no sky map has been created')
  120 +
  121 + # Check that clearing does not lead to an exception or segfault
  122 + skymap.clear()
  123 +
  124 + # Now set ctskymap parameters
108 skymap['inobs'] = self._events 125 skymap['inobs'] = self._events
109 - skymap['outmap'] = 'ctskymap_py1.fits'  
110 skymap['emin'] = 0.1 126 skymap['emin'] = 0.1
111 skymap['emax'] = 100 127 skymap['emax'] = 100
112 skymap['nxpix'] = 200 128 skymap['nxpix'] = 200
@@ -116,6 +132,7 @@ class Test(test): @@ -116,6 +132,7 @@ class Test(test):
116 skymap['proj'] = 'CAR' 132 skymap['proj'] = 'CAR'
117 skymap['xref'] = 83.63 133 skymap['xref'] = 83.63
118 skymap['yref'] = 22.01 134 skymap['yref'] = 22.01
  135 + skymap['outmap'] = 'ctskymap_py1.fits'
119 skymap['logfile'] = 'ctskymap_py1.log' 136 skymap['logfile'] = 'ctskymap_py1.log'
120 skymap['chatter'] = 2 137 skymap['chatter'] = 2
121 138
@@ -127,21 +144,111 @@ class Test(test): @@ -127,21 +144,111 @@ class Test(test):
127 # Check result file 144 # Check result file
128 self._check_result_file('ctskymap_py1.fits') 145 self._check_result_file('ctskymap_py1.fits')
129 146
  147 + # Copy ctskymap tool
  148 + cpy_skymap = skymap.copy()
  149 +
  150 + # Check sky map of ctskymap copy
  151 + self._check_map(cpy_skymap.map())
  152 +
  153 + # Execute copy of ctskymap tool again, now with a higher chatter
  154 + # level than before
  155 + cpy_skymap['usepnt'] = True
  156 + cpy_skymap['emin'] = 0.2
  157 + cpy_skymap['emax'] = 150.0
  158 + cpy_skymap['outmap'] = 'ctskymap_py2.fits'
  159 + cpy_skymap['logfile'] = 'ctskymap_py2.log'
  160 + cpy_skymap['publish'] = True
  161 + cpy_skymap['chatter'] = 3
  162 + cpy_skymap.logFileOpen() # Needed to get a new log file
  163 + cpy_skymap.execute()
  164 +
  165 + # Check result file
  166 + self._check_result_file('ctskymap_py2.fits')
  167 +
  168 + # Now clear copy of ctskymap tool
  169 + cpy_skymap.clear()
  170 +
  171 + # Check that cleared ctskymap tool holds a map that has no pixels
  172 + self._check_map(cpy_skymap.map(), nx=0, ny=0)
  173 +
  174 + # Get mixed observation container
  175 + obs = self._obs_mixed()
  176 +
  177 + # Allocate ctskymap tool from observation container
  178 + skymap = ctools.ctskymap(obs)
  179 + skymap['emin'] = 0.1
  180 + skymap['emax'] = 100
  181 + skymap['nxpix'] = 200
  182 + skymap['nypix'] = 200
  183 + skymap['binsz'] = 0.02
  184 + skymap['coordsys'] = 'CEL'
  185 + skymap['proj'] = 'CAR'
  186 + skymap['xref'] = 83.63
  187 + skymap['yref'] = 22.01
  188 + skymap['outmap'] = 'ctskymap_py3.fits'
  189 + skymap['logfile'] = 'ctskymap_py3.log'
  190 + skymap['chatter'] = 4
  191 +
  192 + # Execute tool
  193 + skymap.logFileOpen() # Needed to get a new log file
  194 + skymap.execute()
  195 +
  196 + # Check result file
  197 + self._check_result_file('ctskymap_py3.fits')
  198 +
  199 + # Publish with name
  200 + skymap.publish('My sky map')
  201 +
130 # Return 202 # Return
131 return 203 return
132 204
133 # Check result file 205 # Check result file
134 - def _check_result_file(self, filename): 206 + def _check_result_file(self, filename, nx=200, ny=200):
135 """ 207 """
136 Check result file 208 Check result file
  209 +
  210 + Parameters
  211 + ----------
  212 + filename : str
  213 + Sky map file name
  214 + nx : int, optional
  215 + Number of X pixels
  216 + ny : int, optional
  217 + Number of Y pixels
137 """ 218 """
138 # Open result file 219 # Open result file
139 - result = gammalib.GSkyMap(filename) 220 + skymap = gammalib.GSkyMap(filename)
140 221
  222 + # Check sky map
  223 + self._check_map(skymap, nx=nx, ny=ny)
  224 +
  225 + # Return
  226 + return
  227 +
  228 + # Check sky map
  229 + def _check_map(self, skymap, nx=200, ny=200):
  230 + """
  231 + Check sky map
  232 +
  233 + Parameters
  234 + ----------
  235 + skymap : `~gammalib.GSkyMap`
  236 + Sky map
  237 + nx : int, optional
  238 + Number of X pixels
  239 + ny : int, optional
  240 + Number of Y pixels
  241 + """
  242 + # Determine number of maps
  243 + if nx > 0 and ny > 0:
  244 + nmaps = 1
  245 + else:
  246 + nmaps = 0
  247 +
141 # Check dimensions 248 # Check dimensions
142 - self.test_value(result.nmaps(), 1, 'Check for one map')  
143 - self.test_value(result.nx(), 200, 'Check for 200 pixels in X')  
144 - self.test_value(result.ny(), 200, 'Check for 200 pixels in Y') 249 + self.test_value(skymap.nmaps(), nmaps, 'Check number of maps')
  250 + self.test_value(skymap.nx(), nx, 'Check for number of X pixels')
  251 + self.test_value(skymap.ny(), ny, 'Check for number of Y pixels')
145 252
146 # Return 253 # Return
147 return 254 return
test/test_ctulimit.py
@@ -99,7 +99,25 @@ class Test(test): @@ -99,7 +99,25 @@ class Test(test):
99 """ 99 """
100 Test ctulimit from Python 100 Test ctulimit from Python
101 """ 101 """
102 - # Set-up ctulimit 102 + # Allocate ctulimit
  103 + ulimit = ctools.ctulimit()
  104 +
  105 + # Check that empty ctulimit tool holds zero upper limits
  106 + self.test_value(ulimit.diff_ulimit(), 0.0, 1.0e-23,
  107 + 'Check differential upper limit')
  108 + self.test_value(ulimit.flux_ulimit(), 0.0, 1.0e-17,
  109 + 'Check upper limit on photon flux')
  110 + self.test_value(ulimit.eflux_ulimit(), 0.0, 1.0e-16,
  111 + 'Check upper limit on energy flux')
  112 +
  113 + # Check that saving does not nothing
  114 + ulimit.logFileOpen()
  115 + ulimit.save()
  116 +
  117 + # Check that clearing does not lead to an exception or segfault
  118 + ulimit.clear()
  119 +
  120 + # Now set ctulimit parameters
103 ulimit = ctools.ctulimit() 121 ulimit = ctools.ctulimit()
104 ulimit['inobs'] = self._events 122 ulimit['inobs'] = self._events
105 ulimit['inmodel'] = self._model 123 ulimit['inmodel'] = self._model
@@ -114,8 +132,50 @@ class Test(test): @@ -114,8 +132,50 @@ class Test(test):
114 ulimit.run() 132 ulimit.run()
115 ulimit.save() 133 ulimit.save()
116 134
117 - # Check result file  
118 - self._check_result_file('ctulimit_py1.log') 135 + # Check results
  136 + self.test_value(ulimit.diff_ulimit(), 8.86803e-18, 1.0e-23,
  137 + 'Check differential upper limit')
  138 + self.test_value(ulimit.flux_ulimit(), 6.10509e-12, 1.0e-17,
  139 + 'Check upper limit on photon flux')
  140 + self.test_value(ulimit.eflux_ulimit(), 2.75669e-11, 1.0e-16,
  141 + 'Check upper limit on energy flux')
  142 +
  143 + # Copy ctulimit tool
  144 + cpy_ulimit = ulimit.copy()
  145 +
  146 + # Check results of copy
  147 + self.test_value(cpy_ulimit.diff_ulimit(), 8.86803e-18, 1.0e-23,
  148 + 'Check differential upper limit')
  149 + self.test_value(cpy_ulimit.flux_ulimit(), 6.10509e-12, 1.0e-17,
  150 + 'ulimit upper limit on photon flux')
  151 + self.test_value(cpy_ulimit.eflux_ulimit(), 2.75669e-11, 1.0e-16,
  152 + 'Check upper limit on energy flux')
  153 +
  154 + # Execute copy of ctulimit tool again, now with a higher chatter
  155 + # level than before
  156 + cpy_ulimit['logfile'] = 'ctulimit_py2.log'
  157 + cpy_ulimit['chatter'] = 3
  158 + cpy_ulimit.logFileOpen() # Needed to get a new log file
  159 + cpy_ulimit.execute()
  160 +
  161 + # Check results
  162 + self.test_value(cpy_ulimit.diff_ulimit(), 8.86803e-18, 1.0e-23,
  163 + 'Check differential upper limit')
  164 + self.test_value(cpy_ulimit.flux_ulimit(), 6.10509e-12, 1.0e-17,
  165 + 'ulimit upper limit on photon flux')
  166 + self.test_value(cpy_ulimit.eflux_ulimit(), 2.75669e-11, 1.0e-16,
  167 + 'Check upper limit on energy flux')
  168 +
  169 + # Now clear copy of ctulimit tool
  170 + cpy_ulimit.clear()
  171 +
  172 + # Check that empty ctulimit tool holds zero upper limits
  173 + self.test_value(cpy_ulimit.diff_ulimit(), 0.0, 1.0e-23,
  174 + 'Check differential upper limit')
  175 + self.test_value(cpy_ulimit.flux_ulimit(), 0.0, 1.0e-17,
  176 + 'Check upper limit on photon flux')
  177 + self.test_value(cpy_ulimit.eflux_ulimit(), 0.0, 1.0e-16,
  178 + 'Check upper limit on energy flux')
119 179
120 # Return 180 # Return
121 return 181 return