Commit ba9b66ded85bdbf2a8d77fb8977dbc9a814d8b72
1 parent
c31806e6
Fix consistency problem in GCTABackground3D class
Showing
7 changed files
with
453 additions
and
264 deletions
ChangeLog
| 1 | -2016-07-12 Juergen Knoedlseder <jurgen.knodlseder@irap.omp.eu> | 1 | +2016-07-18 Juergen Knoedlseder <jurgen.knodlseder@irap.omp.eu> |
| 2 | 2 | ||
| 3 | * Version 1.1.0 released | 3 | * Version 1.1.0 released |
| 4 | ========================= | 4 | ========================= |
| 5 | 5 | ||
| 6 | + Fix consistency problem in GCTABackground3D (#1815) | ||
| 6 | Models accessing file now support relative file names (#1808) | 7 | Models accessing file now support relative file names (#1808) |
| 7 | Add GXmlElement and GXmlDocument filename() methods (#1808) | 8 | Add GXmlElement and GXmlDocument filename() methods (#1808) |
| 8 | Move node parent from GXmlElement to GXmlNode class (#1808) | 9 | Move node parent from GXmlElement to GXmlNode class (#1808) |
| 9 | - Correct Makefile for example executables (#1806) | 10 | + Fix Makefile for example executables (#1806) |
| 10 | Include examples in unit tests (#1803) | 11 | Include examples in unit tests (#1803) |
| 11 | Handle additional columns in GArf class | 12 | Handle additional columns in GArf class |
| 12 | GCTAAeff2D no longer extrapolates beyond response table (#1784) | 13 | GCTAAeff2D no longer extrapolates beyond response table (#1784) |
| @@ -56,8 +57,8 @@ | @@ -56,8 +57,8 @@ | ||
| 56 | Add GFilename::fullname() method | 57 | Add GFilename::fullname() method |
| 57 | Add GCTAEventList::remove() method | 58 | Add GCTAEventList::remove() method |
| 58 | Handle also additional columns in GCTAEventList::append() method | 59 | Handle also additional columns in GCTAEventList::append() method |
| 59 | - Correct GFitsTableCol::insert() method for empty columns | ||
| 60 | - Correct GCTAEdisp2D::compute_ebounds_xxx() methods (#1662) | 60 | + Fix GFitsTableCol::insert() method for empty columns |
| 61 | + Fix GCTAEdisp2D::compute_ebounds_xxx() methods (#1662) | ||
| 61 | Use GFilename in GFits to store filename (#1649) | 62 | Use GFilename in GFits to store filename (#1649) |
| 62 | Load CTA GTI extension from the HDU name provided by the DSS keyword (#1598) | 63 | Load CTA GTI extension from the HDU name provided by the DSS keyword (#1598) |
| 63 | Support Fermi LAT Pass 8 response functions (#1517) | 64 | Support Fermi LAT Pass 8 response functions (#1517) |
NEWS
| @@ -2,7 +2,7 @@ New Features and Important Changes in GammaLib 1.1.0 | @@ -2,7 +2,7 @@ New Features and Important Changes in GammaLib 1.1.0 | ||
| 2 | 2 | ||
| 3 | Author(s) : Juergen Knoedlseder | 3 | Author(s) : Juergen Knoedlseder |
| 4 | 4 | ||
| 5 | -12 July 2016 | 5 | +18 July 2016 |
| 6 | 6 | ||
| 7 | 7 | ||
| 8 | 1. Introduction | 8 | 1. Introduction |
| @@ -440,6 +440,10 @@ handled by the weight array (#1789). | @@ -440,6 +440,10 @@ handled by the weight array (#1789). | ||
| 440 | The GCTAAeff2D class no longer extrapolates the effective area beyond the boundaries | 440 | The GCTAAeff2D class no longer extrapolates the effective area beyond the boundaries |
| 441 | of the response table, but sets the effective area values to zero (#1784). | 441 | of the response table, but sets the effective area values to zero (#1784). |
| 442 | 442 | ||
| 443 | +The GCTABackground3D class now assures that the mc() method covers the same DETX and DETY | ||
| 444 | +range than the access operator. This fixes a consistency problem between Monte Carlo | ||
| 445 | +sampling and function evaluation (#1815). | ||
| 446 | + | ||
| 443 | 447 | ||
| 444 | 21. COMPTEL interface | 448 | 21. COMPTEL interface |
| 445 | --------------------- | 449 | --------------------- |
inst/cta/include/GCTABackground3D.hpp
| @@ -83,33 +83,39 @@ public: | @@ -83,33 +83,39 @@ public: | ||
| 83 | 83 | ||
| 84 | private: | 84 | private: |
| 85 | // Methods | 85 | // Methods |
| 86 | - void init_members(void); | ||
| 87 | - void copy_members(const GCTABackground3D& bgd); | ||
| 88 | - void free_members(void); | ||
| 89 | - void init_mc_cache(void) const; | 86 | + void init_members(void); |
| 87 | + void copy_members(const GCTABackground3D& bgd); | ||
| 88 | + void free_members(void); | ||
| 89 | + void set_limits(void); | ||
| 90 | + int index(const int& idetx, const int& idety, const int& iebin) const; | ||
| 91 | + void init_mc_cache(void) const; | ||
| 92 | + void init_mc_max_rate(void) const; | ||
| 93 | + double solid_angle(const double& detx1, const double& dety1, | ||
| 94 | + const double& detx2, const double& dety2, | ||
| 95 | + const double& detx3, const double& dety3) const; | ||
| 90 | 96 | ||
| 91 | // Members | 97 | // Members |
| 92 | GFilename m_filename; //!< Name of background response file | 98 | GFilename m_filename; //!< Name of background response file |
| 93 | GCTAResponseTable m_background; //!< Background response table | 99 | GCTAResponseTable m_background; //!< Background response table |
| 94 | - double m_mc_max_bin; //!< Maximum spatial binsize for MC | ||
| 95 | - double m_mc_max_logE; //!< Maximum log energy binsize for MC | ||
| 96 | int m_inx_detx; //!< DETX index | 100 | int m_inx_detx; //!< DETX index |
| 97 | int m_inx_dety; //!< DETY index | 101 | int m_inx_dety; //!< DETY index |
| 98 | int m_inx_energy; //!< Energy index | 102 | int m_inx_energy; //!< Energy index |
| 99 | int m_inx_bgd; //!< Background index | 103 | int m_inx_bgd; //!< Background index |
| 104 | + int m_num_detx; //!< Number of DETX bins | ||
| 105 | + int m_num_dety; //!< Number of DETY bins | ||
| 106 | + int m_num_energy; //!< Number of energy bins | ||
| 107 | + int m_num[3]; //!< Array of number of bins | ||
| 108 | + double m_detx_min; //!< DETX minimum (radians) | ||
| 109 | + double m_detx_max; //!< DETX maximum (radians) | ||
| 110 | + double m_dety_min; //!< DETY minimum (radians) | ||
| 111 | + double m_dety_max; //!< DETY maximum (radians) | ||
| 112 | + double m_logE_min; //!< Log10(E/TeV) minimum | ||
| 113 | + double m_logE_max; //!< Log10(E/TeV) maximum | ||
| 100 | 114 | ||
| 101 | // Monte Carlo cache | 115 | // Monte Carlo cache |
| 102 | - mutable std::vector<double> m_mc_max; //!< Maximum rate | ||
| 103 | - mutable GModelSpectralNodes m_mc_spectrum; //!< Response cube spectrum | ||
| 104 | - mutable double m_mc_detx_min; //!< DETX minimum (deg) | ||
| 105 | - mutable double m_mc_detx_max; //!< DETX maximum (deg) | ||
| 106 | - mutable double m_mc_detx_bin; //!< DETX binsize (deg) | ||
| 107 | - mutable double m_mc_dety_min; //!< DETY minimum (deg) | ||
| 108 | - mutable double m_mc_dety_max; //!< DETY maximum (deg) | ||
| 109 | - mutable double m_mc_dety_bin; //!< DETY binsize (deg) | ||
| 110 | - mutable double m_mc_logE_min; //!< log10 energy minimum (TeV) | ||
| 111 | - mutable double m_mc_logE_max; //!< log10 energy maximum (TeV) | ||
| 112 | - mutable double m_mc_logE_bin; //!< log10 energy binsize (TeV) | 116 | + mutable std::vector<double> m_mc_max; //!< Maximum background rate |
| 117 | + mutable GModelSpectralNodes m_mc_spectrum; //!< Response cube spectrum | ||
| 118 | + mutable double m_mc_one_minus_costheta; //!< 1-cos(theta_max) | ||
| 113 | }; | 119 | }; |
| 114 | 120 | ||
| 115 | 121 |
inst/cta/src/GCTABackground3D.cpp
| @@ -174,27 +174,28 @@ GCTABackground3D& GCTABackground3D::operator=(const GCTABackground3D& bgd) | @@ -174,27 +174,28 @@ GCTABackground3D& GCTABackground3D::operator=(const GCTABackground3D& bgd) | ||
| 174 | * @brief Return background rate in units of events/s/MeV/sr | 174 | * @brief Return background rate in units of events/s/MeV/sr |
| 175 | * | 175 | * |
| 176 | * @param[in] logE Log10 of the true photon energy (TeV). | 176 | * @param[in] logE Log10 of the true photon energy (TeV). |
| 177 | - * @param[in] detx Tangential coord in nominal sys (radians). | ||
| 178 | - * @param[in] dety Tangential coord in nominal sys (radians). | 177 | + * @param[in] detx Tangential X coordinate in nominal system (radians). |
| 178 | + * @param[in] dety Tangential Y coordinate in nominal system (radians). | ||
| 179 | * | 179 | * |
| 180 | * Returns the background rate in units of events/s/MeV/sr for a given energy | 180 | * Returns the background rate in units of events/s/MeV/sr for a given energy |
| 181 | - * and detector coordinates. The method assures that the background rate | ||
| 182 | - * never becomes negative. | 181 | + * and detector coordinates. The operator interpolates linearly in DETX and |
| 182 | + * DETY, and logarithmically in energy. | ||
| 183 | * | 183 | * |
| 184 | - * The method interpolates linearly in DETX and DETY, and logarithmically | ||
| 185 | - * in energy. | ||
| 186 | - * | ||
| 187 | - * If the background model is not valid the operator returns 0. | 184 | + * The operator assures that the background rate never becomes negative. For |
| 185 | + * invalid background models or detector coordinates outside the range | ||
| 186 | + * covered by the response cube, the operator returns a rate of zero. | ||
| 188 | ***************************************************************************/ | 187 | ***************************************************************************/ |
| 189 | double GCTABackground3D::operator()(const double& logE, | 188 | double GCTABackground3D::operator()(const double& logE, |
| 190 | const double& detx, | 189 | const double& detx, |
| 191 | const double& dety) const | 190 | const double& dety) const |
| 192 | { | 191 | { |
| 193 | - // Initialise rate | ||
| 194 | - double rate(0.0); | 192 | + // Initialise background rate |
| 193 | + double rate = 0.0; | ||
| 195 | 194 | ||
| 196 | - // Continue only if background model is valid | ||
| 197 | - if (is_valid()) { | 195 | + // Continue only if background model is valid and if the DETX and DETY |
| 196 | + // values are in validity range | ||
| 197 | + if (is_valid() && (detx >= m_detx_min) && (detx <= m_detx_max) && | ||
| 198 | + (dety >= m_dety_min) && (dety <= m_dety_max)) { | ||
| 198 | 199 | ||
| 199 | // Retrieve references to node arrays | 200 | // Retrieve references to node arrays |
| 200 | const GNodeArray& detx_nodes = m_background.axis_nodes(m_inx_detx); | 201 | const GNodeArray& detx_nodes = m_background.axis_nodes(m_inx_detx); |
| @@ -206,46 +207,43 @@ double GCTABackground3D::operator()(const double& logE, | @@ -206,46 +207,43 @@ double GCTABackground3D::operator()(const double& logE, | ||
| 206 | dety_nodes.set_value(dety); | 207 | dety_nodes.set_value(dety); |
| 207 | energy_nodes.set_value(logE); | 208 | energy_nodes.set_value(logE); |
| 208 | 209 | ||
| 209 | - // Compute offsets of DETY in DETX-DETY plane | ||
| 210 | - int size1 = m_background.axis_bins(m_inx_detx); | ||
| 211 | - int offset_left = dety_nodes.inx_left() * size1; | ||
| 212 | - int offset_right = dety_nodes.inx_right() * size1; | ||
| 213 | - | ||
| 214 | - // Set indices for bi-linear interpolation in DETX-DETY plane | ||
| 215 | - int inx_ll = detx_nodes.inx_left() + offset_left; | ||
| 216 | - int inx_lr = detx_nodes.inx_left() + offset_right; | ||
| 217 | - int inx_rl = detx_nodes.inx_right() + offset_left; | ||
| 218 | - int inx_rr = detx_nodes.inx_right() + offset_right; | ||
| 219 | - | ||
| 220 | // Set weighting factors for bi-linear interpolation in DETX-DETY plane | 210 | // Set weighting factors for bi-linear interpolation in DETX-DETY plane |
| 221 | double wgt_ll = detx_nodes.wgt_left() * dety_nodes.wgt_left(); | 211 | double wgt_ll = detx_nodes.wgt_left() * dety_nodes.wgt_left(); |
| 222 | double wgt_lr = detx_nodes.wgt_left() * dety_nodes.wgt_right(); | 212 | double wgt_lr = detx_nodes.wgt_left() * dety_nodes.wgt_right(); |
| 223 | double wgt_rl = detx_nodes.wgt_right() * dety_nodes.wgt_left(); | 213 | double wgt_rl = detx_nodes.wgt_right() * dety_nodes.wgt_left(); |
| 224 | double wgt_rr = detx_nodes.wgt_right() * dety_nodes.wgt_right(); | 214 | double wgt_rr = detx_nodes.wgt_right() * dety_nodes.wgt_right(); |
| 225 | 215 | ||
| 226 | - // Set indices for energy interpolation | ||
| 227 | - int inx_emin = energy_nodes.inx_left(); | ||
| 228 | - int inx_emax = energy_nodes.inx_right(); | ||
| 229 | - | ||
| 230 | // Set weighting factors for energy interpolation | 216 | // Set weighting factors for energy interpolation |
| 231 | double wgt_emin = energy_nodes.wgt_left(); | 217 | double wgt_emin = energy_nodes.wgt_left(); |
| 232 | double wgt_emax = energy_nodes.wgt_right(); | 218 | double wgt_emax = energy_nodes.wgt_right(); |
| 233 | 219 | ||
| 234 | - // Compute offsets in energy dimension | ||
| 235 | - int npixels = m_background.axis_bins(m_inx_detx) * | ||
| 236 | - m_background.axis_bins(m_inx_dety); | ||
| 237 | - int offset_emin = inx_emin * npixels; | ||
| 238 | - int offset_emax = inx_emax * npixels; | ||
| 239 | - | ||
| 240 | - // Bi-linear interpolation the rates in both energy layers | ||
| 241 | - double rate_emin = wgt_ll * m_background(m_inx_bgd, inx_ll + offset_emin) + | ||
| 242 | - wgt_lr * m_background(m_inx_bgd, inx_lr + offset_emin) + | ||
| 243 | - wgt_rl * m_background(m_inx_bgd, inx_rl + offset_emin) + | ||
| 244 | - wgt_rr * m_background(m_inx_bgd, inx_rr + offset_emin); | ||
| 245 | - double rate_emax = wgt_ll * m_background(m_inx_bgd, inx_ll + offset_emax) + | ||
| 246 | - wgt_lr * m_background(m_inx_bgd, inx_lr + offset_emax) + | ||
| 247 | - wgt_rl * m_background(m_inx_bgd, inx_rl + offset_emax) + | ||
| 248 | - wgt_rr * m_background(m_inx_bgd, inx_rr + offset_emax); | 220 | + // Get bin indices |
| 221 | + int inx_ll_emin = index(detx_nodes.inx_left(), dety_nodes.inx_left(), | ||
| 222 | + energy_nodes.inx_left()); | ||
| 223 | + int inx_lr_emin = index(detx_nodes.inx_left(), dety_nodes.inx_right(), | ||
| 224 | + energy_nodes.inx_left()); | ||
| 225 | + int inx_rl_emin = index(detx_nodes.inx_right(), dety_nodes.inx_left(), | ||
| 226 | + energy_nodes.inx_left()); | ||
| 227 | + int inx_rr_emin = index(detx_nodes.inx_right(), dety_nodes.inx_right(), | ||
| 228 | + energy_nodes.inx_left()); | ||
| 229 | + int inx_ll_emax = index(detx_nodes.inx_left(), dety_nodes.inx_left(), | ||
| 230 | + energy_nodes.inx_right()); | ||
| 231 | + int inx_lr_emax = index(detx_nodes.inx_left(), dety_nodes.inx_right(), | ||
| 232 | + energy_nodes.inx_right()); | ||
| 233 | + int inx_rl_emax = index(detx_nodes.inx_right(), dety_nodes.inx_left(), | ||
| 234 | + energy_nodes.inx_right()); | ||
| 235 | + int inx_rr_emax = index(detx_nodes.inx_right(), dety_nodes.inx_right(), | ||
| 236 | + energy_nodes.inx_right()); | ||
| 237 | + | ||
| 238 | + // Bi-linearly interpolate the rates in both energy layers | ||
| 239 | + double rate_emin = wgt_ll * m_background(m_inx_bgd, inx_ll_emin) + | ||
| 240 | + wgt_lr * m_background(m_inx_bgd, inx_lr_emin) + | ||
| 241 | + wgt_rl * m_background(m_inx_bgd, inx_rl_emin) + | ||
| 242 | + wgt_rr * m_background(m_inx_bgd, inx_rr_emin); | ||
| 243 | + double rate_emax = wgt_ll * m_background(m_inx_bgd, inx_ll_emax) + | ||
| 244 | + wgt_lr * m_background(m_inx_bgd, inx_lr_emax) + | ||
| 245 | + wgt_rl * m_background(m_inx_bgd, inx_rl_emax) + | ||
| 246 | + wgt_rr * m_background(m_inx_bgd, inx_rr_emax); | ||
| 249 | 247 | ||
| 250 | // If both rates are positive then perform a logarithmic interpolation | 248 | // If both rates are positive then perform a logarithmic interpolation |
| 251 | // in energy | 249 | // in energy |
| @@ -338,12 +336,6 @@ void GCTABackground3D::read(const GFitsTable& table) | @@ -338,12 +336,6 @@ void GCTABackground3D::read(const GFitsTable& table) | ||
| 338 | // Read background table | 336 | // Read background table |
| 339 | m_background.read(table); | 337 | m_background.read(table); |
| 340 | 338 | ||
| 341 | - // Get mandatory indices (throw exception if not found) | ||
| 342 | - m_inx_detx = m_background.axis("DETX"); | ||
| 343 | - m_inx_dety = m_background.axis("DETY"); | ||
| 344 | - m_inx_energy = m_background.axis("ENERG"); | ||
| 345 | - m_inx_bgd = m_background.table("BGD"); | ||
| 346 | - | ||
| 347 | // Throw an exception if the table is not three-dimensional | 339 | // Throw an exception if the table is not three-dimensional |
| 348 | if (m_background.axes() != 3) { | 340 | if (m_background.axes() != 3) { |
| 349 | std::string msg = "Expected three-dimensional background " | 341 | std::string msg = "Expected three-dimensional background " |
| @@ -354,6 +346,22 @@ void GCTABackground3D::read(const GFitsTable& table) | @@ -354,6 +346,22 @@ void GCTABackground3D::read(const GFitsTable& table) | ||
| 354 | throw GException::invalid_value(G_READ, msg); | 346 | throw GException::invalid_value(G_READ, msg); |
| 355 | } | 347 | } |
| 356 | 348 | ||
| 349 | + // Get mandatory indices (throw exception if not found) | ||
| 350 | + m_inx_detx = m_background.axis("DETX"); | ||
| 351 | + m_inx_dety = m_background.axis("DETY"); | ||
| 352 | + m_inx_energy = m_background.axis("ENERG"); | ||
| 353 | + m_inx_bgd = m_background.table("BGD"); | ||
| 354 | + | ||
| 355 | + // Get axes dimensions | ||
| 356 | + m_num_detx = m_background.axis_bins(m_inx_detx); | ||
| 357 | + m_num_dety = m_background.axis_bins(m_inx_dety); | ||
| 358 | + m_num_energy = m_background.axis_bins(m_inx_energy); | ||
| 359 | + | ||
| 360 | + // Set dimension array | ||
| 361 | + m_num[m_inx_detx] = m_num_detx; | ||
| 362 | + m_num[m_inx_dety] = m_num_dety; | ||
| 363 | + m_num[m_inx_energy] = m_num_energy; | ||
| 364 | + | ||
| 357 | // Set DETX and DETY axis to radians | 365 | // Set DETX and DETY axis to radians |
| 358 | m_background.axis_radians(m_inx_detx); | 366 | m_background.axis_radians(m_inx_detx); |
| 359 | m_background.axis_radians(m_inx_dety); | 367 | m_background.axis_radians(m_inx_dety); |
| @@ -361,6 +369,9 @@ void GCTABackground3D::read(const GFitsTable& table) | @@ -361,6 +369,9 @@ void GCTABackground3D::read(const GFitsTable& table) | ||
| 361 | // Set energy axis to logarithmic scale | 369 | // Set energy axis to logarithmic scale |
| 362 | m_background.axis_log10(m_inx_energy); | 370 | m_background.axis_log10(m_inx_energy); |
| 363 | 371 | ||
| 372 | + // Set DETX and DETY limits | ||
| 373 | + set_limits(); | ||
| 374 | + | ||
| 364 | // Return | 375 | // Return |
| 365 | return; | 376 | return; |
| 366 | } | 377 | } |
| @@ -484,7 +495,7 @@ GCTAInstDir GCTABackground3D::mc(const GEnergy& energy, | @@ -484,7 +495,7 @@ GCTAInstDir GCTABackground3D::mc(const GEnergy& energy, | ||
| 484 | const GTime& time, | 495 | const GTime& time, |
| 485 | GRan& ran) const | 496 | GRan& ran) const |
| 486 | { | 497 | { |
| 487 | - // Initialise Monte Carlo Cache | 498 | + // If Monte Carlo has not been initialised then initialise the cache now |
| 488 | if (m_mc_max.empty()) { | 499 | if (m_mc_max.empty()) { |
| 489 | init_mc_cache(); | 500 | init_mc_cache(); |
| 490 | } | 501 | } |
| @@ -508,35 +519,35 @@ GCTAInstDir GCTABackground3D::mc(const GEnergy& energy, | @@ -508,35 +519,35 @@ GCTAInstDir GCTABackground3D::mc(const GEnergy& energy, | ||
| 508 | int inx_left = energy_nodes.inx_left(); | 519 | int inx_left = energy_nodes.inx_left(); |
| 509 | int inx_right = energy_nodes.inx_right(); | 520 | int inx_right = energy_nodes.inx_right(); |
| 510 | 521 | ||
| 511 | - // Get maximum background rates | ||
| 512 | - double max_rate_left = m_mc_max[inx_left]; | ||
| 513 | - double max_rate_right = m_mc_max[inx_right]; | ||
| 514 | - | ||
| 515 | - // Compute maximum background rate | ||
| 516 | - double max_rate = 0.0; | ||
| 517 | - if (max_rate_left > 0.0 && max_rate_right > 0.0) { | ||
| 518 | - double wgt_left = energy_nodes.wgt_left(); | ||
| 519 | - double wgt_right = energy_nodes.wgt_right(); | ||
| 520 | - max_rate = std::exp(wgt_left * std::log(max_rate_left) + | ||
| 521 | - wgt_right * std::log(max_rate_right)); | ||
| 522 | - } | ||
| 523 | - else { | ||
| 524 | - max_rate = 0.0; | 522 | + // Get maximum background rate as the maximum of the left and the right |
| 523 | + // energy node | ||
| 524 | + double max_rate = m_mc_max[inx_left]; | ||
| 525 | + if (m_mc_max[inx_right] > max_rate) { | ||
| 526 | + max_rate = m_mc_max[inx_right]; | ||
| 525 | } | 527 | } |
| 526 | 528 | ||
| 527 | - // Get instrument direction | 529 | + // Get instrument direction using a rejection method. This assures |
| 530 | + // that the Monte Carlo sample follows the model distribution | ||
| 528 | while (true) { | 531 | while (true) { |
| 529 | 532 | ||
| 530 | - // Get randomized detx and dety (radians) | ||
| 531 | - double detx = (m_mc_detx_min + | ||
| 532 | - ran.uniform() * (m_mc_detx_max - m_mc_detx_min)) * | ||
| 533 | - gammalib::deg2rad; | ||
| 534 | - double dety = (m_mc_dety_min + | ||
| 535 | - ran.uniform() * (m_mc_dety_max - m_mc_dety_min)) * | ||
| 536 | - gammalib::deg2rad; | 533 | + // Get randomized detx and dety in radians. If the direction falls |
| 534 | + // outside the validity range then get another pair of detx and | ||
| 535 | + // dety values. | ||
| 536 | + double theta = std::acos(1.0 - ran.uniform() * m_mc_one_minus_costheta); | ||
| 537 | + double phi = gammalib::twopi * ran.uniform(); | ||
| 538 | + double detx = theta * std::cos(phi); | ||
| 539 | + double dety = theta * std::sin(phi); | ||
| 540 | + if ((detx < m_detx_min) || (detx > m_detx_max) || | ||
| 541 | + (dety < m_dety_min) || (dety > m_dety_max)) { | ||
| 542 | + continue; | ||
| 543 | + } | ||
| 537 | 544 | ||
| 538 | - // Get background rate for these coordinates | 545 | + // Get background rate for these coordinates. If the background |
| 546 | + // rate is zero then get other coordinates. | ||
| 539 | double value = this->operator()(logE, detx, dety); | 547 | double value = this->operator()(logE, detx, dety); |
| 548 | + if (value <= 0.0) { | ||
| 549 | + continue; | ||
| 550 | + } | ||
| 540 | 551 | ||
| 541 | // Get uniform random number | 552 | // Get uniform random number |
| 542 | double uniform = ran.uniform() * max_rate; | 553 | double uniform = ran.uniform() * max_rate; |
| @@ -581,37 +592,30 @@ std::string GCTABackground3D::print(const GChatter& chatter) const | @@ -581,37 +592,30 @@ std::string GCTABackground3D::print(const GChatter& chatter) const | ||
| 581 | if (is_valid()) { | 592 | if (is_valid()) { |
| 582 | 593 | ||
| 583 | // Compute DETX boundaries in deg | 594 | // Compute DETX boundaries in deg |
| 584 | - double detx_min = m_background.axis_lo(m_inx_detx,0); | ||
| 585 | - double detx_max = m_background.axis_hi(m_inx_detx, | ||
| 586 | - m_background.axis_bins(m_inx_detx)-1); | 595 | + double detx_min = m_background.axis_lo(m_inx_detx, 0); |
| 596 | + double detx_max = m_background.axis_hi(m_inx_detx, m_num_detx-1); | ||
| 587 | 597 | ||
| 588 | // Compute DETY boundaries in deg | 598 | // Compute DETY boundaries in deg |
| 589 | double dety_min = m_background.axis_lo(m_inx_dety,0); | 599 | double dety_min = m_background.axis_lo(m_inx_dety,0); |
| 590 | - double dety_max = m_background.axis_hi(m_inx_dety, | ||
| 591 | - m_background.axis_bins(m_inx_dety)-1); | 600 | + double dety_max = m_background.axis_hi(m_inx_dety, m_num_dety-1); |
| 592 | 601 | ||
| 593 | // Compute energy boundaries in TeV | 602 | // Compute energy boundaries in TeV |
| 594 | - double emin = m_background.axis_lo(m_inx_energy,0); | ||
| 595 | - double emax = m_background.axis_hi(m_inx_energy, | ||
| 596 | - m_background.axis_bins(m_inx_energy)-1); | 603 | + double emin = m_background.axis_lo(m_inx_energy, 0); |
| 604 | + double emax = m_background.axis_hi(m_inx_energy, m_num_energy-1); | ||
| 597 | 605 | ||
| 598 | // Append information | 606 | // Append information |
| 599 | result.append("\n"+gammalib::parformat("Number of DETX bins") + | 607 | result.append("\n"+gammalib::parformat("Number of DETX bins") + |
| 600 | - gammalib::str(m_background.axis_bins(m_inx_detx))); | 608 | + gammalib::str(m_num_detx)); |
| 601 | result.append("\n"+gammalib::parformat("Number of DETY bins") + | 609 | result.append("\n"+gammalib::parformat("Number of DETY bins") + |
| 602 | - gammalib::str(m_background.axis_bins(m_inx_dety))); | 610 | + gammalib::str(m_num_dety)); |
| 603 | result.append("\n"+gammalib::parformat("Number of energy bins") + | 611 | result.append("\n"+gammalib::parformat("Number of energy bins") + |
| 604 | - gammalib::str(m_background.axis_bins(m_inx_energy))); | 612 | + gammalib::str(m_num_energy)); |
| 605 | result.append("\n"+gammalib::parformat("DETX range")); | 613 | result.append("\n"+gammalib::parformat("DETX range")); |
| 606 | result.append(gammalib::str(detx_min)+" - "+gammalib::str(detx_max)+" deg"); | 614 | result.append(gammalib::str(detx_min)+" - "+gammalib::str(detx_max)+" deg"); |
| 607 | result.append("\n"+gammalib::parformat("DETX range")); | 615 | result.append("\n"+gammalib::parformat("DETX range")); |
| 608 | result.append(gammalib::str(dety_min)+" - "+gammalib::str(dety_max)+" deg"); | 616 | result.append(gammalib::str(dety_min)+" - "+gammalib::str(dety_max)+" deg"); |
| 609 | result.append("\n"+gammalib::parformat("Energy range")); | 617 | result.append("\n"+gammalib::parformat("Energy range")); |
| 610 | result.append(gammalib::str(emin)+" - "+gammalib::str(emax)+" TeV"); | 618 | result.append(gammalib::str(emin)+" - "+gammalib::str(emax)+" TeV"); |
| 611 | - result.append("\n"+gammalib::parformat("Maximum bin size for MC")); | ||
| 612 | - result.append(gammalib::str(m_mc_max_bin)+" deg"); | ||
| 613 | - result.append("\n"+gammalib::parformat("Maximum logE step for MC")); | ||
| 614 | - result.append(gammalib::str(m_mc_max_logE)+"^10 TeV"); | ||
| 615 | 619 | ||
| 616 | } // endif: there were 3 axis | 620 | } // endif: there were 3 axis |
| 617 | 621 | ||
| @@ -646,25 +650,27 @@ void GCTABackground3D::init_members(void) | @@ -646,25 +650,27 @@ void GCTABackground3D::init_members(void) | ||
| 646 | // Initialise members | 650 | // Initialise members |
| 647 | m_filename.clear(); | 651 | m_filename.clear(); |
| 648 | m_background.clear(); | 652 | m_background.clear(); |
| 649 | - m_mc_max_bin = 0.05; //!< Spatial binning not worse than 0.05 deg | ||
| 650 | - m_mc_max_logE = 0.02; //!< Spectral binning not worse than 0.02^10 TeV | ||
| 651 | - m_inx_detx = 0; | ||
| 652 | - m_inx_dety = 1; | ||
| 653 | - m_inx_energy = 2; | ||
| 654 | - m_inx_bgd = 0; | 653 | + m_inx_detx = 0; |
| 654 | + m_inx_dety = 1; | ||
| 655 | + m_inx_energy = 2; | ||
| 656 | + m_inx_bgd = 0; | ||
| 657 | + m_num_detx = 0; | ||
| 658 | + m_num_dety = 0; | ||
| 659 | + m_num_energy = 0; | ||
| 660 | + m_num[0] = 0; | ||
| 661 | + m_num[1] = 0; | ||
| 662 | + m_num[2] = 0; | ||
| 663 | + m_detx_min = 0.0; | ||
| 664 | + m_detx_max = 0.0; | ||
| 665 | + m_dety_min = 0.0; | ||
| 666 | + m_dety_max = 0.0; | ||
| 667 | + m_logE_min = 0.0; | ||
| 668 | + m_logE_max = 0.0; | ||
| 655 | 669 | ||
| 656 | // Initialise MC cache | 670 | // Initialise MC cache |
| 657 | m_mc_max.clear(); | 671 | m_mc_max.clear(); |
| 658 | m_mc_spectrum.clear(); | 672 | m_mc_spectrum.clear(); |
| 659 | - m_mc_detx_min = 0.0; | ||
| 660 | - m_mc_detx_max = 0.0; | ||
| 661 | - m_mc_detx_bin = 0.0; | ||
| 662 | - m_mc_dety_min = 0.0; | ||
| 663 | - m_mc_dety_max = 0.0; | ||
| 664 | - m_mc_dety_bin = 0.0; | ||
| 665 | - m_mc_logE_min = 0.0; | ||
| 666 | - m_mc_logE_max = 0.0; | ||
| 667 | - m_mc_logE_bin = 0.0; | 673 | + m_mc_one_minus_costheta = 0.0; |
| 668 | 674 | ||
| 669 | // Return | 675 | // Return |
| 670 | return; | 676 | return; |
| @@ -679,27 +685,29 @@ void GCTABackground3D::init_members(void) | @@ -679,27 +685,29 @@ void GCTABackground3D::init_members(void) | ||
| 679 | void GCTABackground3D::copy_members(const GCTABackground3D& bgd) | 685 | void GCTABackground3D::copy_members(const GCTABackground3D& bgd) |
| 680 | { | 686 | { |
| 681 | // Copy members | 687 | // Copy members |
| 682 | - m_filename = bgd.m_filename; | ||
| 683 | - m_background = bgd.m_background; | ||
| 684 | - m_mc_max_bin = bgd.m_mc_max_bin; | ||
| 685 | - m_mc_max_logE = bgd.m_mc_max_logE; | ||
| 686 | - m_inx_detx = bgd.m_inx_detx; | ||
| 687 | - m_inx_dety = bgd.m_inx_dety; | ||
| 688 | - m_inx_energy = bgd.m_inx_energy; | ||
| 689 | - m_inx_bgd = bgd.m_inx_bgd; | 688 | + m_filename = bgd.m_filename; |
| 689 | + m_background = bgd.m_background; | ||
| 690 | + m_inx_detx = bgd.m_inx_detx; | ||
| 691 | + m_inx_dety = bgd.m_inx_dety; | ||
| 692 | + m_inx_energy = bgd.m_inx_energy; | ||
| 693 | + m_inx_bgd = bgd.m_inx_bgd; | ||
| 694 | + m_num_detx = bgd.m_num_detx; | ||
| 695 | + m_num_dety = bgd.m_num_dety; | ||
| 696 | + m_num_energy = bgd.m_num_energy; | ||
| 697 | + m_num[0] = bgd.m_num[0]; | ||
| 698 | + m_num[1] = bgd.m_num[1]; | ||
| 699 | + m_num[2] = bgd.m_num[2]; | ||
| 700 | + m_detx_min = bgd.m_detx_min; | ||
| 701 | + m_detx_max = bgd.m_detx_max; | ||
| 702 | + m_dety_min = bgd.m_dety_min; | ||
| 703 | + m_dety_max = bgd.m_dety_max; | ||
| 704 | + m_logE_min = bgd.m_logE_min; | ||
| 705 | + m_logE_max = bgd.m_logE_max; | ||
| 690 | 706 | ||
| 691 | // Copy MC cache | 707 | // Copy MC cache |
| 692 | - m_mc_max = bgd.m_mc_max; | ||
| 693 | - m_mc_spectrum = bgd.m_mc_spectrum; | ||
| 694 | - m_mc_detx_min = bgd.m_mc_detx_min; | ||
| 695 | - m_mc_detx_max = bgd.m_mc_detx_max; | ||
| 696 | - m_mc_detx_bin = bgd.m_mc_detx_bin; | ||
| 697 | - m_mc_dety_min = bgd.m_mc_dety_min; | ||
| 698 | - m_mc_dety_max = bgd.m_mc_dety_max; | ||
| 699 | - m_mc_dety_bin = bgd.m_mc_dety_bin; | ||
| 700 | - m_mc_logE_min = bgd.m_mc_logE_min; | ||
| 701 | - m_mc_logE_max = bgd.m_mc_logE_max; | ||
| 702 | - m_mc_logE_bin = bgd.m_mc_logE_bin; | 708 | + m_mc_max = bgd.m_mc_max; |
| 709 | + m_mc_spectrum = bgd.m_mc_spectrum; | ||
| 710 | + m_mc_one_minus_costheta = bgd.m_mc_one_minus_costheta; | ||
| 703 | 711 | ||
| 704 | // Return | 712 | // Return |
| 705 | return; | 713 | return; |
| @@ -707,6 +715,66 @@ void GCTABackground3D::copy_members(const GCTABackground3D& bgd) | @@ -707,6 +715,66 @@ void GCTABackground3D::copy_members(const GCTABackground3D& bgd) | ||
| 707 | 715 | ||
| 708 | 716 | ||
| 709 | /***********************************************************************//** | 717 | /***********************************************************************//** |
| 718 | + * @brief Set DETX, DETY and log10(E/TeV) limits | ||
| 719 | + * | ||
| 720 | + * Set the DETX, DETY and log10(E/TeV) limits. | ||
| 721 | + ***************************************************************************/ | ||
| 722 | +void GCTABackground3D::set_limits(void) | ||
| 723 | +{ | ||
| 724 | + // Compute DETX boundaries in radians | ||
| 725 | + m_detx_min = m_background.axis_lo(m_inx_detx, 0) * | ||
| 726 | + gammalib::deg2rad; | ||
| 727 | + m_detx_max = m_background.axis_hi(m_inx_detx, m_num_detx-1) * | ||
| 728 | + gammalib::deg2rad; | ||
| 729 | + | ||
| 730 | + // Compute DETY boundaries in radians | ||
| 731 | + m_dety_min = m_background.axis_lo(m_inx_dety, 0) * | ||
| 732 | + gammalib::deg2rad; | ||
| 733 | + m_dety_max = m_background.axis_hi(m_inx_dety, m_num_dety-1) * | ||
| 734 | + gammalib::deg2rad; | ||
| 735 | + | ||
| 736 | + // Compute energy boundaries in log10(TeV) | ||
| 737 | + GEnergy emin(m_background.axis_lo(m_inx_energy, 0), | ||
| 738 | + m_background.axis_lo_unit(m_inx_energy)); | ||
| 739 | + GEnergy emax(m_background.axis_hi(m_inx_energy, m_num_energy-1), | ||
| 740 | + m_background.axis_hi_unit(m_inx_energy)); | ||
| 741 | + m_logE_min = emin.log10TeV(); | ||
| 742 | + m_logE_max = emax.log10TeV(); | ||
| 743 | + | ||
| 744 | + // Return | ||
| 745 | + return; | ||
| 746 | +} | ||
| 747 | + | ||
| 748 | + | ||
| 749 | +/***********************************************************************//** | ||
| 750 | + * @brief Return background rate bin index | ||
| 751 | + * | ||
| 752 | + * @param[in] idetx DETX index. | ||
| 753 | + * @param[in] idety DETY index. | ||
| 754 | + * @param[in] iebin Energy index. | ||
| 755 | + * @return Background rate bin index. | ||
| 756 | + * | ||
| 757 | + * Returns the background rate bin index independent of the ordering. | ||
| 758 | + ***************************************************************************/ | ||
| 759 | +int GCTABackground3D::index(const int& idetx, | ||
| 760 | + const int& idety, | ||
| 761 | + const int& iebin) const | ||
| 762 | +{ | ||
| 763 | + // Set index arrays | ||
| 764 | + int inx[3]; | ||
| 765 | + inx[m_inx_detx] = idetx; | ||
| 766 | + inx[m_inx_dety] = idety; | ||
| 767 | + inx[m_inx_energy] = iebin; | ||
| 768 | + | ||
| 769 | + // Compute index | ||
| 770 | + int index = inx[0] + (inx[1] + inx[2] * m_num[1]) * m_num[0]; | ||
| 771 | + | ||
| 772 | + // Return index | ||
| 773 | + return index; | ||
| 774 | +} | ||
| 775 | + | ||
| 776 | + | ||
| 777 | +/***********************************************************************//** | ||
| 710 | * @brief Delete class members | 778 | * @brief Delete class members |
| 711 | ***************************************************************************/ | 779 | ***************************************************************************/ |
| 712 | void GCTABackground3D::free_members(void) | 780 | void GCTABackground3D::free_members(void) |
| @@ -719,15 +787,7 @@ void GCTABackground3D::free_members(void) | @@ -719,15 +787,7 @@ void GCTABackground3D::free_members(void) | ||
| 719 | /***********************************************************************//** | 787 | /***********************************************************************//** |
| 720 | * @brief Initialise Monte Carlo cache | 788 | * @brief Initialise Monte Carlo cache |
| 721 | * | 789 | * |
| 722 | - * @exception GException::invalid_value | ||
| 723 | - * No valid background model defined. | ||
| 724 | - * | ||
| 725 | * Initialises the cache for Monte Carlo sampling. | 790 | * Initialises the cache for Monte Carlo sampling. |
| 726 | - * | ||
| 727 | - * @todo Verify assumption made about the solid angles of the response table | ||
| 728 | - * elements. | ||
| 729 | - * @todo Implement method that computes the "flux" in a background map | ||
| 730 | - * pixel (inspired from GSkyMap::flux()) | ||
| 731 | ***************************************************************************/ | 791 | ***************************************************************************/ |
| 732 | void GCTABackground3D::init_mc_cache(void) const | 792 | void GCTABackground3D::init_mc_cache(void) const |
| 733 | { | 793 | { |
| @@ -735,125 +795,243 @@ void GCTABackground3D::init_mc_cache(void) const | @@ -735,125 +795,243 @@ void GCTABackground3D::init_mc_cache(void) const | ||
| 735 | m_mc_max.clear(); | 795 | m_mc_max.clear(); |
| 736 | m_mc_spectrum.clear(); | 796 | m_mc_spectrum.clear(); |
| 737 | 797 | ||
| 738 | - // Determine number of response cube pixels and maps | ||
| 739 | - int nx = m_background.axis_bins(m_inx_detx); | ||
| 740 | - int ny = m_background.axis_bins(m_inx_dety); | ||
| 741 | - int npix = nx * ny; | ||
| 742 | - int nmaps = m_background.axis_bins(m_inx_energy); | ||
| 743 | - | ||
| 744 | - // Compute DETX boundaries and binsize in degress | ||
| 745 | - m_mc_detx_min = m_background.axis_lo(m_inx_detx,0); | ||
| 746 | - m_mc_detx_max = m_background.axis_hi(m_inx_detx,nx-1); | ||
| 747 | - m_mc_detx_bin = (m_mc_detx_max - m_mc_detx_min) / nx; | 798 | + // Initialise maximum rate array |
| 799 | + init_mc_max_rate(); | ||
| 800 | + | ||
| 801 | + // Compute 1-cos(theta_max) | ||
| 802 | + double theta1 = std::sqrt(m_detx_min * m_detx_min + m_dety_min * m_dety_min); | ||
| 803 | + double theta2 = std::sqrt(m_detx_min * m_detx_min + m_dety_max * m_dety_max); | ||
| 804 | + double theta3 = std::sqrt(m_detx_max * m_detx_max + m_dety_min * m_dety_min); | ||
| 805 | + double theta4 = std::sqrt(m_detx_max * m_detx_max + m_dety_max * m_dety_max); | ||
| 806 | + double theta = theta1; | ||
| 807 | + if (theta2 > theta) { | ||
| 808 | + theta = theta2; | ||
| 809 | + } | ||
| 810 | + if (theta3 > theta) { | ||
| 811 | + theta = theta3; | ||
| 812 | + } | ||
| 813 | + if (theta4 > theta) { | ||
| 814 | + theta = theta4; | ||
| 815 | + } | ||
| 816 | + m_mc_one_minus_costheta = 1.0 - std::cos(theta); | ||
| 748 | 817 | ||
| 749 | - // Compute DETY boundaries and binsize in degress | ||
| 750 | - m_mc_dety_min = m_background.axis_lo(m_inx_dety,0); | ||
| 751 | - m_mc_dety_max = m_background.axis_hi(m_inx_dety,ny-1); | ||
| 752 | - m_mc_dety_bin = (m_mc_dety_max - m_mc_dety_min) / ny; | 818 | + // Set number of energy bins. Make sure that we compute at least 100 energy |
| 819 | + // bins | ||
| 820 | + int nbins = m_num_energy * 10; | ||
| 821 | + if (nbins < 100) { | ||
| 822 | + nbins = 100; | ||
| 823 | + } | ||
| 753 | 824 | ||
| 754 | - // Compute energy boundaries and binsize in log10(TeV) | ||
| 755 | - GEnergy emin(m_background.axis_lo(m_inx_energy,0), | ||
| 756 | - m_background.axis_lo_unit(m_inx_energy)); | ||
| 757 | - GEnergy emax(m_background.axis_hi(m_inx_energy,nmaps-1), | ||
| 758 | - m_background.axis_hi_unit(m_inx_energy)); | ||
| 759 | - m_mc_logE_min = emin.log10TeV(); | ||
| 760 | - m_mc_logE_max = emax.log10TeV(); | ||
| 761 | - m_mc_logE_bin = (m_mc_logE_max - m_mc_logE_min) / nmaps; | ||
| 762 | - | ||
| 763 | - // Determine solid angle of the pixel. We assume here simply that we | ||
| 764 | - // have square pixels of identical solid angle. It needs to be checked | ||
| 765 | - // whether this is a valid assumption. | ||
| 766 | - double solidangle = m_mc_detx_bin * gammalib::deg2rad * | ||
| 767 | - m_mc_dety_bin * gammalib::deg2rad; | ||
| 768 | - | ||
| 769 | - // Compute flux normalisation factor. As we add-up 3 intensity we have | ||
| 770 | - // do divide by 3, and as we add up 8 wedges we have to divide the | ||
| 771 | - // solid angle by 8. | ||
| 772 | - double flux_norm = gammalib::onethird * solidangle / 8.0; | ||
| 773 | - | ||
| 774 | - // Continue only if there are pixels and maps | ||
| 775 | - if (npix > 0 && nmaps > 0) { | ||
| 776 | - | ||
| 777 | - // Loop over all maps | ||
| 778 | - for (int i = 0; i < nmaps; ++i) { | ||
| 779 | - | ||
| 780 | - // Get logE | ||
| 781 | - double logE = m_mc_logE_min + (i+0.5) * m_mc_logE_bin; | ||
| 782 | - | ||
| 783 | - // Initialise cache with cumulative pixel fluxes and compute | ||
| 784 | - // total flux in response table for normalization. Negative | ||
| 785 | - // pixels are excluded from the cumulative map. | ||
| 786 | - double total_flux = 0.0; // units: events/s/MeV | ||
| 787 | - double max_rate = 0.0; // units: events/s/sr/MeV | ||
| 788 | - double xmin = m_mc_detx_min * gammalib::deg2rad; | ||
| 789 | - double xbin = m_mc_detx_bin * gammalib::deg2rad; | ||
| 790 | - double ymin = m_mc_dety_min * gammalib::deg2rad; | ||
| 791 | - double ybin = m_mc_dety_bin * gammalib::deg2rad; | ||
| 792 | - double dx = 0.5 * xbin; | ||
| 793 | - double dy = 0.5 * ybin; | ||
| 794 | - double detx = xmin + 0.5 * xbin; | ||
| 795 | - for (int ix = 0; ix < nx; ++ix, detx += xbin) { | ||
| 796 | - double dety = ymin + 0.5 * ybin; | ||
| 797 | - for (int iy = 0; iy < ny; ++iy, dety += ybin) { | ||
| 798 | - | ||
| 799 | - // Compute flux by summing the flux in 8 pixel wedges | ||
| 800 | - double i0 = (*this)(logE, detx, dety); | ||
| 801 | - double i1 = (*this)(logE, detx-dx, dety-dy); | ||
| 802 | - double i2 = (*this)(logE, detx, dety-dy); | ||
| 803 | - double i3 = (*this)(logE, detx+dx, dety-dy); | ||
| 804 | - double i4 = (*this)(logE, detx+dx, dety); | ||
| 805 | - double i5 = (*this)(logE, detx+dx, dety+dy); | ||
| 806 | - double i6 = (*this)(logE, detx, dety+dy); | ||
| 807 | - double i7 = (*this)(logE, detx-dx, dety+dy); | ||
| 808 | - double i8 = (*this)(logE, detx-dx, dety); | ||
| 809 | - double flux1 = flux_norm * (i1 + i2 + i0); | ||
| 810 | - double flux2 = flux_norm * (i2 + i3 + i0); | ||
| 811 | - double flux3 = flux_norm * (i3 + i4 + i0); | ||
| 812 | - double flux4 = flux_norm * (i4 + i5 + i0); | ||
| 813 | - double flux5 = flux_norm * (i5 + i6 + i0); | ||
| 814 | - double flux6 = flux_norm * (i6 + i7 + i0); | ||
| 815 | - double flux7 = flux_norm * (i7 + i8 + i0); | ||
| 816 | - double flux8 = flux_norm * (i8 + i1 + i0); | ||
| 817 | - double flux = (flux1 + flux2 + flux3 + flux4 + | ||
| 818 | - flux5 + flux6 + flux7 + flux8); | ||
| 819 | - | ||
| 820 | - // Sum flux | ||
| 821 | - if (flux > 0.0) { | ||
| 822 | - total_flux += flux; | ||
| 823 | - } | ||
| 824 | - | ||
| 825 | - // Get maximum rate | ||
| 826 | - if (i0 > max_rate) { | ||
| 827 | - max_rate = i0; | ||
| 828 | - } | 825 | + // Compute DETX and DETY binsize in radians and energy binsize in log10(TeV) |
| 826 | + double detx_bin = (m_detx_max - m_detx_min) / m_num_detx; | ||
| 827 | + double dety_bin = (m_dety_max - m_dety_min) / m_num_dety; | ||
| 828 | + double logE_bin = (m_logE_max - m_logE_min) / (nbins+1); | ||
| 829 | + | ||
| 830 | + // Loop over all energy bins | ||
| 831 | + for (int i = 0; i < nbins; ++i) { | ||
| 832 | + | ||
| 833 | + // Get logE of energy bin | ||
| 834 | + double logE = m_logE_min + i * logE_bin; | ||
| 835 | + | ||
| 836 | + // Initialise cache with cumulative pixel fluxes and compute total flux | ||
| 837 | + // in response table for normalization. Negative pixels are excluded | ||
| 838 | + // from the cumulative map. | ||
| 839 | + double total_flux = 0.0; // units: events/s/MeV | ||
| 840 | + double dx = 0.5 * detx_bin; | ||
| 841 | + double dy = 0.5 * dety_bin; | ||
| 842 | + double detx = m_detx_min + dx; | ||
| 843 | + for (int ix = 0; ix < m_num_detx; ++ix, detx += detx_bin) { | ||
| 844 | + double dety = m_dety_min + dy; | ||
| 845 | + for (int iy = 0; iy < m_num_dety; ++iy, dety += dety_bin) { | ||
| 846 | + | ||
| 847 | + // Compute intensities | ||
| 848 | + double i0 = (*this)(logE, detx, dety); | ||
| 849 | + double i1 = (*this)(logE, detx-dx, dety-dy); | ||
| 850 | + double i2 = (*this)(logE, detx, dety-dy); | ||
| 851 | + double i3 = (*this)(logE, detx+dx, dety-dy); | ||
| 852 | + double i4 = (*this)(logE, detx+dx, dety); | ||
| 853 | + double i5 = (*this)(logE, detx+dx, dety+dy); | ||
| 854 | + double i6 = (*this)(logE, detx, dety+dy); | ||
| 855 | + double i7 = (*this)(logE, detx-dx, dety+dy); | ||
| 856 | + double i8 = (*this)(logE, detx-dx, dety); | ||
| 857 | + | ||
| 858 | + // Compute solid angle of the 8 pixel wedges | ||
| 859 | + double s1 = solid_angle(detx, dety, detx-dx, dety-dy, detx, dety-dy); | ||
| 860 | + double s2 = solid_angle(detx, dety, detx, dety-dy, detx+dx, dety-dy); | ||
| 861 | + double s3 = solid_angle(detx, dety, detx+dx, dety-dy, detx+dx, dety); | ||
| 862 | + double s4 = solid_angle(detx, dety, detx+dx, dety, detx+dx, dety+dy); | ||
| 863 | + double s5 = solid_angle(detx, dety, detx+dx, dety+dy, detx, dety+dy); | ||
| 864 | + double s6 = solid_angle(detx, dety, detx, dety+dy, detx-dx, dety+dy); | ||
| 865 | + double s7 = solid_angle(detx, dety, detx-dx, dety+dy, detx-dx, dety); | ||
| 866 | + double s8 = solid_angle(detx, dety, detx-dx, dety, detx-dx, dety-dy); | ||
| 867 | + | ||
| 868 | + // Compute flux by summing the flux in 8 pixel wedges | ||
| 869 | + double flux1 = s1 * (i1 + i2 + i0); | ||
| 870 | + double flux2 = s2 * (i2 + i3 + i0); | ||
| 871 | + double flux3 = s3 * (i3 + i4 + i0); | ||
| 872 | + double flux4 = s4 * (i4 + i5 + i0); | ||
| 873 | + double flux5 = s5 * (i5 + i6 + i0); | ||
| 874 | + double flux6 = s6 * (i6 + i7 + i0); | ||
| 875 | + double flux7 = s7 * (i7 + i8 + i0); | ||
| 876 | + double flux8 = s8 * (i8 + i1 + i0); | ||
| 877 | + double flux = (flux1 + flux2 + flux3 + flux4 + | ||
| 878 | + flux5 + flux6 + flux7 + flux8) / 3.0; | ||
| 879 | + | ||
| 880 | + // Sum flux | ||
| 881 | + if (flux > 0.0) { | ||
| 882 | + total_flux += flux; | ||
| 829 | } | 883 | } |
| 830 | - } | ||
| 831 | 884 | ||
| 832 | - // Append maximum rate | ||
| 833 | - m_mc_max.push_back(max_rate); | 885 | + } // endfor: loop over DETY |
| 834 | 886 | ||
| 835 | - // Set energy value (unit independent) | ||
| 836 | - GEnergy energy; | ||
| 837 | - energy.log10(logE, m_background.axis_lo_unit(m_inx_energy)); | 887 | + } // endfor: loop over DETX |
| 838 | 888 | ||
| 839 | - // Only append node if flux is positive | ||
| 840 | - if (total_flux > 0.0) { | ||
| 841 | - m_mc_spectrum.append(energy, total_flux); | ||
| 842 | - } | 889 | + // Set energy value (unit independent) |
| 890 | + GEnergy energy; | ||
| 891 | + energy.log10(logE, m_background.axis_lo_unit(m_inx_energy)); | ||
| 843 | 892 | ||
| 844 | - // Dump spectrum for debugging | ||
| 845 | - #if defined(G_DEBUG_MC_INIT) | ||
| 846 | - std::cout << "Energy=" << energy; | ||
| 847 | - std::cout << " Rate_max=" << max_rate; | ||
| 848 | - std::cout << " events/s/sr/MeV"; | ||
| 849 | - std::cout << " Rate_total=" << total_flux; | ||
| 850 | - std::cout << " events/s/MeV" << std::endl; | ||
| 851 | - #endif | 893 | + // Only append node if flux is positive |
| 894 | + if (total_flux > 0.0) { | ||
| 895 | + m_mc_spectrum.append(energy, total_flux); | ||
| 896 | + } | ||
| 852 | 897 | ||
| 853 | - } // endfor: looped over all maps | 898 | + // Dump spectrum for debugging |
| 899 | + #if defined(G_DEBUG_MC_INIT) | ||
| 900 | + std::cout << "Energy=" << energy; | ||
| 901 | + std::cout << " Rate_total=" << total_flux; | ||
| 902 | + std::cout << " events/s/MeV" << std::endl; | ||
| 903 | + #endif | ||
| 904 | + | ||
| 905 | + } // endfor: looped over energy bins | ||
| 906 | + | ||
| 907 | + // Dump maximum rate | ||
| 908 | + #if defined(G_DEBUG_MC_INIT) | ||
| 909 | + for (int i = 0; i < m_mc_max.size(); ++i) { | ||
| 910 | + double logE = m_logE_min + (i+0.5) * logE_bin; | ||
| 911 | + GEnergy energy; | ||
| 912 | + energy.log10(logE, m_background.axis_lo_unit(m_inx_energy)); | ||
| 913 | + std::cout << "Energy=" << energy; | ||
| 914 | + std::cout << " Rate_max=" << max_rate; | ||
| 915 | + std::cout << " events/s/sr/MeV"; | ||
| 916 | + } | ||
| 917 | + #endif | ||
| 854 | 918 | ||
| 855 | - } // endif: there were cube pixels and maps | 919 | + // Return |
| 920 | + return; | ||
| 921 | +} | ||
| 922 | + | ||
| 923 | + | ||
| 924 | +/***********************************************************************//** | ||
| 925 | + * @brief Initialise array of maximum background rate | ||
| 926 | + * | ||
| 927 | + * Initialises the array m_mc_max that holds the maximum background rate in | ||
| 928 | + * each DETX-DETY plane of the background model. | ||
| 929 | + ***************************************************************************/ | ||
| 930 | +void GCTABackground3D::init_mc_max_rate(void) const | ||
| 931 | +{ | ||
| 932 | + // Initialise cache | ||
| 933 | + m_mc_max.clear(); | ||
| 934 | + | ||
| 935 | + // Loop over all maps to determine the maximum map intensity in units | ||
| 936 | + // of events/s/sr/MeV | ||
| 937 | + for (int iebin = 0; iebin < m_num_energy; ++iebin) { | ||
| 938 | + | ||
| 939 | + // Initialise maximum rate | ||
| 940 | + double max_rate = 0.0; | ||
| 941 | + | ||
| 942 | + // Loop over DETX-DETY plane | ||
| 943 | + for (int ix = 0; ix < m_num_detx; ++ix) { | ||
| 944 | + for (int iy = 0; iy < m_num_dety; ++iy) { | ||
| 945 | + | ||
| 946 | + // Get bin index | ||
| 947 | + int inx = index(ix, iy, iebin); | ||
| 948 | + | ||
| 949 | + // Get background rate | ||
| 950 | + double rate = m_background(m_inx_bgd, inx); | ||
| 951 | + | ||
| 952 | + // If background rate is larger than maximum then store the | ||
| 953 | + // background rate as new maximum | ||
| 954 | + if (rate > max_rate) { | ||
| 955 | + max_rate = rate; | ||
| 956 | + } | ||
| 957 | + | ||
| 958 | + } // endfor: looped over DETY | ||
| 959 | + } // endfor: looped over DETX | ||
| 960 | + | ||
| 961 | + // Append maximum rate | ||
| 962 | + m_mc_max.push_back(max_rate); | ||
| 963 | + | ||
| 964 | + } // endif: looped over all maps | ||
| 856 | 965 | ||
| 857 | // Return | 966 | // Return |
| 858 | return; | 967 | return; |
| 859 | } | 968 | } |
| 969 | + | ||
| 970 | + | ||
| 971 | +/***********************************************************************//** | ||
| 972 | + * @brief Compute solid angle of pixel wedge | ||
| 973 | + * | ||
| 974 | + * @param[in] detx1 DETX of first edge. | ||
| 975 | + * @param[in] dety1 DETY of first edge. | ||
| 976 | + * @param[in] detx2 DETX of second edge. | ||
| 977 | + * @param[in] dety2 DETY of second edge. | ||
| 978 | + * @param[in] detx3 DETX of third edge. | ||
| 979 | + * @param[in] dety3 DETY of third edge. | ||
| 980 | + * @return Solid angle (steradians). | ||
| 981 | + * | ||
| 982 | + * Estimate the solid angle subtended by 3 coordinates using Huilier's | ||
| 983 | + * theorem. | ||
| 984 | + * | ||
| 985 | + * Below, the definiton of the pixel cornes and sides are shown as used | ||
| 986 | + * within the code. | ||
| 987 | + * | ||
| 988 | + * a12 | ||
| 989 | + * 1---------2 | ||
| 990 | + * | / | ||
| 991 | + * | / | ||
| 992 | + * | / | ||
| 993 | + * | / | ||
| 994 | + * a13| /a23 | ||
| 995 | + * | / | ||
| 996 | + * | / | ||
| 997 | + * | / | ||
| 998 | + * |/ | ||
| 999 | + * 3 | ||
| 1000 | + * | ||
| 1001 | + ***************************************************************************/ | ||
| 1002 | +double GCTABackground3D::solid_angle(const double& detx1, const double& dety1, | ||
| 1003 | + const double& detx2, const double& dety2, | ||
| 1004 | + const double& detx3, const double& dety3) const | ||
| 1005 | +{ | ||
| 1006 | + // Initialise solid angle | ||
| 1007 | + double solidangle = 0.0; | ||
| 1008 | + | ||
| 1009 | + // Set sky directions | ||
| 1010 | + double theta1 = std::sqrt(detx1 * detx1 + dety1 * dety1); | ||
| 1011 | + double phi1 = std::atan2(dety1, detx1); | ||
| 1012 | + double theta2 = std::sqrt(detx2 * detx2 + dety2 * dety2); | ||
| 1013 | + double phi2 = std::atan2(dety2, detx2); | ||
| 1014 | + double theta3 = std::sqrt(detx3 * detx3 + dety3 * dety3); | ||
| 1015 | + double phi3 = std::atan2(dety3, detx3); | ||
| 1016 | + GSkyDir dir1; | ||
| 1017 | + GSkyDir dir2; | ||
| 1018 | + GSkyDir dir3; | ||
| 1019 | + dir1.radec(phi1, gammalib::pihalf - theta1); | ||
| 1020 | + dir2.radec(phi2, gammalib::pihalf - theta2); | ||
| 1021 | + dir3.radec(phi3, gammalib::pihalf - theta3); | ||
| 1022 | + | ||
| 1023 | + // Compute angular distances between pixel corners | ||
| 1024 | + double a12 = dir1.dist(dir2); | ||
| 1025 | + double a13 = dir1.dist(dir3); | ||
| 1026 | + double a23 = dir2.dist(dir3); | ||
| 1027 | + | ||
| 1028 | + // Compute solid angle | ||
| 1029 | + double s = 0.5 * (a12 + a23 + a13); | ||
| 1030 | + solidangle = 4.0 * std::atan(std::sqrt(std::tan(0.5*s) * | ||
| 1031 | + std::tan(0.5*(s-a12)) * | ||
| 1032 | + std::tan(0.5*(s-a23)) * | ||
| 1033 | + std::tan(0.5*(s-a13)))); | ||
| 1034 | + | ||
| 1035 | + // Return solid angle | ||
| 1036 | + return solidangle; | ||
| 1037 | +} |
inst/cta/src/GCTAModelIrfBackground.cpp
| @@ -659,7 +659,7 @@ GCTAEventList* GCTAModelIrfBackground::mc(const GObservation& obs, GRan& ran) co | @@ -659,7 +659,7 @@ GCTAEventList* GCTAModelIrfBackground::mc(const GObservation& obs, GRan& ran) co | ||
| 659 | for (int i = 0; i < spectral.nodes(); ++i) { | 659 | for (int i = 0; i < spectral.nodes(); ++i) { |
| 660 | GEnergy energy = spectral.energy(i); | 660 | GEnergy energy = spectral.energy(i); |
| 661 | double intensity = spectral.intensity(i); | 661 | double intensity = spectral.intensity(i); |
| 662 | - double norm = m_spectral->eval(energy, events->tstart()); | 662 | + double norm = m_spectral->eval(energy); |
| 663 | spectral.intensity(i, norm*intensity); | 663 | spectral.intensity(i, norm*intensity); |
| 664 | } | 664 | } |
| 665 | 665 |
inst/cta/src/GCTAResponseIrf.cpp
| @@ -634,7 +634,7 @@ GCTAEventAtom* GCTAResponseIrf::mc(const double& area, const GPhoton& photon, | @@ -634,7 +634,7 @@ GCTAEventAtom* GCTAResponseIrf::mc(const double& area, const GPhoton& photon, | ||
| 634 | } | 634 | } |
| 635 | 635 | ||
| 636 | // Continue only if event is detected | 636 | // Continue only if event is detected |
| 637 | - if (ran.uniform() <= ulimite) { | 637 | + if ((ulimite > 0.0) && (ran.uniform() <= ulimite)) { |
| 638 | 638 | ||
| 639 | // Apply deadtime correction | 639 | // Apply deadtime correction |
| 640 | double deadc = obs.deadc(photon.time()); | 640 | double deadc = obs.deadc(photon.time()); |
src/sky/GSkyMap.cpp
| @@ -2802,7 +2802,7 @@ GFitsImageDouble* GSkyMap::create_wcs_hdu(void) const | @@ -2802,7 +2802,7 @@ GFitsImageDouble* GSkyMap::create_wcs_hdu(void) const | ||
| 2802 | * @return Solid angle (steradians). | 2802 | * @return Solid angle (steradians). |
| 2803 | * | 2803 | * |
| 2804 | * Estimate the solid angle subtended by 4 sky directions using Huilier's | 2804 | * Estimate the solid angle subtended by 4 sky directions using Huilier's |
| 2805 | - * theroem. | 2805 | + * theorem. |
| 2806 | * | 2806 | * |
| 2807 | * Below, the definiton of the pixel cornes and sides are shown as used | 2807 | * Below, the definiton of the pixel cornes and sides are shown as used |
| 2808 | * within the code. | 2808 | * within the code. |
| @@ -2891,7 +2891,7 @@ double GSkyMap::solidangle(const GSkyDir& dir1, const GSkyDir& dir2, | @@ -2891,7 +2891,7 @@ double GSkyMap::solidangle(const GSkyDir& dir1, const GSkyDir& dir2, | ||
| 2891 | * @return Solid angle (steradians). | 2891 | * @return Solid angle (steradians). |
| 2892 | * | 2892 | * |
| 2893 | * Estimate the solid angle subtended by 3 sky directions using Huilier's | 2893 | * Estimate the solid angle subtended by 3 sky directions using Huilier's |
| 2894 | - * theroem. | 2894 | + * theorem. |
| 2895 | * | 2895 | * |
| 2896 | * Below, the definiton of the pixel cornes and sides are shown as used | 2896 | * Below, the definiton of the pixel cornes and sides are shown as used |
| 2897 | * within the code. | 2897 | * within the code. |