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 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&amp; 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&amp; 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&amp; 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&amp; 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&amp; 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&amp; 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&amp; 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&amp; 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&amp; 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&amp; 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&amp; 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();
... ...