Commit f25aeb73625d6d907da46a75ada6823c19f96513

Authored by Jürgen Knödlseder
1 parent cf3f1ec9

Enable ctbin to produce counts cubes for a joint binned analysis (#2714)

ChangeLog
1 -2018-11-03 1 +2018-11-07
2 2
3 * Version 1.6.0 released 3 * Version 1.6.0 released
4 ======================== 4 ========================
5 5
  6 + Enable ctbin to produce counts cubes for a joint binned analysis (#2714)
6 Add code generator (#2709) 7 Add code generator (#2709)
7 Add csbkgmodel script (#2694) 8 Add csbkgmodel script (#2694)
8 Compute DETX and DETY in ctmodel if they are not available (#2694) 9 Compute DETX and DETY in ctmodel if they are not available (#2694)
@@ -55,6 +55,14 @@ of the filename is now done by the GApplicationPar class. @@ -55,6 +55,14 @@ of the filename is now done by the GApplicationPar class.
55 55
56 ctbin - CTA event binning 56 ctbin - CTA event binning
57 ------------------------- 57 -------------------------
  58 +Added the option to produce an individual counts cube for each event list in
  59 +the input observation definition XML file. The option is enabled by setting
  60 +the new hidden parameter "stack" to "no". The cube() method now has an optional
  61 +index argument, providing access to the i'th counts cube that was produced.
  62 +The cubes() method specifies the number of counts cubes that were produced.
  63 +If no counts cube was produced, the cube() method now throws an exception
  64 +(#2714).
  65 +
58 ctbin now accouts for varying energy thresholds when computing the weight 66 ctbin now accouts for varying energy thresholds when computing the weight
59 array (#2685). 67 array (#2685).
60 68
doc/source/admin/release_history/1.6.rst
@@ -41,6 +41,8 @@ Bug fixes @@ -41,6 +41,8 @@ Bug fixes
41 Improvements 41 Improvements
42 ------------ 42 ------------
43 43
  44 +* [`2714 <https://cta-redmine.irap.omp.eu/issues/2714>`_] -
  45 + Enable :ref:`ctbin` to produce counts cubes for a joint binned analysis
44 * [`2685 <https://cta-redmine.irap.omp.eu/issues/2685>`_] - 46 * [`2685 <https://cta-redmine.irap.omp.eu/issues/2685>`_] -
45 Add ``ctbkgcube::cntcube()`` method 47 Add ``ctbkgcube::cntcube()`` method
46 Account for varying energy thresholds in :ref:`ctbin` weight computation 48 Account for varying energy thresholds in :ref:`ctbin` weight computation
pyext/ctbin.i
1 /*************************************************************************** 1 /***************************************************************************
2 * ctbin - Event binning tool * 2 * ctbin - Event binning tool *
3 * ----------------------------------------------------------------------- * 3 * ----------------------------------------------------------------------- *
4 - * copyright (C) 2010-2016 by Juergen Knoedlseder * 4 + * copyright (C) 2010-2018 by Juergen Knoedlseder *
5 * ----------------------------------------------------------------------- * 5 * ----------------------------------------------------------------------- *
6 * * 6 * *
7 * This program is free software: you can redistribute it and/or modify * 7 * This program is free software: you can redistribute it and/or modify *
@@ -46,10 +46,10 @@ public: @@ -46,10 +46,10 @@ public:
46 // Methods 46 // Methods
47 void clear(void); 47 void clear(void);
48 void run(void); 48 void run(void);
  49 + int cubes(void) const;
  50 + const GCTAEventCube& cube(const int& index = 0) const;
49 void save(void); 51 void save(void);
50 void publish(const std::string& name = ""); 52 void publish(const std::string& name = "");
51 - const GCTAEventCube& cube(void) const;  
52 - const GCTAEventCube& cube(const size_t& index) const;  
53 }; 53 };
54 54
55 55
src/ctbin/ctbin.cpp
@@ -38,6 +38,7 @@ @@ -38,6 +38,7 @@
38 #endif 38 #endif
39 39
40 /* __ Method name definitions ____________________________________________ */ 40 /* __ Method name definitions ____________________________________________ */
  41 +#define G_CUBE "ctbin::cube(int&)"
41 #define G_GET_PARAMETERS "ctbin::get_parameters()" 42 #define G_GET_PARAMETERS "ctbin::get_parameters()"
42 #define G_FILL_CUBE "ctbin::fill_cube(GCTAObservation*)" 43 #define G_FILL_CUBE "ctbin::fill_cube(GCTAObservation*)"
43 #define G_SET_WEIGHTS "ctbin::set_weights(GCTAObservation*)" 44 #define G_SET_WEIGHTS "ctbin::set_weights(GCTAObservation*)"
@@ -234,6 +235,9 @@ void ctbin::run(void) @@ -234,6 +235,9 @@ void ctbin::run(void)
234 // Get task parameters 235 // Get task parameters
235 get_parameters(); 236 get_parameters();
236 237
  238 + // Initialise sky direction cache for stacking
  239 + init_sky_dir_cache();
  240 +
237 // Write input observation container into logger 241 // Write input observation container into logger
238 log_observations(NORMAL, m_obs, "Input observation"); 242 log_observations(NORMAL, m_obs, "Input observation");
239 243
@@ -255,33 +259,33 @@ void ctbin::run(void) @@ -255,33 +259,33 @@ void ctbin::run(void)
255 log_string(NORMAL, msg); 259 log_string(NORMAL, msg);
256 } 260 }
257 261
258 - // Initialise cubes and ids if unstacked  
259 - if (!m_stack) {  
260 - m_cubes = std::vector<GCTAEventCube>(obs_list.size());  
261 - m_ids = std::vector<std::string>(obs_list.size()); 262 + // Set number of relevant observations
  263 + int nobs = obs_list.size();
  264 +
  265 + // Initialise arrays
  266 + if (nobs > 0) {
  267 + m_counts = std::vector<GSkyMap>(nobs);
  268 + m_weights = std::vector<GSkyMap>(nobs);
  269 + m_cubes = std::vector<GCTAEventCube>(nobs);
  270 + m_ids = std::vector<std::string>(nobs);
  271 + }
  272 + else {
  273 + m_counts.clear();
  274 + m_weights.clear();
  275 + m_cubes.clear();
  276 + m_ids.clear();
262 } 277 }
263 278
264 // Write header into logger 279 // Write header into logger
265 - log_header1(TERSE, gammalib::number("Bin observation", m_obs.size())); 280 + log_header1(TERSE, gammalib::number("Bin observation", nobs));
266 281
267 // Loop over all unbinned CTA observations in the container 282 // Loop over all unbinned CTA observations in the container
268 #pragma omp parallel for 283 #pragma omp parallel for
269 - for (int i = 0; i < obs_list.size(); ++i) { 284 + for (int i = 0; i < nobs; ++i) {
270 285
271 // Get pointer to observation 286 // Get pointer to observation
272 GCTAObservation* obs = obs_list[i]; 287 GCTAObservation* obs = obs_list[i];
273 288
274 - // Make sure that the observation holds a CTA event list. If this  
275 - // is not the case then throw an exception.  
276 - const GCTAEventList* events = dynamic_cast<const GCTAEventList*>  
277 - (obs->events());  
278 - if (events == NULL) {  
279 - std::string msg = "CTA Observation does not contain an event "  
280 - "list. An event list is needed to fill the "  
281 - "counts cube.";  
282 - throw GException::invalid_value(G_FILL_CUBE, msg);  
283 - }  
284 -  
285 // Fill the cube 289 // Fill the cube
286 GSkyMap counts = fill_cube(obs); 290 GSkyMap counts = fill_cube(obs);
287 291
@@ -291,88 +295,118 @@ void ctbin::run(void) @@ -291,88 +295,118 @@ void ctbin::run(void)
291 // Dispose events to free memory 295 // Dispose events to free memory
292 obs->dispose_events(); 296 obs->dispose_events();
293 297
  298 + // Post process cubes
294 #pragma omp critical(ctbin_fill_cube) 299 #pragma omp critical(ctbin_fill_cube)
295 - if (m_stack) {  
296 -  
297 -  
298 - m_counts += counts;  
299 -  
300 - for (int iebin = 0; iebin < m_ebounds.size(); ++iebin) {  
301 - for (int pixel = 0; pixel < m_counts.npix(); ++pixel) {  
302 - if (weights(pixel, iebin) == 1.0) {  
303 - m_weights(pixel, iebin) = 1.0;  
304 - }  
305 - }  
306 - }  
307 -  
308 -  
309 - // If GTI is empty then set its time reference from the observation.  
310 - // From then on we keep that time reference 300 + {
  301 + // If stacking of cubes is requested then add up counts cube
  302 + // and set weights in the first slot of the m_counts and
  303 + // m_weights arrays
  304 + if (m_stack) {
  305 + if (i == 0) {
  306 + m_counts[0] = counts;
  307 + m_weights[0] = weights;
  308 + }
  309 + else {
  310 + for (int iebin = 0; iebin < m_ebounds.size(); ++iebin) {
  311 + for (int pixel = 0; pixel < counts.npix(); ++pixel) {
  312 + m_counts[0](pixel, iebin) += counts(pixel, iebin);
  313 + if (weights(pixel, iebin) == 1.0) {
  314 + m_weights[0](pixel, iebin) = 1.0;
  315 + }
  316 + }
  317 + }
  318 + }
  319 + }
  320 +
  321 + // ... otherwise store counts, weights and observation ID
  322 + else {
  323 + m_counts[i] = counts;
  324 + m_weights[i] = weights;
  325 + m_ids[i] = obs->id();
  326 + }
  327 +
  328 + } // end: omp critical section
  329 +
  330 + } // endfor: looped over observations
  331 +
  332 + // Combine GTIs of all observations and compute total ontime and livetime
  333 + for (int i = 0; i < nobs; ++i) {
  334 +
  335 + // Get pointer to observation
  336 + GCTAObservation* obs = obs_list[i];
  337 +
  338 + // Get Good Time Intervals for observation
  339 + GGti gti = obs->gti();
  340 +
  341 + // If resulting GTI is not filled then copy over the reference from
  342 + // the observation and use it for the resulting GTI
311 if (m_gti.is_empty()) { 343 if (m_gti.is_empty()) {
312 - m_gti.reference(events->gti().reference()); 344 + m_gti.reference(gti.reference());
313 } 345 }
314 - 346 +
315 // Append GTIs 347 // Append GTIs
316 - m_gti.extend(events->gti());  
317 - 348 + m_gti.extend(gti);
  349 +
318 // Update ontime and livetime 350 // Update ontime and livetime
319 m_ontime += obs->ontime(); 351 m_ontime += obs->ontime();
320 m_livetime += obs->livetime(); 352 m_livetime += obs->livetime();
321 -  
322 - } else { // don't stack cubes  
323 353
324 - // Construct a cube from this observation  
325 - GCTAEventCube cube(counts, weights, m_ebounds, events->gti());  
326 - m_cubes[i] = cube;  
327 - obs->events(cube); 354 + } // endfor: looped over all observations
  355 +
  356 + // Post process data for a stacked cube
  357 + if (m_stack && nobs > 0) {
  358 +
  359 + // Compute livetime fraction per energy bin so that varying energy
  360 + // thresholds are correctly taken into account in the weighting
  361 + // array. Computational speed is not optimum, we have to see later
  362 + // whether this is an issue.
  363 + for (int iebin = 0; iebin < m_ebounds.size(); ++iebin) {
  364 + double livetime_ebin = 0.0;
  365 + for (int i = 0; i < obs_list.size(); ++i) {
  366 + std::vector<bool> usage =
  367 + cube_layer_usage(m_ebounds, obs_list[i]->ebounds());
  368 + if (usage[iebin]) {
  369 + livetime_ebin += obs_list[i]->livetime();
  370 + }
  371 + }
  372 + if (m_livetime > 0.0) {
  373 + double fraction = livetime_ebin / m_livetime;
  374 + for (int pixel = 0; pixel < m_counts[0].npix(); ++pixel) {
  375 + if (m_weights[0](pixel, iebin) > 0.0) {
  376 + m_weights[0](pixel, iebin) *= fraction;
  377 + }
  378 + }
  379 + }
  380 + }
328 381
329 - // Store id of the observation  
330 - m_ids[i] = obs->id();  
331 -  
332 - } 382 + // Set a single cube in the observation container
  383 + obs_cube_stacked();
333 384
  385 + } // endif: stacked cube post processing
334 386
335 - } // endfor: looped over observations 387 + // Post process data for non-stacked cubes
  388 + else {
336 389
337 - if (m_stack) {  
338 - // Compute total livetime in all observations  
339 - double livetime = 0.0;  
340 - for (int i = 0; i < obs_list.size(); ++i) {  
341 - livetime += obs_list[i]->livetime();  
342 - }  
343 -  
344 - // Compute livetime fraction per energy bin so that varying energy  
345 - // thresholds are correctly taken into account in the weighting  
346 - // array. Computational speed is not optimum, we have to see later  
347 - // whether this is an issue.  
348 - for (int iebin = 0; iebin < m_ebounds.size(); ++iebin) {  
349 - double livetime_ebin = 0.0;  
350 - for (int i = 0; i < obs_list.size(); ++i) {  
351 - const GCTAEventList* events = dynamic_cast<const GCTAEventList*>  
352 - (obs_list[i]->events());  
353 - std::vector<bool> usage = cube_layer_usage(m_ebounds,  
354 - events->ebounds());  
355 - if (usage[iebin]) {  
356 - livetime_ebin += obs_list[i]->livetime();  
357 - }  
358 - }  
359 - if (livetime > 0.0) {  
360 - double fraction = livetime_ebin / livetime;  
361 - for (int pixel = 0; pixel < m_counts.npix(); ++pixel) {  
362 - if (m_weights(pixel, iebin) > 0.0) {  
363 - m_weights(pixel, iebin) *= fraction;  
364 - }  
365 - }  
366 - }  
367 - } 390 + // Loop over all relevant observations
  391 + for (int i = 0; i < nobs; ++i) {
368 392
369 - // Build event cube (needs to come before obs_cube() since this method  
370 - // relies on correct setting of m_cube)  
371 - m_cube = GCTAEventCube(m_counts, m_weights, m_ebounds, m_gti); 393 + // Get pointer to observation
  394 + GCTAObservation* obs = obs_list[i];
372 395
373 - // Set a single cube in the observation container  
374 - obs_cube();  
375 - } 396 + // Build counts cube
  397 + m_cubes[i] = GCTAEventCube(m_counts[i], m_weights[i], m_ebounds,
  398 + obs->gti());
  399 +
  400 + // Attach event cube in the correct slot of the observation
  401 + // container
  402 + obs->events(m_cubes[i]);
  403 +
  404 + // Store ID of the observation for saving of XML file
  405 + m_ids[i] = obs->id();
  406 +
  407 + } // endfor: looped over relevant observations
  408 +
  409 + } // endelse: non-stacked cube post processing
376 410
377 // Write resulting observation container into logger 411 // Write resulting observation container into logger
378 log_observations(NORMAL, m_obs, "Binned observation"); 412 log_observations(NORMAL, m_obs, "Binned observation");
@@ -388,72 +422,108 @@ void ctbin::run(void) @@ -388,72 +422,108 @@ void ctbin::run(void)
388 422
389 423
390 /***********************************************************************//** 424 /***********************************************************************//**
  425 + * @brief Return event cube at index
  426 + *
  427 + * @param[in] index Cube index (0,...,cubes()-1).
  428 + * @return Reference to event cube.
  429 + *
  430 + * @exception GException::out_of_range
  431 + * Event cube index out of range.
  432 + *
  433 + * Returns a reference to the event cube at the given index.
  434 + ***************************************************************************/
  435 +const GCTAEventCube& ctbin::cube(const int& index) const
  436 +{
  437 + // Compile option: raise an exception if index is out of range
  438 + if (index < 0 || index >= cubes()) {
  439 + throw GException::out_of_range(G_CUBE, "Cube index", index, cubes());
  440 + }
  441 +
  442 + // Return cube
  443 + return m_cubes[index];
  444 +}
  445 +
  446 +
  447 +/***********************************************************************//**
391 * @brief Save counts cube 448 * @brief Save counts cube
392 * 449 *
393 - * Saves the counts cube into a FITS file. 450 + * Saves the counts cube. If the counts cube was stacked single FITS file
  451 + * will be written. Otherwise each generated counts cube will be written
  452 + * into a FITS file, and an observation definition XML file will be written
  453 + * out that contains an observation for each counts cube.
394 ***************************************************************************/ 454 ***************************************************************************/
395 void ctbin::save(void) 455 void ctbin::save(void)
396 { 456 {
397 // Write header 457 // Write header
398 log_header1(TERSE, "Save counts cube"); 458 log_header1(TERSE, "Save counts cube");
399 459
400 - if (!m_stack) {  
401 -  
402 - // Loop over all observations  
403 - for (size_t i = 0; i < m_obs.size(); ++i) {  
404 -  
405 - // Loop over all ids of observations that were binned  
406 - for (size_t j = 0; j < m_ids.size(); ++j) {  
407 -  
408 - // Check if ids are equal  
409 - if (m_ids[j] == m_obs[i]->id()) {  
410 -  
411 - // Construct filename  
412 - std::string filename(m_ids[j]);  
413 - filename.insert(0, m_prefix);  
414 - filename.append(".fits");  
415 -  
416 - // Set filename for counts cube  
417 - GCTAObservation* obs = dynamic_cast<GCTAObservation*>(m_obs[i]);  
418 - obs->eventfile(filename);  
419 -  
420 - // Save countscube  
421 - obs->save(filename, clobber());  
422 - }  
423 - }  
424 - }  
425 - if ((*this)["outcube"].is_valid()) {  
426 - // Get counts cube filename  
427 - m_outcube = (*this)["outcube"].filename();  
428 - m_obs.save(m_outcube);  
429 - }  
430 -  
431 - } else { // else: stack  
432 -  
433 - // Save only if filename is valid and if there is at least one  
434 - // observation  
435 - if ((*this)["outcube"].is_valid() && m_obs.size() > 0) {  
436 -  
437 - // Get counts cube filename  
438 - m_outcube = (*this)["outcube"].filename();  
439 -  
440 - // Get CTA observation from observation container  
441 - GCTAObservation* obs = dynamic_cast<GCTAObservation*>(m_obs[0]);  
442 -  
443 - // Save only if observation is valid  
444 - if (obs != NULL) {  
445 -  
446 - // Log counts cube file name  
447 - log_value(NORMAL, "Counts cube file", m_outcube.url());  
448 -  
449 - // Save cube  
450 - obs->save(m_outcube, clobber());  
451 -  
452 - } // endif: observation was valid  
453 -  
454 - } // endif: outcube file was valid  
455 -  
456 - } // endelse: stack 460 + // If counts cube was stacked then save first observation
  461 + if (m_stack) {
  462 +
  463 + // Save only if filename is valid and if there is at least one
  464 + // observation
  465 + if ((*this)["outcube"].is_valid() && m_obs.size() > 0) {
  466 +
  467 + // Get counts cube filename
  468 + m_outcube = (*this)["outcube"].filename();
  469 +
  470 + // Get CTA observation from observation container
  471 + GCTAObservation* obs = dynamic_cast<GCTAObservation*>(m_obs[0]);
  472 +
  473 + // Save only if observation is valid
  474 + if (obs != NULL) {
  475 +
  476 + // Log counts cube file name
  477 + log_value(NORMAL, "Counts cube file", m_outcube.url());
  478 +
  479 + // Save cube
  480 + obs->save(m_outcube, clobber());
  481 +
  482 + } // endif: observation was valid
  483 +
  484 + } // endif: outcube file was valid
  485 +
  486 + } // endif: counts cube was stacked
  487 +
  488 + // ... otherwise write out all relevant counts cubes and the observation
  489 + // definition XML file
  490 + else {
  491 +
  492 + // Loop over all observations
  493 + for (int i = 0; i < m_obs.size(); ++i) {
  494 +
  495 + // Loop over all ids of observations that were binned
  496 + for (int j = 0; j < m_ids.size(); ++j) {
  497 +
  498 + // Skip if ID differes
  499 + if (m_ids[j] != m_obs[i]->id()) {
  500 + continue;
  501 + }
  502 +
  503 + // Construct filename
  504 + std::string filename(m_ids[j]);
  505 + filename.insert(0, m_prefix);
  506 + filename.append(".fits");
  507 +
  508 + // Set filename for counts cube
  509 + GCTAObservation* obs = dynamic_cast<GCTAObservation*>(m_obs[i]);
  510 + obs->eventfile(filename);
  511 +
  512 + // Save countscube
  513 + obs->save(filename, clobber());
  514 +
  515 + } // endfor: looped over all IDs
  516 +
  517 + } // endfor: looped over all observations
  518 +
  519 + // Write XML file
  520 + if ((*this)["outcube"].is_valid()) {
  521 + m_outcube = (*this)["outcube"].filename();
  522 + m_obs.save(m_outcube);
  523 + }
  524 +
  525 + } // endelse: counts cube were not stacked
  526 +
457 // Return 527 // Return
458 return; 528 return;
459 } 529 }
@@ -478,8 +548,10 @@ void ctbin::publish(const std::string&amp; name) @@ -478,8 +548,10 @@ void ctbin::publish(const std::string&amp; name)
478 // Write counts cube name into logger 548 // Write counts cube name into logger
479 log_value(NORMAL, "Counts cube name", user_name); 549 log_value(NORMAL, "Counts cube name", user_name);
480 550
481 - // Publish counts cube  
482 - m_counts.publish(user_name); 551 + // Publish first counts cube if it exists
  552 + if (cubes() > 0) {
  553 + m_counts[0].publish(user_name);
  554 + }
483 555
484 // Return 556 // Return
485 return; 557 return;
@@ -505,15 +577,14 @@ void ctbin::init_members(void) @@ -505,15 +577,14 @@ void ctbin::init_members(void)
505 m_chatter = static_cast<GChatter>(2); 577 m_chatter = static_cast<GChatter>(2);
506 578
507 // Initialise protected members 579 // Initialise protected members
  580 + m_cubes.clear();
508 m_counts.clear(); 581 m_counts.clear();
509 m_weights.clear(); 582 m_weights.clear();
  583 + m_ids.clear();
510 m_ebounds.clear(); 584 m_ebounds.clear();
511 m_gti.clear(); 585 m_gti.clear();
512 - m_cube.clear();  
513 m_ontime = 0.0; 586 m_ontime = 0.0;
514 m_livetime = 0.0; 587 m_livetime = 0.0;
515 - m_cubes.clear();  
516 - m_ids.clear();  
517 588
518 // Initialise cache members 589 // Initialise cache members
519 m_dirs.clear(); 590 m_dirs.clear();
@@ -538,15 +609,14 @@ void ctbin::copy_members(const ctbin&amp; app) @@ -538,15 +609,14 @@ void ctbin::copy_members(const ctbin&amp; app)
538 m_chatter = app.m_chatter; 609 m_chatter = app.m_chatter;
539 610
540 // Copy protected members 611 // Copy protected members
  612 + m_cubes = app.m_cubes;
541 m_counts = app.m_counts; 613 m_counts = app.m_counts;
542 m_weights = app.m_weights; 614 m_weights = app.m_weights;
  615 + m_ids = app.m_ids;
543 m_ebounds = app.m_ebounds; 616 m_ebounds = app.m_ebounds;
544 m_gti = app.m_gti; 617 m_gti = app.m_gti;
545 - m_cube = app.m_cube;  
546 m_ontime = app.m_ontime; 618 m_ontime = app.m_ontime;
547 m_livetime = app.m_livetime; 619 m_livetime = app.m_livetime;
548 - m_cubes = app.m_cubes;  
549 - m_ids = app.m_ids;  
550 620
551 // Copy cache members 621 // Copy cache members
552 m_dirs = app.m_dirs; 622 m_dirs = app.m_dirs;
@@ -581,14 +651,8 @@ void ctbin::get_parameters(void) @@ -581,14 +651,8 @@ void ctbin::get_parameters(void)
581 // information and do not accept counts cubes. 651 // information and do not accept counts cubes.
582 setup_observations(m_obs, false, true, false); 652 setup_observations(m_obs, false, true, false);
583 653
584 - // Create an event cube based on task parameters  
585 - GCTAEventCube cube = create_cube(m_obs);  
586 -  
587 - // Get the skymap from the cube and initialise all counts cube bins and  
588 - // weights to zero  
589 - m_counts = cube.counts();  
590 - m_counts = 0.0;  
591 - m_weights = m_counts; 654 + // Create an event cube to query task parameters
  655 + GCTAEventCube cube = this->ctool::create_cube(m_obs);
592 656
593 // Get energy boundaries 657 // Get energy boundaries
594 m_ebounds = cube.ebounds(); 658 m_ebounds = cube.ebounds();
@@ -608,13 +672,6 @@ void ctbin::get_parameters(void) @@ -608,13 +672,6 @@ void ctbin::get_parameters(void)
608 (*this)["outcube"].query(); 672 (*this)["outcube"].query();
609 } 673 }
610 674
611 - // Cache sky directions in m_counts  
612 - m_dirs.resize(m_counts.npix());  
613 - for (int pix = 0; pix < m_counts.npix(); ++pix) {  
614 - m_dirs[pix] = m_counts.inx2dir(pix);  
615 - m_dirs[pix].radec(m_dirs[pix].ra(), m_dirs[pix].dec());  
616 - }  
617 -  
618 // Set number of OpenMP threads 675 // Set number of OpenMP threads
619 #ifdef _OPENMP 676 #ifdef _OPENMP
620 int nthreads = (*this)["nthreads"].integer(); 677 int nthreads = (*this)["nthreads"].integer();
@@ -632,6 +689,95 @@ void ctbin::get_parameters(void) @@ -632,6 +689,95 @@ void ctbin::get_parameters(void)
632 689
633 690
634 /***********************************************************************//** 691 /***********************************************************************//**
  692 + * @brief Initialise sky direction cache for cube stack
  693 + *
  694 + * Initialises a cache that holds the sky directions of each counts cube
  695 + * pixel for a stacked analysis. This will speed up the computations later.
  696 + *
  697 + * If no stacking is requested, the method does nothing.
  698 + ***************************************************************************/
  699 +void ctbin::init_sky_dir_cache(void)
  700 +{
  701 + // Continue only is stacking is requested
  702 + if (m_stack) {
  703 +
  704 + // Create a sky map from task parameters
  705 + GSkyMap map = this->ctool::create_map(m_obs);
  706 +
  707 + // Initialise sky direction
  708 + m_dirs.resize(map.npix());
  709 +
  710 + // Compute sky direction for each pixel
  711 + for (int pixel = 0; pixel < map.npix(); ++pixel) {
  712 + m_dirs[pixel] = map.inx2dir(pixel);
  713 + }
  714 +
  715 + } // endif: stacking was requested
  716 +
  717 + // Return
  718 + return;
  719 +}
  720 +
  721 +
  722 +/***********************************************************************//**
  723 + * @brief Create counts cube sky map for current observation
  724 + *
  725 + * @param[in] obs CTA observation.
  726 + *
  727 + * Creates a counts cube sky map for current observations.
  728 + ***************************************************************************/
  729 +GSkyMap ctbin::create_cube(const GCTAObservation* obs)
  730 +{
  731 + // Read coordinate system and projection
  732 + std::string coordsys = (*this)["coordsys"].string();
  733 + std::string proj = (*this)["proj"].string();
  734 +
  735 + // Read sky map reference
  736 + double xref = 0.0;
  737 + double yref = 0.0;
  738 + bool usepnt = (*this)["usepnt"].boolean();
  739 + if (!usepnt) {
  740 + xref = (*this)["xref"].real();
  741 + yref = (*this)["yref"].real();
  742 + }
  743 +
  744 + // Read sky map pixel size and number of bins
  745 + double binsz = (*this)["binsz"].real();
  746 + int nxpix = (*this)["nxpix"].integer();
  747 + int nypix = (*this)["nypix"].integer();
  748 +
  749 + // Read energy boundaries
  750 + GEbounds ebounds = create_ebounds();
  751 +
  752 + // If requested, get pointing from observations
  753 + if (usepnt) {
  754 +
  755 + // Get pointing direction for observation
  756 + GSkyDir pnt = obs->pointing().dir();
  757 +
  758 + // Set xref/yref based on the coordinate system
  759 + if (gammalib::toupper(coordsys) == "GAL") {
  760 + xref = pnt.l_deg();
  761 + yref = pnt.b_deg();
  762 + }
  763 + else {
  764 + xref = pnt.ra_deg();
  765 + yref = pnt.dec_deg();
  766 + }
  767 +
  768 + } // endif: got pointing from observation
  769 +
  770 + // Initialise counts cube
  771 + GSkyMap cube = GSkyMap(proj, coordsys, xref, yref, -binsz, binsz,
  772 + nxpix, nypix, ebounds.size());
  773 +
  774 +
  775 + // Return cube
  776 + return cube;
  777 +}
  778 +
  779 +
  780 +/***********************************************************************//**
635 * @brief Fill events into counts cube 781 * @brief Fill events into counts cube
636 * 782 *
637 * @param[in] obs CTA observation. 783 * @param[in] obs CTA observation.
@@ -641,10 +787,10 @@ void ctbin::get_parameters(void) @@ -641,10 +787,10 @@ void ctbin::get_parameters(void)
641 * 787 *
642 * Fills the events from an event list into the counts cube. 788 * Fills the events from an event list into the counts cube.
643 ***************************************************************************/ 789 ***************************************************************************/
644 -GSkyMap ctbin::fill_cube(GCTAObservation* obs) 790 +GSkyMap ctbin::fill_cube(const GCTAObservation* obs)
645 { 791 {
646 - // Make sure that the observation holds a CTA event list. If this  
647 - // is not the case then throw an exception. 792 + // Make sure that the observation holds a CTA event list. If this is
  793 + // not the case then throw an exception.
648 const GCTAEventList* events = dynamic_cast<const GCTAEventList*> 794 const GCTAEventList* events = dynamic_cast<const GCTAEventList*>
649 (obs->events()); 795 (obs->events());
650 if (events == NULL) { 796 if (events == NULL) {
@@ -655,7 +801,7 @@ GSkyMap ctbin::fill_cube(GCTAObservation* obs) @@ -655,7 +801,7 @@ GSkyMap ctbin::fill_cube(GCTAObservation* obs)
655 } 801 }
656 802
657 // Get the RoI 803 // Get the RoI
658 - const GCTARoi& roi = events->roi(); 804 + const GCTARoi& roi = obs->roi();
659 805
660 // Check for RoI sanity 806 // Check for RoI sanity
661 if (!roi.is_valid()) { 807 if (!roi.is_valid()) {
@@ -667,7 +813,7 @@ GSkyMap ctbin::fill_cube(GCTAObservation* obs) @@ -667,7 +813,7 @@ GSkyMap ctbin::fill_cube(GCTAObservation* obs)
667 } 813 }
668 814
669 // Get counts cube usage flags 815 // Get counts cube usage flags
670 - std::vector<bool> usage = cube_layer_usage(m_ebounds, events->ebounds()); 816 + std::vector<bool> usage = cube_layer_usage(m_ebounds, obs->ebounds());
671 817
672 // Initialise binning statistics 818 // Initialise binning statistics
673 int num_outside_roi = 0; 819 int num_outside_roi = 0;
@@ -676,9 +822,12 @@ GSkyMap ctbin::fill_cube(GCTAObservation* obs) @@ -676,9 +822,12 @@ GSkyMap ctbin::fill_cube(GCTAObservation* obs)
676 int num_outside_ebds = 0; 822 int num_outside_ebds = 0;
677 int num_in_map = 0; 823 int num_in_map = 0;
678 824
679 - // Initialise new empty counts map  
680 - GSkyMap counts(m_counts);  
681 - counts *= 0.; 825 + // Create counts cube
  826 + GSkyMap counts = create_cube(obs);
  827 +
  828 + // Extract counts cube boundaries
  829 + double pixel_x_max = counts.nx() - 0.5;
  830 + double pixel_y_max = counts.ny() - 0.5;
682 831
683 // Fill counts sky map 832 // Fill counts sky map
684 for (int i = 0; i < events->size(); ++i) { 833 for (int i = 0; i < events->size(); ++i) {
@@ -699,7 +848,7 @@ GSkyMap ctbin::fill_cube(GCTAObservation* obs) @@ -699,7 +848,7 @@ GSkyMap ctbin::fill_cube(GCTAObservation* obs)
699 // Determine sky pixel 848 // Determine sky pixel
700 GSkyPixel pixel; 849 GSkyPixel pixel;
701 try { 850 try {
702 - pixel = m_counts.dir2pix(dir); 851 + pixel = counts.dir2pix(dir);
703 } 852 }
704 catch (std::exception &e) { 853 catch (std::exception &e) {
705 num_invalid_wcs++; 854 num_invalid_wcs++;
@@ -708,8 +857,8 @@ GSkyMap ctbin::fill_cube(GCTAObservation* obs) @@ -708,8 +857,8 @@ GSkyMap ctbin::fill_cube(GCTAObservation* obs)
708 857
709 // Skip event if corresponding counts cube pixel is outside the 858 // Skip event if corresponding counts cube pixel is outside the
710 // counts cube map range 859 // counts cube map range
711 - if (pixel.x() < -0.5 || pixel.x() > (m_counts.nx()-0.5) ||  
712 - pixel.y() < -0.5 || pixel.y() > (m_counts.ny()-0.5)) { 860 + if (pixel.x() < -0.5 || pixel.x() > pixel_x_max ||
  861 + pixel.y() < -0.5 || pixel.y() > pixel_y_max) {
713 num_outside_map++; 862 num_outside_map++;
714 continue; 863 continue;
715 } 864 }
@@ -726,7 +875,6 @@ GSkyMap ctbin::fill_cube(GCTAObservation* obs) @@ -726,7 +875,6 @@ GSkyMap ctbin::fill_cube(GCTAObservation* obs)
726 } 875 }
727 876
728 // Fill event in skymap 877 // Fill event in skymap
729 - //#pragma omp critical(ctbin_fill_cube)  
730 counts(pixel, iebin) += 1.0; 878 counts(pixel, iebin) += 1.0;
731 879
732 // Increment number of maps 880 // Increment number of maps
@@ -734,23 +882,6 @@ GSkyMap ctbin::fill_cube(GCTAObservation* obs) @@ -734,23 +882,6 @@ GSkyMap ctbin::fill_cube(GCTAObservation* obs)
734 882
735 } // endfor: looped over all events 883 } // endfor: looped over all events
736 884
737 - // Update time information  
738 - /*#pragma omp critical(ctbin_fill_cube)  
739 - {  
740 - // If GTI is empty then set its time reference from the observation.  
741 - // From then on we keep that time reference  
742 - if (m_gti.is_empty()) {  
743 - m_gti.reference(events->gti().reference());  
744 - }  
745 -  
746 - // Append GTIs  
747 - m_gti.extend(events->gti());  
748 -  
749 - // Update ontime and livetime  
750 - m_ontime += obs->ontime();  
751 - m_livetime += obs->livetime();  
752 - }*/  
753 -  
754 // Log filling results 885 // Log filling results
755 #pragma omp critical(ctbin_fill_cube) 886 #pragma omp critical(ctbin_fill_cube)
756 { 887 {
@@ -779,21 +910,10 @@ GSkyMap ctbin::fill_cube(GCTAObservation* obs) @@ -779,21 +910,10 @@ GSkyMap ctbin::fill_cube(GCTAObservation* obs)
779 * Sets the counts cube weights for all bins that are considered for the 910 * Sets the counts cube weights for all bins that are considered for the
780 * specific observation to unity. 911 * specific observation to unity.
781 ***************************************************************************/ 912 ***************************************************************************/
782 -GSkyMap ctbin::set_weights(GCTAObservation* obs) 913 +GSkyMap ctbin::set_weights(const GCTAObservation* obs)
783 { 914 {
784 - // Make sure that the observation holds a CTA event list. If this  
785 - // is not the case then throw an exception.  
786 - const GCTAEventList* events = dynamic_cast<const GCTAEventList*>  
787 - (obs->events());  
788 - if (events == NULL) {  
789 - std::string msg = "CTA Observation does not contain an event "  
790 - "list. An event list is needed to fill the "  
791 - "counts cube.";  
792 - throw GException::invalid_value(G_SET_WEIGHTS, msg);  
793 - }  
794 -  
795 // Get the RoI 915 // Get the RoI
796 - const GCTARoi& roi = events->roi(); 916 + const GCTARoi& roi = obs->roi();
797 917
798 // Check for RoI sanity 918 // Check for RoI sanity
799 if (!roi.is_valid()) { 919 if (!roi.is_valid()) {
@@ -804,19 +924,32 @@ GSkyMap ctbin::set_weights(GCTAObservation* obs) @@ -804,19 +924,32 @@ GSkyMap ctbin::set_weights(GCTAObservation* obs)
804 throw GException::invalid_value(G_SET_WEIGHTS, msg); 924 throw GException::invalid_value(G_SET_WEIGHTS, msg);
805 } 925 }
806 926
  927 + // Get the RoI centre and radius in radians
  928 + GSkyDir roi_centre = roi.centre().dir();
  929 + double roi_radius = roi.radius() * gammalib::deg2rad;
  930 +
807 // Get counts cube usage flags 931 // Get counts cube usage flags
808 - std::vector<bool> usage = cube_layer_usage(m_ebounds, events->ebounds()); 932 + std::vector<bool> usage = cube_layer_usage(m_ebounds, obs->ebounds());
  933 +
  934 + // Create weights cube
  935 + GSkyMap weights = create_cube(obs);
809 936
810 - // Initialise new empty weights map  
811 - GSkyMap weights(m_weights);  
812 - weights *= 0.; 937 + // Set sky direction cache availability flag
  938 + bool cache = (m_stack && m_dirs.size() == weights.npix());
813 939
814 // Loop over all pixels in counts cube 940 // Loop over all pixels in counts cube
815 - for (int pixel = 0; pixel < m_counts.npix(); ++pixel) { 941 + for (int pixel = 0; pixel < weights.npix(); ++pixel) {
816 942
817 // Skip pixel if it is outside the RoI 943 // Skip pixel if it is outside the RoI
818 - if (roi.centre().dir().dist_deg(m_dirs[pixel]) > roi.radius()) {  
819 - continue; 944 + if (cache) {
  945 + if (m_dirs[pixel].dist(roi_centre) > roi_radius) {
  946 + continue;
  947 + }
  948 + }
  949 + else {
  950 + if (roi_centre.dist(weights.inx2dir(pixel)) > roi_radius) {
  951 + continue;
  952 + }
820 } 953 }
821 954
822 // Loop over all energy layers of counts cube 955 // Loop over all energy layers of counts cube
@@ -828,7 +961,6 @@ GSkyMap ctbin::set_weights(GCTAObservation* obs) @@ -828,7 +961,6 @@ GSkyMap ctbin::set_weights(GCTAObservation* obs)
828 } 961 }
829 962
830 // Signal that bin was filled 963 // Signal that bin was filled
831 - //#pragma omp critical(ctbin_set_weights)  
832 weights(pixel, iebin) = 1.0; 964 weights(pixel, iebin) = 1.0;
833 965
834 } // endfor: looped over energy layers of counts cube 966 } // endfor: looped over energy layers of counts cube
@@ -851,8 +983,11 @@ GSkyMap ctbin::set_weights(GCTAObservation* obs) @@ -851,8 +983,11 @@ GSkyMap ctbin::set_weights(GCTAObservation* obs)
851 * information in case that a single CTA observation is provided to support 983 * information in case that a single CTA observation is provided to support
852 * binned analysis. 984 * binned analysis.
853 ***************************************************************************/ 985 ***************************************************************************/
854 -void ctbin::obs_cube(void) 986 +void ctbin::obs_cube_stacked(void)
855 { 987 {
  988 + // Set event cube in first slot
  989 + m_cubes[0] = GCTAEventCube(m_counts[0], m_weights[0], m_ebounds, m_gti);
  990 +
856 // If we have only a single CTA observation in the container, then 991 // If we have only a single CTA observation in the container, then
857 // keep that observation and just attach the event cube to it. Reset 992 // keep that observation and just attach the event cube to it. Reset
858 // the filename, otherwise we still will have the old event filename 993 // the filename, otherwise we still will have the old event filename
@@ -865,9 +1000,9 @@ void ctbin::obs_cube(void) @@ -865,9 +1000,9 @@ void ctbin::obs_cube(void)
865 1000
866 // Change the event type if we had an unbinned observation 1001 // Change the event type if we had an unbinned observation
867 if (obs->eventtype() == "EventList") { 1002 if (obs->eventtype() == "EventList") {
868 - 1003 +
869 // Assign cube to the observation 1004 // Assign cube to the observation
870 - obs->events(this->cube()); 1005 + obs->events(m_cubes[0]);
871 1006
872 } 1007 }
873 1008
@@ -877,7 +1012,8 @@ void ctbin::obs_cube(void) @@ -877,7 +1012,8 @@ void ctbin::obs_cube(void)
877 else { 1012 else {
878 1013
879 // Create empty counts cube 1014 // Create empty counts cube
880 - GCTAEventCube cube(m_counts, m_weights, m_ebounds, obs->gti()); 1015 + GCTAEventCube cube(m_counts[0], m_weights[0], m_ebounds,
  1016 + obs->gti());
881 1017
882 // Assign empty cube to observation 1018 // Assign empty cube to observation
883 obs->events(cube); 1019 obs->events(cube);
@@ -899,49 +1035,49 @@ void ctbin::obs_cube(void) @@ -899,49 +1035,49 @@ void ctbin::obs_cube(void)
899 // Allocate observation container 1035 // Allocate observation container
900 GObservations container; 1036 GObservations container;
901 1037
902 - // Allocate CTA observation.  
903 - GCTAObservation obs;  
904 -  
905 - // Attach event cube to CTA observation  
906 - obs.events(this->cube());  
907 -  
908 - // Compute average pointing direction for all CTA event lists  
909 - double ra = 0.0;  
910 - double dec = 0.0;  
911 - double number = 0.0;  
912 - for (int i = 0; i < m_obs.size(); ++i) {  
913 - GCTAObservation* cta = dynamic_cast<GCTAObservation*>(m_obs[i]);  
914 - if ((cta != NULL) && (cta->eventtype() == "EventList")) {  
915 - ra += cta->pointing().dir().ra();  
916 - dec += cta->pointing().dir().dec();  
917 - number += 1.0;  
918 - }  
919 - }  
920 - if (number > 0.0) {  
921 - ra /= number;  
922 - dec /= number;  
923 - }  
924 - GSkyDir dir;  
925 - dir.radec(ra, dec);  
926 - GCTAPointing pointing(dir);  
927 -  
928 - // Compute deadtime correction  
929 - double deadc = (m_ontime > 0.0) ? m_livetime / m_ontime : 0.0;  
930 -  
931 - // Set CTA observation attributes  
932 - obs.pointing(pointing);  
933 - obs.ra_obj(dir.ra_deg()); //!< Dummy  
934 - obs.dec_obj(dir.dec_deg()); //!< Dummy  
935 - obs.ontime(m_ontime);  
936 - obs.livetime(m_livetime);  
937 - obs.deadc(deadc);  
938 -  
939 - // Append CTA observation  
940 - container.append(obs); 1038 + // Allocate CTA observation.
  1039 + GCTAObservation obs;
  1040 +
  1041 + // Attach event cube to CTA observation
  1042 + obs.events(m_cubes[0]);
  1043 +
  1044 + // Compute average pointing direction for all CTA event lists
  1045 + double ra = 0.0;
  1046 + double dec = 0.0;
  1047 + double number = 0.0;
  1048 + for (int i = 0; i < m_obs.size(); ++i) {
  1049 + GCTAObservation* cta = dynamic_cast<GCTAObservation*>(m_obs[i]);
  1050 + if ((cta != NULL) && (cta->eventtype() == "EventList")) {
  1051 + ra += cta->pointing().dir().ra();
  1052 + dec += cta->pointing().dir().dec();
  1053 + number += 1.0;
  1054 + }
  1055 + }
  1056 + if (number > 0.0) {
  1057 + ra /= number;
  1058 + dec /= number;
  1059 + }
  1060 + GSkyDir dir;
  1061 + dir.radec(ra, dec);
  1062 + GCTAPointing pointing(dir);
  1063 +
  1064 + // Compute deadtime correction
  1065 + double deadc = (m_ontime > 0.0) ? m_livetime / m_ontime : 0.0;
  1066 +
  1067 + // Set CTA observation attributes
  1068 + obs.pointing(pointing);
  1069 + obs.ra_obj(dir.ra_deg()); //!< Dummy
  1070 + obs.dec_obj(dir.dec_deg()); //!< Dummy
  1071 + obs.ontime(m_ontime);
  1072 + obs.livetime(m_livetime);
  1073 + obs.deadc(deadc);
941 1074
942 // Set models in observation container 1075 // Set models in observation container
943 container.models(m_obs.models()); 1076 container.models(m_obs.models());
944 1077
  1078 + // Append CTA observation
  1079 + container.append(obs);
  1080 +
945 // Copy over all remaining non-CTA observations 1081 // Copy over all remaining non-CTA observations
946 for (int i = 0; i < m_obs.size(); ++i) { 1082 for (int i = 0; i < m_obs.size(); ++i) {
947 GCTAObservation* obs = dynamic_cast<GCTAObservation*>(m_obs[i]); 1083 GCTAObservation* obs = dynamic_cast<GCTAObservation*>(m_obs[i]);
src/ctbin/ctbin.hpp
1 /*************************************************************************** 1 /***************************************************************************
2 * ctbin - Event binning tool * 2 * ctbin - Event binning tool *
3 * ----------------------------------------------------------------------- * 3 * ----------------------------------------------------------------------- *
4 - * copyright (C) 2010-2017 by Juergen Knoedlseder * 4 + * copyright (C) 2010-2018 by Juergen Knoedlseder *
5 * ----------------------------------------------------------------------- * 5 * ----------------------------------------------------------------------- *
6 * * 6 * *
7 * This program is free software: you can redistribute it and/or modify * 7 * This program is free software: you can redistribute it and/or modify *
@@ -81,10 +81,10 @@ public: @@ -81,10 +81,10 @@ public:
81 // Methods 81 // Methods
82 void clear(void); 82 void clear(void);
83 void run(void); 83 void run(void);
  84 + int cubes(void) const;
  85 + const GCTAEventCube& cube(const int& index = 0) const;
84 void save(void); 86 void save(void);
85 void publish(const std::string& name = ""); 87 void publish(const std::string& name = "");
86 - const GCTAEventCube& cube(void) const;  
87 - const GCTAEventCube& cube(const size_t& index) const;  
88 88
89 protected: 89 protected:
90 // Protected methods 90 // Protected methods
@@ -92,60 +92,46 @@ protected: @@ -92,60 +92,46 @@ protected:
92 void copy_members(const ctbin& app); 92 void copy_members(const ctbin& app);
93 void free_members(void); 93 void free_members(void);
94 void get_parameters(void); 94 void get_parameters(void);
95 - GSkyMap fill_cube(GCTAObservation* obs);  
96 - GSkyMap set_weights(GCTAObservation* obs);  
97 - void obs_cube(void); 95 + void init_sky_dir_cache(void);
  96 + GSkyMap create_cube(const GCTAObservation* obs);
  97 + GSkyMap fill_cube(const GCTAObservation* obs);
  98 + GSkyMap set_weights(const GCTAObservation* obs);
  99 + void obs_cube_stacked(void);
98 100
99 // User parameters 101 // User parameters
100 - GFilename m_outcube; //!< Output counts map file name  
101 - bool m_usepnt; //!< Use pointing instead of xref/yref parameters  
102 - bool m_stack; //!< Output one stacked cube or multiple cubes  
103 - std::string m_prefix; //!< Prefix for output path of multiple cubes  
104 - bool m_publish; //!< Publish counts cube?  
105 - GChatter m_chatter; //!< Chattiness 102 + GFilename m_outcube; //!< Output counts map file name
  103 + bool m_usepnt; //!< Use pointing instead of xref/yref parameters
  104 + bool m_stack; //!< Output one stacked cube or multiple cubes
  105 + std::string m_prefix; //!< Prefix for output path of multiple cubes
  106 + bool m_publish; //!< Publish counts cube?
  107 + GChatter m_chatter; //!< Chattiness
106 108
107 // Protected members 109 // Protected members
108 - GSkyMap m_counts; //!< Event cube counts  
109 - GSkyMap m_weights; //!< Event cube weights  
110 - GEbounds m_ebounds; //!< Energy boundaries  
111 - GGti m_gti; //!< Good time intervals  
112 - GCTAEventCube m_cube; //!< Events cube (for cube() method)  
113 - double m_ontime; //!< Total ontime  
114 - double m_livetime; //!< Total livetime  
115 - std::vector<GCTAEventCube> m_cubes; //!< Event cubes (for cube() method)  
116 - std::vector<std::string> m_ids; //!< Observation ids 110 + std::vector<GCTAEventCube> m_cubes; //!< Event cubes
  111 + std::vector<GSkyMap> m_counts; //!< List of event cube counts
  112 + std::vector<GSkyMap> m_weights; //!< List of event cube weights
  113 + std::vector<std::string> m_ids; //!< List of observation ids
  114 + GEbounds m_ebounds; //!< Energy boundaries
  115 + GGti m_gti; //!< Stacked Good time intervals
  116 + double m_ontime; //!< Total ontime
  117 + double m_livetime; //!< Total livetime
117 118
118 // Cache members 119 // Cache members
119 - std::vector<GSkyDir> m_dirs; //!< Cached GSkyDir for each pixel in m_counts 120 + std::vector<GSkyDir> m_dirs; //!< Cached GSkyDir for all pixels
120 }; 121 };
121 122
122 123
123 /***********************************************************************//** 124 /***********************************************************************//**
124 - * @brief Return event cube 125 + * @brief Return number of event cubes
125 * 126 *
126 - * @return Reference to event cube 127 + * @return Number of event cubes.
127 * 128 *
128 - * Returns a reference to the event cube. 129 + * Returns number of event cubes.
129 ***************************************************************************/ 130 ***************************************************************************/
130 inline 131 inline
131 -const GCTAEventCube& ctbin::cube(void) const 132 +int ctbin::cubes(void) const
132 { 133 {
133 - return m_cube;  
134 -}  
135 -  
136 -  
137 -/***********************************************************************//**  
138 - * @brief Return event cube at index  
139 - *  
140 - * @param[in] index Index of cube  
141 - * @return Reference to event cube  
142 - *  
143 - * Returns a reference to the event cube at the given index.  
144 - ***************************************************************************/  
145 -inline  
146 -const GCTAEventCube& ctbin::cube(const size_t& index) const  
147 -{  
148 - return m_cubes[index]; 134 + return m_cubes.size();
149 } 135 }
150 136
151 #endif /* CTBIN_HPP */ 137 #endif /* CTBIN_HPP */
src/ctbin/ctbin.par
@@ -18,7 +18,7 @@ @@ -18,7 +18,7 @@
18 #=================== 18 #===================
19 inobs, f, a, events.fits,,, "Input event list or observation definition XML file" 19 inobs, f, a, events.fits,,, "Input event list or observation definition XML file"
20 outcube, f, a, cntcube.fits,,, "Output counts cube file" 20 outcube, f, a, cntcube.fits,,, "Output counts cube file"
21 -stack, b, h, yes,,, "Stack events to one cube?" 21 +stack, b, h, yes,,, "Stack events into one cube?"
22 prefix, s, h, binned_,,, "Prefix for counts cubes in observation definition XML file" 22 prefix, s, h, binned_,,, "Prefix for counts cubes in observation definition XML file"
23 23
24 # 24 #
test/test_ctbin.py
@@ -165,7 +165,7 @@ class Test(test): @@ -165,7 +165,7 @@ class Test(test):
165 # cube 165 # cube
166 self.test_value(binning.obs().size(), 0, 166 self.test_value(binning.obs().size(), 0,
167 'Check that empty ctbin has an empty observation container') 167 'Check that empty ctbin has an empty observation container')
168 - self.test_value(binning.cube().size(), 0, 168 + self.test_value(binning.cubes(), 0,
169 'Check that empty ctbin has an empty counts cube') 169 'Check that empty ctbin has an empty counts cube')
170 170
171 # Check that saving does not nothing 171 # Check that saving does not nothing
@@ -220,11 +220,13 @@ class Test(test): @@ -220,11 +220,13 @@ class Test(test):
220 cpy_bin.logFileOpen() 220 cpy_bin.logFileOpen()
221 cpy_bin.run() 221 cpy_bin.run()
222 222
223 - # Check content of observation container and counts cube. Now an empty  
224 - # event cube is expected since on input the observation is binned, and  
225 - # any binned observation will be skipped.  
226 - self._check_observation(cpy_bin, 0)  
227 - self._check_cube(cpy_bin.cube(), 0) 223 + # Check content of observation container and number of counts cubes.
  224 + # There should be a single binned observation in the observation
  225 + # container, which is the one that was produced in the run before.
  226 + # Since ctbin finds no unbinned observation in the container, the
  227 + # number of cubes should be zero.
  228 + self._check_observation(cpy_bin, 245)
  229 + self.test_value(cpy_bin.cubes(), 0, 'Check that there are no counts cubes')
228 230
229 # Save counts cube 231 # Save counts cube
230 binning.save() 232 binning.save()
@@ -240,7 +242,7 @@ class Test(test): @@ -240,7 +242,7 @@ class Test(test):
240 # counts cube 242 # counts cube
241 self.test_value(binning.obs().size(), 0, 243 self.test_value(binning.obs().size(), 0,
242 'Check that empty ctbin has an empty observation container') 244 'Check that empty ctbin has an empty observation container')
243 - self.test_value(binning.cube().size(), 0, 245 + self.test_value(binning.cubes(), 0,
244 'Check that empty ctbin has an empty counts cube') 246 'Check that empty ctbin has an empty counts cube')
245 247
246 # Prepare observation container for stacking of events into a 248 # Prepare observation container for stacking of events into a