Commit b5c6b9a469fa3cb19a42605dbdfe72516c7b100f

Authored by Jürgen Knödlseder
1 parent f18179e5

Normalize migration matrix upon loading by performing a numerical integration of…

… the energy dispersion access operator().

By this method we assure that the matrix is properly normalized, including the effects of numerical interpolation.
inst/cta/include/GCTAEdisp2D.hpp
... ... @@ -31,6 +31,7 @@
31 31 #include <string>
32 32 #include "GCTAEdisp.hpp"
33 33 #include "GCTAResponseTable.hpp"
  34 +#include "GFunction.hpp"
34 35  
35 36 /* __ Forward declarations _______________________________________________ */
36 37 class GRan;
... ... @@ -112,6 +113,22 @@ private:
112 113 const double& azimuth = 0.0) const;
113 114 void set_max_edisp(void) const;
114 115  
  116 + // Protected classes
  117 + class edisp_kern : public GFunction {
  118 + public:
  119 + edisp_kern(const GCTAEdisp2D* parent,
  120 + const double& logEsrc,
  121 + const double& theta) :
  122 + m_parent(parent),
  123 + m_logEsrc(logEsrc),
  124 + m_theta(theta) { }
  125 + double eval(const double& x);
  126 + protected:
  127 + const GCTAEdisp2D* m_parent; //!< Pointer to parent class
  128 + const double& m_logEsrc; //!< True photon energy
  129 + const double& m_theta; //!< Offset angle
  130 + };
  131 +
115 132 // Members
116 133 std::string m_filename; //!< Name of Edisp response file
117 134 GCTAResponseTable m_edisp; //!< Edisp response table
... ...
inst/cta/src/GCTAEdisp2D.cpp
... ... @@ -32,6 +32,7 @@
32 32 #include <vector>
33 33 #include "GTools.hpp"
34 34 #include "GMath.hpp"
  35 +#include "GIntegral.hpp"
35 36 #include "GFits.hpp"
36 37 #include "GFitsTable.hpp"
37 38 #include "GFitsBinTable.hpp"
... ... @@ -278,50 +279,78 @@ void GCTAEdisp2D::read(const GFits&amp; fits)
278 279 // Read energy dispersion table
279 280 m_edisp.read(table);
280 281  
  282 + // Set true energy axis to logarithmic scale
  283 + m_edisp.axis_log10(0);
  284 +
  285 + // Set offset angle axis to radians
  286 + m_edisp.axis_radians(2);
  287 +
281 288 // Get axes dimensions
282 289 int etrue_size = m_edisp.axis(0);
283 290 int migra_size = m_edisp.axis(1);
284 291 int theta_size = m_edisp.axis(2);
285 292  
286   - // Normalize vectors of migration matrix for all etrue and theta
287   - for (int i_etrue = 0; i_etrue < etrue_size; ++i_etrue) {
288   - for (int i_theta = 0; i_theta < theta_size; ++i_theta) {
  293 + // Normalize vectors of migration matrix for all etrue and theta.
  294 + // Loop over offset angle.
  295 + for (int i_theta = 0; i_theta < theta_size; ++i_theta) {
289 296  
290   - // Initialise sum and offset
291   - double sum = 0.0;
292   - int offset = i_etrue + (i_theta*migra_size) * etrue_size;
  297 + // Get offset angle (in radians)
  298 + double theta = 0.5 * (m_edisp.axis_lo(2,i_theta) +
  299 + m_edisp.axis_hi(2,i_theta)) * gammalib::deg2rad;
293 300  
294   - // Compute sum
295   - for (int i_migra = 0, i = offset; i_migra < migra_size;
296   - ++i_migra, i += etrue_size) {
  301 + // Loop over true photon energy
  302 + for (int i_etrue = 0; i_etrue < etrue_size; ++i_etrue) {
297 303  
298   - // Compute delta(Eobs/Etrue) and Eobs/Etrue
299   - double delta = m_edisp.axis_hi(1, i_migra) -
300   - m_edisp.axis_lo(1, i_migra);
301   - double EobsOverEsrc = 0.5 * (m_edisp.axis_hi(1, i_migra) +
302   - m_edisp.axis_lo(1, i_migra));
  304 + // Get energy
  305 + double emin = std::log10(m_edisp.axis_lo(0,i_etrue));
  306 + double emax = std::log10(m_edisp.axis_hi(0,i_etrue));
  307 + double logEsrc = 0.5*(emin+emax);
303 308  
304   - // Add dispersion matrix element to sum
305   - sum += m_edisp(0, i) * delta / EobsOverEsrc;
  309 + // Now loop twice as ebounds_obs() actually depends on the
  310 + // value of the edisp matrix, hence we want to be sure that
  311 + // we integrate over the same range that is later used for
  312 + // the energy dispersion handling.
  313 + for (int iter = 0; iter < 2; ++iter) {
306 314  
307   - }
  315 + // Get integration boundaries
  316 + GEbounds ebounds = ebounds_obs(logEsrc, theta);
308 317  
309   - // Normalise by sum/log(10.0)
310   - if (sum != 0.0) {
311   - for (int i_migra = 0, i = offset; i_migra < migra_size;
312   - ++i_migra, i += etrue_size) {
313   - m_edisp(0, i) *= (gammalib::ln10 / sum);
314   - }
315   - }
  318 + // Initialise integration
  319 + double sum = 0.0;
316 320  
317   - } // endfor: looped over theta
318   - } // endfor: looped over Etrue
  321 + // Loop over all energy intervals
  322 + for (int i = 0; i < ebounds.size(); ++i) {
319 323  
320   - // Set true energy axis to logarithmic scale
321   - m_edisp.axis_log10(0);
  324 + // Get energy boundaries
  325 + emin = ebounds.emin(i).log10TeV();
  326 + emax = ebounds.emax(i).log10TeV();
322 327  
323   - // Set offset angle axis to radians
324   - m_edisp.axis_radians(2);
  328 + // Setup integration function
  329 + edisp_kern integrand(this, logEsrc, theta);
  330 + GIntegral integral(&integrand);
  331 +
  332 + // Set integration precision
  333 + integral.eps(1.0e-6);
  334 +
  335 + // Do Romberg integration
  336 + sum += integral.romberg(emin, emax);
  337 +//std::cout << theta << " " << logEsrc << " " << sum;
  338 +//std::cout << " e=[" << emin << "," << emax << "]" << std::endl;
  339 +
  340 + } // endfor: looped over all energy intervals
  341 +
  342 + // If sum is positive then renormalize vector
  343 + if (sum > 0.0) {
  344 + int offset = i_etrue + (i_theta*migra_size) * etrue_size;
  345 + for (int k = 0, i = offset; k < migra_size; ++k, i += etrue_size) {
  346 + m_edisp(0,i) /= sum;
  347 + }
  348 + }
  349 +
  350 + } // endfor: did two iterations
  351 +
  352 + } // endfor: looped over Etrue
  353 + } // endfor: looped over theta
325 354  
326 355 // Set maximum energy dispersion value
327 356 set_max_edisp();
... ... @@ -678,14 +707,14 @@ void GCTAEdisp2D::compute_ebounds_obs(const double&amp; theta,
678 707 m_ebounds_obs.clear();
679 708  
680 709 // Set epsilon
681   - const double eps = 1.0e-06;
  710 + const double eps = 1.0e-12;
682 711  
683 712 // Loop over Esrc
684 713 for (int isrc = 0; isrc < m_edisp.axis(0); ++isrc) {
685 714  
686 715 // Set Esrc
687   - double Esrc = 0.5 * (m_edisp.axis_hi(0, isrc) +
688   - m_edisp.axis_lo(0, isrc));
  716 + double Esrc = std::sqrt(m_edisp.axis_hi(0, isrc) *
  717 + m_edisp.axis_lo(0, isrc));
689 718 double logEsrc = std::log10(Esrc);
690 719  
691 720 // Initialise results
... ... @@ -762,13 +791,14 @@ void GCTAEdisp2D::compute_ebounds_src(const double&amp; theta,
762 791 m_ebounds_src.clear();
763 792  
764 793 // Set epsilon
765   - const double eps = 1.0e-06;
  794 + const double eps = 1.0e-12;
766 795  
767 796 // Loop over Eobs
768 797 for (int iobs = 0; iobs < m_edisp.axis(0); ++iobs) {
769 798  
770   - double Eobs = 0.5 * (m_edisp.axis_hi(0, iobs) +
771   - m_edisp.axis_lo(0, iobs));
  799 + // Set Eobs
  800 + double Eobs = std::sqrt(m_edisp.axis_hi(0, iobs) *
  801 + m_edisp.axis_lo(0, iobs));
772 802 double logEobs = std::log10(Eobs);
773 803  
774 804 // Initialise results
... ... @@ -780,8 +810,9 @@ void GCTAEdisp2D::compute_ebounds_src(const double&amp; theta,
780 810 // Find boundaries, loop over Esrc
781 811 for (int isrc = 0; isrc < m_edisp.axis(0); ++isrc) {
782 812  
783   - double Esrc = 0.5 * (m_edisp.axis_hi(0, isrc) +
784   - m_edisp.axis_lo(0, isrc));
  813 + // Set Esrc
  814 + double Esrc = std::sqrt(m_edisp.axis_hi(0, isrc) *
  815 + m_edisp.axis_lo(0, isrc));
785 816 double logEsrc = std::log10(Esrc);
786 817  
787 818 // Find first non-negligible matrix term
... ... @@ -846,3 +877,32 @@ void GCTAEdisp2D::set_max_edisp(void) const
846 877 // Return
847 878 return;
848 879 }
  880 +
  881 +
  882 +/***********************************************************************//**
  883 + * @brief Integration kernel for edisp_kern() class
  884 + *
  885 + * @param[in] x Function value.
  886 + *
  887 + * This method implements the integration kernel needed for the edisp_kern()
  888 + * class.
  889 + ***************************************************************************/
  890 +double GCTAEdisp2D::edisp_kern::eval(const double& x)
  891 +{
  892 + // Get function value
  893 + double value = m_parent->operator()(x, m_logEsrc, m_theta);
  894 +
  895 + // Compile option: Check for NaN
  896 + #if defined(G_NAN_CHECK)
  897 + if (gammalib::is_notanumber(value) || gammalib::is_infinite(value)) {
  898 + std::cout << "*** ERROR: GCTAEdisp2D::edisp_kern::eval";
  899 + std::cout << "(x=" << x << "): ";
  900 + std::cout << " NaN/Inf encountered";
  901 + std::cout << " (value=" << value;
  902 + std::cout << ")" << std::endl;
  903 + }
  904 + #endif
  905 +
  906 + // Return value
  907 + return value;
  908 +}
... ...