Commit cf079ae01f3a526d6bc4b35fc1642b08b6729554

Authored by Jürgen Knödlseder
1 parent de558787

Add RING background method to ctskymap (#2207)

ChangeLog
1 -2017-10-11 1 +2017-10-16
2 2
3 * Version 1.5.0 released 3 * Version 1.5.0 released
4 ======================== 4 ========================
5 5
  6 + Add RING background method to ctskymap (#2207)
6 Add keywords to sky maps produced by ctskymap (#2194) 7 Add keywords to sky maps produced by ctskymap (#2194)
7 Enhance efficiency of computations in ctmodel (#2189) 8 Enhance efficiency of computations in ctmodel (#2189)
8 Add csmodelsois script (#2180) 9 Add csmodelsois script (#2180)
1 New Features and Important Changes in ctools 1.5.0 1 New Features and Important Changes in ctools 1.5.0
2 2
3 -11 October 2017 3 +16 October 2017
4 4
5 5
6 Introduction 6 Introduction
@@ -57,6 +57,9 @@ Header keywords informing about the dates and times covered by the observations @@ -57,6 +57,9 @@ Header keywords informing about the dates and times covered by the observations
57 used to generate the skymap, as well as the event energy range, are now appended 57 used to generate the skymap, as well as the event energy range, are now appended
58 as header keywords to the FITS file (#2194). 58 as header keywords to the FITS file (#2194).
59 59
  60 +Add the RING background method, which is a classical analysis methods for IACT
  61 +data analysis, for background subtraction (#2207).
  62 +
60 63
61 ctmodel - CTA counts cube modelling 64 ctmodel - CTA counts cube modelling
62 ----------------------------------- 65 -----------------------------------
doc/source/admin/release_history/1.5.rst
@@ -22,6 +22,8 @@ Bug fixes @@ -22,6 +22,8 @@ Bug fixes
22 Improvements 22 Improvements
23 ------------ 23 ------------
24 24
  25 +* [`2207 <https://cta-redmine.irap.omp.eu/issues/2207>`_] -
  26 + Add ``RING`` background method to :ref:`ctskymap`
25 * [`2194 <https://cta-redmine.irap.omp.eu/issues/2194>`_] - 27 * [`2194 <https://cta-redmine.irap.omp.eu/issues/2194>`_] -
26 Add keywords to sky maps produced by :ref:`ctskymap` 28 Add keywords to sky maps produced by :ref:`ctskymap`
27 * [`2189 <https://cta-redmine.irap.omp.eu/issues/2189>`_] - 29 * [`2189 <https://cta-redmine.irap.omp.eu/issues/2189>`_] -
src/ctskymap/ctskymap.cpp
@@ -33,10 +33,11 @@ @@ -33,10 +33,11 @@
33 #include "GSkyRegions.hpp" 33 #include "GSkyRegions.hpp"
34 34
35 /* __ Method name definitions ____________________________________________ */ 35 /* __ Method name definitions ____________________________________________ */
36 -#define G_INIT_MAP "ctskymap::init_map(GCTAObservation* obs)"  
37 -#define G_MAP_EVENTS "ctskymap::map_events(GCTAObservation*)"  
38 -#define G_MAP_BACKGROUND_IRF "ctskymap::map_background_irf(GCTAObservation*)"  
39 -#define G_MAP_BACKGROUND_RING "ctskymap::map_background_ring(GCTAObservation*)" 36 +#define G_GET_PARAMETERS "ctskymap::get_parameter()"
  37 +#define G_MAP_EVENTS "ctskymap::map_events(GCTAObservation*)"
  38 +#define G_MAP_BACKGROUND_IRF "ctskymap::map_background_irf(GCTAObservation*)"
  39 +#define G_MAP_BACKGROUND_RING "ctskymap::map_background_ring("\
  40 + "GCTAObservation*)"
40 41
41 /* __ Debug definitions __________________________________________________ */ 42 /* __ Debug definitions __________________________________________________ */
42 43
@@ -242,10 +243,12 @@ void ctskymap::run(void) @@ -242,10 +243,12 @@ void ctskymap::run(void)
242 243
243 } // endfor: looped over observations 244 } // endfor: looped over observations
244 245
  246 + // If background subtraction is selected then compute significance map
245 if (m_bkgsubtract != "NONE") { 247 if (m_bkgsubtract != "NONE") {
  248 +
246 // Compute the significance map 249 // Compute the significance map
247 map_significance(); 250 map_significance();
248 - 251 +
249 // Subtract background map from counts map 252 // Subtract background map from counts map
250 m_skymap -= m_bkgmap; 253 m_skymap -= m_bkgmap;
251 } 254 }
@@ -320,7 +323,7 @@ void ctskymap::save(void) @@ -320,7 +323,7 @@ void ctskymap::save(void)
320 // Save FITS file to disk 323 // Save FITS file to disk
321 fits.saveto(m_outmap, clobber()); 324 fits.saveto(m_outmap, clobber());
322 325
323 - } 326 + } // endif: filename and map were not empty
324 327
325 // Write into logger what has been done 328 // Write into logger what has been done
326 std::string fname = (m_outmap.is_empty()) ? "NONE" : m_outmap.url(); 329 std::string fname = (m_outmap.is_empty()) ? "NONE" : m_outmap.url();
@@ -376,18 +379,21 @@ void ctskymap::init_members(void) @@ -376,18 +379,21 @@ void ctskymap::init_members(void)
376 m_skymap.clear(); 379 m_skymap.clear();
377 m_bkgmap.clear(); 380 m_bkgmap.clear();
378 m_sigmap.clear(); 381 m_sigmap.clear();
  382 + m_exclmap.clear();
  383 + m_alphamap.clear();
  384 + m_onmap.clear();
379 m_emin = 0.0; 385 m_emin = 0.0;
380 m_emax = 0.0; 386 m_emax = 0.0;
381 m_bkgsubtract = "NONE"; 387 m_bkgsubtract = "NONE";
  388 + m_roiradius = 0.0;
  389 + m_inradius = 0.0;
  390 + m_outradius = 0.0;
382 m_publish = false; 391 m_publish = false;
383 m_chatter = static_cast<GChatter>(2); 392 m_chatter = static_cast<GChatter>(2);
384 - m_solidangle = std::vector<double>(0);  
385 393
386 - // Variables for RING bkgd subtraction  
387 - m_exclmap.clear();  
388 - m_roiradius = 0.0;  
389 - m_inradius = 0.0;  
390 - m_outradius = 0.0; 394 + // Initialise cache
  395 + m_solidangle.clear();
  396 + m_dirs.clear();
391 397
392 // Return 398 // Return
393 return; 399 return;
@@ -405,18 +411,21 @@ void ctskymap::copy_members(const ctskymap&amp; app) @@ -405,18 +411,21 @@ void ctskymap::copy_members(const ctskymap&amp; app)
405 m_skymap = app.m_skymap; 411 m_skymap = app.m_skymap;
406 m_bkgmap = app.m_bkgmap; 412 m_bkgmap = app.m_bkgmap;
407 m_sigmap = app.m_sigmap; 413 m_sigmap = app.m_sigmap;
  414 + m_exclmap = app.m_exclmap;
  415 + m_alphamap = app.m_alphamap;
  416 + m_onmap = app.m_onmap;
408 m_emin = app.m_emin; 417 m_emin = app.m_emin;
409 m_emax = app.m_emax; 418 m_emax = app.m_emax;
410 m_bkgsubtract = app.m_bkgsubtract; 419 m_bkgsubtract = app.m_bkgsubtract;
  420 + m_roiradius = app.m_roiradius;
  421 + m_inradius = app.m_inradius;
  422 + m_outradius = app.m_outradius;
411 m_publish = app.m_publish; 423 m_publish = app.m_publish;
412 m_chatter = app.m_chatter; 424 m_chatter = app.m_chatter;
413 - m_solidangle = app.m_solidangle;  
414 425
415 - // Variables for RING bkgd subtraction  
416 - m_exclmap = app.m_exclmap;  
417 - m_roiradius = app.m_roiradius;  
418 - m_inradius = app.m_inradius;  
419 - m_outradius = app.m_outradius; 426 + // Copy cache
  427 + m_solidangle = app.m_solidangle;
  428 + m_dirs = app.m_dirs;
420 429
421 // Return 430 // Return
422 return; 431 return;
@@ -456,6 +465,26 @@ void ctskymap::get_parameters(void) @@ -456,6 +465,26 @@ void ctskymap::get_parameters(void)
456 m_emax = (*this)["emax"].real(); 465 m_emax = (*this)["emax"].real();
457 m_bkgsubtract = (*this)["bkgsubtract"].string(); 466 m_bkgsubtract = (*this)["bkgsubtract"].string();
458 467
  468 + // Get RING background parameters
  469 + if (m_bkgsubtract == "RING") {
  470 +
  471 + // Get parameters
  472 + m_roiradius = (*this)["roiradius"].real();
  473 + m_inradius = (*this)["inradius"].real();
  474 + m_outradius = (*this)["outradius"].real();
  475 +
  476 + // Make sure that (roiradius < inradius < outradius)
  477 + if (m_roiradius > m_inradius) {
  478 + std::string msg("'roiradius' must be smaller than 'inradius'");
  479 + throw GException::invalid_value(G_GET_PARAMETERS, msg);
  480 + }
  481 + else if (m_inradius > m_outradius) {
  482 + std::string msg("'inradius' must be smaller than 'outradius'");
  483 + throw GException::invalid_value(G_GET_PARAMETERS, msg);
  484 + }
  485 +
  486 + } // endif: read RING background parameters
  487 +
459 // If IRF background subtraction is requested then make sure that the 488 // If IRF background subtraction is requested then make sure that the
460 // CTA observations in the observation container have response information 489 // CTA observations in the observation container have response information
461 if (m_bkgsubtract != "NONE") { 490 if (m_bkgsubtract != "NONE") {
@@ -463,43 +492,29 @@ void ctskymap::get_parameters(void) @@ -463,43 +492,29 @@ void ctskymap::get_parameters(void)
463 } 492 }
464 493
465 // Get remaining parameters 494 // Get remaining parameters
466 - m_publish = (*this)["publish"].boolean();  
467 - m_chatter = static_cast<GChatter>((*this)["chatter"].integer()); 495 + m_publish = (*this)["publish"].boolean();
  496 + m_chatter = static_cast<GChatter>((*this)["chatter"].integer());
468 497
469 // Read ahead parameters 498 // Read ahead parameters
470 if (read_ahead()) { 499 if (read_ahead()) {
471 m_outmap = (*this)["outmap"].filename(); 500 m_outmap = (*this)["outmap"].filename();
472 -  
473 - if (m_bkgsubtract == "RING") {  
474 - // Get the integration region radii  
475 - m_roiradius = (*this)["roiradius"].real();  
476 - m_inradius = (*this)["inradius"].real();  
477 - m_outradius = (*this)["outradius"].real();  
478 -  
479 - // Make sure that (roiradius < inradius < outradius)  
480 - if (m_roiradius > m_inradius) {  
481 - std::string err("'roiradius' must be smaller than inner ring radius");  
482 - throw GException::invalid_value("ctskymap::get_parameter()",err);  
483 - } else if (m_inradius > m_outradius) {  
484 - std::string err("Inner ring radius must be smaller than outer ring radius");  
485 - throw GException::invalid_value("ctskymap::get_parameter()",err);  
486 - } // endif: Ring radius checks  
487 - }  
488 } 501 }
489 502
490 // Create background map and significance map if background subtraction 503 // Create background map and significance map if background subtraction
491 // is requested 504 // is requested
492 if (m_bkgsubtract != "NONE") { 505 if (m_bkgsubtract != "NONE") {
  506 +
  507 + // Create backgrond and significance maps
493 m_bkgmap = create_map(m_obs); 508 m_bkgmap = create_map(m_obs);
494 m_sigmap = create_map(m_obs); 509 m_sigmap = create_map(m_obs);
495 510
496 // Setup the exclusions map 511 // Setup the exclusions map
497 - map_exclusions((*this)["regfile"].filename()); 512 + map_exclusions((*this)["inexclusion"].filename());
498 513
499 - // Cache the pixel areas 514 + // Cache the pixel solid angles and sky directions
500 m_solidangle.reserve(m_bkgmap.npix()); 515 m_solidangle.reserve(m_bkgmap.npix());
501 m_dirs.reserve(m_bkgmap.npix()); 516 m_dirs.reserve(m_bkgmap.npix());
502 - for (int i=0; i<m_bkgmap.npix(); i++) { 517 + for (int i = 0; i < m_bkgmap.npix(); ++i) {
503 m_solidangle.push_back(m_bkgmap.solidangle(i)); 518 m_solidangle.push_back(m_bkgmap.solidangle(i));
504 m_dirs.push_back(m_bkgmap.inx2dir(i)); 519 m_dirs.push_back(m_bkgmap.inx2dir(i));
505 } 520 }
@@ -511,7 +526,7 @@ void ctskymap::get_parameters(void) @@ -511,7 +526,7 @@ void ctskymap::get_parameters(void)
511 m_onmap = create_map(m_obs); 526 m_onmap = create_map(m_obs);
512 } 527 }
513 528
514 - } 529 + } // endif: background subtraction selected
515 530
516 // Write parameters into logger 531 // Write parameters into logger
517 log_parameters(TERSE); 532 log_parameters(TERSE);
@@ -623,13 +638,15 @@ void ctskymap::map_events(GCTAObservation* obs) @@ -623,13 +638,15 @@ void ctskymap::map_events(GCTAObservation* obs)
623 * 638 *
624 * NONE - No background estimation 639 * NONE - No background estimation
625 * IRF - Background estimation based on IRF template 640 * IRF - Background estimation based on IRF template
  641 + * RING - Ring background estimation
626 ***************************************************************************/ 642 ***************************************************************************/
627 void ctskymap::map_background(GCTAObservation* obs) 643 void ctskymap::map_background(GCTAObservation* obs)
628 { 644 {
629 // Dispatch to appropriate background estimation method 645 // Dispatch to appropriate background estimation method
630 if (m_bkgsubtract == "IRF") { 646 if (m_bkgsubtract == "IRF") {
631 map_background_irf(obs); 647 map_background_irf(obs);
632 - } else if (m_bkgsubtract == "RING") { 648 + }
  649 + else if (m_bkgsubtract == "RING") {
633 map_background_ring(obs); 650 map_background_ring(obs);
634 } 651 }
635 652
@@ -738,7 +755,8 @@ void ctskymap::map_background_irf(GCTAObservation* obs) @@ -738,7 +755,8 @@ void ctskymap::map_background_irf(GCTAObservation* obs)
738 755
739 756
740 /***********************************************************************//** 757 /***********************************************************************//**
741 - * @brief Estimates the background in sky map based on the ring background method 758 + * @brief Estimates the background in sky map based on the ring background
  759 + * method
742 * 760 *
743 * @param[in] obs CTA observation. 761 * @param[in] obs CTA observation.
744 * 762 *
@@ -746,16 +764,16 @@ void ctskymap::map_background_irf(GCTAObservation* obs) @@ -746,16 +764,16 @@ void ctskymap::map_background_irf(GCTAObservation* obs)
746 * No response information available for observation. 764 * No response information available for observation.
747 * No background template available in instrument response function. 765 * No background template available in instrument response function.
748 * 766 *
749 - * Estimates the background in the sky map by summing the events within a ring  
750 - * centered at a given pixel's position. The pixels in the ring are weighted  
751 - * also by the background IRF value 767 + * Estimates the background in the sky map by summing the events within a
  768 + * ring centered at a given pixel's position. The pixels in the ring are
  769 + * weighted also by the background IRF value.
752 ***************************************************************************/ 770 ***************************************************************************/
753 void ctskymap::map_background_ring(GCTAObservation* obs) 771 void ctskymap::map_background_ring(GCTAObservation* obs)
754 { 772 {
755 // Get IRF response (to scale background counts) 773 // Get IRF response (to scale background counts)
756 const GCTAResponseIrf* rsp = dynamic_cast<const GCTAResponseIrf*>(obs->response()); 774 const GCTAResponseIrf* rsp = dynamic_cast<const GCTAResponseIrf*>(obs->response());
757 775
758 - // Throw an exception if observation has no instrument response function 776 + // Throw an exception if observation has no instrument response function
759 if (rsp == NULL) { 777 if (rsp == NULL) {
760 std::string msg = "No response information available for "+ 778 std::string msg = "No response information available for "+
761 get_obs_header(obs)+" to compute IRF background. " 779 get_obs_header(obs)+" to compute IRF background. "
@@ -784,9 +802,8 @@ void ctskymap::map_background_ring(GCTAObservation* obs) @@ -784,9 +802,8 @@ void ctskymap::map_background_ring(GCTAObservation* obs)
784 GCTARoi roi = obs->roi(); 802 GCTARoi roi = obs->roi();
785 803
786 // Initialise statistics 804 // Initialise statistics
787 - int calls = 0;  
788 - double total = 0.0;  
789 - double exposure = obs->livetime() ; 805 + int calls = 0;
  806 + double exposure = obs->livetime();
790 807
791 // Loop over all map pixels 808 // Loop over all map pixels
792 for (int i = 0; i < m_bkgmap.npix(); ++i) { 809 for (int i = 0; i < m_bkgmap.npix(); ++i) {
@@ -807,23 +824,26 @@ void ctskymap::map_background_ring(GCTAObservation* obs) @@ -807,23 +824,26 @@ void ctskymap::map_background_ring(GCTAObservation* obs)
807 ctskymap::irf_kern integrand(bkg, &instdir); 824 ctskymap::irf_kern integrand(bkg, &instdir);
808 GIntegral integral(&integrand); 825 GIntegral integral(&integrand);
809 826
810 - // Set precision (has been carefully adjusted using a test simulation  
811 - // over the energy range 20 GeV - 120 TeV) 827 + // Set precision
812 integral.eps(1.0e-6); 828 integral.eps(1.0e-6);
813 829
814 - // Do Romberg integration to get the sensitivity (note: this assumes the  
815 - // background IRF is a good approximation for the radial sensitivity) 830 + // Do Romberg integration to get the sensitivity (note: this assumes
  831 + // the background IRF is a good approximation for the radial
  832 + // sensitivity)
816 m_alphamap(i,0) += integral.romberg(lnEmin, lnEmax) * 833 m_alphamap(i,0) += integral.romberg(lnEmin, lnEmax) *
817 - m_solidangle[i] * obs->livetime(); 834 + m_solidangle[i] * exposure;
818 835
819 // Store the counts in this bin 836 // Store the counts in this bin
820 m_onmap(i,0) += m_skymap(i,0); 837 m_onmap(i,0) += m_skymap(i,0);
821 - 838 +
822 // Update number of background function calls 839 // Update number of background function calls
823 calls += integral.calls(); 840 calls += integral.calls();
824 841
825 } // endfor: Loop for caching bkg IRF sensitivity 842 } // endfor: Loop for caching bkg IRF sensitivity
826 843
  844 + // Log background subtraction results
  845 + log_value(NORMAL, "Alpha map evaluations", calls);
  846 +
827 // Zero out the counts map to prepare it for the next observation 847 // Zero out the counts map to prepare it for the next observation
828 m_skymap = 0.0; 848 m_skymap = 0.0;
829 849
@@ -858,12 +878,13 @@ void ctskymap::write_hdu_keywords(GFitsHDU* hdu) const @@ -858,12 +878,13 @@ void ctskymap::write_hdu_keywords(GFitsHDU* hdu) const
858 878
859 879
860 /***********************************************************************//** 880 /***********************************************************************//**
861 - * @brief Computes the bin-by-bin significance if background subtraction requested 881 + * @brief Computes the bin-by-bin significance if background subtraction
  882 + * requested
862 * 883 *
863 * Method computes the bin-by bin significance if a background subtraction 884 * Method computes the bin-by bin significance if a background subtraction
864 - * method is specified. If method is "IRF", Poisson statistics (in the Gaussian  
865 - * limit) are assumed. If method is "RING" a Li & Ma significance is computed  
866 - * for each bin (as well as a map of the computed alpha values). 885 + * method is specified. If method is "IRF", Poisson statistics (in the
  886 + * Gaussian limit) are assumed. If method is "RING" a Li & Ma significance
  887 + * is computed for each bin (as well as a map of the computed alpha values).
867 ***************************************************************************/ 888 ***************************************************************************/
868 void ctskymap::map_significance(void) 889 void ctskymap::map_significance(void)
869 { 890 {
@@ -875,16 +896,19 @@ void ctskymap::map_significance(void) @@ -875,16 +896,19 @@ void ctskymap::map_significance(void)
875 log_value(NORMAL, "Total pixels to process", m_onmap.npix()); 896 log_value(NORMAL, "Total pixels to process", m_onmap.npix());
876 897
877 // Store the number of bins with an inappropriate alpha parameter 898 // Store the number of bins with an inappropriate alpha parameter
878 - int num_bad_alpha(0); 899 + int num_bad_alpha = 0;
  900 +
879 // Loop through each bin in the on-counts map 901 // Loop through each bin in the on-counts map
880 for (int i = 0; i < m_onmap.npix(); ++i) { 902 for (int i = 0; i < m_onmap.npix(); ++i) {
881 903
882 - // Reset the on/off counts and alpha for this bin  
883 - double n_on(0), n_off(0), alpha(0.0); 904 + // Initialise the on/off counts and alpha for this bin
  905 + double n_on = 0.0;
  906 + double n_off = 0.0;
  907 + double alpha = 0.0;
884 908
885 // Since this can take a long time, keep the user updated on 909 // Since this can take a long time, keep the user updated on
886 // the progress when another 10% of pixels is processed 910 // the progress when another 10% of pixels is processed
887 - if (i%(m_onmap.npix()/10)==0) { 911 + if (i % (m_onmap.npix()/10) == 0) {
888 log_value(NORMAL, "Pixels remaining", m_onmap.npix()-i); 912 log_value(NORMAL, "Pixels remaining", m_onmap.npix()-i);
889 } 913 }
890 914
@@ -895,49 +919,51 @@ void ctskymap::map_significance(void) @@ -895,49 +919,51 @@ void ctskymap::map_significance(void)
895 compute_ring_values(m_onmap, m_alphamap, skydir, 919 compute_ring_values(m_onmap, m_alphamap, skydir,
896 n_on, n_off, alpha); 920 n_on, n_off, alpha);
897 921
898 - // If alpha is reasonable, then store the results 922 + // If alpha is zero then increment the bad alpha counter
899 if (alpha == 0.0) { 923 if (alpha == 0.0) {
900 - // Increment the bad alpha counter  
901 num_bad_alpha++; 924 num_bad_alpha++;
902 } 925 }
903 - 926 +
  927 + // ... otherwise store the results
904 else { 928 else {
905 - // Save the on-counts and alpha-weighted off-counts 929 +
  930 + // Store the on-counts and alpha-weighted off-counts
906 m_skymap(i,0) = n_on; 931 m_skymap(i,0) = n_on;
907 m_bkgmap(i,0) = alpha * n_off; 932 m_bkgmap(i,0) = alpha * n_off;
908 933
909 - // Compute significance 934 + // Compute and store significance (Li & Ma eq. 17)
910 if (n_on == 0.0) { 935 if (n_on == 0.0) {
911 -  
912 - // The significance computation reduces to the following form  
913 m_sigmap(i,0) = -2.0 * n_off * std::log(1.0+alpha); 936 m_sigmap(i,0) = -2.0 * n_off * std::log(1.0+alpha);
914 -  
915 - } else {  
916 - // Save the significance  
917 - m_sigmap(i,0) = (n_on < (alpha*n_off) ? -2.0:2.0 ) *  
918 - (n_on * std::log((1.0+alpha) * n_on / (alpha * (n_on+n_off))) +  
919 - n_off * std::log((1.0+alpha) * n_off / (n_on+n_off)));  
920 } 937 }
921 - }  
922 - } // endfor: Loop over all pixels 938 + else {
  939 + m_sigmap(i,0) = (n_on < (alpha*n_off) ? -2.0 : 2.0) *
  940 + (n_on * std::log((1.0+alpha) *
  941 + n_on / (alpha * (n_on+n_off))) +
  942 + n_off * std::log((1.0+alpha) *
  943 + n_off / (n_on+n_off)));
  944 + }
  945 +
  946 + } // endelse: alpha was non-zero
  947 +
  948 + } // endfor: looped over all pixels
923 949
924 // Log the number of bad-alpha bins 950 // Log the number of bad-alpha bins
925 - log_value(NORMAL, "Bins with bad alpha", num_bad_alpha); 951 + log_value(NORMAL, "Bins with alpha=0", num_bad_alpha);
926 952
927 - // Now take the square root. Since some bins can be negative, first take  
928 - // the absolute value of the map, square root that, then multiply each  
929 - // bin by its sign to preserve the +/- significance.  
930 - m_sigmap = sign(m_sigmap) * sqrt( abs(m_sigmap) );  
931 - } 953 + // Now take the square root. Since some bins can be negative, first
  954 + // take the absolute value of the map, square root that, then multiply
  955 + // each bin by its sign to preserve the +/- significance.
  956 + m_sigmap = sign(m_sigmap) * sqrt(abs(m_sigmap));
  957 +
  958 + } // endif: ring background selected
932 959
933 // Compute significance using Poisson statistics in the Gaussian limit 960 // Compute significance using Poisson statistics in the Gaussian limit
934 else { 961 else {
935 -  
936 - // Compute significance map  
937 m_sigmap = (m_skymap - m_bkgmap) / sqrt(m_skymap); 962 m_sigmap = (m_skymap - m_bkgmap) / sqrt(m_skymap);
938 -  
939 - } // endif: background subtraction was requested  
940 - 963 + }
  964 +
  965 + // Return
  966 + return;
941 } 967 }
942 968
943 969
@@ -957,13 +983,17 @@ void ctskymap::map_significance(void) @@ -957,13 +983,17 @@ void ctskymap::map_significance(void)
957 void ctskymap::compute_ring_values(const GSkyMap& counts, 983 void ctskymap::compute_ring_values(const GSkyMap& counts,
958 const GSkyMap& sensitivity, 984 const GSkyMap& sensitivity,
959 const GSkyDir& position, 985 const GSkyDir& position,
960 - double& non, double& noff, double& alpha) 986 + double& non,
  987 + double& noff,
  988 + double& alpha)
961 { 989 {
962 - // Reset non and noff  
963 - non = 0.0; 990 + // Reset Non and Noff
  991 + non = 0.0;
964 noff = 0.0; 992 noff = 0.0;
965 - double alpha_on(0.0);  
966 - double alpha_off(0.0); 993 +
  994 + // Initialise On and Off alpha
  995 + double alpha_on = 0.0;
  996 + double alpha_off = 0.0;
967 997
968 // Define the regions necessary to do the pixel checks 998 // Define the regions necessary to do the pixel checks
969 GSkyRegionCircle roi_reg(position, m_roiradius); 999 GSkyRegionCircle roi_reg(position, m_roiradius);
@@ -976,9 +1006,8 @@ void ctskymap::compute_ring_values(const GSkyMap&amp; counts, @@ -976,9 +1006,8 @@ void ctskymap::compute_ring_values(const GSkyMap&amp; counts,
976 // Get the index and sky direction of this pixel 1006 // Get the index and sky direction of this pixel
977 GSkyDir& skydir = m_dirs[j]; 1007 GSkyDir& skydir = m_dirs[j];
978 1008
979 - // Check if it is in the background region  
980 - if ((m_exclmap(j,0) == 1.0) &&  
981 - outer_reg.contains(skydir) && 1009 + // Check if pixel is inside the background region
  1010 + if ((m_exclmap(j,0) == 0.0) && outer_reg.contains(skydir) &&
982 !inner_reg.contains(skydir)) { 1011 !inner_reg.contains(skydir)) {
983 1012
984 // Update n_off 1013 // Update n_off
@@ -986,9 +1015,10 @@ void ctskymap::compute_ring_values(const GSkyMap&amp; counts, @@ -986,9 +1015,10 @@ void ctskymap::compute_ring_values(const GSkyMap&amp; counts,
986 1015
987 // Update alpha_off 1016 // Update alpha_off
988 alpha_off += sensitivity(j,0); 1017 alpha_off += sensitivity(j,0);
  1018 +
989 } 1019 }
990 1020
991 - // Check if pixel is inside source region 1021 + // ... otherwise check if pixel is inside source region
992 else if (roi_reg.contains(skydir)) { 1022 else if (roi_reg.contains(skydir)) {
993 1023
994 // Update n_on for significance computation 1024 // Update n_on for significance computation
@@ -998,55 +1028,67 @@ void ctskymap::compute_ring_values(const GSkyMap&amp; counts, @@ -998,55 +1028,67 @@ void ctskymap::compute_ring_values(const GSkyMap&amp; counts,
998 alpha_on += sensitivity(j,0); 1028 alpha_on += sensitivity(j,0);
999 1029
1000 } // endif: source and background region check 1030 } // endif: source and background region check
1001 -  
1002 - } // endfor: sub-loop over pixels  
1003 1031
1004 - // Compute alpha 1032 + } // endfor: looped over pixels
  1033 +
  1034 + // Compute alpha. If the off region does not have any sensitivity then
  1035 + // set Non = Noff = 0
1005 if (alpha_off == 0.0) { 1036 if (alpha_off == 0.0) {
1006 - // The off region does not have any sensitivity or there are no events  
1007 - // in either the on region or the off region  
1008 alpha = 0.0; 1037 alpha = 0.0;
1009 non = 0.0; 1038 non = 0.0;
1010 noff = 0.0; 1039 noff = 0.0;
1011 -  
1012 - } else { 1040 + }
  1041 + else {
1013 alpha = alpha_on / alpha_off; 1042 alpha = alpha_on / alpha_off;
1014 } 1043 }
  1044 +
  1045 + // Return
1015 return; 1046 return;
1016 } 1047 }
1017 1048
1018 1049
1019 /***********************************************************************//** 1050 /***********************************************************************//**
1020 - * @brief Generates a map of pixels to be excluded from background estimation 1051 + * @brief Generates map of pixel exclusions
1021 * 1052 *
1022 - * @param[in] filename Filename from which to take the exclusions from 1053 + * @param[in] filename Exclusion file name.
  1054 + *
  1055 + * Generates a sky map of the pixels that are to be excluded from the
  1056 + * background estimation. Pixels with values different from 0 will be
  1057 + * excluded.
1023 ***************************************************************************/ 1058 ***************************************************************************/
1024 void ctskymap::map_exclusions(const GFilename& filename) 1059 void ctskymap::map_exclusions(const GFilename& filename)
1025 { 1060 {
1026 - // Create the exlusion map and set all pixels to 1 1061 + // Create exlusion map
1027 m_exclmap = create_map(m_obs); 1062 m_exclmap = create_map(m_obs);
1028 - m_exclmap = 1.0; 1063 +
  1064 + // Set all pixels to 0 (no pixel excluded)
  1065 + m_exclmap = 0.0;
1029 1066
1030 // Make sure the exclusions filename is valid 1067 // Make sure the exclusions filename is valid
1031 - if (!is_valid_filename(filename)) {  
1032 - return;  
1033 - } 1068 + if (is_valid_filename(filename)) {
1034 1069
1035 - // Fill the exclusions based on the regions supplied  
1036 - if (filename.is_fits()) {  
1037 - map_exclusions_fits(filename);  
1038 - } else {  
1039 - map_exclusions_reg(filename);  
1040 - } // endif: generate exclusion map from filename 1070 + // Fill the exclusions based on the regions supplied
  1071 + if (filename.is_fits()) {
  1072 + map_exclusions_fits(filename);
  1073 + }
  1074 + else {
  1075 + map_exclusions_reg(filename);
  1076 + }
1041 1077
  1078 + } // endif: filename was valid
  1079 +
  1080 + // Return
1042 return; 1081 return;
1043 } 1082 }
1044 1083
1045 1084
1046 /***********************************************************************//** 1085 /***********************************************************************//**
1047 - * @brief Fills exclusions map if file is a FITS image 1086 + * @brief Fills exclusions map from FITS image
1048 * 1087 *
1049 - * @param[in] filename Filename from which to take the exclusions from 1088 + * @param[in] filename FITS image file name.
  1089 + *
  1090 + * Sets all exclusion map pixels to 1 that correspond to non-zero pixels in
  1091 + * the exclusion sky map FITS file.
1050 ***************************************************************************/ 1092 ***************************************************************************/
1051 void ctskymap::map_exclusions_fits(const GFilename& filename) 1093 void ctskymap::map_exclusions_fits(const GFilename& filename)
1052 { 1094 {
@@ -1054,26 +1096,33 @@ void ctskymap::map_exclusions_fits(const GFilename&amp; filename) @@ -1054,26 +1096,33 @@ void ctskymap::map_exclusions_fits(const GFilename&amp; filename)
1054 GSkyMap inmap(filename); 1096 GSkyMap inmap(filename);
1055 1097
1056 // Loop through the individual pixels in the exclusion map 1098 // Loop through the individual pixels in the exclusion map
1057 - GSkyDir dir;  
1058 - for (int i=0; i<m_exclmap.npix(); i++) { 1099 + for (int i = 0; i < m_exclmap.npix(); ++i) {
  1100 +
1059 // Get the pixel direction 1101 // Get the pixel direction
1060 - dir = m_exclmap.pix2dir(i); 1102 + GSkyDir dir = m_exclmap.pix2dir(i);
1061 1103
1062 // Check this sky position in the fits map 1104 // Check this sky position in the fits map
1063 - if (inmap.contains(dir) && (inmap(inmap.dir2inx(dir)) == 0.0)) { 1105 + if (inmap.contains(dir) && (inmap(inmap.dir2inx(dir)) != 0.0)) {
1064 1106
1065 - // Set the pixel to 0  
1066 - m_exclmap(i) = 0.0; 1107 + // Set the pixel to 1
  1108 + m_exclmap(i) = 1.0;
1067 1109
1068 } // endif: pixel,region overlap check 1110 } // endif: pixel,region overlap check
1069 - } // endfor: Loop over exclusion map pixels 1111 +
  1112 + } // endfor: looped over exclusion map pixels
  1113 +
  1114 + // Return
  1115 + return;
1070 } 1116 }
1071 1117
1072 1118
1073 /***********************************************************************//** 1119 /***********************************************************************//**
1074 - * @brief Fills exclusions map if file is a DS9 region file 1120 + * @brief Fills exclusions map from DS9 region file
  1121 + *
  1122 + * @param[in] filename DS9 region file name.
1075 * 1123 *
1076 - * @param[in] filename Filename from which to take the exclusions from 1124 + * Sets all exclusion map pixels to 1 that are contained in any of the DS9
  1125 + * regions.
1077 ***************************************************************************/ 1126 ***************************************************************************/
1078 void ctskymap::map_exclusions_reg(const GFilename& filename) 1127 void ctskymap::map_exclusions_reg(const GFilename& filename)
1079 { 1128 {
@@ -1081,18 +1130,20 @@ void ctskymap::map_exclusions_reg(const GFilename&amp; filename) @@ -1081,18 +1130,20 @@ void ctskymap::map_exclusions_reg(const GFilename&amp; filename)
1081 GSkyRegions regions(filename); 1130 GSkyRegions regions(filename);
1082 1131
1083 // Loop through the individual pixels in the exclusion map 1132 // Loop through the individual pixels in the exclusion map
1084 - for (int i=0; i<m_exclmap.npix(); i++) { 1133 + for (int i = 0; i < m_exclmap.npix(); ++i) {
1085 1134
1086 // Get the pixel position 1135 // Get the pixel position
1087 GSkyDir dir = m_exclmap.pix2dir(i); 1136 GSkyDir dir = m_exclmap.pix2dir(i);
1088 1137
1089 - // Check if GSkyDir overlaps with the regions 1138 + // If pixel position overlaps with the regions
1090 if (regions.contains(dir)) { 1139 if (regions.contains(dir)) {
1091 - // Set the pixel to 0  
1092 - m_exclmap(i) = 0.0; 1140 + m_exclmap(i) = 1.0;
  1141 + }
1093 1142
1094 - } // endif: pixel,region overlap check  
1095 - } // endfor: loop over exclusion map pixels 1143 + } // endfor: looped over exclusion map pixels
  1144 +
  1145 + // Return
  1146 + return;
1096 } 1147 }
1097 1148
1098 1149
@@ -1115,4 +1166,3 @@ double ctskymap::irf_kern::eval(const double&amp; lnE) @@ -1115,4 +1166,3 @@ double ctskymap::irf_kern::eval(const double&amp; lnE)
1115 // Return value 1166 // Return value
1116 return value; 1167 return value;
1117 } 1168 }
1118 -  
src/ctskymap/ctskymap.hpp
@@ -100,21 +100,19 @@ protected: @@ -100,21 +100,19 @@ protected:
100 double m_emin; //!< Minimum energy (TeV) 100 double m_emin; //!< Minimum energy (TeV)
101 double m_emax; //!< Maximum energy (TeV) 101 double m_emax; //!< Maximum energy (TeV)
102 std::string m_bkgsubtract; //!< Background subtraction method 102 std::string m_bkgsubtract; //!< Background subtraction method
  103 + double m_roiradius; //!< Region of interest radius for RING bkg.
  104 + double m_inradius; //!< Inner ring radius for RING bkg.
  105 + double m_outradius; //!< Outer ring radius for RING bkg.
103 bool m_publish; //!< Publish sky map? 106 bool m_publish; //!< Publish sky map?
104 GChatter m_chatter; //!< Chattiness 107 GChatter m_chatter; //!< Chattiness
105 108
106 - // Parameters for configuring the RING background subtraction  
107 - double m_roiradius; //!< Region of interest radius  
108 - double m_inradius; //!< Inner ring radius  
109 - double m_outradius; //!< Outer ring radius  
110 -  
111 // Protected members 109 // Protected members
112 - GSkyMap m_skymap; //!< Sky map  
113 - GSkyMap m_bkgmap; //!< Background map  
114 - GSkyMap m_sigmap; //!< Significance map  
115 - GSkyMap m_exclmap; //!< Exclusion map (if "RING" bkg subtraction)  
116 - GSkyMap m_alphamap; //!< Alpha map values (if "RING" subtraction)  
117 - GSkyMap m_onmap; //!< On counts map (if "RING" subtraction) 110 + GSkyMap m_skymap; //!< Sky map
  111 + GSkyMap m_bkgmap; //!< Background map
  112 + GSkyMap m_sigmap; //!< Significance map
  113 + GSkyMap m_exclmap; //!< Exclusion map for RING background
  114 + GSkyMap m_alphamap; //!< Alpha map values for RING background
  115 + GSkyMap m_onmap; //!< On counts map for RING background
118 116
119 // Caching variables to prevent multiple computations of the same thing 117 // Caching variables to prevent multiple computations of the same thing
120 std::vector<double> m_solidangle; //!< Cached pixel solid angles 118 std::vector<double> m_solidangle; //!< Cached pixel solid angles
src/ctskymap/ctskymap.par
@@ -46,7 +46,7 @@ bkgsubtract, s, a, NONE, NONE|IRF|RING,, &quot;Background subtraction method&quot; @@ -46,7 +46,7 @@ bkgsubtract, s, a, NONE, NONE|IRF|RING,, &quot;Background subtraction method&quot;
46 roiradius, r, a, 0.0,,, "Source region radius for estimating on-counts (degrees)" 46 roiradius, r, a, 0.0,,, "Source region radius for estimating on-counts (degrees)"
47 inradius, r, a, 0.6,,, "Inner ring radius (degrees)" 47 inradius, r, a, 0.6,,, "Inner ring radius (degrees)"
48 outradius, r, a, 0.8,,, "Outer ring radius (degrees)" 48 outradius, r, a, 0.8,,, "Outer ring radius (degrees)"
49 -regfile, f, h, NONE,,, "Exclusion regions definition file" 49 +inexclusion, f, h, NONE,,, "Exclusion map or DS9 region file name"
50 50
51 # 51 #
52 # Standard parameters 52 # Standard parameters
test/test_ctskymap.py
@@ -237,31 +237,27 @@ class Test(test): @@ -237,31 +237,27 @@ class Test(test):
237 self._check_result_file('ctskymap_py4.fits[BACKGROUND]') 237 self._check_result_file('ctskymap_py4.fits[BACKGROUND]')
238 self._check_result_file('ctskymap_py4.fits[SIGNIFICANCE]') 238 self._check_result_file('ctskymap_py4.fits[SIGNIFICANCE]')
239 239
240 - # ================================  
241 - # TEST RING BACKGROUND SUBTRACTION  
242 - # ================================  
243 -  
244 - # Allocate ctskymap tool from observation container 240 + # Test RING background subtraction
245 skymap = ctools.ctskymap(obs) 241 skymap = ctools.ctskymap(obs)
246 skymap['emin'] = 0.1 242 skymap['emin'] = 0.1
247 skymap['emax'] = 100 243 skymap['emax'] = 100
248 - skymap['nxpix'] = 100 # Make region smaller since the 'RING'  
249 - skymap['nypix'] = 100 # method takes longer to run than 'IRF' 244 + skymap['nxpix'] = 100 # Make region smaller since the 'RING'
  245 + skymap['nypix'] = 100 # method takes longer to run than 'IRF'
250 skymap['binsz'] = 0.02 246 skymap['binsz'] = 0.02
251 skymap['coordsys'] = 'CEL' 247 skymap['coordsys'] = 'CEL'
252 skymap['proj'] = 'CAR' 248 skymap['proj'] = 'CAR'
253 skymap['xref'] = 83.63 249 skymap['xref'] = 83.63
254 skymap['yref'] = 22.01 250 skymap['yref'] = 22.01
255 skymap['bkgsubtract'] = 'RING' 251 skymap['bkgsubtract'] = 'RING'
  252 + skymap['roiradius'] = 0.1
  253 + skymap['inradius'] = 0.6
  254 + skymap['outradius'] = 0.8
  255 + skymap['inexclusion'] = 'NONE'
256 skymap['caldb'] = self._caldb 256 skymap['caldb'] = self._caldb
257 skymap['irf'] = self._irf 257 skymap['irf'] = self._irf
258 skymap['outmap'] = 'ctskymap_py5.fits' 258 skymap['outmap'] = 'ctskymap_py5.fits'
259 skymap['logfile'] = 'ctskymap_py5.log' 259 skymap['logfile'] = 'ctskymap_py5.log'
260 skymap['chatter'] = 4 260 skymap['chatter'] = 4
261 - skymap['roiradius'] = 0.1  
262 - skymap['inradius'] = 0.6  
263 - skymap['outradius'] = 0.8  
264 - skymap['regfile'] = 'NONE'  
265 261
266 # Execute tool 262 # Execute tool
267 skymap.logFileOpen() # Needed to get a new log file 263 skymap.logFileOpen() # Needed to get a new log file