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,45 +54,37 @@ namespace gammalib { | ||
| 54 | * | 54 | * |
| 55 | * This class implements the energy dispersion for the CTA 2D response. The | 55 | * This class implements the energy dispersion for the CTA 2D response. The |
| 56 | * CTA 2D energy dispersion is in fact a 3-dimensional function, where the | 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 | * \f[ | 59 | * \f[ |
| 62 | - * E_{\rm disp}(E_{\rm true}, m, \theta) | 60 | + * E_{\rm disp}(E_{\rm reco} | E_{\rm true}, \theta) |
| 63 | * \f] | 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 | * \f[ | 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 | * \f] | 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 | * \f[ | 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 | * \f] | 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 | * \f[ | 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 | * \f] | 88 | * \f] |
| 97 | ***************************************************************************/ | 89 | ***************************************************************************/ |
| 98 | class GCTAEdisp2D : public GCTAEdisp { | 90 | class GCTAEdisp2D : public GCTAEdisp { |
| @@ -152,46 +144,47 @@ public: | @@ -152,46 +144,47 @@ public: | ||
| 152 | 144 | ||
| 153 | private: | 145 | private: |
| 154 | // Methods | 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 | // Kludge | 189 | // Kludge |
| 197 | void smooth_table(void); | 190 | void smooth_table(void); |
| @@ -213,15 +206,15 @@ private: | @@ -213,15 +206,15 @@ private: | ||
| 213 | const double& total) const; | 206 | const double& total) const; |
| 214 | 207 | ||
| 215 | // Protected classes | 208 | // Protected classes |
| 216 | - class edisp_kern : public GFunction { | 209 | + class edisp_ereco_kern : public GFunction { |
| 217 | public: | 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 | protected: | 218 | protected: |
| 226 | const GCTAEdisp2D* m_parent; //!< Pointer to parent class | 219 | const GCTAEdisp2D* m_parent; //!< Pointer to parent class |
| 227 | GEnergy m_etrue; //!< True photon energy | 220 | GEnergy m_etrue; //!< True photon energy |
| @@ -229,32 +222,32 @@ private: | @@ -229,32 +222,32 @@ private: | ||
| 229 | }; | 222 | }; |
| 230 | 223 | ||
| 231 | // Members | 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 | // Computation cache | 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 | mutable GEnergy m_last_etrue; | 245 | mutable GEnergy m_last_etrue; |
| 252 | mutable GEnergy m_last_ereco; | 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,15 +191,14 @@ GCTAEdisp2D& GCTAEdisp2D::operator=(const GCTAEdisp2D& edisp) | ||
| 191 | * Returns the energy dispersion | 191 | * Returns the energy dispersion |
| 192 | * | 192 | * |
| 193 | * \f[ | 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 | * \f] | 196 | * \f] |
| 197 | * | 197 | * |
| 198 | * in units of MeV\f$^{-1}\f$ where | 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 | double GCTAEdisp2D::operator()(const GEnergy& ereco, | 203 | double GCTAEdisp2D::operator()(const GEnergy& ereco, |
| 205 | const GEnergy& etrue, | 204 | const GEnergy& etrue, |
| @@ -222,7 +221,6 @@ double GCTAEdisp2D::operator()(const GEnergy& ereco, | @@ -222,7 +221,6 @@ double GCTAEdisp2D::operator()(const GEnergy& ereco, | ||
| 222 | (theta >= m_theta_min) && (theta <= m_theta_max)) { | 221 | (theta >= m_theta_min) && (theta <= m_theta_max)) { |
| 223 | 222 | ||
| 224 | // Compute migration (migra=Ereco/Etrue | 223 | // Compute migration (migra=Ereco/Etrue |
| 225 | - //double EobsOverEsrc = std::exp((logEobs-logEsrc) * gammalib::ln10); | ||
| 226 | double migra = ereco.MeV() / etrue.MeV(); | 224 | double migra = ereco.MeV() / etrue.MeV(); |
| 227 | 225 | ||
| 228 | // Continue only if migration is in validity range | 226 | // Continue only if migration is in validity range |
| @@ -478,7 +476,7 @@ void GCTAEdisp2D::save(const GFilename& filename, const bool& clobber) const | @@ -478,7 +476,7 @@ void GCTAEdisp2D::save(const GFilename& filename, const bool& clobber) const | ||
| 478 | * No energy dispersion information found for parameters or | 476 | * No energy dispersion information found for parameters or |
| 479 | * energy dispersion matrix is invalid. | 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 | * angle @p theta. If no energy dispersion information is available the | 480 | * angle @p theta. If no energy dispersion information is available the |
| 483 | * method will return the true photon energy. | 481 | * method will return the true photon energy. |
| 484 | ***************************************************************************/ | 482 | ***************************************************************************/ |
| @@ -492,20 +490,20 @@ GEnergy GCTAEdisp2D::mc(GRan& ran, | @@ -492,20 +490,20 @@ GEnergy GCTAEdisp2D::mc(GRan& ran, | ||
| 492 | // Make sure that energy dispersion is online | 490 | // Make sure that energy dispersion is online |
| 493 | fetch(); | 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 | GEbounds ebounds = ereco_bounds(etrue, theta, phi, zenith, azimuth); | 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 | // Throw an exception if minimum energy is equal or larger than maximum | 501 | // Throw an exception if minimum energy is equal or larger than maximum |
| 504 | // energy (this means that no energy dispersion information is available | 502 | // energy (this means that no energy dispersion information is available |
| 505 | // for that energy) | 503 | // for that energy) |
| 506 | if (emin >= emax) { | 504 | if (emin >= emax) { |
| 507 | std::string msg = "No valid energy dispersion information available " | 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 | "offset angle "+ | 507 | "offset angle "+ |
| 510 | gammalib::str(theta*gammalib::rad2deg)+" deg " | 508 | gammalib::str(theta*gammalib::rad2deg)+" deg " |
| 511 | "and azimuth angle "+ | 509 | "and azimuth angle "+ |
| @@ -515,8 +513,11 @@ GEnergy GCTAEdisp2D::mc(GRan& ran, | @@ -515,8 +513,11 @@ GEnergy GCTAEdisp2D::mc(GRan& ran, | ||
| 515 | throw GException::invalid_return_value(G_MC, msg); | 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 | // Throw an exception if maximum energy dispersion is zero | 519 | // Throw an exception if maximum energy dispersion is zero |
| 519 | - if (m_max_edisp <= 0.0) { | 520 | + if (max_edisp <= 0.0) { |
| 520 | std::string msg = "Energy dispersion matrix is empty. Please provide " | 521 | std::string msg = "Energy dispersion matrix is empty. Please provide " |
| 521 | "a valid energy dispersion matrix."; | 522 | "a valid energy dispersion matrix."; |
| 522 | throw GException::invalid_return_value(G_MC, msg); | 523 | throw GException::invalid_return_value(G_MC, msg); |
| @@ -524,7 +525,6 @@ GEnergy GCTAEdisp2D::mc(GRan& ran, | @@ -524,7 +525,6 @@ GEnergy GCTAEdisp2D::mc(GRan& ran, | ||
| 524 | 525 | ||
| 525 | // Initialise rejection method | 526 | // Initialise rejection method |
| 526 | double ewidth = emax - emin; | 527 | double ewidth = emax - emin; |
| 527 | - double logEobs = etrue.log10TeV(); | ||
| 528 | double f = 0.0; | 528 | double f = 0.0; |
| 529 | double ftest = 1.0; | 529 | double ftest = 1.0; |
| 530 | int zeros = 0; | 530 | int zeros = 0; |
| @@ -540,14 +540,13 @@ GEnergy GCTAEdisp2D::mc(GRan& ran, | @@ -540,14 +540,13 @@ GEnergy GCTAEdisp2D::mc(GRan& ran, | ||
| 540 | // subsequent zeros the loop is terminated with an exception | 540 | // subsequent zeros the loop is terminated with an exception |
| 541 | int zeros = 0; | 541 | int zeros = 0; |
| 542 | do { | 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 | if (f == 0.0) { | 545 | if (f == 0.0) { |
| 547 | zeros++; | 546 | zeros++; |
| 548 | if (zeros > max_subsequent_zeros) { | 547 | if (zeros > max_subsequent_zeros) { |
| 549 | std::string msg = "No valid energy dispersion information " | 548 | std::string msg = "No valid energy dispersion information " |
| 550 | - "available for true photon energy "+ | 549 | + "available for true photon energy "+ |
| 551 | etrue.print()+", offset angle "+ | 550 | etrue.print()+", offset angle "+ |
| 552 | gammalib::str(theta*gammalib::rad2deg)+ | 551 | gammalib::str(theta*gammalib::rad2deg)+ |
| 553 | " deg and azimuth angle "+ | 552 | " deg and azimuth angle "+ |
| @@ -564,7 +563,7 @@ GEnergy GCTAEdisp2D::mc(GRan& ran, | @@ -564,7 +563,7 @@ GEnergy GCTAEdisp2D::mc(GRan& ran, | ||
| 564 | // Get uniform random value between zero and the maximum energy | 563 | // Get uniform random value between zero and the maximum energy |
| 565 | // dispersion value. The loop is quite if the random number is smaller | 564 | // dispersion value. The loop is quite if the random number is smaller |
| 566 | // than the energy dispersion value | 565 | // than the energy dispersion value |
| 567 | - ftest = ran.uniform() * m_max_edisp; | 566 | + ftest = ran.uniform() * max_edisp; |
| 568 | 567 | ||
| 569 | } // endwhile: | 568 | } // endwhile: |
| 570 | 569 | ||
| @@ -584,7 +583,7 @@ GEnergy GCTAEdisp2D::mc(GRan& ran, | @@ -584,7 +583,7 @@ GEnergy GCTAEdisp2D::mc(GRan& ran, | ||
| 584 | * @return Observed energy boundaries. | 583 | * @return Observed energy boundaries. |
| 585 | * | 584 | * |
| 586 | * Returns the band of observed energies outside of which the energy | 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 | * offset angle @p theta. | 587 | * offset angle @p theta. |
| 589 | ***************************************************************************/ | 588 | ***************************************************************************/ |
| 590 | GEbounds GCTAEdisp2D::ereco_bounds(const GEnergy& etrue, | 589 | GEbounds GCTAEdisp2D::ereco_bounds(const GEnergy& etrue, |
| @@ -597,15 +596,15 @@ GEbounds GCTAEdisp2D::ereco_bounds(const GEnergy& etrue, | @@ -597,15 +596,15 @@ GEbounds GCTAEdisp2D::ereco_bounds(const GEnergy& etrue, | ||
| 597 | fetch(); | 596 | fetch(); |
| 598 | 597 | ||
| 599 | // Compute only if parameters changed | 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 | // Set computation flag | 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,7 +619,7 @@ GEbounds GCTAEdisp2D::ereco_bounds(const GEnergy& etrue, | ||
| 620 | 619 | ||
| 621 | // Find right index with bisection | 620 | // Find right index with bisection |
| 622 | int low = 0; | 621 | int low = 0; |
| 623 | - int high = m_ebounds_obs.size() - 1; | 622 | + int high = m_ereco_bounds.size() - 1; |
| 624 | while ((high-low) > 1) { | 623 | while ((high-low) > 1) { |
| 625 | int mid = (low+high) / 2; | 624 | int mid = (low+high) / 2; |
| 626 | double e = m_edisp.axis_lo(m_inx_etrue, mid); | 625 | double e = m_edisp.axis_lo(m_inx_etrue, mid); |
| @@ -633,12 +632,12 @@ GEbounds GCTAEdisp2D::ereco_bounds(const GEnergy& etrue, | @@ -633,12 +632,12 @@ GEbounds GCTAEdisp2D::ereco_bounds(const GEnergy& etrue, | ||
| 633 | } | 632 | } |
| 634 | 633 | ||
| 635 | // Index found | 634 | // Index found |
| 636 | - m_index_obs = low; | 635 | + m_index_ereco = low; |
| 637 | 636 | ||
| 638 | } | 637 | } |
| 639 | 638 | ||
| 640 | // Return energy boundaries | 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,7 +652,7 @@ GEbounds GCTAEdisp2D::ereco_bounds(const GEnergy& etrue, | ||
| 653 | * @return True energy boundaries. | 652 | * @return True energy boundaries. |
| 654 | * | 653 | * |
| 655 | * Returns the band of true photon energies outside of which the energy | 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 | * offset angle @p theta. | 656 | * offset angle @p theta. |
| 658 | ***************************************************************************/ | 657 | ***************************************************************************/ |
| 659 | GEbounds GCTAEdisp2D::etrue_bounds(const GEnergy& ereco, | 658 | GEbounds GCTAEdisp2D::etrue_bounds(const GEnergy& ereco, |
| @@ -666,15 +665,15 @@ GEbounds GCTAEdisp2D::etrue_bounds(const GEnergy& ereco, | @@ -666,15 +665,15 @@ GEbounds GCTAEdisp2D::etrue_bounds(const GEnergy& ereco, | ||
| 666 | fetch(); | 665 | fetch(); |
| 667 | 666 | ||
| 668 | // Compute true energy boundaries only if parameters changed | 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 | // Set computation flag | 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,7 +688,7 @@ GEbounds GCTAEdisp2D::etrue_bounds(const GEnergy& ereco, | ||
| 689 | 688 | ||
| 690 | // Find right index with bisection | 689 | // Find right index with bisection |
| 691 | int low = 0; | 690 | int low = 0; |
| 692 | - int high = m_ebounds_src.size() - 1; | 691 | + int high = m_etrue_bounds.size() - 1; |
| 693 | while ((high-low) > 1) { | 692 | while ((high-low) > 1) { |
| 694 | int mid = (low+high) / 2; | 693 | int mid = (low+high) / 2; |
| 695 | double e = m_edisp.axis_lo(m_inx_etrue, mid); | 694 | double e = m_edisp.axis_lo(m_inx_etrue, mid); |
| @@ -702,12 +701,12 @@ GEbounds GCTAEdisp2D::etrue_bounds(const GEnergy& ereco, | @@ -702,12 +701,12 @@ GEbounds GCTAEdisp2D::etrue_bounds(const GEnergy& ereco, | ||
| 702 | } | 701 | } |
| 703 | 702 | ||
| 704 | // Index found | 703 | // Index found |
| 705 | - m_index_src = low; | 704 | + m_index_etrue = low; |
| 706 | 705 | ||
| 707 | } | 706 | } |
| 708 | 707 | ||
| 709 | // Return energy boundaries | 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,7 +820,7 @@ void GCTAEdisp2D::fetch(void) const | ||
| 821 | * | 820 | * |
| 822 | * \f[ | 821 | * \f[ |
| 823 | * \int_{E_{\rm reco}^{\rm min}}^{E_{\rm reco}^{\rm max}} | 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 | * \f] | 824 | * \f] |
| 826 | * | 825 | * |
| 827 | * where | 826 | * where |
| @@ -1046,19 +1045,19 @@ void GCTAEdisp2D::init_members(void) | @@ -1046,19 +1045,19 @@ void GCTAEdisp2D::init_members(void) | ||
| 1046 | m_migra_max = 0.0; | 1045 | m_migra_max = 0.0; |
| 1047 | m_theta_min = 0.0; | 1046 | m_theta_min = 0.0; |
| 1048 | m_theta_max = 0.0; | 1047 | m_theta_max = 0.0; |
| 1048 | + m_max_edisp.clear(); | ||
| 1049 | 1049 | ||
| 1050 | // Initialise computation cache | 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 | // Return | 1062 | // Return |
| 1064 | return; | 1063 | return; |
| @@ -1086,19 +1085,19 @@ void GCTAEdisp2D::copy_members(const GCTAEdisp2D& edisp) | @@ -1086,19 +1085,19 @@ void GCTAEdisp2D::copy_members(const GCTAEdisp2D& edisp) | ||
| 1086 | m_migra_max = edisp.m_migra_max; | 1085 | m_migra_max = edisp.m_migra_max; |
| 1087 | m_theta_min = edisp.m_theta_min; | 1086 | m_theta_min = edisp.m_theta_min; |
| 1088 | m_theta_max = edisp.m_theta_max; | 1087 | m_theta_max = edisp.m_theta_max; |
| 1088 | + m_max_edisp = edisp.m_max_edisp; | ||
| 1089 | 1089 | ||
| 1090 | // Copy computation cache | 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 | // Return | 1102 | // Return |
| 1104 | return; | 1103 | return; |
| @@ -1116,89 +1115,141 @@ void GCTAEdisp2D::free_members(void) | @@ -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 | * @param[in] theta Offset angle (radians). | 1176 | * @param[in] theta Offset angle (radians). |
| 1122 | * @param[in] phi Azimuth angle (radians). | 1177 | * @param[in] phi Azimuth angle (radians). |
| 1123 | * @param[in] zenith Zenith angle (radians). | 1178 | * @param[in] zenith Zenith angle (radians). |
| 1124 | * @param[in] azimuth Azimuth angle (radians). | 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 | * energies covered by valid migration matrix elements. Only matrix elements | 1182 | * energies covered by valid migration matrix elements. Only matrix elements |
| 1128 | * with values >= 1.0e-12 are considered as valid elements. In case that no | 1183 | * with values >= 1.0e-12 are considered as valid elements. In case that no |
| 1129 | * matrix elements are found of a given true energy, the interval of observed | 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 | // Clear ebounds_obs vector | 1192 | // Clear ebounds_obs vector |
| 1138 | - m_ebounds_obs.clear(); | 1193 | + m_ereco_bounds.clear(); |
| 1139 | 1194 | ||
| 1140 | // Set epsilon | 1195 | // Set epsilon |
| 1141 | const double eps = 1.0e-12; | 1196 | const double eps = 1.0e-12; |
| 1142 | 1197 | ||
| 1143 | // Determine number of true energy and migration bins | 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 | // Initialise results | 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 | // Find minimum boundary | 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 | // Get matrix term | 1220 | // Get matrix term |
| 1169 | double arg[3]; | 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 | arg[m_inx_theta] = theta; | 1224 | arg[m_inx_theta] = theta; |
| 1173 | double edisp = m_edisp(m_inx_matrix, arg[0], arg[1], arg[2]); | 1225 | double edisp = m_edisp(m_inx_matrix, arg[0], arg[1], arg[2]); |
| 1174 | 1226 | ||
| 1175 | // Find first non-negligible matrix term | 1227 | // Find first non-negligible matrix term |
| 1176 | if (edisp >= eps) { | 1228 | if (edisp >= eps) { |
| 1177 | - minFound = true; | ||
| 1178 | - logEobsMin = std::log10(EobsOverEsrc * Esrc); | 1229 | + found_min = true; |
| 1230 | + ereco_min = migra * etrue.TeV(); | ||
| 1179 | break; | 1231 | break; |
| 1180 | } | 1232 | } |
| 1181 | 1233 | ||
| 1182 | } // endfor: find minimum boundary | 1234 | } // endfor: find minimum boundary |
| 1183 | 1235 | ||
| 1184 | // Find maximum boundary | 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 | // Get matrix term | 1242 | // Get matrix term |
| 1192 | double arg[3]; | 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 | arg[m_inx_theta] = theta; | 1246 | arg[m_inx_theta] = theta; |
| 1196 | double edisp = m_edisp(m_inx_matrix, arg[0], arg[1], arg[2]); | 1247 | double edisp = m_edisp(m_inx_matrix, arg[0], arg[1], arg[2]); |
| 1197 | 1248 | ||
| 1198 | // Find first non-negligible matrix term | 1249 | // Find first non-negligible matrix term |
| 1199 | if (edisp >= eps) { | 1250 | if (edisp >= eps) { |
| 1200 | - maxFound = true; | ||
| 1201 | - logEobsMax = std::log10(EobsOverEsrc * Esrc); | 1251 | + found_max = true; |
| 1252 | + ereco_max = migra * etrue.TeV(); | ||
| 1202 | break; | 1253 | break; |
| 1203 | } | 1254 | } |
| 1204 | 1255 | ||
| @@ -1206,19 +1257,19 @@ void GCTAEdisp2D::compute_ebounds_obs(const double& theta, | @@ -1206,19 +1257,19 @@ void GCTAEdisp2D::compute_ebounds_obs(const double& theta, | ||
| 1206 | 1257 | ||
| 1207 | // If we did not find boundaries then set the interval to | 1258 | // If we did not find boundaries then set the interval to |
| 1208 | // a zero interval for safety | 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 | // Set energy boundaries | 1265 | // Set energy boundaries |
| 1215 | GEnergy emin; | 1266 | GEnergy emin; |
| 1216 | GEnergy emax; | 1267 | GEnergy emax; |
| 1217 | - emin.log10TeV(logEobsMin); | ||
| 1218 | - emax.log10TeV(logEobsMax); | 1268 | + emin.TeV(ereco_min); |
| 1269 | + emax.TeV(ereco_max); | ||
| 1219 | 1270 | ||
| 1220 | // Add energy boundaries to vector | 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 | } // endfor: looped over logEsrc | 1274 | } // endfor: looped over logEsrc |
| 1224 | 1275 | ||
| @@ -1228,7 +1279,7 @@ void GCTAEdisp2D::compute_ebounds_obs(const double& theta, | @@ -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 | * @param[in] theta Offset angle (radians). | 1284 | * @param[in] theta Offset angle (radians). |
| 1234 | * @param[in] phi Azimuth angle (radians). | 1285 | * @param[in] phi Azimuth angle (radians). |
| @@ -1239,62 +1290,59 @@ void GCTAEdisp2D::compute_ebounds_obs(const double& theta, | @@ -1239,62 +1290,59 @@ void GCTAEdisp2D::compute_ebounds_obs(const double& theta, | ||
| 1239 | * energies covered by valid migration matrix elements. Only matrix elements | 1290 | * energies covered by valid migration matrix elements. Only matrix elements |
| 1240 | * with values >= 1.0e-12 are considered as valid elements. In case that no | 1291 | * with values >= 1.0e-12 are considered as valid elements. In case that no |
| 1241 | * matrix elements are found of a given observed energy, the interval of true | 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 | // Initialise energies | 1300 | // Initialise energies |
| 1250 | GEnergy etrue; | 1301 | GEnergy etrue; |
| 1251 | GEnergy ereco; | 1302 | GEnergy ereco; |
| 1252 | 1303 | ||
| 1253 | // Clear ebounds_src vector | 1304 | // Clear ebounds_src vector |
| 1254 | - m_ebounds_src.clear(); | 1305 | + m_etrue_bounds.clear(); |
| 1255 | 1306 | ||
| 1256 | // Set epsilon | 1307 | // Set epsilon |
| 1257 | const double eps = 1.0e-12; | 1308 | const double eps = 1.0e-12; |
| 1258 | 1309 | ||
| 1259 | // Determine number of true energy bins | 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 | // Initialise results | 1320 | // Initialise results |
| 1272 | GEnergy etrue_min; | 1321 | GEnergy etrue_min; |
| 1273 | GEnergy etrue_max; | 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 | // Find minimum boundary | 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 | // Fall through if migration is zero | 1332 | // Fall through if migration is zero |
| 1285 | if (migra == 0.0) { | 1333 | if (migra == 0.0) { |
| 1286 | continue; | 1334 | continue; |
| 1287 | } | 1335 | } |
| 1288 | 1336 | ||
| 1289 | - // Compute log10 of true energy | 1337 | + // Compute true energy |
| 1290 | etrue = ereco / migra; | 1338 | etrue = ereco / migra; |
| 1291 | 1339 | ||
| 1292 | - // Get matrix term | 1340 | + // Get energy dispersion |
| 1293 | double edisp = operator()(ereco, etrue, theta); | 1341 | double edisp = operator()(ereco, etrue, theta); |
| 1294 | 1342 | ||
| 1295 | // Find first non-negligible matrix term | 1343 | // Find first non-negligible matrix term |
| 1296 | if (edisp >= eps) { | 1344 | if (edisp >= eps) { |
| 1297 | - maxFound = true; | 1345 | + found_max = true; |
| 1298 | etrue_max = etrue; | 1346 | etrue_max = etrue; |
| 1299 | break; | 1347 | break; |
| 1300 | } | 1348 | } |
| @@ -1302,41 +1350,40 @@ void GCTAEdisp2D::compute_ebounds_src(const double& theta, | @@ -1302,41 +1350,40 @@ void GCTAEdisp2D::compute_ebounds_src(const double& theta, | ||
| 1302 | } // endfor: find minimum boundary | 1350 | } // endfor: find minimum boundary |
| 1303 | 1351 | ||
| 1304 | // Find maximum boundary | 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 | // Fall through if migration is zero | 1358 | // Fall through if migration is zero |
| 1312 | if (migra == 0.0) { | 1359 | if (migra == 0.0) { |
| 1313 | continue; | 1360 | continue; |
| 1314 | } | 1361 | } |
| 1315 | 1362 | ||
| 1316 | - // Compute log10 of true energy | 1363 | + // Compute true energy |
| 1317 | etrue = ereco / migra; | 1364 | etrue = ereco / migra; |
| 1318 | 1365 | ||
| 1319 | - // Get matrix term | 1366 | + // Get energy dispersion |
| 1320 | double edisp = operator()(ereco, etrue, theta); | 1367 | double edisp = operator()(ereco, etrue, theta); |
| 1321 | 1368 | ||
| 1322 | // Find first non-negligible matrix term | 1369 | // Find first non-negligible matrix term |
| 1323 | if (edisp >= eps) { | 1370 | if (edisp >= eps) { |
| 1324 | - minFound = true; | 1371 | + found_min = true; |
| 1325 | etrue_min = etrue; | 1372 | etrue_min = etrue; |
| 1326 | break; | 1373 | break; |
| 1327 | } | 1374 | } |
| 1328 | 1375 | ||
| 1329 | } // endfor: find maximum boundary | 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 | etrue_min.clear(); | 1381 | etrue_min.clear(); |
| 1335 | etrue_max.clear(); | 1382 | etrue_max.clear(); |
| 1336 | } | 1383 | } |
| 1337 | 1384 | ||
| 1338 | // Add energy boundaries to vector | 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 | } // endfor : loopend over observed energy | 1388 | } // endfor : loopend over observed energy |
| 1342 | 1389 | ||
| @@ -1349,15 +1396,7 @@ void GCTAEdisp2D::compute_ebounds_src(const double& theta, | @@ -1349,15 +1396,7 @@ void GCTAEdisp2D::compute_ebounds_src(const double& theta, | ||
| 1349 | * @brief Set table | 1396 | * @brief Set table |
| 1350 | * | 1397 | * |
| 1351 | * After assigning or loading a response table, performs all necessary steps | 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 | * For CTA, the method applies a kludge that smoothes the energy dispersion | 1401 | * For CTA, the method applies a kludge that smoothes the energy dispersion |
| 1363 | * table to get rid of numerical fluctuations. | 1402 | * table to get rid of numerical fluctuations. |
| @@ -1407,7 +1446,7 @@ void GCTAEdisp2D::set_table(void) | @@ -1407,7 +1446,7 @@ void GCTAEdisp2D::set_table(void) | ||
| 1407 | // Normalize energy dispersion table | 1446 | // Normalize energy dispersion table |
| 1408 | normalize_table(); | 1447 | normalize_table(); |
| 1409 | 1448 | ||
| 1410 | - // Set maximum energy dispersion value | 1449 | + // Set maximum energy dispersion value array |
| 1411 | set_max_edisp(); | 1450 | set_max_edisp(); |
| 1412 | 1451 | ||
| 1413 | // Save test | 1452 | // Save test |
| @@ -1452,20 +1491,50 @@ void GCTAEdisp2D::set_boundaries(void) | @@ -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 | // Return | 1539 | // Return |
| 1471 | return; | 1540 | return; |
| @@ -1473,13 +1542,76 @@ void GCTAEdisp2D::set_max_edisp(void) const | @@ -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 | * @brief Normalize energy dispersion table | 1608 | * @brief Normalize energy dispersion table |
| 1477 | * | 1609 | * |
| 1478 | * Normalize the energy dispersion table using | 1610 | * Normalize the energy dispersion table using |
| 1479 | * | 1611 | * |
| 1480 | * \f[ | 1612 | * \f[ |
| 1481 | * \int_{E_{\rm reco}^{\rm min}}^{E_{\rm reco}^{\rm max}} | 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 | * \f] | 1615 | * \f] |
| 1484 | * | 1616 | * |
| 1485 | * where | 1617 | * where |
| @@ -1487,8 +1619,7 @@ void GCTAEdisp2D::set_max_edisp(void) const | @@ -1487,8 +1619,7 @@ void GCTAEdisp2D::set_max_edisp(void) const | ||
| 1487 | * minimum and maximum migration value for a given true energy as returned | 1619 | * minimum and maximum migration value for a given true energy as returned |
| 1488 | * by the ebounds_obs() method, and | 1620 | * by the ebounds_obs() method, and |
| 1489 | * \f$E_{\rm disp}(E_{\rm true}, E_{\rm reco}, \theta)\f$ is the energy | 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 | * The normalisation is performed for each \f$E_{\rm true}\f$ and | 1624 | * The normalisation is performed for each \f$E_{\rm true}\f$ and |
| 1494 | * \f$\theta\f$ bin using a Romberg integration method. Since the | 1625 | * \f$\theta\f$ bin using a Romberg integration method. Since the |
| @@ -1518,21 +1649,13 @@ void GCTAEdisp2D::normalize_table(void) | @@ -1518,21 +1649,13 @@ void GCTAEdisp2D::normalize_table(void) | ||
| 1518 | for (int i_theta = 0; i_theta < theta_size; ++i_theta) { | 1649 | for (int i_theta = 0; i_theta < theta_size; ++i_theta) { |
| 1519 | 1650 | ||
| 1520 | // Get offset angle (in radians) | 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 | // Loop over true photon energy | 1654 | // Loop over true photon energy |
| 1526 | for (int i_etrue = 0; i_etrue < etrue_size; ++i_etrue) { | 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 | // Initialise integration | 1660 | // Initialise integration |
| 1538 | double sum = 0.0; | 1661 | double sum = 0.0; |
| @@ -1544,12 +1667,12 @@ void GCTAEdisp2D::normalize_table(void) | @@ -1544,12 +1667,12 @@ void GCTAEdisp2D::normalize_table(void) | ||
| 1544 | for (int i = 0; i < ebounds.size(); ++i) { | 1667 | for (int i = 0; i < ebounds.size(); ++i) { |
| 1545 | 1668 | ||
| 1546 | // Get energy boundaries in log10 of energy in MeV | 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 | // Setup integration function | 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 | // Set integration precision | 1677 | // Set integration precision |
| 1555 | integral.eps(1.0e-6); | 1678 | integral.eps(1.0e-6); |
| @@ -2108,37 +2231,37 @@ GNdarray GCTAEdisp2D::gaussian_array(const double& mean, | @@ -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 | * @return Energy dispersion (\f$(\log_{10}\f$ MeV\f$)^{-1}\f$). | 2237 | * @return Energy dispersion (\f$(\log_{10}\f$ MeV\f$)^{-1}\f$). |
| 2115 | * | 2238 | * |
| 2116 | * This method implements the function | 2239 | * This method implements the function |
| 2117 | * | 2240 | * |
| 2118 | * \f[ | 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 | * \frac{\log 10 \times E_{\rm reco}}{E_{\rm true}} | 2244 | * \frac{\log 10 \times E_{\rm reco}}{E_{\rm true}} |
| 2122 | * \f] | 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 | * \f[ | 2252 | * \f[ |
| 2129 | * \int_{\log_{10} E_{\rm reco}^{\rm min}}^{\log_{10} E_{\rm reco}^{\rm max}} | 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 | * d(\log_{10} E_{\rm reco}) | 2255 | * d(\log_{10} E_{\rm reco}) |
| 2132 | * \f] | 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 | GEnergy ereco; | 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 | double value = m_parent->operator()(ereco, m_etrue, m_theta); | 2265 | double value = m_parent->operator()(ereco, m_etrue, m_theta); |
| 2143 | 2266 | ||
| 2144 | // Normalize energy dispersion so that the units are per log10 MeV | 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,8 +2270,8 @@ double GCTAEdisp2D::edisp_kern::eval(const double& logEobs) | ||
| 2147 | // Compile option: Check for NaN | 2270 | // Compile option: Check for NaN |
| 2148 | #if defined(G_NAN_CHECK) | 2271 | #if defined(G_NAN_CHECK) |
| 2149 | if (gammalib::is_notanumber(value) || gammalib::is_infinite(value)) { | 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 | std::cout << " NaN/Inf encountered"; | 2275 | std::cout << " NaN/Inf encountered"; |
| 2153 | std::cout << " (value=" << value; | 2276 | std::cout << " (value=" << value; |
| 2154 | std::cout << ")" << std::endl; | 2277 | std::cout << ")" << std::endl; |
inst/cta/test/dev/test_sim_edisp.py
| @@ -31,7 +31,7 @@ import time | @@ -31,7 +31,7 @@ import time | ||
| 31 | # ============== # | 31 | # ============== # |
| 32 | # Simulate Edisp # | 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 | Simulate Edisp and show results using matplotlib (if available). | 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,20 +40,31 @@ def sim_edisp(edisp, etrue, eobs_max=40.0, ebins=1000, nmc=100000): | ||
| 40 | import matplotlib.pyplot as plt | 40 | import matplotlib.pyplot as plt |
| 41 | has_matplotlib = True | 41 | has_matplotlib = True |
| 42 | except ImportError: | 42 | except ImportError: |
| 43 | - print("Matplotlib is not (correctly) installed on your system.") | 43 | + print('Matplotlib is not (correctly) installed on your system.') |
| 44 | has_matplotlib = False | 44 | has_matplotlib = False |
| 45 | 45 | ||
| 46 | # Continue only if matplotlib is available | 46 | # Continue only if matplotlib is available |
| 47 | if has_matplotlib: | 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 | for i in range(ebins): | 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 | # Reset histogram | 69 | # Reset histogram |
| 59 | counts = [0.0 for i in range(ebins)] | 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,87 +75,122 @@ def sim_edisp(edisp, etrue, eobs_max=40.0, ebins=1000, nmc=100000): | ||
| 64 | # Simulate observed energies | 75 | # Simulate observed energies |
| 65 | tstart = time.clock() | 76 | tstart = time.clock() |
| 66 | for i in range(nmc): | 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 | if (index < ebins): | 80 | if (index < ebins): |
| 71 | counts[index] += 1.0 | 81 | counts[index] += 1.0 |
| 72 | telapsed = (time.clock() - tstart) | 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 | # Create error bars | 89 | # Create error bars |
| 76 | error = [math.sqrt(c) for c in counts] | 90 | error = [math.sqrt(c) for c in counts] |
| 77 | 91 | ||
| 78 | # Get expected energy dispersion | 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 | for i in range(ebins): | 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 | else: | 99 | else: |
| 91 | value = 0.0 | 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 | # Notify | 151 | # Notify |
| 121 | - print("PLEASE CLOSE WINDOW TO CONTINUE ...") | 152 | + print('PLEASE CLOSE WINDOW TO CONTINUE ...') |
| 122 | 153 | ||
| 123 | # Show plot | 154 | # Show plot |
| 124 | - plt.title("MC simulated Energy dispersion (true photon energy "+str(etrue)+" TeV)") | ||
| 125 | plt.show() | 155 | plt.show() |
| 126 | 156 | ||
| 127 | # Return | 157 | # Return |
| 128 | return | 158 | return |
| 129 | 159 | ||
| 130 | 160 | ||
| 131 | -#==========================# | 161 | +# ======================== # |
| 132 | # Main routine entry point # | 162 | # Main routine entry point # |
| 133 | -#==========================# | 163 | +# ======================== # |
| 134 | if __name__ == '__main__': | 164 | if __name__ == '__main__': |
| 135 | - """ | ||
| 136 | - Simulate Energy dispersion. | ||
| 137 | - """ | 165 | + |
| 138 | # Dump header | 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 | # Load edisp | 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 |