GammaLib 2.0.0
Loading...
Searching...
No Matches
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
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
96
97 // Copy members
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 ***************************************************************************/
140GCTACubeEdisp::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 ***************************************************************************/
212GCTACubeEdisp::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
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 ***************************************************************************/
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 ***************************************************************************/
492void 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
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 ***************************************************************************/
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 ***************************************************************************/
573void 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 ***************************************************************************/
606void 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()) {
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 ***************************************************************************/
689std::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
750 m_cube.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 ***************************************************************************/
1015void GCTACubeEdisp::update(const GEnergy& ereco, const GEnergy& etrue) const
1016{
1017 // Set node array for energy interpolation
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
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 ***************************************************************************/
1070void 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}
#define G_EBOUNDS
#define G_FILL_CUBE
CTA cube analysis energy disperson class definition.
CTA event bin container class interface definition.
CTA event list class interface definition.
CTA observation class interface definition.
CTA instrument response function class definition.
Abstract FITS image base class definition.
FITS table abstract base class interface definition.
FITS file class interface definition.
Information logger class definition.
Mathematical function definitions.
#define G_CONSTRUCTOR1
#define G_CONSTRUCTOR2
Observations container class interface definition.
Circular sky region class interface definition.
Gammalib tools definition.
GChatter
Definition GTypemaps.hpp:33
@ SILENT
Definition GTypemaps.hpp:34
double norm(const GVector &vector)
Computes vector norm.
Definition GVector.cpp:864
GVector log(const GVector &vector)
Computes natural logarithm of vector elements.
Definition GVector.cpp:1274
CTA energy dispersion for stacked analysis.
void write(GFits &file) const
Write energy dispersion cube into FITS file.
void free_members(void)
Delete class members.
void clear_cube(void)
Clear all pixels in the energy dispersion cube.
void init_members(void)
Initialise class members.
int m_inx3
Index of upper right node.
void update(const GEnergy &ereco, const GEnergy &etrue) const
Update energy dispersion cube indices and weights cache.
GCTACubeEdisp & operator=(const GCTACubeEdisp &cube)
Assignment operator.
double m_wgt3
Weight of upper right node.
virtual ~GCTACubeEdisp(void)
Destructor.
std::string print(const GChatter &chatter=NORMAL) const
Print energy dispersion cube information.
GNodeArray m_migras
Migra bins for the Edisp cube.
void set(const GCTAObservation &obs)
Set energy dispersion cube for one CTA observation.
void read(const GFits &fits)
Read energy dispersion cube from FITS file.
const GFilename & filename(void) const
Return edisp cube filename.
GCTACubeEdisp * clone(void) const
Clone energy dispersion cube.
void set_migras(const double &mmax, const int &nmbins)
Set nodes for interpolation in migration.
double m_wgt2
Weight of lower left node.
void set_eng_axis(void)
Set nodes for interpolation in true energy.
void fill(const GObservations &obs, GLog *log=NULL)
Fill energy dispersion cube from observation container.
void clear(void)
Clear energy dispersion cube.
std::vector< GEbounds > m_ebounds
Energy boundaries.
void copy_members(const GCTACubeEdisp &cube)
Copy class members.
GEbounds ebounds(const GEnergy &obsEng) const
Return boundaries in true energy.
int m_inx1
Index of upper left node.
void fill_cube(const GCTAObservation &obs, GSkyMap *exposure=NULL, GLog *log=NULL)
Fill energy dispersion cube from observation container.
double migra_max(void) const
Return maximum migra value.
int offset(const int &imigra, const int &iebin) const
Return map offset.
int m_inx4
Index of lower right node.
double m_wgt1
Weight of upper left node.
GNodeArray m_elogmeans
Mean log10TeV energy for the Edisp cube.
void compute_ebounds(void) const
Compute true energy boundary vector.
double m_wgt4
Weight of lower right node.
void save(const GFilename &filename, const bool &clobber=false) const
Save energy dispersion cube into FITS file.
double operator()(const GEnergy &ereco, const GEnergy &etrue, const GSkyDir &dir) const
Return energy dispersion in units of MeV .
GSkyMap m_cube
Energy dispersion cube.
void load(const GFilename &filename)
Load energy dispersion cube from FITS file.
int m_inx2
Index of lower left node.
const GSkyMap & cube(void) const
Return energy dispersion cube sky map.
GCTACubeEdisp(void)
Void constructor.
const GEnergies & energies(void) const
Return energies for energy dispersion cub.
GFilename m_filename
Filename.
GEnergies m_energies
True energy values of cube.
Abstract base class for the CTA energy dispersion.
Definition GCTAEdisp.hpp:49
CTA event bin container class.
void dir(const GSkyDir &dir)
Set sky direction.
CTA observation class.
void pointing(const GCTAPointing &pointing)
Set CTA pointing.
virtual double livetime(void) const
Return livetime.
std::string eventtype(void) const
Return event type string.
virtual std::string instrument(void) const
Return instrument name.
GCTARoi roi(void) const
Get Region of Interest.
GEbounds ebounds(void) const
Get energy boundaries.
virtual void response(const GResponse &rsp)
Set response function.
CTA instrument response function class.
const GCTAAeff * aeff(void) const
Return pointer to effective area response.
const GCTAEdisp * edisp(void) const
Return pointer to energy dispersion.
Interface for the CTA region of interest class.
Definition GCTARoi.hpp:49
const double & radius(void) const
Returns radius of region of interest in degrees.
Definition GCTARoi.hpp:125
bool is_valid(void) const
Checks if RoI is valid.
Definition GCTARoi.hpp:152
const GCTAInstDir & centre(void) const
Returns region of interest centre.
Definition GCTARoi.hpp:111
Energy boundaries container class.
Definition GEbounds.hpp:60
Energy container class.
Definition GEnergies.hpp:60
std::string print(const GChatter &chatter=NORMAL) const
Print energy container information.
void read(const GFitsTable &table)
Read energies from FITS table.
void clear(void)
Clear energy container.
int size(void) const
Return number of energies in container.
void set(const GEbounds &ebounds)
Set energies from energy boundaries.
void write(GFits &file, const std::string &extname=gammalib::extname_energies) const
Write energies into FITS object.
bool is_empty(void) const
Signals if there are no energies in container.
Class that handles energies in a unit independent way.
Definition GEnergy.hpp:48
double log10TeV(void) const
Return log10 of energy in TeV.
Definition GEnergy.cpp:457
void clear(void)
Clear instance.
Definition GEnergy.cpp:261
Filename class.
Definition GFilename.hpp:62
void clear(void)
Clear file name.
Abstract FITS extension base class.
Definition GFitsHDU.hpp:51
GFitsHeaderCard & card(const int &cardno)
Return header card.
Definition GFitsHDU.hpp:259
Abstract FITS image base class.
Abstract interface for FITS table.
FITS file class.
Definition GFits.hpp:63
void saveto(const GFilename &filename, const bool &clobber=false)
Saves to specified FITS file.
Definition GFits.cpp:1293
int size(void) const
Return number of HDUs in FITS file.
Definition GFits.hpp:237
bool contains(const int &extno) const
Check if HDU exists in FITS file.
Definition GFits.hpp:282
GFitsImage * image(const int &extno)
Get pointer to image HDU.
Definition GFits.cpp:368
void close(void)
Close FITS file.
Definition GFits.cpp:1342
void remove(const int &extno)
Remove HDU from FITS file.
Definition GFits.cpp:862
GFitsTable * table(const int &extno)
Get pointer to table HDU.
Definition GFits.cpp:482
Information logger interface definition.
Definition GLog.hpp:62
void set_value(const double &value) const
Set indices and weighting factors for interpolation.
const int & inx_right(void) const
Returns right node index.
const int & inx_left(void) const
Returns left node index.
const double & wgt_right(void) const
Returns right node weight.
const double & wgt_left(void) const
Returns left node weight.
void clear(void)
Clear node array.
int size(void) const
Return number of nodes in node array.
void write(GFits &fits, const std::string &extname="NODES") const
Write nodes into FITS object.
void append(const double &node)
Append one node to array.
void read(const GFitsTable &table)
Read nodes from FITS table.
void name(const std::string &name)
Set observation name.
void id(const std::string &id)
Set observation identifier.
Observation container class.
int size(void) const
Return number of observations in container.
Sky direction class.
Definition GSkyDir.hpp:62
double dist(const GSkyDir &dir) const
Compute angular distance between sky directions in radians.
Definition GSkyDir.hpp:271
Sky map class.
Definition GSkyMap.hpp:89
const int & nmaps(void) const
Returns number of maps.
Definition GSkyMap.hpp:389
void clear(void)
Clear instance.
Definition GSkyMap.cpp:1100
GSkyDir inx2dir(const int &index) const
Returns sky direction of pixel.
Definition GSkyMap.cpp:1331
void read(const GFitsHDU &hdu)
Read skymap from FITS HDU.
Definition GSkyMap.cpp:2664
const int & npix(void) const
Returns number of pixels.
Definition GSkyMap.hpp:345
GNdarray counts(void) const
Returns array with total number of counts for count maps.
Definition GSkyMap.cpp:1496
GFitsHDU * write(GFits &file, const std::string &extname="") const
Write sky map into FITS file.
Definition GSkyMap.cpp:2697
const std::vector< int > & shape(void) const
Returns shape of maps.
Definition GSkyMap.hpp:401
bool overlaps(const GSkyRegion &region) const
Checks whether a region overlaps with this map.
Definition GSkyMap.cpp:2102
std::string print(const GChatter &chatter=NORMAL) const
Print sky map.
Definition GSkyMap.cpp:2787
Interface for the circular sky region class.
std::string parformat(const std::string &s, const int &indent=0)
Convert string in parameter format.
Definition GTools.cpp:1143
std::string str(const unsigned short int &value)
Convert unsigned short integer value into string.
Definition GTools.cpp:489
const std::string extname_cta_migras
const std::string extname_energies
Definition GEnergies.hpp:44