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 3 * Version 1.1.0 released
4 4 =========================
5 5  
  6 + Fix consistency problem in GCTABackground3D (#1815)
6 7 Models accessing file now support relative file names (#1808)
7 8 Add GXmlElement and GXmlDocument filename() methods (#1808)
8 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 11 Include examples in unit tests (#1803)
11 12 Handle additional columns in GArf class
12 13 GCTAAeff2D no longer extrapolates beyond response table (#1784)
... ... @@ -56,8 +57,8 @@
56 57 Add GFilename::fullname() method
57 58 Add GCTAEventList::remove() method
58 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 62 Use GFilename in GFits to store filename (#1649)
62 63 Load CTA GTI extension from the HDU name provided by the DSS keyword (#1598)
63 64 Support Fermi LAT Pass 8 response functions (#1517)
... ...
... ... @@ -2,7 +2,7 @@ New Features and Important Changes in GammaLib 1.1.0
2 2  
3 3 Author(s) : Juergen Knoedlseder
4 4  
5   -12 July 2016
  5 +18 July 2016
6 6  
7 7  
8 8 1. Introduction
... ... @@ -440,6 +440,10 @@ handled by the weight array (#1789).
440 440 The GCTAAeff2D class no longer extrapolates the effective area beyond the boundaries
441 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 448 21. COMPTEL interface
445 449 ---------------------
... ...
inst/cta/include/GCTABackground3D.hpp
... ... @@ -83,33 +83,39 @@ public:
83 83  
84 84 private:
85 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 97 // Members
92 98 GFilename m_filename; //!< Name of background response file
93 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 100 int m_inx_detx; //!< DETX index
97 101 int m_inx_dety; //!< DETY index
98 102 int m_inx_energy; //!< Energy index
99 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 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 174 * @brief Return background rate in units of events/s/MeV/sr
175 175 *
176 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 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 188 double GCTABackground3D::operator()(const double& logE,
190 189 const double& detx,
191 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 200 // Retrieve references to node arrays
200 201 const GNodeArray& detx_nodes = m_background.axis_nodes(m_inx_detx);
... ... @@ -206,46 +207,43 @@ double GCTABackground3D::operator()(const double&amp; logE,
206 207 dety_nodes.set_value(dety);
207 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 210 // Set weighting factors for bi-linear interpolation in DETX-DETY plane
221 211 double wgt_ll = detx_nodes.wgt_left() * dety_nodes.wgt_left();
222 212 double wgt_lr = detx_nodes.wgt_left() * dety_nodes.wgt_right();
223 213 double wgt_rl = detx_nodes.wgt_right() * dety_nodes.wgt_left();
224 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 216 // Set weighting factors for energy interpolation
231 217 double wgt_emin = energy_nodes.wgt_left();
232 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 248 // If both rates are positive then perform a logarithmic interpolation
251 249 // in energy
... ... @@ -338,12 +336,6 @@ void GCTABackground3D::read(const GFitsTable&amp; table)
338 336 // Read background table
339 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 339 // Throw an exception if the table is not three-dimensional
348 340 if (m_background.axes() != 3) {
349 341 std::string msg = "Expected three-dimensional background "
... ... @@ -354,6 +346,22 @@ void GCTABackground3D::read(const GFitsTable&amp; table)
354 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 365 // Set DETX and DETY axis to radians
358 366 m_background.axis_radians(m_inx_detx);
359 367 m_background.axis_radians(m_inx_dety);
... ... @@ -361,6 +369,9 @@ void GCTABackground3D::read(const GFitsTable&amp; table)
361 369 // Set energy axis to logarithmic scale
362 370 m_background.axis_log10(m_inx_energy);
363 371  
  372 + // Set DETX and DETY limits
  373 + set_limits();
  374 +
364 375 // Return
365 376 return;
366 377 }
... ... @@ -484,7 +495,7 @@ GCTAInstDir GCTABackground3D::mc(const GEnergy&amp; energy,
484 495 const GTime& time,
485 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 499 if (m_mc_max.empty()) {
489 500 init_mc_cache();
490 501 }
... ... @@ -508,35 +519,35 @@ GCTAInstDir GCTABackground3D::mc(const GEnergy&amp; energy,
508 519 int inx_left = energy_nodes.inx_left();
509 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 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 547 double value = this->operator()(logE, detx, dety);
  548 + if (value <= 0.0) {
  549 + continue;
  550 + }
540 551  
541 552 // Get uniform random number
542 553 double uniform = ran.uniform() * max_rate;
... ... @@ -581,37 +592,30 @@ std::string GCTABackground3D::print(const GChatter&amp; chatter) const
581 592 if (is_valid()) {
582 593  
583 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 598 // Compute DETY boundaries in deg
589 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 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 606 // Append information
599 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 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 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 613 result.append("\n"+gammalib::parformat("DETX range"));
606 614 result.append(gammalib::str(detx_min)+" - "+gammalib::str(detx_max)+" deg");
607 615 result.append("\n"+gammalib::parformat("DETX range"));
608 616 result.append(gammalib::str(dety_min)+" - "+gammalib::str(dety_max)+" deg");
609 617 result.append("\n"+gammalib::parformat("Energy range"));
610 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 620 } // endif: there were 3 axis
617 621  
... ... @@ -646,25 +650,27 @@ void GCTABackground3D::init_members(void)
646 650 // Initialise members
647 651 m_filename.clear();
648 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 670 // Initialise MC cache
657 671 m_mc_max.clear();
658 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 675 // Return
670 676 return;
... ... @@ -679,27 +685,29 @@ void GCTABackground3D::init_members(void)
679 685 void GCTABackground3D::copy_members(const GCTABackground3D& bgd)
680 686 {
681 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 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 712 // Return
705 713 return;
... ... @@ -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 778 * @brief Delete class members
711 779 ***************************************************************************/
712 780 void GCTABackground3D::free_members(void)
... ... @@ -719,15 +787,7 @@ void GCTABackground3D::free_members(void)
719 787 /***********************************************************************//**
720 788 * @brief Initialise Monte Carlo cache
721 789 *
722   - * @exception GException::invalid_value
723   - * No valid background model defined.
724   - *
725 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 792 void GCTABackground3D::init_mc_cache(void) const
733 793 {
... ... @@ -735,125 +795,243 @@ void GCTABackground3D::init_mc_cache(void) const
735 795 m_mc_max.clear();
736 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 966 // Return
858 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 659 for (int i = 0; i < spectral.nodes(); ++i) {
660 660 GEnergy energy = spectral.energy(i);
661 661 double intensity = spectral.intensity(i);
662   - double norm = m_spectral->eval(energy, events->tstart());
  662 + double norm = m_spectral->eval(energy);
663 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 634 }
635 635  
636 636 // Continue only if event is detected
637   - if (ran.uniform() <= ulimite) {
  637 + if ((ulimite > 0.0) && (ran.uniform() <= ulimite)) {
638 638  
639 639 // Apply deadtime correction
640 640 double deadc = obs.deadc(photon.time());
... ...
src/sky/GSkyMap.cpp
... ... @@ -2802,7 +2802,7 @@ GFitsImageDouble* GSkyMap::create_wcs_hdu(void) const
2802 2802 * @return Solid angle (steradians).
2803 2803 *
2804 2804 * Estimate the solid angle subtended by 4 sky directions using Huilier's
2805   - * theroem.
  2805 + * theorem.
2806 2806 *
2807 2807 * Below, the definiton of the pixel cornes and sides are shown as used
2808 2808 * within the code.
... ... @@ -2891,7 +2891,7 @@ double GSkyMap::solidangle(const GSkyDir&amp; dir1, const GSkyDir&amp; dir2,
2891 2891 * @return Solid angle (steradians).
2892 2892 *
2893 2893 * Estimate the solid angle subtended by 3 sky directions using Huilier's
2894   - * theroem.
  2894 + * theorem.
2895 2895 *
2896 2896 * Below, the definiton of the pixel cornes and sides are shown as used
2897 2897 * within the code.
... ...