Commit f3da83abb4d8a0f71f8d34925446084a3cfb1dc7

Authored by Jürgen Knödlseder
1 parent 285fb246

Enhance precision of GCTAEdisp2D::compute_ebounds_src

ChangeLog
1 -2017-03-24 1 +2017-03-27
2 2
3 * Version 1.3.0 released 3 * Version 1.3.0 released
4 ========================= 4 =========================
5 5
  6 + Enhance precision of GCTAEdisp2D::compute_ebounds_src
6 Add conversion methods between GEbounds and GEnergies (#1977) 7 Add conversion methods between GEbounds and GEnergies (#1977)
7 Add GModelSpectralSmoothBrokenPlaw class (#1948) 8 Add GModelSpectralSmoothBrokenPlaw class (#1948)
8 Implement lazy loading of energy dispersion in GCTAEdisp2D (#1940) 9 Implement lazy loading of energy dispersion in GCTAEdisp2D (#1940)
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 -24 March 2017 3 +27 March 2017
4 4
5 5
6 1. Introduction 6 1. Introduction
@@ -154,6 +154,12 @@ Lazy loading of the energy dispersion in GCTAEdisp2D has been implemented by @@ -154,6 +154,12 @@ Lazy loading of the energy dispersion in GCTAEdisp2D has been implemented by
154 adding a fetch() method to the class. Now the energy dispersion is only loaded 154 adding a fetch() method to the class. Now the energy dispersion is only loaded
155 when needed, for example for function evaluation or Monte Carlo sampling (#1940). 155 when needed, for example for function evaluation or Monte Carlo sampling (#1940).
156 156
  157 +The GCTAEdisp2D::compute_ebounds_src method now loops over the migration to
  158 +enhance the precision in the true energy boundaries that are returned by this
  159 +method. In addition, the validity range of the table is now stored in members
  160 +of the class, and a non-zero energy dispersion is only returned for valid
  161 +elements of the response table.
  162 +
157 163
158 21. COMPTEL interface 164 21. COMPTEL interface
159 --------------------- 165 ---------------------
inst/cta/include/GCTAEdisp2D.hpp
@@ -119,7 +119,10 @@ private: @@ -119,7 +119,10 @@ private:
119 const double& phi = 0.0, 119 const double& phi = 0.0,
120 const double& zenith = 0.0, 120 const double& zenith = 0.0,
121 const double& azimuth = 0.0) const; 121 const double& azimuth = 0.0) const;
  122 + void set_indices(void);
  123 + void set_boundaries(void);
122 void set_max_edisp(void) const; 124 void set_max_edisp(void) const;
  125 + void normalize_table(void);
123 126
124 // Protected classes 127 // Protected classes
125 class edisp_kern : public GFunction { 128 class edisp_kern : public GFunction {
@@ -138,13 +141,19 @@ private: @@ -138,13 +141,19 @@ private:
138 }; 141 };
139 142
140 // Members 143 // Members
141 - mutable GFilename m_filename; //!< Name of Edisp response file  
142 - GCTAResponseTable m_edisp; //!< Edisp response table  
143 - mutable bool m_fetched; //!< Signals that Edisp has been fetched  
144 - int m_inx_etrue; //!< True energy index  
145 - int m_inx_migra; //!< Migration index  
146 - int m_inx_theta; //!< Theta index  
147 - int m_inx_matrix; //!< Matrix 144 + mutable GFilename m_filename; //!< Name of Edisp response file
  145 + GCTAResponseTable m_edisp; //!< Edisp response table
  146 + mutable bool m_fetched; //!< Signals that Edisp has been fetched
  147 + int m_inx_etrue; //!< True energy index
  148 + int m_inx_migra; //!< Migration index
  149 + int m_inx_theta; //!< Theta index
  150 + int m_inx_matrix; //!< Matrix
  151 + double m_logEsrc_min; //!< Minimum logE (log10(E/TeV))
  152 + double m_logEsrc_max; //!< Maximum logE (log10(E/TeV))
  153 + double m_migra_min; //!< Minimum migration
  154 + double m_migra_max; //!< Maximum migration
  155 + double m_theta_min; //!< Minimum theta (radians)
  156 + double m_theta_max; //!< Maximum theta (radians)
148 157
149 // Computation cache 158 // Computation cache
150 mutable bool m_ebounds_obs_computed; 159 mutable bool m_ebounds_obs_computed;
@@ -198,17 +207,4 @@ const GCTAResponseTable&amp; GCTAEdisp2D::table(void) const @@ -198,17 +207,4 @@ const GCTAResponseTable&amp; GCTAEdisp2D::table(void) const
198 return (m_edisp); 207 return (m_edisp);
199 } 208 }
200 209
201 -  
202 -/***********************************************************************//**  
203 - * @brief Set response table  
204 - *  
205 - * @param[in] table Response table.  
206 - ***************************************************************************/  
207 -inline  
208 -void GCTAEdisp2D::table(const GCTAResponseTable& table)  
209 -{  
210 - m_edisp = table;  
211 - return;  
212 -}  
213 -  
214 #endif /* GCTAEDISP2D_HPP */ 210 #endif /* GCTAEDISP2D_HPP */
inst/cta/src/GCTAEdisp2D.cpp
@@ -198,22 +198,33 @@ double GCTAEdisp2D::operator()(const double&amp; logEobs, @@ -198,22 +198,33 @@ double GCTAEdisp2D::operator()(const double&amp; logEobs,
198 // Initalize edisp 198 // Initalize edisp
199 double edisp = 0.0; 199 double edisp = 0.0;
200 200
201 - // Compute Eobs/Esrc  
202 - double EobsOverEsrc = std::exp((logEobs-logEsrc) * gammalib::ln10); 201 + // Continue only if logEsrc and theta are in validity range
  202 + if ((logEsrc >= m_logEsrc_min) && (logEsrc <= m_logEsrc_max) &&
  203 + (theta >= m_theta_min) && (theta <= m_theta_max)) {
203 204
204 - // Setup argument vector  
205 - double arg[3];  
206 - arg[m_inx_etrue] = logEsrc;  
207 - arg[m_inx_migra] = EobsOverEsrc;  
208 - arg[m_inx_theta] = theta; 205 + // Compute Eobs/Esrc
  206 + double EobsOverEsrc = std::exp((logEobs-logEsrc) * gammalib::ln10);
209 207
210 - // Compute edisp  
211 - edisp = m_edisp(m_inx_matrix, arg[0], arg[1], arg[2]); 208 + // Continue only if migration is in validity range
  209 + if ((EobsOverEsrc >= m_migra_min) && (EobsOverEsrc <= m_migra_max)) {
212 210
213 - // Make sure that energy dispersion is non-negative  
214 - if (edisp < 0.0) {  
215 - edisp = 0.0;  
216 - } 211 + // Setup argument vector
  212 + double arg[3];
  213 + arg[m_inx_etrue] = logEsrc;
  214 + arg[m_inx_migra] = EobsOverEsrc;
  215 + arg[m_inx_theta] = theta;
  216 +
  217 + // Compute edisp
  218 + edisp = m_edisp(m_inx_matrix, arg[0], arg[1], arg[2]);
  219 +
  220 + // Make sure that energy dispersion is non-negative
  221 + if (edisp < 0.0) {
  222 + edisp = 0.0;
  223 + }
  224 +
  225 + } // endif: migration was in valid range
  226 +
  227 + } // endif: true energy and offset angle was in valid range
217 228
218 // Return 229 // Return
219 return edisp; 230 return edisp;
@@ -260,6 +271,41 @@ GCTAEdisp2D* GCTAEdisp2D::clone(void) const @@ -260,6 +271,41 @@ GCTAEdisp2D* GCTAEdisp2D::clone(void) const
260 271
261 272
262 /***********************************************************************//** 273 /***********************************************************************//**
  274 + * @brief Assign response table
  275 + *
  276 + * @param[in] table Response table.
  277 + *
  278 + * Assigns the response table for an energy dispersion.
  279 + ***************************************************************************/
  280 +void GCTAEdisp2D::table(const GCTAResponseTable& table)
  281 +{
  282 + // Assign response table
  283 + m_edisp = table;
  284 +
  285 + // Set indices
  286 + set_indices();
  287 +
  288 + // Set true energy axis to logarithmic scale
  289 + m_edisp.axis_log10(m_inx_etrue);
  290 +
  291 + // Set offset angle axis to radians
  292 + m_edisp.axis_radians(m_inx_theta);
  293 +
  294 + // Normalize energy dispersion table
  295 + normalize_table();
  296 +
  297 + // Set maximum energy dispersion value
  298 + set_max_edisp();
  299 +
  300 + // Set table boundaries
  301 + set_boundaries();
  302 +
  303 + // Return
  304 + return;
  305 +}
  306 +
  307 +
  308 +/***********************************************************************//**
263 * @brief Read energy dispersion from FITS table 309 * @brief Read energy dispersion from FITS table
264 * 310 *
265 * @param[in] table FITS table. 311 * @param[in] table FITS table.
@@ -291,12 +337,6 @@ void GCTAEdisp2D::read(const GFitsTable&amp; table) @@ -291,12 +337,6 @@ void GCTAEdisp2D::read(const GFitsTable&amp; table)
291 // Read energy dispersion table 337 // Read energy dispersion table
292 m_edisp.read(table); 338 m_edisp.read(table);
293 339
294 - // Get mandatory indices (throw exception if not found)  
295 - m_inx_etrue = m_edisp.axis("ETRUE");  
296 - m_inx_migra = m_edisp.axis("MIGRA");  
297 - m_inx_theta = m_edisp.axis("THETA");  
298 - m_inx_matrix = m_edisp.table("MATRIX");  
299 -  
300 // Throw an exception if the table is not two-dimensional 340 // Throw an exception if the table is not two-dimensional
301 if (m_edisp.axes() != 3) { 341 if (m_edisp.axes() != 3) {
302 std::string msg = "Expected three-dimensional energy dispersion " 342 std::string msg = "Expected three-dimensional energy dispersion "
@@ -307,94 +347,24 @@ void GCTAEdisp2D::read(const GFitsTable&amp; table) @@ -307,94 +347,24 @@ void GCTAEdisp2D::read(const GFitsTable&amp; table)
307 throw GException::invalid_value(G_READ, msg); 347 throw GException::invalid_value(G_READ, msg);
308 } 348 }
309 349
  350 + // Set table indices
  351 + set_indices();
  352 +
310 // Set true energy axis to logarithmic scale 353 // Set true energy axis to logarithmic scale
311 m_edisp.axis_log10(m_inx_etrue); 354 m_edisp.axis_log10(m_inx_etrue);
312 355
313 // Set offset angle axis to radians 356 // Set offset angle axis to radians
314 m_edisp.axis_radians(m_inx_theta); 357 m_edisp.axis_radians(m_inx_theta);
315 358
316 - // Get axes dimensions  
317 - int etrue_size = m_edisp.axis_bins(m_inx_etrue);  
318 - int migra_size = m_edisp.axis_bins(m_inx_migra);  
319 - int theta_size = m_edisp.axis_bins(m_inx_theta);  
320 -  
321 - // Now normalize the migration matrix vectors. We do this by integrating  
322 - // for each true energy and offset angle the measured energy. We perform  
323 - // here two passes as the ebounds_obs() method depends on the content of  
324 - // the matrix, and by having two passes we are sure that we get a  
325 - // consistent energy interval.  
326 - for (int pass = 0; pass < 2; ++pass) {  
327 -  
328 - // Initialise sums  
329 - std::vector<double> sums;  
330 - sums.reserve(theta_size*etrue_size);  
331 -  
332 - // Loop over offset angle.  
333 - for (int i_theta = 0; i_theta < theta_size; ++i_theta) {  
334 -  
335 - // Get offset angle (in radians)  
336 - double theta = 0.5 * (m_edisp.axis_lo(m_inx_theta,i_theta) +  
337 - m_edisp.axis_hi(m_inx_theta,i_theta)) *  
338 - gammalib::deg2rad;  
339 -  
340 - // Loop over true photon energy  
341 - for (int i_etrue = 0; i_etrue < etrue_size; ++i_etrue) {  
342 -  
343 - // Get energy  
344 - double emin = std::log10(m_edisp.axis_lo(m_inx_etrue,i_etrue));  
345 - double emax = std::log10(m_edisp.axis_hi(m_inx_etrue,i_etrue));  
346 - double logEsrc = 0.5*(emin+emax);  
347 -  
348 - // Initialise integration  
349 - double sum = 0.0;  
350 -  
351 - // Get integration boundaries  
352 - GEbounds ebounds = ebounds_obs(logEsrc, theta);  
353 -  
354 - // Loop over all energy intervals  
355 - for (int i = 0; i < ebounds.size(); ++i) {  
356 -  
357 - // Get energy boundaries  
358 - emin = ebounds.emin(i).log10TeV();  
359 - emax = ebounds.emax(i).log10TeV();  
360 -  
361 - // Setup integration function  
362 - edisp_kern integrand(this, logEsrc, theta);  
363 - GIntegral integral(&integrand);  
364 -  
365 - // Set integration precision  
366 - integral.eps(1.0e-6);  
367 -  
368 - // Do Romberg integration  
369 - sum += integral.romberg(emin, emax);  
370 -  
371 - } // endfor: looped over energy intervals  
372 -  
373 - // Store sum  
374 - sums.push_back(sum);  
375 -  
376 - } // endfor: looped over Etrue  
377 -  
378 - } // endfor: looped over theta  
379 -  
380 - // Normalize vectors of migration matrix for all etrue and theta.  
381 - for (int i_theta = 0, inx = 0; i_theta < theta_size; ++i_theta) {  
382 - for (int i_etrue = 0; i_etrue < etrue_size; ++i_etrue, ++inx) {  
383 - double sum = sums[inx];  
384 - if (sum > 0.0) {  
385 - int offset = i_etrue + (i_theta*migra_size) * etrue_size;  
386 - for (int k = 0, i = offset; k < migra_size; ++k, i += etrue_size) {  
387 - m_edisp(m_inx_matrix,i) /= sum;  
388 - }  
389 - }  
390 - }  
391 - }  
392 -  
393 - } // endfor: made 2 passes 359 + // Normalize energy dispersion table
  360 + normalize_table();
394 361
395 // Set maximum energy dispersion value 362 // Set maximum energy dispersion value
396 set_max_edisp(); 363 set_max_edisp();
397 364
  365 + // Set table boundaries
  366 + set_boundaries();
  367 +
398 // Return 368 // Return
399 return; 369 return;
400 } 370 }
@@ -857,11 +827,17 @@ void GCTAEdisp2D::init_members(void) @@ -857,11 +827,17 @@ void GCTAEdisp2D::init_members(void)
857 // Initialise members 827 // Initialise members
858 m_filename.clear(); 828 m_filename.clear();
859 m_edisp.clear(); 829 m_edisp.clear();
860 - m_fetched = false;  
861 - m_inx_etrue = 0;  
862 - m_inx_migra = 1;  
863 - m_inx_theta = 2;  
864 - m_inx_matrix = 0; 830 + m_fetched = false;
  831 + m_inx_etrue = 0;
  832 + m_inx_migra = 1;
  833 + m_inx_theta = 2;
  834 + m_inx_matrix = 0;
  835 + m_logEsrc_min = 0.0;
  836 + m_logEsrc_max = 0.0;
  837 + m_migra_min = 0.0;
  838 + m_migra_max = 0.0;
  839 + m_theta_min = 0.0;
  840 + m_theta_max = 0.0;
865 841
866 // Initialise computation cache 842 // Initialise computation cache
867 m_ebounds_obs_computed = false; 843 m_ebounds_obs_computed = false;
@@ -889,13 +865,19 @@ void GCTAEdisp2D::init_members(void) @@ -889,13 +865,19 @@ void GCTAEdisp2D::init_members(void)
889 void GCTAEdisp2D::copy_members(const GCTAEdisp2D& edisp) 865 void GCTAEdisp2D::copy_members(const GCTAEdisp2D& edisp)
890 { 866 {
891 // Copy members 867 // Copy members
892 - m_filename = edisp.m_filename;  
893 - m_edisp = edisp.m_edisp;  
894 - m_fetched = edisp.m_fetched;  
895 - m_inx_etrue = edisp.m_inx_etrue;  
896 - m_inx_migra = edisp.m_inx_migra;  
897 - m_inx_theta = edisp.m_inx_theta;  
898 - m_inx_matrix = edisp.m_inx_matrix; 868 + m_filename = edisp.m_filename;
  869 + m_edisp = edisp.m_edisp;
  870 + m_fetched = edisp.m_fetched;
  871 + m_inx_etrue = edisp.m_inx_etrue;
  872 + m_inx_migra = edisp.m_inx_migra;
  873 + m_inx_theta = edisp.m_inx_theta;
  874 + m_inx_matrix = edisp.m_inx_matrix;
  875 + m_logEsrc_min = edisp.m_logEsrc_min;
  876 + m_logEsrc_max = edisp.m_logEsrc_max;
  877 + m_migra_min = edisp.m_migra_min;
  878 + m_migra_max = edisp.m_migra_max;
  879 + m_theta_min = edisp.m_theta_min;
  880 + m_theta_max = edisp.m_theta_max;
899 881
900 // Copy computation cache 882 // Copy computation cache
901 m_ebounds_obs_computed = edisp.m_ebounds_obs_computed; 883 m_ebounds_obs_computed = edisp.m_ebounds_obs_computed;
@@ -1063,7 +1045,8 @@ void GCTAEdisp2D::compute_ebounds_src(const double&amp; theta, @@ -1063,7 +1045,8 @@ void GCTAEdisp2D::compute_ebounds_src(const double&amp; theta,
1063 const double eps = 1.0e-12; 1045 const double eps = 1.0e-12;
1064 1046
1065 // Determine number of true energy bins 1047 // Determine number of true energy bins
1066 - int n_bins = m_edisp.axis_bins(m_inx_etrue); 1048 + int n_bins = m_edisp.axis_bins(m_inx_etrue);
  1049 + int n_migras = m_edisp.axis_bins(m_inx_migra);
1067 1050
1068 // Loop over Eobs 1051 // Loop over Eobs
1069 for (int iobs = 0; iobs < n_bins; ++iobs) { 1052 for (int iobs = 0; iobs < n_bins; ++iobs) {
@@ -1080,40 +1063,54 @@ void GCTAEdisp2D::compute_ebounds_src(const double&amp; theta, @@ -1080,40 +1063,54 @@ void GCTAEdisp2D::compute_ebounds_src(const double&amp; theta,
1080 bool maxFound = false; 1063 bool maxFound = false;
1081 1064
1082 // Find minimum boundary 1065 // Find minimum boundary
1083 - for (int isrc = 0; isrc < n_bins; ++isrc) { 1066 + for (int imigra = 0; imigra < n_migras; ++imigra) {
1084 1067
1085 - // Set Esrc  
1086 - double Esrc = std::sqrt(m_edisp.axis_hi(m_inx_etrue,isrc) *  
1087 - m_edisp.axis_lo(m_inx_etrue,isrc));  
1088 - double logEsrc = std::log10(Esrc); 1068 + // Get mean migra
  1069 + double migra = 0.5 * (m_edisp.axis_lo(m_inx_migra,imigra) +
  1070 + m_edisp.axis_lo(m_inx_migra,imigra));
  1071 +
  1072 + // Fall through if migration is zero
  1073 + if (migra == 0.0) {
  1074 + continue;
  1075 + }
  1076 +
  1077 + // Compute log10 of true energy
  1078 + double logEsrc = logEobs - std::log10(migra);
1089 1079
1090 // Get matrix term 1080 // Get matrix term
1091 double edisp = operator()(logEobs, logEsrc, theta); 1081 double edisp = operator()(logEobs, logEsrc, theta);
1092 1082
1093 // Find first non-negligible matrix term 1083 // Find first non-negligible matrix term
1094 if (edisp >= eps) { 1084 if (edisp >= eps) {
1095 - minFound = true;  
1096 - logEsrcMin = logEsrc; 1085 + maxFound = true;
  1086 + logEsrcMax = logEsrc;
1097 break; 1087 break;
1098 } 1088 }
1099 1089
1100 } // endfor: find minimum boundary 1090 } // endfor: find minimum boundary
1101 1091
1102 // Find maximum boundary 1092 // Find maximum boundary
1103 - for (int isrc = n_bins-1; isrc >= 0; isrc--) { 1093 + for (int imigra = n_migras-1; imigra >= 0; imigra--) {
  1094 +
  1095 + // Get mean migra
  1096 + double migra = 0.5 * (m_edisp.axis_lo(m_inx_migra,imigra) +
  1097 + m_edisp.axis_lo(m_inx_migra,imigra));
  1098 +
  1099 + // Fall through if migration is zero
  1100 + if (migra == 0.0) {
  1101 + continue;
  1102 + }
1104 1103
1105 - // Set Esrc  
1106 - double Esrc = std::sqrt(m_edisp.axis_hi(m_inx_etrue,isrc) *  
1107 - m_edisp.axis_lo(m_inx_etrue,isrc));  
1108 - double logEsrc = std::log10(Esrc); 1104 + // Compute log10 of true energy
  1105 + double logEsrc = logEobs - std::log10(migra);
1109 1106
1110 // Get matrix term 1107 // Get matrix term
1111 double edisp = operator()(logEobs, logEsrc, theta); 1108 double edisp = operator()(logEobs, logEsrc, theta);
1112 1109
1113 // Find first non-negligible matrix term 1110 // Find first non-negligible matrix term
1114 if (edisp >= eps) { 1111 if (edisp >= eps) {
1115 - maxFound = true;  
1116 - logEsrcMax = logEsrc; 1112 + minFound = true;
  1113 + logEsrcMin = logEsrc;
1117 break; 1114 break;
1118 } 1115 }
1119 1116
@@ -1143,6 +1140,56 @@ void GCTAEdisp2D::compute_ebounds_src(const double&amp; theta, @@ -1143,6 +1140,56 @@ void GCTAEdisp2D::compute_ebounds_src(const double&amp; theta,
1143 1140
1144 1141
1145 /***********************************************************************//** 1142 /***********************************************************************//**
  1143 + * @brief Set table indices
  1144 + *
  1145 + * Sets the data members m_inx_etrue, m_inx_migra, m_inx_theta, and
  1146 + * m_inx_matrix.
  1147 + ***************************************************************************/
  1148 +void GCTAEdisp2D::set_indices(void)
  1149 +{
  1150 + // Get mandatory indices (throw exception if not found)
  1151 + m_inx_etrue = m_edisp.axis("ETRUE");
  1152 + m_inx_migra = m_edisp.axis("MIGRA");
  1153 + m_inx_theta = m_edisp.axis("THETA");
  1154 + m_inx_matrix = m_edisp.table("MATRIX");
  1155 +
  1156 + // Return
  1157 + return;
  1158 +}
  1159 +
  1160 +
  1161 +/***********************************************************************//**
  1162 + * @brief Set energy dispersion boundaries
  1163 + *
  1164 + * Sets the data members m_logEsrc_min, m_logEsrc_max, m_migra_min,
  1165 + * m_migra_max, m_theta_min and m_theta_max that define the validity range
  1166 + * of the energy dispersion.
  1167 + ***************************************************************************/
  1168 +void GCTAEdisp2D::set_boundaries(void)
  1169 +{
  1170 + // Get number of bins
  1171 + int netrue = m_edisp.axis_bins(m_inx_etrue);
  1172 + int nmigra = m_edisp.axis_bins(m_inx_migra);
  1173 + int ntheta = m_edisp.axis_bins(m_inx_theta);
  1174 +
  1175 + // Compute minimum and maximum logEsrc boundaries
  1176 + m_logEsrc_min = std::log10(m_edisp.axis_lo(m_inx_etrue, 0));
  1177 + m_logEsrc_max = std::log10(m_edisp.axis_hi(m_inx_etrue, netrue-1));
  1178 +
  1179 + // Compute minimum and maximum migration boundaries
  1180 + m_migra_min = m_edisp.axis_lo(m_inx_migra, 0);
  1181 + m_migra_max = m_edisp.axis_hi(m_inx_migra, nmigra-1);
  1182 +
  1183 + // Compute minimum and maximum theta boundaries
  1184 + m_theta_min = m_edisp.axis_lo(m_inx_theta, 0) * gammalib::deg2rad;
  1185 + m_theta_max = m_edisp.axis_hi(m_inx_theta, ntheta-1) * gammalib::deg2rad;
  1186 +
  1187 + // Return
  1188 + return;
  1189 +}
  1190 +
  1191 +
  1192 +/***********************************************************************//**
1146 * @brief Set maximum energy dispersion value 1193 * @brief Set maximum energy dispersion value
1147 ***************************************************************************/ 1194 ***************************************************************************/
1148 void GCTAEdisp2D::set_max_edisp(void) const 1195 void GCTAEdisp2D::set_max_edisp(void) const
@@ -1164,6 +1211,95 @@ void GCTAEdisp2D::set_max_edisp(void) const @@ -1164,6 +1211,95 @@ void GCTAEdisp2D::set_max_edisp(void) const
1164 1211
1165 1212
1166 /***********************************************************************//** 1213 /***********************************************************************//**
  1214 + * @brief Normalize energy dispersion table
  1215 + ***************************************************************************/
  1216 +void GCTAEdisp2D::normalize_table(void)
  1217 +{
  1218 + // Get axes dimensions
  1219 + int etrue_size = m_edisp.axis_bins(m_inx_etrue);
  1220 + int migra_size = m_edisp.axis_bins(m_inx_migra);
  1221 + int theta_size = m_edisp.axis_bins(m_inx_theta);
  1222 +
  1223 + // Now normalize the migration matrix vectors. We do this by integrating
  1224 + // for each true energy and offset angle the measured energy. We perform
  1225 + // here two passes as the ebounds_obs() method depends on the content of
  1226 + // the matrix, and by having two passes we are sure that we get a
  1227 + // consistent energy interval.
  1228 + for (int pass = 0; pass < 2; ++pass) {
  1229 +
  1230 + // Initialise sums
  1231 + std::vector<double> sums;
  1232 + sums.reserve(theta_size*etrue_size);
  1233 +
  1234 + // Loop over offset angle.
  1235 + for (int i_theta = 0; i_theta < theta_size; ++i_theta) {
  1236 +
  1237 + // Get offset angle (in radians)
  1238 + double theta = 0.5 * (m_edisp.axis_lo(m_inx_theta,i_theta) +
  1239 + m_edisp.axis_hi(m_inx_theta,i_theta)) *
  1240 + gammalib::deg2rad;
  1241 +
  1242 + // Loop over true photon energy
  1243 + for (int i_etrue = 0; i_etrue < etrue_size; ++i_etrue) {
  1244 +
  1245 + // Get energy
  1246 + double emin = std::log10(m_edisp.axis_lo(m_inx_etrue,i_etrue));
  1247 + double emax = std::log10(m_edisp.axis_hi(m_inx_etrue,i_etrue));
  1248 + double logEsrc = 0.5*(emin+emax);
  1249 +
  1250 + // Initialise integration
  1251 + double sum = 0.0;
  1252 +
  1253 + // Get integration boundaries
  1254 + GEbounds ebounds = ebounds_obs(logEsrc, theta);
  1255 +
  1256 + // Loop over all energy intervals
  1257 + for (int i = 0; i < ebounds.size(); ++i) {
  1258 +
  1259 + // Get energy boundaries
  1260 + emin = ebounds.emin(i).log10TeV();
  1261 + emax = ebounds.emax(i).log10TeV();
  1262 +
  1263 + // Setup integration function
  1264 + edisp_kern integrand(this, logEsrc, theta);
  1265 + GIntegral integral(&integrand);
  1266 +
  1267 + // Set integration precision
  1268 + integral.eps(1.0e-6);
  1269 +
  1270 + // Do Romberg integration
  1271 + sum += integral.romberg(emin, emax);
  1272 +
  1273 + } // endfor: looped over energy intervals
  1274 +
  1275 + // Store sum
  1276 + sums.push_back(sum);
  1277 +
  1278 + } // endfor: looped over Etrue
  1279 +
  1280 + } // endfor: looped over theta
  1281 +
  1282 + // Normalize vectors of migration matrix for all etrue and theta.
  1283 + for (int i_theta = 0, inx = 0; i_theta < theta_size; ++i_theta) {
  1284 + for (int i_etrue = 0; i_etrue < etrue_size; ++i_etrue, ++inx) {
  1285 + double sum = sums[inx];
  1286 + if (sum > 0.0) {
  1287 + int offset = i_etrue + (i_theta*migra_size) * etrue_size;
  1288 + for (int k = 0, i = offset; k < migra_size; ++k, i += etrue_size) {
  1289 + m_edisp(m_inx_matrix,i) /= sum;
  1290 + }
  1291 + }
  1292 + }
  1293 + }
  1294 +
  1295 + } // endfor: made 2 passes
  1296 +
  1297 + // Return
  1298 + return;
  1299 +}
  1300 +
  1301 +
  1302 +/***********************************************************************//**
1167 * @brief Integration kernel for edisp_kern() class 1303 * @brief Integration kernel for edisp_kern() class
1168 * 1304 *
1169 * @param[in] x Function value. 1305 * @param[in] x Function value.