Commit f2695a7538b9d5983cc293f9c4bef44cd38d1167
1 parent
2de42d5d
GCTAEdisp2D::operator() now returns energy dispersion per MeV (#2704)
The previous code returned the energy dispersion as function of the base 10 logarithm of the energy. It is not really clear why this was the case, as the GCTAResponseIrf::edisp() class returns energy dispersion per MeV, and all high-level code assumes that energy dispersion is per MeV. Therefore, to avoid confusion and renormalization of energy dispersion information, also the GCTAEdisp2D::operator() now also returns the information per MeV. The data is stored per migration bin, and hence a simple integration over migration should be unity for a give for a given offset angle and energy. The class was adapted so that this is now the case.
Showing
2 changed files
with
123 additions
and
147 deletions
inst/cta/include/GCTAEdisp2D.hpp
| @@ -52,14 +52,47 @@ namespace gammalib { | @@ -52,14 +52,47 @@ namespace gammalib { | ||
| 52 | * @brief CTA 2D energy dispersion class | 52 | * @brief CTA 2D energy dispersion class |
| 53 | * | 53 | * |
| 54 | * This class implements the energy dispersion for the CTA 2D response. The | 54 | * This class implements the energy dispersion for the CTA 2D response. The |
| 55 | - * energy dispersion is defined as | 55 | + * CTA 2D energy dispersion is in fact a 3-dimensional function, where the |
| 56 | + * energy dispersion is given as function of true energy \f$E_{\rm true}\f$, | ||
| 57 | + * migration value \f$m = E_{\rm reco}/E_{\rm true}\f$ and offset angle | ||
| 58 | + * \f$\theta\f$ | ||
| 56 | * | 59 | * |
| 57 | * \f[ | 60 | * \f[ |
| 58 | - * E_{\rm disp}(\log_{10} E_{\rm reco} | \log_{10} E_{\rm true}, \theta) = | ||
| 59 | - * \frac{dP}{d\log_{10} E_{\rm reco}} | 61 | + * E_{\rm disp}(E_{\rm true}, m, \theta) |
| 60 | * \f] | 62 | * \f] |
| 61 | * | 63 | * |
| 62 | - * and given in units of \f$(\log_{10} MeV)^{-1}\f$. | 64 | + * with |
| 65 | + * | ||
| 66 | + * \f[ | ||
| 67 | + * \int_{m_{\rm min}}^{m_{\rm max}} | ||
| 68 | + * E_{\rm disp}(E_{\rm true}, m, \theta) \, dm = 1 | ||
| 69 | + * \f] | ||
| 70 | + * | ||
| 71 | + * The energy dispersion as function of reconstructed energy | ||
| 72 | + * \f$E_{\rm reco}\f$ is given by | ||
| 73 | + * | ||
| 74 | + * \f[ | ||
| 75 | + * E_{\rm disp}(E_{\rm true}, E_{\rm reco}, \theta) = | ||
| 76 | + * \frac{E_{\rm disp}(E_{\rm true}, m, \theta)}{E_{\rm true}} | ||
| 77 | + * \f] | ||
| 78 | + * | ||
| 79 | + * the energy dispersion as function of the natural logarithm of | ||
| 80 | + * reconstructed energy \f$\log E_{\rm reco}\f$ is given by | ||
| 81 | + * | ||
| 82 | + * \f[ | ||
| 83 | + * E_{\rm disp}(E_{\rm true}, \log E_{\rm reco}, \theta) = | ||
| 84 | + * E_{\rm disp}(E_{\rm true}, m, \theta) \times | ||
| 85 | + * \frac{E_{\rm reco}}{E_{\rm true}} | ||
| 86 | + * \f] | ||
| 87 | + * | ||
| 88 | + * and the energy dispersion as function of the base 10 logarithm of | ||
| 89 | + * reconstructed energy \f$\log_{10} E_{\rm reco}\f$ is given by | ||
| 90 | + * | ||
| 91 | + * \f[ | ||
| 92 | + * E_{\rm disp}(E_{\rm true}, \log_{10} E_{\rm reco}, \theta) = | ||
| 93 | + * E_{\rm disp}(E_{\rm true}, m, \theta) \times | ||
| 94 | + * \frac{\log 10 \times E_{\rm reco}}{E_{\rm true}} | ||
| 95 | + * \f] | ||
| 63 | ***************************************************************************/ | 96 | ***************************************************************************/ |
| 64 | class GCTAEdisp2D : public GCTAEdisp { | 97 | class GCTAEdisp2D : public GCTAEdisp { |
| 65 | 98 | ||
| @@ -135,7 +168,6 @@ private: | @@ -135,7 +168,6 @@ private: | ||
| 135 | void set_table(void); | 168 | void set_table(void); |
| 136 | void set_boundaries(void); | 169 | void set_boundaries(void); |
| 137 | void set_max_edisp(void) const; | 170 | void set_max_edisp(void) const; |
| 138 | - void convert_dm2dlogE(void); | ||
| 139 | void normalize_table(void); | 171 | void normalize_table(void); |
| 140 | int table_index(const int& ietrue, | 172 | int table_index(const int& ietrue, |
| 141 | const int& imigra, | 173 | const int& imigra, |
| @@ -188,7 +220,7 @@ private: | @@ -188,7 +220,7 @@ private: | ||
| 188 | m_parent(parent), | 220 | m_parent(parent), |
| 189 | m_logEsrc(logEsrc), | 221 | m_logEsrc(logEsrc), |
| 190 | m_theta(theta) { } | 222 | m_theta(theta) { } |
| 191 | - double eval(const double& x); | 223 | + double eval(const double& logEobs); |
| 192 | protected: | 224 | protected: |
| 193 | const GCTAEdisp2D* m_parent; //!< Pointer to parent class | 225 | const GCTAEdisp2D* m_parent; //!< Pointer to parent class |
| 194 | double m_logEsrc; //!< True photon energy | 226 | double m_logEsrc; //!< True photon energy |
inst/cta/src/GCTAEdisp2D.cpp
| @@ -178,7 +178,7 @@ GCTAEdisp2D& GCTAEdisp2D::operator=(const GCTAEdisp2D& edisp) | @@ -178,7 +178,7 @@ GCTAEdisp2D& GCTAEdisp2D::operator=(const GCTAEdisp2D& edisp) | ||
| 178 | 178 | ||
| 179 | 179 | ||
| 180 | /***********************************************************************//** | 180 | /***********************************************************************//** |
| 181 | - * @brief Return energy dispersion in units of \f$(\log_{10} MeV)^{-1}\f$ | 181 | + * @brief Return energy dispersion in units of MeV\f$^{-1}\f$ |
| 182 | * | 182 | * |
| 183 | * @param[in] logEobs Log10 of the measured energy (TeV). | 183 | * @param[in] logEobs Log10 of the measured energy (TeV). |
| 184 | * @param[in] logEsrc Log10 of the true photon energy (TeV). | 184 | * @param[in] logEsrc Log10 of the true photon energy (TeV). |
| @@ -186,19 +186,20 @@ GCTAEdisp2D& GCTAEdisp2D::operator=(const GCTAEdisp2D& edisp) | @@ -186,19 +186,20 @@ GCTAEdisp2D& GCTAEdisp2D::operator=(const GCTAEdisp2D& edisp) | ||
| 186 | * @param[in] phi Azimuth angle in camera system (rad). Not used in this method. | 186 | * @param[in] phi Azimuth angle in camera system (rad). Not used in this method. |
| 187 | * @param[in] zenith Zenith angle in Earth system (rad). Not used in this method. | 187 | * @param[in] zenith Zenith angle in Earth system (rad). Not used in this method. |
| 188 | * @param[in] azimuth Azimuth angle in Earth system (rad). Not used in this method. | 188 | * @param[in] azimuth Azimuth angle in Earth system (rad). Not used in this method. |
| 189 | + * @return Energy dispersion (MeV\f$^{-1}\f$) | ||
| 189 | * | 190 | * |
| 190 | * Returns the energy dispersion | 191 | * Returns the energy dispersion |
| 191 | * | 192 | * |
| 192 | * \f[ | 193 | * \f[ |
| 193 | - * E_{\rm disp}(\log_{10} E_{\rm reco} | \log_{10} E_{\rm true}, \theta) = | ||
| 194 | - * \frac{dP}{d\log_{10} E_{\rm reco}} | 194 | + * E_{\rm disp}(E_{\rm true}, E_{\rm reco}, \theta) = |
| 195 | + * \frac{E_{\rm disp}(E_{\rm true}, m, \theta)}{E_{\rm true}} | ||
| 195 | * \f] | 196 | * \f] |
| 196 | * | 197 | * |
| 197 | - * in units of \f$(\log_{10} MeV)^{-1}\f$ where | ||
| 198 | - * \f$\log_{10} E_{\rm reco}\f$ is the logarithm of the reconstructed energy | ||
| 199 | - * in TeV, | ||
| 200 | - * \f$\log_{10} E_{\rm true}\f$ is the logarithm of the true energy in TeV and | ||
| 201 | - * \f$\theta\f$ is the offset angle in radians. | 198 | + * in units of MeV\f$^{-1}\f$ where |
| 199 | + * \f$\log_{10} E_{\rm reco}\f$ is the base 10 logarithm of the reconstructed | ||
| 200 | + * energy in TeV, | ||
| 201 | + * \f$\log_{10} E_{\rm true}\f$ is the base 10 logarithm of the true energy | ||
| 202 | + * in TeV and \f$\theta\f$ is the offset angle in radians. | ||
| 202 | ***************************************************************************/ | 203 | ***************************************************************************/ |
| 203 | double GCTAEdisp2D::operator()(const double& logEobs, | 204 | double GCTAEdisp2D::operator()(const double& logEobs, |
| 204 | const double& logEsrc, | 205 | const double& logEsrc, |
| @@ -237,6 +238,19 @@ double GCTAEdisp2D::operator()(const double& logEobs, | @@ -237,6 +238,19 @@ double GCTAEdisp2D::operator()(const double& logEobs, | ||
| 237 | edisp = 0.0; | 238 | edisp = 0.0; |
| 238 | } | 239 | } |
| 239 | 240 | ||
| 241 | + // If energy dispersion is positive then compute true energy | ||
| 242 | + // in MeV and divide the energy dispersion value by this | ||
| 243 | + // quantity to get the energy dispersion per MeV | ||
| 244 | + else { | ||
| 245 | + | ||
| 246 | + // Compute true energy | ||
| 247 | + double etrue = std::pow(10.0, logEsrc+6.0); | ||
| 248 | + | ||
| 249 | + // Compute energy dispersion per MeV | ||
| 250 | + edisp /= etrue; | ||
| 251 | + | ||
| 252 | + } | ||
| 253 | + | ||
| 240 | } // endif: migration was in valid range | 254 | } // endif: migration was in valid range |
| 241 | 255 | ||
| 242 | } // endif: true energy and offset angle was in valid range | 256 | } // endif: true energy and offset angle was in valid range |
| @@ -325,7 +339,7 @@ void GCTAEdisp2D::table(const GCTAResponseTable& table) | @@ -325,7 +339,7 @@ void GCTAEdisp2D::table(const GCTAResponseTable& table) | ||
| 325 | * MATRIX - Migration matrix | 339 | * MATRIX - Migration matrix |
| 326 | * | 340 | * |
| 327 | * The data are stored in the m_edisp member. The energy axis will be set to | 341 | * The data are stored in the m_edisp member. The energy axis will be set to |
| 328 | - * log10, the offset angle axis to radians. | 342 | + * \f$log_{10} E_{\rm true}\f$, the offset angle axis to radians. |
| 329 | * | 343 | * |
| 330 | * The method assures that the energy dispersion is properly normalised. | 344 | * The method assures that the energy dispersion is properly normalised. |
| 331 | ***************************************************************************/ | 345 | ***************************************************************************/ |
| @@ -405,7 +419,7 @@ void GCTAEdisp2D::load(const GFilename& filename) | @@ -405,7 +419,7 @@ void GCTAEdisp2D::load(const GFilename& filename) | ||
| 405 | * @brief Save energy dispersion table into FITS file | 419 | * @brief Save energy dispersion table into FITS file |
| 406 | * | 420 | * |
| 407 | * @param[in] filename FITS file name. | 421 | * @param[in] filename FITS file name. |
| 408 | - * @param[in] clobber Overwrite existing file? (default: false) | 422 | + * @param[in] clobber Overwrite existing file? |
| 409 | * | 423 | * |
| 410 | * Saves energy dispersion into a FITS file. If a file with the given | 424 | * Saves energy dispersion into a FITS file. If a file with the given |
| 411 | * @p filename does not yet exist it will be created, otherwise the method | 425 | * @p filename does not yet exist it will be created, otherwise the method |
| @@ -901,7 +915,7 @@ double GCTAEdisp2D::prob_erecobin(const GEnergy& ereco_min, | @@ -901,7 +915,7 @@ double GCTAEdisp2D::prob_erecobin(const GEnergy& ereco_min, | ||
| 901 | } | 915 | } |
| 902 | 916 | ||
| 903 | // Compute integral | 917 | // Compute integral |
| 904 | - prob += 0.5 * (f1 + f2) * std::log10(x2/x1); | 918 | + prob += 0.5 * (f1 + f2) * (x2 - x1); |
| 905 | 919 | ||
| 906 | // Set second node as first node | 920 | // Set second node as first node |
| 907 | x1 = x2; | 921 | x1 = x2; |
| @@ -923,7 +937,7 @@ double GCTAEdisp2D::prob_erecobin(const GEnergy& ereco_min, | @@ -923,7 +937,7 @@ double GCTAEdisp2D::prob_erecobin(const GEnergy& ereco_min, | ||
| 923 | etrue_nodes.wgt_right(), | 937 | etrue_nodes.wgt_right(), |
| 924 | theta_nodes.wgt_left(), | 938 | theta_nodes.wgt_left(), |
| 925 | theta_nodes.wgt_right(), x2); | 939 | theta_nodes.wgt_right(), x2); |
| 926 | - prob += 0.5 * (f1 + f2) * std::log10(x2/x1); | 940 | + prob += 0.5 * (f1 + f2) * (x2 - x1); |
| 927 | } | 941 | } |
| 928 | 942 | ||
| 929 | } // endif: logEsrc, theta and migration range were valid | 943 | } // endif: logEsrc, theta and migration range were valid |
| @@ -1329,8 +1343,18 @@ void GCTAEdisp2D::compute_ebounds_src(const double& theta, | @@ -1329,8 +1343,18 @@ void GCTAEdisp2D::compute_ebounds_src(const double& theta, | ||
| 1329 | * @brief Set table | 1343 | * @brief Set table |
| 1330 | * | 1344 | * |
| 1331 | * After assigning or loading a response table, performs all necessary steps | 1345 | * After assigning or loading a response table, performs all necessary steps |
| 1332 | - * to set the table. The method also smoothes the table get rid of numerical | ||
| 1333 | - * fluctuations and normalises the table properly. | 1346 | + * to set the table. Specifically, the method normalizes the energy |
| 1347 | + * dispersion for each offset angle and true energy so that | ||
| 1348 | + * | ||
| 1349 | + * \f[ | ||
| 1350 | + * \int_{m_{\rm min}}^{m_{\rm max}} | ||
| 1351 | + * E_{\rm disp}(E_{\rm true}, m, \theta) \, dm = 1 | ||
| 1352 | + * \f] | ||
| 1353 | + * | ||
| 1354 | + * where \f$m=E_{\rm reco}/E_{\rm true}\f$. | ||
| 1355 | + * | ||
| 1356 | + * For CTA, the method applies a kludge that smoothes the energy dispersion | ||
| 1357 | + * table to get rid of numerical fluctuations. | ||
| 1334 | * | 1358 | * |
| 1335 | * Sets the data members | 1359 | * Sets the data members |
| 1336 | * m_inx_etrue | 1360 | * m_inx_etrue |
| @@ -1367,9 +1391,6 @@ void GCTAEdisp2D::set_table(void) | @@ -1367,9 +1391,6 @@ void GCTAEdisp2D::set_table(void) | ||
| 1367 | // Set table boundaries | 1391 | // Set table boundaries |
| 1368 | set_boundaries(); | 1392 | set_boundaries(); |
| 1369 | 1393 | ||
| 1370 | - // Convert table from dP/dm to dP/dlogE | ||
| 1371 | - convert_dm2dlogE(); | ||
| 1372 | - | ||
| 1373 | // Smooth energy dispersion table (kludge only for CTA) | 1394 | // Smooth energy dispersion table (kludge only for CTA) |
| 1374 | #if defined(G_SMOOTH_EDISP_KLUDGE) | 1395 | #if defined(G_SMOOTH_EDISP_KLUDGE) |
| 1375 | if (gammalib::strip_whitespace(m_edisp.telescope()) == "CTA") { | 1396 | if (gammalib::strip_whitespace(m_edisp.telescope()) == "CTA") { |
| @@ -1451,21 +1472,17 @@ void GCTAEdisp2D::set_max_edisp(void) const | @@ -1451,21 +1472,17 @@ void GCTAEdisp2D::set_max_edisp(void) const | ||
| 1451 | * Normalize the energy dispersion table using | 1472 | * Normalize the energy dispersion table using |
| 1452 | * | 1473 | * |
| 1453 | * \f[ | 1474 | * \f[ |
| 1454 | - * \int_{\log_{10} E_{\rm reco}^{\rm min}}^{\log_{10} E_{\rm reco}^{\rm max}} | ||
| 1455 | - * E_{\rm disp}(\log_{10} E_{\rm reco} | \log_{10} E_{\rm true}, \theta) | ||
| 1456 | - * \, d(\log_{10} E_{\rm reco}) = 1 | 1475 | + * \int_{E_{\rm reco}^{\rm min}}^{E_{\rm reco}^{\rm max}} |
| 1476 | + * E_{\rm disp}(E_{\rm true}, E_{\rm reco}, \theta) \, dE_{\rm reco} = 1 | ||
| 1457 | * \f] | 1477 | * \f] |
| 1458 | * | 1478 | * |
| 1459 | * where | 1479 | * where |
| 1460 | - * \f$E_{\rm reco}^{\rm min}\f$ is the minimum and | ||
| 1461 | - * \f$E_{\rm reco}^{\rm max}\f$ the maximum reconstructed energy for a given | ||
| 1462 | - * true energy, returned by the method ebounds_obs(), | ||
| 1463 | - * \f$E_{\rm disp}(\log_{10} E_{\rm reco} | \log_{10} E_{\rm true}, \theta)\f$ | ||
| 1464 | - * is the energy dispersion returned by the GCTAEdisp2D::operator(), given in | ||
| 1465 | - * units of \f$(\log_{10} MeV)^{-1}\f$, | ||
| 1466 | - * \f$\log_{10} E_{\rm reco}\f$ is the logarithm of the reconstructued energy | ||
| 1467 | - * in TeV, and | ||
| 1468 | - * \f$\log_{10} E_{\rm true}\f$ is the logarithm of the true energy in TeV. | 1480 | + * \f$E_{\rm reco}_{\rm min}\f$ and \f$E_{\rm reco}_{\rm max}\f$ is the |
| 1481 | + * minimum and maximum migration value for a given true energy as returned | ||
| 1482 | + * by the ebounds_obs() method, and | ||
| 1483 | + * \f$E_{\rm disp}(E_{\rm true}, E_{\rm reco}, \theta)\f$ is the energy | ||
| 1484 | + * dispersion returned by the GCTAEdisp2D::operator(), given in units of | ||
| 1485 | + * \f$MeV^{-1}\f$. | ||
| 1469 | * | 1486 | * |
| 1470 | * The normalisation is performed for each \f$E_{\rm true}\f$ and | 1487 | * The normalisation is performed for each \f$E_{\rm true}\f$ and |
| 1471 | * \f$\theta\f$ bin using a Romberg integration method. Since the | 1488 | * \f$\theta\f$ bin using a Romberg integration method. Since the |
| @@ -1502,7 +1519,7 @@ void GCTAEdisp2D::normalize_table(void) | @@ -1502,7 +1519,7 @@ void GCTAEdisp2D::normalize_table(void) | ||
| 1502 | // Loop over true photon energy | 1519 | // Loop over true photon energy |
| 1503 | for (int i_etrue = 0; i_etrue < etrue_size; ++i_etrue) { | 1520 | for (int i_etrue = 0; i_etrue < etrue_size; ++i_etrue) { |
| 1504 | 1521 | ||
| 1505 | - // Get energy | 1522 | + // Get the base 10 logarithm of the true energy |
| 1506 | double emin = std::log10(m_edisp.axis_lo(m_inx_etrue,i_etrue)); | 1523 | double emin = std::log10(m_edisp.axis_lo(m_inx_etrue,i_etrue)); |
| 1507 | double emax = std::log10(m_edisp.axis_hi(m_inx_etrue,i_etrue)); | 1524 | double emax = std::log10(m_edisp.axis_hi(m_inx_etrue,i_etrue)); |
| 1508 | double logEsrc = 0.5*(emin+emax); | 1525 | double logEsrc = 0.5*(emin+emax); |
| @@ -1516,9 +1533,9 @@ void GCTAEdisp2D::normalize_table(void) | @@ -1516,9 +1533,9 @@ void GCTAEdisp2D::normalize_table(void) | ||
| 1516 | // Loop over all energy intervals | 1533 | // Loop over all energy intervals |
| 1517 | for (int i = 0; i < ebounds.size(); ++i) { | 1534 | for (int i = 0; i < ebounds.size(); ++i) { |
| 1518 | 1535 | ||
| 1519 | - // Get energy boundaries | ||
| 1520 | - emin = ebounds.emin(i).log10TeV(); | ||
| 1521 | - emax = ebounds.emax(i).log10TeV(); | 1536 | + // Get energy boundaries in log10 of energy in MeV |
| 1537 | + emin = ebounds.emin(i).log10MeV(); | ||
| 1538 | + emax = ebounds.emax(i).log10MeV(); | ||
| 1522 | 1539 | ||
| 1523 | // Setup integration function | 1540 | // Setup integration function |
| 1524 | edisp_kern integrand(this, logEsrc, theta); | 1541 | edisp_kern integrand(this, logEsrc, theta); |
| @@ -1561,106 +1578,6 @@ void GCTAEdisp2D::normalize_table(void) | @@ -1561,106 +1578,6 @@ void GCTAEdisp2D::normalize_table(void) | ||
| 1561 | 1578 | ||
| 1562 | 1579 | ||
| 1563 | /***********************************************************************//** | 1580 | /***********************************************************************//** |
| 1564 | - * @brief Convert table from dP/dm to dP/dlog10E | ||
| 1565 | - * | ||
| 1566 | - * The energy dispersion table when read-in is normalized so that | ||
| 1567 | - * | ||
| 1568 | - * \f[ | ||
| 1569 | - * \int_{{\rm migra}_{\rm min}}^{{\rm migra}_{\rm max}} | ||
| 1570 | - * E_{\rm disp}({\rm migra}) \, d{\rm migra} = 1 | ||
| 1571 | - * \f] | ||
| 1572 | - * | ||
| 1573 | - * while the class needs values that are normalize according to | ||
| 1574 | - * | ||
| 1575 | - * \f[ | ||
| 1576 | - * \int_{\log_{10} E_{\rm reco}^{\rm min}}^{\log_{10} E_{\rm reco}^{\rm max}} | ||
| 1577 | - * E_{\rm disp}(\log_{10} E_{\rm reco}) \, d(\log_{10} E_{\rm reco}) = 1 | ||
| 1578 | - * \f] | ||
| 1579 | - * | ||
| 1580 | - * The method thus multiplies each matrix element with | ||
| 1581 | - * | ||
| 1582 | - * \f[ | ||
| 1583 | - * \frac{{\rm migra}_{\rm max} - {\rm migra}_{\rm min}} | ||
| 1584 | - * {\log_{10} E_{\rm reco}^{\rm max} - \log_{10} E_{\rm reco}^{\rm min}} | ||
| 1585 | - * \f] | ||
| 1586 | - * | ||
| 1587 | - * where | ||
| 1588 | - * \f${\rm migra}_{\rm min}\f$ and \f${\rm migra}_{\rm max}\f$ are the | ||
| 1589 | - * minimum and maximum migration values of a given bin and | ||
| 1590 | - * \f$E_{\rm reco}^{\rm min}\f$ and \f$E_{\rm reco}^{\rm max}\f$ are the | ||
| 1591 | - * minimum and maximum reconstructed energy values for the same bin. | ||
| 1592 | - ***************************************************************************/ | ||
| 1593 | -void GCTAEdisp2D::convert_dm2dlogE(void) | ||
| 1594 | -{ | ||
| 1595 | - // Optional dump header | ||
| 1596 | - #if defined(G_DEBUG_HESS_RENORMALIZATION) | ||
| 1597 | - std::cout << "GCTAEdisp2D::hess_renormalization:" << std::endl; | ||
| 1598 | - #endif | ||
| 1599 | - | ||
| 1600 | - // Get axes dimensions | ||
| 1601 | - int etrue_size = m_edisp.axis_bins(m_inx_etrue); | ||
| 1602 | - int migra_size = m_edisp.axis_bins(m_inx_migra); | ||
| 1603 | - int theta_size = m_edisp.axis_bins(m_inx_theta); | ||
| 1604 | - | ||
| 1605 | - // Perform renormalization | ||
| 1606 | - for (int i_etrue = 0; i_etrue < etrue_size; ++i_etrue) { | ||
| 1607 | - | ||
| 1608 | - // Get true energy in TeV | ||
| 1609 | - double etrue = std::sqrt(m_edisp.axis_lo(m_inx_etrue, i_etrue) * | ||
| 1610 | - m_edisp.axis_hi(m_inx_etrue, i_etrue)); | ||
| 1611 | - | ||
| 1612 | - // Get offset angle | ||
| 1613 | - for (int i_theta = 0; i_theta < theta_size; ++i_theta) { | ||
| 1614 | - | ||
| 1615 | - // Intialize control sum | ||
| 1616 | - #if defined(G_DEBUG_HESS_RENORMALIZATION) | ||
| 1617 | - double sum = 0.0; | ||
| 1618 | - #endif | ||
| 1619 | - | ||
| 1620 | - // Loop over all migration bins | ||
| 1621 | - int offset = table_index(i_etrue, 0, i_theta); | ||
| 1622 | - int stride = table_stride(m_inx_migra); | ||
| 1623 | - for (int i_migra = 0, i = offset; i_migra < migra_size; ++i_migra, i += stride) { | ||
| 1624 | - | ||
| 1625 | - // Get migration bin size | ||
| 1626 | - double migra_bin = m_edisp.axis_hi(m_inx_migra, i_migra) - | ||
| 1627 | - m_edisp.axis_lo(m_inx_migra, i_migra); | ||
| 1628 | - | ||
| 1629 | - // Get dlog10(E_reco) bin size | ||
| 1630 | - double ereco_min = m_edisp.axis_lo(m_inx_migra, i_migra) * etrue; | ||
| 1631 | - double ereco_max = m_edisp.axis_hi(m_inx_migra, i_migra) * etrue; | ||
| 1632 | - double dlogE_bin = std::log10(ereco_max) - std::log10(ereco_min); | ||
| 1633 | - | ||
| 1634 | - // Compute normalization factor | ||
| 1635 | - double norm = migra_bin / dlogE_bin; | ||
| 1636 | - | ||
| 1637 | - // Multiply matrix element | ||
| 1638 | - m_edisp(m_inx_matrix, i) *= norm; | ||
| 1639 | - | ||
| 1640 | - // Update test sum | ||
| 1641 | - #if defined(G_DEBUG_HESS_RENORMALIZATION) | ||
| 1642 | - sum += m_edisp(m_inx_matrix, i) * dlogE_bin; | ||
| 1643 | - #endif | ||
| 1644 | - | ||
| 1645 | - } // endfor: looped over migration bins | ||
| 1646 | - | ||
| 1647 | - // Optional test dump | ||
| 1648 | - #if defined(G_DEBUG_HESS_RENORMALIZATION) | ||
| 1649 | - std::cout << "etrue=" << etrue << " TeV "; | ||
| 1650 | - std::cout << "i_theta=" << i_theta << ": "; | ||
| 1651 | - std::cout << sum << std::endl; | ||
| 1652 | - #endif | ||
| 1653 | - | ||
| 1654 | - } // endfor: looped over offset angle | ||
| 1655 | - | ||
| 1656 | - } // endfor: looped over true energies | ||
| 1657 | - | ||
| 1658 | - // Return | ||
| 1659 | - return; | ||
| 1660 | -} | ||
| 1661 | - | ||
| 1662 | - | ||
| 1663 | -/***********************************************************************//** | ||
| 1664 | * @brief Return index of response table element | 1581 | * @brief Return index of response table element |
| 1665 | * | 1582 | * |
| 1666 | * @param[in] ietrue True energy index. | 1583 | * @param[in] ietrue True energy index. |
| @@ -1811,6 +1728,9 @@ double GCTAEdisp2D::table_value(const int& base_ll, | @@ -1811,6 +1728,9 @@ double GCTAEdisp2D::table_value(const int& base_ll, | ||
| 1811 | 1728 | ||
| 1812 | /***********************************************************************//** | 1729 | /***********************************************************************//** |
| 1813 | * @brief Smoothed energy dispersion table | 1730 | * @brief Smoothed energy dispersion table |
| 1731 | + * | ||
| 1732 | + * This method implements the kludge for CTA that reduces the statistical | ||
| 1733 | + * noise in the energy dispersion matrix. | ||
| 1814 | ***************************************************************************/ | 1734 | ***************************************************************************/ |
| 1815 | void GCTAEdisp2D::smooth_table(void) | 1735 | void GCTAEdisp2D::smooth_table(void) |
| 1816 | { | 1736 | { |
| @@ -2178,23 +2098,47 @@ GNdarray GCTAEdisp2D::gaussian_array(const double& mean, | @@ -2178,23 +2098,47 @@ GNdarray GCTAEdisp2D::gaussian_array(const double& mean, | ||
| 2178 | 2098 | ||
| 2179 | 2099 | ||
| 2180 | /***********************************************************************//** | 2100 | /***********************************************************************//** |
| 2181 | - * @brief Integration kernel for edisp_kern() class | 2101 | + * @brief Integration kernel for GCTAEdisp2D::edisp_kern class |
| 2182 | * | 2102 | * |
| 2183 | - * @param[in] x Function value. | 2103 | + * @param[in] logEobs Base 10 logarithm of observation energy (\f$\log_{10}\f$ MeV). |
| 2104 | + * @return Energy dispersion (\f$(\log_{10}\f$ MeV\f$)^{-1}\f$). | ||
| 2184 | * | 2105 | * |
| 2185 | - * This method implements the integration kernel needed for the edisp_kern() | ||
| 2186 | - * class. | 2106 | + * This method implements the function |
| 2107 | + * | ||
| 2108 | + * \f[ | ||
| 2109 | + * E_{\rm disp}(E_{\rm true}, \log_{10} E_{\rm reco}, \theta) = | ||
| 2110 | + * E_{\rm disp}(E_{\rm true}, m, \theta) \times | ||
| 2111 | + * \frac{\log 10 \times E_{\rm reco}}{E_{\rm true}} | ||
| 2112 | + * \f] | ||
| 2113 | + * | ||
| 2114 | + * which is the integration kernel needed for the GCTAEdisp2D::edisp_kern | ||
| 2115 | + * class. The class is used by GCTAEdisp2D::normalize_table to integrate the | ||
| 2116 | + * energy dispersion information using | ||
| 2117 | + * | ||
| 2118 | + * \f[ | ||
| 2119 | + * \int_{\log_{10} E_{\rm reco}^{\rm min}}^{\log_{10} E_{\rm reco}^{\rm max}} | ||
| 2120 | + * E_{\rm disp}(E_{\rm true}, \log_{10} E_{\rm reco}, \theta) \, | ||
| 2121 | + * d(\log_{10} E_{\rm reco}) | ||
| 2122 | + * \f] | ||
| 2187 | ***************************************************************************/ | 2123 | ***************************************************************************/ |
| 2188 | -double GCTAEdisp2D::edisp_kern::eval(const double& x) | 2124 | +double GCTAEdisp2D::edisp_kern::eval(const double& logEobs) |
| 2189 | { | 2125 | { |
| 2190 | - // Get function value | ||
| 2191 | - double value = m_parent->operator()(x, m_logEsrc, m_theta); | 2126 | + // Get function value. Note that logEobs is in log10 MeV, while we need |
| 2127 | + // the value in log10 TeV. | ||
| 2128 | + double value = m_parent->operator()(logEobs-6.0, m_logEsrc, m_theta); | ||
| 2129 | + | ||
| 2130 | + // Compute reconstructed energy in MeV. Note that logEobs is in | ||
| 2131 | + // log10 MeV! | ||
| 2132 | + double ereco = std::pow(10.0, logEobs); | ||
| 2133 | + | ||
| 2134 | + // Normalize energy dispersion so that the units are per log10 MeV | ||
| 2135 | + value *= gammalib::ln10 * ereco; | ||
| 2192 | 2136 | ||
| 2193 | // Compile option: Check for NaN | 2137 | // Compile option: Check for NaN |
| 2194 | #if defined(G_NAN_CHECK) | 2138 | #if defined(G_NAN_CHECK) |
| 2195 | if (gammalib::is_notanumber(value) || gammalib::is_infinite(value)) { | 2139 | if (gammalib::is_notanumber(value) || gammalib::is_infinite(value)) { |
| 2196 | std::cout << "*** ERROR: GCTAEdisp2D::edisp_kern::eval"; | 2140 | std::cout << "*** ERROR: GCTAEdisp2D::edisp_kern::eval"; |
| 2197 | - std::cout << "(x=" << x << "): "; | 2141 | + std::cout << "(logEobs=" << logEobs << "): "; |
| 2198 | std::cout << " NaN/Inf encountered"; | 2142 | std::cout << " NaN/Inf encountered"; |
| 2199 | std::cout << " (value=" << value; | 2143 | std::cout << " (value=" << value; |
| 2200 | std::cout << ")" << std::endl; | 2144 | std::cout << ")" << std::endl; |