Commit a3ed1fd1cf08f614f11e276e5fab2369f7fb3cde

Authored by Jürgen Knödlseder
1 parent 7d605f42

Set maximum CTA King PSF radius to 2 degrees (#2112)

The maximum radius of the King PSF has been increased from 0.7 to 2 degrees to cope with some extreme cases. Now also the maximum PSF radius is computed for a given energy so that if the PSF is smaller than the maximum radius of 2 degrees a smaller region of interest will be used for the computation.
ChangeLog
1 -2017-05-14 1 +2017-06-06
2 2
3 * Version 1.3.0 released 3 * Version 1.3.0 released
4 ========================= 4 =========================
5 5
  6 + Increase maximum CTA King PSF radius to 2 degrees (#2112)
6 Correct set names of spatial GLON/GLAT model parameters (#2113) 7 Correct set names of spatial GLON/GLAT model parameters (#2113)
7 Support "BKG" as name of CTA background template 8 Support "BKG" as name of CTA background template
8 Scale optimizer convergence criterion with step size (#2022) 9 Scale optimizer convergence criterion with step size (#2022)
1 New Features and Important Changes in GammaLib 1.3.0.dev1 1 New Features and Important Changes in GammaLib 1.3.0.dev1
2 2
3 -14 May 2017 3 +6 June 2017
4 4
5 5
6 1. Introduction 6 1. Introduction
@@ -268,6 +268,11 @@ avoid FITS file pointer exceptions (#2021). @@ -268,6 +268,11 @@ avoid FITS file pointer exceptions (#2021).
268 The CTA background template HDUCLAS2 was changed from "BGD" to "BKG". Both strings 268 The CTA background template HDUCLAS2 was changed from "BGD" to "BKG". Both strings
269 are supported by GammaLib. 269 are supported by GammaLib.
270 270
  271 +The maximum radius of the King PSF has been increased from 0.7 to 2 degrees to cope
  272 +with some extreme cases. Now also the maximum PSF radius is computed for a given
  273 +energy so that if the PSF is smaller than the maximum radius of 2 degrees a smaller
  274 +region of interest will be used for the computation (#2112).
  275 +
271 276
272 21. COMPTEL interface 277 21. COMPTEL interface
273 --------------------- 278 ---------------------
inst/cta/include/GCTAPsfKing.hpp
@@ -111,10 +111,11 @@ public: @@ -111,10 +111,11 @@ public:
111 111
112 private: 112 private:
113 // Methods 113 // Methods
114 - void init_members(void);  
115 - void copy_members(const GCTAPsfKing& psf);  
116 - void free_members(void);  
117 - void update(const double& logE, const double& theta) const; 114 + void init_members(void);
  115 + void copy_members(const GCTAPsfKing& psf);
  116 + void free_members(void);
  117 + void update(const double& logE, const double& theta) const;
  118 + double r_max(const double& logE, const double& theta) const;
118 119
119 // Members 120 // Members
120 GFilename m_filename; //!< Name of Aeff response file 121 GFilename m_filename; //!< Name of Aeff response file
@@ -131,6 +132,7 @@ private: @@ -131,6 +132,7 @@ private:
131 mutable double m_par_sigma; //!< King profile sigma (radians) 132 mutable double m_par_sigma; //!< King profile sigma (radians)
132 mutable double m_par_sigma2; //!< King profile sigma squared 133 mutable double m_par_sigma2; //!< King profile sigma squared
133 mutable double m_par_gamma; //!< King profile gamma parameter 134 mutable double m_par_gamma; //!< King profile gamma parameter
  135 + mutable double m_par_rmax; //!< Maximum PSF radius
134 }; 136 };
135 137
136 138
inst/cta/src/GCTAPsfKing.cpp
@@ -49,14 +49,11 @@ @@ -49,14 +49,11 @@
49 /* __ Macros _____________________________________________________________ */ 49 /* __ Macros _____________________________________________________________ */
50 50
51 /* __ Coding definitions _________________________________________________ */ 51 /* __ Coding definitions _________________________________________________ */
52 -#define G_FIX_DELTA_MAX  
53 #define G_SMOOTH_PSF 52 #define G_SMOOTH_PSF
54 53
55 /* __ Debug definitions __________________________________________________ */ 54 /* __ Debug definitions __________________________________________________ */
56 55
57 /* __ Constants __________________________________________________________ */ 56 /* __ Constants __________________________________________________________ */
58 -const double r_max = 0.7 * gammalib::deg2rad; // Maximum delta for fixed  
59 - // delta_max computation  
60 57
61 58
62 /*========================================================================== 59 /*==========================================================================
@@ -198,51 +195,42 @@ double GCTAPsfKing::operator()(const double&amp; delta, @@ -198,51 +195,42 @@ double GCTAPsfKing::operator()(const double&amp; delta,
198 const double& azimuth, 195 const double& azimuth,
199 const bool& etrue) const 196 const bool& etrue) const
200 { 197 {
201 - #if defined(G_FIX_DELTA_MAX)  
202 #if defined(G_SMOOTH_PSF) 198 #if defined(G_SMOOTH_PSF)
203 // Set ramp down radius 199 // Set ramp down radius
204 - static const double ramp_down = 0.95 * r_max;  
205 - static const double norm_down = 1.0 / (r_max - ramp_down);  
206 - #endif 200 + static const double ramp_down = 0.95 * m_par_rmax;
  201 + static const double norm_down = 1.0 / (m_par_rmax - ramp_down);
207 #endif 202 #endif
208 203
209 // Initialise PSF value 204 // Initialise PSF value
210 double psf = 0.0; 205 double psf = 0.0;
211 206
212 - // Compile option: set PSF to zero outside delta_max  
213 - #if defined(G_FIX_DELTA_MAX)  
214 - if (delta <= r_max) {  
215 - #endif  
216 -  
217 // Update the parameter cache 207 // Update the parameter cache
218 update(logE, theta); 208 update(logE, theta);
219 209
220 - // Continue only if normalization is positive  
221 - if (m_par_norm > 0.0) {  
222 -  
223 - // Compute PSF value  
224 - double arg = delta / m_par_sigma;  
225 - double arg2 = arg * arg;  
226 - psf = m_par_norm *  
227 - std::pow((1.0 + 1.0 / (2.0 * m_par_gamma) * arg2), -m_par_gamma);  
228 -  
229 - // If we are at large offset angles, add a smooth ramp down to  
230 - // avoid steps in the log-likelihood computation  
231 - #if defined(G_FIX_DELTA_MAX)  
232 - #if defined(G_SMOOTH_PSF)  
233 - if (delta > ramp_down) {  
234 - double x = norm_down * (delta - ramp_down);  
235 - psf *= 1.0 - x * x;  
236 - }  
237 - #endif  
238 - #endif 210 + // Continue only if delta is smaller than PSF radius
  211 + if (delta <= m_par_rmax) {
  212 +
  213 + // Continue only if normalization is positive
  214 + if (m_par_norm > 0.0) {
  215 +
  216 + // Compute PSF value
  217 + double arg = delta / m_par_sigma;
  218 + double arg2 = arg * arg;
  219 + psf = m_par_norm *
  220 + std::pow((1.0 + 1.0 / (2.0 * m_par_gamma) * arg2), -m_par_gamma);
  221 +
  222 + // If we are at large offset angles, add a smooth ramp down to
  223 + // avoid steps in the log-likelihood computation
  224 + #if defined(G_SMOOTH_PSF)
  225 + if (delta > ramp_down) {
  226 + double x = norm_down * (delta - ramp_down);
  227 + psf *= 1.0 - x * x;
  228 + }
  229 + #endif
239 230
240 - } // endif: normalization was positive 231 + } // endif: normalization was positive
241 232
242 - // Compile option: set PSF to zero outside delta_max  
243 - #if defined(G_FIX_DELTA_MAX)  
244 - }  
245 - #endif 233 + } // endif: PSF radius was smaller than PSF radius
246 234
247 // Return PSF 235 // Return PSF
248 return psf; 236 return psf;
@@ -488,22 +476,17 @@ double GCTAPsfKing::mc(GRan&amp; ran, @@ -488,22 +476,17 @@ double GCTAPsfKing::mc(GRan&amp; ran,
488 // Compute exponent 476 // Compute exponent
489 double exponent = 1.0 / (1.0-m_par_gamma); 477 double exponent = 1.0 / (1.0-m_par_gamma);
490 478
491 - // Compile option: sample until delta <= r_max  
492 - #if defined(G_FIX_DELTA_MAX) 479 + // Sample until delta <= m_par_rmax
493 do { 480 do {
494 - #endif  
495 481
496 - // Get uniform random number  
497 - double u = ran.uniform(); 482 + // Get uniform random number
  483 + double u = ran.uniform();
498 484
499 - // Draw random offset using inversion sampling  
500 - double u_max = (std::pow((1.0 - u), exponent) - 1.0) * m_par_gamma;  
501 - delta = m_par_sigma * std::sqrt(2.0 * u_max); 485 + // Draw random offset using inversion sampling
  486 + double u_max = (std::pow((1.0 - u), exponent) - 1.0) * m_par_gamma;
  487 + delta = m_par_sigma * std::sqrt(2.0 * u_max);
502 488
503 - // Compile option: sample until delta <= r_max  
504 - #if defined(G_FIX_DELTA_MAX)  
505 - } while (delta > r_max);  
506 - #endif 489 + } while (delta > m_par_rmax);
507 490
508 } // endif: normalization was positive 491 } // endif: normalization was positive
509 492
@@ -537,25 +520,13 @@ double GCTAPsfKing::delta_max(const double&amp; logE, @@ -537,25 +520,13 @@ double GCTAPsfKing::delta_max(const double&amp; logE,
537 // Initialise PSF radius 520 // Initialise PSF radius
538 double radius = 0.0; 521 double radius = 0.0;
539 522
540 - // Compile option: use fixed maximum delta  
541 - #if defined(G_FIX_DELTA_MAX)  
542 - radius = r_max;  
543 - #else  
544 -  
545 // Update the parameter cache 523 // Update the parameter cache
546 update(logE, theta); 524 update(logE, theta);
547 525
548 - // Continue only if normalization is positive 526 + // Compute radius if normalization is positive
549 if (m_par_norm > 0.0) { 527 if (m_par_norm > 0.0) {
550 -  
551 - // Compute maximum PSF radius (99.995% containment)  
552 - double F = 0.99995;  
553 - double u_max = (std::pow((1.0 - F), (1.0/(1.0-m_par_gamma))) - 1.0) *  
554 - m_par_gamma;  
555 - radius = m_par_sigma * std::sqrt(2.0 * u_max);  
556 -  
557 - } // endif: King profile parameters were valid  
558 - #endif 528 + radius = r_max(logE, theta);
  529 + }
559 530
560 // Return maximum PSF radius 531 // Return maximum PSF radius
561 return radius; 532 return radius;
@@ -687,6 +658,7 @@ void GCTAPsfKing::init_members(void) @@ -687,6 +658,7 @@ void GCTAPsfKing::init_members(void)
687 m_par_gamma = 0.0; 658 m_par_gamma = 0.0;
688 m_par_sigma = 0.0; 659 m_par_sigma = 0.0;
689 m_par_sigma2 = 0.0; 660 m_par_sigma2 = 0.0;
  661 + m_par_rmax = 0.0;
690 662
691 // Return 663 // Return
692 return; 664 return;
@@ -713,6 +685,7 @@ void GCTAPsfKing::copy_members(const GCTAPsfKing&amp; psf) @@ -713,6 +685,7 @@ void GCTAPsfKing::copy_members(const GCTAPsfKing&amp; psf)
713 m_par_gamma = psf.m_par_gamma; 685 m_par_gamma = psf.m_par_gamma;
714 m_par_sigma = psf.m_par_sigma; 686 m_par_sigma = psf.m_par_sigma;
715 m_par_sigma2 = psf.m_par_sigma2; 687 m_par_sigma2 = psf.m_par_sigma2;
  688 + m_par_rmax = psf.m_par_rmax;
716 689
717 // Return 690 // Return
718 return; 691 return;
@@ -738,10 +711,15 @@ void GCTAPsfKing::free_members(void) @@ -738,10 +711,15 @@ void GCTAPsfKing::free_members(void)
738 * This method updates the PSF parameter cache. As the performance table PSF 711 * This method updates the PSF parameter cache. As the performance table PSF
739 * only depends on energy, the only parameter on which the cache values 712 * only depends on energy, the only parameter on which the cache values
740 * depend is the energy. If the PSF parameters are invalid the m_par_norm 713 * depend is the energy. If the PSF parameters are invalid the m_par_norm
741 - * member will be set to zero. 714 + * member will be set to zero. Valid PSF parameters are \f$\gamma > 1\f$ and
  715 + * \f$\simga > 0\f$.
742 ***************************************************************************/ 716 ***************************************************************************/
743 void GCTAPsfKing::update(const double& logE, const double& theta) const 717 void GCTAPsfKing::update(const double& logE, const double& theta) const
744 { 718 {
  719 + // Maximum PSF radius in radians. We set the maximum radius to 2 degrees
  720 + // since it's hard to imaging that a PSF will ever be larger.
  721 + const double r_max = 2.0 * gammalib::deg2rad;
  722 +
745 // Only compute PSF parameters if arguments have changed 723 // Only compute PSF parameters if arguments have changed
746 if (logE != m_par_logE || theta != m_par_theta) { 724 if (logE != m_par_logE || theta != m_par_theta) {
747 725
@@ -758,31 +736,38 @@ void GCTAPsfKing::update(const double&amp; logE, const double&amp; theta) const @@ -758,31 +736,38 @@ void GCTAPsfKing::update(const double&amp; logE, const double&amp; theta) const
758 m_par_sigma = pars[m_inx_sigma]; 736 m_par_sigma = pars[m_inx_sigma];
759 m_par_sigma2 = m_par_sigma * m_par_sigma; 737 m_par_sigma2 = m_par_sigma * m_par_sigma;
760 738
761 - // Check for parameter sanity  
762 - if (m_par_gamma <= 0.0 || m_par_sigma <= 0.0) { 739 + // Check for parameter sanity. The King function is not defined for
  740 + // gamma values equal or smaller than one and non-positive sigma
  741 + // values. If this is the case we set the normalization and the
  742 + // maximum radius to zero ...
  743 + if (m_par_gamma <= 1.0 || m_par_sigma <= 0.0) {
763 m_par_norm = 0.0; 744 m_par_norm = 0.0;
764 - /*  
765 - std::string msg = "King function parameters gamma="+  
766 - gammalib::str(m_par_gamma)+" or sigma="+  
767 - gammalib::str(m_par_sigma)+" are not positive "  
768 - "for logE="+gammalib::str(logE)+" and theta="+  
769 - gammalib::str(theta)+". Setting normalization "  
770 - "to zero.";  
771 - gammalib::warning(G_UPDATE, msg);  
772 - */ 745 + m_par_rmax = 0.0;
773 } 746 }
  747 +
  748 + // ... otherwise we compute the normalization
774 else { 749 else {
775 750
776 // Determine normalisation for given parameters 751 // Determine normalisation for given parameters
777 m_par_norm = 1.0 / gammalib::twopi * (1.0 - 1.0 / m_par_gamma) / 752 m_par_norm = 1.0 / gammalib::twopi * (1.0 - 1.0 / m_par_gamma) /
778 m_par_sigma2; 753 m_par_sigma2;
779 754
780 - // Optionally correct for fixed delta_max  
781 - #if defined(G_FIX_DELTA_MAX)  
782 - double u_max = (r_max*r_max) / (2.0 * m_par_sigma2);  
783 - double norm = 1.0 - std::pow((1.0 + u_max/m_par_gamma), 1.0-m_par_gamma);  
784 - m_par_norm /= norm;  
785 - #endif 755 + // Determine maximum PSF radius
  756 + m_par_rmax = this->r_max(logE, theta);
  757 +
  758 + // Make sure that radius is smaller than 2 degrees
  759 + if (m_par_rmax > r_max) {
  760 +
  761 + // Restrict PSF radius to 2 degrees
  762 + m_par_rmax = r_max;
  763 +
  764 + // Correct PSF normalization for restriction
  765 + double u_max = (m_par_rmax*m_par_rmax) / (2.0 * m_par_sigma2);
  766 + double norm = 1.0 - std::pow((1.0 + u_max/m_par_gamma),
  767 + (1.0-m_par_gamma));
  768 + m_par_norm /= norm;
  769 +
  770 + } // endif: restricted PSF radius
786 771
787 } // endif: King profile parameters were valid 772 } // endif: King profile parameters were valid
788 773
@@ -791,3 +776,32 @@ void GCTAPsfKing::update(const double&amp; logE, const double&amp; theta) const @@ -791,3 +776,32 @@ void GCTAPsfKing::update(const double&amp; logE, const double&amp; theta) const
791 // Return 776 // Return
792 return; 777 return;
793 } 778 }
  779 +
  780 +
  781 +/***********************************************************************//**
  782 + * @brief Return maximum size of PSF (radians)
  783 + *
  784 + * @param[in] logE Log10 of the true photon energy (TeV).
  785 + * @param[in] theta Offset angle in camera system (rad).
  786 + *
  787 + * Determine the radius beyond which the PSF becomes negligible. This radius
  788 + * is set by this method to where the containment fraction become 99.995%
  789 + * which equals \f$5 \times \sigma\f$ of a Gaussian width.
  790 + *
  791 + * This method requires the m_par_gamma and m_par_sigma to be set to valid
  792 + * values.
  793 + ***************************************************************************/
  794 +double GCTAPsfKing::r_max(const double& logE,
  795 + const double& theta) const
  796 +{
  797 + // Set 99.995% containment fraction
  798 + const double F = 0.99995;
  799 +
  800 + // Compute maximum PSF radius for containment fraction
  801 + double u_max = (std::pow((1.0 - F), (1.0/(1.0-m_par_gamma))) - 1.0) *
  802 + m_par_gamma;
  803 + double radius = m_par_sigma * std::sqrt(2.0 * u_max);
  804 +
  805 + // Return maximum PSF radius
  806 + return radius;
  807 +}