GammaLib  2.1.0.dev
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
GCTACubeEdisp.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  * GCTACubeEdisp.cpp - CTA cube analysis energy dispersion class *
3  * ----------------------------------------------------------------------- *
4  * copyright (C) 2016-2020 by Michael Mayer *
5  * ----------------------------------------------------------------------- *
6  * *
7  * This program is free software: you can redistribute it and/or modify *
8  * it under the terms of the GNU General Public License as published by *
9  * the Free Software Foundation, either version 3 of the License, or *
10  * (at your option) any later version. *
11  * *
12  * This program is distributed in the hope that it will be useful, *
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
15  * GNU General Public License for more details. *
16  * *
17  * You should have received a copy of the GNU General Public License *
18  * along with this program. If not, see <http://www.gnu.org/licenses/>. *
19  * *
20  ***************************************************************************/
21 /**
22  * @file GCTACubeEdisp.cpp
23  * @brief CTA cube analysis energy dispersion class implementation
24  * @author Michael Mayer
25  */
26 
27 /* __ Includes ___________________________________________________________ */
28 #ifdef HAVE_CONFIG_H
29 #include <config.h>
30 #endif
31 #include "GTools.hpp"
32 #include "GMath.hpp"
33 #include "GLog.hpp"
34 #include "GFits.hpp"
35 #include "GFitsImage.hpp"
36 #include "GFitsTable.hpp"
37 #include "GObservations.hpp"
38 #include "GSkyRegionCircle.hpp"
39 #include "GCTACubeEdisp.hpp"
40 #include "GCTAObservation.hpp"
41 #include "GCTAResponseIrf.hpp"
42 #include "GCTAEventList.hpp"
43 #include "GCTAEventCube.hpp"
44 
45 /* __ Method name definitions ____________________________________________ */
46 #define G_CONSTRUCTOR1 "GCTACubeEdisp(GCTAEventCube&, double&, int&)"
47 #define G_CONSTRUCTOR2 "GCTACubeEdisp(std::string&, std::string&, double&, "\
48  "double&, double&, double&, int&, int&, GEbounds&, "\
49  "double&, int&)"
50 #define G_EBOUNDS "GCTACubeEdisp::ebounds(GEnergy&)"
51 #define G_FILL_CUBE "GCTACubeEdisp::fill_cube(GCTAObservation&, GSkyMap*, "\
52  "GLog*)"
53 
54 /* __ Macros _____________________________________________________________ */
55 
56 /* __ Coding definitions _________________________________________________ */
57 
58 /* __ Debug definitions __________________________________________________ */
59 
60 /* __ Constants __________________________________________________________ */
61 
62 
63 /*==========================================================================
64  = =
65  = Constructors/destructors =
66  = =
67  ==========================================================================*/
68 
69 /***********************************************************************//**
70  * @brief Void constructor
71  *
72  * Constructs an empty energy dispersion cube.
73  ***************************************************************************/
75 {
76  // Initialise class members
77  init_members();
78 
79  // Return
80  return;
81 }
82 
83 
84 /***********************************************************************//**
85  * @brief Copy constructor
86  *
87  * @param[in] cube Energy dispersion cube.
88  *
89  * Constructs an energy dispersion cube by copying another energy dispersion
90  * cube.
91  ***************************************************************************/
93 {
94  // Initialise class members
95  init_members();
96 
97  // Copy members
98  copy_members(cube);
99 
100  // Return
101  return;
102 }
103 
104 
105 /***********************************************************************//**
106  * @brief File constructor
107  *
108  * @param[in] filename Energy dispersion cube filename.
109  *
110  * Constructs an energy dispersion cube by loading the energy dispersion
111  * information from an energy dispersion cube FITS file. See the load()
112  * method for details about the format of the FITS file.
113  ***************************************************************************/
115 {
116  // Initialise class members
117  init_members();
118 
119  // Load Edisp cube from file
120  load(filename);
121 
122  // Return
123  return;
124 }
125 
126 
127 /***********************************************************************//**
128  * @brief Event cube constructor
129  *
130  * @param[in] cube Event cube.
131  * @param[in] mmax Maximum energy migration.
132  * @param[in] nmbins Number of migration bins (2 ...).
133  *
134  * @exception GException::invalid_argument
135  * Maximum energy migration or number of migration bins invalid.
136  *
137  * Construct an energy dispersion cube with all elements set to zero using
138  * the same binning and sky projection that is used for an event cube.
139  ***************************************************************************/
140 GCTACubeEdisp::GCTACubeEdisp(const GCTAEventCube& cube, const double& mmax,
141  const int& nmbins)
142 {
143  // Throw an exception if the number of migra bins is invalid or the
144  // maximum migration is not positive
145  if (nmbins < 2) {
146  std::string msg = "Number "+gammalib::str(nmbins)+" of migration "
147  "bins is smaller than 2. Please request at least "
148  "2 migration bins.";
150  }
151  if (mmax <= 0.0) {
152  std::string msg = "Maximum migration "+gammalib::str(mmax)+" is not "
153  "positive. Please specify a positive maximum "
154  "energy migration.";
156  }
157 
158  // Initialise class members
159  init_members();
160 
161  // Store energy boundaries
162  m_energies.set(cube.ebounds());
163 
164  // Set energy node array used for interpolation
165  set_eng_axis();
166 
167  // Set migration axis used for interpolation
168  set_migras(mmax, nmbins);
169 
170  // Compute number of sky maps
171  int nmaps = m_energies.size() * m_migras.size();
172 
173  // Set energy dispersion cube to event cube
174  m_cube = cube.counts();
175 
176  // Set appropriate number of skymaps
177  m_cube.nmaps(nmaps);
178 
179  // Set cube shape
181 
182  // Set all energy dispersion cube pixels to zero as we want to have
183  // a clean map upon construction
184  m_cube = 0.0;
185 
186  // Return
187  return;
188 }
189 
190 
191 /***********************************************************************//**
192  * @brief Energy dispersion cube constructor
193  *
194  * @param[in] wcs World Coordinate System.
195  * @param[in] coords Coordinate System (CEL or GAL).
196  * @param[in] x X coordinate of sky map centre (deg).
197  * @param[in] y Y coordinate of sky map centre (deg).
198  * @param[in] dx Pixel size in x direction at centre (deg/pixel).
199  * @param[in] dy Pixel size in y direction at centre (deg/pixel).
200  * @param[in] nx Number of pixels in x direction.
201  * @param[in] ny Number of pixels in y direction.
202  * @param[in] energies True energies.
203  * @param[in] mmax Maximum energy migration.
204  * @param[in] nmbins Number of migration bins (2 ...).
205  *
206  * @exception GException::invalid_argument
207  * Maximum energy migration or number of migration bins invalid.
208  *
209  * Constructs an energy dispersion cube by specifying the sky map grid and
210  * the energies.
211  ***************************************************************************/
212 GCTACubeEdisp::GCTACubeEdisp(const std::string& wcs,
213  const std::string& coords,
214  const double& x,
215  const double& y,
216  const double& dx,
217  const double& dy,
218  const int& nx,
219  const int& ny,
220  const GEnergies& energies,
221  const double& mmax,
222  const int& nmbins)
223 {
224  // Throw an exception if the number of migra bins is invalid or the
225  // maximum migration is not positive
226  if (nmbins < 2) {
227  std::string msg = "Number "+gammalib::str(nmbins)+" of migration "
228  "bins is smaller than 2. Please request at least "
229  "2 migration bins.";
231  }
232  if (mmax <= 0.0) {
233  std::string msg = "Maximum migration "+gammalib::str(mmax)+" is not "
234  "positive. Please specify a positive maximum "
235  "energy migration.";
237  }
238 
239  // Initialise class members
240  init_members();
241 
242  // Store true energies
244 
245  // Set energy node array used for interpolation
246  set_eng_axis();
247 
248  // Set migration axis used for interpolation
249  set_migras(mmax, nmbins);
250 
251  // Compute number of sky maps
252  int nmaps = m_energies.size() * m_migras.size();
253 
254  // Create sky map
255  m_cube = GSkyMap(wcs, coords, x, y, dx, dy, nx, ny, nmaps);
256 
257  // Set cube shape
259 
260  // Return
261  return;
262 }
263 
264 
265 /***********************************************************************//**
266  * @brief Destructor
267  *
268  * Destructs energy dispersion cube.
269  ***************************************************************************/
271 {
272  // Free members
273  free_members();
274 
275  // Return
276  return;
277 }
278 
279 
280 /*==========================================================================
281  = =
282  = Operators =
283  = =
284  ==========================================================================*/
285 
286 /***********************************************************************//**
287  * @brief Assignment operator
288  *
289  * @param[in] cube Energy dispersion cube.
290  * @return Energy dispersion cube.
291  *
292  * Assigns energy dispersion cube.
293  ***************************************************************************/
295 {
296  // Execute only if object is not identical
297  if (this != &cube) {
298 
299  // Free members
300  free_members();
301 
302  // Initialise private members
303  init_members();
304 
305  // Copy members
306  copy_members(cube);
307 
308  } // endif: object was not identical
309 
310  // Return this object
311  return *this;
312 }
313 
314 
315 /***********************************************************************//**
316  * @brief Return energy dispersion in units of MeV\f$^{-1}\f$
317  *
318  * @param[in] ereco Reconstructed event energy.
319  * @param[in] etrue True photon energy.
320  * @param[in] dir Coordinate of the true photon position.
321  * @return Energy dispersion (MeV\f$^{-1}\f$)
322  *
323  * Returns the energy dispersion for a given energy migration (reconstructed
324  * over true energy), true photon energy, and sky direction in units of
325  * MeV\f$^{-1}\f$.
326  ***************************************************************************/
327 double GCTACubeEdisp::operator()(const GEnergy& ereco,
328  const GEnergy& etrue,
329  const GSkyDir& dir) const
330 {
331  // Update indices and weighting factors for interpolation
332  update(ereco, etrue);
333 
334  // Perform bi-linear interpolation
335  double edisp = m_wgt1 * m_cube(dir, m_inx1) +
336  m_wgt2 * m_cube(dir, m_inx2) +
337  m_wgt3 * m_cube(dir, m_inx3) +
338  m_wgt4 * m_cube(dir, m_inx4);
339 
340  // Make sure that energy dispersion does not become negative
341  if (edisp < 0.0) {
342  edisp = 0.0;
343  }
344 
345  // Return energy dispersion
346  return edisp;
347 }
348 
349 
350 /*==========================================================================
351  = =
352  = Public methods =
353  = =
354  ==========================================================================*/
355 
356 /***********************************************************************//**
357  * @brief Clear energy dispersion cube
358  *
359  * Clears energy dispersion cube.
360  ***************************************************************************/
362 {
363  // Free class members
364  free_members();
365 
366  // Initialise members
367  init_members();
368 
369  // Return
370  return;
371 }
372 
373 
374 /***********************************************************************//**
375  * @brief Clone energy dispersion cube
376  *
377  * @return Pointer to deep copy of energy dispersion cube.
378  ***************************************************************************/
380 {
381  return new GCTACubeEdisp(*this);
382 }
383 
384 
385 /***********************************************************************//**
386  * @brief Set energy dispersion cube for one CTA observation
387  *
388  * @param[in] obs CTA observation.
389  *
390  * Sets the energy dispersion cube for one CTA observation.
391  ***************************************************************************/
393 {
394  // Clear energy dispersion cube
395  clear_cube();
396 
397  // Fill energy dispersion cube
398  fill_cube(obs);
399 
400  // Return
401  return;
402 }
403 
404 
405 /***********************************************************************//**
406  * @brief Fill energy dispersion cube from observation container
407  *
408  * @param[in] obs Observation container.
409  * @param[in] log Pointer towards logger.
410  *
411  * Sets the energy dispersion cube from all CTA observations in the
412  * observation container.
413  ***************************************************************************/
415 {
416  // Clear energy dispersion cube
417  clear_cube();
418 
419  // Initialise skymap for exposure weight accumulation
420  GSkyMap exposure(m_cube);
421 
422  // Loop over all observations in container
423  for (int i = 0; i < obs.size(); ++i) {
424 
425  // Get observation and continue only if it is a CTA observation
426  const GCTAObservation* cta = dynamic_cast<const GCTAObservation*>
427  (obs[i]);
428 
429  // Skip observation if it's not CTA
430  if (cta == NULL) {
431  if (log != NULL) {
432  *log << "Skipping ";
433  *log << obs[i]->instrument();
434  *log << " observation ";
435  *log << "\"" << obs[i]->name() << "\"";
436  *log << " (id=" << obs[i]->id() << ")" << std::endl;
437  }
438  continue;
439  }
440 
441  // Skip observation if we have a binned observation
442  if (cta->eventtype() == "CountsCube") {
443  if (log != NULL) {
444  *log << "Skipping binned ";
445  *log << cta->instrument();
446  *log << " observation ";
447  *log << "\"" << cta->name() << "\"";
448  *log << " (id=" << cta->id() << ")" << std::endl;
449  }
450  continue;
451  }
452 
453  // Fill exposure cube cube
454  fill_cube(*cta, &exposure, log);
455 
456  } // endfor: looped over observations
457 
458  // Compute mean energy dispersion cube by dividing through the weights
459  for (int pixel = 0; pixel < m_cube.npix(); ++pixel) {
460  for (int iebin = 0; iebin < m_energies.size(); ++iebin) {
461  if (exposure(pixel, iebin) > 0.0) {
462  double norm = 1.0 / exposure(pixel, iebin);
463  for (int imigra = 0; imigra < m_migras.size(); ++imigra) {
464  int imap = offset(imigra, iebin);
465  m_cube(pixel, imap) *= norm;
466  }
467  }
468  else {
469  for (int imigra = 0; imigra < m_migras.size(); ++imigra) {
470  int imap = offset(imigra, iebin);
471  m_cube(pixel, imap) = 0.0;
472  }
473  }
474  }
475  }
476 
477  // Return
478  return;
479 }
480 
481 
482 /***********************************************************************//**
483  * @brief Read energy dispersion cube from FITS file
484  *
485  * @param[in] fits FITS file.
486  *
487  * Reads the energy dispersion cube from a FITS file. The energy dispersion
488  * cube values are expected in the primary extension of the FITS file, the
489  * true energy boundaries are expected in the `ENERGIES` extension, and the
490  * migration values are expected in the `MIGRAS` extension.
491  ***************************************************************************/
492 void GCTACubeEdisp::read(const GFits& fits)
493 {
494  // Clear object
495  clear();
496 
497  // Get HDUs
498  const GFitsImage& hdu_edispcube = *fits.image("Primary");
499  const GFitsTable& hdu_energies = *fits.table(gammalib::extname_energies);
500  const GFitsTable& hdu_migras = *fits.table(gammalib::extname_cta_migras);
501 
502  // Read energy dispersion cube
503  m_cube.read(hdu_edispcube);
504 
505  // Read true energy nodes
506  m_energies.read(hdu_energies);
507 
508  // Read migration nodes
509  m_migras.read(hdu_migras);
510 
511  // Set energy node array used for interpolation
512  set_eng_axis();
513 
514  // Compute true energy boundaries
515  compute_ebounds();
516 
517  // Return
518  return;
519 }
520 
521 
522 /***********************************************************************//**
523  * @brief Write energy dispersion cube into FITS file.
524  *
525  * @param[in] fits FITS file.
526  *
527  * Write the energy dispersion cube into a FITS file. The energy dispersion
528  * cube values are written into the primary extension of the FITS file, the
529  * true energy boundaries are written into the `ENERGIES` extension, and the
530  * migration values are written into the `MIGRAS` extension.
531  ***************************************************************************/
532 void GCTACubeEdisp::write(GFits& fits) const
533 {
534  // Remove extensions from FITS file
537  }
540  }
541  if (fits.contains("Primary")) {
542  fits.remove("Primary");
543  }
544 
545  // Write cube
546  m_cube.write(fits);
547 
548  // Get last HDU and write attributes
549  if (fits.size() > 0) {
550  GFitsHDU& hdu = *fits[fits.size()-1];
551  hdu.card("BUNIT", "MeV**(-1)", "Unit of energy dispersion cube");
552  }
553 
554  // Write energy boundaries
556 
557  // Write migration nodes
559 
560  // Return
561  return;
562 }
563 
564 
565 /***********************************************************************//**
566  * @brief Load energy dispersion cube from FITS file
567  *
568  * @param[in] filename FITS file name.
569  *
570  * Loads the energy dispersion cube from a FITS file. See the read() method
571  * for information about the expected FITS file structure.
572  ***************************************************************************/
573 void GCTACubeEdisp::load(const GFilename& filename)
574 {
575  // Put into OpenMP criticial zone
576  #pragma omp critical(GCTACubeEdisp_load)
577  {
578 
579  // Open FITS file
580  GFits fits(filename);
581 
582  // Read Edisp cube
583  read(fits);
584 
585  // Close Edisp file
586  fits.close();
587 
588  } // end of OpenMP critical zone
589 
590  // Store filename
592 
593  // Return
594  return;
595 }
596 
597 
598 /***********************************************************************//**
599  * @brief Save energy dispersion cube into FITS file
600  *
601  * @param[in] filename FITS file name.
602  * @param[in] clobber Overwrite existing file?
603  *
604  * Save the energy dispersion cube into a FITS file.
605  ***************************************************************************/
606 void GCTACubeEdisp::save(const GFilename& filename, const bool& clobber) const
607 {
608  // Put into OpenMP criticial zone
609  #pragma omp critical(GCTACubeEdisp_save)
610  {
611 
612  // Create FITS file
613  GFits fits;
614 
615  // Write PSF cube
616  write(fits);
617 
618  // Save FITS file
619  fits.saveto(filename, clobber);
620 
621  // Close Edisp file
622  fits.close();
623 
624  } // end of OpenMP critical zone
625 
626  // Store filename
628 
629  // Return
630  return;
631 }
632 
633 
634 /***********************************************************************//**
635  * @brief Return boundaries in true energy
636  *
637  * @param[in] obsEng Observed photon energy.
638  * @return Boundaries in true energy.
639  *
640  * @exception GException::invalid_value
641  * No energies defined for energy dispersion cube
642  *
643  * Returns the boundaries in true photon energies that enclose the energy
644  * disperson for a given observed photon energy @p obsEng.
645  ***************************************************************************/
647 {
648  // Throw an exception if there are no energies
649  if (m_energies.is_empty()) {
650  std::string msg = "No energies defined for energy dispersion cube. "
651  "Please define the energies before calling the "
652  "method.";
654  }
655 
656  // If ebounds were not computed, before, compute them now
657  if (m_ebounds.empty()) {
658  compute_ebounds();
659  }
660 
661  // Return the index of the energy boundaries that are just below the
662  // observed energy. As the energy dispersion decreases with increasing
663  // energy we assure in that way that we integrate over a sufficiently
664  // large true energy range.
665  int index = 0;
666  if (obsEng > m_energies[0]) {
667  for (int i = 1; i < m_energies.size(); ++i) {
668  if (obsEng < m_energies[i]) {
669  break;
670  }
671  index++;
672  }
673  if (index >= m_energies.size()) {
674  index = m_energies.size();
675  }
676  }
677 
678  // Return true energy boundaries
679  return (m_ebounds[index]);
680 }
681 
682 
683 /***********************************************************************//**
684  * @brief Print energy dispersion cube information
685  *
686  * @param[in] chatter Chattiness.
687  * @return String containing energy dispersion cube information.
688  ***************************************************************************/
689 std::string GCTACubeEdisp::print(const GChatter& chatter) const
690 {
691  // Initialise result string
692  std::string result;
693 
694  // Continue only if chatter is not silent
695  if (chatter != SILENT) {
696 
697  // Append header
698  result.append("=== GCTACubeEdisp ===");
699 
700  // Append information
701  result.append("\n"+gammalib::parformat("Filename")+m_filename);
702 
703  // Append energies
704  if (m_energies.size() > 0) {
705  result.append("\n"+m_energies.print(chatter));
706  }
707  else {
708  result.append("\n"+gammalib::parformat("Energies") +
709  "Not defined");
710  }
711 
712  // Append number of migra bins
713  result.append("\n"+gammalib::parformat("Number of migra bins") +
715 
716  // Append migra range
717  result.append("\n"+gammalib::parformat("Migra range"));
718  if (m_migras.size() > 0) {
719  result.append(gammalib::str(m_migras[0]));
720  result.append(" - ");
721  result.append(gammalib::str(m_migras[m_migras.size()-1]));
722  }
723  else {
724  result.append("not defined");
725  }
726 
727  // Append skymap definition
728  result.append("\n"+m_cube.print(chatter));
729 
730  } // endif: chatter was not silent
731 
732  // Return result
733  return result;
734 }
735 
736 
737 /*==========================================================================
738  = =
739  = Private methods =
740  = =
741  ==========================================================================*/
742 
743 /***********************************************************************//**
744  * @brief Initialise class members
745  ***************************************************************************/
747 {
748  // Initialise members
749  m_filename.clear();
750  m_cube.clear();
751  m_energies.clear();
752  m_elogmeans.clear();
753  m_migras.clear();
754 
755  // Initialise cache
756  m_inx1 = 0;
757  m_inx2 = 0;
758  m_inx3 = 0;
759  m_inx4 = 0;
760  m_wgt1 = 0.0;
761  m_wgt2 = 0.0;
762  m_wgt3 = 0.0;
763  m_wgt4 = 0.0;
764  m_ebounds.clear();
765 
766  // Return
767  return;
768 }
769 
770 
771 /***********************************************************************//**
772  * @brief Copy class members
773  *
774  * @param[in] cube Energy dispersion cube.
775  ***************************************************************************/
777 {
778  // Copy members
779  m_filename = cube.m_filename;
780  m_cube = cube.m_cube;
781  m_energies = cube.m_energies;
782  m_elogmeans = cube.m_elogmeans;
783  m_migras = cube.m_migras;
784 
785  // Copy cache
786  m_inx1 = cube.m_inx1;
787  m_inx2 = cube.m_inx2;
788  m_inx3 = cube.m_inx3;
789  m_inx4 = cube.m_inx4;
790  m_wgt1 = cube.m_wgt1;
791  m_wgt2 = cube.m_wgt2;
792  m_wgt3 = cube.m_wgt3;
793  m_wgt4 = cube.m_wgt4;
794  m_ebounds = cube.m_ebounds;
795 
796  // Return
797  return;
798 }
799 
800 /***********************************************************************//**
801  * @brief Delete class members
802  ***************************************************************************/
804 {
805  // Return
806  return;
807 }
808 
809 
810 /***********************************************************************//**
811  * @brief Clear all pixels in the energy dispersion cube
812  ***************************************************************************/
814 {
815  // Loop over all maps
816  for (int imap = 0; imap < m_cube.nmaps(); ++imap) {
817 
818  // Loop over all pixels in sky map
819  for (int pixel = 0; pixel < m_cube.npix(); ++pixel) {
820 
821  // Reset cube value to zero
822  m_cube(pixel, imap) = 0.0;
823 
824  } // endfor: looped over all pixels
825 
826  } // endfor: looped over maps
827 
828  // Return
829  return;
830 }
831 
832 
833 /***********************************************************************//**
834  * @brief Fill energy dispersion cube from observation container
835  *
836  * @param[in] obs CTA observation.
837  * @param[in] exposure Pointer towards exposure map.
838  * @param[in] log Pointer towards logger.
839  *
840  * @exception GException::invalid_value
841  * No RoI or response found in CTA observation.
842  *
843  * Fills the energy dispersion cube from the observation container.
844  *
845  * If the @p exposure argument is not NULL, the energy dispersion is weighted
846  * by the exposure, and the exposure weighting is added to the exposure map.
847  *
848  * If the @p log argument is not NULL, the method puts information about
849  * exclusion and inclusion of a CTA observation into the energy dispersion
850  * cube.
851  ***************************************************************************/
853  GSkyMap* exposure,
854  GLog* log)
855 {
856  // Only continue if we have an event list
857  if (obs.eventtype() == "EventList") {
858 
859  // Extract pointing direction, energy boundaries and ROI from
860  // observation
861  GSkyDir pnt = obs.pointing().dir();
862  GEbounds obs_ebounds = obs.ebounds();
863  GCTARoi roi = obs.roi();
864 
865  // Check for RoI sanity
866  if (!roi.is_valid()) {
867  std::string msg = "No RoI information found in "+obs.instrument()+
868  " observation \""+obs.name()+"\". Run ctselect "
869  "to specify an RoI for this observation.";
871  }
872 
873  // Convert RoI into a circular region for overlap checking
874  GSkyRegionCircle roi_reg(roi.centre().dir(), roi.radius());
875 
876  // Extract response from observation
877  const GCTAResponseIrf* rsp = dynamic_cast<const GCTAResponseIrf*>
878  (obs.response());
879  if (rsp == NULL) {
880  std::string msg = "No valid instrument response function found in "+
881  obs.instrument()+" observation \""+obs.name()+
882  "\". Please specify the instrument response "
883  "function for this observation.";
885  }
886 
887  // Get energy dispersion component
888  const GCTAEdisp* edisp = rsp->edisp();
889  if (edisp == NULL) {
890  std::string msg = "No energy dispersion rcomponent found in the "
891  "the instrument response of "+
892  obs.instrument()+" observation \""+obs.name()+
893  "\". Please provide an instrument response that "
894  "comprises an energy dispersion component.";
896  }
897 
898  // Skip observation if livetime is zero
899  if (obs.livetime() == 0.0) {
900  if (log != NULL) {
901  *log << "Skipping unbinned ";
902  *log << obs.instrument();
903  *log << " observation ";
904  *log << "\"" << obs.name() << "\"";
905  *log << " (id=" << obs.id() << ") due to zero livetime";
906  *log << std::endl;
907  }
908  }
909 
910  // Skip observation if observation is outside the bounds of the cube
911  else if (!m_cube.overlaps(roi_reg)) {
912  if (log != NULL) {
913  *log << "Skipping unbinned ";
914  *log << obs.instrument();
915  *log << " observation ";
916  *log << "\"" << obs.name() << "\"";
917  *log << " (id=" << obs.id() << ") since it does not overlap ";
918  *log << "with the energy dispersion cube.";
919  *log << std::endl;
920  }
921  }
922 
923  // ... otherwise continue
924  else {
925 
926  // Announce observation usage
927  if (log != NULL) {
928  *log << "Including ";
929  *log << obs.instrument();
930  *log << " observation \"" << obs.name();
931  *log << "\" (id=" << obs.id() << ")";
932  *log << " in energy dispersion cube computation." << std::endl;
933  }
934 
935  // Loop over all pixels in sky map
936  for (int pixel = 0; pixel < m_cube.npix(); ++pixel) {
937 
938  // Get pixel sky direction
939  GSkyDir dir = m_cube.inx2dir(pixel);
940 
941  // Skip pixel if it is outside the RoI
942  if (roi.centre().dir().dist_deg(dir) > roi.radius()) {
943  continue;
944  }
945 
946  // Compute theta angle with respect to pointing direction in
947  // radians
948  double theta = pnt.dist(dir);
949 
950  // Loop over all energy bins
951  for (int iebin = 0; iebin < m_energies.size(); ++iebin) {
952 
953  // Get log10 of true energy in TeV
954  double logEsrc = m_energies[iebin].log10TeV();
955 
956  // Compute exposure weight. If no exposure map is
957  // specified, the weight is one. Otherwise, the weight
958  // is the effective area for this offset angle and
959  // source energy times the livetime of the observation.
960  double weight = 1.0;
961  if (exposure != NULL) {
962  weight = rsp->aeff(theta, 0.0, 0.0, 0.0, logEsrc) *
963  obs.livetime();
964  (*exposure)(pixel, iebin) += weight;
965  }
966 
967  // Loop over migration values
968  for (int imigra = 0; imigra < m_migras.size(); ++imigra) {
969 
970  // Compute energy migration fraction
971  double migra = m_migras[imigra];
972 
973  // Skip migra bin if zero
974  if (migra <= 0.0) {
975  continue;
976  }
977 
978  // Compute reconstructed energy
979  GEnergy Eobs = migra * m_energies[iebin];
980 
981  // Set map index
982  int imap = offset(imigra, iebin);
983 
984  // Add energy dispersion cube value
985  m_cube(pixel, imap) += rsp->edisp(Eobs,
986  m_energies[iebin],
987  theta, 0.0,
988  0.0, 0.0) * weight;
989 
990  } // endfor: looped over migration bins
991 
992  } // endfor: looped over energy bins
993 
994  } // endfor: looped over all pixels
995 
996  } // endelse: livetime was not zero
997 
998  } // endif: observation contained an event list
999 
1000  // Return
1001  return;
1002 }
1003 
1004 
1005 /***********************************************************************//**
1006  * @brief Update energy dispersion cube indices and weights cache
1007  *
1008  * @param[in] ereco Reconstructed event energy.
1009  * @param[in] etrue True photon energy.
1010  *
1011  * Updates the energy dispersion cube indices, stored in the members m_inx1,
1012  * m_inx2, m_inx3, and m_inx4, and weights cache, stored in m_wgt1, m_wgt2,
1013  * m_wgt3, and m_wgt4.
1014  ***************************************************************************/
1015 void GCTACubeEdisp::update(const GEnergy& ereco, const GEnergy& etrue) const
1016 {
1017  // Set node array for energy interpolation
1018  m_elogmeans.set_value(etrue.log10TeV());
1019 
1020  // Compute migration value
1021  double migra = ereco/etrue;
1022 
1023  // Set node array for migra interpolation
1024  m_migras.set_value(migra);
1025 
1026  // Set indices for bi-linear interpolation
1031 
1032  // Set weighting factors for bi-linear interpolation
1037 
1038  // Return
1039  return;
1040 }
1041 
1042 
1043 /***********************************************************************//**
1044  * @brief Set nodes for interpolation in true energy
1045  ***************************************************************************/
1047 {
1048  // Clear nodes
1049  m_elogmeans.clear();
1050 
1051  // Set nodes
1052  for (int i = 0; i < m_energies.size(); ++i) {
1053  m_elogmeans.append(m_energies[i].log10TeV());
1054  }
1055 
1056  // Return
1057  return;
1058 }
1059 
1060 
1061 /***********************************************************************//**
1062  * @brief Set nodes for interpolation in migration
1063  *
1064  * @param[in] mmax Maximum energy migration (>0).
1065  * @param[in] nmbins Number of migration bins (2 ...).
1066  *
1067  * Sets the nodes for interpolation in migration. None of the nodes will
1068  * have a value of zero unless mmax is zero.
1069  ***************************************************************************/
1070 void GCTACubeEdisp::set_migras(const double& mmax, const int& nmbins)
1071 {
1072  // Clear migration axis
1073  m_migras.clear();
1074 
1075  // Set the migration nodes
1076  double binsize = mmax / double(nmbins);
1077  for (int i = 0; i < nmbins; ++i) {
1078  double migra = binsize * double(i+1); // avoid central singularity
1079  m_migras.append(migra);
1080  }
1081 
1082  // Return
1083  return;
1084 }
1085 
1086 
1087 /***********************************************************************//**
1088  * @brief Compute true energy boundary vector
1089  *
1090  * Computes for all energies of the energy dispersion cube the boundaries in
1091  * true energy that encompass non-zero migration matrix elements. In case
1092  * that no matrix elements are found for a given energy, the interval of true
1093  * energies will be set to [0,0] (i.e. an empty interval).
1094  ***************************************************************************/
1096 {
1097  // Clear energy boundary vector
1098  m_ebounds.clear();
1099 
1100  // Loop over all reconstructed energies
1101  for (int i = 0; i < m_energies.size(); i++) {
1102 
1103  // Initialise results
1104  double migra_min = 0.0;
1105  double migra_max = 0.0;
1106  bool minFound = false;
1107  bool maxFound = false;
1108 
1109  // Compute map of sky ranges to be considered
1110  int mapmin = m_migras.size() * i;
1111  int mapmax = m_migras.size() * (i + 1);
1112 
1113  // Loop over sky map entries from lower migra
1114  for (int imap = mapmin; imap < mapmax; ++imap) {
1115 
1116  // Get maximum energy dispersion term for all sky pixels
1117  double edisp = 0.0;
1118  for (int pixel = 0; pixel < m_cube.npix(); ++pixel) {
1119  double value = m_cube(pixel, imap);
1120  if (value > edisp) {
1121  edisp = value;
1122  }
1123  }
1124 
1125  // Find first non-zero energy dispersion term
1126  if (edisp > 0.0) {
1127  minFound = true;
1128  migra_min = m_migras[imap - mapmin];
1129  break;
1130  }
1131 
1132  } // endfor: loop over maps
1133 
1134  // Loop over sky map entries from high migra
1135  for (int imap = mapmax-1; imap >= mapmin; --imap) {
1136 
1137  // Get maximum energy dispersion term for all sky pixels
1138  double edisp = 0.0;
1139  for (int pixel = 0; pixel < m_cube.npix(); ++pixel) {
1140  double value = m_cube(pixel, imap);
1141  if (value > edisp) {
1142  edisp = value;
1143  }
1144  }
1145 
1146  // Find first non-zero energy dispersion term
1147  if (edisp > 0.0) {
1148  maxFound = true;
1149  migra_max = m_migras[imap - mapmin];
1150  break;
1151  }
1152 
1153  } // endfor: loop over maps
1154 
1155  // Initialise energy boundaries
1156  GEnergy emin;
1157  GEnergy emax;
1158 
1159  // Compute energy boundaries if they were found and if they are
1160  // valid
1161  if (minFound && maxFound && migra_min > 0.0 && migra_max > 0.0 &&
1162  migra_max > migra_min) {
1163  emin = m_energies[i] * migra_min;
1164  emax = m_energies[i] * migra_max;
1165  }
1166 
1167  // ... otherwise we set the interval to a zero interval for safety
1168  else {
1169  emin.clear();
1170  emax.clear();
1171  }
1172 
1173  // Append energy boundaries
1174  m_ebounds.push_back(GEbounds(emin, emax));
1175 
1176  } // endfor: looped over all reconstruced energies
1177 
1178  // Return
1179  return;
1180 }
const GFilename & filename(void) const
Return edisp cube filename.
std::string print(const GChatter &chatter=NORMAL) const
Print energy container information.
Definition: GEnergies.cpp:698
int offset(const int &imigra, const int &iebin) const
Return map offset.
int size(void) const
Return number of nodes in node array.
Definition: GNodeArray.hpp:192
void clear(void)
Clear energy container.
Definition: GEnergies.cpp:227
std::string print(const GChatter &chatter=NORMAL) const
Print energy dispersion cube information.
Sky map class.
Definition: GSkyMap.hpp:89
Abstract FITS image base class.
Definition: GFitsImage.hpp:43
CTA event list class interface definition.
void read(const GFitsTable &table)
Read energies from FITS table.
Definition: GEnergies.cpp:615
GFitsTable * table(const int &extno)
Get pointer to table HDU.
Definition: GFits.cpp:482
bool contains(const int &extno) const
Check if HDU exists in FITS file.
Definition: GFits.hpp:282
double norm(const GVector &vector)
Computes vector norm.
Definition: GVector.cpp:864
void write(GFits &file, const std::string &extname=gammalib::extname_energies) const
Write energies into FITS object.
Definition: GEnergies.cpp:659
std::vector< GEbounds > m_ebounds
Energy boundaries.
GFilename m_filename
Filename.
void copy_members(const GCTACubeEdisp &cube)
Copy class members.
void ebounds(const GEbounds &ebounds)
Set energy boundaries.
Definition: GEvents.cpp:136
Abstract FITS extension base class.
Definition: GFitsHDU.hpp:51
bool overlaps(const GSkyRegion &region) const
Checks whether a region overlaps with this map.
Definition: GSkyMap.cpp:2102
GEbounds ebounds(void) const
Get energy boundaries.
int m_inx3
Index of upper right node.
void free_members(void)
Delete class members.
CTA instrument response function class definition.
const double & radius(void) const
Returns radius of region of interest in degrees.
Definition: GCTARoi.hpp:125
virtual ~GCTACubeEdisp(void)
Destructor.
const double & wgt_left(void) const
Returns left node weight.
Definition: GNodeArray.hpp:264
int m_inx1
Index of upper left node.
Abstract base class for the CTA energy dispersion.
Definition: GCTAEdisp.hpp:49
void write(GFits &file) const
Write energy dispersion cube into FITS file.
virtual void response(const GResponse &rsp)
Set response function.
void clear(void)
Clear node array.
Definition: GNodeArray.cpp:235
void counts(const GSkyMap &counts)
Set event cube counts from sky map.
double log10TeV(void) const
Return log10 of energy in TeV.
Definition: GEnergy.cpp:457
void fill(const GObservations &obs, GLog *log=NULL)
Fill energy dispersion cube from observation container.
void init_members(void)
Initialise class members.
const GEnergies & energies(void) const
Return energies for energy dispersion cub.
Interface for the CTA region of interest class.
Definition: GCTARoi.hpp:49
void load(const GFilename &filename)
Load energy dispersion cube from FITS file.
Gammalib tools definition.
FITS file class.
Definition: GFits.hpp:63
FITS file class interface definition.
Interface for the circular sky region class.
GSkyMap m_cube
Energy dispersion cube.
void set_value(const double &value) const
Set indices and weighting factors for interpolation.
Definition: GNodeArray.cpp:587
void pointing(const GCTAPointing &pointing)
Set CTA pointing.
GNodeArray m_migras
Migra bins for the Edisp cube.
void read(const GFits &fits)
Read energy dispersion cube from FITS file.
int m_inx4
Index of lower right node.
void read(const GFitsHDU &hdu)
Read skymap from FITS HDU.
Definition: GSkyMap.cpp:2664
const std::vector< int > & shape(void) const
Returns shape of maps.
Definition: GSkyMap.hpp:403
void dir(const GSkyDir &dir)
Set sky direction.
Energy container class.
Definition: GEnergies.hpp:60
#define G_EBOUNDS
void id(const std::string &id)
Set observation identifier.
Abstract FITS image base class definition.
const std::string extname_cta_migras
double migra_max(void) const
Return maximum migra value.
void write(GFits &fits, const std::string &extname="NODES") const
Write nodes into FITS object.
Definition: GNodeArray.cpp:812
void saveto(const GFilename &filename, const bool &clobber=false)
Saves to specified FITS file.
Definition: GFits.cpp:1293
std::string print(const GChatter &chatter=NORMAL) const
Print sky map.
Definition: GSkyMap.cpp:2787
GCTACubeEdisp(void)
Void constructor.
Information logger interface definition.
Definition: GLog.hpp:62
CTA event bin container class.
virtual double livetime(void) const
Return livetime.
double m_wgt4
Weight of lower right node.
const double & wgt_right(void) const
Returns right node weight.
Definition: GNodeArray.hpp:279
Energy boundaries container class.
Definition: GEbounds.hpp:60
void remove(const int &extno)
Remove HDU from FITS file.
Definition: GFits.cpp:862
GEbounds ebounds(const GEnergy &obsEng) const
Return boundaries in true energy.
const int & nmaps(void) const
Returns number of maps.
Definition: GSkyMap.hpp:391
GSkyDir inx2dir(const int &index) const
Returns sky direction of pixel.
Definition: GSkyMap.cpp:1331
GCTARoi roi(void) const
Get Region of Interest.
void clear_cube(void)
Clear all pixels in the energy dispersion cube.
GFitsHDU * write(GFits &file, const std::string &extname="") const
Write sky map into FITS file.
Definition: GSkyMap.cpp:2697
Filename class.
Definition: GFilename.hpp:62
const GCTAInstDir & centre(void) const
Returns region of interest centre.
Definition: GCTARoi.hpp:111
void set(const GCTAObservation &obs)
Set energy dispersion cube for one CTA observation.
int size(void) const
Return number of energies in container.
Definition: GEnergies.hpp:170
virtual std::string instrument(void) const
Return instrument name.
double m_wgt3
Weight of upper right node.
GVector log(const GVector &vector)
Computes natural logarithm of vector elements.
Definition: GVector.cpp:1274
GEnergies m_energies
True energy values of cube.
#define G_CONSTRUCTOR2
Abstract interface for FITS table.
Definition: GFitsTable.hpp:44
CTA energy dispersion for stacked analysis.
GChatter
Definition: GTypemaps.hpp:33
const int & inx_left(void) const
Returns left node index.
Definition: GNodeArray.hpp:235
void clear(void)
Clear instance.
Definition: GSkyMap.cpp:1100
void set_migras(const double &mmax, const int &nmbins)
Set nodes for interpolation in migration.
GCTACubeEdisp & operator=(const GCTACubeEdisp &cube)
Assignment operator.
#define G_CONSTRUCTOR1
double m_wgt1
Weight of upper left node.
int size(void) const
Return number of observations in container.
void compute_ebounds(void) const
Compute true energy boundary vector.
CTA observation class interface definition.
Observation container class.
double operator()(const GEnergy &ereco, const GEnergy &etrue, const GSkyDir &dir) const
Return energy dispersion in units of MeV .
CTA instrument response function class.
const int & inx_right(void) const
Returns right node index.
Definition: GNodeArray.hpp:249
void update(const GEnergy &ereco, const GEnergy &etrue) const
Update energy dispersion cube indices and weights cache.
bool is_empty(void) const
Signals if there are no energies in container.
Definition: GEnergies.hpp:184
const std::string extname_energies
Definition: GEnergies.hpp:44
void set(const GEbounds &ebounds)
Set energies from energy boundaries.
Definition: GEnergies.cpp:421
void name(const std::string &name)
Set observation name.
#define G_FILL_CUBE
GFitsImage * image(const int &extno)
Get pointer to image HDU.
Definition: GFits.cpp:368
Information logger class definition.
void clear(void)
Clear file name.
Definition: GFilename.cpp:188
GCTACubeEdisp * clone(void) const
Clone energy dispersion cube.
GNodeArray m_elogmeans
Mean log10TeV energy for the Edisp cube.
int size(void) const
Return number of HDUs in FITS file.
Definition: GFits.hpp:237
CTA event bin container class interface definition.
void set_eng_axis(void)
Set nodes for interpolation in true energy.
std::string eventtype(void) const
Return event type string.
bool is_valid(void) const
Checks if RoI is valid.
Definition: GCTARoi.hpp:152
double dist(const GSkyDir &dir) const
Compute angular distance between sky directions in radians.
Definition: GSkyDir.hpp:271
double m_wgt2
Weight of lower left node.
void read(const GFitsTable &table)
Read nodes from FITS table.
Definition: GNodeArray.cpp:775
int m_inx2
Index of lower left node.
CTA observation class.
std::string parformat(const std::string &s, const int &indent=0)
Convert string in parameter format.
Definition: GTools.cpp:1143
void clear(void)
Clear energy dispersion cube.
void append(const double &node)
Append one node to array.
Definition: GNodeArray.cpp:351
const int & npix(void) const
Returns number of pixels.
Definition: GSkyMap.hpp:347
void fill_cube(const GCTAObservation &obs, GSkyMap *exposure=NULL, GLog *log=NULL)
Fill energy dispersion cube from observation container.
GFitsHeaderCard & card(const int &cardno)
Return header card.
Definition: GFitsHDU.hpp:259
Observations container class interface definition.
Sky direction class.
Definition: GSkyDir.hpp:62
void close(void)
Close FITS file.
Definition: GFits.cpp:1342
Circular sky region class interface definition.
void save(const GFilename &filename, const bool &clobber=false) const
Save energy dispersion cube into FITS file.
void clear(void)
Clear instance.
Definition: GEnergy.cpp:261
CTA cube analysis energy disperson class definition.
Mathematical function definitions.
Class that handles energies in a unit independent way.
Definition: GEnergy.hpp:48
std::string str(const unsigned short int &value)
Convert unsigned short integer value into string.
Definition: GTools.cpp:489
FITS table abstract base class interface definition.