Commit d3d2a2fd1be5c29906269a22cee3915570d54d23

Authored by Jürgen Knödlseder
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.
inst/cta/include/GCTAOnOffObservation.hpp
@@ -118,7 +118,6 @@ protected: @@ -118,7 +118,6 @@ protected:
118 void init_members(void); 118 void init_members(void);
119 void copy_members(const GCTAOnOffObservation& obs); 119 void copy_members(const GCTAOnOffObservation& obs);
120 void free_members(void); 120 void free_members(void);
121 - void set_logetrue(void);  
122 void set_exposure(void); 121 void set_exposure(void);
123 void check_consistency(const std::string& method) const; 122 void check_consistency(const std::string& method) const;
124 void set(const GCTAObservation& obs, 123 void set(const GCTAObservation& obs,
@@ -183,7 +182,6 @@ protected: @@ -183,7 +182,6 @@ protected:
183 GPha m_off_spec; //!< Off counts spectrum 182 GPha m_off_spec; //!< Off counts spectrum
184 GArf m_arf; //!< Auxiliary Response Function vector 183 GArf m_arf; //!< Auxiliary Response Function vector
185 GRmf m_rmf; //!< Redistribution matrix 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&amp; pha_on, @@ -165,9 +165,6 @@ GCTAOnOffObservation::GCTAOnOffObservation(const GPha&amp; pha_on,
165 m_arf = arf; 165 m_arf = arf;
166 m_rmf = rmf; 166 m_rmf = rmf;
167 167
168 - // Set log true energy node array  
169 - set_logetrue();  
170 -  
171 // Set the ontime, livetime and deadtime correction 168 // Set the ontime, livetime and deadtime correction
172 set_exposure(); 169 set_exposure();
173 170
@@ -244,8 +241,12 @@ GCTAOnOffObservation::GCTAOnOffObservation(const GCTAObservation&amp; obs, @@ -244,8 +241,12 @@ GCTAOnOffObservation::GCTAOnOffObservation(const GCTAObservation&amp; obs,
244 * 241 *
245 * \f[ 242 * \f[
246 * \alpha(E_{\rm reco}) = \frac{\sum_i \alpha_i(E_{\rm reco}) 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 * \f] 250 * \f]
250 * 251 *
251 * \f[ 252 * \f[
@@ -265,6 +266,7 @@ GCTAOnOffObservation::GCTAOnOffObservation(const GCTAObservation&amp; obs, @@ -265,6 +266,7 @@ GCTAOnOffObservation::GCTAOnOffObservation(const GCTAObservation&amp; obs,
265 * \f$i\f$, 266 * \f$i\f$,
266 * \f$\alpha_i(E_{\rm reco})\f$ is the background scaling of observation 267 * \f$\alpha_i(E_{\rm reco})\f$ is the background scaling of observation
267 * \f$i\f$, 268 * \f$i\f$,
  269 + * \f$B_i(E_{\rm reco})\f$ is the background rate for observation \f$i\f$,
268 * \f$ARF_i(E_{\rm true})\f$ is the Auxiliary Response File of observation 270 * \f$ARF_i(E_{\rm true})\f$ is the Auxiliary Response File of observation
269 * \f$i\f$, 271 * \f$i\f$,
270 * \f$RMF_i(E_{\rm true}, E_{\rm reco})\f$ is the Redistribution Matrix File 272 * \f$RMF_i(E_{\rm true}, E_{\rm reco})\f$ is the Redistribution Matrix File
@@ -311,6 +313,23 @@ GCTAOnOffObservation::GCTAOnOffObservation(const GObservations&amp; obs) : @@ -311,6 +313,23 @@ GCTAOnOffObservation::GCTAOnOffObservation(const GObservations&amp; obs) :
311 m_livetime = onoff->livetime(); 313 m_livetime = onoff->livetime();
312 exposure = onoff->on_spec().exposure(); 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 // Compute RMF contribution 333 // Compute RMF contribution
315 for (int itrue = 0; itrue < m_rmf.ntrue(); ++itrue) { 334 for (int itrue = 0; itrue < m_rmf.ntrue(); ++itrue) {
316 double arf = m_arf[itrue]; 335 double arf = m_arf[itrue];
@@ -355,29 +374,31 @@ GCTAOnOffObservation::GCTAOnOffObservation(const GObservations&amp; obs) : @@ -355,29 +374,31 @@ GCTAOnOffObservation::GCTAOnOffObservation(const GObservations&amp; obs) :
355 throw GException::invalid_value(G_CONSTRUCTOR2, msg); 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 // Add On and Off spectrum 377 // Add On and Off spectrum
375 m_on_spec += onoff->on_spec(); 378 m_on_spec += onoff->on_spec();
376 m_off_spec += onoff->off_spec(); 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 // Add ARF 392 // Add ARF
379 m_arf += onoff->arf() * onoff->on_spec().exposure(); 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 // Add RMF 402 // Add RMF
382 for (int itrue = 0; itrue < m_arf.size(); ++itrue) { 403 for (int itrue = 0; itrue < m_arf.size(); ++itrue) {
383 double arf = onoff->arf()[itrue] * onoff->on_spec().exposure(); 404 double arf = onoff->arf()[itrue] * onoff->on_spec().exposure();
@@ -397,6 +418,19 @@ GCTAOnOffObservation::GCTAOnOffObservation(const GObservations&amp; obs) : @@ -397,6 +418,19 @@ GCTAOnOffObservation::GCTAOnOffObservation(const GObservations&amp; obs) :
397 418
398 } // endfor: looped over observations 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 // Compute RMF 434 // Compute RMF
401 for (int itrue = 0; itrue < m_arf.size(); ++itrue) { 435 for (int itrue = 0; itrue < m_arf.size(); ++itrue) {
402 double arf = m_arf[itrue]; 436 double arf = m_arf[itrue];
@@ -407,8 +441,18 @@ GCTAOnOffObservation::GCTAOnOffObservation(const GObservations&amp; obs) : @@ -407,8 +441,18 @@ GCTAOnOffObservation::GCTAOnOffObservation(const GObservations&amp; obs) :
407 441
408 // Compute ARF 442 // Compute ARF
409 if (exposure > 0.0) { 443 if (exposure > 0.0) {
  444 +
  445 + // Normalise Arf
410 m_arf /= exposure; 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 // Return 457 // Return
414 return; 458 return;
@@ -639,9 +683,6 @@ void GCTAOnOffObservation::read(const GXmlElement&amp; xml) @@ -639,9 +683,6 @@ void GCTAOnOffObservation::read(const GXmlElement&amp; xml)
639 m_arf.load(arf); 683 m_arf.load(arf);
640 m_rmf.load(rmf); 684 m_rmf.load(rmf);
641 685
642 - // Set log true energy node array  
643 - set_logetrue();  
644 -  
645 // Set the ontime, livetime and deadtime correction 686 // Set the ontime, livetime and deadtime correction
646 set_exposure(); 687 set_exposure();
647 688
@@ -809,7 +850,6 @@ void GCTAOnOffObservation::init_members(void) @@ -809,7 +850,6 @@ void GCTAOnOffObservation::init_members(void)
809 m_off_spec.clear(); 850 m_off_spec.clear();
810 m_arf.clear(); 851 m_arf.clear();
811 m_rmf.clear(); 852 m_rmf.clear();
812 - m_logetrue.clear();  
813 853
814 // Return 854 // Return
815 return; 855 return;
@@ -832,7 +872,6 @@ void GCTAOnOffObservation::copy_members(const GCTAOnOffObservation&amp; obs) @@ -832,7 +872,6 @@ void GCTAOnOffObservation::copy_members(const GCTAOnOffObservation&amp; obs)
832 m_off_spec = obs.m_off_spec; 872 m_off_spec = obs.m_off_spec;
833 m_arf = obs.m_arf; 873 m_arf = obs.m_arf;
834 m_rmf = obs.m_rmf; 874 m_rmf = obs.m_rmf;
835 - m_logetrue = obs.m_logetrue;  
836 875
837 // Clone members 876 // Clone members
838 m_response = (obs.m_response != NULL) ? obs.m_response->clone() : NULL; 877 m_response = (obs.m_response != NULL) ? obs.m_response->clone() : NULL;
@@ -854,39 +893,6 @@ void GCTAOnOffObservation::free_members(void) @@ -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 * @brief Set ontime, livetime and deadtime correction factor 896 * @brief Set ontime, livetime and deadtime correction factor
891 ***************************************************************************/ 897 ***************************************************************************/
892 void GCTAOnOffObservation::set_exposure(void) 898 void GCTAOnOffObservation::set_exposure(void)
@@ -1007,9 +1013,6 @@ void GCTAOnOffObservation::set(const GCTAObservation&amp; obs, @@ -1007,9 +1013,6 @@ void GCTAOnOffObservation::set(const GCTAObservation&amp; obs,
1007 compute_alpha(obs, reg_on, reg_off); 1013 compute_alpha(obs, reg_on, reg_off);
1008 compute_rmf(obs, reg_on); 1014 compute_rmf(obs, reg_on);
1009 1015
1010 - // Set log true energy node array  
1011 - set_logetrue();  
1012 -  
1013 // Return 1016 // Return
1014 return; 1017 return;
1015 } 1018 }
@@ -1659,28 +1662,13 @@ double GCTAOnOffObservation::N_bgd(const GModels&amp; models, @@ -1659,28 +1662,13 @@ double GCTAOnOffObservation::N_bgd(const GModels&amp; models,
1659 // Initialise parameter index 1662 // Initialise parameter index
1660 int ipar = 0; 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 // Get reconstructed energy bin mean and width 1665 // Get reconstructed energy bin mean and width
1666 GEnergy emean = m_on_spec.ebounds().elogmean(ibin); 1666 GEnergy emean = m_on_spec.ebounds().elogmean(ibin);
1667 double ewidth = m_on_spec.ebounds().ewidth(ibin).MeV(); 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 // Compute normalisation factor (events) 1673 // Compute normalisation factor (events)
1686 double exposure = m_on_spec.exposure(); 1674 double exposure = m_on_spec.exposure();