Commit 1aec5cfdf84dd7c2adb9b98e03cbf5ee29725ae2

Authored by Josh Cardenzana
1 parent ce98da1e

Implemented "RING" bkg subtraction to ctskymap (#2207)

Implemented the ring background subtraction method to ctskymap.
Tests are still to come.
src/ctskymap/ctskymap.cpp
... ... @@ -30,6 +30,7 @@
30 30 #endif
31 31 #include <cstdio>
32 32 #include "ctskymap.hpp"
  33 +#include "GSkyRegions.hpp"
33 34  
34 35 /* __ Method name definitions ____________________________________________ */
35 36 #define G_INIT_MAP "ctskymap::init_map(GCTAObservation* obs)"
... ... @@ -241,18 +242,9 @@ void ctskymap::run(void)
241 242  
242 243 } // endfor: looped over observations
243 244  
244   - // If background subtract was required then compute significance map
245   - // and subtract background map from counts map
246   - if (m_bkgsubtract != "NONE") {
247   -
248   - // Compute significance map
249   - m_sigmap = (m_skymap - m_bkgmap) / sqrt(m_skymap);
250   -
251   - // Subtract background map from counts map
252   - m_skymap -= m_bkgmap;
253   -
254   - } // endif: background subtraction was requested
255   -
  245 + // Compute the significance map
  246 + map_significance();
  247 +
256 248 // Optionally publish sky map
257 249 if (m_publish) {
258 250 publish();
... ... @@ -368,6 +360,14 @@ void ctskymap::init_members(void)
368 360 m_bkgsubtract = "NONE";
369 361 m_publish = false;
370 362 m_chatter = static_cast<GChatter>(2);
  363 + m_solidangle = std::vector<double>(0);
  364 +
  365 + // Variables for RING bkgd subtraction
  366 + m_exclmap.clear();
  367 + m_roiradius = 0.0;
  368 + m_inradius = 0.0;
  369 + m_outradius = 0.0;
  370 + m_cnts = std::vector<int>(0);
371 371  
372 372 // Return
373 373 return;
... ... @@ -441,10 +441,10 @@ void ctskymap::get_parameters(void)
441 441  
442 442 // Read ahead parameters
443 443 if (read_ahead()) {
444   - m_outmap = (*this)["outmap"].filename();
445   - m_roiradius = (this)["roiradius"].real();
446   - m_inradius = (this)["m_inradius"].real();
447   - m_outradius = (this)["m_outradius"].real();
  444 + m_outmap = (*this)["outmap"].filename();
  445 + m_roiradius = (*this)["roiradius"].real();
  446 + m_inradius = (*this)["inradius"].real();
  447 + m_outradius = (*this)["outradius"].real();
448 448 }
449 449  
450 450 // Create background map and significance map if background subtraction
... ... @@ -452,10 +452,19 @@ void ctskymap::get_parameters(void)
452 452 if (m_bkgsubtract != "NONE") {
453 453 m_bkgmap = create_map(m_obs);
454 454 m_sigmap = create_map(m_obs);
  455 +
  456 + // Setup the exclusions map
  457 + map_exclusions((*this)["regfile"].filename());
455 458  
456 459 // If doing a ring background subtraction, generate an alpha map
457 460 if (m_bkgsubtract == "RING") {
458   - m_alphamap = create_map(m_obs);
  461 + m_cnts = std::vector<int>(m_bkgmap.npix(), 0);
  462 + }
  463 +
  464 + // Cache the pixel solid angles
  465 + m_solidangle = std::vector<double>(m_bkgmap.npix(), 0.0);
  466 + for (int i=0; i<m_bkgmap.npix(); i++) {
  467 + m_solidangle[i] = m_bkgmap.solidangle(i);
459 468 }
460 469 }
461 470  
... ... @@ -539,6 +548,11 @@ void ctskymap::map_events(GCTAObservation* obs)
539 548 m_skymap(pixel, 0) += 1.0;
540 549 num_in_map++;
541 550  
  551 + // If doing the RING subtraction, keep tabs on counts from this obs
  552 + if (m_bkgsubtract == "RING") {
  553 + m_cnts[m_skymap.pix2inx(pixel)] += 1.0;
  554 + }
  555 +
542 556 } // endfor: looped over all events
543 557  
544 558 // Log binning results
... ... @@ -673,7 +687,7 @@ void ctskymap::map_background_irf(GCTAObservation* obs)
673 687 total += value;
674 688  
675 689 } // endfor: looped over background map pixels
676   -
  690 +
677 691 // Log background subtraction results
678 692 log_value(NORMAL, "Events in background", int(total+0.5));
679 693 log_value(NORMAL, "Background evaluations", calls);
... ... @@ -697,7 +711,293 @@ void ctskymap::map_background_irf(GCTAObservation* obs)
697 711 * also by the background IRF value
698 712 ***************************************************************************/
699 713 void ctskymap::map_background_ring(GCTAObservation* obs)
700   -{}
  714 +{
  715 + // Get IRF response (to scale background counts)
  716 + const GCTAResponseIrf* rsp = dynamic_cast<const GCTAResponseIrf*>(obs->response());
  717 +
  718 + // Throw an exception if observation has no instrument response function
  719 + if (rsp == NULL) {
  720 + std::string msg = "No response information available for "+
  721 + get_obs_header(obs)+" to compute IRF background. "
  722 + "Please specify response information or use "
  723 + "another background subtraction method.";
  724 + throw GException::invalid_value(G_BKG_SUBTRACT_IRF, msg);
  725 + }
  726 +
  727 + // Get IRF background template
  728 + const GCTABackground* bkg = rsp->background();
  729 +
  730 + // Throw an exception if observation has no IRF background template
  731 + if (bkg == NULL) {
  732 + std::string msg = "No IRF background template found in instrument "
  733 + "response function for "+
  734 + get_obs_header(obs)+". Please specify an instrument "
  735 + "response function containing a background template.";
  736 + throw GException::invalid_value(G_BKG_SUBTRACT_IRF, msg);
  737 + }
  738 +
  739 + // Compute natural logarithm of energy range in MeV
  740 + double lnEmin = std::log(m_emin * 1.0e6);
  741 + double lnEmax = std::log(m_emax * 1.0e6);
  742 +
  743 + // Extract region of interest from observation
  744 + GCTARoi roi = obs->roi();
  745 +
  746 + // Initialise statistics
  747 + int calls = 0;
  748 + double total = 0.0;
  749 + double exposure = obs->livetime() ;
  750 +
  751 + // Cached background IRF sensitivities
  752 + std::vector<double> sens(m_cnts.size(), 0.0);
  753 +
  754 + // Keep track of the minimum and maximum pixels that need to be looped over
  755 + int imin(m_bkgmap.npix());
  756 + int imax(0);
  757 +
  758 + // Cache the IRF sensitivities
  759 + // Loop over all map pixels
  760 + for (int i = 0; i < m_bkgmap.npix(); ++i) {
  761 +
  762 + // Get sky direction of pixel
  763 + GSkyDir skydir = m_bkgmap.inx2dir(i);
  764 +
  765 + // Convert sky direction in instrument direction
  766 + GCTAInstDir instdir = obs->pointing().instdir(skydir);
  767 +
  768 + // If RoI is valid and instrument direction is not within RoI then
  769 + // skip pixel
  770 + if (roi.is_valid() && !roi.contains(instdir)) {
  771 + continue;
  772 + }
  773 +
  774 + // If bin is excluded in the exclusion, skip it
  775 + if (m_exclmap(i) == 0.0) {
  776 + continue;
  777 + }
  778 +
  779 + // Update pixel ranges
  780 + if (i > imax) imax = i;
  781 + if (i < imin) imin = i;
  782 +
  783 + // Setup integration function
  784 + ctskymap::irf_kern integrand(bkg, &instdir);
  785 + GIntegral integral(&integrand);
  786 +
  787 + // Set precision (has been carefully adjusted using a test simulation
  788 + // over the energy range 20 GeV - 120 TeV)
  789 + integral.eps(1.0e-6);
  790 +
  791 + // Do Romberg integration to get the sensitivity (note: this assumes the
  792 + // background IRF is a good approximation for the radial sensitivity)
  793 + sens[i] = integral.romberg(lnEmin, lnEmax);
  794 +
  795 + // Update number of background function calls
  796 + calls += integral.calls();
  797 +
  798 + } // endfor: Loop for caching bkg IRF sensitivity
  799 +
  800 + // Define the regions necessary to do the pixel checks
  801 + GSkyRegionCircle roi_reg(0.0, 0.0, m_roiradius);
  802 + GSkyRegionCircle inner_reg(0.0, 0.0, m_inradius);
  803 + GSkyRegionCircle outer_reg(0.0, 0.0, m_outradius);
  804 +
  805 + // Loop over all map pixels
  806 + for (int i = imin; i <= imax; ++i) {
  807 +
  808 + // Get sky direction of pixel
  809 + GSkyDir skydir = m_bkgmap.inx2dir(i);
  810 +
  811 + // Convert sky direction in instrument direction
  812 + GCTAInstDir instdir = obs->pointing().instdir(skydir);
  813 +
  814 + // If RoI is valid and instrument direction is not within RoI then
  815 + // skip pixel
  816 + if (roi.is_valid() && !roi.contains(instdir)) {
  817 + continue;
  818 + }
  819 +
  820 + // Update the region positions
  821 + roi_reg.centre(skydir);
  822 + inner_reg.centre(skydir);
  823 + outer_reg.centre(skydir);
  824 +
  825 + // Loop again over every pixel in the observation to compute Non/Noff
  826 + int n_on(0), n_off(0);
  827 + double alpha_on(0.0), alpha_off(0.0);
  828 + for (int j = imin; j < imax; ++j) {
  829 +
  830 + // Get sky direction of pixel
  831 + skydir = m_bkgmap.inx2dir(i);
  832 +
  833 + // Convert sky direction in instrument direction
  834 + instdir = obs->pointing().instdir(skydir);
  835 +
  836 + // If RoI is valid and instrument direction is not within RoI then
  837 + // skip pixel
  838 + if (roi.is_valid() && !roi.contains(instdir)) {
  839 + continue;
  840 + }
  841 +
  842 + // Check if pixel is inside source region
  843 + if (roi_reg.contains(skydir)) {
  844 + // Update n_on
  845 + n_on += m_cnts[j];
  846 +
  847 + // Update alpha_on
  848 + alpha_on += m_solidangle[j] * sens[j];
  849 + }
  850 +
  851 + // Otherwise check if it is in the background region
  852 + else if ((m_exclmap(j) == 1.0) &&
  853 + !inner_reg.contains(skydir) &&
  854 + outer_reg.contains(skydir)) {
  855 + // Update n_off
  856 + n_off += m_cnts[j];
  857 +
  858 + // Update alpha_off
  859 + alpha_off += m_solidangle[j] * sens[j];
  860 + }
  861 +
  862 + } // endfor: sub-loop over pixels
  863 +
  864 + // Compute the likelihood for this bin
  865 + double alpha = alpha_on / alpha_off;
  866 + m_sigmap(i,0) += std::log(1);
  867 +
  868 + // Update the predicted background counts
  869 + m_bkgmap(i,0) = n_on - (alpha * n_off);
  870 +
  871 + } // endfor: looped over background map pixels
  872 +
  873 + // Update the likelihood values in the
  874 +
  875 +
  876 + // Zero out the alpha map to prepare it for the next observation
  877 + std::vector<int>::iterator iter;
  878 + for (iter=m_cnts.begin(); iter!=m_cnts.end(); ++iter) {
  879 + (*iter) = 0;
  880 + }
  881 +
  882 + // Return
  883 + return;
  884 +}
  885 +
  886 +
  887 +/***********************************************************************//**
  888 + * @brief Computes the bin-by-bin significance if background subtraction requested
  889 + *
  890 + * Method computes the bin-by bin significance if a background subtraction
  891 + * method is specified. If method is "IRF", Poisson statistics (in the Gaussian
  892 + * limit) are assumed. If method is "RING" a Li & Ma significance is computed
  893 + * for each bin (as well as a map of the computed alpha values).
  894 + ***************************************************************************/
  895 +void ctskymap::map_significance(void)
  896 +{
  897 + // Check if no background subtraction is requested
  898 + if (m_bkgsubtract == "NONE") {
  899 + return;
  900 + }
  901 +
  902 + // If background subtract was required then compute significance map..
  903 +
  904 + // ... from "RING" method (Li & Ma eq. 17)
  905 + else if (m_bkgsubtract == "RING") {
  906 + // Multiply the significance map by '-2'
  907 + m_sigmap *= -2.0;
  908 +
  909 + // Now the square root
  910 + m_sigmap = sqrt(m_sigmap);
  911 + }
  912 +
  913 + // ... using Poisson statistics in the Gaussian limit
  914 + else {
  915 + // Compute significance map
  916 + m_sigmap = (m_skymap - m_bkgmap) / sqrt(m_skymap);
  917 + } // endif: background subtraction was requested
  918 +
  919 + // Subtract background map from counts map
  920 + m_skymap -= m_bkgmap;
  921 +}
  922 +
  923 +
  924 +/***********************************************************************//**
  925 + * @brief Generates a map of pixels to be excluded from background estimation
  926 + *
  927 + * @param[in] filename Filename from which to take the exclusions from
  928 + ***************************************************************************/
  929 +void ctskymap::map_exclusions(const GFilename& filename)
  930 +{
  931 + // Otherwise, create the map and set all pixels to 1
  932 + m_exclmap = create_map(m_obs);
  933 + m_exclmap = 1.0;
  934 +
  935 + // Make sure the exclusions filename is valid
  936 + if (!is_valid_filename(filename)) {
  937 + return;
  938 + }
  939 +
  940 + // Fill the exclusions based on the regions supplied
  941 + if (filename.is_fits()) {
  942 + map_exclusions_fits(filename);
  943 + } else {
  944 + map_exclusions_reg(filename);
  945 + } // endif: generate exclusion map from filename
  946 +
  947 + return;
  948 +}
  949 +
  950 +
  951 +/***********************************************************************//**
  952 + * @brief Fills exclusions map if file is a FITS image
  953 + *
  954 + * @param[in] filename Filename from which to take the exclusions from
  955 + ***************************************************************************/
  956 +void ctskymap::map_exclusions_fits(const GFilename& filename)
  957 +{
  958 + // Load the fits image
  959 + GSkyMap inmap(filename);
  960 +
  961 + // Loop through the individual pixels in the exclusion map
  962 + GSkyDir dir;
  963 + for (int i=0; i<m_exclmap.npix(); i++) {
  964 + // Get the pixel direction
  965 + dir = m_exclmap.pix2dir(i);
  966 +
  967 + // Check this sky position in the fits map
  968 + if (inmap.contains(dir) && (inmap(inmap.dir2inx(dir)) == 0.0)) {
  969 + // Set the pixel to 0
  970 + m_exclmap(i) = 0.0;
  971 +
  972 + } // endif: pixel,region overlap check
  973 + } // endfor: Loop over exclusion map pixels
  974 +}
  975 +
  976 +
  977 +/***********************************************************************//**
  978 + * @brief Fills exclusions map if file is a DS9 region file
  979 + *
  980 + * @param[in] filename Filename from which to take the exclusions from
  981 + ***************************************************************************/
  982 +void ctskymap::map_exclusions_reg(const GFilename& filename)
  983 +{
  984 + // Load the exclusion regions
  985 + GSkyRegions regions(filename);
  986 +
  987 + // Loop through the individual pixels in the exclusion map
  988 + GSkyDir dir;
  989 + for (int i=0; i<m_exclmap.npix(); i++) {
  990 + // Get the pixel position
  991 + dir = m_exclmap.pix2dir(i);
  992 +
  993 + // Check if GSkyDir overlaps with the regions
  994 + if (regions.contains(dir)) {
  995 + // Set the pixel to 0
  996 + m_exclmap(i) = 0.0;
  997 +
  998 + } // endif: pixel,region overlap check
  999 + } // endfor: loop over exclusion map pixels
  1000 +}
701 1001  
702 1002  
703 1003 /***********************************************************************//**
... ...
src/ctskymap/ctskymap.hpp
... ... @@ -72,6 +72,10 @@ protected:
72 72 void map_background(GCTAObservation* obs);
73 73 void map_background_irf(GCTAObservation* obs);
74 74 void map_background_ring(GCTAObservation* obs);
  75 + void map_significance(void);
  76 + void map_exclusions(const GFilename& filename);
  77 + void map_exclusions_reg(const GFilename& filename);
  78 + void map_exclusions_fits(const GFilename& filename);
75 79  
76 80 // Background integration kernel
77 81 class irf_kern : public GFunction {
... ... @@ -101,7 +105,11 @@ protected:
101 105 GSkyMap m_skymap; //!< Sky map
102 106 GSkyMap m_bkgmap; //!< Background map
103 107 GSkyMap m_sigmap; //!< Significance map
104   - GSkyMap m_alphamap; //!< Alpha map (if doing "RING" bkg subtraction)
  108 + GSkyMap m_exclmap; //!< Exclusion map (if doing "RING" bkg subtraction)
  109 +
  110 + // Caching variables to prevent multiple computations of the same thing
  111 + std::vector<int> m_cnts; //!< Current observation counts (for "RING")
  112 + std::vector<double> m_solidangle; //!< Cached pixel solid angles
105 113 };
106 114  
107 115  
... ...