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,7 +3,13 @@ Rolf Buehler (rolf.buehler@desy.de)
3 Jean-Baptiste Cayrou (cayrou@etud.insa-toulouse.fr) 3 Jean-Baptiste Cayrou (cayrou@etud.insa-toulouse.fr)
4 Christoph Deil (Christoph.Deil@mpi-hd.mpg.de) 4 Christoph Deil (Christoph.Deil@mpi-hd.mpg.de)
5 Florent Forest (florent.forest@isae.fr) 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 Juergen Knoedlseder (jurgen.knodlseder@irap.omp.eu) 9 Juergen Knoedlseder (jurgen.knodlseder@irap.omp.eu)
7 Chia-Chun Lu (Chia-Chun.Lu@mpi-hd.mpg.de) 10 Chia-Chun Lu (Chia-Chun.Lu@mpi-hd.mpg.de)
  11 +Pierrick Martin (pierrick.martin@irap.omp.eu)
8 Michael Mayer (michael.mayer@physik.hu-berlin.de) 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,29 +84,26 @@ public:
84 virtual void execute(void); 84 virtual void execute(void);
85 85
86 // Make methods private in Python by prepending an underscore 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 // Protected methods 108 // Protected methods
112 const bool& read_ahead(void) const; 109 const bool& read_ahead(void) const;
@@ -140,6 +137,10 @@ public: @@ -140,6 +137,10 @@ public:
140 GSkyDir get_mean_pointing(const GObservations& obs); 137 GSkyDir get_mean_pointing(const GObservations& obs);
141 size_t get_current_rss(void); 138 size_t get_current_rss(void);
142 std::string get_obs_header(const GObservation* obs); 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,7 +345,7 @@ void ctbin::publish(const std::string& name)
345 user_name = CTBIN_NAME; 345 user_name = CTBIN_NAME;
346 } 346 }
347 347
348 - // Log counts cube name 348 + // Write counts cube name into logger
349 log_value(NORMAL, "Counts cube name", user_name); 349 log_value(NORMAL, "Counts cube name", user_name);
350 350
351 // Publish counts cube 351 // Publish counts cube
src/ctbkgcube/ctbkgcube.cpp
@@ -218,23 +218,7 @@ void ctbkgcube::run(void) @@ -218,23 +218,7 @@ void ctbkgcube::run(void)
218 get_parameters(); 218 get_parameters();
219 219
220 // Warn if there are not enough energy bins 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 // Write input observation container into logger 223 // Write input observation container into logger
240 log_observations(NORMAL, m_obs, "Input observation"); 224 log_observations(NORMAL, m_obs, "Input observation");
@@ -242,6 +226,9 @@ void ctbkgcube::run(void) @@ -242,6 +226,9 @@ void ctbkgcube::run(void)
242 // Write input model container into logger 226 // Write input model container into logger
243 log_models(NORMAL, m_obs.models(), "Input model"); 227 log_models(NORMAL, m_obs.models(), "Input model");
244 228
  229 + // Initialise exposure cube
  230 + init_cube();
  231 +
245 // Write header 232 // Write header
246 log_header1(TERSE, "Prepare model"); 233 log_header1(TERSE, "Prepare model");
247 234
@@ -296,11 +283,11 @@ void ctbkgcube::run(void) @@ -296,11 +283,11 @@ void ctbkgcube::run(void)
296 // Assign background models to container 283 // Assign background models to container
297 m_obs.models(m_bkgmdl); 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 // Fill background cube from observations 289 // Fill background cube from observations
303 - m_background.fill(m_obs, logp); 290 + m_background.fill(m_obs, logger);
304 291
305 // Create a background model for the output background cube and append 292 // Create a background model for the output background cube and append
306 // that model to the input model in place of the original 293 // that model to the input model in place of the original
@@ -356,28 +343,33 @@ void ctbkgcube::save(void) @@ -356,28 +343,33 @@ void ctbkgcube::save(void)
356 m_outcube = (*this)["outcube"].filename(); 343 m_outcube = (*this)["outcube"].filename();
357 m_outmodel = (*this)["outmodel"].filename(); 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 m_background.save(m_outcube, clobber()); 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 m_outmdl.save(m_outmodel.url()); 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 // Return 374 // Return
383 return; 375 return;
@@ -394,7 +386,7 @@ void ctbkgcube::publish(const std::string&amp; name) @@ -394,7 +386,7 @@ void ctbkgcube::publish(const std::string&amp; name)
394 // Write header 386 // Write header
395 log_header1(TERSE, "Publish background cube"); 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 std::string user_name(name); 390 std::string user_name(name);
399 if (user_name.empty()) { 391 if (user_name.empty()) {
400 user_name = CTBKGCUBE_NAME; 392 user_name = CTBKGCUBE_NAME;
@@ -588,9 +580,6 @@ void ctbkgcube::init_cube(void) @@ -588,9 +580,6 @@ void ctbkgcube::init_cube(void)
588 // If requested, insert energies at all event list energy boundaries 580 // If requested, insert energies at all event list energy boundaries
589 if (m_addbounds) { 581 if (m_addbounds) {
590 582
591 - // Set pointer on logger depending on chattiness  
592 - GLog* logp = (logNormal()) ? &log : NULL;  
593 -  
594 // Loop over all observations 583 // Loop over all observations
595 for (int i = 0; i < m_obs.size(); ++i) { 584 for (int i = 0; i < m_obs.size(); ++i) {
596 585
@@ -609,7 +598,7 @@ void ctbkgcube::init_cube(void) @@ -609,7 +598,7 @@ void ctbkgcube::init_cube(void)
609 } 598 }
610 599
611 // Insert energy boundaries 600 // Insert energy boundaries
612 - energies = insert_energy_boundaries(energies, *cta, logp); 601 + energies = insert_energy_boundaries(energies, *cta);
613 602
614 } // endfor: looped over all observations 603 } // endfor: looped over all observations
615 604
@@ -623,4 +612,4 @@ void ctbkgcube::init_cube(void) @@ -623,4 +612,4 @@ void ctbkgcube::init_cube(void)
623 612
624 // Return 613 // Return
625 return; 614 return;
626 -}; 615 +}
src/ctedispcube/ctedispcube.cpp
@@ -227,35 +227,23 @@ void ctedispcube::run(void) @@ -227,35 +227,23 @@ void ctedispcube::run(void)
227 // Get task parameters 227 // Get task parameters
228 get_parameters(); 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 // Initialise energy dispersion cube 233 // Initialise energy dispersion cube
237 init_cube(); 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 // Fill energy dispersion cube 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 // Return 248 // Return
261 return; 249 return;
@@ -270,28 +258,29 @@ void ctedispcube::run(void) @@ -270,28 +258,29 @@ void ctedispcube::run(void)
270 ***************************************************************************/ 258 ***************************************************************************/
271 void ctedispcube::save(void) 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 // Get output filename 264 // Get output filename
280 m_outcube = (*this)["outcube"].filename(); 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 m_edispcube.save(m_outcube, clobber()); 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 // Return 285 // Return
297 return; 286 return;
@@ -430,6 +419,9 @@ void ctedispcube::get_parameters(void) @@ -430,6 +419,9 @@ void ctedispcube::get_parameters(void)
430 ***************************************************************************/ 419 ***************************************************************************/
431 void ctedispcube::init_cube(void) 420 void ctedispcube::init_cube(void)
432 { 421 {
  422 + // Write header into logger
  423 + log_header1(TERSE, "Initialise energy dispersion cube");
  424 +
433 // Extract energy dispersion definition 425 // Extract energy dispersion definition
434 const GWcs* proj = static_cast<const GWcs*>(m_edispcube.cube().projection()); 426 const GWcs* proj = static_cast<const GWcs*>(m_edispcube.cube().projection());
435 std::string wcs = m_edispcube.cube().projection()->code(); 427 std::string wcs = m_edispcube.cube().projection()->code();
@@ -449,12 +441,6 @@ void ctedispcube::init_cube(void) @@ -449,12 +441,6 @@ void ctedispcube::init_cube(void)
449 // If requested, insert energies at all event list energy boundaries 441 // If requested, insert energies at all event list energy boundaries
450 if (m_addbounds) { 442 if (m_addbounds) {
451 443
452 - // Set logger  
453 - GLog* logger = NULL;  
454 - if (logTerse()) {  
455 - logger = &log;  
456 - }  
457 -  
458 // Loop over all observations 444 // Loop over all observations
459 for (int i = 0; i < m_obs.size(); ++i) { 445 for (int i = 0; i < m_obs.size(); ++i) {
460 446
@@ -473,7 +459,7 @@ void ctedispcube::init_cube(void) @@ -473,7 +459,7 @@ void ctedispcube::init_cube(void)
473 } 459 }
474 460
475 // Insert energy boundaries 461 // Insert energy boundaries
476 - energies = insert_energy_boundaries(energies, *cta, logger); 462 + energies = insert_energy_boundaries(energies, *cta);
477 463
478 } // endfor: looped over all observations 464 } // endfor: looped over all observations
479 465
@@ -483,11 +469,9 @@ void ctedispcube::init_cube(void) @@ -483,11 +469,9 @@ void ctedispcube::init_cube(void)
483 m_edispcube = GCTACubeEdisp(wcs, coords, x, y, dx, dy, nx, ny, energies, 469 m_edispcube = GCTACubeEdisp(wcs, coords, x, y, dx, dy, nx, ny, energies,
484 mmax, nmbins); 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 // Return 475 // Return
492 return; 476 return;
493 -}; 477 +}
src/ctexpcube/ctexpcube.cpp
@@ -218,53 +218,25 @@ void ctexpcube::run(void) @@ -218,53 +218,25 @@ void ctexpcube::run(void)
218 get_parameters(); 218 get_parameters();
219 219
220 // Warn if there are not enough energy bins 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 // Initialise exposure cube 226 // Initialise exposure cube
246 init_cube(); 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 // Fill exposure cube 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 // Optionally publish exposure cube 241 // Optionally publish exposure cube
270 if (m_publish) { 242 if (m_publish) {
@@ -283,28 +255,28 @@ void ctexpcube::run(void) @@ -283,28 +255,28 @@ void ctexpcube::run(void)
283 ***************************************************************************/ 255 ***************************************************************************/
284 void ctexpcube::save(void) 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 // Get exposure cube filename 261 // Get exposure cube filename
293 m_outcube = (*this)["outcube"].filename(); 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 m_expcube.save(m_outcube, clobber()); 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 // Return 281 // Return
310 return; 282 return;
@@ -318,23 +290,17 @@ void ctexpcube::save(void) @@ -318,23 +290,17 @@ void ctexpcube::save(void)
318 ***************************************************************************/ 290 ***************************************************************************/
319 void ctexpcube::publish(const std::string& name) 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 std::string user_name(name); 297 std::string user_name(name);
329 if (user_name.empty()) { 298 if (user_name.empty()) {
330 user_name = CTEXPCUBE_NAME; 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 // Publish exposure cube 305 // Publish exposure cube
340 m_expcube.cube().publish(user_name); 306 m_expcube.cube().publish(user_name);
@@ -475,6 +441,9 @@ void ctexpcube::get_parameters(void) @@ -475,6 +441,9 @@ void ctexpcube::get_parameters(void)
475 ***************************************************************************/ 441 ***************************************************************************/
476 void ctexpcube::init_cube(void) 442 void ctexpcube::init_cube(void)
477 { 443 {
  444 + // Write header into logger
  445 + log_header1(TERSE, "Initialise exposure cube");
  446 +
478 // Extract exposure cube definition 447 // Extract exposure cube definition
479 const GWcs* proj = static_cast<const GWcs*>(m_expcube.cube().projection()); 448 const GWcs* proj = static_cast<const GWcs*>(m_expcube.cube().projection());
480 std::string wcs = m_expcube.cube().projection()->code(); 449 std::string wcs = m_expcube.cube().projection()->code();
@@ -492,12 +461,6 @@ void ctexpcube::init_cube(void) @@ -492,12 +461,6 @@ void ctexpcube::init_cube(void)
492 // If requested, insert energies at all event list energy boundaries 461 // If requested, insert energies at all event list energy boundaries
493 if (m_addbounds) { 462 if (m_addbounds) {
494 463
495 - // Set logger  
496 - GLog* logger = NULL;  
497 - if (logTerse()) {  
498 - logger = &log;  
499 - }  
500 -  
501 // Loop over all observations 464 // Loop over all observations
502 for (int i = 0; i < m_obs.size(); ++i) { 465 for (int i = 0; i < m_obs.size(); ++i) {
503 466
@@ -516,7 +479,7 @@ void ctexpcube::init_cube(void) @@ -516,7 +479,7 @@ void ctexpcube::init_cube(void)
516 } 479 }
517 480
518 // Insert energy boundaries 481 // Insert energy boundaries
519 - energies = insert_energy_boundaries(energies, *cta, logger); 482 + energies = insert_energy_boundaries(energies, *cta);
520 483
521 } // endfor: looped over all observations 484 } // endfor: looped over all observations
522 485
@@ -525,11 +488,9 @@ void ctexpcube::init_cube(void) @@ -525,11 +488,9 @@ void ctexpcube::init_cube(void)
525 // Setup exposure cube 488 // Setup exposure cube
526 m_expcube = GCTACubeExposure(wcs, coords, x, y, dx, dy, nx, ny, energies); 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 // Return 494 // Return
534 return; 495 return;
535 -}; 496 +}
src/ctobssim/ctobssim.cpp
@@ -324,7 +324,7 @@ void ctobssim::run(void) @@ -324,7 +324,7 @@ void ctobssim::run(void)
324 wrklog.name(log.name()); 324 wrklog.name(log.name());
325 325
326 // Set a big value to avoid flushing 326 // Set a big value to avoid flushing
327 - wrklog.max_size(10000000); 327 + wrklog.buffer_size(10000000);
328 328
329 // Loop over all observation in the container. If OpenMP support 329 // Loop over all observation in the container. If OpenMP support
330 // is enabled, this loop will be parallelized. 330 // is enabled, this loop will be parallelized.
src/ctpsfcube/ctpsfcube.cpp
@@ -216,35 +216,20 @@ void ctpsfcube::run(void) @@ -216,35 +216,20 @@ void ctpsfcube::run(void)
216 // Get task parameters 216 // Get task parameters
217 get_parameters(); 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 init_cube(); 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 // Fill PSF cube 228 // Fill PSF cube
242 m_psfcube.fill(m_obs, &log); 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 // Return 234 // Return
250 return; 235 return;
@@ -253,31 +238,34 @@ void ctpsfcube::run(void) @@ -253,31 +238,34 @@ void ctpsfcube::run(void)
253 238
254 /***********************************************************************//** 239 /***********************************************************************//**
255 * @brief Save PSF cube 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 void ctpsfcube::save(void) 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 // Get output filename 250 // Get output filename
266 m_outcube = (*this)["outcube"].filename(); 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 m_psfcube.save(m_outcube, clobber()); 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 // Return 270 // Return
283 return; 271 return;
@@ -381,7 +369,7 @@ void ctpsfcube::get_parameters(void) @@ -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 else { 373 else {
386 374
387 // Load event cube from filename 375 // Load event cube from filename
@@ -416,7 +404,10 @@ void ctpsfcube::get_parameters(void) @@ -416,7 +404,10 @@ void ctpsfcube::get_parameters(void)
416 ***************************************************************************/ 404 ***************************************************************************/
417 void ctpsfcube::init_cube(void) 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 const GWcs* proj = static_cast<const GWcs*>(m_psfcube.cube().projection()); 411 const GWcs* proj = static_cast<const GWcs*>(m_psfcube.cube().projection());
421 std::string wcs = m_psfcube.cube().projection()->code(); 412 std::string wcs = m_psfcube.cube().projection()->code();
422 std::string coords = m_psfcube.cube().projection()->coordsys(); 413 std::string coords = m_psfcube.cube().projection()->coordsys();
@@ -435,12 +426,6 @@ void ctpsfcube::init_cube(void) @@ -435,12 +426,6 @@ void ctpsfcube::init_cube(void)
435 // If requested, insert energies at all event list energy boundaries 426 // If requested, insert energies at all event list energy boundaries
436 if (m_addbounds) { 427 if (m_addbounds) {
437 428
438 - // Set logger  
439 - GLog* logger = NULL;  
440 - if (logTerse()) {  
441 - logger = &log;  
442 - }  
443 -  
444 // Loop over all observations 429 // Loop over all observations
445 for (int i = 0; i < m_obs.size(); ++i) { 430 for (int i = 0; i < m_obs.size(); ++i) {
446 431
@@ -459,7 +444,7 @@ void ctpsfcube::init_cube(void) @@ -459,7 +444,7 @@ void ctpsfcube::init_cube(void)
459 } 444 }
460 445
461 // Insert energy boundaries 446 // Insert energy boundaries
462 - energies = insert_energy_boundaries(energies, *cta, logger); 447 + energies = insert_energy_boundaries(energies, *cta);
463 448
464 } // endfor: looped over all observations 449 } // endfor: looped over all observations
465 450
@@ -469,11 +454,9 @@ void ctpsfcube::init_cube(void) @@ -469,11 +454,9 @@ void ctpsfcube::init_cube(void)
469 m_psfcube = GCTACubePsf(wcs, coords, x, y, dx, dy, nx, ny, energies, 454 m_psfcube = GCTACubePsf(wcs, coords, x, y, dx, dy, nx, ny, energies,
470 dmax, ndbins); 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 // Return 460 // Return
478 return; 461 return;
479 -}; 462 +}
src/support/ctool.cpp
@@ -68,15 +68,14 @@ @@ -68,15 +68,14 @@
68 68
69 /***********************************************************************//** 69 /***********************************************************************//**
70 * @brief Void constructor 70 * @brief Void constructor
  71 + *
  72 + * Constructs an empty ctools.
71 ***************************************************************************/ 73 ***************************************************************************/
72 ctool::ctool(void) : GApplication() 74 ctool::ctool(void) : GApplication()
73 { 75 {
74 // Initialise members 76 // Initialise members
75 init_members(); 77 init_members();
76 78
77 - // Write header into logger  
78 - log_header();  
79 -  
80 // Return 79 // Return
81 return; 80 return;
82 } 81 }
@@ -87,6 +86,16 @@ ctool::ctool(void) : GApplication() @@ -87,6 +86,16 @@ ctool::ctool(void) : GApplication()
87 * 86 *
88 * @param[in] name Application name. 87 * @param[in] name Application name.
89 * @param[in] version Application version. 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 ctool::ctool(const std::string& name, const std::string& version) : 100 ctool::ctool(const std::string& name, const std::string& version) :
92 GApplication(name, version) 101 GApplication(name, version)
@@ -94,9 +103,6 @@ ctool::ctool(const std::string&amp; name, const std::string&amp; version) : @@ -94,9 +103,6 @@ ctool::ctool(const std::string&amp; name, const std::string&amp; version) :
94 // Initialise members 103 // Initialise members
95 init_members(); 104 init_members();
96 105
97 - // Write header into logger  
98 - log_header();  
99 -  
100 // Return 106 // Return
101 return; 107 return;
102 } 108 }
@@ -109,6 +115,16 @@ ctool::ctool(const std::string&amp; name, const std::string&amp; version) : @@ -109,6 +115,16 @@ ctool::ctool(const std::string&amp; name, const std::string&amp; version) :
109 * @param[in] version Application version. 115 * @param[in] version Application version.
110 * @param[in] argc Number of arguments in command line. 116 * @param[in] argc Number of arguments in command line.
111 * @param[in] argv Array of command line arguments. 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 ctool::ctool(const std::string& name, const std::string& version, 129 ctool::ctool(const std::string& name, const std::string& version,
114 int argc, char *argv[]) : 130 int argc, char *argv[]) :
@@ -123,8 +139,8 @@ ctool::ctool(const std::string&amp; name, const std::string&amp; version, @@ -123,8 +139,8 @@ ctool::ctool(const std::string&amp; name, const std::string&amp; version,
123 // Initialise members 139 // Initialise members
124 init_members(); 140 init_members();
125 141
126 - // Write header into logger  
127 - log_header(); 142 + // Open the log file
  143 + logFileOpen();
128 144
129 // Return 145 // Return
130 return; 146 return;
@@ -135,6 +151,8 @@ ctool::ctool(const std::string&amp; name, const std::string&amp; version, @@ -135,6 +151,8 @@ ctool::ctool(const std::string&amp; name, const std::string&amp; version,
135 * @brief Copy constructor 151 * @brief Copy constructor
136 * 152 *
137 * @param[in] app Application. 153 * @param[in] app Application.
  154 + *
  155 + * Construct a ctools from another ctool instance.
138 ***************************************************************************/ 156 ***************************************************************************/
139 ctool::ctool(const ctool& app) : GApplication(app) 157 ctool::ctool(const ctool& app) : GApplication(app)
140 { 158 {
@@ -172,6 +190,8 @@ ctool::~ctool(void) @@ -172,6 +190,8 @@ ctool::~ctool(void)
172 * @brief Assignment operator 190 * @brief Assignment operator
173 * 191 *
174 * @param[in] app Application. 192 * @param[in] app Application.
  193 + *
  194 + * Assigns one ctool to another.
175 ***************************************************************************/ 195 ***************************************************************************/
176 ctool& ctool::operator=(const ctool& app) 196 ctool& ctool::operator=(const ctool& app)
177 { 197 {
@@ -207,7 +227,8 @@ ctool&amp; ctool::operator=(const ctool&amp; app) @@ -207,7 +227,8 @@ ctool&amp; ctool::operator=(const ctool&amp; app)
207 * @brief Execute application 227 * @brief Execute application
208 * 228 *
209 * This is the main execution method of a ctool. The method is invoked when 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 void ctool::execute(void) 233 void ctool::execute(void)
213 { 234 {
@@ -275,9 +296,6 @@ void ctool::copy_members(const ctool&amp; app) @@ -275,9 +296,6 @@ void ctool::copy_members(const ctool&amp; app)
275 ***************************************************************************/ 296 ***************************************************************************/
276 void ctool::free_members(void) 297 void ctool::free_members(void)
277 { 298 {
278 - // Write separator into logger  
279 - log_string(TERSE, "");  
280 -  
281 // Return 299 // Return
282 return; 300 return;
283 } 301 }
@@ -1374,10 +1392,11 @@ std::string ctool::get_obs_header(const GObservation* obs) @@ -1374,10 +1392,11 @@ std::string ctool::get_obs_header(const GObservation* obs)
1374 * @param[in] energies Energies. 1392 * @param[in] energies Energies.
1375 * @param[in] obs Observation container. 1393 * @param[in] obs Observation container.
1376 * @return Energies. 1394 * @return Energies.
  1395 + *
  1396 + * Inserts the energy boundaries of an observation in a list of @p energies.
1377 ***************************************************************************/ 1397 ***************************************************************************/
1378 GEnergies ctool::insert_energy_boundaries(const GEnergies& energies, 1398 GEnergies ctool::insert_energy_boundaries(const GEnergies& energies,
1379 - const GCTAObservation& obs,  
1380 - GLog* log) const 1399 + const GCTAObservation& obs)
1381 { 1400 {
1382 // Create copy of input energies 1401 // Create copy of input energies
1383 GEnergies engs = energies; 1402 GEnergies engs = energies;
@@ -1428,11 +1447,8 @@ GEnergies ctool::insert_energy_boundaries(const GEnergies&amp; energies, @@ -1428,11 +1447,8 @@ GEnergies ctool::insert_energy_boundaries(const GEnergies&amp; energies,
1428 engs.append(energy); 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 } // endif: energy insertion requested 1453 } // endif: energy insertion requested
1438 1454
@@ -1444,7 +1460,63 @@ GEnergies ctool::insert_energy_boundaries(const GEnergies&amp; energies, @@ -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 void ctool::provide_help(void) const 1521 void ctool::provide_help(void) const
1450 { 1522 {
src/support/ctool.hpp
@@ -101,8 +101,10 @@ protected: @@ -101,8 +101,10 @@ protected:
101 size_t get_current_rss(void); 101 size_t get_current_rss(void);
102 std::string get_obs_header(const GObservation* obs); 102 std::string get_obs_header(const GObservation* obs);
103 GEnergies insert_energy_boundaries(const GEnergies& energies, 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 // Protected members 109 // Protected members
108 bool m_read_ahead; //!< Read ahead output parameters 110 bool m_read_ahead; //!< Read ahead output parameters
test/test_ctbin.py
@@ -134,7 +134,6 @@ class Test(test): @@ -134,7 +134,6 @@ class Test(test):
134 bin.clear() 134 bin.clear()
135 135
136 # Now set ctbin parameters 136 # Now set ctbin parameters
137 - #bin = ctools.ctbin()  
138 bin['inobs'] = self._events 137 bin['inobs'] = self._events
139 bin['outcube'] = 'ctbin_py1.fits' 138 bin['outcube'] = 'ctbin_py1.fits'
140 bin['ebinalg'] = 'LOG' 139 bin['ebinalg'] = 'LOG'
@@ -159,7 +158,7 @@ class Test(test): @@ -159,7 +158,7 @@ class Test(test):
159 self._check_observation(bin, 5542) 158 self._check_observation(bin, 5542)
160 self._check_cube(bin.cube(), 5542) 159 self._check_cube(bin.cube(), 5542)
161 160
162 - # Test copy constructor 161 + # Copy ctbin tool
163 cpy_bin = bin.copy() 162 cpy_bin = bin.copy()
164 163
165 # Check content of observation container and counts cube 164 # Check content of observation container and counts cube
test/test_ctbkgcube.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
@@ -85,7 +86,7 @@ class Test(test): @@ -85,7 +86,7 @@ class Test(test):
85 'Check successful execution from command line') 86 'Check successful execution from command line')
86 87
87 # Check result file 88 # Check result file
88 - self._check_result_file('ctbkgcube_cmd1.fits') 89 + self._check_result_files('ctbkgcube_cmd1')
89 90
90 # Setup ctbkgcube command 91 # Setup ctbkgcube command
91 cmd = ctbkgcube+' inobs="event_file_that_does_not_exist.fits"'+ \ 92 cmd = ctbkgcube+' inobs="event_file_that_does_not_exist.fits"'+ \
@@ -111,8 +112,29 @@ class Test(test): @@ -111,8 +112,29 @@ class Test(test):
111 """ 112 """
112 Test ctbkgcube from Python 113 Test ctbkgcube from Python
113 """ 114 """
114 - # Set-up ctbkgcube 115 + # Allocate ctbkgcube
115 bkgcube = ctools.ctbkgcube() 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 bkgcube['inobs'] = self._events 138 bkgcube['inobs'] = self._events
117 bkgcube['inmodel'] = self._model 139 bkgcube['inmodel'] = self._model
118 bkgcube['incube'] = 'NONE' 140 bkgcube['incube'] = 'NONE'
@@ -120,7 +142,7 @@ class Test(test): @@ -120,7 +142,7 @@ class Test(test):
120 bkgcube['irf'] = self._irf 142 bkgcube['irf'] = self._irf
121 bkgcube['ebinalg'] = 'LOG' 143 bkgcube['ebinalg'] = 'LOG'
122 bkgcube['emin'] = 0.1 144 bkgcube['emin'] = 0.1
123 - bkgcube['emax'] = 100 145 + bkgcube['emax'] = 100.0
124 bkgcube['enumbins'] = 20 146 bkgcube['enumbins'] = 20
125 bkgcube['nxpix'] = 10 147 bkgcube['nxpix'] = 10
126 bkgcube['nypix'] = 10 148 bkgcube['nypix'] = 10
@@ -139,45 +161,43 @@ class Test(test): @@ -139,45 +161,43 @@ class Test(test):
139 bkgcube.run() 161 bkgcube.run()
140 bkgcube.save() 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 cpy_bkgcube.execute() 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 # Prepare observation container 202 # Prepare observation container
183 obs1 = gammalib.GCTAObservation(self._events) 203 obs1 = gammalib.GCTAObservation(self._events)
@@ -195,47 +215,94 @@ class Test(test): @@ -195,47 +215,94 @@ class Test(test):
195 215
196 # Set-up ctbkgcube from observation container 216 # Set-up ctbkgcube from observation container
197 bkgcube = ctools.ctbkgcube(obs) 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 # Execute ctbkgcube tool 238 # Execute ctbkgcube tool
220 bkgcube.logFileOpen() # Make sure we get a log file 239 bkgcube.logFileOpen() # Make sure we get a log file
221 bkgcube.execute() 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 # Return 245 # Return
227 return 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 Check result file 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 # Check dimensions 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 # Return 291 # Return
241 return 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,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
@@ -109,16 +110,32 @@ class Test(test): @@ -109,16 +110,32 @@ class Test(test):
109 """ 110 """
110 Test ctedispcube from Python 111 Test ctedispcube from Python
111 """ 112 """
112 - # Set-up ctedispcube 113 + # Allocate ctedispcube
113 edispcube = ctools.ctedispcube() 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 edispcube['inobs'] = self._events 132 edispcube['inobs'] = self._events
115 edispcube['incube'] = 'NONE' 133 edispcube['incube'] = 'NONE'
116 - edispcube['outcube'] = 'ctedispcube_py1.fits'  
117 edispcube['caldb'] = self._caldb 134 edispcube['caldb'] = self._caldb
118 edispcube['irf'] = self._irf 135 edispcube['irf'] = self._irf
119 edispcube['ebinalg'] = 'LOG' 136 edispcube['ebinalg'] = 'LOG'
120 edispcube['emin'] = 0.1 137 edispcube['emin'] = 0.1
121 - edispcube['emax'] = 100 138 + edispcube['emax'] = 100.0
122 edispcube['enumbins'] = 20 139 edispcube['enumbins'] = 20
123 edispcube['nxpix'] = 10 140 edispcube['nxpix'] = 10
124 edispcube['nypix'] = 10 141 edispcube['nypix'] = 10
@@ -129,31 +146,88 @@ class Test(test): @@ -129,31 +146,88 @@ class Test(test):
129 edispcube['yref'] = 22.01 146 edispcube['yref'] = 22.01
130 edispcube['migramax'] = 2.0 147 edispcube['migramax'] = 2.0
131 edispcube['migrabins'] = 10 148 edispcube['migrabins'] = 10
  149 + edispcube['outcube'] = 'ctedispcube_py1.fits'
132 edispcube['logfile'] = 'ctedispcube_py1.log' 150 edispcube['logfile'] = 'ctedispcube_py1.log'
133 edispcube['chatter'] = 2 151 edispcube['chatter'] = 2
134 152
135 # Run ctedispcube tool 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 edispcube.run() 155 edispcube.run()
138 edispcube.save() 156 edispcube.save()
139 157
140 # Check result file 158 # Check result file
141 self._check_result_file('ctedispcube_py1.fits') 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 # Return 188 # Return
144 return 189 return
145 190
146 # Check result file 191 # Check result file
147 - def _check_result_file(self, filename): 192 + def _check_result_file(self, filename, nenergies=21):
148 """ 193 """
149 Check result file 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 # Check dimensions 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 # Return 232 # Return
159 return 233 return
test/test_ctexpcube.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
@@ -107,16 +108,32 @@ class Test(test): @@ -107,16 +108,32 @@ class Test(test):
107 """ 108 """
108 Test ctexpcube from Python 109 Test ctexpcube from Python
109 """ 110 """
110 - # Set-up ctexpcube 111 + # Allocate ctexpcube
111 expcube = ctools.ctexpcube() 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 expcube['inobs'] = self._events 130 expcube['inobs'] = self._events
113 expcube['incube'] = 'NONE' 131 expcube['incube'] = 'NONE'
114 - expcube['outcube'] = 'ctexpcube_py1.fits'  
115 expcube['caldb'] = self._caldb 132 expcube['caldb'] = self._caldb
116 expcube['irf'] = self._irf 133 expcube['irf'] = self._irf
117 expcube['ebinalg'] = 'LOG' 134 expcube['ebinalg'] = 'LOG'
118 expcube['emin'] = 0.1 135 expcube['emin'] = 0.1
119 - expcube['emax'] = 100 136 + expcube['emax'] = 100.0
120 expcube['enumbins'] = 20 137 expcube['enumbins'] = 20
121 expcube['nxpix'] = 200 138 expcube['nxpix'] = 200
122 expcube['nypix'] = 200 139 expcube['nypix'] = 200
@@ -126,6 +143,7 @@ class Test(test): @@ -126,6 +143,7 @@ class Test(test):
126 expcube['xref'] = 83.63 143 expcube['xref'] = 83.63
127 expcube['yref'] = 22.01 144 expcube['yref'] = 22.01
128 expcube['logfile'] = 'ctexpcube_py1.log' 145 expcube['logfile'] = 'ctexpcube_py1.log'
  146 + expcube['outcube'] = 'ctexpcube_py1.fits'
129 expcube['chatter'] = 2 147 expcube['chatter'] = 2
130 148
131 # Run ctexpcube tool 149 # Run ctexpcube tool
@@ -136,19 +154,72 @@ class Test(test): @@ -136,19 +154,72 @@ class Test(test):
136 # Check result file 154 # Check result file
137 self._check_result_file('ctexpcube_py1.fits') 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 # Return 184 # Return
140 return 185 return
141 186
142 # Check result file 187 # Check result file
143 - def _check_result_file(self, filename): 188 + def _check_result_file(self, filename, nenergies=21):
144 """ 189 """
145 Check result file 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 # Check dimensions 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 # Return 224 # Return
154 return 225 return
test/test_ctpsfcube.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
@@ -109,8 +110,25 @@ class Test(test): @@ -109,8 +110,25 @@ class Test(test):
109 """ 110 """
110 Test ctpsfcube from Python 111 Test ctpsfcube from Python
111 """ 112 """
112 - # Set-up ctpsfcube 113 + # Allocate ctpsfcube
113 psfcube = ctools.ctpsfcube() 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 psfcube['inobs'] = self._events 132 psfcube['inobs'] = self._events
115 psfcube['incube'] = 'NONE' 133 psfcube['incube'] = 'NONE'
116 psfcube['outcube'] = 'ctpsfcube_py1.fits' 134 psfcube['outcube'] = 'ctpsfcube_py1.fits'
@@ -140,19 +158,71 @@ class Test(test): @@ -140,19 +158,71 @@ class Test(test):
140 # Check result file 158 # Check result file
141 self._check_result_file('ctpsfcube_py1.fits') 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 # Return 187 # Return
144 return 188 return
145 189
146 # Check result file 190 # Check result file
147 - def _check_result_file(self, filename): 191 + def _check_result_file(self, filename, nenergies=21):
148 """ 192 """
149 Check result file 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 # Check dimensions 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 # Return 227 # Return
158 return 228 return