Commit d3d2a2fd1be5c29906269a22cee3915570d54d23
1 parent
1bcfc0f8
Correct stacked response computation.
The BACKRESP column in Arf has originally not been updated when stacking On/Off observations and the background scaling factor has not been computed correctly. This has been fixed now.
Showing
2 changed files
with
69 additions
and
83 deletions
inst/cta/include/GCTAOnOffObservation.hpp
| ... | ... | @@ -118,7 +118,6 @@ protected: |
| 118 | 118 | void init_members(void); |
| 119 | 119 | void copy_members(const GCTAOnOffObservation& obs); |
| 120 | 120 | void free_members(void); |
| 121 | - void set_logetrue(void); | |
| 122 | 121 | void set_exposure(void); |
| 123 | 122 | void check_consistency(const std::string& method) const; |
| 124 | 123 | void set(const GCTAObservation& obs, |
| ... | ... | @@ -183,7 +182,6 @@ protected: |
| 183 | 182 | GPha m_off_spec; //!< Off counts spectrum |
| 184 | 183 | GArf m_arf; //!< Auxiliary Response Function vector |
| 185 | 184 | GRmf m_rmf; //!< Redistribution matrix |
| 186 | - GNodeArray m_logetrue; //!< Node array of log10 true energies in TeV | |
| 187 | 185 | }; |
| 188 | 186 | |
| 189 | 187 | ... | ... |
inst/cta/src/GCTAOnOffObservation.cpp
| ... | ... | @@ -165,9 +165,6 @@ GCTAOnOffObservation::GCTAOnOffObservation(const GPha& pha_on, |
| 165 | 165 | m_arf = arf; |
| 166 | 166 | m_rmf = rmf; |
| 167 | 167 | |
| 168 | - // Set log true energy node array | |
| 169 | - set_logetrue(); | |
| 170 | - | |
| 171 | 168 | // Set the ontime, livetime and deadtime correction |
| 172 | 169 | set_exposure(); |
| 173 | 170 | |
| ... | ... | @@ -244,8 +241,12 @@ GCTAOnOffObservation::GCTAOnOffObservation(const GCTAObservation& obs, |
| 244 | 241 | * |
| 245 | 242 | * \f[ |
| 246 | 243 | * \alpha(E_{\rm reco}) = \frac{\sum_i \alpha_i(E_{\rm reco}) |
| 247 | - * N^{\rm off}_i(E_{\rm reco})} | |
| 248 | - * {\sum_i N^{\rm off}_i(E_{\rm reco})} | |
| 244 | + * B_i(E_{\rm reco}) \tau_i} | |
| 245 | + * {\sum_i B_i(E_{\rm reco}) \tau_i} | |
| 246 | + * \f] | |
| 247 | + * | |
| 248 | + * \f[ | |
| 249 | + * B(E_{\rm reco}) = \frac{\sum_i B_i(E_{\rm reco}) \tau_i}{\sum_i \tau_i} | |
| 249 | 250 | * \f] |
| 250 | 251 | * |
| 251 | 252 | * \f[ |
| ... | ... | @@ -265,6 +266,7 @@ GCTAOnOffObservation::GCTAOnOffObservation(const GCTAObservation& obs, |
| 265 | 266 | * \f$i\f$, |
| 266 | 267 | * \f$\alpha_i(E_{\rm reco})\f$ is the background scaling of observation |
| 267 | 268 | * \f$i\f$, |
| 269 | + * \f$B_i(E_{\rm reco})\f$ is the background rate for observation \f$i\f$, | |
| 268 | 270 | * \f$ARF_i(E_{\rm true})\f$ is the Auxiliary Response File of observation |
| 269 | 271 | * \f$i\f$, |
| 270 | 272 | * \f$RMF_i(E_{\rm true}, E_{\rm reco})\f$ is the Redistribution Matrix File |
| ... | ... | @@ -311,6 +313,23 @@ GCTAOnOffObservation::GCTAOnOffObservation(const GObservations& obs) : |
| 311 | 313 | m_livetime = onoff->livetime(); |
| 312 | 314 | exposure = onoff->on_spec().exposure(); |
| 313 | 315 | |
| 316 | + // Compute number of background events/MeV from BACKRESP column | |
| 317 | + // and store result intermediately into Arf BACKRESP column | |
| 318 | + std::vector<double>& backresp = m_arf["BACKRESP"]; | |
| 319 | + for (int i = 0; i < backresp.size(); ++i) { | |
| 320 | + backresp[i] *= onoff->on_spec().exposure(); | |
| 321 | + } | |
| 322 | + | |
| 323 | + // Compute background scaling factor contribution | |
| 324 | + for (int i = 0; i < m_on_spec.size(); ++i) { | |
| 325 | + GEnergy emean = m_on_spec.ebounds().elogmean(i); | |
| 326 | + double background = onoff->arf()("BACKRESP", emean); | |
| 327 | + double exposure = onoff->on_spec().exposure(); | |
| 328 | + double alpha = onoff->on_spec().backscal(i); | |
| 329 | + double scale = alpha * background * exposure; | |
| 330 | + m_on_spec.backscal(i,scale); | |
| 331 | + } | |
| 332 | + | |
| 314 | 333 | // Compute RMF contribution |
| 315 | 334 | for (int itrue = 0; itrue < m_rmf.ntrue(); ++itrue) { |
| 316 | 335 | double arf = m_arf[itrue]; |
| ... | ... | @@ -355,29 +374,31 @@ GCTAOnOffObservation::GCTAOnOffObservation(const GObservations& obs) : |
| 355 | 374 | throw GException::invalid_value(G_CONSTRUCTOR2, msg); |
| 356 | 375 | } |
| 357 | 376 | |
| 358 | - // Compute background scaling factor | |
| 359 | - for (int i = 0; i < m_on_spec.size(); ++i) { | |
| 360 | - | |
| 361 | - // Compute background scaling factor for spectral bin | |
| 362 | - double n1 = m_off_spec[i]; // Counts from Off | |
| 363 | - double n2 = onoff->off_spec()[i]; // Counts from Off | |
| 364 | - double a1 = m_on_spec.backscal(i); // Alpha from On | |
| 365 | - double a2 = onoff->on_spec().backscal(i); // Alpha from On | |
| 366 | - double n = n1 + n2; | |
| 367 | - double a = (n > 0.0) ? (a1 * n1 + a2 * n2)/n : 1.0; | |
| 368 | - | |
| 369 | - // Set background scaling factor of On Spectrum | |
| 370 | - m_on_spec.backscal(i,a); | |
| 371 | - | |
| 372 | - } // endfor: computed background scaling factors | |
| 373 | - | |
| 374 | 377 | // Add On and Off spectrum |
| 375 | 378 | m_on_spec += onoff->on_spec(); |
| 376 | 379 | m_off_spec += onoff->off_spec(); |
| 377 | 380 | |
| 381 | + // Compute background scaling factor contribution | |
| 382 | + for (int i = 0; i < m_on_spec.size(); ++i) { | |
| 383 | + GEnergy emean = m_on_spec.ebounds().elogmean(i); | |
| 384 | + double background = onoff->arf()("BACKRESP", emean); | |
| 385 | + double exposure = onoff->on_spec().exposure(); | |
| 386 | + double alpha = onoff->on_spec().backscal(i); | |
| 387 | + double scale = m_on_spec.backscal(i) + | |
| 388 | + alpha * background * exposure; | |
| 389 | + m_on_spec.backscal(i,scale); | |
| 390 | + } | |
| 391 | + | |
| 378 | 392 | // Add ARF |
| 379 | 393 | m_arf += onoff->arf() * onoff->on_spec().exposure(); |
| 380 | 394 | |
| 395 | + // Add number of background events/MeV from BACKRESP column | |
| 396 | + const std::vector<double>& src = onoff->arf()["BACKRESP"]; | |
| 397 | + std::vector<double>& dst = m_arf["BACKRESP"]; | |
| 398 | + for (int i = 0; i < dst.size(); ++i) { | |
| 399 | + dst[i] += src[i] * onoff->on_spec().exposure(); | |
| 400 | + } | |
| 401 | + | |
| 381 | 402 | // Add RMF |
| 382 | 403 | for (int itrue = 0; itrue < m_arf.size(); ++itrue) { |
| 383 | 404 | double arf = onoff->arf()[itrue] * onoff->on_spec().exposure(); |
| ... | ... | @@ -397,6 +418,19 @@ GCTAOnOffObservation::GCTAOnOffObservation(const GObservations& obs) : |
| 397 | 418 | |
| 398 | 419 | } // endfor: looped over observations |
| 399 | 420 | |
| 421 | + // Compute stacked background scaling factor | |
| 422 | + for (int i = 0; i < m_on_spec.size(); ++i) { | |
| 423 | + GEnergy emean = m_on_spec.ebounds().elogmean(i); | |
| 424 | + double norm = m_arf("BACKRESP", emean); | |
| 425 | + if (norm > 0.0) { | |
| 426 | + double scale = m_on_spec.backscal(i) / norm; | |
| 427 | + m_on_spec.backscal(i,scale); | |
| 428 | + } | |
| 429 | + else { | |
| 430 | + m_on_spec.backscal(i,0.0); | |
| 431 | + } | |
| 432 | + } | |
| 433 | + | |
| 400 | 434 | // Compute RMF |
| 401 | 435 | for (int itrue = 0; itrue < m_arf.size(); ++itrue) { |
| 402 | 436 | double arf = m_arf[itrue]; |
| ... | ... | @@ -407,8 +441,18 @@ GCTAOnOffObservation::GCTAOnOffObservation(const GObservations& obs) : |
| 407 | 441 | |
| 408 | 442 | // Compute ARF |
| 409 | 443 | if (exposure > 0.0) { |
| 444 | + | |
| 445 | + // Normalise Arf | |
| 410 | 446 | m_arf /= exposure; |
| 411 | - } | |
| 447 | + | |
| 448 | + // Compute background events/MeV/sec and store them in BACKRESP | |
| 449 | + // column | |
| 450 | + std::vector<double>& backresp = m_arf["BACKRESP"]; | |
| 451 | + for (int i = 0; i < backresp.size(); ++i) { | |
| 452 | + backresp[i] /= exposure; | |
| 453 | + } | |
| 454 | + | |
| 455 | + } // endif: total exposure was positive | |
| 412 | 456 | |
| 413 | 457 | // Return |
| 414 | 458 | return; |
| ... | ... | @@ -639,9 +683,6 @@ void GCTAOnOffObservation::read(const GXmlElement& xml) |
| 639 | 683 | m_arf.load(arf); |
| 640 | 684 | m_rmf.load(rmf); |
| 641 | 685 | |
| 642 | - // Set log true energy node array | |
| 643 | - set_logetrue(); | |
| 644 | - | |
| 645 | 686 | // Set the ontime, livetime and deadtime correction |
| 646 | 687 | set_exposure(); |
| 647 | 688 | |
| ... | ... | @@ -809,7 +850,6 @@ void GCTAOnOffObservation::init_members(void) |
| 809 | 850 | m_off_spec.clear(); |
| 810 | 851 | m_arf.clear(); |
| 811 | 852 | m_rmf.clear(); |
| 812 | - m_logetrue.clear(); | |
| 813 | 853 | |
| 814 | 854 | // Return |
| 815 | 855 | return; |
| ... | ... | @@ -832,7 +872,6 @@ void GCTAOnOffObservation::copy_members(const GCTAOnOffObservation& obs) |
| 832 | 872 | m_off_spec = obs.m_off_spec; |
| 833 | 873 | m_arf = obs.m_arf; |
| 834 | 874 | m_rmf = obs.m_rmf; |
| 835 | - m_logetrue = obs.m_logetrue; | |
| 836 | 875 | |
| 837 | 876 | // Clone members |
| 838 | 877 | m_response = (obs.m_response != NULL) ? obs.m_response->clone() : NULL; |
| ... | ... | @@ -854,39 +893,6 @@ void GCTAOnOffObservation::free_members(void) |
| 854 | 893 | |
| 855 | 894 | |
| 856 | 895 | /***********************************************************************//** |
| 857 | - * @brief Set true energy node array | |
| 858 | - ***************************************************************************/ | |
| 859 | -void GCTAOnOffObservation::set_logetrue(void) | |
| 860 | -{ | |
| 861 | - // Clear node array | |
| 862 | - m_logetrue.clear(); | |
| 863 | - | |
| 864 | - // Continue only if there are true energies in Arf | |
| 865 | - int netrue = m_arf.size(); | |
| 866 | - if (netrue > 0) { | |
| 867 | - | |
| 868 | - // Reserve space in node array | |
| 869 | - m_logetrue.reserve(netrue); | |
| 870 | - | |
| 871 | - // Append all log mean energies to node array | |
| 872 | - for (int i = 0; i < netrue; ++i) { | |
| 873 | - | |
| 874 | - // Get log mean of true energy in TeV | |
| 875 | - double logE = m_arf.ebounds().elogmean(i).log10TeV(); | |
| 876 | - | |
| 877 | - // Append energy to node array | |
| 878 | - m_logetrue.append(logE); | |
| 879 | - | |
| 880 | - } // endfor: appended log mean energies | |
| 881 | - | |
| 882 | - } // endif: there were true energies in Arf | |
| 883 | - | |
| 884 | - // Return | |
| 885 | - return; | |
| 886 | -} | |
| 887 | - | |
| 888 | - | |
| 889 | -/***********************************************************************//** | |
| 890 | 896 | * @brief Set ontime, livetime and deadtime correction factor |
| 891 | 897 | ***************************************************************************/ |
| 892 | 898 | void GCTAOnOffObservation::set_exposure(void) |
| ... | ... | @@ -1007,9 +1013,6 @@ void GCTAOnOffObservation::set(const GCTAObservation& obs, |
| 1007 | 1013 | compute_alpha(obs, reg_on, reg_off); |
| 1008 | 1014 | compute_rmf(obs, reg_on); |
| 1009 | 1015 | |
| 1010 | - // Set log true energy node array | |
| 1011 | - set_logetrue(); | |
| 1012 | - | |
| 1013 | 1016 | // Return |
| 1014 | 1017 | return; |
| 1015 | 1018 | } |
| ... | ... | @@ -1659,28 +1662,13 @@ double GCTAOnOffObservation::N_bgd(const GModels& models, |
| 1659 | 1662 | // Initialise parameter index |
| 1660 | 1663 | int ipar = 0; |
| 1661 | 1664 | |
| 1662 | - // Get reference to background response (events/MeV/s) | |
| 1663 | - const std::vector<double>& backresp = m_arf["BACKRESP"]; | |
| 1664 | - | |
| 1665 | 1665 | // Get reconstructed energy bin mean and width |
| 1666 | 1666 | GEnergy emean = m_on_spec.ebounds().elogmean(ibin); |
| 1667 | 1667 | double ewidth = m_on_spec.ebounds().ewidth(ibin).MeV(); |
| 1668 | 1668 | |
| 1669 | - // Perform log-log interpolation of background rate at reconstructed | |
| 1670 | - // energy | |
| 1671 | - m_logetrue.set_value(emean.log10TeV()); | |
| 1672 | - double wgt_left = m_logetrue.wgt_left(); | |
| 1673 | - double wgt_right = m_logetrue.wgt_right(); | |
| 1674 | - double bkg_left = backresp[m_logetrue.inx_left()]; | |
| 1675 | - double bkg_right = backresp[m_logetrue.inx_right()]; | |
| 1676 | - double background = 0.0; | |
| 1677 | - if (bkg_left > 0.0 && bkg_right > 0.0) { | |
| 1678 | - background = std::exp(wgt_left * std::log(bkg_left) + | |
| 1679 | - wgt_right * std::log(bkg_right)); | |
| 1680 | - } | |
| 1681 | - if (background < 0.0) { | |
| 1682 | - background = 0.0; | |
| 1683 | - } | |
| 1669 | + // Perform log-log interpolation of background rate (events/MeV/s) | |
| 1670 | + // at reconstructed energy | |
| 1671 | + double background = m_arf("BACKRESP", emean); | |
| 1684 | 1672 | |
| 1685 | 1673 | // Compute normalisation factor (events) |
| 1686 | 1674 | double exposure = m_on_spec.exposure(); | ... | ... |