Commit 9830efd99119e0ba7f9a09f1a668926158e2cf0b

Authored by Jürgen Knödlseder
1 parent 8d1e8235

Use new GApplication logger methods and enhance ctool unit tests (#1828)

... ... @@ -3,7 +3,13 @@ Rolf Buehler (rolf.buehler@desy.de)
3 3 Jean-Baptiste Cayrou (cayrou@etud.insa-toulouse.fr)
4 4 Christoph Deil (Christoph.Deil@mpi-hd.mpg.de)
5 5 Florent Forest (florent.forest@isae.fr)
  6 +Lucie Gerard (lgerard@wgs18.zeuthen.desy.de)
  7 +Maria Haupt (maria.haupt@desy.de)
  8 +Nathan Kelley-Hoskins (nathan.kelley-hoskins@desy.de)
6 9 Juergen Knoedlseder (jurgen.knodlseder@irap.omp.eu)
7 10 Chia-Chun Lu (Chia-Chun.Lu@mpi-hd.mpg.de)
  11 +Pierrick Martin (pierrick.martin@irap.omp.eu)
8 12 Michael Mayer (michael.mayer@physik.hu-berlin.de)
9   -
  13 +Stefan Ohm (stefan.ohm81@gmail.com)
  14 +David Sanchez (david.sanchez@lapp.in2p3.fr)
  15 +Hubert Siejkowski (h.siejkowski@cyfronet.pl)
... ...
pyext/ctool.i
... ... @@ -84,29 +84,26 @@ public:
84 84 virtual void execute(void);
85 85  
86 86 // Make methods private in Python by prepending an underscore
87   - %rename(_read_ahead) read_ahead() const;
88   - %rename(_time_reference) time_reference() const;
89   - %rename(_get_observations) get_observations(const bool& get_response = true);
90   - %rename(_setup_observations) setup_observations(GObservations& obs);
91   - %rename(_create_ebounds) create_ebounds();
92   - %rename(_create_map) create_map(const GObservations& obs);
93   - %rename(_create_cube) create_cube(const GObservations& obs);
94   - %rename(_create_cta_obs) create_cta_obs();
95   - %rename(_require_inobs) require_inobs(const std::string& method);
96   - %rename(_require_inobs_nolist) require_inobs_nolist(const std::string& method);
97   - %rename(_require_inobs_nocube) require_inobs_nocube(const std::string& method);
98   - %rename(_log_observations) log_observations(const GChatter& chatter,
99   - const GObservations& obs,
100   - const std::string& what);
101   - %rename(_log_models) log_models(const GChatter& chatter,
102   - const GModels& models,
103   - const std::string& what);
104   - %rename(_set_response) set_response(GObservations& obs);
105   - %rename(_set_obs_response) set_obs_response(GCTAObservation* obs);
106   - %rename(_set_obs_bounds) set_obs_bounds(GObservations& obs);
107   - %rename(_get_mean_pointing) get_mean_pointing(const GObservations& obs);
108   - %rename(_get_current_rss) get_current_rss();
109   - %rename(_get_obs_header) get_obs_header();
  87 + %rename(_read_ahead) read_ahead;
  88 + %rename(_time_reference) time_reference;
  89 + %rename(_get_observations) get_observations;
  90 + %rename(_setup_observations) setup_observations;
  91 + %rename(_create_ebounds) create_ebounds;
  92 + %rename(_create_map) create_map;
  93 + %rename(_create_cube) create_cube;
  94 + %rename(_create_cta_obs) create_cta_obs;
  95 + %rename(_require_inobs) require_inobs;
  96 + %rename(_require_inobs_nolist) require_inobs_nolist;
  97 + %rename(_require_inobs_nocube) require_inobs_nocube;
  98 + %rename(_log_observations) log_observations;
  99 + %rename(_log_models) log_models;
  100 + %rename(_set_response) set_response;
  101 + %rename(_set_obs_response) set_obs_response;
  102 + %rename(_set_obs_bounds) set_obs_bounds;
  103 + %rename(_get_mean_pointing) get_mean_pointing;
  104 + %rename(_get_current_rss) get_current_rss;
  105 + %rename(_get_obs_header) get_obs_header;
  106 + %rename(_warn_too_few_energies) warn_too_few_energies;
110 107  
111 108 // Protected methods
112 109 const bool& read_ahead(void) const;
... ... @@ -140,6 +137,10 @@ public:
140 137 GSkyDir get_mean_pointing(const GObservations& obs);
141 138 size_t get_current_rss(void);
142 139 std::string get_obs_header(const GObservation* obs);
  140 + GEnergies insert_energy_boundaries(const GEnergies& energies,
  141 + const GCTAObservation& obs);
  142 + // Protected warning strings
  143 + std::string warn_too_few_energies(const GEnergies& energies) const;
143 144 };
144 145  
145 146  
... ...
src/ctbin/ctbin.cpp
... ... @@ -345,7 +345,7 @@ void ctbin::publish(const std::string& name)
345 345 user_name = CTBIN_NAME;
346 346 }
347 347  
348   - // Log counts cube name
  348 + // Write counts cube name into logger
349 349 log_value(NORMAL, "Counts cube name", user_name);
350 350  
351 351 // Publish counts cube
... ...
src/ctbkgcube/ctbkgcube.cpp
... ... @@ -218,23 +218,7 @@ void ctbkgcube::run(void)
218 218 get_parameters();
219 219  
220 220 // Warn if there are not enough energy bins
221   - int n = m_background.energies().size();
222   - double logEmin = std::log10(m_background.energies()[0].TeV());
223   - double logEmax = std::log10(m_background.energies()[n-1].TeV());
224   - int nrequired = int((logEmax - logEmin) * 25.0);
225   - if (n < nrequired) {
226   - std::string msg = "\nWARNING: Only "+gammalib::str(n-1)+" energy bins "
227   - "have been requested. This may be too few energy "
228   - "bins.\n At least 25 bins per decade in "
229   - "energy are recommended, which in your case\n"
230   - " would be "+gammalib::str(nrequired-1)+
231   - " bins. Consider increasing the number of energy "
232   - "bins.";
233   - log_string(TERSE, msg);
234   - }
235   -
236   - // Initialise exposure cube
237   - init_cube();
  221 + log_string(TERSE, warn_too_few_energies(m_background.energies()));
238 222  
239 223 // Write input observation container into logger
240 224 log_observations(NORMAL, m_obs, "Input observation");
... ... @@ -242,6 +226,9 @@ void ctbkgcube::run(void)
242 226 // Write input model container into logger
243 227 log_models(NORMAL, m_obs.models(), "Input model");
244 228  
  229 + // Initialise exposure cube
  230 + init_cube();
  231 +
245 232 // Write header
246 233 log_header1(TERSE, "Prepare model");
247 234  
... ... @@ -296,11 +283,11 @@ void ctbkgcube::run(void)
296 283 // Assign background models to container
297 284 m_obs.models(m_bkgmdl);
298 285  
299   - // Set pointer on logger depending on chattiness
300   - GLog* logp = (logNormal()) ? &log : NULL;
  286 + // Set pointer to logger dependent on chattiness
  287 + GLog* logger = (logNormal()) ? &log : NULL;
301 288  
302 289 // Fill background cube from observations
303   - m_background.fill(m_obs, logp);
  290 + m_background.fill(m_obs, logger);
304 291  
305 292 // Create a background model for the output background cube and append
306 293 // that model to the input model in place of the original
... ... @@ -356,28 +343,33 @@ void ctbkgcube::save(void)
356 343 m_outcube = (*this)["outcube"].filename();
357 344 m_outmodel = (*this)["outmodel"].filename();
358 345  
359   - // Save only if filename is non-empty
360   - if (!m_outcube.is_empty()) {
  346 + // Determine whether the cube is empty
  347 + bool cube_is_empty = ((m_background.cube().nx() == 0) ||
  348 + (m_background.cube().ny() == 0) ||
  349 + (m_background.cube().nmaps() == 0));
361 350  
362   - // Log background cube file name
363   - log_value(NORMAL, "Background cube file", m_outcube.url());
  351 + // Determine whether a model definition file should be written
  352 + bool write_moddef = ((!m_outmodel.is_empty()) &&
  353 + (gammalib::tolower(m_outmodel.url()) != "none"));
364 354  
365   - // Save background cube
  355 + // Save background cube if filename and the background cube are not empty
  356 + if (!m_outcube.is_empty() && !cube_is_empty) {
366 357 m_background.save(m_outcube, clobber());
367   -
368 358 }
369 359  
370   - // Write output models if filename is valid
371   - if ((!m_outmodel.is_empty()) &&
372   - (gammalib::tolower(m_outmodel.url()) != "none")) {
373   -
374   - // Log model definition file name
375   - log_value(NORMAL, "Model definition file", m_outmodel.url());
376   -
377   - // Save output model for stacked analyses
  360 + // Save model definition file if requested
  361 + if (write_moddef) {
378 362 m_outmdl.save(m_outmodel.url());
  363 + }
379 364  
  365 + // Write into logger what has been done
  366 + std::string fname = (m_outcube.is_empty()) ? "NONE" : m_outcube.url();
  367 + std::string mname = (write_moddef) ? m_outmodel.url() : "NONE";
  368 + if (cube_is_empty) {
  369 + fname.append(" (cube is empty, no file created)");
380 370 }
  371 + log_value(NORMAL, "Background cube file", fname);
  372 + log_value(NORMAL, "Model definition file", mname);
381 373  
382 374 // Return
383 375 return;
... ... @@ -394,7 +386,7 @@ void ctbkgcube::publish(const std::string&amp; name)
394 386 // Write header
395 387 log_header1(TERSE, "Publish background cube");
396 388  
397   - // Set default name is user name is empty
  389 + // Set default name if user name is empty
398 390 std::string user_name(name);
399 391 if (user_name.empty()) {
400 392 user_name = CTBKGCUBE_NAME;
... ... @@ -588,9 +580,6 @@ void ctbkgcube::init_cube(void)
588 580 // If requested, insert energies at all event list energy boundaries
589 581 if (m_addbounds) {
590 582  
591   - // Set pointer on logger depending on chattiness
592   - GLog* logp = (logNormal()) ? &log : NULL;
593   -
594 583 // Loop over all observations
595 584 for (int i = 0; i < m_obs.size(); ++i) {
596 585  
... ... @@ -609,7 +598,7 @@ void ctbkgcube::init_cube(void)
609 598 }
610 599  
611 600 // Insert energy boundaries
612   - energies = insert_energy_boundaries(energies, *cta, logp);
  601 + energies = insert_energy_boundaries(energies, *cta);
613 602  
614 603 } // endfor: looped over all observations
615 604  
... ... @@ -623,4 +612,4 @@ void ctbkgcube::init_cube(void)
623 612  
624 613 // Return
625 614 return;
626   -};
  615 +}
... ...
src/ctedispcube/ctedispcube.cpp
... ... @@ -227,35 +227,23 @@ void ctedispcube::run(void)
227 227 // Get task parameters
228 228 get_parameters();
229 229  
230   - // Write header
231   - if (logTerse()) {
232   - log << std::endl;
233   - log.header1("Initialise energy dispersion cube");
234   - }
  230 + // Write input observation container into logger
  231 + log_observations(NORMAL, m_obs, "Input observation");
235 232  
236 233 // Initialise energy dispersion cube
237 234 init_cube();
238 235  
239   - // Write observation(s) into logger
240   - if (logTerse()) {
241   - log << std::endl;
242   - log.header1(gammalib::number("Observation",m_obs.size()));
243   - log << m_obs.print(m_chatter) << std::endl;
244   - }
  236 + // Write header into logger
  237 + log_header1(TERSE, "Generate energy dispersion cube");
245 238  
246   - // Write header
247   - if (logTerse()) {
248   - log << std::endl;
249   - log.header1("Generate energy dispersion cube");
250   - }
  239 + // Set pointer to logger dependent on chattiness
  240 + GLog* logger = (logNormal()) ? &log : NULL;
251 241  
252 242 // Fill energy dispersion cube
253   - m_edispcube.fill(m_obs, &log);
  243 + m_edispcube.fill(m_obs, logger);
254 244  
255   - // Log energy dispersion cube
256   - if (logTerse()) {
257   - log << m_edispcube.print(m_chatter) << std::endl;
258   - }
  245 + // Write energy dispersion cube into logger
  246 + log_string(EXPLICIT, m_edispcube.print(m_chatter));
259 247  
260 248 // Return
261 249 return;
... ... @@ -270,28 +258,29 @@ void ctedispcube::run(void)
270 258 ***************************************************************************/
271 259 void ctedispcube::save(void)
272 260 {
273   - // Write header
274   - if (logTerse()) {
275   - log << std::endl;
276   - log.header1("Save Edisp cube");
277   - }
  261 + // Write header into logger
  262 + log_header1(TERSE, "Save energy dispersion cube");
278 263  
279 264 // Get output filename
280 265 m_outcube = (*this)["outcube"].filename();
281 266  
282   - // Save only if filename is non-empty
283   - if (!m_outcube.is_empty()) {
  267 + // Determine whether the cube is empty
  268 + bool cube_is_empty = ((m_edispcube.cube().nx() == 0) ||
  269 + (m_edispcube.cube().ny() == 0) ||
  270 + (m_edispcube.cube().nmaps() == 0));
284 271  
285   - // Log filename
286   - if (logTerse()) {
287   - log << gammalib::parformat("Energy dispersion cube file");
288   - log << m_outcube.url() << std::endl;
289   - }
290   -
291   - // Save energy dispersion cube
  272 + // Save energy dispersion cube if filename and the energy dispersion cube
  273 + // are not empty
  274 + if (!m_outcube.is_empty() && !cube_is_empty) {
292 275 m_edispcube.save(m_outcube, clobber());
  276 + }
293 277  
  278 + // Write into logger what has been done
  279 + std::string fname = (m_outcube.is_empty()) ? "NONE" : m_outcube.url();
  280 + if (cube_is_empty) {
  281 + fname.append(" (cube is empty, no file created)");
294 282 }
  283 + log_value(NORMAL, "Energy dispersion cube file", fname);
295 284  
296 285 // Return
297 286 return;
... ... @@ -430,6 +419,9 @@ void ctedispcube::get_parameters(void)
430 419 ***************************************************************************/
431 420 void ctedispcube::init_cube(void)
432 421 {
  422 + // Write header into logger
  423 + log_header1(TERSE, "Initialise energy dispersion cube");
  424 +
433 425 // Extract energy dispersion definition
434 426 const GWcs* proj = static_cast<const GWcs*>(m_edispcube.cube().projection());
435 427 std::string wcs = m_edispcube.cube().projection()->code();
... ... @@ -449,12 +441,6 @@ void ctedispcube::init_cube(void)
449 441 // If requested, insert energies at all event list energy boundaries
450 442 if (m_addbounds) {
451 443  
452   - // Set logger
453   - GLog* logger = NULL;
454   - if (logTerse()) {
455   - logger = &log;
456   - }
457   -
458 444 // Loop over all observations
459 445 for (int i = 0; i < m_obs.size(); ++i) {
460 446  
... ... @@ -473,7 +459,7 @@ void ctedispcube::init_cube(void)
473 459 }
474 460  
475 461 // Insert energy boundaries
476   - energies = insert_energy_boundaries(energies, *cta, logger);
  462 + energies = insert_energy_boundaries(energies, *cta);
477 463  
478 464 } // endfor: looped over all observations
479 465  
... ... @@ -483,11 +469,9 @@ void ctedispcube::init_cube(void)
483 469 m_edispcube = GCTACubeEdisp(wcs, coords, x, y, dx, dy, nx, ny, energies,
484 470 mmax, nmbins);
485 471  
486   - // Log energy dispersion cube
487   - if (logTerse()) {
488   - log << m_edispcube.print(m_chatter) << std::endl;
489   - }
  472 + // Write energy dispersion cube in logger
  473 + log_string(NORMAL, m_edispcube.print(m_chatter));
490 474  
491 475 // Return
492 476 return;
493   -};
  477 +}
... ...
src/ctexpcube/ctexpcube.cpp
... ... @@ -218,53 +218,25 @@ void ctexpcube::run(void)
218 218 get_parameters();
219 219  
220 220 // Warn if there are not enough energy bins
221   - int n = m_expcube.energies().size();
222   - double logEmin = std::log10(m_expcube.energies()[0].TeV());
223   - double logEmax = std::log10(m_expcube.energies()[n-1].TeV());
224   - int nrequired = int((logEmax - logEmin) * 25.0);
225   - if ((n < nrequired) && logTerse()) {
226   - log << std::endl;
227   - log << "WARNING: ";
228   - log << "Only " << n-1 << " energy bins have been requested. This may be "
229   - "too few energy bins.";
230   - log << std::endl << " ";
231   - log << "At least 25 bins per decade in energy are recommended, which "
232   - "in your case";
233   - log << std::endl << " ";
234   - log << "would be " << nrequired-1 << " bins. Consider increasing the "
235   - "number of energy bins.";
236   - log << std::endl;
237   - }
  221 + log_string(TERSE, warn_too_few_energies(m_expcube.energies()));
238 222  
239   - // Write header
240   - if (logTerse()) {
241   - log << std::endl;
242   - log.header1("Initialise exposure cube");
243   - }
  223 + // Write input observation container into logger
  224 + log_observations(NORMAL, m_obs, "Input observation");
244 225  
245 226 // Initialise exposure cube
246 227 init_cube();
247 228  
248   - // Write observation(s) into logger
249   - if (logTerse()) {
250   - log << std::endl;
251   - log.header1(gammalib::number("Observation",m_obs.size()));
252   - log << m_obs.print(m_chatter) << std::endl;
253   - }
  229 + // Write header into logger
  230 + log_header1(TERSE, "Generate exposure cube");
254 231  
255   - // Write header
256   - if (logTerse()) {
257   - log << std::endl;
258   - log.header1("Generate exposure cube");
259   - }
  232 + // Set pointer to logger dependent on chattiness
  233 + GLog* logger = (logNormal()) ? &log : NULL;
260 234  
261 235 // Fill exposure cube
262   - m_expcube.fill(m_obs, &log);
  236 + m_expcube.fill(m_obs, logger);
263 237  
264   - // Log exposure cube
265   - if (logTerse()) {
266   - log << m_expcube.print(m_chatter) << std::endl;
267   - }
  238 + // Write exposure cube into logger
  239 + log_string(EXPLICIT, m_expcube.print(m_chatter));
268 240  
269 241 // Optionally publish exposure cube
270 242 if (m_publish) {
... ... @@ -283,28 +255,28 @@ void ctexpcube::run(void)
283 255 ***************************************************************************/
284 256 void ctexpcube::save(void)
285 257 {
286   - // Write header
287   - if (logTerse()) {
288   - log << std::endl;
289   - log.header1("Save exposure cube");
290   - }
  258 + // Write header into logger
  259 + log_header1(TERSE, "Save exposure cube");
291 260  
292 261 // Get exposure cube filename
293 262 m_outcube = (*this)["outcube"].filename();
294 263  
295   - // Save only if filename is non-empty
296   - if (!m_outcube.is_empty()) {
297   -
298   - // Log filename
299   - if (logTerse()) {
300   - log << gammalib::parformat("Exposure cube file");
301   - log << m_outcube.url() << std::endl;
302   - }
  264 + // Determine whether the cube is empty
  265 + bool cube_is_empty = ((m_expcube.cube().nx() == 0) ||
  266 + (m_expcube.cube().ny() == 0) ||
  267 + (m_expcube.cube().nmaps() == 0));
303 268  
304   - // Save exposure cube
  269 + // Save exposure cube if filename and the exposure cube are not empty
  270 + if (!m_outcube.is_empty() && !cube_is_empty) {
305 271 m_expcube.save(m_outcube, clobber());
  272 + }
306 273  
  274 + // Write into logger what has been done
  275 + std::string fname = (m_outcube.is_empty()) ? "NONE" : m_outcube.url();
  276 + if (cube_is_empty) {
  277 + fname.append(" (cube is empty, no file created)");
307 278 }
  279 + log_value(NORMAL, "Exposure cube file", fname);
308 280  
309 281 // Return
310 282 return;
... ... @@ -318,23 +290,17 @@ void ctexpcube::save(void)
318 290 ***************************************************************************/
319 291 void ctexpcube::publish(const std::string& name)
320 292 {
321   - // Write header
322   - if (logTerse()) {
323   - log << std::endl;
324   - log.header1("Publish exposure cube");
325   - }
  293 + // Write header into logger
  294 + log_header1(TERSE, "Publish exposure cube");
326 295  
327   - // Set default name is user name is empty
  296 + // Set default name if user name is empty
328 297 std::string user_name(name);
329 298 if (user_name.empty()) {
330 299 user_name = CTEXPCUBE_NAME;
331 300 }
332 301  
333   - // Log filename
334   - if (logTerse()) {
335   - log << gammalib::parformat("Publish exposure cube");
336   - log << user_name << std::endl;
337   - }
  302 + // Write exposure cube name into logger
  303 + log_value(NORMAL, "Counts cube name", user_name);
338 304  
339 305 // Publish exposure cube
340 306 m_expcube.cube().publish(user_name);
... ... @@ -475,6 +441,9 @@ void ctexpcube::get_parameters(void)
475 441 ***************************************************************************/
476 442 void ctexpcube::init_cube(void)
477 443 {
  444 + // Write header into logger
  445 + log_header1(TERSE, "Initialise exposure cube");
  446 +
478 447 // Extract exposure cube definition
479 448 const GWcs* proj = static_cast<const GWcs*>(m_expcube.cube().projection());
480 449 std::string wcs = m_expcube.cube().projection()->code();
... ... @@ -492,12 +461,6 @@ void ctexpcube::init_cube(void)
492 461 // If requested, insert energies at all event list energy boundaries
493 462 if (m_addbounds) {
494 463  
495   - // Set logger
496   - GLog* logger = NULL;
497   - if (logTerse()) {
498   - logger = &log;
499   - }
500   -
501 464 // Loop over all observations
502 465 for (int i = 0; i < m_obs.size(); ++i) {
503 466  
... ... @@ -516,7 +479,7 @@ void ctexpcube::init_cube(void)
516 479 }
517 480  
518 481 // Insert energy boundaries
519   - energies = insert_energy_boundaries(energies, *cta, logger);
  482 + energies = insert_energy_boundaries(energies, *cta);
520 483  
521 484 } // endfor: looped over all observations
522 485  
... ... @@ -525,11 +488,9 @@ void ctexpcube::init_cube(void)
525 488 // Setup exposure cube
526 489 m_expcube = GCTACubeExposure(wcs, coords, x, y, dx, dy, nx, ny, energies);
527 490  
528   - // Log exposure cube
529   - if (logTerse()) {
530   - log << m_expcube.print(m_chatter) << std::endl;
531   - }
  491 + // Write exposure cube in logger
  492 + log_string(NORMAL, m_expcube.print(m_chatter));
532 493  
533 494 // Return
534 495 return;
535   -};
  496 +}
... ...
src/ctobssim/ctobssim.cpp
... ... @@ -324,7 +324,7 @@ void ctobssim::run(void)
324 324 wrklog.name(log.name());
325 325  
326 326 // Set a big value to avoid flushing
327   - wrklog.max_size(10000000);
  327 + wrklog.buffer_size(10000000);
328 328  
329 329 // Loop over all observation in the container. If OpenMP support
330 330 // is enabled, this loop will be parallelized.
... ...
src/ctpsfcube/ctpsfcube.cpp
... ... @@ -216,35 +216,20 @@ void ctpsfcube::run(void)
216 216 // Get task parameters
217 217 get_parameters();
218 218  
219   - // Write header
220   - if (logTerse()) {
221   - log << std::endl;
222   - log.header1("Initialise PSF cube");
223   - }
  219 + // Write input observation container into logger
  220 + log_observations(NORMAL, m_obs, "Input observation");
224 221  
225   - // Initialise exposure cube
  222 + // Initialise PSF cube
226 223 init_cube();
227 224  
228   - // Write observation(s) into logger
229   - if (logTerse()) {
230   - log << std::endl;
231   - log.header1(gammalib::number("Observation",m_obs.size()));
232   - log << m_obs.print(m_chatter) << std::endl;
233   - }
234   -
235   - // Write header
236   - if (logTerse()) {
237   - log << std::endl;
238   - log.header1("Generate PSF cube");
239   - }
  225 + // Write header into logger
  226 + log_header1(TERSE, "Generate PSF cube");
240 227  
241 228 // Fill PSF cube
242 229 m_psfcube.fill(m_obs, &log);
243 230  
244   - // Log PSF cube
245   - if (logTerse()) {
246   - log << m_psfcube.print(m_chatter) << std::endl;
247   - }
  231 + // Write PSF cube into logger
  232 + log_string(EXPLICIT, m_psfcube.print(m_chatter));
248 233  
249 234 // Return
250 235 return;
... ... @@ -253,31 +238,34 @@ void ctpsfcube::run(void)
253 238  
254 239 /***********************************************************************//**
255 240 * @brief Save PSF cube
  241 + *
  242 + * Saves the PSF cube into a FITS file. A file is only created if the
  243 + * "outcube" parameter is not empty and if a PSF cube has been computed.
256 244 ***************************************************************************/
257 245 void ctpsfcube::save(void)
258 246 {
259   - // Write header
260   - if (logTerse()) {
261   - log << std::endl;
262   - log.header1("Save PSF cube");
263   - }
  247 + // Write header into logger
  248 + log_header1(TERSE, "Save PSF cube");
264 249  
265 250 // Get output filename
266 251 m_outcube = (*this)["outcube"].filename();
267 252  
268   - // Save only if filename is non-empty
269   - if (!m_outcube.is_empty()) {
270   -
271   - // Log filename
272   - if (logTerse()) {
273   - log << gammalib::parformat("PSF cube file");
274   - log << m_outcube.url() << std::endl;
275   - }
  253 + // Determine whether the cube is empty
  254 + bool cube_is_empty = ((m_psfcube.cube().nx() == 0) ||
  255 + (m_psfcube.cube().ny() == 0) ||
  256 + (m_psfcube.cube().nmaps() == 0));
276 257  
277   - // Save PSF cube
  258 + // Save PSF cube if filename and the PSF cube are not empty
  259 + if (!m_outcube.is_empty() && !cube_is_empty) {
278 260 m_psfcube.save(m_outcube, clobber());
  261 + }
279 262  
  263 + // Write into logger what has been done
  264 + std::string fname = (m_outcube.is_empty()) ? "NONE" : m_outcube.url();
  265 + if (cube_is_empty) {
  266 + fname.append(" (cube is empty, no file created)");
280 267 }
  268 + log_value(NORMAL, "PSF cube file", fname);
281 269  
282 270 // Return
283 271 return;
... ... @@ -381,7 +369,7 @@ void ctpsfcube::get_parameters(void)
381 369  
382 370 }
383 371  
384   - // ... otherwise setup the exposure cube from the counts map
  372 + // ... otherwise setup the PSF cube from the counts map
385 373 else {
386 374  
387 375 // Load event cube from filename
... ... @@ -416,7 +404,10 @@ void ctpsfcube::get_parameters(void)
416 404 ***************************************************************************/
417 405 void ctpsfcube::init_cube(void)
418 406 {
419   - // Extract exposure cube definition
  407 + // Write header into logger
  408 + log_header1(TERSE, "Initialise PSF cube");
  409 +
  410 + // Extract PSF cube definition
420 411 const GWcs* proj = static_cast<const GWcs*>(m_psfcube.cube().projection());
421 412 std::string wcs = m_psfcube.cube().projection()->code();
422 413 std::string coords = m_psfcube.cube().projection()->coordsys();
... ... @@ -435,12 +426,6 @@ void ctpsfcube::init_cube(void)
435 426 // If requested, insert energies at all event list energy boundaries
436 427 if (m_addbounds) {
437 428  
438   - // Set logger
439   - GLog* logger = NULL;
440   - if (logTerse()) {
441   - logger = &log;
442   - }
443   -
444 429 // Loop over all observations
445 430 for (int i = 0; i < m_obs.size(); ++i) {
446 431  
... ... @@ -459,7 +444,7 @@ void ctpsfcube::init_cube(void)
459 444 }
460 445  
461 446 // Insert energy boundaries
462   - energies = insert_energy_boundaries(energies, *cta, logger);
  447 + energies = insert_energy_boundaries(energies, *cta);
463 448  
464 449 } // endfor: looped over all observations
465 450  
... ... @@ -469,11 +454,9 @@ void ctpsfcube::init_cube(void)
469 454 m_psfcube = GCTACubePsf(wcs, coords, x, y, dx, dy, nx, ny, energies,
470 455 dmax, ndbins);
471 456  
472   - // Log PSF cube
473   - if (logTerse()) {
474   - log << m_psfcube.print(m_chatter) << std::endl;
475   - }
  457 + // Write PSF cube in logger
  458 + log_string(NORMAL, m_psfcube.print(m_chatter));
476 459  
477 460 // Return
478 461 return;
479   -};
  462 +}
... ...
src/support/ctool.cpp
... ... @@ -68,15 +68,14 @@
68 68  
69 69 /***********************************************************************//**
70 70 * @brief Void constructor
  71 + *
  72 + * Constructs an empty ctools.
71 73 ***************************************************************************/
72 74 ctool::ctool(void) : GApplication()
73 75 {
74 76 // Initialise members
75 77 init_members();
76 78  
77   - // Write header into logger
78   - log_header();
79   -
80 79 // Return
81 80 return;
82 81 }
... ... @@ -87,6 +86,16 @@ ctool::ctool(void) : GApplication()
87 86 *
88 87 * @param[in] name Application name.
89 88 * @param[in] version Application version.
  89 + *
  90 + * Constructs a ctool from the @p name and @p version. The constructor uses
  91 + * the equivalent GApplication constructor to set the parameter filename to
  92 + * "<name>.par" and the log filename to "<name>".log. The parameters will be
  93 + * loaded from the parameter file.
  94 + *
  95 + * No log file will be opened. To open the log file an explicit call to the
  96 + * logFileOpen() method is required.
  97 + *
  98 + * This constructor should be used for using a ctool from Python.
90 99 ***************************************************************************/
91 100 ctool::ctool(const std::string& name, const std::string& version) :
92 101 GApplication(name, version)
... ... @@ -94,9 +103,6 @@ ctool::ctool(const std::string&amp; name, const std::string&amp; version) :
94 103 // Initialise members
95 104 init_members();
96 105  
97   - // Write header into logger
98   - log_header();
99   -
100 106 // Return
101 107 return;
102 108 }
... ... @@ -109,6 +115,16 @@ ctool::ctool(const std::string&amp; name, const std::string&amp; version) :
109 115 * @param[in] version Application version.
110 116 * @param[in] argc Number of arguments in command line.
111 117 * @param[in] argv Array of command line arguments.
  118 + *
  119 + * Constructs a ctool from the @p name, @p version and command line
  120 + * arguments. The constructor uses the equivalent GApplication constructor
  121 + * to set the parameter filename to "<name>.par" and the log filename to
  122 + * "<name>".log. The parameters will be loaded from the parameter file.
  123 + * In addition, the constructor opens the log file.
  124 + *
  125 + * If the "--help" option is provided as command line argument a help text
  126 + * about the usage of the ctool will be shown in the console and the ctool
  127 + * will exit. No log file will be opened in that case.
112 128 ***************************************************************************/
113 129 ctool::ctool(const std::string& name, const std::string& version,
114 130 int argc, char *argv[]) :
... ... @@ -123,8 +139,8 @@ ctool::ctool(const std::string&amp; name, const std::string&amp; version,
123 139 // Initialise members
124 140 init_members();
125 141  
126   - // Write header into logger
127   - log_header();
  142 + // Open the log file
  143 + logFileOpen();
128 144  
129 145 // Return
130 146 return;
... ... @@ -135,6 +151,8 @@ ctool::ctool(const std::string&amp; name, const std::string&amp; version,
135 151 * @brief Copy constructor
136 152 *
137 153 * @param[in] app Application.
  154 + *
  155 + * Construct a ctools from another ctool instance.
138 156 ***************************************************************************/
139 157 ctool::ctool(const ctool& app) : GApplication(app)
140 158 {
... ... @@ -172,6 +190,8 @@ ctool::~ctool(void)
172 190 * @brief Assignment operator
173 191 *
174 192 * @param[in] app Application.
  193 + *
  194 + * Assigns one ctool to another.
175 195 ***************************************************************************/
176 196 ctool& ctool::operator=(const ctool& app)
177 197 {
... ... @@ -207,7 +227,8 @@ ctool&amp; ctool::operator=(const ctool&amp; app)
207 227 * @brief Execute application
208 228 *
209 229 * This is the main execution method of a ctool. The method is invoked when
210   - * the executable is called from command line.
  230 + * the executable is called from the command line. It signals that output
  231 + * parameters should be read ahead, runs the tools, and saves the results.
211 232 ***************************************************************************/
212 233 void ctool::execute(void)
213 234 {
... ... @@ -275,9 +296,6 @@ void ctool::copy_members(const ctool&amp; app)
275 296 ***************************************************************************/
276 297 void ctool::free_members(void)
277 298 {
278   - // Write separator into logger
279   - log_string(TERSE, "");
280   -
281 299 // Return
282 300 return;
283 301 }
... ... @@ -1374,10 +1392,11 @@ std::string ctool::get_obs_header(const GObservation* obs)
1374 1392 * @param[in] energies Energies.
1375 1393 * @param[in] obs Observation container.
1376 1394 * @return Energies.
  1395 + *
  1396 + * Inserts the energy boundaries of an observation in a list of @p energies.
1377 1397 ***************************************************************************/
1378 1398 GEnergies ctool::insert_energy_boundaries(const GEnergies& energies,
1379   - const GCTAObservation& obs,
1380   - GLog* log) const
  1399 + const GCTAObservation& obs)
1381 1400 {
1382 1401 // Create copy of input energies
1383 1402 GEnergies engs = energies;
... ... @@ -1428,11 +1447,8 @@ GEnergies ctool::insert_energy_boundaries(const GEnergies&amp; energies,
1428 1447 engs.append(energy);
1429 1448 }
1430 1449  
1431   - // Log energy insertion
1432   - if (log != NULL) {
1433   - *log << gammalib::parformat("Insert energy");
1434   - *log << energy.print() << std::endl;
1435   - }
  1450 + // Log energy insertion. Circumvent const correctness
  1451 + log_value(NORMAL, "Insert energy", energy.print());
1436 1452  
1437 1453 } // endif: energy insertion requested
1438 1454  
... ... @@ -1444,7 +1460,63 @@ GEnergies ctool::insert_energy_boundaries(const GEnergies&amp; energies,
1444 1460  
1445 1461  
1446 1462 /***********************************************************************//**
1447   - * @brief Dumps help text in the console
  1463 + * @brief Set warning string if there are too few energies
  1464 + *
  1465 + * @param[in] energies Energies.
  1466 + * @return Warning string.
  1467 + *
  1468 + * Sets a warning string if there are too few @p energies in the container.
  1469 + * For a binned or stacked analysis to be accurate, at least 25 bins per
  1470 + * decade are required. If the provided number of energies is less than this
  1471 + * number a warning string will be returned. Otherwise an empty string will
  1472 + * be returned.
  1473 + ***************************************************************************/
  1474 +std::string ctool::warn_too_few_energies(const GEnergies& energies) const
  1475 +{
  1476 + // Initialise warning
  1477 + std::string warning;
  1478 +
  1479 + // Retrieve the number of energies
  1480 + int n = energies.size();
  1481 +
  1482 + // Compute the required number of energy bins
  1483 + double logEmin = std::log10(energies[0].TeV());
  1484 + double logEmax = std::log10(energies[n-1].TeV());
  1485 + int nrequired = int((logEmax - logEmin) * 25.0);
  1486 +
  1487 + // Warn if there are not enough energy bins
  1488 + if (n < nrequired) {
  1489 + warning.append("\nWARNING: Only "+gammalib::str(n-1)+" energy bins "
  1490 + "have been requested. This may be too few energy "
  1491 + "bins."
  1492 + "\n At least 25 bins per decade in energy are "
  1493 + "recommended, which for the given"
  1494 + "\n energy range would be "+
  1495 + gammalib::str(nrequired)+" bins. Consider increasing "
  1496 + "the number of energy bins.");
  1497 + }
  1498 +
  1499 + // Return warning
  1500 + return warning;
  1501 +}
  1502 +
  1503 +
  1504 +/*==========================================================================
  1505 + = =
  1506 + = Private methods for SWIG =
  1507 + = =
  1508 + ==========================================================================*/
  1509 +
  1510 +/***********************************************************************//**
  1511 + * @brief Dump help text in the console
  1512 + *
  1513 + * Dumps the help text for the ctool into the console. The help text is
  1514 + * located in the folder
  1515 + *
  1516 + * $CTOOLS//share/help/
  1517 + *
  1518 + * of the ctools installation and the help file has the name "<name>.txt",
  1519 + * where <name> stands for the name of the ctool.
1448 1520 ***************************************************************************/
1449 1521 void ctool::provide_help(void) const
1450 1522 {
... ...
src/support/ctool.hpp
... ... @@ -101,8 +101,10 @@ protected:
101 101 size_t get_current_rss(void);
102 102 std::string get_obs_header(const GObservation* obs);
103 103 GEnergies insert_energy_boundaries(const GEnergies& energies,
104   - const GCTAObservation& obs,
105   - GLog* log = NULL) const;
  104 + const GCTAObservation& obs);
  105 +
  106 + // Protected warning strings
  107 + std::string warn_too_few_energies(const GEnergies& energies) const;
106 108  
107 109 // Protected members
108 110 bool m_read_ahead; //!< Read ahead output parameters
... ...
test/test_ctbin.py
... ... @@ -134,7 +134,6 @@ class Test(test):
134 134 bin.clear()
135 135  
136 136 # Now set ctbin parameters
137   - #bin = ctools.ctbin()
138 137 bin['inobs'] = self._events
139 138 bin['outcube'] = 'ctbin_py1.fits'
140 139 bin['ebinalg'] = 'LOG'
... ... @@ -159,7 +158,7 @@ class Test(test):
159 158 self._check_observation(bin, 5542)
160 159 self._check_cube(bin.cube(), 5542)
161 160  
162   - # Test copy constructor
  161 + # Copy ctbin tool
163 162 cpy_bin = bin.copy()
164 163  
165 164 # Check content of observation container and counts cube
... ...
test/test_ctbkgcube.py
... ... @@ -18,6 +18,7 @@
18 18 # along with this program. If not, see <http://www.gnu.org/licenses/>.
19 19 #
20 20 # ==========================================================================
  21 +import os
21 22 import gammalib
22 23 import ctools
23 24 from testing import test
... ... @@ -85,7 +86,7 @@ class Test(test):
85 86 'Check successful execution from command line')
86 87  
87 88 # Check result file
88   - self._check_result_file('ctbkgcube_cmd1.fits')
  89 + self._check_result_files('ctbkgcube_cmd1')
89 90  
90 91 # Setup ctbkgcube command
91 92 cmd = ctbkgcube+' inobs="event_file_that_does_not_exist.fits"'+ \
... ... @@ -111,8 +112,29 @@ class Test(test):
111 112 """
112 113 Test ctbkgcube from Python
113 114 """
114   - # Set-up ctbkgcube
  115 + # Allocate ctbkgcube
115 116 bkgcube = ctools.ctbkgcube()
  117 +
  118 + # Check that empty ctbkgcube tool holds a background cube that has
  119 + # no energy bins
  120 + self._check_cube(bkgcube.bkgcube(), nenergies=0)
  121 +
  122 + # Check that saving does not nothing
  123 + bkgcube['outcube'] = 'ctbkgcube_py0.fits'
  124 + bkgcube['outmodel'] = 'ctbkgcube_py0.xml'
  125 + bkgcube['logfile'] = 'ctbkgcube_py0.log'
  126 + bkgcube.logFileOpen()
  127 + bkgcube.save()
  128 + self.test_assert(not os.path.isfile('ctbkgcube_py0.fits'),
  129 + 'Check that no background cube has been created')
  130 +
  131 + # Check model container
  132 + self._check_models(gammalib.GModels('ctbkgcube_py0.xml'), nmodels=0)
  133 +
  134 + # Check that clearing does not lead to an exception or segfault
  135 + bkgcube.clear()
  136 +
  137 + # Now set ctbkgcube parameters
116 138 bkgcube['inobs'] = self._events
117 139 bkgcube['inmodel'] = self._model
118 140 bkgcube['incube'] = 'NONE'
... ... @@ -120,7 +142,7 @@ class Test(test):
120 142 bkgcube['irf'] = self._irf
121 143 bkgcube['ebinalg'] = 'LOG'
122 144 bkgcube['emin'] = 0.1
123   - bkgcube['emax'] = 100
  145 + bkgcube['emax'] = 100.0
124 146 bkgcube['enumbins'] = 20
125 147 bkgcube['nxpix'] = 10
126 148 bkgcube['nypix'] = 10
... ... @@ -139,45 +161,43 @@ class Test(test):
139 161 bkgcube.run()
140 162 bkgcube.save()
141 163  
142   - # Check result file
143   - self._check_result_file('ctbkgcube_py1.fits')
144   -
145   - # Set-up ctbkgcube from counts cube
146   - bkgcube = ctools.ctbkgcube()
147   - bkgcube['inobs'] = self._events
148   - bkgcube['inmodel'] = self._model
149   - bkgcube['incube'] = self._cntcube
150   - bkgcube['caldb'] = self._caldb
151   - bkgcube['irf'] = self._irf
152   - bkgcube['outcube'] = 'ctbkgcube_py2.fits'
153   - bkgcube['outmodel'] = 'ctbkgcube_py2.xml'
154   - bkgcube['logfile'] = 'ctbkgcube_py2.log'
155   - bkgcube['chatter'] = 3
156   - bkgcube['publish'] = True
157   - bkgcube['addbounds'] = True
158   -
159   - # Execute ctbkgcube tool
160   - bkgcube.logFileOpen() # Make sure we get a log file
161   - bkgcube.execute()
162   -
163   - # Check result file
164   - self._check_result_file('ctbkgcube_py2.fits')
165   -
166   - # Copy ctbkgcube tool and execute copied tool
167   - cpy_bkgcube = bkgcube
168   - cpy_bkgcube['outcube'] = 'ctbkgcube_py3.fits'
169   - cpy_bkgcube['outmodel'] = 'ctbkgcube_py3.xml'
170   - cpy_bkgcube['logfile'] = 'ctbkgcube_py3.log'
171   - cpy_bkgcube['chatter'] = 4
  164 + # Check result files
  165 + self._check_result_files('ctbkgcube_py1')
  166 +
  167 + # Copy ctbkgcube tool
  168 + cpy_bkgcube = bkgcube.copy()
  169 +
  170 + # Check background cube and model container of ctbkgcube copy
  171 + self._check_cube(cpy_bkgcube.bkgcube())
  172 + self._check_models(cpy_bkgcube.models())
  173 +
  174 + # Execute copy of ctbkgcube tool again, now with a higher chatter
  175 + # level than before. In addition, use counts cube to define the
  176 + # background cube
  177 + #cpy_bkgcube['inobs'] = self._events
  178 + #cpy_bkgcube['inmodel'] = self._model
  179 + cpy_bkgcube['incube'] = self._cntcube
  180 + #cpy_bkgcube['caldb'] = self._caldb
  181 + #cpy_bkgcube['irf'] = self._irf
  182 + cpy_bkgcube['outcube'] = 'ctbkgcube_py2.fits'
  183 + cpy_bkgcube['outmodel'] = 'ctbkgcube_py2.xml'
  184 + cpy_bkgcube['logfile'] = 'ctbkgcube_py2.log'
  185 + cpy_bkgcube['chatter'] = 3
  186 + cpy_bkgcube['publish'] = True
  187 + cpy_bkgcube['addbounds'] = True
  188 + cpy_bkgcube.logFileOpen() # Make sure we get a log file
172 189 cpy_bkgcube.execute()
173 190  
174   - # Check result file
175   - self._check_result_file('ctbkgcube_py3.fits')
  191 + # Check result files
  192 + self._check_result_files('ctbkgcube_py2')
176 193  
177   - # Clear ctbkgcube tool
178   - bkgcube.clear()
  194 + # Now clear copy of ctbkgcube tool
  195 + cpy_bkgcube.clear()
179 196  
180   - # TODO: Do some test after clearing of ctbkgcube tool
  197 + # Check that the cleared copy has also cleared the background cube
  198 + # and model container
  199 + self._check_cube(cpy_bkgcube.bkgcube(), nenergies=0)
  200 + self._check_models(cpy_bkgcube.models(), nmodels=0)
181 201  
182 202 # Prepare observation container
183 203 obs1 = gammalib.GCTAObservation(self._events)
... ... @@ -195,47 +215,94 @@ class Test(test):
195 215  
196 216 # Set-up ctbkgcube from observation container
197 217 bkgcube = ctools.ctbkgcube(obs)
198   - #bkgcube['inobs'] = self._events
199   - #bkgcube['inmodel'] = self._model
200   - bkgcube['incube'] = 'NONE'
201   - bkgcube['caldb'] = self._caldb
202   - bkgcube['irf'] = self._irf
203   - bkgcube['ebinalg'] = 'LOG'
204   - bkgcube['emin'] = 0.1
205   - bkgcube['emax'] = 100
206   - bkgcube['enumbins'] = 20
207   - bkgcube['nxpix'] = 10
208   - bkgcube['nypix'] = 10
209   - bkgcube['binsz'] = 0.4
210   - bkgcube['coordsys'] = 'CEL'
211   - bkgcube['proj'] = 'CAR'
212   - bkgcube['xref'] = 83.63
213   - bkgcube['yref'] = 22.01
214   - bkgcube['outcube'] = 'ctbkgcube_py4.fits'
215   - bkgcube['outmodel'] = 'ctbkgcube_py4.xml'
216   - bkgcube['logfile'] = 'ctbkgcube_py4.log'
217   - bkgcube['chatter'] = 4
  218 + bkgcube['incube'] = 'NONE'
  219 + bkgcube['caldb'] = self._caldb
  220 + bkgcube['irf'] = self._irf
  221 + bkgcube['ebinalg'] = 'LOG'
  222 + bkgcube['emin'] = 0.2
  223 + bkgcube['emax'] = 150.0
  224 + bkgcube['enumbins'] = 20
  225 + bkgcube['nxpix'] = 10
  226 + bkgcube['nypix'] = 10
  227 + bkgcube['binsz'] = 0.4
  228 + bkgcube['coordsys'] = 'CEL'
  229 + bkgcube['proj'] = 'CAR'
  230 + bkgcube['xref'] = 83.63
  231 + bkgcube['yref'] = 22.01
  232 + bkgcube['outcube'] = 'ctbkgcube_py3.fits'
  233 + bkgcube['outmodel'] = 'ctbkgcube_py3.xml'
  234 + bkgcube['logfile'] = 'ctbkgcube_py3.log'
  235 + bkgcube['addbounds'] = True
  236 + bkgcube['chatter'] = 4
218 237  
219 238 # Execute ctbkgcube tool
220 239 bkgcube.logFileOpen() # Make sure we get a log file
221 240 bkgcube.execute()
222 241  
223   - # Check result file
224   - self._check_result_file('ctbkgcube_py4.fits')
  242 + # Check result files
  243 + self._check_result_files('ctbkgcube_py3', nenergies=23)
225 244  
226 245 # Return
227 246 return
228 247  
229   - # Check result file
230   - def _check_result_file(self, filename):
  248 + # Check result files
  249 + def _check_result_files(self, filename, nenergies=21, nmodels=2):
231 250 """
232 251 Check result file
  252 +
  253 + Parameters
  254 + ----------
  255 + filename : str
  256 + Test file name without extension
  257 + nenergies : int, optional
  258 + Number of energy bins
233 259 """
234   - # Open result file
235   - result = gammalib.GCTACubeBackground(filename)
  260 + # Open background cube
  261 + cube = gammalib.GCTACubeBackground(filename+'.fits')
  262 +
  263 + # Open model definition file
  264 + models = gammalib.GModels(filename+'.xml')
236 265  
  266 + # Check cube
  267 + self._check_cube(cube, nenergies=nenergies)
  268 +
  269 + # Check models
  270 + self._check_models(models, nmodels=nmodels)
  271 +
  272 + # Return
  273 + return
  274 +
  275 + # Check background cube
  276 + def _check_cube(self, cube, nenergies=21):
  277 + """
  278 + Check background cube
  279 +
  280 + Parameters
  281 + ----------
  282 + cube : `~gammalib.GCTACubeBackground`
  283 + Background cube
  284 + nenergies : int, optional
  285 + Number of energy bins
  286 + """
237 287 # Check dimensions
238   - self.test_value(len(result.energies()), 21, 'Check for 21 energy values')
  288 + self.test_value(len(cube.energies()), nenergies,
  289 + 'Check number of energy maps')
239 290  
240 291 # Return
241 292 return
  293 +
  294 + # Check model container
  295 + def _check_models(self, models, nmodels=2):
  296 + """
  297 + Check model container
  298 +
  299 + Parameters
  300 + ----------
  301 + models : `~gammalib.GModels`
  302 + Model container
  303 + """
  304 + # Check number of models
  305 + self.test_value(models.size(), nmodels, 'Check number of models')
  306 +
  307 + # Return
  308 + return
... ...
test/test_ctedispcube.py
... ... @@ -18,6 +18,7 @@
18 18 # along with this program. If not, see <http://www.gnu.org/licenses/>.
19 19 #
20 20 # ==========================================================================
  21 +import os
21 22 import gammalib
22 23 import ctools
23 24 from testing import test
... ... @@ -109,16 +110,32 @@ class Test(test):
109 110 """
110 111 Test ctedispcube from Python
111 112 """
112   - # Set-up ctedispcube
  113 + # Allocate ctedispcube
113 114 edispcube = ctools.ctedispcube()
  115 +
  116 + # Check that empty ctedispcube tool holds an energy dispersion
  117 + # cube that has no energy and migration bins
  118 + self._check_cube(edispcube.edispcube(), nenergies=0, nmigras=0)
  119 +
  120 + # Check that saving does not nothing
  121 + edispcube['outcube'] = 'ctedispcube_py0.fits'
  122 + edispcube['logfile'] = 'ctedispcube_py0.log'
  123 + edispcube.logFileOpen()
  124 + edispcube.save()
  125 + self.test_assert(not os.path.isfile('ctedispcube_py0.fits'),
  126 + 'Check that no energy dispersion cube has been created')
  127 +
  128 + # Check that clearing does not lead to an exception or segfault
  129 + edispcube.clear()
  130 +
  131 + # Now set ctedispcube parameters
114 132 edispcube['inobs'] = self._events
115 133 edispcube['incube'] = 'NONE'
116   - edispcube['outcube'] = 'ctedispcube_py1.fits'
117 134 edispcube['caldb'] = self._caldb
118 135 edispcube['irf'] = self._irf
119 136 edispcube['ebinalg'] = 'LOG'
120 137 edispcube['emin'] = 0.1
121   - edispcube['emax'] = 100
  138 + edispcube['emax'] = 100.0
122 139 edispcube['enumbins'] = 20
123 140 edispcube['nxpix'] = 10
124 141 edispcube['nypix'] = 10
... ... @@ -129,31 +146,88 @@ class Test(test):
129 146 edispcube['yref'] = 22.01
130 147 edispcube['migramax'] = 2.0
131 148 edispcube['migrabins'] = 10
  149 + edispcube['outcube'] = 'ctedispcube_py1.fits'
132 150 edispcube['logfile'] = 'ctedispcube_py1.log'
133 151 edispcube['chatter'] = 2
134 152  
135 153 # Run ctedispcube tool
136   - edispcube.logFileOpen() # Make sure we get a log file
  154 + edispcube.logFileOpen() # Make sure we get a log file
137 155 edispcube.run()
138 156 edispcube.save()
139 157  
140 158 # Check result file
141 159 self._check_result_file('ctedispcube_py1.fits')
142 160  
  161 + # Copy ctedispcube tool
  162 + cpy_edispcube = edispcube.copy()
  163 +
  164 + # Check energy dispersion cube of ctedispcube copy
  165 + self._check_cube(cpy_edispcube.edispcube())
  166 +
  167 + # Execute copy of ctedispcube tool again, now with a higher chatter
  168 + # level than before
  169 + cpy_edispcube['emin'] = 0.2
  170 + cpy_edispcube['emax'] = 150.0
  171 + cpy_edispcube['outcube'] = 'ctedispcube_py2.fits'
  172 + cpy_edispcube['logfile'] = 'ctedispcube_py2.log'
  173 + cpy_edispcube['addbounds'] = True
  174 + cpy_edispcube['chatter'] = 3
  175 + cpy_edispcube.logFileOpen() # Needed to get a new log file
  176 + cpy_edispcube.execute()
  177 +
  178 + # Check result file
  179 + self._check_result_file('ctedispcube_py2.fits', nenergies=23)
  180 +
  181 + # Now clear copy of ctedispcube tool
  182 + cpy_edispcube.clear()
  183 +
  184 + # Check that the cleared copy has also cleared the energy dispersion
  185 + # cube
  186 + self._check_cube(cpy_edispcube.edispcube(), nenergies=0, nmigras=0)
  187 +
143 188 # Return
144 189 return
145 190  
146 191 # Check result file
147   - def _check_result_file(self, filename):
  192 + def _check_result_file(self, filename, nenergies=21):
148 193 """
149 194 Check result file
  195 +
  196 + Parameters
  197 + ----------
  198 + filename : str
  199 + Energy dispersion cube file name
  200 + nenergies : int, optional
  201 + Number of energy bins
150 202 """
151   - # Open result file
152   - result = gammalib.GCTACubeEdisp(filename)
  203 + # Open energy dispersion cube
  204 + cube = gammalib.GCTACubeEdisp(filename)
  205 +
  206 + # Check cube
  207 + self._check_cube(cube, nenergies=nenergies)
153 208  
  209 + # Return
  210 + return
  211 +
  212 + # Check energy dispersion cube
  213 + def _check_cube(self, cube, nenergies=21, nmigras=10):
  214 + """
  215 + Check energy dispersion cube
  216 +
  217 + Parameters
  218 + ----------
  219 + cube : `~gammalib.GCTACubeEdisp`
  220 + Energy dispersion cube
  221 + nenergies : int, optional
  222 + Number of energy bins
  223 + nmigras : int, optional
  224 + Number of migration bins
  225 + """
154 226 # Check dimensions
155   - self.test_value(len(result.energies()), 21, 'Check for 21 energy maps')
156   - self.test_value(len(result.migras()), 10, 'Check for 10 migration bins')
  227 + self.test_value(len(cube.energies()), nenergies,
  228 + 'Check number of energy maps')
  229 + self.test_value(len(cube.migras()), nmigras,
  230 + 'Check number of migration bins')
157 231  
158 232 # Return
159 233 return
... ...
test/test_ctexpcube.py
... ... @@ -18,6 +18,7 @@
18 18 # along with this program. If not, see <http://www.gnu.org/licenses/>.
19 19 #
20 20 # ==========================================================================
  21 +import os
21 22 import gammalib
22 23 import ctools
23 24 from testing import test
... ... @@ -107,16 +108,32 @@ class Test(test):
107 108 """
108 109 Test ctexpcube from Python
109 110 """
110   - # Set-up ctexpcube
  111 + # Allocate ctexpcube
111 112 expcube = ctools.ctexpcube()
  113 +
  114 + # Check that empty ctexpcube tool holds an exposure cube that has
  115 + # no energy bins
  116 + self._check_cube(expcube.expcube(), nenergies=0)
  117 +
  118 + # Check that saving does not nothing
  119 + expcube['outcube'] = 'ctexpcube_py0.fits'
  120 + expcube['logfile'] = 'ctexpcube_py0.log'
  121 + expcube.logFileOpen()
  122 + expcube.save()
  123 + self.test_assert(not os.path.isfile('ctexpcube_py0.fits'),
  124 + 'Check that no exposure cube has been created')
  125 +
  126 + # Check that clearing does not lead to an exception or segfault
  127 + expcube.clear()
  128 +
  129 + # Now set ctexpcube parameters
112 130 expcube['inobs'] = self._events
113 131 expcube['incube'] = 'NONE'
114   - expcube['outcube'] = 'ctexpcube_py1.fits'
115 132 expcube['caldb'] = self._caldb
116 133 expcube['irf'] = self._irf
117 134 expcube['ebinalg'] = 'LOG'
118 135 expcube['emin'] = 0.1
119   - expcube['emax'] = 100
  136 + expcube['emax'] = 100.0
120 137 expcube['enumbins'] = 20
121 138 expcube['nxpix'] = 200
122 139 expcube['nypix'] = 200
... ... @@ -126,6 +143,7 @@ class Test(test):
126 143 expcube['xref'] = 83.63
127 144 expcube['yref'] = 22.01
128 145 expcube['logfile'] = 'ctexpcube_py1.log'
  146 + expcube['outcube'] = 'ctexpcube_py1.fits'
129 147 expcube['chatter'] = 2
130 148  
131 149 # Run ctexpcube tool
... ... @@ -136,19 +154,72 @@ class Test(test):
136 154 # Check result file
137 155 self._check_result_file('ctexpcube_py1.fits')
138 156  
  157 + # Copy ctexpcube tool
  158 + cpy_expcube = expcube.copy()
  159 +
  160 + # Check exposure cube of ctexpcube copy
  161 + self._check_cube(cpy_expcube.expcube())
  162 +
  163 + # Execute copy of ctexpcube tool again, now with a higher chatter
  164 + # level than before
  165 + cpy_expcube['emin'] = 0.2
  166 + cpy_expcube['emax'] = 150.0
  167 + cpy_expcube['outcube'] = 'ctexpcube_py2.fits'
  168 + cpy_expcube['logfile'] = 'ctexpcube_py2.log'
  169 + cpy_expcube['publish'] = True
  170 + cpy_expcube['addbounds'] = True
  171 + cpy_expcube['chatter'] = 3
  172 + cpy_expcube.logFileOpen() # Needed to get a new log file
  173 + cpy_expcube.execute()
  174 +
  175 + # Check result file
  176 + self._check_result_file('ctexpcube_py2.fits', nenergies=23)
  177 +
  178 + # Now clear copy of ctexpcube tool
  179 + cpy_expcube.clear()
  180 +
  181 + # Check that the cleared copy has also cleared the exposure cube
  182 + self._check_cube(cpy_expcube.expcube(), nenergies=0)
  183 +
139 184 # Return
140 185 return
141 186  
142 187 # Check result file
143   - def _check_result_file(self, filename):
  188 + def _check_result_file(self, filename, nenergies=21):
144 189 """
145 190 Check result file
  191 +
  192 + Parameters
  193 + ----------
  194 + filename : str
  195 + Exposure cube file name
  196 + nenergies : int, optional
  197 + Number of energy bins
146 198 """
147   - # Open result file
148   - result = gammalib.GCTACubeExposure(filename)
  199 + # Open exposure cube
  200 + cube = gammalib.GCTACubeExposure(filename)
  201 +
  202 + # Check cube
  203 + self._check_cube(cube, nenergies=nenergies)
149 204  
  205 + # Return
  206 + return
  207 +
  208 + # Check exposure cube
  209 + def _check_cube(self, cube, nenergies=21):
  210 + """
  211 + Check exposure cube
  212 +
  213 + Parameters
  214 + ----------
  215 + cube : `~gammalib.GCTACubeExposure`
  216 + Exposure cube
  217 + nenergies : int, optional
  218 + Number of energy bins
  219 + """
150 220 # Check dimensions
151   - self.test_value(len(result.energies()), 21, 'Check for 21 energy values')
  221 + self.test_value(len(cube.energies()), nenergies,
  222 + 'Check number of energy maps')
152 223  
153 224 # Return
154 225 return
... ...
test/test_ctpsfcube.py
... ... @@ -18,6 +18,7 @@
18 18 # along with this program. If not, see <http://www.gnu.org/licenses/>.
19 19 #
20 20 # ==========================================================================
  21 +import os
21 22 import gammalib
22 23 import ctools
23 24 from testing import test
... ... @@ -109,8 +110,25 @@ class Test(test):
109 110 """
110 111 Test ctpsfcube from Python
111 112 """
112   - # Set-up ctpsfcube
  113 + # Allocate ctpsfcube
113 114 psfcube = ctools.ctpsfcube()
  115 +
  116 + # Check that empty ctpsfcube tool holds a PSF cube that has no energy
  117 + # bins
  118 + self._check_cube(psfcube.psfcube(), nenergies=0)
  119 +
  120 + # Check that saving does not nothing
  121 + psfcube['outcube'] = 'ctpsfcube_py0.fits'
  122 + psfcube['logfile'] = 'ctpsfcube_py0.log'
  123 + psfcube.logFileOpen()
  124 + psfcube.save()
  125 + self.test_assert(not os.path.isfile('ctpsfcube_py0.fits'),
  126 + 'Check that no PSF cube has been created')
  127 +
  128 + # Check that clearing does not lead to an exception or segfault
  129 + psfcube.clear()
  130 +
  131 + # Now set ctpsfcube parameters
114 132 psfcube['inobs'] = self._events
115 133 psfcube['incube'] = 'NONE'
116 134 psfcube['outcube'] = 'ctpsfcube_py1.fits'
... ... @@ -140,19 +158,71 @@ class Test(test):
140 158 # Check result file
141 159 self._check_result_file('ctpsfcube_py1.fits')
142 160  
  161 + # Copy ctpsfcube tool
  162 + cpy_psfcube = psfcube.copy()
  163 +
  164 + # Check PSF cube of ctpsfcube copy
  165 + self._check_cube(cpy_psfcube.psfcube())
  166 +
  167 + # Execute copy of ctpsfcube tool again, now with a higher chatter
  168 + # level than before
  169 + cpy_psfcube['emin'] = 0.2
  170 + cpy_psfcube['emax'] = 150.0
  171 + cpy_psfcube['outcube'] = 'ctpsfcube_py2.fits'
  172 + cpy_psfcube['logfile'] = 'ctpsfcube_py2.log'
  173 + cpy_psfcube['addbounds'] = True
  174 + cpy_psfcube['chatter'] = 3
  175 + cpy_psfcube.logFileOpen() # Needed to get a new log file
  176 + cpy_psfcube.execute()
  177 +
  178 + # Check result file
  179 + self._check_result_file('ctpsfcube_py2.fits', nenergies=23)
  180 +
  181 + # Now clear copy of ctpsfcube tool
  182 + cpy_psfcube.clear()
  183 +
  184 + # Check that the cleared copy has also cleared the PSF cube
  185 + self._check_cube(cpy_psfcube.psfcube(), nenergies=0)
  186 +
143 187 # Return
144 188 return
145 189  
146 190 # Check result file
147   - def _check_result_file(self, filename):
  191 + def _check_result_file(self, filename, nenergies=21):
148 192 """
149 193 Check result file
  194 +
  195 + Parameters
  196 + ----------
  197 + filename : str
  198 + PSF cube file name
  199 + nenergies : int, optional
  200 + Number of energy bins
150 201 """
151   - # Open result file
152   - result = gammalib.GCTACubePsf(filename)
  202 + # Open PSF cube
  203 + cube = gammalib.GCTACubePsf(filename)
153 204  
  205 + # Check cube
  206 + self._check_cube(cube, nenergies=nenergies)
  207 +
  208 + # Return
  209 + return
  210 +
  211 + # Check PSF cube
  212 + def _check_cube(self, cube, nenergies=21):
  213 + """
  214 + Check PSF cube
  215 +
  216 + Parameters
  217 + ----------
  218 + cube : `~gammalib.GCTACubePsf`
  219 + PSF cube
  220 + nenergies : int, optional
  221 + Number of energy bins
  222 + """
154 223 # Check dimensions
155   - self.test_value(len(result.energies()), 21, 'Check for 21 energy values')
  224 + self.test_value(len(cube.energies()), nenergies,
  225 + 'Check number of energy maps')
156 226  
157 227 # Return
158 228 return
... ...