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 3 * Version 1.6.0 released
4 4 ========================
5 5  
  6 + Enable ctbin to produce counts cubes for a joint binned analysis (#2714)
6 7 Add code generator (#2709)
7 8 Add csbkgmodel script (#2694)
8 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 55  
56 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 66 ctbin now accouts for varying energy thresholds when computing the weight
59 67 array (#2685).
60 68  
... ...
doc/source/admin/release_history/1.6.rst
... ... @@ -41,6 +41,8 @@ Bug fixes
41 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 46 * [`2685 <https://cta-redmine.irap.omp.eu/issues/2685>`_] -
45 47 Add ``ctbkgcube::cntcube()`` method
46 48 Account for varying energy thresholds in :ref:`ctbin` weight computation
... ...
pyext/ctbin.i
1 1 /***************************************************************************
2 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 7 * This program is free software: you can redistribute it and/or modify *
... ... @@ -46,10 +46,10 @@ public:
46 46 // Methods
47 47 void clear(void);
48 48 void run(void);
  49 + int cubes(void) const;
  50 + const GCTAEventCube& cube(const int& index = 0) const;
49 51 void save(void);
50 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 38 #endif
39 39  
40 40 /* __ Method name definitions ____________________________________________ */
  41 +#define G_CUBE "ctbin::cube(int&)"
41 42 #define G_GET_PARAMETERS "ctbin::get_parameters()"
42 43 #define G_FILL_CUBE "ctbin::fill_cube(GCTAObservation*)"
43 44 #define G_SET_WEIGHTS "ctbin::set_weights(GCTAObservation*)"
... ... @@ -234,6 +235,9 @@ void ctbin::run(void)
234 235 // Get task parameters
235 236 get_parameters();
236 237  
  238 + // Initialise sky direction cache for stacking
  239 + init_sky_dir_cache();
  240 +
237 241 // Write input observation container into logger
238 242 log_observations(NORMAL, m_obs, "Input observation");
239 243  
... ... @@ -255,33 +259,33 @@ void ctbin::run(void)
255 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 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 282 // Loop over all unbinned CTA observations in the container
268 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 286 // Get pointer to observation
272 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 289 // Fill the cube
286 290 GSkyMap counts = fill_cube(obs);
287 291  
... ... @@ -291,88 +295,118 @@ void ctbin::run(void)
291 295 // Dispose events to free memory
292 296 obs->dispose_events();
293 297  
  298 + // Post process cubes
294 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 343 if (m_gti.is_empty()) {
312   - m_gti.reference(events->gti().reference());
  344 + m_gti.reference(gti.reference());
313 345 }
314   -
  346 +
315 347 // Append GTIs
316   - m_gti.extend(events->gti());
317   -
  348 + m_gti.extend(gti);
  349 +
318 350 // Update ontime and livetime
319 351 m_ontime += obs->ontime();
320 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 411 // Write resulting observation container into logger
378 412 log_observations(NORMAL, m_obs, "Binned observation");
... ... @@ -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 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 455 void ctbin::save(void)
396 456 {
397 457 // Write header
398 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 527 // Return
458 528 return;
459 529 }
... ... @@ -478,8 +548,10 @@ void ctbin::publish(const std::string&amp; name)
478 548 // Write counts cube name into logger
479 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 556 // Return
485 557 return;
... ... @@ -505,15 +577,14 @@ void ctbin::init_members(void)
505 577 m_chatter = static_cast<GChatter>(2);
506 578  
507 579 // Initialise protected members
  580 + m_cubes.clear();
508 581 m_counts.clear();
509 582 m_weights.clear();
  583 + m_ids.clear();
510 584 m_ebounds.clear();
511 585 m_gti.clear();
512   - m_cube.clear();
513 586 m_ontime = 0.0;
514 587 m_livetime = 0.0;
515   - m_cubes.clear();
516   - m_ids.clear();
517 588  
518 589 // Initialise cache members
519 590 m_dirs.clear();
... ... @@ -538,15 +609,14 @@ void ctbin::copy_members(const ctbin&amp; app)
538 609 m_chatter = app.m_chatter;
539 610  
540 611 // Copy protected members
  612 + m_cubes = app.m_cubes;
541 613 m_counts = app.m_counts;
542 614 m_weights = app.m_weights;
  615 + m_ids = app.m_ids;
543 616 m_ebounds = app.m_ebounds;
544 617 m_gti = app.m_gti;
545   - m_cube = app.m_cube;
546 618 m_ontime = app.m_ontime;
547 619 m_livetime = app.m_livetime;
548   - m_cubes = app.m_cubes;
549   - m_ids = app.m_ids;
550 620  
551 621 // Copy cache members
552 622 m_dirs = app.m_dirs;
... ... @@ -581,14 +651,8 @@ void ctbin::get_parameters(void)
581 651 // information and do not accept counts cubes.
582 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 657 // Get energy boundaries
594 658 m_ebounds = cube.ebounds();
... ... @@ -608,13 +672,6 @@ void ctbin::get_parameters(void)
608 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 675 // Set number of OpenMP threads
619 676 #ifdef _OPENMP
620 677 int nthreads = (*this)["nthreads"].integer();
... ... @@ -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 781 * @brief Fill events into counts cube
636 782 *
637 783 * @param[in] obs CTA observation.
... ... @@ -641,10 +787,10 @@ void ctbin::get_parameters(void)
641 787 *
642 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 794 const GCTAEventList* events = dynamic_cast<const GCTAEventList*>
649 795 (obs->events());
650 796 if (events == NULL) {
... ... @@ -655,7 +801,7 @@ GSkyMap ctbin::fill_cube(GCTAObservation* obs)
655 801 }
656 802  
657 803 // Get the RoI
658   - const GCTARoi& roi = events->roi();
  804 + const GCTARoi& roi = obs->roi();
659 805  
660 806 // Check for RoI sanity
661 807 if (!roi.is_valid()) {
... ... @@ -667,7 +813,7 @@ GSkyMap ctbin::fill_cube(GCTAObservation* obs)
667 813 }
668 814  
669 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 818 // Initialise binning statistics
673 819 int num_outside_roi = 0;
... ... @@ -676,9 +822,12 @@ GSkyMap ctbin::fill_cube(GCTAObservation* obs)
676 822 int num_outside_ebds = 0;
677 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 832 // Fill counts sky map
684 833 for (int i = 0; i < events->size(); ++i) {
... ... @@ -699,7 +848,7 @@ GSkyMap ctbin::fill_cube(GCTAObservation* obs)
699 848 // Determine sky pixel
700 849 GSkyPixel pixel;
701 850 try {
702   - pixel = m_counts.dir2pix(dir);
  851 + pixel = counts.dir2pix(dir);
703 852 }
704 853 catch (std::exception &e) {
705 854 num_invalid_wcs++;
... ... @@ -708,8 +857,8 @@ GSkyMap ctbin::fill_cube(GCTAObservation* obs)
708 857  
709 858 // Skip event if corresponding counts cube pixel is outside the
710 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 862 num_outside_map++;
714 863 continue;
715 864 }
... ... @@ -726,7 +875,6 @@ GSkyMap ctbin::fill_cube(GCTAObservation* obs)
726 875 }
727 876  
728 877 // Fill event in skymap
729   - //#pragma omp critical(ctbin_fill_cube)
730 878 counts(pixel, iebin) += 1.0;
731 879  
732 880 // Increment number of maps
... ... @@ -734,23 +882,6 @@ GSkyMap ctbin::fill_cube(GCTAObservation* obs)
734 882  
735 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 885 // Log filling results
755 886 #pragma omp critical(ctbin_fill_cube)
756 887 {
... ... @@ -779,21 +910,10 @@ GSkyMap ctbin::fill_cube(GCTAObservation* obs)
779 910 * Sets the counts cube weights for all bins that are considered for the
780 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 915 // Get the RoI
796   - const GCTARoi& roi = events->roi();
  916 + const GCTARoi& roi = obs->roi();
797 917  
798 918 // Check for RoI sanity
799 919 if (!roi.is_valid()) {
... ... @@ -804,19 +924,32 @@ GSkyMap ctbin::set_weights(GCTAObservation* obs)
804 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 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 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 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 955 // Loop over all energy layers of counts cube
... ... @@ -828,7 +961,6 @@ GSkyMap ctbin::set_weights(GCTAObservation* obs)
828 961 }
829 962  
830 963 // Signal that bin was filled
831   - //#pragma omp critical(ctbin_set_weights)
832 964 weights(pixel, iebin) = 1.0;
833 965  
834 966 } // endfor: looped over energy layers of counts cube
... ... @@ -851,8 +983,11 @@ GSkyMap ctbin::set_weights(GCTAObservation* obs)
851 983 * information in case that a single CTA observation is provided to support
852 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 991 // If we have only a single CTA observation in the container, then
857 992 // keep that observation and just attach the event cube to it. Reset
858 993 // the filename, otherwise we still will have the old event filename
... ... @@ -865,9 +1000,9 @@ void ctbin::obs_cube(void)
865 1000  
866 1001 // Change the event type if we had an unbinned observation
867 1002 if (obs->eventtype() == "EventList") {
868   -
  1003 +
869 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 1012 else {
878 1013  
879 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 1018 // Assign empty cube to observation
883 1019 obs->events(cube);
... ... @@ -899,49 +1035,49 @@ void ctbin::obs_cube(void)
899 1035 // Allocate observation container
900 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 1075 // Set models in observation container
943 1076 container.models(m_obs.models());
944 1077  
  1078 + // Append CTA observation
  1079 + container.append(obs);
  1080 +
945 1081 // Copy over all remaining non-CTA observations
946 1082 for (int i = 0; i < m_obs.size(); ++i) {
947 1083 GCTAObservation* obs = dynamic_cast<GCTAObservation*>(m_obs[i]);
... ...
src/ctbin/ctbin.hpp
1 1 /***************************************************************************
2 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 7 * This program is free software: you can redistribute it and/or modify *
... ... @@ -81,10 +81,10 @@ public:
81 81 // Methods
82 82 void clear(void);
83 83 void run(void);
  84 + int cubes(void) const;
  85 + const GCTAEventCube& cube(const int& index = 0) const;
84 86 void save(void);
85 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 89 protected:
90 90 // Protected methods
... ... @@ -92,60 +92,46 @@ protected:
92 92 void copy_members(const ctbin& app);
93 93 void free_members(void);
94 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 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 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 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 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 137 #endif /* CTBIN_HPP */
... ...
src/ctbin/ctbin.par
... ... @@ -18,7 +18,7 @@
18 18 #===================
19 19 inobs, f, a, events.fits,,, "Input event list or observation definition XML file"
20 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 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 165 # cube
166 166 self.test_value(binning.obs().size(), 0,
167 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 169 'Check that empty ctbin has an empty counts cube')
170 170  
171 171 # Check that saving does not nothing
... ... @@ -220,11 +220,13 @@ class Test(test):
220 220 cpy_bin.logFileOpen()
221 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 231 # Save counts cube
230 232 binning.save()
... ... @@ -240,7 +242,7 @@ class Test(test):
240 242 # counts cube
241 243 self.test_value(binning.obs().size(), 0,
242 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 246 'Check that empty ctbin has an empty counts cube')
245 247  
246 248 # Prepare observation container for stacking of events into a
... ...