Commit 05fc86b04f3eee85d1e473ae9c7e2c011ce1430b
1 parent
b3e3958f
Revise GCTAEdisp2D class (#2704)
Showing
3 changed files
with
516 additions
and
354 deletions
inst/cta/include/GCTAEdisp2D.hpp
| ... | ... | @@ -54,45 +54,37 @@ namespace gammalib { |
| 54 | 54 | * |
| 55 | 55 | * This class implements the energy dispersion for the CTA 2D response. The |
| 56 | 56 | * CTA 2D energy dispersion is in fact a 3-dimensional function, where the |
| 57 | - * energy dispersion is given as function of true energy \f$E_{\rm true}\f$, | |
| 58 | - * migration value \f$m = E_{\rm reco}/E_{\rm true}\f$ and offset angle | |
| 59 | - * \f$\theta\f$ | |
| 57 | + * energy dispersion | |
| 60 | 58 | * |
| 61 | 59 | * \f[ |
| 62 | - * E_{\rm disp}(E_{\rm true}, m, \theta) | |
| 60 | + * E_{\rm disp}(E_{\rm reco} | E_{\rm true}, \theta) | |
| 63 | 61 | * \f] |
| 64 | 62 | * |
| 65 | - * with | |
| 63 | + * is given as function of true energy \f$E_{\rm true}\f$, reconstructed | |
| 64 | + * energy \f$E_{\rm reco}\f$ and offset angle \f$\theta\f$. The | |
| 65 | + * GCTAEdisp2D::operator() returns the value of the function | |
| 66 | + * \f$E_{\rm disp}(E_{\rm reco} | E_{\rm true}, \theta)\f$ in units of | |
| 67 | + * MeV\f$^{-1}\f$ with | |
| 66 | 68 | * |
| 67 | 69 | * \f[ |
| 68 | - * \int_{m_{\rm min}}^{m_{\rm max}} | |
| 69 | - * E_{\rm disp}(E_{\rm true}, m, \theta) \, dm = 1 | |
| 70 | - * \f] | |
| 71 | - * | |
| 72 | - * The energy dispersion as function of reconstructed energy | |
| 73 | - * \f$E_{\rm reco}\f$ is given by | |
| 74 | - * | |
| 75 | - * \f[ | |
| 76 | - * E_{\rm disp}(E_{\rm true}, E_{\rm reco}, \theta) = | |
| 77 | - * \frac{E_{\rm disp}(E_{\rm true}, m, \theta)}{E_{\rm true}} | |
| 70 | + * \int_{E_{\rm reco}^{\rm min}}^{E_{\rm reco}^{\rm max}} | |
| 71 | + * E_{\rm disp}(E_{\rm reco} | E_{\rm true}, \theta) \, dE_{\rm reco} = 1 | |
| 78 | 72 | * \f] |
| 79 | 73 | * |
| 80 | - * the energy dispersion as function of the natural logarithm of | |
| 81 | - * reconstructed energy \f$\log E_{\rm reco}\f$ is given by | |
| 74 | + * The energy dispersion is stored internally as a 3-dimensional response | |
| 75 | + * table, spanned by true energy \f$E_{\rm true}\f$, migration | |
| 76 | + * \f$m=E_{\rm reco}/E_{\rm true}\f$ and offset angle \f$\theta\f$, with | |
| 82 | 77 | * |
| 83 | 78 | * \f[ |
| 84 | - * E_{\rm disp}(E_{\rm true}, \log E_{\rm reco}, \theta) = | |
| 85 | - * E_{\rm disp}(E_{\rm true}, m, \theta) \times | |
| 86 | - * \frac{E_{\rm reco}}{E_{\rm true}} | |
| 79 | + * E_{\rm disp}(E_{\rm reco} | E_{\rm true}, \theta) = | |
| 80 | + * \frac{E_{\rm disp}(m | E_{\rm true}, \theta)}{E_{\rm true}} | |
| 87 | 81 | * \f] |
| 88 | 82 | * |
| 89 | - * and the energy dispersion as function of the base 10 logarithm of | |
| 90 | - * reconstructed energy \f$\log_{10} E_{\rm reco}\f$ is given by | |
| 83 | + * and | |
| 91 | 84 | * |
| 92 | 85 | * \f[ |
| 93 | - * E_{\rm disp}(E_{\rm true}, \log_{10} E_{\rm reco}, \theta) = | |
| 94 | - * E_{\rm disp}(E_{\rm true}, m, \theta) \times | |
| 95 | - * \frac{\log 10 \times E_{\rm reco}}{E_{\rm true}} | |
| 86 | + * \int_{m_{\rm min}}^{m_{\rm max}} | |
| 87 | + * E_{\rm disp}(m | E_{\rm true}, \theta) \, dm = 1 | |
| 96 | 88 | * \f] |
| 97 | 89 | ***************************************************************************/ |
| 98 | 90 | class GCTAEdisp2D : public GCTAEdisp { |
| ... | ... | @@ -152,46 +144,47 @@ public: |
| 152 | 144 | |
| 153 | 145 | private: |
| 154 | 146 | // Methods |
| 155 | - void init_members(void); | |
| 156 | - void copy_members(const GCTAEdisp2D& edisp); | |
| 157 | - void free_members(void); | |
| 158 | - void update(const double& logEobs, | |
| 159 | - const double& logEsrc, | |
| 160 | - const double& theta) const; | |
| 161 | - void compute_ebounds_obs(const double& theta = 0.0, | |
| 162 | - const double& phi = 0.0, | |
| 163 | - const double& zenith = 0.0, | |
| 164 | - const double& azimuth = 0.0) const; | |
| 165 | - void compute_ebounds_src(const double& theta = 0.0, | |
| 166 | - const double& phi = 0.0, | |
| 167 | - const double& zenith = 0.0, | |
| 168 | - const double& azimuth = 0.0) const; | |
| 169 | - void set_table(void); | |
| 170 | - void set_boundaries(void); | |
| 171 | - void set_max_edisp(void) const; | |
| 172 | - void normalize_table(void); | |
| 173 | - int table_index(const int& ietrue, | |
| 174 | - const int& imigra, | |
| 175 | - const int& itheta) const; | |
| 176 | - int table_stride(const int& axis) const; | |
| 177 | - double table_value(const int& base_ll, | |
| 178 | - const int& base_lr, | |
| 179 | - const int& base_rl, | |
| 180 | - const int& base_rr, | |
| 181 | - const double& wgt_el, | |
| 182 | - const double& wgt_er, | |
| 183 | - const double& wgt_tl, | |
| 184 | - const double& wgt_tr, | |
| 185 | - const int& offset) const; | |
| 186 | - double table_value(const int& base_ll, | |
| 187 | - const int& base_lr, | |
| 188 | - const int& base_rl, | |
| 189 | - const int& base_rr, | |
| 190 | - const double& wgt_el, | |
| 191 | - const double& wgt_er, | |
| 192 | - const double& wgt_tl, | |
| 193 | - const double& wgt_tr, | |
| 194 | - const double& migra) const; | |
| 147 | + void init_members(void); | |
| 148 | + void copy_members(const GCTAEdisp2D& edisp); | |
| 149 | + void free_members(void); | |
| 150 | + GEnergy etrue(const int& ietrue) const; | |
| 151 | + double migra(const int& imigra) const; | |
| 152 | + double theta(const int& itheta) const; | |
| 153 | + void compute_ereco_bounds(const double& theta = 0.0, | |
| 154 | + const double& phi = 0.0, | |
| 155 | + const double& zenith = 0.0, | |
| 156 | + const double& azimuth = 0.0) const; | |
| 157 | + void compute_etrue_bounds(const double& theta = 0.0, | |
| 158 | + const double& phi = 0.0, | |
| 159 | + const double& zenith = 0.0, | |
| 160 | + const double& azimuth = 0.0) const; | |
| 161 | + void set_table(void); | |
| 162 | + void set_boundaries(void); | |
| 163 | + void set_max_edisp(void); | |
| 164 | + double get_max_edisp(const GEnergy& etrue, const double& theta) const; | |
| 165 | + void normalize_table(void); | |
| 166 | + int table_index(const int& ietrue, | |
| 167 | + const int& imigra, | |
| 168 | + const int& itheta) const; | |
| 169 | + int table_stride(const int& axis) const; | |
| 170 | + double table_value(const int& base_ll, | |
| 171 | + const int& base_lr, | |
| 172 | + const int& base_rl, | |
| 173 | + const int& base_rr, | |
| 174 | + const double& wgt_el, | |
| 175 | + const double& wgt_er, | |
| 176 | + const double& wgt_tl, | |
| 177 | + const double& wgt_tr, | |
| 178 | + const int& offset) const; | |
| 179 | + double table_value(const int& base_ll, | |
| 180 | + const int& base_lr, | |
| 181 | + const int& base_rl, | |
| 182 | + const int& base_rr, | |
| 183 | + const double& wgt_el, | |
| 184 | + const double& wgt_er, | |
| 185 | + const double& wgt_tl, | |
| 186 | + const double& wgt_tr, | |
| 187 | + const double& migra) const; | |
| 195 | 188 | |
| 196 | 189 | // Kludge |
| 197 | 190 | void smooth_table(void); |
| ... | ... | @@ -213,15 +206,15 @@ private: |
| 213 | 206 | const double& total) const; |
| 214 | 207 | |
| 215 | 208 | // Protected classes |
| 216 | - class edisp_kern : public GFunction { | |
| 209 | + class edisp_ereco_kern : public GFunction { | |
| 217 | 210 | public: |
| 218 | - edisp_kern(const GCTAEdisp2D* parent, | |
| 219 | - const GEnergy& etrue, | |
| 220 | - const double& theta) : | |
| 221 | - m_parent(parent), | |
| 222 | - m_etrue(etrue), | |
| 223 | - m_theta(theta) { } | |
| 224 | - double eval(const double& logEobs); | |
| 211 | + edisp_ereco_kern(const GCTAEdisp2D* parent, | |
| 212 | + const GEnergy& etrue, | |
| 213 | + const double& theta) : | |
| 214 | + m_parent(parent), | |
| 215 | + m_etrue(etrue), | |
| 216 | + m_theta(theta) { } | |
| 217 | + double eval(const double& log10Ereco); | |
| 225 | 218 | protected: |
| 226 | 219 | const GCTAEdisp2D* m_parent; //!< Pointer to parent class |
| 227 | 220 | GEnergy m_etrue; //!< True photon energy |
| ... | ... | @@ -229,32 +222,32 @@ private: |
| 229 | 222 | }; |
| 230 | 223 | |
| 231 | 224 | // Members |
| 232 | - mutable GFilename m_filename; //!< Name of Edisp response file | |
| 233 | - GCTAResponseTable m_edisp; //!< Edisp response table | |
| 234 | - mutable bool m_fetched; //!< Signals that Edisp has been fetched | |
| 235 | - int m_inx_etrue; //!< True energy index | |
| 236 | - int m_inx_migra; //!< Migration index | |
| 237 | - int m_inx_theta; //!< Theta index | |
| 238 | - int m_inx_matrix; //!< Matrix | |
| 239 | - double m_logEsrc_min; //!< Minimum logE (log10(E/TeV)) | |
| 240 | - double m_logEsrc_max; //!< Maximum logE (log10(E/TeV)) | |
| 241 | - double m_migra_min; //!< Minimum migration | |
| 242 | - double m_migra_max; //!< Maximum migration | |
| 243 | - double m_theta_min; //!< Minimum theta (radians) | |
| 244 | - double m_theta_max; //!< Maximum theta (radians) | |
| 225 | + mutable GFilename m_filename; //!< Name of Edisp response file | |
| 226 | + GCTAResponseTable m_edisp; //!< Edisp response table | |
| 227 | + mutable bool m_fetched; //!< Signals that Edisp has been fetched | |
| 228 | + int m_inx_etrue; //!< True energy index | |
| 229 | + int m_inx_migra; //!< Migration index | |
| 230 | + int m_inx_theta; //!< Theta index | |
| 231 | + int m_inx_matrix; //!< Matrix | |
| 232 | + double m_logEsrc_min; //!< Minimum logE (log10(E/TeV)) | |
| 233 | + double m_logEsrc_max; //!< Maximum logE (log10(E/TeV)) | |
| 234 | + double m_migra_min; //!< Minimum migration | |
| 235 | + double m_migra_max; //!< Maximum migration | |
| 236 | + double m_theta_min; //!< Minimum theta (radians) | |
| 237 | + double m_theta_max; //!< Maximum theta (radians) | |
| 238 | + std::vector<double> m_max_edisp; //!< Maximum Edisp | |
| 245 | 239 | |
| 246 | 240 | // Computation cache |
| 247 | - mutable bool m_ebounds_obs_computed; | |
| 248 | - mutable bool m_ebounds_src_computed; | |
| 249 | - mutable double m_last_theta_obs; | |
| 250 | - mutable double m_last_theta_src; | |
| 241 | + mutable bool m_ereco_bounds_computed; | |
| 242 | + mutable bool m_etrue_bounds_computed; | |
| 243 | + mutable double m_last_theta_ereco; | |
| 244 | + mutable double m_last_theta_etrue; | |
| 251 | 245 | mutable GEnergy m_last_etrue; |
| 252 | 246 | mutable GEnergy m_last_ereco; |
| 253 | - mutable int m_index_obs; | |
| 254 | - mutable int m_index_src; | |
| 255 | - mutable double m_max_edisp; | |
| 256 | - mutable std::vector<GEbounds> m_ebounds_obs; | |
| 257 | - mutable std::vector<GEbounds> m_ebounds_src; | |
| 247 | + mutable int m_index_ereco; | |
| 248 | + mutable int m_index_etrue; | |
| 249 | + mutable std::vector<GEbounds> m_ereco_bounds; | |
| 250 | + mutable std::vector<GEbounds> m_etrue_bounds; | |
| 258 | 251 | }; |
| 259 | 252 | |
| 260 | 253 | ... | ... |
inst/cta/src/GCTAEdisp2D.cpp
| ... | ... | @@ -191,15 +191,14 @@ GCTAEdisp2D& GCTAEdisp2D::operator=(const GCTAEdisp2D& edisp) |
| 191 | 191 | * Returns the energy dispersion |
| 192 | 192 | * |
| 193 | 193 | * \f[ |
| 194 | - * E_{\rm disp}(E_{\rm true}, E_{\rm reco}, \theta) = | |
| 195 | - * \frac{E_{\rm disp}(E_{\rm true}, m, \theta)}{E_{\rm true}} | |
| 194 | + * E_{\rm disp}(E_{\rm reco} | E_{\rm true}, \theta) = | |
| 195 | + * \frac{E_{\rm disp}(m | E_{\rm true}, \theta)}{E_{\rm true}} | |
| 196 | 196 | * \f] |
| 197 | 197 | * |
| 198 | 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. | |
| 199 | + * \f$E_{\rm reco}\f$ is the reconstructed energy, | |
| 200 | + * \f$E_{\rm true}\f$ is the true energy, and | |
| 201 | + * \f$\theta\f$ is the offset angle. | |
| 203 | 202 | ***************************************************************************/ |
| 204 | 203 | double GCTAEdisp2D::operator()(const GEnergy& ereco, |
| 205 | 204 | const GEnergy& etrue, |
| ... | ... | @@ -222,7 +221,6 @@ double GCTAEdisp2D::operator()(const GEnergy& ereco, |
| 222 | 221 | (theta >= m_theta_min) && (theta <= m_theta_max)) { |
| 223 | 222 | |
| 224 | 223 | // Compute migration (migra=Ereco/Etrue |
| 225 | - //double EobsOverEsrc = std::exp((logEobs-logEsrc) * gammalib::ln10); | |
| 226 | 224 | double migra = ereco.MeV() / etrue.MeV(); |
| 227 | 225 | |
| 228 | 226 | // Continue only if migration is in validity range |
| ... | ... | @@ -478,7 +476,7 @@ void GCTAEdisp2D::save(const GFilename& filename, const bool& clobber) const |
| 478 | 476 | * No energy dispersion information found for parameters or |
| 479 | 477 | * energy dispersion matrix is invalid. |
| 480 | 478 | * |
| 481 | - * Draws observed energy value given a true energy @p logEsrc and offset | |
| 479 | + * Draws observed energy value given a true energy @p etrue and offset | |
| 482 | 480 | * angle @p theta. If no energy dispersion information is available the |
| 483 | 481 | * method will return the true photon energy. |
| 484 | 482 | ***************************************************************************/ |
| ... | ... | @@ -492,20 +490,20 @@ GEnergy GCTAEdisp2D::mc(GRan& ran, |
| 492 | 490 | // Make sure that energy dispersion is online |
| 493 | 491 | fetch(); |
| 494 | 492 | |
| 495 | - // Initialise reconstructed photon energy | |
| 496 | - GEnergy ereco; | |
| 493 | + // Initialise reconstructed event energy with true photon energy | |
| 494 | + GEnergy ereco = etrue; | |
| 497 | 495 | |
| 498 | - // Get boundaries for observed energy | |
| 496 | + // Get boundaries for reconstructed energy | |
| 499 | 497 | GEbounds ebounds = ereco_bounds(etrue, theta, phi, zenith, azimuth); |
| 500 | - double emin = ebounds.emin().log10TeV(); | |
| 501 | - double emax = ebounds.emax().log10TeV(); | |
| 498 | + double emin = ebounds.emin().TeV(); | |
| 499 | + double emax = ebounds.emax().TeV(); | |
| 502 | 500 | |
| 503 | 501 | // Throw an exception if minimum energy is equal or larger than maximum |
| 504 | 502 | // energy (this means that no energy dispersion information is available |
| 505 | 503 | // for that energy) |
| 506 | 504 | if (emin >= emax) { |
| 507 | 505 | std::string msg = "No valid energy dispersion information available " |
| 508 | - "for true photon energy "+etrue.print()+"," | |
| 506 | + "for true photon energy "+etrue.print()+", " | |
| 509 | 507 | "offset angle "+ |
| 510 | 508 | gammalib::str(theta*gammalib::rad2deg)+" deg " |
| 511 | 509 | "and azimuth angle "+ |
| ... | ... | @@ -515,8 +513,11 @@ GEnergy GCTAEdisp2D::mc(GRan& ran, |
| 515 | 513 | throw GException::invalid_return_value(G_MC, msg); |
| 516 | 514 | } |
| 517 | 515 | |
| 516 | + // Get maximum energy dispersion value (including some margin) | |
| 517 | + double max_edisp = 1.5 * get_max_edisp(etrue, theta); | |
| 518 | + | |
| 518 | 519 | // Throw an exception if maximum energy dispersion is zero |
| 519 | - if (m_max_edisp <= 0.0) { | |
| 520 | + if (max_edisp <= 0.0) { | |
| 520 | 521 | std::string msg = "Energy dispersion matrix is empty. Please provide " |
| 521 | 522 | "a valid energy dispersion matrix."; |
| 522 | 523 | throw GException::invalid_return_value(G_MC, msg); |
| ... | ... | @@ -524,7 +525,6 @@ GEnergy GCTAEdisp2D::mc(GRan& ran, |
| 524 | 525 | |
| 525 | 526 | // Initialise rejection method |
| 526 | 527 | double ewidth = emax - emin; |
| 527 | - double logEobs = etrue.log10TeV(); | |
| 528 | 528 | double f = 0.0; |
| 529 | 529 | double ftest = 1.0; |
| 530 | 530 | int zeros = 0; |
| ... | ... | @@ -540,14 +540,13 @@ GEnergy GCTAEdisp2D::mc(GRan& ran, |
| 540 | 540 | // subsequent zeros the loop is terminated with an exception |
| 541 | 541 | int zeros = 0; |
| 542 | 542 | do { |
| 543 | - logEobs = emin + ewidth * ran.uniform(); | |
| 544 | - ereco.log10TeV(logEobs); | |
| 545 | - f = operator()(ereco, etrue, theta, phi, zenith, azimuth); | |
| 543 | + ereco.TeV(emin + ewidth * ran.uniform()); | |
| 544 | + f = operator()(ereco, etrue, theta, phi, zenith, azimuth); | |
| 546 | 545 | if (f == 0.0) { |
| 547 | 546 | zeros++; |
| 548 | 547 | if (zeros > max_subsequent_zeros) { |
| 549 | 548 | std::string msg = "No valid energy dispersion information " |
| 550 | - "available for true photon energy "+ | |
| 549 | + "available for true photon energy "+ | |
| 551 | 550 | etrue.print()+", offset angle "+ |
| 552 | 551 | gammalib::str(theta*gammalib::rad2deg)+ |
| 553 | 552 | " deg and azimuth angle "+ |
| ... | ... | @@ -564,7 +563,7 @@ GEnergy GCTAEdisp2D::mc(GRan& ran, |
| 564 | 563 | // Get uniform random value between zero and the maximum energy |
| 565 | 564 | // dispersion value. The loop is quite if the random number is smaller |
| 566 | 565 | // than the energy dispersion value |
| 567 | - ftest = ran.uniform() * m_max_edisp; | |
| 566 | + ftest = ran.uniform() * max_edisp; | |
| 568 | 567 | |
| 569 | 568 | } // endwhile: |
| 570 | 569 | |
| ... | ... | @@ -584,7 +583,7 @@ GEnergy GCTAEdisp2D::mc(GRan& ran, |
| 584 | 583 | * @return Observed energy boundaries. |
| 585 | 584 | * |
| 586 | 585 | * Returns the band of observed energies outside of which the energy |
| 587 | - * dispersion becomes negligible for a given true energy @p logEsrc and | |
| 586 | + * dispersion becomes negligible for a given true energy @p etrue and | |
| 588 | 587 | * offset angle @p theta. |
| 589 | 588 | ***************************************************************************/ |
| 590 | 589 | GEbounds GCTAEdisp2D::ereco_bounds(const GEnergy& etrue, |
| ... | ... | @@ -597,15 +596,15 @@ GEbounds GCTAEdisp2D::ereco_bounds(const GEnergy& etrue, |
| 597 | 596 | fetch(); |
| 598 | 597 | |
| 599 | 598 | // Compute only if parameters changed |
| 600 | - if (!m_ebounds_obs_computed || theta != m_last_theta_obs) { | |
| 599 | + if (!m_ereco_bounds_computed || theta != m_last_theta_ereco) { | |
| 601 | 600 | |
| 602 | 601 | // Set computation flag |
| 603 | - m_ebounds_obs_computed = true; | |
| 604 | - m_last_theta_obs = theta; | |
| 605 | - m_last_etrue.log10TeV(-30.0); // force update | |
| 602 | + m_ereco_bounds_computed = true; | |
| 603 | + m_last_theta_ereco = theta; | |
| 604 | + m_last_etrue.TeV(0.0); // force update | |
| 606 | 605 | |
| 607 | - // Compute ebounds_obs | |
| 608 | - compute_ebounds_obs(theta, phi, zenith, azimuth); | |
| 606 | + // Compute reconstructed energy bounds | |
| 607 | + compute_ereco_bounds(theta, phi, zenith, azimuth); | |
| 609 | 608 | |
| 610 | 609 | } |
| 611 | 610 | |
| ... | ... | @@ -620,7 +619,7 @@ GEbounds GCTAEdisp2D::ereco_bounds(const GEnergy& etrue, |
| 620 | 619 | |
| 621 | 620 | // Find right index with bisection |
| 622 | 621 | int low = 0; |
| 623 | - int high = m_ebounds_obs.size() - 1; | |
| 622 | + int high = m_ereco_bounds.size() - 1; | |
| 624 | 623 | while ((high-low) > 1) { |
| 625 | 624 | int mid = (low+high) / 2; |
| 626 | 625 | double e = m_edisp.axis_lo(m_inx_etrue, mid); |
| ... | ... | @@ -633,12 +632,12 @@ GEbounds GCTAEdisp2D::ereco_bounds(const GEnergy& etrue, |
| 633 | 632 | } |
| 634 | 633 | |
| 635 | 634 | // Index found |
| 636 | - m_index_obs = low; | |
| 635 | + m_index_ereco = low; | |
| 637 | 636 | |
| 638 | 637 | } |
| 639 | 638 | |
| 640 | 639 | // Return energy boundaries |
| 641 | - return m_ebounds_obs[m_index_obs]; | |
| 640 | + return m_ereco_bounds[m_index_ereco]; | |
| 642 | 641 | } |
| 643 | 642 | |
| 644 | 643 | |
| ... | ... | @@ -653,7 +652,7 @@ GEbounds GCTAEdisp2D::ereco_bounds(const GEnergy& etrue, |
| 653 | 652 | * @return True energy boundaries. |
| 654 | 653 | * |
| 655 | 654 | * Returns the band of true photon energies outside of which the energy |
| 656 | - * dispersion becomes negligible for a given observed energy @p logEobs and | |
| 655 | + * dispersion becomes negligible for a given observed energy @p ereco and | |
| 657 | 656 | * offset angle @p theta. |
| 658 | 657 | ***************************************************************************/ |
| 659 | 658 | GEbounds GCTAEdisp2D::etrue_bounds(const GEnergy& ereco, |
| ... | ... | @@ -666,15 +665,15 @@ GEbounds GCTAEdisp2D::etrue_bounds(const GEnergy& ereco, |
| 666 | 665 | fetch(); |
| 667 | 666 | |
| 668 | 667 | // Compute true energy boundaries only if parameters changed |
| 669 | - if (!m_ebounds_src_computed || theta != m_last_theta_src) { | |
| 668 | + if (!m_etrue_bounds_computed || theta != m_last_theta_etrue) { | |
| 670 | 669 | |
| 671 | 670 | // Set computation flag |
| 672 | - m_ebounds_src_computed = true; | |
| 673 | - m_last_theta_src = theta; | |
| 674 | - m_last_ereco.log10TeV(-30.0); // force update | |
| 671 | + m_etrue_bounds_computed = true; | |
| 672 | + m_last_theta_etrue = theta; | |
| 673 | + m_last_ereco.TeV(0.0); // force update | |
| 675 | 674 | |
| 676 | - // Compute ebounds_src | |
| 677 | - compute_ebounds_src(theta, phi, zenith, azimuth); | |
| 675 | + // Compute true energy boundaries | |
| 676 | + compute_etrue_bounds(theta, phi, zenith, azimuth); | |
| 678 | 677 | |
| 679 | 678 | } |
| 680 | 679 | |
| ... | ... | @@ -689,7 +688,7 @@ GEbounds GCTAEdisp2D::etrue_bounds(const GEnergy& ereco, |
| 689 | 688 | |
| 690 | 689 | // Find right index with bisection |
| 691 | 690 | int low = 0; |
| 692 | - int high = m_ebounds_src.size() - 1; | |
| 691 | + int high = m_etrue_bounds.size() - 1; | |
| 693 | 692 | while ((high-low) > 1) { |
| 694 | 693 | int mid = (low+high) / 2; |
| 695 | 694 | double e = m_edisp.axis_lo(m_inx_etrue, mid); |
| ... | ... | @@ -702,12 +701,12 @@ GEbounds GCTAEdisp2D::etrue_bounds(const GEnergy& ereco, |
| 702 | 701 | } |
| 703 | 702 | |
| 704 | 703 | // Index found |
| 705 | - m_index_src = low; | |
| 704 | + m_index_etrue = low; | |
| 706 | 705 | |
| 707 | 706 | } |
| 708 | 707 | |
| 709 | 708 | // Return energy boundaries |
| 710 | - return m_ebounds_src[m_index_src]; | |
| 709 | + return m_etrue_bounds[m_index_etrue]; | |
| 711 | 710 | } |
| 712 | 711 | |
| 713 | 712 | |
| ... | ... | @@ -821,7 +820,7 @@ void GCTAEdisp2D::fetch(void) const |
| 821 | 820 | * |
| 822 | 821 | * \f[ |
| 823 | 822 | * \int_{E_{\rm reco}^{\rm min}}^{E_{\rm reco}^{\rm max}} |
| 824 | - * E_{\rm disp}(E_{\rm true}, E_{\rm reco}, \theta) \, dE_{\rm reco} | |
| 823 | + * E_{\rm disp}(E_{\rm reco} | E_{\rm true}, \theta) \, dE_{\rm reco} | |
| 825 | 824 | * \f] |
| 826 | 825 | * |
| 827 | 826 | * where |
| ... | ... | @@ -1046,19 +1045,19 @@ void GCTAEdisp2D::init_members(void) |
| 1046 | 1045 | m_migra_max = 0.0; |
| 1047 | 1046 | m_theta_min = 0.0; |
| 1048 | 1047 | m_theta_max = 0.0; |
| 1048 | + m_max_edisp.clear(); | |
| 1049 | 1049 | |
| 1050 | 1050 | // Initialise computation cache |
| 1051 | - m_ebounds_obs_computed = false; | |
| 1052 | - m_ebounds_src_computed = false; | |
| 1053 | - m_last_theta_obs = -1.0; | |
| 1054 | - m_last_theta_src = -1.0; | |
| 1055 | - m_last_etrue.log10TeV(-30.0); | |
| 1056 | - m_last_ereco.log10TeV(-30.0); | |
| 1057 | - m_index_obs = 0; | |
| 1058 | - m_index_src = 0; | |
| 1059 | - m_max_edisp = 0.0; | |
| 1060 | - m_ebounds_obs.clear(); | |
| 1061 | - m_ebounds_src.clear(); | |
| 1051 | + m_ereco_bounds_computed = false; | |
| 1052 | + m_etrue_bounds_computed = false; | |
| 1053 | + m_index_ereco = 0; | |
| 1054 | + m_index_etrue = 0; | |
| 1055 | + m_last_theta_ereco = -1.0; | |
| 1056 | + m_last_theta_etrue = -1.0; | |
| 1057 | + m_last_etrue.TeV(0.0); | |
| 1058 | + m_last_ereco.TeV(0.0); | |
| 1059 | + m_ereco_bounds.clear(); | |
| 1060 | + m_etrue_bounds.clear(); | |
| 1062 | 1061 | |
| 1063 | 1062 | // Return |
| 1064 | 1063 | return; |
| ... | ... | @@ -1086,19 +1085,19 @@ void GCTAEdisp2D::copy_members(const GCTAEdisp2D& edisp) |
| 1086 | 1085 | m_migra_max = edisp.m_migra_max; |
| 1087 | 1086 | m_theta_min = edisp.m_theta_min; |
| 1088 | 1087 | m_theta_max = edisp.m_theta_max; |
| 1088 | + m_max_edisp = edisp.m_max_edisp; | |
| 1089 | 1089 | |
| 1090 | 1090 | // Copy computation cache |
| 1091 | - m_ebounds_obs_computed = edisp.m_ebounds_obs_computed; | |
| 1092 | - m_ebounds_src_computed = edisp.m_ebounds_src_computed; | |
| 1093 | - m_last_theta_obs = edisp.m_last_theta_obs; | |
| 1094 | - m_last_theta_src = edisp.m_last_theta_src; | |
| 1095 | - m_last_etrue = edisp.m_last_etrue; | |
| 1096 | - m_last_ereco = edisp.m_last_ereco; | |
| 1097 | - m_index_obs = edisp.m_index_obs; | |
| 1098 | - m_index_src = edisp.m_index_src; | |
| 1099 | - m_max_edisp = edisp.m_max_edisp; | |
| 1100 | - m_ebounds_obs = edisp.m_ebounds_obs; | |
| 1101 | - m_ebounds_src = edisp.m_ebounds_src; | |
| 1091 | + m_ereco_bounds_computed = edisp.m_ereco_bounds_computed; | |
| 1092 | + m_etrue_bounds_computed = edisp.m_etrue_bounds_computed; | |
| 1093 | + m_index_ereco = edisp.m_index_ereco; | |
| 1094 | + m_index_etrue = edisp.m_index_etrue; | |
| 1095 | + m_last_theta_ereco = edisp.m_last_theta_ereco; | |
| 1096 | + m_last_theta_etrue = edisp.m_last_theta_etrue; | |
| 1097 | + m_last_etrue = edisp.m_last_etrue; | |
| 1098 | + m_last_ereco = edisp.m_last_ereco; | |
| 1099 | + m_ereco_bounds = edisp.m_ereco_bounds; | |
| 1100 | + m_etrue_bounds = edisp.m_etrue_bounds; | |
| 1102 | 1101 | |
| 1103 | 1102 | // Return |
| 1104 | 1103 | return; |
| ... | ... | @@ -1116,89 +1115,141 @@ void GCTAEdisp2D::free_members(void) |
| 1116 | 1115 | |
| 1117 | 1116 | |
| 1118 | 1117 | /***********************************************************************//** |
| 1119 | - * @brief Compute ebounds_obs vector | |
| 1118 | + * @brief Get true energy | |
| 1119 | + * | |
| 1120 | + * @param[in] ietrue True energy index. | |
| 1121 | + * @return True energy. | |
| 1122 | + ***************************************************************************/ | |
| 1123 | +GEnergy GCTAEdisp2D::etrue(const int& ietrue) const | |
| 1124 | +{ | |
| 1125 | + // Compute true energy in TeV | |
| 1126 | + double etrue_TeV = std::sqrt(m_edisp.axis_lo(m_inx_etrue, ietrue) * | |
| 1127 | + m_edisp.axis_hi(m_inx_etrue, ietrue)); | |
| 1128 | + | |
| 1129 | + // Set true energy | |
| 1130 | + GEnergy etrue; | |
| 1131 | + etrue.TeV(etrue_TeV); | |
| 1132 | + | |
| 1133 | + // Return true energy | |
| 1134 | + return etrue; | |
| 1135 | +} | |
| 1136 | + | |
| 1137 | + | |
| 1138 | +/***********************************************************************//** | |
| 1139 | + * @brief Get migration | |
| 1140 | + * | |
| 1141 | + * @param[in] imigra Migration index. | |
| 1142 | + * @return Migration. | |
| 1143 | + ***************************************************************************/ | |
| 1144 | +double GCTAEdisp2D::migra(const int& imigra) const | |
| 1145 | +{ | |
| 1146 | + // Compute migration | |
| 1147 | + double migra = 0.5 * (m_edisp.axis_hi(m_inx_migra, imigra) + | |
| 1148 | + m_edisp.axis_lo(m_inx_migra, imigra)); | |
| 1149 | + | |
| 1150 | + // Return migration | |
| 1151 | + return migra; | |
| 1152 | +} | |
| 1153 | + | |
| 1154 | + | |
| 1155 | +/***********************************************************************//** | |
| 1156 | + * @brief Get offset angle in radiaus | |
| 1157 | + * | |
| 1158 | + * @param[in] itheta Offset angle index. | |
| 1159 | + * @return Offset angle (radians). | |
| 1160 | + ***************************************************************************/ | |
| 1161 | +double GCTAEdisp2D::theta(const int& itheta) const | |
| 1162 | +{ | |
| 1163 | + // Compute offset angle in radians | |
| 1164 | + double theta = 0.5 * (m_edisp.axis_lo(m_inx_theta, itheta) + | |
| 1165 | + m_edisp.axis_hi(m_inx_theta, itheta)) * | |
| 1166 | + gammalib::deg2rad; | |
| 1167 | + | |
| 1168 | + // Return offset angle | |
| 1169 | + return theta; | |
| 1170 | +} | |
| 1171 | + | |
| 1172 | + | |
| 1173 | +/***********************************************************************//** | |
| 1174 | + * @brief Compute vector of reconstructed energy bounds | |
| 1120 | 1175 | * |
| 1121 | 1176 | * @param[in] theta Offset angle (radians). |
| 1122 | 1177 | * @param[in] phi Azimuth angle (radians). |
| 1123 | 1178 | * @param[in] zenith Zenith angle (radians). |
| 1124 | 1179 | * @param[in] azimuth Azimuth angle (radians). |
| 1125 | 1180 | * |
| 1126 | - * Computes for all true energies the energy boundaries of the observed | |
| 1181 | + * Computes for all true energies the energy boundaries of the reconstructed | |
| 1127 | 1182 | * energies covered by valid migration matrix elements. Only matrix elements |
| 1128 | 1183 | * with values >= 1.0e-12 are considered as valid elements. In case that no |
| 1129 | 1184 | * matrix elements are found of a given true energy, the interval of observed |
| 1130 | - * energies will be set to [1 TeV, 1 TeV] (i.e. an empty interval). | |
| 1185 | + * energies will be set to [0,0] (i.e. an empty interval). | |
| 1131 | 1186 | ***************************************************************************/ |
| 1132 | -void GCTAEdisp2D::compute_ebounds_obs(const double& theta, | |
| 1133 | - const double& phi, | |
| 1134 | - const double& zenith, | |
| 1135 | - const double& azimuth) const | |
| 1187 | +void GCTAEdisp2D::compute_ereco_bounds(const double& theta, | |
| 1188 | + const double& phi, | |
| 1189 | + const double& zenith, | |
| 1190 | + const double& azimuth) const | |
| 1136 | 1191 | { |
| 1137 | 1192 | // Clear ebounds_obs vector |
| 1138 | - m_ebounds_obs.clear(); | |
| 1193 | + m_ereco_bounds.clear(); | |
| 1139 | 1194 | |
| 1140 | 1195 | // Set epsilon |
| 1141 | 1196 | const double eps = 1.0e-12; |
| 1142 | 1197 | |
| 1143 | 1198 | // Determine number of true energy and migration bins |
| 1144 | - int n_bins = m_edisp.axis_bins(m_inx_etrue); | |
| 1145 | - int n_migra = m_edisp.axis_bins(m_inx_migra); | |
| 1199 | + int netrue = m_edisp.axis_bins(m_inx_etrue); | |
| 1200 | + int nmigra = m_edisp.axis_bins(m_inx_migra); | |
| 1146 | 1201 | |
| 1147 | - // Loop over Esrc | |
| 1148 | - for (int isrc = 0; isrc < n_bins; ++isrc) { | |
| 1202 | + // Loop over true photon energy | |
| 1203 | + for (int ietrue = 0; ietrue < netrue; ++ietrue) { | |
| 1149 | 1204 | |
| 1150 | - // Set Esrc | |
| 1151 | - double Esrc = std::sqrt(m_edisp.axis_hi(m_inx_etrue,isrc) * | |
| 1152 | - m_edisp.axis_lo(m_inx_etrue,isrc)); | |
| 1153 | - double logEsrc = std::log10(Esrc); | |
| 1205 | + // Compute true photon energy | |
| 1206 | + GEnergy etrue = this->etrue(ietrue); | |
| 1154 | 1207 | |
| 1155 | 1208 | // Initialise results |
| 1156 | - double logEobsMin = 0.0; | |
| 1157 | - double logEobsMax = 0.0; | |
| 1158 | - bool minFound = false; | |
| 1159 | - bool maxFound = false; | |
| 1209 | + double ereco_min = 0.0; | |
| 1210 | + double ereco_max = 0.0; | |
| 1211 | + bool found_min = false; | |
| 1212 | + bool found_max = false; | |
| 1160 | 1213 | |
| 1161 | 1214 | // Find minimum boundary |
| 1162 | - for (int i = 0; i < n_migra; ++i) { | |
| 1215 | + for (int imigra = 0; imigra < nmigra; ++imigra) { | |
| 1163 | 1216 | |
| 1164 | - // Compute EobsOverEsrc value | |
| 1165 | - double EobsOverEsrc = 0.5 * (m_edisp.axis_hi(m_inx_migra,i) + | |
| 1166 | - m_edisp.axis_lo(m_inx_migra,i)); | |
| 1217 | + // Compute migra value | |
| 1218 | + double migra = this->migra(imigra); | |
| 1167 | 1219 | |
| 1168 | 1220 | // Get matrix term |
| 1169 | 1221 | double arg[3]; |
| 1170 | - arg[m_inx_etrue] = logEsrc; | |
| 1171 | - arg[m_inx_migra] = EobsOverEsrc; | |
| 1222 | + arg[m_inx_etrue] = etrue.log10TeV(); | |
| 1223 | + arg[m_inx_migra] = migra; | |
| 1172 | 1224 | arg[m_inx_theta] = theta; |
| 1173 | 1225 | double edisp = m_edisp(m_inx_matrix, arg[0], arg[1], arg[2]); |
| 1174 | 1226 | |
| 1175 | 1227 | // Find first non-negligible matrix term |
| 1176 | 1228 | if (edisp >= eps) { |
| 1177 | - minFound = true; | |
| 1178 | - logEobsMin = std::log10(EobsOverEsrc * Esrc); | |
| 1229 | + found_min = true; | |
| 1230 | + ereco_min = migra * etrue.TeV(); | |
| 1179 | 1231 | break; |
| 1180 | 1232 | } |
| 1181 | 1233 | |
| 1182 | 1234 | } // endfor: find minimum boundary |
| 1183 | 1235 | |
| 1184 | 1236 | // Find maximum boundary |
| 1185 | - for (int i = n_migra-1; i >= 0; i--) { | |
| 1237 | + for (int imigra = nmigra-1; imigra >= 0; imigra--) { | |
| 1186 | 1238 | |
| 1187 | - // Compute EobsOverEsrc value | |
| 1188 | - double EobsOverEsrc = 0.5 * (m_edisp.axis_hi(m_inx_migra,i) + | |
| 1189 | - m_edisp.axis_lo(m_inx_migra,i)); | |
| 1239 | + // Compute migra value | |
| 1240 | + double migra = this->migra(imigra); | |
| 1190 | 1241 | |
| 1191 | 1242 | // Get matrix term |
| 1192 | 1243 | double arg[3]; |
| 1193 | - arg[m_inx_etrue] = logEsrc; | |
| 1194 | - arg[m_inx_migra] = EobsOverEsrc; | |
| 1244 | + arg[m_inx_etrue] = etrue.log10TeV(); | |
| 1245 | + arg[m_inx_migra] = migra; | |
| 1195 | 1246 | arg[m_inx_theta] = theta; |
| 1196 | 1247 | double edisp = m_edisp(m_inx_matrix, arg[0], arg[1], arg[2]); |
| 1197 | 1248 | |
| 1198 | 1249 | // Find first non-negligible matrix term |
| 1199 | 1250 | if (edisp >= eps) { |
| 1200 | - maxFound = true; | |
| 1201 | - logEobsMax = std::log10(EobsOverEsrc * Esrc); | |
| 1251 | + found_max = true; | |
| 1252 | + ereco_max = migra * etrue.TeV(); | |
| 1202 | 1253 | break; |
| 1203 | 1254 | } |
| 1204 | 1255 | |
| ... | ... | @@ -1206,19 +1257,19 @@ void GCTAEdisp2D::compute_ebounds_obs(const double& theta, |
| 1206 | 1257 | |
| 1207 | 1258 | // If we did not find boundaries then set the interval to |
| 1208 | 1259 | // a zero interval for safety |
| 1209 | - if (!minFound || !maxFound) { | |
| 1210 | - logEobsMin = 0.0; | |
| 1211 | - logEobsMax = 0.0; | |
| 1260 | + if (!found_min || !found_max) { | |
| 1261 | + ereco_min = 0.0; | |
| 1262 | + ereco_max = 0.0; | |
| 1212 | 1263 | } |
| 1213 | 1264 | |
| 1214 | 1265 | // Set energy boundaries |
| 1215 | 1266 | GEnergy emin; |
| 1216 | 1267 | GEnergy emax; |
| 1217 | - emin.log10TeV(logEobsMin); | |
| 1218 | - emax.log10TeV(logEobsMax); | |
| 1268 | + emin.TeV(ereco_min); | |
| 1269 | + emax.TeV(ereco_max); | |
| 1219 | 1270 | |
| 1220 | 1271 | // Add energy boundaries to vector |
| 1221 | - m_ebounds_obs.push_back(GEbounds(emin, emax)); | |
| 1272 | + m_ereco_bounds.push_back(GEbounds(emin, emax)); | |
| 1222 | 1273 | |
| 1223 | 1274 | } // endfor: looped over logEsrc |
| 1224 | 1275 | |
| ... | ... | @@ -1228,7 +1279,7 @@ void GCTAEdisp2D::compute_ebounds_obs(const double& theta, |
| 1228 | 1279 | |
| 1229 | 1280 | |
| 1230 | 1281 | /***********************************************************************//** |
| 1231 | - * @brief Compute ebounds_src vector | |
| 1282 | + * @brief Compute vector of true energy boundaries | |
| 1232 | 1283 | * |
| 1233 | 1284 | * @param[in] theta Offset angle (radians). |
| 1234 | 1285 | * @param[in] phi Azimuth angle (radians). |
| ... | ... | @@ -1239,62 +1290,59 @@ void GCTAEdisp2D::compute_ebounds_obs(const double& theta, |
| 1239 | 1290 | * energies covered by valid migration matrix elements. Only matrix elements |
| 1240 | 1291 | * with values >= 1.0e-12 are considered as valid elements. In case that no |
| 1241 | 1292 | * matrix elements are found of a given observed energy, the interval of true |
| 1242 | - * energies will be set to [1 TeV, 1 TeV] (i.e. an empty interval). | |
| 1293 | + * energies will be set to [0,0] (i.e. an empty interval). | |
| 1243 | 1294 | ***************************************************************************/ |
| 1244 | -void GCTAEdisp2D::compute_ebounds_src(const double& theta, | |
| 1245 | - const double& phi, | |
| 1246 | - const double& zenith, | |
| 1247 | - const double& azimuth) const | |
| 1295 | +void GCTAEdisp2D::compute_etrue_bounds(const double& theta, | |
| 1296 | + const double& phi, | |
| 1297 | + const double& zenith, | |
| 1298 | + const double& azimuth) const | |
| 1248 | 1299 | { |
| 1249 | 1300 | // Initialise energies |
| 1250 | 1301 | GEnergy etrue; |
| 1251 | 1302 | GEnergy ereco; |
| 1252 | 1303 | |
| 1253 | 1304 | // Clear ebounds_src vector |
| 1254 | - m_ebounds_src.clear(); | |
| 1305 | + m_etrue_bounds.clear(); | |
| 1255 | 1306 | |
| 1256 | 1307 | // Set epsilon |
| 1257 | 1308 | const double eps = 1.0e-12; |
| 1258 | 1309 | |
| 1259 | 1310 | // Determine number of true energy bins |
| 1260 | - int n_bins = m_edisp.axis_bins(m_inx_etrue); | |
| 1261 | - int n_migras = m_edisp.axis_bins(m_inx_migra); | |
| 1311 | + int nereco = m_edisp.axis_bins(m_inx_etrue); | |
| 1312 | + int nmigras = m_edisp.axis_bins(m_inx_migra); | |
| 1262 | 1313 | |
| 1263 | - // Loop over Eobs | |
| 1264 | - for (int iobs = 0; iobs < n_bins; ++iobs) { | |
| 1314 | + // Loop over reconstructed energy | |
| 1315 | + for (int iereco = 0; iereco < nereco; ++iereco) { | |
| 1265 | 1316 | |
| 1266 | - // Set Eobs | |
| 1267 | - double Eobs = std::sqrt(m_edisp.axis_hi(m_inx_etrue,iobs) * | |
| 1268 | - m_edisp.axis_lo(m_inx_etrue,iobs)); | |
| 1269 | - ereco.TeV(Eobs); | |
| 1317 | + // Set ereco (we use the true energy axis for the computation) | |
| 1318 | + GEnergy ereco = this->etrue(iereco); | |
| 1270 | 1319 | |
| 1271 | 1320 | // Initialise results |
| 1272 | 1321 | GEnergy etrue_min; |
| 1273 | 1322 | GEnergy etrue_max; |
| 1274 | - bool minFound = false; | |
| 1275 | - bool maxFound = false; | |
| 1323 | + bool found_min = false; | |
| 1324 | + bool found_max = false; | |
| 1276 | 1325 | |
| 1277 | 1326 | // Find minimum boundary |
| 1278 | - for (int imigra = 0; imigra < n_migras; ++imigra) { | |
| 1327 | + for (int imigra = 0; imigra < nmigras; ++imigra) { | |
| 1279 | 1328 | |
| 1280 | - // Get mean migra | |
| 1281 | - double migra = 0.5 * (m_edisp.axis_lo(m_inx_migra,imigra) + | |
| 1282 | - m_edisp.axis_lo(m_inx_migra,imigra)); | |
| 1329 | + // Compute migra value | |
| 1330 | + double migra = this->migra(imigra); | |
| 1283 | 1331 | |
| 1284 | 1332 | // Fall through if migration is zero |
| 1285 | 1333 | if (migra == 0.0) { |
| 1286 | 1334 | continue; |
| 1287 | 1335 | } |
| 1288 | 1336 | |
| 1289 | - // Compute log10 of true energy | |
| 1337 | + // Compute true energy | |
| 1290 | 1338 | etrue = ereco / migra; |
| 1291 | 1339 | |
| 1292 | - // Get matrix term | |
| 1340 | + // Get energy dispersion | |
| 1293 | 1341 | double edisp = operator()(ereco, etrue, theta); |
| 1294 | 1342 | |
| 1295 | 1343 | // Find first non-negligible matrix term |
| 1296 | 1344 | if (edisp >= eps) { |
| 1297 | - maxFound = true; | |
| 1345 | + found_max = true; | |
| 1298 | 1346 | etrue_max = etrue; |
| 1299 | 1347 | break; |
| 1300 | 1348 | } |
| ... | ... | @@ -1302,41 +1350,40 @@ void GCTAEdisp2D::compute_ebounds_src(const double& theta, |
| 1302 | 1350 | } // endfor: find minimum boundary |
| 1303 | 1351 | |
| 1304 | 1352 | // Find maximum boundary |
| 1305 | - for (int imigra = n_migras-1; imigra >= 0; imigra--) { | |
| 1353 | + for (int imigra = nmigras-1; imigra >= 0; imigra--) { | |
| 1306 | 1354 | |
| 1307 | - // Get mean migra | |
| 1308 | - double migra = 0.5 * (m_edisp.axis_lo(m_inx_migra,imigra) + | |
| 1309 | - m_edisp.axis_lo(m_inx_migra,imigra)); | |
| 1355 | + // Compute migra value | |
| 1356 | + double migra = this->migra(imigra); | |
| 1310 | 1357 | |
| 1311 | 1358 | // Fall through if migration is zero |
| 1312 | 1359 | if (migra == 0.0) { |
| 1313 | 1360 | continue; |
| 1314 | 1361 | } |
| 1315 | 1362 | |
| 1316 | - // Compute log10 of true energy | |
| 1363 | + // Compute true energy | |
| 1317 | 1364 | etrue = ereco / migra; |
| 1318 | 1365 | |
| 1319 | - // Get matrix term | |
| 1366 | + // Get energy dispersion | |
| 1320 | 1367 | double edisp = operator()(ereco, etrue, theta); |
| 1321 | 1368 | |
| 1322 | 1369 | // Find first non-negligible matrix term |
| 1323 | 1370 | if (edisp >= eps) { |
| 1324 | - minFound = true; | |
| 1371 | + found_min = true; | |
| 1325 | 1372 | etrue_min = etrue; |
| 1326 | 1373 | break; |
| 1327 | 1374 | } |
| 1328 | 1375 | |
| 1329 | 1376 | } // endfor: find maximum boundary |
| 1330 | 1377 | |
| 1331 | - // If we did not find boundaries then set the interval to | |
| 1332 | - // a zero interval for safety | |
| 1333 | - if (!minFound || !maxFound) { | |
| 1378 | + // If we did not find boundaries then set the interval to a zero | |
| 1379 | + // interval for safety | |
| 1380 | + if (!found_min || !found_max) { | |
| 1334 | 1381 | etrue_min.clear(); |
| 1335 | 1382 | etrue_max.clear(); |
| 1336 | 1383 | } |
| 1337 | 1384 | |
| 1338 | 1385 | // Add energy boundaries to vector |
| 1339 | - m_ebounds_src.push_back(GEbounds(etrue_min, etrue_max)); | |
| 1386 | + m_etrue_bounds.push_back(GEbounds(etrue_min, etrue_max)); | |
| 1340 | 1387 | |
| 1341 | 1388 | } // endfor : loopend over observed energy |
| 1342 | 1389 | |
| ... | ... | @@ -1349,15 +1396,7 @@ void GCTAEdisp2D::compute_ebounds_src(const double& theta, |
| 1349 | 1396 | * @brief Set table |
| 1350 | 1397 | * |
| 1351 | 1398 | * After assigning or loading a response table, performs all necessary steps |
| 1352 | - * to set the table. Specifically, the method normalizes the energy | |
| 1353 | - * dispersion for each offset angle and true energy so that | |
| 1354 | - * | |
| 1355 | - * \f[ | |
| 1356 | - * \int_{m_{\rm min}}^{m_{\rm max}} | |
| 1357 | - * E_{\rm disp}(E_{\rm true}, m, \theta) \, dm = 1 | |
| 1358 | - * \f] | |
| 1359 | - * | |
| 1360 | - * where \f$m=E_{\rm reco}/E_{\rm true}\f$. | |
| 1399 | + * to set the table. | |
| 1361 | 1400 | * |
| 1362 | 1401 | * For CTA, the method applies a kludge that smoothes the energy dispersion |
| 1363 | 1402 | * table to get rid of numerical fluctuations. |
| ... | ... | @@ -1407,7 +1446,7 @@ void GCTAEdisp2D::set_table(void) |
| 1407 | 1446 | // Normalize energy dispersion table |
| 1408 | 1447 | normalize_table(); |
| 1409 | 1448 | |
| 1410 | - // Set maximum energy dispersion value | |
| 1449 | + // Set maximum energy dispersion value array | |
| 1411 | 1450 | set_max_edisp(); |
| 1412 | 1451 | |
| 1413 | 1452 | // Save test |
| ... | ... | @@ -1452,20 +1491,50 @@ void GCTAEdisp2D::set_boundaries(void) |
| 1452 | 1491 | |
| 1453 | 1492 | |
| 1454 | 1493 | /***********************************************************************//** |
| 1455 | - * @brief Set maximum energy dispersion value | |
| 1494 | + * @brief Set array of maximum energy dispersion values | |
| 1495 | + * | |
| 1496 | + * Sets an internal array of maximum energy dispersion values for each given | |
| 1497 | + * true photon energy and offset angle in the response table. This array will | |
| 1498 | + * be used for Monte Carlo simulations. | |
| 1456 | 1499 | ***************************************************************************/ |
| 1457 | -void GCTAEdisp2D::set_max_edisp(void) const | |
| 1500 | +void GCTAEdisp2D::set_max_edisp(void) | |
| 1458 | 1501 | { |
| 1459 | - // Initialise maximum | |
| 1460 | - m_max_edisp = 0.0; | |
| 1461 | - | |
| 1462 | - // Loop over all response table elements | |
| 1463 | - for (int i = 0; i < m_edisp.elements(); ++i) { | |
| 1464 | - double value = m_edisp(m_inx_matrix,i); | |
| 1465 | - if (value > m_max_edisp) { | |
| 1466 | - m_max_edisp = value; | |
| 1467 | - } | |
| 1468 | - } | |
| 1502 | + // Get number of bins | |
| 1503 | + int netrue = m_edisp.axis_bins(m_inx_etrue); | |
| 1504 | + int nmigra = m_edisp.axis_bins(m_inx_migra); | |
| 1505 | + int ntheta = m_edisp.axis_bins(m_inx_theta); | |
| 1506 | + | |
| 1507 | + // Initialise maximum energy dispersion | |
| 1508 | + m_max_edisp.assign(netrue*ntheta, 0.0); | |
| 1509 | + | |
| 1510 | + // Loop over offset angle | |
| 1511 | + for (int itheta = 0, index = 0; itheta < ntheta; ++itheta) { | |
| 1512 | + | |
| 1513 | + // Loop over true photon energy | |
| 1514 | + for (int ietrue = 0; ietrue < netrue; ++ietrue, ++index) { | |
| 1515 | + | |
| 1516 | + // Get true photon energy | |
| 1517 | + GEnergy etrue = this->etrue(ietrue); | |
| 1518 | + | |
| 1519 | + // Get offset and stride | |
| 1520 | + int offset = table_index(ietrue, 0, itheta); | |
| 1521 | + int stride = table_stride(m_inx_migra); | |
| 1522 | + | |
| 1523 | + // Find maximum | |
| 1524 | + double maximum = 0.0; | |
| 1525 | + for (int imigra = 0, i = offset; imigra < nmigra; ++imigra, i += stride) { | |
| 1526 | + double value = m_edisp(m_inx_matrix, i); | |
| 1527 | + if (value > maximum) { | |
| 1528 | + maximum = value; | |
| 1529 | + } | |
| 1530 | + } | |
| 1531 | + | |
| 1532 | + // Set maximum | |
| 1533 | + m_max_edisp[index] = maximum / etrue.MeV(); | |
| 1534 | + | |
| 1535 | + } // endfor: looped over true photon energy | |
| 1536 | + | |
| 1537 | + } // endfor: looped over offset angle | |
| 1469 | 1538 | |
| 1470 | 1539 | // Return |
| 1471 | 1540 | return; |
| ... | ... | @@ -1473,13 +1542,76 @@ void GCTAEdisp2D::set_max_edisp(void) const |
| 1473 | 1542 | |
| 1474 | 1543 | |
| 1475 | 1544 | /***********************************************************************//** |
| 1545 | + * @brief Get maximum energy dispersion value | |
| 1546 | + * | |
| 1547 | + * @param[in] etrue True photon energy. | |
| 1548 | + * @param[in] theta Offset angle (radians). | |
| 1549 | + * @return Maximum energy dispersion value. | |
| 1550 | + * | |
| 1551 | + * For a given true energy @p etrue and offset angle @p theta, returns the | |
| 1552 | + * maximum energy dispersion value that may occur. This method makes use | |
| 1553 | + * of the internal array pre-computed by set_max_edisp(). | |
| 1554 | + * | |
| 1555 | + * If set_max_edisp() was not called, the method returns 0. | |
| 1556 | + ***************************************************************************/ | |
| 1557 | +double GCTAEdisp2D::get_max_edisp(const GEnergy& etrue, | |
| 1558 | + const double& theta) const | |
| 1559 | +{ | |
| 1560 | + // Initialise maximum energy dispersion | |
| 1561 | + double max_edisp = 0.0; | |
| 1562 | + | |
| 1563 | + // Continue only if maximum energy dispersion array is set | |
| 1564 | + if (m_max_edisp.size() > 0) { | |
| 1565 | + | |
| 1566 | + // Get number of true energy bins | |
| 1567 | + int netrue = m_edisp.axis_bins(m_inx_etrue); | |
| 1568 | + | |
| 1569 | + // Get references to axis nodes | |
| 1570 | + const GNodeArray& etrue_nodes = m_edisp.axis_nodes(m_inx_etrue); | |
| 1571 | + const GNodeArray& theta_nodes = m_edisp.axis_nodes(m_inx_theta); | |
| 1572 | + | |
| 1573 | + // Set values | |
| 1574 | + etrue_nodes.set_value(etrue.log10TeV()); | |
| 1575 | + theta_nodes.set_value(theta); | |
| 1576 | + | |
| 1577 | + // Get indices that bound the true energy and offset angle | |
| 1578 | + int inx_left = theta_nodes.inx_left() * netrue; | |
| 1579 | + int inx_right = theta_nodes.inx_right() * netrue; | |
| 1580 | + int index_1 = inx_left + etrue_nodes.inx_left(); | |
| 1581 | + int index_2 = inx_left + etrue_nodes.inx_right(); | |
| 1582 | + int index_3 = inx_right + etrue_nodes.inx_left(); | |
| 1583 | + int index_4 = inx_right + etrue_nodes.inx_right(); | |
| 1584 | + | |
| 1585 | + // Get maximum energy dispersion | |
| 1586 | + max_edisp = m_max_edisp[index_1]; | |
| 1587 | + double max_edisp_2 = m_max_edisp[index_2]; | |
| 1588 | + double max_edisp_3 = m_max_edisp[index_3]; | |
| 1589 | + double max_edisp_4 = m_max_edisp[index_4]; | |
| 1590 | + if (max_edisp_2 > max_edisp) { | |
| 1591 | + max_edisp = max_edisp_2; | |
| 1592 | + } | |
| 1593 | + if (max_edisp_3 > max_edisp) { | |
| 1594 | + max_edisp = max_edisp_3; | |
| 1595 | + } | |
| 1596 | + if (max_edisp_4 > max_edisp) { | |
| 1597 | + max_edisp = max_edisp_4; | |
| 1598 | + } | |
| 1599 | + | |
| 1600 | + } // endif: Maximum energy dispersion array was set | |
| 1601 | + | |
| 1602 | + // Return maximum energy dispersion | |
| 1603 | + return max_edisp; | |
| 1604 | +} | |
| 1605 | + | |
| 1606 | + | |
| 1607 | +/***********************************************************************//** | |
| 1476 | 1608 | * @brief Normalize energy dispersion table |
| 1477 | 1609 | * |
| 1478 | 1610 | * Normalize the energy dispersion table using |
| 1479 | 1611 | * |
| 1480 | 1612 | * \f[ |
| 1481 | 1613 | * \int_{E_{\rm reco}^{\rm min}}^{E_{\rm reco}^{\rm max}} |
| 1482 | - * E_{\rm disp}(E_{\rm true}, E_{\rm reco}, \theta) \, dE_{\rm reco} = 1 | |
| 1614 | + * E_{\rm disp}(E_{\rm reco} | E_{\rm true}, \theta) \, dE_{\rm reco} = 1 | |
| 1483 | 1615 | * \f] |
| 1484 | 1616 | * |
| 1485 | 1617 | * where |
| ... | ... | @@ -1487,8 +1619,7 @@ void GCTAEdisp2D::set_max_edisp(void) const |
| 1487 | 1619 | * minimum and maximum migration value for a given true energy as returned |
| 1488 | 1620 | * by the ebounds_obs() method, and |
| 1489 | 1621 | * \f$E_{\rm disp}(E_{\rm true}, E_{\rm reco}, \theta)\f$ is the energy |
| 1490 | - * dispersion returned by the GCTAEdisp2D::operator(), given in units of | |
| 1491 | - * \f$MeV^{-1}\f$. | |
| 1622 | + * dispersion returned by the operator(), given in units of \f$MeV^{-1}\f$. | |
| 1492 | 1623 | * |
| 1493 | 1624 | * The normalisation is performed for each \f$E_{\rm true}\f$ and |
| 1494 | 1625 | * \f$\theta\f$ bin using a Romberg integration method. Since the |
| ... | ... | @@ -1518,21 +1649,13 @@ void GCTAEdisp2D::normalize_table(void) |
| 1518 | 1649 | for (int i_theta = 0; i_theta < theta_size; ++i_theta) { |
| 1519 | 1650 | |
| 1520 | 1651 | // Get offset angle (in radians) |
| 1521 | - double theta = 0.5 * (m_edisp.axis_lo(m_inx_theta,i_theta) + | |
| 1522 | - m_edisp.axis_hi(m_inx_theta,i_theta)) * | |
| 1523 | - gammalib::deg2rad; | |
| 1652 | + double theta = this->theta(i_theta); | |
| 1524 | 1653 | |
| 1525 | 1654 | // Loop over true photon energy |
| 1526 | 1655 | for (int i_etrue = 0; i_etrue < etrue_size; ++i_etrue) { |
| 1527 | 1656 | |
| 1528 | - // Get the base 10 logarithm of the true energy | |
| 1529 | - double emin = std::log10(m_edisp.axis_lo(m_inx_etrue,i_etrue)); | |
| 1530 | - double emax = std::log10(m_edisp.axis_hi(m_inx_etrue,i_etrue)); | |
| 1531 | - double logEsrc = 0.5*(emin+emax); | |
| 1532 | - | |
| 1533 | - // Set true energy | |
| 1534 | - GEnergy etrue; | |
| 1535 | - etrue.log10TeV(logEsrc); | |
| 1657 | + // Get true photon energy | |
| 1658 | + GEnergy etrue = this->etrue(i_etrue); | |
| 1536 | 1659 | |
| 1537 | 1660 | // Initialise integration |
| 1538 | 1661 | double sum = 0.0; |
| ... | ... | @@ -1544,12 +1667,12 @@ void GCTAEdisp2D::normalize_table(void) |
| 1544 | 1667 | for (int i = 0; i < ebounds.size(); ++i) { |
| 1545 | 1668 | |
| 1546 | 1669 | // Get energy boundaries in log10 of energy in MeV |
| 1547 | - emin = ebounds.emin(i).log10MeV(); | |
| 1548 | - emax = ebounds.emax(i).log10MeV(); | |
| 1670 | + double emin = ebounds.emin(i).log10MeV(); | |
| 1671 | + double emax = ebounds.emax(i).log10MeV(); | |
| 1549 | 1672 | |
| 1550 | 1673 | // Setup integration function |
| 1551 | - edisp_kern integrand(this, etrue, theta); | |
| 1552 | - GIntegral integral(&integrand); | |
| 1674 | + edisp_ereco_kern integrand(this, etrue, theta); | |
| 1675 | + GIntegral integral(&integrand); | |
| 1553 | 1676 | |
| 1554 | 1677 | // Set integration precision |
| 1555 | 1678 | integral.eps(1.0e-6); |
| ... | ... | @@ -2108,37 +2231,37 @@ GNdarray GCTAEdisp2D::gaussian_array(const double& mean, |
| 2108 | 2231 | |
| 2109 | 2232 | |
| 2110 | 2233 | /***********************************************************************//** |
| 2111 | - * @brief Integration kernel for GCTAEdisp2D::edisp_kern class | |
| 2234 | + * @brief Integration kernel for GCTAEdisp2D::edisp_ereco_kern class | |
| 2112 | 2235 | * |
| 2113 | - * @param[in] logEobs Log10 of observation energy (\f$\log_{10}\f$ MeV). | |
| 2236 | + * @param[in] log10Ereco Log10 of reconstructed energy (\f$\log_{10}\f$ MeV). | |
| 2114 | 2237 | * @return Energy dispersion (\f$(\log_{10}\f$ MeV\f$)^{-1}\f$). |
| 2115 | 2238 | * |
| 2116 | 2239 | * This method implements the function |
| 2117 | 2240 | * |
| 2118 | 2241 | * \f[ |
| 2119 | - * E_{\rm disp}(E_{\rm true}, \log_{10} E_{\rm reco}, \theta) = | |
| 2120 | - * E_{\rm disp}(E_{\rm true}, m, \theta) \times | |
| 2242 | + * E_{\rm disp}(\log_{10} E_{\rm reco} | E_{\rm true}, \theta) = | |
| 2243 | + * E_{\rm disp}(m | E_{\rm true}, \theta) \times | |
| 2121 | 2244 | * \frac{\log 10 \times E_{\rm reco}}{E_{\rm true}} |
| 2122 | 2245 | * \f] |
| 2123 | 2246 | * |
| 2124 | - * which is the integration kernel needed for the GCTAEdisp2D::edisp_kern | |
| 2125 | - * class. The class is used by GCTAEdisp2D::normalize_table to integrate the | |
| 2126 | - * energy dispersion information using | |
| 2247 | + * which is the integration kernel needed for the | |
| 2248 | + * GCTAEdisp2D::edisp_ereco_kern class. The class is used by | |
| 2249 | + * GCTAEdisp2D::normalize_table() to integrate the energy dispersion | |
| 2250 | + * information using | |
| 2127 | 2251 | * |
| 2128 | 2252 | * \f[ |
| 2129 | 2253 | * \int_{\log_{10} E_{\rm reco}^{\rm min}}^{\log_{10} E_{\rm reco}^{\rm max}} |
| 2130 | - * E_{\rm disp}(E_{\rm true}, \log_{10} E_{\rm reco}, \theta) \, | |
| 2254 | + * E_{\rm disp}(\log_{10} E_{\rm reco} | E_{\rm true}, \theta) \, | |
| 2131 | 2255 | * d(\log_{10} E_{\rm reco}) |
| 2132 | 2256 | * \f] |
| 2133 | 2257 | ***************************************************************************/ |
| 2134 | -double GCTAEdisp2D::edisp_kern::eval(const double& logEobs) | |
| 2258 | +double GCTAEdisp2D::edisp_ereco_kern::eval(const double& log10Ereco) | |
| 2135 | 2259 | { |
| 2136 | - // Set reconstructued energy | |
| 2260 | + // Set reconstructued event energy | |
| 2137 | 2261 | GEnergy ereco; |
| 2138 | - ereco.log10MeV(logEobs); | |
| 2262 | + ereco.log10MeV(log10Ereco); | |
| 2139 | 2263 | |
| 2140 | - // Get function value. Note that logEobs is in log10 MeV, while we need | |
| 2141 | - // the value in log10 TeV. | |
| 2264 | + // Get function value | |
| 2142 | 2265 | double value = m_parent->operator()(ereco, m_etrue, m_theta); |
| 2143 | 2266 | |
| 2144 | 2267 | // Normalize energy dispersion so that the units are per log10 MeV |
| ... | ... | @@ -2147,8 +2270,8 @@ double GCTAEdisp2D::edisp_kern::eval(const double& logEobs) |
| 2147 | 2270 | // Compile option: Check for NaN |
| 2148 | 2271 | #if defined(G_NAN_CHECK) |
| 2149 | 2272 | if (gammalib::is_notanumber(value) || gammalib::is_infinite(value)) { |
| 2150 | - std::cout << "*** ERROR: GCTAEdisp2D::edisp_kern::eval"; | |
| 2151 | - std::cout << "(logEobs=" << logEobs << "): "; | |
| 2273 | + std::cout << "*** ERROR: GCTAEdisp2D::edisp_ereco_kern::eval"; | |
| 2274 | + std::cout << "(log10Ereco=" << log10Ereco << "): "; | |
| 2152 | 2275 | std::cout << " NaN/Inf encountered"; |
| 2153 | 2276 | std::cout << " (value=" << value; |
| 2154 | 2277 | std::cout << ")" << std::endl; | ... | ... |
inst/cta/test/dev/test_sim_edisp.py
| ... | ... | @@ -31,7 +31,7 @@ import time |
| 31 | 31 | # ============== # |
| 32 | 32 | # Simulate Edisp # |
| 33 | 33 | # ============== # |
| 34 | -def sim_edisp(edisp, etrue, eobs_max=40.0, ebins=1000, nmc=100000): | |
| 34 | +def sim_edisp(edisp, etrue, theta=0.0, ebins=1000, nmc=1000000): | |
| 35 | 35 | """ |
| 36 | 36 | Simulate Edisp and show results using matplotlib (if available). |
| 37 | 37 | """ |
| ... | ... | @@ -40,20 +40,31 @@ def sim_edisp(edisp, etrue, eobs_max=40.0, ebins=1000, nmc=100000): |
| 40 | 40 | import matplotlib.pyplot as plt |
| 41 | 41 | has_matplotlib = True |
| 42 | 42 | except ImportError: |
| 43 | - print("Matplotlib is not (correctly) installed on your system.") | |
| 43 | + print('Matplotlib is not (correctly) installed on your system.') | |
| 44 | 44 | has_matplotlib = False |
| 45 | 45 | |
| 46 | 46 | # Continue only if matplotlib is available |
| 47 | 47 | if has_matplotlib: |
| 48 | 48 | |
| 49 | - # Set log10(etrue) | |
| 50 | - logEtrue = math.log10(etrue) | |
| 51 | - | |
| 52 | - # Create eobs axis | |
| 53 | - eobs_axis = [] | |
| 54 | - dEobs = eobs_max/ebins | |
| 49 | + # Create ereco axis | |
| 50 | + ereco_axis = [] | |
| 51 | + ereco_bounds = edisp.ereco_bounds(etrue, theta) | |
| 52 | + ereco_min = ereco_bounds.emin().TeV() | |
| 53 | + ereco_max = ereco_bounds.emax().TeV() | |
| 54 | + dE = (ereco_max-ereco_min)/ebins | |
| 55 | + for i in range(ebins): | |
| 56 | + ereco_axis.append(ereco_min+i*dE) | |
| 57 | + print('Ereco: %.3f - %.3f TeV' % (ereco_min, ereco_max)) | |
| 58 | + | |
| 59 | + # Create etrue axis | |
| 60 | + etrue_axis = [] | |
| 61 | + etrue_bounds = edisp.etrue_bounds(etrue, theta) | |
| 62 | + etrue_min = etrue_bounds.emin().TeV() | |
| 63 | + etrue_max = etrue_bounds.emax().TeV() | |
| 64 | + dEtrue = (etrue_max-etrue_min)/ebins | |
| 55 | 65 | for i in range(ebins): |
| 56 | - eobs_axis.append((i+0.5)*dEobs) | |
| 66 | + etrue_axis.append(etrue_min+i*dEtrue) | |
| 67 | + print('Etrue: %.3f - %.3f TeV' % (etrue_min, etrue_max)) | |
| 57 | 68 | |
| 58 | 69 | # Reset histogram |
| 59 | 70 | counts = [0.0 for i in range(ebins)] |
| ... | ... | @@ -64,87 +75,122 @@ def sim_edisp(edisp, etrue, eobs_max=40.0, ebins=1000, nmc=100000): |
| 64 | 75 | # Simulate observed energies |
| 65 | 76 | tstart = time.clock() |
| 66 | 77 | for i in range(nmc): |
| 67 | - eobs = edisp.mc(ran, logEtrue).TeV() | |
| 68 | - #eobs = edisp.mc(ran, logEtrue, float(i)).TeV() | |
| 69 | - index = int(eobs/dEobs) | |
| 78 | + eobs = edisp.mc(ran, etrue, theta).TeV() | |
| 79 | + index = int((eobs-ereco_min)/dE-0.5) | |
| 70 | 80 | if (index < ebins): |
| 71 | 81 | counts[index] += 1.0 |
| 72 | 82 | telapsed = (time.clock() - tstart) |
| 73 | - print("Elapsed CPU time (sec) %f" % telapsed) | |
| 83 | + print('Elapsed CPU time (sec) %f' % telapsed) | |
| 84 | + | |
| 85 | + # Dump probabilities | |
| 86 | + print('Probability integrated over Ereco: %f' % \ | |
| 87 | + edisp.prob_erecobin(ereco_bounds.emin(), ereco_bounds.emax(), etrue, theta)) | |
| 74 | 88 | |
| 75 | 89 | # Create error bars |
| 76 | 90 | error = [math.sqrt(c) for c in counts] |
| 77 | 91 | |
| 78 | 92 | # Get expected energy dispersion |
| 79 | - sum = 0.0 | |
| 80 | - exp_edisp = [] | |
| 81 | - cumul = [] | |
| 82 | - #fmax = 0.0 | |
| 93 | + edisp_ereco = [] | |
| 94 | + sum_ereco = 0.0 | |
| 83 | 95 | for i in range(ebins): |
| 84 | - eobs = eobs_axis[i] | |
| 85 | - if eobs <= edisp.ebounds_obs(logEtrue).emax().TeV(): | |
| 86 | - dLogEobs = math.log10(eobs_axis[i]) - math.log10(eobs_axis[i-1]) | |
| 87 | - value = edisp(math.log10(eobs), logEtrue) * dLogEobs * nmc | |
| 88 | - #if fmax < value / dLogEobs / nmc: | |
| 89 | - # fmax = value / dLogEobs / nmc | |
| 96 | + ereco = gammalib.GEnergy(ereco_axis[i], 'TeV') | |
| 97 | + if ereco.TeV() >= ereco_min and ereco.TeV() <= ereco_max: | |
| 98 | + value = edisp(ereco, etrue, theta) * dE * 1.0e6 | |
| 90 | 99 | else: |
| 91 | 100 | value = 0.0 |
| 92 | - sum += value | |
| 93 | - exp_edisp.append(value) | |
| 94 | - cumul.append(sum/nmc) | |
| 95 | - print(sum/nmc) | |
| 96 | - #print(fmax) | |
| 97 | - | |
| 98 | - # Create figure | |
| 99 | - fig, ax1 = plt.subplots() | |
| 100 | - | |
| 101 | - # Plot simulated data | |
| 102 | - ax1.plot(eobs_axis, counts, 'ro') | |
| 103 | - #ax1.errorbar(eobs_axis, counts, error, ecolor='g') | |
| 104 | - | |
| 105 | - # Plot energy dispersion | |
| 106 | - ax1.plot(eobs_axis, exp_edisp, 'b-') | |
| 107 | - | |
| 108 | - # Set axes | |
| 109 | - ax1.set_xlabel("Observed energy (TeV)") | |
| 110 | - ax1.set_ylabel("Number of counts") | |
| 101 | + sum_ereco += value | |
| 102 | + edisp_ereco.append(value) | |
| 103 | + print('Sum computed over Ereco: %f' % sum_ereco) | |
| 111 | 104 | |
| 112 | - #ax2 = ax1.twinx() | |
| 105 | + # Get energy dispersion versus etrue | |
| 106 | + edisp_etrue = [] | |
| 107 | + sum_etrue = 0.0 | |
| 108 | + for i in range(ebins): | |
| 109 | + ereco = gammalib.GEnergy(etrue_axis[i], 'TeV') | |
| 110 | + if ereco.TeV() >= etrue_min and ereco.TeV() <= etrue_max: | |
| 111 | + value = edisp(etrue, ereco, theta) * dEtrue * 1.0e6 | |
| 112 | + else: | |
| 113 | + value = 0.0 | |
| 114 | + sum_etrue += value | |
| 115 | + edisp_etrue.append(value) | |
| 116 | + print('Sum computed over Etrue: %f' % sum_etrue) | |
| 113 | 117 | |
| 114 | - # Plot cumulative dispersion | |
| 115 | - #ax2.plot(eobs_axis, cumul, 'm') | |
| 118 | + # Normalise expectation | |
| 119 | + if sum_ereco > 0.0: | |
| 120 | + norm = nmc/sum_ereco | |
| 121 | + for i in range(ebins): | |
| 122 | + edisp_ereco[i] *= norm | |
| 116 | 123 | |
| 117 | - # Set axis | |
| 118 | - #ax2.set_ylabel("Cumulative probability") | |
| 124 | + # Create figure | |
| 125 | + fig = plt.figure(figsize=(14,6)) | |
| 126 | + | |
| 127 | + # Add title | |
| 128 | + title = r'MC simulated energy dispersion (E = %s, theta = %.2f)' % \ | |
| 129 | + (str(etrue), theta*gammalib.rad2deg) | |
| 130 | + fig.suptitle(title, fontsize=16) | |
| 131 | + | |
| 132 | + # Plot energy dispersion versus ereco | |
| 133 | + ax1 = fig.add_subplot(121) | |
| 134 | + ax1.plot(ereco_axis, edisp_ereco, '-', linewidth=2) | |
| 135 | + ax1.step(ereco_axis, counts, 'r', where='mid', linewidth=1) | |
| 136 | + ymin, ymax = ax1.get_ylim() | |
| 137 | + ax1.plot([etrue.TeV(), etrue.TeV()], [ymin,ymax], 'g-', linewidth=2) | |
| 138 | + ax1.set_xlabel('Reconstructed energy (TeV)') | |
| 139 | + ax1.set_ylabel('Number of counts') | |
| 140 | + ax1.set_title(r'Energy dispersion versus E$_{\rm reco}$') | |
| 141 | + | |
| 142 | + # Plot energy dispersion versus etrue | |
| 143 | + ax2 = fig.add_subplot(122) | |
| 144 | + ax2.plot(etrue_axis, edisp_etrue, '-', linewidth=2) | |
| 145 | + ymin, ymax = ax2.get_ylim() | |
| 146 | + ax2.plot([etrue.TeV(), etrue.TeV()], [ymin,ymax], 'g-', linewidth=2) | |
| 147 | + ax2.set_xlabel('True energy (TeV)') | |
| 148 | + ax2.set_ylabel('Arbitrary unit') | |
| 149 | + ax2.set_title(r'Energy dispersion versus E$_{\rm true}$') | |
| 119 | 150 | |
| 120 | 151 | # Notify |
| 121 | - print("PLEASE CLOSE WINDOW TO CONTINUE ...") | |
| 152 | + print('PLEASE CLOSE WINDOW TO CONTINUE ...') | |
| 122 | 153 | |
| 123 | 154 | # Show plot |
| 124 | - plt.title("MC simulated Energy dispersion (true photon energy "+str(etrue)+" TeV)") | |
| 125 | 155 | plt.show() |
| 126 | 156 | |
| 127 | 157 | # Return |
| 128 | 158 | return |
| 129 | 159 | |
| 130 | 160 | |
| 131 | -#==========================# | |
| 161 | +# ======================== # | |
| 132 | 162 | # Main routine entry point # |
| 133 | -#==========================# | |
| 163 | +# ======================== # | |
| 134 | 164 | if __name__ == '__main__': |
| 135 | - """ | |
| 136 | - Simulate Energy dispersion. | |
| 137 | - """ | |
| 165 | + | |
| 138 | 166 | # Dump header |
| 139 | - print("") | |
| 140 | - print("******************************") | |
| 141 | - print("* Simulate Energy dispersion *") | |
| 142 | - print("******************************") | |
| 167 | + print('') | |
| 168 | + print('******************************') | |
| 169 | + print('* Simulate Energy dispersion *') | |
| 170 | + print('******************************') | |
| 143 | 171 | |
| 144 | 172 | # Load edisp |
| 145 | - #edisp = gammalib.GCTAEdispRmf("./caldb/dc1/rmf.fits") | |
| 146 | - #edisp = gammalib.GCTAEdispPerfTable("./caldb/cta_dummy_irf.dat") | |
| 147 | - edisp = gammalib.GCTAEdisp2D("../caldb/edisp_matrix.fits") | |
| 173 | + #edisp = gammalib.GCTAEdispRmf('./caldb/dc1/rmf.fits') | |
| 174 | + #edisp = gammalib.GCTAEdispPerfTable('./caldb/cta_dummy_irf.dat') | |
| 175 | + | |
| 176 | + # Simulate 2D energy dispersion matrix | |
| 177 | + #edisp = gammalib.GCTAEdisp2D('../caldb/edisp_matrix.fits') | |
| 178 | + #sim_edisp(edisp, gammalib.GEnergy(0.1, 'TeV')) | |
| 179 | + #sim_edisp(edisp, gammalib.GEnergy(1.0, 'TeV')) | |
| 180 | + #sim_edisp(edisp, gammalib.GEnergy(10.0, 'TeV')) | |
| 181 | + #sim_edisp(edisp, gammalib.GEnergy(1.0, 'TeV'), theta=5.0*gammalib.deg2rad) | |
| 182 | + | |
| 183 | + # Test H.E.S.S. energy dispersion | |
| 184 | + edisp = gammalib.GCTAEdisp2D('/project-data/hess/hess_dl3_dr1/data/hess_dl3_dr1_obs_id_023523.fits') | |
| 185 | + #sim_edisp(edisp, gammalib.GEnergy(0.2, 'TeV')) | |
| 186 | + #sim_edisp(edisp, gammalib.GEnergy(0.3, 'TeV')) | |
| 187 | + #sim_edisp(edisp, gammalib.GEnergy(0.4, 'TeV'), theta=2.0*gammalib.deg2rad) | |
| 188 | + #sim_edisp(edisp, gammalib.GEnergy(0.5, 'TeV'), theta=2.5*gammalib.deg2rad) | |
| 189 | + #sim_edisp(edisp, gammalib.GEnergy(0.7, 'TeV')) | |
| 190 | + #sim_edisp(edisp, gammalib.GEnergy(1.0, 'TeV')) | |
| 191 | + #sim_edisp(edisp, gammalib.GEnergy(0.7, 'TeV')) | |
| 192 | + sim_edisp(edisp, gammalib.GEnergy(5.0, 'TeV')) | |
| 193 | + #sim_edisp(edisp, gammalib.GEnergy(50.0, 'TeV')) | |
| 194 | + #sim_edisp(edisp, gammalib.GEnergy(1.0, 'TeV'), theta=2.5*gammalib.deg2rad) | |
| 195 | + | |
| 148 | 196 | |
| 149 | - # Simulate Edisp | |
| 150 | - sim_edisp(edisp, 20.0) # Energy in TeV | ... | ... |