Commit f9cdd63373dca6b76c40b73c2a5f460d6b049e82

Authored by Jürgen Knödlseder
1 parent eda368fe

Add King profile PSF unit test, correctly normalize fixed delta_max PSF, and aut…

…omatically detected King profile PSF upon IRF loading.

A unit test was added to verify that the King profile PSF is normalized correctly. For this purpose a King profile PSF calibration was inserted in slot 2 (bcf/000002) of the calibration database.

As the delta_max has been fixed to 0.7 degrees, the King profile PSF now returns 0 for delta angles larger than 0.7 deg. Furthermore, the PSF is normalized so that the integral from 0 to 0.7 deg gives 1. In that way we assure that the PSF integral is always 1.

The GCTAResponse::load_psf method now automatically detected the King profile PSF. Also, the load method now allow specification of a FITS file as the IRF name.
inst/cta/caldb/data/cta/e/bcf/000002/irf_test.fits
No preview for this file type
inst/cta/include/GCTAPsfKing.hpp
1 /*************************************************************************** 1 /***************************************************************************
2 - * GCTAPsfKing.hpp - CTA point spread function vector class * 2 + * GCTAPsfKing.hpp - King profile CTA point spread function class *
3 * ----------------------------------------------------------------------- * 3 * ----------------------------------------------------------------------- *
4 - * copyright (C) 2012 by Juergen Knoedlseder * 4 + * copyright (C) 2013 by Michael Mayer *
5 * ----------------------------------------------------------------------- * 5 * ----------------------------------------------------------------------- *
6 * * 6 * *
7 * This program is free software: you can redistribute it and/or modify * 7 * This program is free software: you can redistribute it and/or modify *
@@ -20,7 +20,7 @@ @@ -20,7 +20,7 @@
20 ***************************************************************************/ 20 ***************************************************************************/
21 /** 21 /**
22 * @file GCTAPsfKing.hpp 22 * @file GCTAPsfKing.hpp
23 - * @brief CTA point spread function class definition with a King profile 23 + * @brief King profile CTA point spread function class definition
24 * @author Michael Mayer 24 * @author Michael Mayer
25 */ 25 */
26 26
@@ -48,7 +48,7 @@ class GCTAPsfKing : public GCTAPsf { @@ -48,7 +48,7 @@ class GCTAPsfKing : public GCTAPsf {
48 public: 48 public:
49 // Constructors and destructors 49 // Constructors and destructors
50 GCTAPsfKing(void); 50 GCTAPsfKing(void);
51 - GCTAPsfKing(const std::string& filename); 51 + explicit GCTAPsfKing(const std::string& filename);
52 GCTAPsfKing(const GCTAPsfKing& psf); 52 GCTAPsfKing(const GCTAPsfKing& psf);
53 virtual ~GCTAPsfKing(void); 53 virtual ~GCTAPsfKing(void);
54 54
@@ -63,24 +63,24 @@ public: @@ -63,24 +63,24 @@ public:
63 const bool& etrue = true) const; 63 const bool& etrue = true) const;
64 64
65 // Implemented pure virtual methods 65 // Implemented pure virtual methods
66 - void clear(void); 66 + void clear(void);
67 GCTAPsfKing* clone(void) const; 67 GCTAPsfKing* clone(void) const;
68 - void load(const std::string& filename);  
69 - std::string filename(void) const;  
70 - double mc(GRan& ran,  
71 - const double& logE,  
72 - const double& theta = 0.0,  
73 - const double& phi = 0.0,  
74 - const double& zenith = 0.0,  
75 - const double& azimuth = 0.0,  
76 - const bool& etrue = true) const;  
77 - double delta_max(const double& logE,  
78 - const double& theta = 0.0,  
79 - const double& phi = 0.0,  
80 - const double& zenith = 0.0,  
81 - const double& azimuth = 0.0,  
82 - const bool& etrue = true) const;  
83 - std::string print(const GChatter& chatter = NORMAL) const; 68 + void load(const std::string& filename);
  69 + std::string filename(void) const;
  70 + double mc(GRan& ran,
  71 + const double& logE,
  72 + const double& theta = 0.0,
  73 + const double& phi = 0.0,
  74 + const double& zenith = 0.0,
  75 + const double& azimuth = 0.0,
  76 + const bool& etrue = true) const;
  77 + double delta_max(const double& logE,
  78 + const double& theta = 0.0,
  79 + const double& phi = 0.0,
  80 + const double& zenith = 0.0,
  81 + const double& azimuth = 0.0,
  82 + const bool& etrue = true) const;
  83 + std::string print(const GChatter& chatter = NORMAL) const;
84 84
85 85
86 private: 86 private:
@@ -91,15 +91,30 @@ private: @@ -91,15 +91,30 @@ private:
91 void update(const double& logE, const double& theta) const; 91 void update(const double& logE, const double& theta) const;
92 92
93 // Members 93 // Members
94 - std::string m_filename; //!< Name of Aeff response file  
95 - GCTAResponseTable m_psf; //!< PSF response table  
96 -  
97 - mutable double m_par_logE; //!< Cache energy  
98 - mutable double m_par_theta; //!< Cache offset angle  
99 - mutable double m_par_norm; //!< King profile normalization  
100 - mutable double m_par_sigma; //!< King profile sigma (radians)  
101 - mutable double m_par_gamma; //!< King profile gamma parameter 94 + std::string m_filename; //!< Name of Aeff response file
  95 + GCTAResponseTable m_psf; //!< PSF response table
102 96
  97 + // Evaluation cache
  98 + mutable double m_par_logE; //!< Cache energy
  99 + mutable double m_par_theta; //!< Cache offset angle
  100 + mutable double m_par_norm; //!< King profile normalization
  101 + mutable double m_par_sigma; //!< King profile sigma (radians)
  102 + mutable double m_par_sigma2; //!< King profile sigma squared
  103 + mutable double m_par_gamma; //!< King profile gamma parameter
103 }; 104 };
104 105
  106 +
  107 +/***********************************************************************//**
  108 + * @brief Return filename
  109 + *
  110 + * @return Filename from which point spread function was loaded.
  111 + *
  112 + * Returns filename from which point spread function was loaded.
  113 + ***************************************************************************/
  114 +inline
  115 +std::string GCTAPsfKing::filename(void) const
  116 +{
  117 + return m_filename;
  118 +}
  119 +
105 #endif /* GCTAPsfKing_HPP */ 120 #endif /* GCTAPsfKing_HPP */
inst/cta/pyext/GCTAPsfKing.i
1 /*************************************************************************** 1 /***************************************************************************
2 - * GCTAPsfKing.i - CTA point spread function class * 2 + * GCTAPsfKing.i - King profile CTA point spread function class *
3 * ----------------------------------------------------------------------- * 3 * ----------------------------------------------------------------------- *
4 - * copyright (C) 2012 by Michael Mayer * 4 + * copyright (C) 2013 by Michael Mayer *
5 * ----------------------------------------------------------------------- * 5 * ----------------------------------------------------------------------- *
6 * * 6 * *
7 * This program is free software: you can redistribute it and/or modify * 7 * This program is free software: you can redistribute it and/or modify *
@@ -20,7 +20,7 @@ @@ -20,7 +20,7 @@
20 ***************************************************************************/ 20 ***************************************************************************/
21 /** 21 /**
22 * @file GCTAPsfKing.i 22 * @file GCTAPsfKing.i
23 - * @brief CTA point spread function with a King Profile 23 + * @brief King profile CTA point spread function class definition
24 * @author Michael Mayer 24 * @author Michael Mayer
25 */ 25 */
26 %{ 26 %{
@@ -39,11 +39,10 @@ @@ -39,11 +39,10 @@
39 * of energy as determined from a FITS table. 39 * of energy as determined from a FITS table.
40 ***************************************************************************/ 40 ***************************************************************************/
41 class GCTAPsfKing : public GCTAPsf { 41 class GCTAPsfKing : public GCTAPsf {
42 -  
43 public: 42 public:
44 // Constructors and destructors 43 // Constructors and destructors
45 GCTAPsfKing(void); 44 GCTAPsfKing(void);
46 - GCTAPsfKing(const std::string& filename); 45 + explicit GCTAPsfKing(const std::string& filename);
47 GCTAPsfKing(const GCTAPsfKing& psf); 46 GCTAPsfKing(const GCTAPsfKing& psf);
48 virtual ~GCTAPsfKing(void); 47 virtual ~GCTAPsfKing(void);
49 48
@@ -57,24 +56,23 @@ public: @@ -57,24 +56,23 @@ public:
57 const bool& etrue = true) const; 56 const bool& etrue = true) const;
58 57
59 // Implemented pure virtual methods 58 // Implemented pure virtual methods
60 - void clear(void); 59 + void clear(void);
61 GCTAPsfKing* clone(void) const; 60 GCTAPsfKing* clone(void) const;
62 - void load(const std::string& filename);  
63 - std::string filename(void) const;  
64 - double mc(GRan& ran,  
65 - const double& logE,  
66 - const double& theta = 0.0,  
67 - const double& phi = 0.0,  
68 - const double& zenith = 0.0,  
69 - const double& azimuth = 0.0,  
70 - const bool& etrue = true) const;  
71 - double delta_max(const double& logE,  
72 - const double& theta = 0.0,  
73 - const double& phi = 0.0,  
74 - const double& zenith = 0.0,  
75 - const double& azimuth = 0.0,  
76 - const bool& etrue = true) const;  
77 - 61 + void load(const std::string& filename);
  62 + std::string filename(void) const;
  63 + double mc(GRan& ran,
  64 + const double& logE,
  65 + const double& theta = 0.0,
  66 + const double& phi = 0.0,
  67 + const double& zenith = 0.0,
  68 + const double& azimuth = 0.0,
  69 + const bool& etrue = true) const;
  70 + double delta_max(const double& logE,
  71 + const double& theta = 0.0,
  72 + const double& phi = 0.0,
  73 + const double& zenith = 0.0,
  74 + const double& azimuth = 0.0,
  75 + const bool& etrue = true) const;
78 }; 76 };
79 77
80 78
@@ -82,4 +80,7 @@ public: @@ -82,4 +80,7 @@ public:
82 * @brief GCTAPsfKing class extension 80 * @brief GCTAPsfKing class extension
83 ***************************************************************************/ 81 ***************************************************************************/
84 %extend GCTAPsfKing { 82 %extend GCTAPsfKing {
  83 + GCTAPsfKing copy() {
  84 + return (*self);
  85 + }
85 }; 86 };
inst/cta/src/GCTAPsfKing.cpp
1 /*************************************************************************** 1 /***************************************************************************
2 - * GCTAPsfKing.cpp - CTA point spread function vector class * 2 + * GCTAPsfKing.cpp - King profile CTA point spread function class *
3 * ----------------------------------------------------------------------- * 3 * ----------------------------------------------------------------------- *
4 - * copyright (C) 2012 by Juergen Knoedlseder * 4 + * copyright (C) 2013 by Michael Mayer *
5 * ----------------------------------------------------------------------- * 5 * ----------------------------------------------------------------------- *
6 * * 6 * *
7 * This program is free software: you can redistribute it and/or modify * 7 * This program is free software: you can redistribute it and/or modify *
@@ -19,8 +19,8 @@ @@ -19,8 +19,8 @@
19 * * 19 * *
20 ***************************************************************************/ 20 ***************************************************************************/
21 /** 21 /**
22 - * @file GCTAPsfKing.cpp  
23 - * @brief CTA point spread function using a King profile 22 + * @file GCTAPsfKing.hpp
  23 + * @brief King profile CTA point spread function class definition
24 * @author Michael Mayer 24 * @author Michael Mayer
25 */ 25 */
26 26
@@ -35,15 +35,18 @@ @@ -35,15 +35,18 @@
35 #include "GCTAException.hpp" 35 #include "GCTAException.hpp"
36 36
37 /* __ Method name definitions ____________________________________________ */ 37 /* __ Method name definitions ____________________________________________ */
38 -#define G_LOAD "GCTAPsfKing::load(std::string&)" 38 +#define G_UPDATE "GCTAPsfKing::update(double&, double&)"
39 39
40 /* __ Macros _____________________________________________________________ */ 40 /* __ Macros _____________________________________________________________ */
41 41
42 /* __ Coding definitions _________________________________________________ */ 42 /* __ Coding definitions _________________________________________________ */
  43 +#define G_FIX_DELTA_MAX
43 44
44 /* __ Debug definitions __________________________________________________ */ 45 /* __ Debug definitions __________________________________________________ */
45 46
46 /* __ Constants __________________________________________________________ */ 47 /* __ Constants __________________________________________________________ */
  48 +const double r_max = 0.7 * gammalib::deg2rad; // Maximum delta for fixed
  49 + // delta_max computation
47 50
48 51
49 /*========================================================================== 52 /*==========================================================================
@@ -169,16 +172,21 @@ GCTAPsfKing&amp; GCTAPsfKing::operator=(const GCTAPsfKing&amp; psf) @@ -169,16 +172,21 @@ GCTAPsfKing&amp; GCTAPsfKing::operator=(const GCTAPsfKing&amp; psf)
169 * of sr^-1 for a given energy. 172 * of sr^-1 for a given energy.
170 ***************************************************************************/ 173 ***************************************************************************/
171 double GCTAPsfKing::operator()(const double& delta, 174 double GCTAPsfKing::operator()(const double& delta,
172 - const double& logE,  
173 - const double& theta,  
174 - const double& phi,  
175 - const double& zenith,  
176 - const double& azimuth,  
177 - const bool& etrue) const 175 + const double& logE,
  176 + const double& theta,
  177 + const double& phi,
  178 + const double& zenith,
  179 + const double& azimuth,
  180 + const bool& etrue) const
178 { 181 {
179 // Initialise PSF value 182 // Initialise PSF value
180 double psf = 0.0; 183 double psf = 0.0;
181 184
  185 + // Compile option: set PSF to zero outside delta_max
  186 + #if defined(G_FIX_DELTA_MAX)
  187 + if (delta <= r_max) {
  188 + #endif
  189 +
182 // Update the parameter cache 190 // Update the parameter cache
183 update(logE, theta); 191 update(logE, theta);
184 192
@@ -186,10 +194,18 @@ double GCTAPsfKing::operator()(const double&amp; delta, @@ -186,10 +194,18 @@ double GCTAPsfKing::operator()(const double&amp; delta,
186 if (m_par_norm > 0.0) { 194 if (m_par_norm > 0.0) {
187 195
188 // Compute PSF value 196 // Compute PSF value
189 - psf = m_par_norm*pow((1 + 1 / (2 * m_par_gamma) * pow(delta / m_par_sigma, 2)), -m_par_gamma); 197 + double arg = delta / m_par_sigma;
  198 + double arg2 = arg * arg;
  199 + psf = m_par_norm *
  200 + std::pow((1.0 + 1.0 / (2.0 * m_par_gamma) * arg2), -m_par_gamma);
190 201
191 } // endif: normalization was positive 202 } // endif: normalization was positive
192 203
  204 + // Compile option: set PSF to zero outside delta_max
  205 + #if defined(G_FIX_DELTA_MAX)
  206 + }
  207 + #endif
  208 +
193 // Return PSF 209 // Return PSF
194 return psf; 210 return psf;
195 } 211 }
@@ -249,10 +265,10 @@ void GCTAPsfKing::load(const std::string&amp; filename) @@ -249,10 +265,10 @@ void GCTAPsfKing::load(const std::string&amp; filename)
249 GFits file(filename); 265 GFits file(filename);
250 266
251 // Get PSF table 267 // Get PSF table
252 - const GFitsTable* table = file.table("POINT SPREAD FUNCTION"); 268 + const GFitsTable& table = *file.table("POINT SPREAD FUNCTION");
253 269
254 // Read PSF table 270 // Read PSF table
255 - m_psf.read(*table); 271 + m_psf.read(table);
256 272
257 // Set energy axis to logarithmic scale 273 // Set energy axis to logarithmic scale
258 m_psf.axis_log10(0); 274 m_psf.axis_log10(0);
@@ -260,7 +276,7 @@ void GCTAPsfKing::load(const std::string&amp; filename) @@ -260,7 +276,7 @@ void GCTAPsfKing::load(const std::string&amp; filename)
260 // Set offset angle axis to radians 276 // Set offset angle axis to radians
261 m_psf.axis_radians(1); 277 m_psf.axis_radians(1);
262 278
263 - // Convert sigma parameters to radians 279 + // Convert sigma parameter to radians
264 m_psf.scale(1, gammalib::deg2rad); 280 m_psf.scale(1, gammalib::deg2rad);
265 281
266 // Close PSF FITS file 282 // Close PSF FITS file
@@ -275,18 +291,6 @@ void GCTAPsfKing::load(const std::string&amp; filename) @@ -275,18 +291,6 @@ void GCTAPsfKing::load(const std::string&amp; filename)
275 291
276 292
277 /***********************************************************************//** 293 /***********************************************************************//**
278 - * @brief Return filename  
279 - *  
280 - * @return Returns filename from which point spread function was loaded  
281 - ***************************************************************************/  
282 -std::string GCTAPsfKing::filename(void) const  
283 -{  
284 - // Return filename  
285 - return m_filename;  
286 -}  
287 -  
288 -  
289 -/***********************************************************************//**  
290 * @brief Simulate PSF offset (radians) 294 * @brief Simulate PSF offset (radians)
291 * 295 *
292 * @param[in] ran Random number generator. 296 * @param[in] ran Random number generator.
@@ -300,12 +304,12 @@ std::string GCTAPsfKing::filename(void) const @@ -300,12 +304,12 @@ std::string GCTAPsfKing::filename(void) const
300 * Draws a random offset for the King Profile 304 * Draws a random offset for the King Profile
301 ***************************************************************************/ 305 ***************************************************************************/
302 double GCTAPsfKing::mc(GRan& ran, 306 double GCTAPsfKing::mc(GRan& ran,
303 - const double& logE,  
304 - const double& theta,  
305 - const double& phi,  
306 - const double& zenith,  
307 - const double& azimuth,  
308 - const bool& etrue) const 307 + const double& logE,
  308 + const double& theta,
  309 + const double& phi,
  310 + const double& zenith,
  311 + const double& azimuth,
  312 + const bool& etrue) const
309 { 313 {
310 // Initialise random offset 314 // Initialise random offset
311 double delta = 0.0; 315 double delta = 0.0;
@@ -317,7 +321,9 @@ double GCTAPsfKing::mc(GRan&amp; ran, @@ -317,7 +321,9 @@ double GCTAPsfKing::mc(GRan&amp; ran,
317 double u = ran.uniform(); 321 double u = ran.uniform();
318 322
319 // Draw random offset using inversion sampling 323 // Draw random offset using inversion sampling
320 - delta = std::sqrt( ( pow(1.0 - u, 1.0 / (1.0 - m_par_gamma) ) - 1.0 ) * 2.0 * pow(m_par_sigma, 2.0) * m_par_gamma); 324 + //delta = std::sqrt( ( pow(1.0 - u, 1.0 / (1.0 - m_par_gamma) ) - 1.0 ) * 2.0 * pow(m_par_sigma, 2.0) * m_par_gamma);
  325 + delta = std::sqrt( ( std::pow(1.0 - u, 1.0 / (1.0 - m_par_gamma) ) - 1.0 ) *
  326 + 2.0 * m_par_sigma2 * m_par_gamma);
321 327
322 // Return PSF offset 328 // Return PSF offset
323 return delta; 329 return delta;
@@ -335,26 +341,30 @@ double GCTAPsfKing::mc(GRan&amp; ran, @@ -335,26 +341,30 @@ double GCTAPsfKing::mc(GRan&amp; ran,
335 * @param[in] etrue Use true energy (true/false). Not used. 341 * @param[in] etrue Use true energy (true/false). Not used.
336 * 342 *
337 * Determine the radius beyond which the PSF becomes negligible. This radius 343 * Determine the radius beyond which the PSF becomes negligible. This radius
338 - * is set by this method to where the containment fraction become 99.995% which equal  
339 - * \f$5 \times \sigma\f$ of a Gaussian width. 344 + * is set by this method to where the containment fraction become 99.995%
  345 + * which equals \f$5 \times \sigma\f$ of a Gaussian width.
340 ***************************************************************************/ 346 ***************************************************************************/
341 double GCTAPsfKing::delta_max(const double& logE, 347 double GCTAPsfKing::delta_max(const double& logE,
342 - const double& theta,  
343 - const double& phi,  
344 - const double& zenith,  
345 - const double& azimuth,  
346 - const bool& etrue) const 348 + const double& theta,
  349 + const double& phi,
  350 + const double& zenith,
  351 + const double& azimuth,
  352 + const bool& etrue) const
347 { 353 {
  354 + // Compile option: use fixed maximum delta
  355 + #if defined(G_FIX_DELTA_MAX)
  356 + double radius = r_max;
  357 + #else
  358 +
348 // Update the parameter cache 359 // Update the parameter cache
349 update(logE, theta); 360 update(logE, theta);
350 361
351 // Compute maximum PSF radius (99.995% containment) 362 // Compute maximum PSF radius (99.995% containment)
352 - double F = 0.99;  
353 -  
354 - // todo: For now fix maximum radius to 0.7 degree until we found a better method.  
355 - // The 99% containment radius can become quite large due to the long tails of the  
356 - // function  
357 - double radius = 0.7*gammalib::deg2rad; //m_par_sigma * std::sqrt(2 * ( std::pow(1 - F, 1 / (1-m_par_gamma) ) -1 )* m_par_gamma); 363 + double F = 0.99995;
  364 + double u_max = (std::pow((1.0 - F), (1.0/(1.0-m_par_gamma))) - 1.0) *
  365 + m_par_gamma;
  366 + double radius = m_par_sigma * std::sqrt(2.0 * u_max);
  367 + #endif
358 368
359 // Return maximum PSF radius 369 // Return maximum PSF radius
360 return radius; 370 return radius;
@@ -398,11 +408,12 @@ void GCTAPsfKing::init_members(void) @@ -398,11 +408,12 @@ void GCTAPsfKing::init_members(void)
398 // Initialise members 408 // Initialise members
399 m_filename.clear(); 409 m_filename.clear();
400 m_psf.clear(); 410 m_psf.clear();
401 - m_par_logE = -1.0e30;  
402 - m_par_theta = -1.0;  
403 - m_par_norm = 0.0;  
404 - m_par_gamma = 0.0;  
405 - m_par_sigma = 0.0; 411 + m_par_logE = -1.0e30;
  412 + m_par_theta = -1.0;
  413 + m_par_norm = 0.0;
  414 + m_par_gamma = 0.0;
  415 + m_par_sigma = 0.0;
  416 + m_par_sigma2 = 0.0;
406 417
407 // Return 418 // Return
408 return; 419 return;
@@ -417,12 +428,14 @@ void GCTAPsfKing::init_members(void) @@ -417,12 +428,14 @@ void GCTAPsfKing::init_members(void)
417 void GCTAPsfKing::copy_members(const GCTAPsfKing& psf) 428 void GCTAPsfKing::copy_members(const GCTAPsfKing& psf)
418 { 429 {
419 // Copy members 430 // Copy members
420 - m_filename = psf.m_filename;  
421 - m_psf = psf.m_psf;  
422 - m_par_logE = psf.m_par_logE;  
423 - m_par_sigma = psf.m_par_sigma;  
424 - m_par_norm = psf.m_par_norm;  
425 - m_par_gamma = psf.m_par_gamma; 431 + m_filename = psf.m_filename;
  432 + m_psf = psf.m_psf;
  433 + m_par_logE = psf.m_par_logE;
  434 + m_par_theta = psf.m_par_theta;
  435 + m_par_norm = psf.m_par_norm;
  436 + m_par_gamma = psf.m_par_gamma;
  437 + m_par_sigma = psf.m_par_sigma;
  438 + m_par_sigma2 = psf.m_par_sigma2;
426 439
427 // Return 440 // Return
428 return; 441 return;
@@ -434,9 +447,6 @@ void GCTAPsfKing::copy_members(const GCTAPsfKing&amp; psf) @@ -434,9 +447,6 @@ void GCTAPsfKing::copy_members(const GCTAPsfKing&amp; psf)
434 ***************************************************************************/ 447 ***************************************************************************/
435 void GCTAPsfKing::free_members(void) 448 void GCTAPsfKing::free_members(void)
436 { 449 {
437 - m_filename.clear();  
438 - m_psf.clear();  
439 -  
440 // Return 450 // Return
441 return; 451 return;
442 } 452 }
@@ -447,6 +457,9 @@ void GCTAPsfKing::free_members(void) @@ -447,6 +457,9 @@ void GCTAPsfKing::free_members(void)
447 * 457 *
448 * @param[in] logE Log10 of the true photon energy (TeV). 458 * @param[in] logE Log10 of the true photon energy (TeV).
449 * 459 *
  460 + * @exception GException::invalid_value
  461 + * No valid point spread function information has been found.
  462 + *
450 * This method updates the PSF parameter cache. As the performance table PSF 463 * This method updates the PSF parameter cache. As the performance table PSF
451 * only depends on energy, the only parameter on which the cache values 464 * only depends on energy, the only parameter on which the cache values
452 * depend is the energy. 465 * depend is the energy.
@@ -457,20 +470,42 @@ void GCTAPsfKing::update(const double&amp; logE, const double&amp; theta) const @@ -457,20 +470,42 @@ void GCTAPsfKing::update(const double&amp; logE, const double&amp; theta) const
457 if (logE != m_par_logE || theta != m_par_theta) { 470 if (logE != m_par_logE || theta != m_par_theta) {
458 471
459 // Save parameters 472 // Save parameters
460 - m_par_logE = logE; 473 + m_par_logE = logE;
461 m_par_theta = theta; 474 m_par_theta = theta;
462 475
463 // Determine sigma and gamma by interpolating between nodes 476 // Determine sigma and gamma by interpolating between nodes
464 std::vector<double> pars = m_psf(logE,theta); 477 std::vector<double> pars = m_psf(logE,theta);
465 478
466 - m_par_gamma = pars[0];  
467 - m_par_sigma = pars[1]; 479 + // Throw an exception if there are not 2 parameters
  480 + if (pars.size() != 2) {
  481 + std::string msg = gammalib::str(pars.size()) + " parameters have"
  482 + " been found in the response table of the"
  483 + " King profile response function while 2"
  484 + " parameters are expected.\n"
  485 + "Possibly, the point spread function information"
  486 + " has not yet been loaded. Please load the point"
  487 + " spread function before using it.";
  488 + throw GException::invalid_value(G_UPDATE, msg);
  489 + }
  490 +
  491 + // Set parameters
  492 + m_par_gamma = pars[0];
  493 + m_par_sigma = pars[1];
  494 + m_par_sigma2 = m_par_sigma * m_par_sigma;
468 495
469 // Determine normalisation for given parameters 496 // Determine normalisation for given parameters
470 - m_par_norm = 1.0 / gammalib::twopi * pow(m_par_sigma, -2)*(1.0 - 1.0 / m_par_gamma); 497 + m_par_norm = 1.0 / gammalib::twopi * (1.0 - 1.0 / m_par_gamma) /
  498 + m_par_sigma2;
  499 +
  500 + // Optionally correct for fixed delta_max
  501 + #if defined(G_FIX_DELTA_MAX)
  502 + double u_max = (r_max*r_max) / (2.0 * m_par_sigma2);
  503 + double norm = 1.0 - std::pow((1.0 + u_max/m_par_gamma), 1.0-m_par_gamma);
  504 + m_par_norm /= norm;
  505 + #endif
  506 +
471 } 507 }
472 508
473 // Return 509 // Return
474 return; 510 return;
475 } 511 }
476 -  
inst/cta/src/GCTAResponse.cpp
@@ -53,6 +53,7 @@ @@ -53,6 +53,7 @@
53 #include "GCTAPsf2D.hpp" 53 #include "GCTAPsf2D.hpp"
54 #include "GCTAPsfVector.hpp" 54 #include "GCTAPsfVector.hpp"
55 #include "GCTAPsfPerfTable.hpp" 55 #include "GCTAPsfPerfTable.hpp"
  56 +#include "GCTAPsfKing.hpp"
56 #include "GCTAAeff.hpp" 57 #include "GCTAAeff.hpp"
57 #include "GCTAPsf.hpp" 58 #include "GCTAPsf.hpp"
58 #include "GCTAEdisp.hpp" 59 #include "GCTAEdisp.hpp"
@@ -549,8 +550,13 @@ void GCTAResponse::load(const std::string&amp; irfname) @@ -549,8 +550,13 @@ void GCTAResponse::load(const std::string&amp; irfname)
549 // Restore calibration database name 550 // Restore calibration database name
550 m_caldb = caldb; 551 m_caldb = caldb;
551 552
552 - // Build filename  
553 - std::string filename = m_caldb + "/" + irfname + ".dat"; 553 + // Build filename. If file is not a FITS file then add ".dat" extension
  554 + // which is typical for a performance table.
  555 + std::string filename = m_caldb + "/" + irfname;
  556 + if (irfname.find_last_of(".fits") != irfname.length()-1 ||
  557 + irfname.find_last_of(".fits.gz") != irfname.length()-1) {
  558 + filename += ".dat";
  559 + }
554 560
555 // Load effective area 561 // Load effective area
556 load_aeff(filename); 562 load_aeff(filename);
@@ -679,10 +685,22 @@ void GCTAResponse::load_psf(const std::string&amp; filename) @@ -679,10 +685,22 @@ void GCTAResponse::load_psf(const std::string&amp; filename)
679 GFits file(filename); 685 GFits file(filename);
680 686
681 // If file contains a "POINT SPREAD FUNCTION" extension then load it 687 // If file contains a "POINT SPREAD FUNCTION" extension then load it
682 - // as CTA response table 688 + // as either a King profile PSF or a 2D PSF
683 if (file.contains("POINT SPREAD FUNCTION")) { 689 if (file.contains("POINT SPREAD FUNCTION")) {
684 - file.close();  
685 - m_psf = new GCTAPsf2D(filename); 690 + const GFitsTable& table = *file.table("POINT SPREAD FUNCTION");
  691 + if (table.contains("GAMMA") && table.contains("SIGMA")) {
  692 + file.close();
  693 + m_psf = new GCTAPsfKing(filename);
  694 + }
  695 + else if (table.contains("SCALE") && table.contains("SIGMA_1") &&
  696 + table.contains("AMPL_2") && table.contains("SIGMA_2") &&
  697 + table.contains("AMPL_3") && table.contains("SIGMA_3")) {
  698 + file.close();
  699 + m_psf = new GCTAPsf2D(filename);
  700 + }
  701 + else {
  702 + file.close();
  703 + }
686 } 704 }
687 705
688 // ... else load it has PSF vector 706 // ... else load it has PSF vector
inst/cta/test/test_CTA.cpp
@@ -40,16 +40,18 @@ @@ -40,16 +40,18 @@
40 /* __ Globals ____________________________________________________________ */ 40 /* __ Globals ____________________________________________________________ */
41 41
42 /* __ Constants __________________________________________________________ */ 42 /* __ Constants __________________________________________________________ */
43 -const std::string datadir = PACKAGE_SOURCE"/inst/cta/test/data";  
44 -const std::string cta_caldb = PACKAGE_SOURCE"/inst/cta/caldb";  
45 -const std::string cta_irf = "kb_E_50h_v3";  
46 -const std::string cta_events = datadir+"/crab_events.fits.gz";  
47 -const std::string cta_cntmap = datadir+"/crab_cntmap.fits.gz";  
48 -const std::string cta_bin_xml = datadir+"/obs_binned.xml";  
49 -const std::string cta_unbin_xml = datadir+"/obs_unbinned.xml";  
50 -const std::string cta_model_xml = datadir+"/crab.xml";  
51 -const std::string cta_rsp_xml = datadir+"/rsp_models.xml"; 43 +const std::string datadir = PACKAGE_SOURCE"/inst/cta/test/data";
  44 +const std::string cta_caldb = PACKAGE_SOURCE"/inst/cta/caldb";
  45 +const std::string cta_irf = "kb_E_50h_v3";
  46 +const std::string cta_events = datadir+"/crab_events.fits.gz";
  47 +const std::string cta_cntmap = datadir+"/crab_cntmap.fits.gz";
  48 +const std::string cta_bin_xml = datadir+"/obs_binned.xml";
  49 +const std::string cta_unbin_xml = datadir+"/obs_unbinned.xml";
  50 +const std::string cta_model_xml = datadir+"/crab.xml";
  51 +const std::string cta_rsp_xml = datadir+"/rsp_models.xml";
52 const std::string cta_modbck_xml = datadir+"/cta_modelbg.xml"; 52 const std::string cta_modbck_xml = datadir+"/cta_modelbg.xml";
  53 +const std::string cta_caldb_king = PACKAGE_SOURCE"/inst/cta/caldb/data/cta/e/bcf/000002";
  54 +const std::string cta_irf_king = "irf_test.fits";
53 55
54 56
55 /***********************************************************************//** 57 /***********************************************************************//**
@@ -64,6 +66,7 @@ void TestGCTAResponse::set(void) @@ -64,6 +66,7 @@ void TestGCTAResponse::set(void)
64 append(static_cast<pfunction>(&TestGCTAResponse::test_response), "Test response"); 66 append(static_cast<pfunction>(&TestGCTAResponse::test_response), "Test response");
65 append(static_cast<pfunction>(&TestGCTAResponse::test_response_aeff), "Test effective area"); 67 append(static_cast<pfunction>(&TestGCTAResponse::test_response_aeff), "Test effective area");
66 append(static_cast<pfunction>(&TestGCTAResponse::test_response_psf), "Test PSF"); 68 append(static_cast<pfunction>(&TestGCTAResponse::test_response_psf), "Test PSF");
  69 + append(static_cast<pfunction>(&TestGCTAResponse::test_response_psf_king), "Test King profile PSF");
67 append(static_cast<pfunction>(&TestGCTAResponse::test_response_npsf), "Test integrated PSF"); 70 append(static_cast<pfunction>(&TestGCTAResponse::test_response_npsf), "Test integrated PSF");
68 append(static_cast<pfunction>(&TestGCTAResponse::test_response_irf_diffuse), "Test diffuse IRF"); 71 append(static_cast<pfunction>(&TestGCTAResponse::test_response_irf_diffuse), "Test diffuse IRF");
69 append(static_cast<pfunction>(&TestGCTAResponse::test_response_npred_diffuse), "Test diffuse IRF integration"); 72 append(static_cast<pfunction>(&TestGCTAResponse::test_response_npred_diffuse), "Test diffuse IRF integration");
@@ -236,6 +239,44 @@ void TestGCTAResponse::test_response_psf(void) @@ -236,6 +239,44 @@ void TestGCTAResponse::test_response_psf(void)
236 239
237 240
238 /***********************************************************************//** 241 /***********************************************************************//**
  242 + * @brief Test CTA King profile psf computation
  243 + *
  244 + * The Psf computation is tested by integrating numerically the Psf
  245 + * function. Integration is done in a rather simplistic way, by stepping
  246 + * radially away from the centre. The integration is done for a set of
  247 + * energies from 0.1-10 TeV.
  248 + ***************************************************************************/
  249 +void TestGCTAResponse::test_response_psf_king(void)
  250 +{
  251 + // Load response
  252 + GCTAResponse rsp;
  253 + rsp.caldb(cta_caldb_king);
  254 + rsp.load(cta_irf_king);
  255 +
  256 + // Integrate Psf
  257 + GEnergy eng;
  258 + for (double e = 0.1; e < 10.0; e *= 2.0) {
  259 + eng.TeV(e);
  260 + double r_max = rsp.psf()->delta_max(eng.log10TeV()) * gammalib::rad2deg;
  261 + double r = 0.0;
  262 + double dr = 0.0001;
  263 + int steps = int(1.0/dr);
  264 + double sum = 0.0;
  265 + for (int i = 0; i < steps; ++i) {
  266 + r += dr;
  267 + sum += rsp.psf(r * gammalib::deg2rad, 0.0, 0.0, 0.0, 0.0, eng.log10TeV()) *
  268 + gammalib::twopi * std::sin(r * gammalib::deg2rad) * dr *
  269 + gammalib::deg2rad;
  270 + }
  271 + test_value(sum, 1.0, 0.001, "PSF integration for "+eng.print());
  272 + }
  273 +
  274 + // Return
  275 + return;
  276 +}
  277 +
  278 +
  279 +/***********************************************************************//**
239 * @brief Test CTA npsf computation 280 * @brief Test CTA npsf computation
240 ***************************************************************************/ 281 ***************************************************************************/
241 void TestGCTAResponse::test_response_npsf(void) 282 void TestGCTAResponse::test_response_npsf(void)
@@ -427,6 +468,30 @@ void TestGCTAResponse::test_response_npred_diffuse(void) @@ -427,6 +468,30 @@ void TestGCTAResponse::test_response_npred_diffuse(void)
427 return; 468 return;
428 } 469 }
429 470
  471 +
  472 +/***********************************************************************//**
  473 + * @brief Test CTA response handling
  474 + ***************************************************************************/
  475 +void TestGCTAResponse::test_response(void)
  476 +{
  477 + // Test CTA response loading
  478 + test_try("Test CTA response loading");
  479 + try {
  480 + // Load response
  481 + GCTAResponse rsp;
  482 + rsp.caldb(cta_caldb);
  483 + rsp.load(cta_irf);
  484 + test_try_success();
  485 + }
  486 + catch (std::exception &e) {
  487 + test_try_failure(e);
  488 + }
  489 +
  490 + // Return
  491 + return;
  492 +}
  493 +
  494 +
430 /***********************************************************************//** 495 /***********************************************************************//**
431 * @brief Test CTA Model Background Npred computation 496 * @brief Test CTA Model Background Npred computation
432 * 497 *
@@ -511,29 +576,6 @@ void TestGCTAModelBackground::test_modelbg_npred(void) @@ -511,29 +576,6 @@ void TestGCTAModelBackground::test_modelbg_npred(void)
511 576
512 577
513 /***********************************************************************//** 578 /***********************************************************************//**
514 - * @brief Test CTA response handling  
515 - ***************************************************************************/  
516 -void TestGCTAResponse::test_response(void)  
517 -{  
518 - // Test CTA response loading  
519 - test_try("Test CTA response loading");  
520 - try {  
521 - // Load response  
522 - GCTAResponse rsp;  
523 - rsp.caldb(cta_caldb);  
524 - rsp.load(cta_irf);  
525 - test_try_success();  
526 - }  
527 - catch (std::exception &e) {  
528 - test_try_failure(e);  
529 - }  
530 -  
531 - // Return  
532 - return;  
533 -}  
534 -  
535 -  
536 -/***********************************************************************//**  
537 * @brief Test unbinned observation handling 579 * @brief Test unbinned observation handling
538 ***************************************************************************/ 580 ***************************************************************************/
539 void TestGCTAObservation::test_unbinned_obs(void) 581 void TestGCTAObservation::test_unbinned_obs(void)
inst/cta/test/test_CTA.hpp
@@ -49,6 +49,7 @@ public: @@ -49,6 +49,7 @@ public:
49 virtual TestGCTAResponse* clone(void) const; 49 virtual TestGCTAResponse* clone(void) const;
50 void test_response_aeff(void); 50 void test_response_aeff(void);
51 void test_response_psf(void); 51 void test_response_psf(void);
  52 + void test_response_psf_king(void);
52 void test_response_npsf(void); 53 void test_response_npsf(void);
53 void test_response_irf_diffuse(void); 54 void test_response_irf_diffuse(void);
54 void test_response_npred_diffuse(void); 55 void test_response_npred_diffuse(void);