Commit e8978f2ad7c05526b4e0c2537d8aad3397102efc

Authored by Jürgen Knödlseder
1 parent d9f9f2bd

Enhance coverage and comment density (#1828)

cscripts/csiactobs.py
... ... @@ -78,6 +78,10 @@ class csiactobs(ctools.cscript):
78 78 self._subdir = ''
79 79 self._debug = False
80 80  
  81 + # Initialise empty observation definition XML file
  82 + self._xml.append(gammalib.GXmlElement('observation_list '
  83 + 'title="observation list"'))
  84 +
81 85 # Initialise application by calling the appropriate class constructor
82 86 self._init_cscript(argv)
83 87  
... ...
cscripts/csinfo.py
... ... @@ -129,22 +129,19 @@ def csinfo_setup_check():
129 129  
130 130 # Check if the "GAMMALIB" environment variable is set
131 131 sys.stdout.write(' GAMMALIB environment variable ... ')
132   - if 'GAMMALIB' in os.environ:
  132 + gammalib_environ = 'GAMMALIB' in os.environ
  133 + if gammalib_environ:
133 134 print('ok')
134   - gammalib_environ = True
135 135 else:
136 136 print('NOT OK')
137   - gammalib_environ = False
138 137  
139 138 # Check if the "CTOOLS" environment variable is set
140 139 sys.stdout.write(' CTOOLS environment variable ... ')
141   - if 'CTOOLS' in os.environ:
  140 + ctools_environ = 'CTOOLS' in os.environ
  141 + if ctools_environ:
142 142 print('ok')
143   - ctools_environ = True
144 143 else:
145 144 print('NOT OK')
146   - ctools_environ = False
147   -
148 145  
149 146 # Check if gammalib Python module import works
150 147 sys.stdout.write(' gammalib Python import .......... ')
... ...
cscripts/csobsdef.py
1 1 #! /usr/bin/env python
2 2 # ==========================================================================
3   -# Generation of an observation definition file.
  3 +# Generation of an observation definition file
4 4 #
5 5 # Copyright (C) 2015-2016 Juergen Knoedlseder
6 6 #
... ...
cscripts/cspull.py
... ... @@ -236,31 +236,37 @@ class cspull(ctools.cscript):
236 236 if self._logNormal():
237 237 self._log.header3('Pulls')
238 238  
239   - # Gather results
240   - colnames = []
241   - values = {}
242   - colnames.append('LogL')
243   - colnames.append('Sim_Events')
244   - colnames.append('Npred_Events')
245   - values['LogL'] = logL
246   - values['Sim_Events'] = nevents
247   - values['Npred_Events'] = npred
  239 + # Gather results in form of a list of result columns and a
  240 + # dictionary containing the results. The result contains the
  241 + # log-likelihood, the number of simulated events, the number of
  242 + # predicted events and for each fitted parameter the fitted value,
  243 + # the pull and the fit error.
  244 + #
  245 + # Note that we do not use the model and parameter iterators
  246 + # because we need the indices to get the true (or real) parameter
  247 + # values from the input models.
  248 + colnames = ['LogL', 'Sim_Events', 'Npred_Events']
  249 + values = {'LogL': logL, 'Sim_Events': nevents, 'Npred_Events': npred}
248 250 for i in range(models.size()):
249   - model = models[i]
250   - model_name = model.name()
  251 + model = models[i]
251 252 for k in range(model.size()):
252 253 par = model[k]
253 254 if par.is_free():
254 255  
255   - # Set parameter name
256   - name = model_name+'_'+par.name()
  256 + # Set name as a combination of model name and parameter
  257 + # name separated by an underscore. In that way each
  258 + # parameter has a unique name.
  259 + name = model.name()+'_'+par.name()
257 260  
258   - # Append parameter, Pull_parameter and e_parameter
  261 + # Append parameter, Pull_parameter and e_parameter column
  262 + # names
259 263 colnames.append(name)
260 264 colnames.append('Pull_'+name)
261 265 colnames.append('e_'+name)
262 266  
263   - # Compute pull
  267 + # Compute pull for this parameter as the difference
  268 + # (fitted - true) / error
  269 + # In case that the error is 0 the pull is set to 99
264 270 fitted_value = par.value()
265 271 real_value = self._obs.models()[i][k].value()
266 272 error = par.error()
... ... @@ -269,16 +275,16 @@ class cspull(ctools.cscript):
269 275 else:
270 276 pull = 99.0
271 277  
272   - # Store results
273   - values[name] = fitted_value
  278 + # Store results in dictionary
  279 + values[name] = fitted_value
274 280 values['Pull_'+name] = pull
275 281 values['e_'+name] = error
276 282  
277   - # Write result
  283 + # Write results into logger
278 284 value = '%.4f (%e +/- %e)' % (pull, fitted_value, error)
279 285 self._log_value(gammalib.NORMAL, name, value)
280 286  
281   - # Bundle together results
  287 + # Bundle together results in a dictionary
282 288 result = {'colnames': colnames, 'values': values}
283 289  
284 290 # Return
... ... @@ -298,20 +304,10 @@ class cspull(ctools.cscript):
298 304 self._get_parameters()
299 305  
300 306 # Write observation into logger
301   - if self._logTerse():
302   - self._log('\n')
303   - self._log.header1(gammalib.number('Observation',len(self._obs)))
304   - self._log(str(self._obs))
305   - self._log('\n')
306   - if self._logExplicit():
307   - for obs in self._obs:
308   - self._log(str(obs))
309   - self._log('\n')
  307 + self._log_observations(gammalib.NORMAL, self._obs, 'Input observation')
310 308  
311 309 # Write header
312   - if self._logTerse():
313   - self._log('\n')
314   - self._log.header1('Generate pull distribution')
  310 + self._log_header1(gammalib.TERSE, 'Generate pull distribution')
315 311  
316 312 # Loop over trials
317 313 for seed in range(self._ntrials):
... ...
cscripts/cssens.py
... ... @@ -73,8 +73,8 @@ class cssens(ctools.cscript):
73 73 self._num_avg = 3
74 74 self._log_clients = False
75 75  
76   - # Initialise application by calling the appropriate class
77   - # constructor.
  76 + # Initialise application by calling the appropriate base class
  77 + # constructor
78 78 self._init_cscript(argv)
79 79  
80 80 # Return
... ... @@ -294,13 +294,13 @@ class cssens(ctools.cscript):
294 294 e_mean = math.pow(10.0, loge)
295 295 erg_mean = e_mean * tev2erg
296 296  
297   - # Compute Crab unit (this is the factor with which the Prefactor needs
298   - # to be multiplied to get 1 Crab
  297 + # Compute Crab unit. This is the factor with which the Prefactor needs
  298 + # to be multiplied to get 1 Crab.
299 299 crab_flux = self._get_crab_flux(emin, emax)
300 300 src_flux = test_model[self._srcname].spectral().flux(emin, emax)
301 301 crab_unit = crab_flux/src_flux
302 302  
303   - # Write header
  303 + # Write header and some introductory information into logger
304 304 self._log_header2(gammalib.TERSE, 'Energies: '+str(emin)+' - '+str(emax))
305 305 self._log_value(gammalib.TERSE, 'Crab flux', str(crab_flux)+' ph/cm2/s')
306 306 self._log_value(gammalib.TERSE, 'Source model flux', str(src_flux)+
... ... @@ -321,37 +321,38 @@ class cssens(ctools.cscript):
321 321 # Update iteration counter
322 322 iterations += 1
323 323  
324   - # Write header
325   - if self._logExplicit():
326   - self._log.header2('Iteration '+str(iterations))
  324 + # Write header for iteration into logger
  325 + self._log_header2(gammalib.EXPLICIT, 'Iteration '+str(iterations))
327 326  
328   - # Set test source model. crab_prefactor is the Prefactor that
329   - # corresponds to 1 Crab
  327 + # Create a copy of the test models, set the prefactor of the test
  328 + # source in the models, and append the models to the observation.
  329 + # "crab_prefactor" is the Prefactor that corresponds to a flux of
  330 + # 1 Crab.
330 331 models = test_model.copy()
331   - crab_prefactor = models[self._srcname]['Prefactor'].value() * \
332   - crab_unit
333   - models[self._srcname]['Prefactor'].value(crab_prefactor * \
334   - test_crab_flux)
  332 + crab_prefactor = models[self._srcname]['Prefactor'].value() * crab_unit
  333 + models[self._srcname]['Prefactor'].value(crab_prefactor * test_crab_flux)
335 334 self._obs.models(models)
336 335  
337   - # Simulate events
  336 + # Simulate events for the models. "sim" holds an observation
  337 + # container with observations containing the simulated events.
338 338 sim = obsutils.sim(self._obs, nbins=self._enumbins, seed=iterations,
339 339 binsz=self._binsz, npix=self._npix,
340 340 log=self._log_clients,
341 341 debug=self['debug'].boolean(),
342 342 edisp=self._edisp)
343 343  
344   - # Determine number of events in simulation
  344 + # Determine number of events in simulation by summing the events
  345 + # over all observations in the observation container
345 346 nevents = 0.0
346 347 for run in sim:
347 348 nevents += run.events().number()
348 349  
349   - # Write simulation results
  350 + # Write simulation results into logger
350 351 self._log_header3(gammalib.EXPLICIT, 'Simulation')
351   - self._log_value(gammalib.EXPLICIT, 'Number of simulated events',
352   - nevents)
  352 + self._log_value(gammalib.EXPLICIT, 'Number of simulated events', nevents)
353 353  
354   - # Fit test source
  354 + # Fit test source to the simulated events in the observation
  355 + # container
355 356 fit = ctools.ctlike(sim)
356 357 fit['edisp'] = edisp=self._edisp
357 358 fit['debug'] = self['debug'].boolean()
... ... @@ -370,24 +371,28 @@ class cssens(ctools.cscript):
370 371 photon_flux = source.spectral().flux(emin, emax)
371 372 energy_flux = source.spectral().eflux(emin, emax)
372 373  
373   - # Compute differential sensitivity in unit erg/cm2/s
  374 + # Compute differential sensitivity in unit erg/cm2/s by evaluating
  375 + # the spectral model at the "e_mean" energy and by multipling the
  376 + # result with the energy squared. Since the "eval()" method returns
  377 + # an intensity in units of ph/cm2/s/MeV we multiply by 1.0e6 to
  378 + # convert into ph/cm2/s/TeV, by "e_mean" to convert into ph/cm2/s,
  379 + # and finally by "erg_mean" to convert to erg/cm2/s.
374 380 energy = gammalib.GEnergy(e_mean, 'TeV')
375   - sensitivity = source.spectral().eval(energy) * e_mean*erg_mean * 1.0e6
  381 + sensitivity = source.spectral().eval(energy) * e_mean*erg_mean*1.0e6
376 382  
377 383 # Assess quality based on a comparison between Npred and Nevents
378 384 quality = npred - nevents
379 385  
380   - # Write test source fit results
  386 + # Write test source fit results into logger
381 387 self._log_header3(gammalib.EXPLICIT, 'Test source model fit')
382 388 self._log_value(gammalib.EXPLICIT, 'Test statistics', ts)
383 389 self._log_value(gammalib.EXPLICIT, 'log likelihood', logL)
384   - self._log_value(gammalib.EXPLICIT, 'Number of predicted events',
385   - quality)
  390 + self._log_value(gammalib.EXPLICIT, 'Number of predicted events', quality)
386 391 self._log_value(gammalib.EXPLICIT, 'Fit quality', npred)
387 392 for model in models:
388 393 self._log_value(gammalib.EXPLICIT, 'Model', model.name())
389 394 for par in model:
390   - self._log(str(par)+'\n')
  395 + self._log_string(gammalib.EXPLICIT, str(par))
391 396  
392 397 # If TS was non-positive then increase the test flux and start
393 398 # over
... ... @@ -395,26 +400,27 @@ class cssens(ctools.cscript):
395 400  
396 401 # If the number of iterations was exceeded then stop
397 402 if (iterations >= self._max_iter):
398   - if self._logTerse():
399   - self._log(' Test ended after %d iterations.\n' %
400   - self._max_iter)
  403 + self._log_string(gammalib.TERSE,
  404 + ' Test ended after %d iterations.' % self._max_iter)
401 405 break
402 406  
403 407 # Increase test flux by a factor of 2
404 408 test_crab_flux = test_crab_flux * 2.0
405 409  
406 410 # Signal start we start over
407   - if self._logExplicit():
408   - self._log('Non positive TS, increase test flux and start '
409   - 'over.\n')
  411 + self._log_string(gammalib.EXPLICIT,
  412 + 'Non positive TS, increase test flux and start over.')
410 413  
411 414 # ... and start over
412 415 continue
413 416  
414   - # Compute flux correction factor based on average TS
  417 + # Compute flux correction factor based on average TS. We
  418 + # assume here that the significance is proportional to the source
  419 + # flux, and that the significance scales as the square root of
  420 + # the Test Statistic
415 421 correct = math.sqrt(self._ts_thres/ts)
416 422  
417   - # Compute extrapolated fluxes
  423 + # Compute extrapolated fluxes based on the flux correction factor
418 424 crab_flux = correct * crab_flux
419 425 photon_flux = correct * photon_flux
420 426 energy_flux = correct * energy_flux
... ... @@ -437,7 +443,7 @@ class cssens(ctools.cscript):
437 443 for model in models:
438 444 self._log_value(gammalib.EXPLICIT, 'Model', model.name())
439 445 for par in model:
440   - self._log(str(par)+'\n')
  446 + self._log_string(gammalib.EXPLICIT, str(par))
441 447 elif self._logTerse():
442 448 name = 'Iteration %d' % iterations
443 449 value = ('TS=%.4f corr=%e %e ph/cm2/s = %e erg/cm2/s = %.2f '
... ... @@ -447,7 +453,7 @@ class cssens(ctools.cscript):
447 453  
448 454 # Compute sliding average of extrapolated fitted prefactor,
449 455 # photon and energy flux. This damps out fluctuations and
450   - # improves convergence
  456 + # improves convergence.
451 457 crab_flux = 0.0
452 458 photon_flux = 0.0
453 459 energy_flux = 0.0
... ... @@ -494,7 +500,7 @@ class cssens(ctools.cscript):
494 500 ' iterations.\n')
495 501 break
496 502  
497   - # Write fit results
  503 + # Write fit results into logger
498 504 self._log_header3(gammalib.TERSE, 'Fit results')
499 505 self._log_value(gammalib.TERSE, 'Test statistics', ts)
500 506 self._log_value(gammalib.TERSE, 'Photon flux',
... ... @@ -512,7 +518,7 @@ class cssens(ctools.cscript):
512 518 for model in models:
513 519 self._log_value(gammalib.TERSE, 'Model', model.name())
514 520 for par in model:
515   - self._log_string(gammalib.TERSE, str(par)+'\n')
  521 + self._log_string(gammalib.TERSE, str(par))
516 522  
517 523 # Restore energy boundaries of observation container
518 524 for i, obs in enumerate(self._obs):
... ... @@ -553,31 +559,15 @@ class cssens(ctools.cscript):
553 559 models = modutils.test_source(self._obs.models(), self._srcname,
554 560 ra=self._ra, dec=self._dec)
555 561  
556   - # Write observations into logger
557   - if self._logTerse():
558   - self._log('\n')
559   - self._log.header1(gammalib.number('Observation',len(self._obs)))
560   - self._log(str(self._obs))
561   - self._log('\n')
562   - if self._logExplicit():
563   - for obs in self._obs:
564   - self._log(str(obs))
565   - self._log('\n')
  562 + # Write observation into logger
  563 + self._log_observations(gammalib.NORMAL, self._obs, 'Observation')
566 564  
567 565 # Write models into logger
568   - if self._logTerse():
569   - self._log('\n')
570   - self._log.header1('Models')
571   - self._log(str(models))
572   - self._log('\n')
  566 + self._log_models(gammalib.NORMAL, models, 'Model')
573 567  
574 568 # Write header
575   - if self._logTerse():
576   - self._log('\n')
577   - self._log.header1('Sensitivity determination')
578   - self._log.parformat('Type')
579   - self._log(self._type)
580   - self._log('\n')
  569 + self._log_header1(gammalib.TERSE, 'Sensitivity determination')
  570 + self._log_value(gammalib.TERSE, 'Type', str(self._type))
581 571  
582 572 # Loop over energy bins
583 573 for ieng in range(self._ebounds.size()):
... ... @@ -620,7 +610,6 @@ class cssens(ctools.cscript):
620 610 return
621 611  
622 612  
623   -
624 613 # ======================== #
625 614 # Main routine entry point #
626 615 # ======================== #
... ...
pyext/ctool.i
... ... @@ -93,8 +93,6 @@ public:
93 93 %rename(_create_cta_obs) create_cta_obs;
94 94 %rename(_require_inobs) require_inobs;
95 95 %rename(_require_inobs_nocube) require_inobs_nocube;
96   - %rename(_log_observations) log_observations;
97   - %rename(_log_models) log_models;
98 96 %rename(_get_roi) get_roi;
99 97 %rename(_set_response) set_response;
100 98 %rename(_set_edisp) set_edisp;
... ... @@ -133,14 +131,6 @@ public:
133 131 // Protected methods that extract user parameters
134 132 GCTARoi get_roi(void);
135 133  
136   - // Protected methods for logging
137   - void log_observations(const GChatter& chatter,
138   - const GObservations& obs,
139   - const std::string& what = "Observation");
140   - void log_models(const GChatter& chatter,
141   - const GModels& models,
142   - const std::string& what = "Model");
143   -
144 134 // Protected support methods
145 135 void set_response(GObservations& obs);
146 136 std::vector<bool> set_edisp(GObservations& obs, const bool& edisp) const;
... ... @@ -193,12 +183,23 @@ public:
193 183 * @brief ctool base class Python extensions
194 184 ***************************************************************************/
195 185 %extend ctool {
  186 + void _log_observations(const int& chatter,
  187 + const GObservations& obs,
  188 + const std::string& what = "Observation") {
  189 + self->log_observations(GChatter(chatter), obs, what);
  190 + }
  191 + void _log_models(const int& chatter,
  192 + const GModels& models,
  193 + const std::string& what = "Model") {
  194 + self->log_models(GChatter(chatter), models, what);
  195 + }
196 196 void _read_ahead(const bool& flag) {
197 197 self->m_read_ahead = flag;
198 198 return;
199 199 }
200 200 }
201 201  
  202 +
202 203 /***********************************************************************//**
203 204 * @brief ctool and cscript base class Python extensions
204 205 ***************************************************************************/
... ...
pyext/ctools/tools.i
... ... @@ -36,6 +36,12 @@
36 36  
37 37 /* __ Include standard typemaps for vectors and strings __________________ */
38 38 %include stl.i
  39 +%include "std_vector.i"
  40 +namespace std {
  41 + %template(vector_int) vector<int>;
  42 + %template(vector_double) vector<double>;
  43 + %template(vector_bool) vector<bool>;
  44 +};
39 45  
40 46 /* __ Make sure that exceptions are catched ______________________________ */
41 47 %import(module="gammalib.support") "GException.i";
... ...
test/test_cscript.py
... ... @@ -108,6 +108,23 @@ class cscript_test(ctools.cscript):
108 108 obs = gammalib.GObservations()
109 109 return self._get_mean_pointing(obs)
110 110  
  111 + # Check set_obs_response() method
  112 + def check_set_obs_response(self, expcube, psfcube, bkgcube, edispcube, edisp):
  113 + obs = gammalib.GCTAObservation()
  114 + counts = gammalib.GCTAEventCube()
  115 + obs.events(counts)
  116 + self.pars()['expcube'].value(expcube)
  117 + self.pars()['psfcube'].value(psfcube)
  118 + self.pars()['bkgcube'].value(bkgcube)
  119 + self.pars()['edispcube'].value(edispcube)
  120 + self.pars()['edisp'].value(edisp)
  121 + self._set_obs_response(obs)
  122 + return
  123 +
  124 + # Check get_current_rss() method
  125 + def check_get_current_rss(self):
  126 + return self._get_current_rss()
  127 +
111 128  
112 129 # ======================== #
113 130 # ctobservation_test class #
... ... @@ -196,6 +213,13 @@ class Test(test):
196 213 pars.append(gammalib.GApplicationPar('rad','r','h','1.0','','',''))
197 214 pars.append(gammalib.GApplicationPar('emin','r','h','1.0','','',''))
198 215 pars.append(gammalib.GApplicationPar('emax','r','h','10.0','','',''))
  216 + pars.append(gammalib.GApplicationPar('expcube','f','h','NONE','','',''))
  217 + pars.append(gammalib.GApplicationPar('psfcube','f','h','NONE','','',''))
  218 + pars.append(gammalib.GApplicationPar('bkgcube','f','h','NONE','','',''))
  219 + pars.append(gammalib.GApplicationPar('edispcube','f','h','NONE','','',''))
  220 + pars.append(gammalib.GApplicationPar('edisp','b','h','yes','','',''))
  221 + pars.append(gammalib.GApplicationPar('caldb','s','h','prod2','','',''))
  222 + pars.append(gammalib.GApplicationPar('irf','s','h','South_0.5h','','',''))
199 223 pars.append_standard()
200 224 pars.save('cscript_test.par')
201 225  
... ... @@ -333,6 +357,14 @@ class Test(test):
333 357 except ValueError:
334 358 self.test_try_success()
335 359  
  360 + # Test require_inobs_nocube() method for valid file name
  361 + self.test_try('Check require_inobs_nocube() method for event list')
  362 + try:
  363 + empty.check_require_inobs_nocube(self._events)
  364 + self.test_try_success()
  365 + except ValueError:
  366 + self.test_try_failure('Exception thrown')
  367 +
336 368 # Test require_inobs_nocube() method for invalid file name
337 369 self.test_try('Check require_inobs_nocube() method for counts cube')
338 370 try:
... ... @@ -346,15 +378,12 @@ class Test(test):
346 378 self.test_assert(not roi.is_valid(), 'Check get_roi() method')
347 379  
348 380 # Test restore_edisp() method for invalid vector size
349   - #self.test_try('Check restore_edisp() method for invalid vector size')
350   - #try:
351   - # empty.check_restore_edisp()
352   - # self.test_try_failure('Exception not thrown')
353   - #except ValueError:
354   - # self.test_try_success()
355   - #
356   - # NOTE: This code does not work because of a missing list to
357   - # integer vector conversion.
  381 + self.test_try('Check restore_edisp() method for invalid vector size')
  382 + try:
  383 + empty.check_restore_edisp()
  384 + self.test_try_failure('Exception not thrown')
  385 + except ValueError:
  386 + self.test_try_success()
358 387  
359 388 # Test set_obs_bounds() method
360 389 obs = empty.check_set_obs_bounds()
... ... @@ -377,6 +406,28 @@ class Test(test):
377 406 except ValueError:
378 407 self.test_try_success()
379 408  
  409 + # Test set_obs_response() method with 'NONE' files. If energy dispersion
  410 + # is requested then an exception will be thrown.
  411 + empty.check_set_obs_response('NONE', 'NONE', 'NONE', 'NONE', 'no')
  412 + empty.check_set_obs_response('NONE', 'NONE', 'NONE', 'NONE', 'yes')
  413 + self.test_try('Check set_obs_response() method for file names')
  414 + try:
  415 + empty.check_set_obs_response(self._datadir+'/crab_expcube.fits',
  416 + self._datadir+'/crab_psfcube.fits',
  417 + self._datadir+'/crab_bkgcube.fits',
  418 + 'NONE', 'yes')
  419 + self.test_try_failure('Exception not thrown')
  420 + except ValueError:
  421 + self.test_try_success()
  422 + empty.check_set_obs_response(self._datadir+'/crab_expcube.fits',
  423 + self._datadir+'/crab_psfcube.fits',
  424 + self._datadir+'/crab_bkgcube.fits',
  425 + self._datadir+'/crab_edispcube.fits',
  426 + 'yes')
  427 +
  428 + # Test get_current_rss() method
  429 + empty.check_get_current_rss()
  430 +
380 431 # Return
381 432 return
382 433  
... ...
test/test_csiactobs.py
... ... @@ -147,7 +147,7 @@ class Test(test):
147 147 iactobs.save()
148 148  
149 149 # Check empty observation definition XML file
150   - #self._check_obsdef('csiactobs_obs_py0.xml', 0) # See #1841
  150 + self._check_obsdef('csiactobs_obs_py0.xml', 0)
151 151  
152 152 # Check empty model definition XML file
153 153 self._check_moddef('csiactobs_bgd_py0.xml', 0)
... ...