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 | 52 | * @brief CTA 2D energy dispersion class |
| 53 | 53 | * |
| 54 | 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 | 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 | 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 | 97 | class GCTAEdisp2D : public GCTAEdisp { |
| 65 | 98 | |
| ... | ... | @@ -135,7 +168,6 @@ private: |
| 135 | 168 | void set_table(void); |
| 136 | 169 | void set_boundaries(void); |
| 137 | 170 | void set_max_edisp(void) const; |
| 138 | - void convert_dm2dlogE(void); | |
| 139 | 171 | void normalize_table(void); |
| 140 | 172 | int table_index(const int& ietrue, |
| 141 | 173 | const int& imigra, |
| ... | ... | @@ -188,7 +220,7 @@ private: |
| 188 | 220 | m_parent(parent), |
| 189 | 221 | m_logEsrc(logEsrc), |
| 190 | 222 | m_theta(theta) { } |
| 191 | - double eval(const double& x); | |
| 223 | + double eval(const double& logEobs); | |
| 192 | 224 | protected: |
| 193 | 225 | const GCTAEdisp2D* m_parent; //!< Pointer to parent class |
| 194 | 226 | double m_logEsrc; //!< True photon energy | ... | ... |
inst/cta/src/GCTAEdisp2D.cpp
| ... | ... | @@ -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 | 183 | * @param[in] logEobs Log10 of the measured energy (TeV). |
| 184 | 184 | * @param[in] logEsrc Log10 of the true photon energy (TeV). |
| ... | ... | @@ -186,19 +186,20 @@ GCTAEdisp2D& GCTAEdisp2D::operator=(const GCTAEdisp2D& edisp) |
| 186 | 186 | * @param[in] phi Azimuth angle in camera system (rad). Not used in this method. |
| 187 | 187 | * @param[in] zenith Zenith angle in Earth system (rad). Not used in this method. |
| 188 | 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 | 191 | * Returns the energy dispersion |
| 191 | 192 | * |
| 192 | 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 | 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 | 204 | double GCTAEdisp2D::operator()(const double& logEobs, |
| 204 | 205 | const double& logEsrc, |
| ... | ... | @@ -237,6 +238,19 @@ double GCTAEdisp2D::operator()(const double& logEobs, |
| 237 | 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 | 254 | } // endif: migration was in valid range |
| 241 | 255 | |
| 242 | 256 | } // endif: true energy and offset angle was in valid range |
| ... | ... | @@ -325,7 +339,7 @@ void GCTAEdisp2D::table(const GCTAResponseTable& table) |
| 325 | 339 | * MATRIX - Migration matrix |
| 326 | 340 | * |
| 327 | 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 | 344 | * The method assures that the energy dispersion is properly normalised. |
| 331 | 345 | ***************************************************************************/ |
| ... | ... | @@ -405,7 +419,7 @@ void GCTAEdisp2D::load(const GFilename& filename) |
| 405 | 419 | * @brief Save energy dispersion table into FITS file |
| 406 | 420 | * |
| 407 | 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 | 424 | * Saves energy dispersion into a FITS file. If a file with the given |
| 411 | 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 | 915 | } |
| 902 | 916 | |
| 903 | 917 | // Compute integral |
| 904 | - prob += 0.5 * (f1 + f2) * std::log10(x2/x1); | |
| 918 | + prob += 0.5 * (f1 + f2) * (x2 - x1); | |
| 905 | 919 | |
| 906 | 920 | // Set second node as first node |
| 907 | 921 | x1 = x2; |
| ... | ... | @@ -923,7 +937,7 @@ double GCTAEdisp2D::prob_erecobin(const GEnergy& ereco_min, |
| 923 | 937 | etrue_nodes.wgt_right(), |
| 924 | 938 | theta_nodes.wgt_left(), |
| 925 | 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 | 943 | } // endif: logEsrc, theta and migration range were valid |
| ... | ... | @@ -1329,8 +1343,18 @@ void GCTAEdisp2D::compute_ebounds_src(const double& theta, |
| 1329 | 1343 | * @brief Set table |
| 1330 | 1344 | * |
| 1331 | 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 | 1359 | * Sets the data members |
| 1336 | 1360 | * m_inx_etrue |
| ... | ... | @@ -1367,9 +1391,6 @@ void GCTAEdisp2D::set_table(void) |
| 1367 | 1391 | // Set table boundaries |
| 1368 | 1392 | set_boundaries(); |
| 1369 | 1393 | |
| 1370 | - // Convert table from dP/dm to dP/dlogE | |
| 1371 | - convert_dm2dlogE(); | |
| 1372 | - | |
| 1373 | 1394 | // Smooth energy dispersion table (kludge only for CTA) |
| 1374 | 1395 | #if defined(G_SMOOTH_EDISP_KLUDGE) |
| 1375 | 1396 | if (gammalib::strip_whitespace(m_edisp.telescope()) == "CTA") { |
| ... | ... | @@ -1451,21 +1472,17 @@ void GCTAEdisp2D::set_max_edisp(void) const |
| 1451 | 1472 | * Normalize the energy dispersion table using |
| 1452 | 1473 | * |
| 1453 | 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 | 1477 | * \f] |
| 1458 | 1478 | * |
| 1459 | 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 | 1487 | * The normalisation is performed for each \f$E_{\rm true}\f$ and |
| 1471 | 1488 | * \f$\theta\f$ bin using a Romberg integration method. Since the |
| ... | ... | @@ -1502,7 +1519,7 @@ void GCTAEdisp2D::normalize_table(void) |
| 1502 | 1519 | // Loop over true photon energy |
| 1503 | 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 | 1523 | double emin = std::log10(m_edisp.axis_lo(m_inx_etrue,i_etrue)); |
| 1507 | 1524 | double emax = std::log10(m_edisp.axis_hi(m_inx_etrue,i_etrue)); |
| 1508 | 1525 | double logEsrc = 0.5*(emin+emax); |
| ... | ... | @@ -1516,9 +1533,9 @@ void GCTAEdisp2D::normalize_table(void) |
| 1516 | 1533 | // Loop over all energy intervals |
| 1517 | 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 | 1540 | // Setup integration function |
| 1524 | 1541 | edisp_kern integrand(this, logEsrc, theta); |
| ... | ... | @@ -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 | 1581 | * @brief Return index of response table element |
| 1665 | 1582 | * |
| 1666 | 1583 | * @param[in] ietrue True energy index. |
| ... | ... | @@ -1811,6 +1728,9 @@ double GCTAEdisp2D::table_value(const int& base_ll, |
| 1811 | 1728 | |
| 1812 | 1729 | /***********************************************************************//** |
| 1813 | 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 | 1735 | void GCTAEdisp2D::smooth_table(void) |
| 1816 | 1736 | { |
| ... | ... | @@ -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 | 2137 | // Compile option: Check for NaN |
| 2194 | 2138 | #if defined(G_NAN_CHECK) |
| 2195 | 2139 | if (gammalib::is_notanumber(value) || gammalib::is_infinite(value)) { |
| 2196 | 2140 | std::cout << "*** ERROR: GCTAEdisp2D::edisp_kern::eval"; |
| 2197 | - std::cout << "(x=" << x << "): "; | |
| 2141 | + std::cout << "(logEobs=" << logEobs << "): "; | |
| 2198 | 2142 | std::cout << " NaN/Inf encountered"; |
| 2199 | 2143 | std::cout << " (value=" << value; |
| 2200 | 2144 | std::cout << ")" << std::endl; | ... | ... |