Commit ba9b66ded85bdbf2a8d77fb8977dbc9a814d8b72

Authored by Jürgen Knödlseder
1 parent c31806e6

Fix consistency problem in GCTABackground3D class

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)
@@ -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&amp; GCTABackground3D::operator=(const GCTABackground3D&amp; bgd) @@ -174,27 +174,28 @@ GCTABackground3D&amp; GCTABackground3D::operator=(const GCTABackground3D&amp; 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&amp; logE, @@ -206,46 +207,43 @@ double GCTABackground3D::operator()(const double&amp; 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&amp; table) @@ -338,12 +336,6 @@ void GCTABackground3D::read(const GFitsTable&amp; 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&amp; table) @@ -354,6 +346,22 @@ void GCTABackground3D::read(const GFitsTable&amp; 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&amp; table) @@ -361,6 +369,9 @@ void GCTABackground3D::read(const GFitsTable&amp; 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&amp; energy, @@ -484,7 +495,7 @@ GCTAInstDir GCTABackground3D::mc(const GEnergy&amp; 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&amp; energy, @@ -508,35 +519,35 @@ GCTAInstDir GCTABackground3D::mc(const GEnergy&amp; 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&amp; chatter) const @@ -581,37 +592,30 @@ std::string GCTABackground3D::print(const GChatter&amp; 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&amp; bgd) @@ -707,6 +715,66 @@ void GCTABackground3D::copy_members(const GCTABackground3D&amp; 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&amp; obs, GRan&amp; ran) co @@ -659,7 +659,7 @@ GCTAEventList* GCTAModelIrfBackground::mc(const GObservation&amp; obs, GRan&amp; 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&amp; area, const GPhoton&amp; photon, @@ -634,7 +634,7 @@ GCTAEventAtom* GCTAResponseIrf::mc(const double&amp; area, const GPhoton&amp; 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&amp; dir1, const GSkyDir&amp; dir2, @@ -2891,7 +2891,7 @@ double GSkyMap::solidangle(const GSkyDir&amp; dir1, const GSkyDir&amp; 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.