Commit 57acac30dab697ad0de21906d16f1bcf7d035fb5

Authored by Jürgen Knödlseder
2 parents b86b2e29 5ed3c029

Merge branch '2694-hess-background-models' into integration

ChangeLog
1   -2018-10-17
  1 +2018-10-25
2 2  
3 3 * Version 1.6.0 released
4 4 ========================
5 5  
  6 + GCTAEdisp returns now energy dispersion values per MeV (#2704)
6 7 Protect GCTAInstDir from access to invalid information (#2694)
7 8 Implement general CTA background model (#2694)
8 9 Fix bug that potentially wrote the wrong exposure time in a PHA file (#2683)
... ...
1 1 New Features and Important Changes in GammaLib 1.6.0
2 2  
3   -17 October 2018
  3 +25 October 2018
4 4  
5 5  
6 6 1. Introduction
... ... @@ -300,6 +300,12 @@ None
300 300  
301 301 20. CTA interface
302 302 -----------------
  303 +The GCTAEdisp::operator() now returns energy dispersion values per MeV. The
  304 +units are now consistent with the expectation in GResponse, and this changes
  305 +assurance the coherence between the various classes. In particular, no re-
  306 +normalization is needed anymore to digest the H.E.S.S. energy dispersion, and
  307 +also the CTA energy dispersion is handled correctly (#2704).
  308 +
303 309 The GCTAInstDir class now assures that only valid information can be accessed.
304 310 Since the class can hold a sky direction and detector coordinates, flags were
305 311 added that signal what information actually exists in the class. If a non-
... ...
doc/source/admin/release_history/1.6.rst
... ... @@ -45,6 +45,8 @@ Bug fixes
45 45 Improvements
46 46 ------------
47 47  
  48 +* [`2704 <https://cta-redmine.irap.omp.eu/issues/2704>`_] -
  49 + ``GCTAEdisp`` returns now energy dispersion values per MeV
48 50 * [`2694 <https://cta-redmine.irap.omp.eu/issues/2694>`_] -
49 51 Protect ``GCTAInstDir`` from access to invalid information
50 52 * [`2677 <https://cta-redmine.irap.omp.eu/issues/2677>`_] -
... ...
include/GResponse.hpp
1 1 /***************************************************************************
2 2 * GResponse.hpp - Abstract response base class *
3 3 * ----------------------------------------------------------------------- *
4   - * copyright (C) 2008-2015 by Juergen Knoedlseder *
  4 + * copyright (C) 2008-2018 by Juergen Knoedlseder *
5 5 * ----------------------------------------------------------------------- *
6 6 * *
7 7 * This program is free software: you can redistribute it and/or modify *
... ... @@ -127,7 +127,7 @@ protected:
127 127 m_event(event),
128 128 m_srcTime(srcTime),
129 129 m_grad(grad) { }
130   - double eval(const double& x);
  130 + double eval(const double& logEsrc);
131 131 protected:
132 132 const GResponse* m_parent; //!< Response
133 133 const GObservation* m_obs; //!< Observation
... ...
inst/cta/include/GCTAEdisp2D.hpp
... ... @@ -52,14 +52,47 @@ namespace gammalib {
52 52 * @brief CTA 2D energy dispersion class
53 53 *
54 54 * This class implements the energy dispersion for the CTA 2D response. The
55   - * energy dispersion is defined as
  55 + * CTA 2D energy dispersion is in fact a 3-dimensional function, where the
  56 + * energy dispersion is given as function of true energy \f$E_{\rm true}\f$,
  57 + * migration value \f$m = E_{\rm reco}/E_{\rm true}\f$ and offset angle
  58 + * \f$\theta\f$
56 59 *
57 60 * \f[
58   - * E_{\rm disp}(\log_{10} E_{\rm reco} | \log_{10} E_{\rm true}, \theta) =
59   - * \frac{dP}{d\log_{10} E_{\rm reco}}
  61 + * E_{\rm disp}(E_{\rm true}, m, \theta)
60 62 * \f]
61 63 *
62   - * and given in units of \f$(\log_{10} MeV)^{-1}\f$.
  64 + * with
  65 + *
  66 + * \f[
  67 + * \int_{m_{\rm min}}^{m_{\rm max}}
  68 + * E_{\rm disp}(E_{\rm true}, m, \theta) \, dm = 1
  69 + * \f]
  70 + *
  71 + * The energy dispersion as function of reconstructed energy
  72 + * \f$E_{\rm reco}\f$ is given by
  73 + *
  74 + * \f[
  75 + * E_{\rm disp}(E_{\rm true}, E_{\rm reco}, \theta) =
  76 + * \frac{E_{\rm disp}(E_{\rm true}, m, \theta)}{E_{\rm true}}
  77 + * \f]
  78 + *
  79 + * the energy dispersion as function of the natural logarithm of
  80 + * reconstructed energy \f$\log E_{\rm reco}\f$ is given by
  81 + *
  82 + * \f[
  83 + * E_{\rm disp}(E_{\rm true}, \log E_{\rm reco}, \theta) =
  84 + * E_{\rm disp}(E_{\rm true}, m, \theta) \times
  85 + * \frac{E_{\rm reco}}{E_{\rm true}}
  86 + * \f]
  87 + *
  88 + * and the energy dispersion as function of the base 10 logarithm of
  89 + * reconstructed energy \f$\log_{10} E_{\rm reco}\f$ is given by
  90 + *
  91 + * \f[
  92 + * E_{\rm disp}(E_{\rm true}, \log_{10} E_{\rm reco}, \theta) =
  93 + * E_{\rm disp}(E_{\rm true}, m, \theta) \times
  94 + * \frac{\log 10 \times E_{\rm reco}}{E_{\rm true}}
  95 + * \f]
63 96 ***************************************************************************/
64 97 class GCTAEdisp2D : public GCTAEdisp {
65 98  
... ... @@ -187,7 +220,7 @@ private:
187 220 m_parent(parent),
188 221 m_logEsrc(logEsrc),
189 222 m_theta(theta) { }
190   - double eval(const double& x);
  223 + double eval(const double& logEobs);
191 224 protected:
192 225 const GCTAEdisp2D* m_parent; //!< Pointer to parent class
193 226 double m_logEsrc; //!< True photon energy
... ...
inst/cta/include/GCTAEdispPerfTable.hpp
... ... @@ -44,18 +44,25 @@ class GRan;
44 44 * @brief CTA performance table energy dispersion class
45 45 *
46 46 * This class implements the CTA energy dispersion response as function
47   - * of energy as determined from a performance table. The performance table is
48   - * an ASCII file that specifies the CTA performance parameters in a simple
49   - * way.
  47 + * of true energy as determined from a performance table. A performance
  48 + * table is an ASCII file that represent a table, specifying the CTA
  49 + * performance parameters as function of true photon energy.
50 50 *
51 51 * The energy dispersion is defined as
52 52 *
53 53 * \f[
54   - * E_{\rm disp}(\log_{10} E_{\rm reco} | \log_{10} E_{\rm true}) =
55   - * \frac{dP}{d\log_{10} E_{\rm reco}}
  54 + * E_{\rm disp}(E_{\rm true}, E_{\rm reco}) =
  55 + * \frac{1}{\sqrt{2\pi}\sigma(E_{\rm true})}
  56 + * \exp \left(\frac{-(\log_{10} E_{\rm reco} - \log_{10} E_{\rm true})^2}
  57 + * {2 \sigma(E_{\rm true})^2} \right) \times
  58 + * \frac{1}{\log_{10} E_{\rm reco}}
56 59 * \f]
57 60 *
58   - * and given in units of \f$(\log_{10} MeV)^{-1}\f$.
  61 + * and given in units of MeV\f$^{-1}\f$, where
  62 + * \f$E_{\rm reco}\f$ is the reconstructed energy in units of MeV,
  63 + * \f$E_{\rm true}\f$ is the true energy in units of MeV, and
  64 + * \f$\sigma(E_{\rm true})\f$ is the standard deviation of the energy
  65 + * dispersion that depends on the true photon energy.
59 66 ***************************************************************************/
60 67 class GCTAEdispPerfTable : public GCTAEdisp {
61 68  
... ...
inst/cta/include/GCTAEdispRmf.hpp
... ... @@ -50,11 +50,14 @@ class GEbounds;
50 50 * The energy dispersion is defined as
51 51 *
52 52 * \f[
53   - * E_{\rm disp}(\log_{10} E_{\rm reco} | \log_{10} E_{\rm true}) =
54   - * \frac{dP}{d\log_{10} E_{\rm reco}}
  53 + * E_{\rm disp}(E_{\rm true}, E_{\rm reco}) =
  54 + * \frac{E_{\rm disp}(E_{\rm true}, \log_{10} E_{\rm reco}, \theta)}
  55 + * {\log_{10} E_{\rm reco}}
55 56 * \f]
56 57 *
57   - * and given in units of \f$(\log_{10} MeV)^{-1}\f$.
  58 + * in units of MeV\f$^{-1}\f$ where
  59 + * \f$E_{\rm reco}\f$ is the reconstructed energy in units of MeV, and
  60 + * \f$E_{\rm true}\f$ is the true energy in units of MeV.
58 61 ***************************************************************************/
59 62 class GCTAEdispRmf : public GCTAEdisp {
60 63  
... ... @@ -113,7 +116,7 @@ private:
113 116 void set_matrix(void);
114 117 void set_cache(void) const;
115 118 void set_max_edisp(void) const;
116   - void update(const double& arg1, const double& arg2) const;
  119 + void update(const double& logEsrc, const double& logEobs) const;
117 120 void compute_ebounds_obs(const double& theta = 0.0,
118 121 const double& phi = 0.0,
119 122 const double& zenith = 0.0,
... ...
inst/cta/include/GCTAModelSpatialGradient.hpp
... ... @@ -50,6 +50,8 @@ class GCTAModelSpatialGradient : public GCTAModelSpatial {
50 50 public:
51 51 // Constructors and destructors
52 52 GCTAModelSpatialGradient(void);
  53 + GCTAModelSpatialGradient(const double& detx_gradient,
  54 + const double& dety_gradient);
53 55 explicit GCTAModelSpatialGradient(const GXmlElement& xml);
54 56 GCTAModelSpatialGradient(const GCTAModelSpatialGradient& model);
55 57 virtual ~GCTAModelSpatialGradient(void);
... ...
inst/cta/include/GCTAModelSpatialMultiplicative.hpp
... ... @@ -30,11 +30,6 @@
30 30 /* __ Includes ___________________________________________________________ */
31 31 #include <string>
32 32 #include <vector>
33   -//#include "GBase.hpp"
34   -//#include "GFunction.hpp"
35   -//#include "GEnergy.hpp"
36   -//#include "GTime.hpp"
37   -//#include "GModelPar.hpp"
38 33 #include "GCTAModelSpatial.hpp"
39 34  
40 35 /* __ Forward declarations _______________________________________________ */
... ...
inst/cta/pyext/GCTAModelSpatialGradient.i 0 → 100644
  1 +/***************************************************************************
  2 + * GCTAModelSpatialGradient.i - Spatial gradient CTA model class *
  3 + * ----------------------------------------------------------------------- *
  4 + * copyright (C) 2018 by Juergen Knoedlseder *
  5 + * ----------------------------------------------------------------------- *
  6 + * *
  7 + * This program is free software: you can redistribute it and/or modify *
  8 + * it under the terms of the GNU General Public License as published by *
  9 + * the Free Software Foundation, either version 3 of the License, or *
  10 + * (at your option) any later version. *
  11 + * *
  12 + * This program is distributed in the hope that it will be useful, *
  13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of *
  14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
  15 + * GNU General Public License for more details. *
  16 + * *
  17 + * You should have received a copy of the GNU General Public License *
  18 + * along with this program. If not, see <http://www.gnu.org/licenses/>. *
  19 + * *
  20 + ***************************************************************************/
  21 +/**
  22 + * @file GCTAModelSpatialGradient.i
  23 + * @brief Spatial gradient CTA interface definition
  24 + * @author Juergen Knoedlseder
  25 + */
  26 +%{
  27 +/* Put headers and other declarations here that are needed for compilation */
  28 +#include "GCTAModelSpatialGradient.hpp"
  29 +%}
  30 +
  31 +
  32 +/***********************************************************************//**
  33 + * @class GCTAModelSpatialGradient
  34 + *
  35 + * @brief Spatial gradient CTA model class
  36 + ***************************************************************************/
  37 +class GCTAModelSpatialGradient : public GCTAModelSpatial {
  38 +
  39 +public:
  40 + // Constructors and destructors
  41 + GCTAModelSpatialGradient(void);
  42 + GCTAModelSpatialGradient(const double& detx_gradient,
  43 + const double& dety_gradient);
  44 + explicit GCTAModelSpatialGradient(const GXmlElement& xml);
  45 + GCTAModelSpatialGradient(const GCTAModelSpatialGradient& model);
  46 + virtual ~GCTAModelSpatialGradient(void);
  47 +
  48 + // Operators
  49 + virtual GCTAModelSpatialGradient& operator=(const GCTAModelSpatialGradient& model);
  50 +
  51 + // Implemented pure virtual methods
  52 + virtual void clear(void);
  53 + virtual GCTAModelSpatialGradient* clone(void) const;
  54 + virtual std::string classname(void) const;
  55 + virtual std::string type(void) const;
  56 + virtual double eval(const GCTAInstDir& dir,
  57 + const GEnergy& energy,
  58 + const GTime& time,
  59 + const bool& gradients = false) const;
  60 + virtual GCTAInstDir mc(const GEnergy& energy,
  61 + const GTime& time,
  62 + GRan& ran) const;
  63 + virtual void read(const GXmlElement& xml);
  64 + virtual void write(GXmlElement& xml) const;
  65 +
  66 + // Other methods
  67 + double detx_gradient(void) const;
  68 + double dety_gradient(void) const;
  69 + void detx_gradient(const double& detx_gradient);
  70 + void dety_gradient(const double& dety_gradient);
  71 +};
  72 +
  73 +
  74 +/***********************************************************************//**
  75 + * @brief GCTAModelSpatialGradient class extension
  76 + ***************************************************************************/
  77 +%extend GCTAModelSpatialGradient {
  78 + GCTAModelSpatialGradient copy() {
  79 + return (*self);
  80 + }
  81 +%pythoncode {
  82 + def __getstate__(self):
  83 + xml = gammalib.GXmlElement()
  84 + self.write(xml)
  85 + state = (xml,)
  86 + return state
  87 + def __setstate__(self, state):
  88 + if state[0].elements('parameter') == 0:
  89 + self.__init__()
  90 + else:
  91 + self.__init__(state[0])
  92 +}
  93 +};
... ...
inst/cta/pyext/GCTAModelSpatialMultiplicative.i 0 → 100644
  1 +/***************************************************************************
  2 + * GCTAModelSpatialMultiplicative.i - Multiplicative spatial model class *
  3 + * ----------------------------------------------------------------------- *
  4 + * copyright (C) 2018 by Juergen Knoedlseder *
  5 + * ----------------------------------------------------------------------- *
  6 + * *
  7 + * This program is free software: you can redistribute it and/or modify *
  8 + * it under the terms of the GNU General Public License as published by *
  9 + * the Free Software Foundation, either version 3 of the License, or *
  10 + * (at your option) any later version. *
  11 + * *
  12 + * This program is distributed in the hope that it will be useful, *
  13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of *
  14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
  15 + * GNU General Public License for more details. *
  16 + * *
  17 + * You should have received a copy of the GNU General Public License *
  18 + * along with this program. If not, see <http://www.gnu.org/licenses/>. *
  19 + * *
  20 + ***************************************************************************/
  21 +/**
  22 + * @file GCTAModelSpatialMultiplicative.i
  23 + * @brief Multiplicative spatial model class interface definition
  24 + * @author Juergen Knoedlseder
  25 + */
  26 +%{
  27 +/* Put headers and other declarations here that are needed for compilation */
  28 +#include "GCTAModelSpatialMultiplicative.hpp"
  29 +%}
  30 +
  31 +
  32 +/***********************************************************************//**
  33 + * @class GCTAModelSpatialMultiplicative
  34 + *
  35 + * @brief Multiplicative spatial model class
  36 + ***************************************************************************/
  37 +class GCTAModelSpatialMultiplicative : public GCTAModelSpatial {
  38 +
  39 +public:
  40 + // Constructors and destructors
  41 + GCTAModelSpatialMultiplicative(void);
  42 + explicit GCTAModelSpatialMultiplicative(const GXmlElement& xml);
  43 + GCTAModelSpatialMultiplicative(const GCTAModelSpatialMultiplicative& model);
  44 + virtual ~GCTAModelSpatialMultiplicative(void);
  45 +
  46 + // Operators
  47 + virtual GCTAModelSpatialMultiplicative& operator=(const GCTAModelSpatialMultiplicative& model);
  48 +
  49 + // Pure virtual methods
  50 + virtual void clear(void);
  51 + virtual GCTAModelSpatialMultiplicative* clone(void) const;
  52 + virtual std::string classname(void) const;
  53 + virtual std::string type(void) const;
  54 + virtual double eval(const GCTAInstDir& dir,
  55 + const GEnergy& energy,
  56 + const GTime& time,
  57 + const bool& gradients = false) const;
  58 + virtual GCTAInstDir mc(const GEnergy& energy,
  59 + const GTime& time,
  60 + GRan& ran) const;
  61 + virtual void read(const GXmlElement& xml);
  62 + virtual void write(GXmlElement& xml) const;
  63 +
  64 + // Other methods
  65 + void append(const GCTAModelSpatial& spatial,
  66 + const std::string& name="");
  67 + int components(void) const;
  68 + const GCTAModelSpatial* component(const int& index) const;
  69 + const GCTAModelSpatial* component(const std::string& name) const;
  70 +};
  71 +
  72 +
  73 +/***********************************************************************//**
  74 + * @brief GCTAModelSpatialMultiplicative class extension
  75 + ***************************************************************************/
  76 +%extend GCTAModelSpatialMultiplicative {
  77 + GCTAModelSpatialMultiplicative copy() {
  78 + return (*self);
  79 + }
  80 +%pythoncode {
  81 + def __getstate__(self):
  82 + xml = gammalib.GXmlElement()
  83 + self.write(xml)
  84 + state = (xml,)
  85 + return state
  86 + def __setstate__(self, state):
  87 + self.__init__(state[0])
  88 +}
  89 +};
... ...
inst/cta/pyext/cta.i
... ... @@ -307,6 +307,8 @@
307 307 %include "GCTAModelIrfBackground.i"
308 308 %include "GCTAModelAeffBackground.i"
309 309 %include "GCTAModelSpatial.i"
  310 +%include "GCTAModelSpatialGradient.i"
  311 +%include "GCTAModelSpatialMultiplicative.i"
310 312 %include "GCTAModelSpatialRegistry.i"
311 313 %include "GCTAModelRadial.i"
312 314 %include "GCTAModelRadialRegistry.i"
... ...
inst/cta/src/GCTACubeBackground.cpp
... ... @@ -153,15 +153,15 @@ GCTACubeBackground::GCTACubeBackground(const GCTACubeBackground&amp; bgd)
153 153 /***********************************************************************//**
154 154 * @brief Background cube constructor
155 155 *
156   - * @param[in] wcs World Coordinate System.
157   - * @param[in] coords Coordinate System (CEL or GAL).
158   - * @param[in] x X coordinate of sky map centre (deg).
159   - * @param[in] y Y coordinate of sky map centre (deg).
160   - * @param[in] dx Pixel size in x direction at centre (deg/pixel).
161   - * @param[in] dy Pixel size in y direction at centre (deg/pixel).
162   - * @param[in] nx Number of pixels in x direction.
163   - * @param[in] ny Number of pixels in y direction.
164   - * @param[in] energies Energies.
  156 + * @param[in] wcs World Coordinate System.
  157 + * @param[in] coords Coordinate System (CEL or GAL).
  158 + * @param[in] x X coordinate of sky map centre (deg).
  159 + * @param[in] y Y coordinate of sky map centre (deg).
  160 + * @param[in] dx Pixel size in x direction at centre (deg/pixel).
  161 + * @param[in] dy Pixel size in y direction at centre (deg/pixel).
  162 + * @param[in] nx Number of pixels in x direction.
  163 + * @param[in] ny Number of pixels in y direction.
  164 + * @param[in] ebounds Energy boundaries.
165 165 *
166 166 * Constructs a background cube by specifying the sky map grid and the
167 167 * energies.
... ...
inst/cta/src/GCTAEdisp2D.cpp
... ... @@ -57,6 +57,7 @@
57 57 //#define G_SMOOTH_EDISP_SAVE_TEST //!< Save test matrix
58 58  
59 59 /* __ Debug definitions __________________________________________________ */
  60 +//#define G_DEBUG_HESS_RENORMALIZATION
60 61  
61 62 /* __ Constants __________________________________________________________ */
62 63  
... ... @@ -177,27 +178,28 @@ GCTAEdisp2D&amp; GCTAEdisp2D::operator=(const GCTAEdisp2D&amp; edisp)
177 178  
178 179  
179 180 /***********************************************************************//**
180   - * @brief Return energy dispersion in units of \f$(\log_{10} MeV)^{-1}\f$
  181 + * @brief Return energy dispersion in units of MeV\f$^{-1}\f$
181 182 *
182   - * @param[in] logEobs Log10 of the measured energy (TeV).
183   - * @param[in] logEsrc Log10 of the true photon energy (TeV).
184   - * @param[in] theta Offset angle in camera system (rad).
185   - * @param[in] phi Azimuth angle in camera system (rad). Not used in this method.
186   - * @param[in] zenith Zenith angle in Earth system (rad). Not used in this method.
187   - * @param[in] azimuth Azimuth angle in Earth system (rad). Not used in this method.
  183 + * @param[in] logEobs Log10 of the observed energy (\f$\log_{10}\f$ TeV).
  184 + * @param[in] logEsrc Log10 of the true photon energy (\f$\log_{10}\f$ TeV).
  185 + * @param[in] theta Offset angle in camera system (radians).
  186 + * @param[in] phi Azimuth angle in camera system (radians). Not used.
  187 + * @param[in] zenith Zenith angle in Earth system (radians). Not used.
  188 + * @param[in] azimuth Azimuth angle in Earth system (radians). Not used.
  189 + * @return Energy dispersion (MeV\f$^{-1}\f$)
188 190 *
189 191 * Returns the energy dispersion
190 192 *
191 193 * \f[
192   - * E_{\rm disp}(\log_{10} E_{\rm reco} | \log_{10} E_{\rm true}, \theta) =
193   - * \frac{dP}{d\log_{10} E_{\rm reco}}
  194 + * E_{\rm disp}(E_{\rm true}, E_{\rm reco}, \theta) =
  195 + * \frac{E_{\rm disp}(E_{\rm true}, m, \theta)}{E_{\rm true}}
194 196 * \f]
195 197 *
196   - * in units of \f$(\log_{10} MeV)^{-1}\f$ where
197   - * \f$\log_{10} E_{\rm reco}\f$ is the logarithm of the reconstructed energy
198   - * in TeV,
199   - * \f$\log_{10} E_{\rm true}\f$ is the logarithm of the true energy in TeV and
200   - * \f$\theta\f$ is the offset angle in radians.
  198 + * in units of MeV\f$^{-1}\f$ where
  199 + * \f$\log_{10} E_{\rm reco}\f$ is the base 10 logarithm of the reconstructed
  200 + * energy in TeV,
  201 + * \f$\log_{10} E_{\rm true}\f$ is the base 10 logarithm of the true energy
  202 + * in TeV and \f$\theta\f$ is the offset angle in radians.
201 203 ***************************************************************************/
202 204 double GCTAEdisp2D::operator()(const double& logEobs,
203 205 const double& logEsrc,
... ... @@ -236,6 +238,19 @@ double GCTAEdisp2D::operator()(const double&amp; logEobs,
236 238 edisp = 0.0;
237 239 }
238 240  
  241 + // If energy dispersion is positive then compute true energy
  242 + // in MeV and divide the energy dispersion value by this
  243 + // quantity to get the energy dispersion per MeV
  244 + else {
  245 +
  246 + // Compute true energy
  247 + double etrue = std::pow(10.0, logEsrc+6.0);
  248 +
  249 + // Compute energy dispersion per MeV
  250 + edisp /= etrue;
  251 +
  252 + }
  253 +
239 254 } // endif: migration was in valid range
240 255  
241 256 } // endif: true energy and offset angle was in valid range
... ... @@ -324,7 +339,7 @@ void GCTAEdisp2D::table(const GCTAResponseTable&amp; table)
324 339 * MATRIX - Migration matrix
325 340 *
326 341 * The data are stored in the m_edisp member. The energy axis will be set to
327   - * log10, the offset angle axis to radians.
  342 + * \f$log_{10} E_{\rm true}\f$, the offset angle axis to radians.
328 343 *
329 344 * The method assures that the energy dispersion is properly normalised.
330 345 ***************************************************************************/
... ... @@ -404,7 +419,7 @@ void GCTAEdisp2D::load(const GFilename&amp; filename)
404 419 * @brief Save energy dispersion table into FITS file
405 420 *
406 421 * @param[in] filename FITS file name.
407   - * @param[in] clobber Overwrite existing file? (default: false)
  422 + * @param[in] clobber Overwrite existing file?
408 423 *
409 424 * Saves energy dispersion into a FITS file. If a file with the given
410 425 * @p filename does not yet exist it will be created, otherwise the method
... ... @@ -454,11 +469,11 @@ void GCTAEdisp2D::save(const GFilename&amp; filename, const bool&amp; clobber) const
454 469 * @brief Simulate energy dispersion
455 470 *
456 471 * @param[in] ran Random number generator.
457   - * @param[in] logEsrc Log10 of the true photon energy (TeV).
458   - * @param[in] theta Offset angle in camera system (rad).
459   - * @param[in] phi Azimuth angle in camera system (rad).
460   - * @param[in] zenith Zenith angle in Earth system (rad).
461   - * @param[in] azimuth Azimuth angle in Earth system (rad).
  472 + * @param[in] logEsrc Log10 of the true photon energy (\f$\log_{10}\f$ TeV).
  473 + * @param[in] theta Offset angle in camera system (radians).
  474 + * @param[in] phi Azimuth angle in camera system (radians). Not used.
  475 + * @param[in] zenith Zenith angle in Earth system (radians). Not used.
  476 + * @param[in] azimuth Azimuth angle in Earth system (radians). Not used.
462 477 * @return Observed energy.
463 478 *
464 479 * @exception GException::invalid_return_value
... ... @@ -565,17 +580,16 @@ GEnergy GCTAEdisp2D::mc(GRan&amp; ran,
565 580 /***********************************************************************//**
566 581 * @brief Return observed energy interval that contains the energy dispersion.
567 582 *
568   - * @param[in] logEsrc Log10 of the true photon energy (TeV).
569   - * @param[in] theta Offset angle in camera system (rad).
570   - * @param[in] phi Azimuth angle in camera system (rad). Not used.
571   - * @param[in] zenith Zenith angle in Earth system (rad). Not used.
572   - * @param[in] azimuth Azimuth angle in Earth system (rad). Not used.
  583 + * @param[in] logEsrc Log10 of the true photon energy (\f$\log_{10}\f$ TeV).
  584 + * @param[in] theta Offset angle in camera system (radians).
  585 + * @param[in] phi Azimuth angle in camera system (radians). Not used.
  586 + * @param[in] zenith Zenith angle in Earth system (radians). Not used.
  587 + * @param[in] azimuth Azimuth angle in Earth system (radians). Not used.
573 588 * @return Observed energy boundaries.
574 589 *
575 590 * Returns the band of observed energies outside of which the energy
576 591 * dispersion becomes negligible for a given true energy @p logEsrc and
577   - * offset angle @p theta. An energy is considered negligible if inferior
578   - * to 1e-6.
  592 + * offset angle @p theta.
579 593 ***************************************************************************/
580 594 GEbounds GCTAEdisp2D::ebounds_obs(const double& logEsrc,
581 595 const double& theta,
... ... @@ -632,17 +646,16 @@ GEbounds GCTAEdisp2D::ebounds_obs(const double&amp; logEsrc,
632 646 /***********************************************************************//**
633 647 * @brief Return true energy interval that contains the energy dispersion.
634 648 *
635   - * @param[in] logEobs Log10 of the observed event energy (TeV).
636   - * @param[in] theta Offset angle in camera system (rad).
637   - * @param[in] phi Azimuth angle in camera system (rad). Not used.
638   - * @param[in] zenith Zenith angle in Earth system (rad). Not used.
639   - * @param[in] azimuth Azimuth angle in Earth system (rad). Not used.
  649 + * @param[in] logEobs Log10 of the observed event energy (\f$\log_{10}\f$ TeV).
  650 + * @param[in] theta Offset angle in camera system (radians).
  651 + * @param[in] phi Azimuth angle in camera system (radians). Not used.
  652 + * @param[in] zenith Zenith angle in Earth system (radians). Not used.
  653 + * @param[in] azimuth Azimuth angle in Earth system (radians). Not used.
640 654 * @return True energy boundaries.
641 655 *
642 656 * Returns the band of true photon energies outside of which the energy
643 657 * dispersion becomes negligible for a given observed energy @p logEobs and
644   - * offset angle @p theta. An energy in considered negligible if inferior to
645   - * 1e-6.
  658 + * offset angle @p theta.
646 659 ***************************************************************************/
647 660 GEbounds GCTAEdisp2D::ebounds_src(const double& logEobs,
648 661 const double& theta,
... ... @@ -799,12 +812,20 @@ void GCTAEdisp2D::fetch(void) const
799 812 * @param[in] ereco_min Minimum of reconstructed energy interval.
800 813 * @param[in] ereco_max Maximum of reconstructed energy interval.
801 814 * @param[in] etrue True energy.
802   - * @param[in] theta Offset angle (rad).
  815 + * @param[in] theta Offset angle (radians).
803 816 * @return Integrated energy dispersion probability.
804 817 *
805   - * Integrates the energy dispersion probability over an interval in
806   - * reconstructed energy, defined by @p ereco_min and @p ereco_max, for a
807   - * given true energy @p etrue and offset angle @p theta.
  818 + * Computes
  819 + *
  820 + * \f[
  821 + * \int_{E_{\rm reco}^{\rm min}}^{E_{\rm reco}^{\rm max}}
  822 + * E_{\rm disp}(E_{\rm true}, E_{\rm reco}, \theta) \, dE_{\rm reco}
  823 + * \f]
  824 + *
  825 + * where
  826 + * \f$E_{\rm reco}\f$ is the reconstructed energy,
  827 + * \f$E_{\rm true}\f$ is the true energy and
  828 + * \f$\theta\f$ is the offset angle.
808 829 *
809 830 * The method takes into account that the energy dispersion data are stored
810 831 * in a matrix and uses the trapezoidal rule for integration of the tabulated
... ... @@ -900,7 +921,7 @@ double GCTAEdisp2D::prob_erecobin(const GEnergy&amp; ereco_min,
900 921 }
901 922  
902 923 // Compute integral
903   - prob += 0.5 * (f1 + f2) * std::log10(x2/x1);
  924 + prob += 0.5 * (f1 + f2) * (x2 - x1);
904 925  
905 926 // Set second node as first node
906 927 x1 = x2;
... ... @@ -922,7 +943,7 @@ double GCTAEdisp2D::prob_erecobin(const GEnergy&amp; ereco_min,
922 943 etrue_nodes.wgt_right(),
923 944 theta_nodes.wgt_left(),
924 945 theta_nodes.wgt_right(), x2);
925   - prob += 0.5 * (f1 + f2) * std::log10(x2/x1);
  946 + prob += 0.5 * (f1 + f2) * (x2 - x1);
926 947 }
927 948  
928 949 } // endif: logEsrc, theta and migration range were valid
... ... @@ -1095,10 +1116,10 @@ void GCTAEdisp2D::free_members(void)
1095 1116 /***********************************************************************//**
1096 1117 * @brief Compute ebounds_obs vector
1097 1118 *
1098   - * @param[in] theta Offset angle (rad).
1099   - * @param[in] phi Azimuth angle (rad).
1100   - * @param[in] zenith Zenith angle (rad).
1101   - * @param[in] azimuth Azimuth angle (rad).
  1119 + * @param[in] theta Offset angle (radians).
  1120 + * @param[in] phi Azimuth angle (radians).
  1121 + * @param[in] zenith Zenith angle (radians).
  1122 + * @param[in] azimuth Azimuth angle (radians).
1102 1123 *
1103 1124 * Computes for all true energies the energy boundaries of the observed
1104 1125 * energies covered by valid migration matrix elements. Only matrix elements
... ... @@ -1207,10 +1228,10 @@ void GCTAEdisp2D::compute_ebounds_obs(const double&amp; theta,
1207 1228 /***********************************************************************//**
1208 1229 * @brief Compute ebounds_src vector
1209 1230 *
1210   - * @param[in] theta Offset angle (rad).
1211   - * @param[in] phi Azimuth angle (rad).
1212   - * @param[in] zenith Zenith angle (rad).
1213   - * @param[in] azimuth Azimuth angle (rad).
  1231 + * @param[in] theta Offset angle (radians).
  1232 + * @param[in] phi Azimuth angle (radians).
  1233 + * @param[in] zenith Zenith angle (radians).
  1234 + * @param[in] azimuth Azimuth angle (radians).
1214 1235 *
1215 1236 * Computes for all observed energies the energy boundaries of the true
1216 1237 * energies covered by valid migration matrix elements. Only matrix elements
... ... @@ -1328,8 +1349,18 @@ void GCTAEdisp2D::compute_ebounds_src(const double&amp; theta,
1328 1349 * @brief Set table
1329 1350 *
1330 1351 * After assigning or loading a response table, performs all necessary steps
1331   - * to set the table. The method also smoothes the table get rid of numerical
1332   - * fluctuations and normalises the table properly.
  1352 + * to set the table. Specifically, the method normalizes the energy
  1353 + * dispersion for each offset angle and true energy so that
  1354 + *
  1355 + * \f[
  1356 + * \int_{m_{\rm min}}^{m_{\rm max}}
  1357 + * E_{\rm disp}(E_{\rm true}, m, \theta) \, dm = 1
  1358 + * \f]
  1359 + *
  1360 + * where \f$m=E_{\rm reco}/E_{\rm true}\f$.
  1361 + *
  1362 + * For CTA, the method applies a kludge that smoothes the energy dispersion
  1363 + * table to get rid of numerical fluctuations.
1333 1364 *
1334 1365 * Sets the data members
1335 1366 * m_inx_etrue
... ... @@ -1447,21 +1478,17 @@ void GCTAEdisp2D::set_max_edisp(void) const
1447 1478 * Normalize the energy dispersion table using
1448 1479 *
1449 1480 * \f[
1450   - * \int_{\log_{10} E_{\rm reco}^{\rm min}}^{\log_{10} E_{\rm reco}^{\rm max}}
1451   - * E_{\rm disp}(\log_{10} E_{\rm reco} | \log_{10} E_{\rm true}, \theta)
1452   - * \, d(\log_{10} E_{\rm reco}) = 1
  1481 + * \int_{E_{\rm reco}^{\rm min}}^{E_{\rm reco}^{\rm max}}
  1482 + * E_{\rm disp}(E_{\rm true}, E_{\rm reco}, \theta) \, dE_{\rm reco} = 1
1453 1483 * \f]
1454 1484 *
1455 1485 * where
1456   - * \f$E_{\rm reco}^{\rm min}\f$ is the minimum and
1457   - * \f$E_{\rm reco}^{\rm max}\f$ the maximum reconstructed energy for a given
1458   - * true energy, returned by the method ebounds_obs(),
1459   - * \f$E_{\rm disp}(\log_{10} E_{\rm reco} | \log_{10} E_{\rm true}, \theta)\f$
1460   - * is the energy dispersion returned by the GCTAEdisp2D::operator(), given in
1461   - * units of \f$(\log_{10} MeV)^{-1}\f$,
1462   - * \f$\log_{10} E_{\rm reco}\f$ is the logarithm of the reconstructued energy
1463   - * in TeV, and
1464   - * \f$\log_{10} E_{\rm true}\f$ is the logarithm of the true energy in TeV.
  1486 + * \f$E_{\rm reco}_{\rm min}\f$ and \f$E_{\rm reco}_{\rm max}\f$ is the
  1487 + * minimum and maximum migration value for a given true energy as returned
  1488 + * by the ebounds_obs() method, and
  1489 + * \f$E_{\rm disp}(E_{\rm true}, E_{\rm reco}, \theta)\f$ is the energy
  1490 + * dispersion returned by the GCTAEdisp2D::operator(), given in units of
  1491 + * \f$MeV^{-1}\f$.
1465 1492 *
1466 1493 * The normalisation is performed for each \f$E_{\rm true}\f$ and
1467 1494 * \f$\theta\f$ bin using a Romberg integration method. Since the
... ... @@ -1498,7 +1525,7 @@ void GCTAEdisp2D::normalize_table(void)
1498 1525 // Loop over true photon energy
1499 1526 for (int i_etrue = 0; i_etrue < etrue_size; ++i_etrue) {
1500 1527  
1501   - // Get energy
  1528 + // Get the base 10 logarithm of the true energy
1502 1529 double emin = std::log10(m_edisp.axis_lo(m_inx_etrue,i_etrue));
1503 1530 double emax = std::log10(m_edisp.axis_hi(m_inx_etrue,i_etrue));
1504 1531 double logEsrc = 0.5*(emin+emax);
... ... @@ -1512,9 +1539,9 @@ void GCTAEdisp2D::normalize_table(void)
1512 1539 // Loop over all energy intervals
1513 1540 for (int i = 0; i < ebounds.size(); ++i) {
1514 1541  
1515   - // Get energy boundaries
1516   - emin = ebounds.emin(i).log10TeV();
1517   - emax = ebounds.emax(i).log10TeV();
  1542 + // Get energy boundaries in log10 of energy in MeV
  1543 + emin = ebounds.emin(i).log10MeV();
  1544 + emax = ebounds.emax(i).log10MeV();
1518 1545  
1519 1546 // Setup integration function
1520 1547 edisp_kern integrand(this, logEsrc, theta);
... ... @@ -1707,6 +1734,9 @@ double GCTAEdisp2D::table_value(const int&amp; base_ll,
1707 1734  
1708 1735 /***********************************************************************//**
1709 1736 * @brief Smoothed energy dispersion table
  1737 + *
  1738 + * This method implements the kludge for CTA that reduces the statistical
  1739 + * noise in the energy dispersion matrix.
1710 1740 ***************************************************************************/
1711 1741 void GCTAEdisp2D::smooth_table(void)
1712 1742 {
... ... @@ -2074,23 +2104,47 @@ GNdarray GCTAEdisp2D::gaussian_array(const double&amp; mean,
2074 2104  
2075 2105  
2076 2106 /***********************************************************************//**
2077   - * @brief Integration kernel for edisp_kern() class
  2107 + * @brief Integration kernel for GCTAEdisp2D::edisp_kern class
  2108 + *
  2109 + * @param[in] logEobs Log10 of observation energy (\f$\log_{10}\f$ MeV).
  2110 + * @return Energy dispersion (\f$(\log_{10}\f$ MeV\f$)^{-1}\f$).
  2111 + *
  2112 + * This method implements the function
  2113 + *
  2114 + * \f[
  2115 + * E_{\rm disp}(E_{\rm true}, \log_{10} E_{\rm reco}, \theta) =
  2116 + * E_{\rm disp}(E_{\rm true}, m, \theta) \times
  2117 + * \frac{\log 10 \times E_{\rm reco}}{E_{\rm true}}
  2118 + * \f]
2078 2119 *
2079   - * @param[in] x Function value.
  2120 + * which is the integration kernel needed for the GCTAEdisp2D::edisp_kern
  2121 + * class. The class is used by GCTAEdisp2D::normalize_table to integrate the
  2122 + * energy dispersion information using
2080 2123 *
2081   - * This method implements the integration kernel needed for the edisp_kern()
2082   - * class.
  2124 + * \f[
  2125 + * \int_{\log_{10} E_{\rm reco}^{\rm min}}^{\log_{10} E_{\rm reco}^{\rm max}}
  2126 + * E_{\rm disp}(E_{\rm true}, \log_{10} E_{\rm reco}, \theta) \,
  2127 + * d(\log_{10} E_{\rm reco})
  2128 + * \f]
2083 2129 ***************************************************************************/
2084   -double GCTAEdisp2D::edisp_kern::eval(const double& x)
  2130 +double GCTAEdisp2D::edisp_kern::eval(const double& logEobs)
2085 2131 {
2086   - // Get function value
2087   - double value = m_parent->operator()(x, m_logEsrc, m_theta);
  2132 + // Get function value. Note that logEobs is in log10 MeV, while we need
  2133 + // the value in log10 TeV.
  2134 + double value = m_parent->operator()(logEobs-6.0, m_logEsrc, m_theta);
  2135 +
  2136 + // Compute reconstructed energy in MeV. Note that logEobs is in
  2137 + // log10 MeV!
  2138 + double ereco = std::pow(10.0, logEobs);
  2139 +
  2140 + // Normalize energy dispersion so that the units are per log10 MeV
  2141 + value *= gammalib::ln10 * ereco;
2088 2142  
2089 2143 // Compile option: Check for NaN
2090 2144 #if defined(G_NAN_CHECK)
2091 2145 if (gammalib::is_notanumber(value) || gammalib::is_infinite(value)) {
2092 2146 std::cout << "*** ERROR: GCTAEdisp2D::edisp_kern::eval";
2093   - std::cout << "(x=" << x << "): ";
  2147 + std::cout << "(logEobs=" << logEobs << "): ";
2094 2148 std::cout << " NaN/Inf encountered";
2095 2149 std::cout << " (value=" << value;
2096 2150 std::cout << ")" << std::endl;
... ...
inst/cta/src/GCTAEdispPerfTable.cpp
... ... @@ -158,34 +158,31 @@ GCTAEdispPerfTable&amp; GCTAEdispPerfTable::operator=(const GCTAEdispPerfTable&amp; edis
158 158  
159 159  
160 160 /***********************************************************************//**
161   - * @brief Return energy dispersion in units of \f$(\log_{10} MeV)^{-1}\f$
  161 + * @brief Return energy dispersion in units of MeV\f$^{-1}\f$
162 162 *
163   - * @param[in] logEobs log10 of the observed photon energy (TeV).
164   - * @param[in] logEsrc log10 of the true photon energy (TeV).
165   - * @param[in] theta Offset angle in camera system (rad). Not used.
166   - * @param[in] phi Azimuth angle in camera system (rad). Not used.
167   - * @param[in] zenith Zenith angle in Earth system (rad). Not used.
168   - * @param[in] azimuth Azimuth angle in Earth system (rad). Not used.
  163 + * @param[in] logEobs Log10 of the observed photon energy (\f$\log_{10}\f$ TeV).
  164 + * @param[in] logEsrc Log10 of the true photon energy (\f$\log_{10}\f$ TeV).
  165 + * @param[in] theta Offset angle in camera system (radians). Not used.
  166 + * @param[in] phi Azimuth angle in camera system (radians). Not used.
  167 + * @param[in] zenith Zenith angle in Earth system (radians). Not used.
  168 + * @param[in] azimuth Azimuth angle in Earth system (radians). Not used.
  169 + * @return Energy dispersion (MeV\f$^{-1}\f$)
169 170 *
170 171 * Returns the energy dispersion
171 172 *
172 173 * \f[
173   - * E_{\rm disp}(\log_{10} E_{\rm reco} | \log_{10} E_{\rm true}) =
174   - * \frac{dP}{d\log_{10} E_{\rm reco}}
  174 + * E_{\rm disp}(E_{\rm true}, E_{\rm reco}) =
  175 + * \frac{1}{\sqrt{2\pi}\sigma(E_{\rm true})}
  176 + * \exp \left(\frac{-(\log_{10} E_{\rm reco} - \log_{10} E_{\rm true})^2}
  177 + * {2 \sigma(E_{\rm true})^2} \right) \times
  178 + * \frac{1}{\log_{10} E_{\rm reco}}
175 179 * \f]
176 180 *
177   - * in units of \f$(\log_{10} MeV)^{-1}\f$ where
178   - * \f$\log_{10} E_{\rm reco}\f$ is the logarithm of the reconstructed energy
179   - * in TeV, and
180   - * \f$\log_{10} E_{\rm true}\f$ is the logarithm of the true energy in TeV.
181   - *
182   - * The operator evaluates
183   - *
184   - * \f[
185   - * E_{\rm disp} = \frac{1}{\sqrt{2\pi}m\_sigma}
186   - * \exp(\frac{-(\log_{10} E_{\rm reco} -
187   - * \log_{10} E_{\rm true})^2}{2 m\_sigma^2})
188   - * \f]
  181 + * in units of MeV\f$^{-1}\f$ where
  182 + * \f$E_{\rm reco}\f$ is the reconstructed energy in units of MeV,
  183 + * \f$E_{\rm true}\f$ is the true energy in units of MeV, and
  184 + * \f$\sigma(E_{\rm true})\f$ is the standard deviation of the energy
  185 + * dispersion that depends on the true photon energy.
189 186 ***************************************************************************/
190 187 double GCTAEdispPerfTable::operator()(const double& logEobs,
191 188 const double& logEsrc,
... ... @@ -201,6 +198,12 @@ double GCTAEdispPerfTable::operator()(const double&amp; logEobs,
201 198 double delta = logEobs - logEsrc;
202 199 double edisp = m_par_scale * std::exp(m_par_width * delta * delta);
203 200  
  201 + // Compute reconstructed energy in MeV
  202 + double ereco = std::pow(10.0, logEobs+6.0);
  203 +
  204 + // Compute energy dispersion per MeV
  205 + edisp /= (gammalib::ln10 * ereco);
  206 +
204 207 // Return energy dispersion
205 208 return edisp;
206 209 }
... ... @@ -318,11 +321,12 @@ void GCTAEdispPerfTable::load(const GFilename&amp; filename)
318 321 * @brief Simulate energy dispersion
319 322 *
320 323 * @param[in] ran Random number generator.
321   - * @param[in] logEsrc Log10 of the true photon energy (TeV).
322   - * @param[in] theta Offset angle in camera system (rad). Not used.
323   - * @param[in] phi Azimuth angle in camera system (rad). Not used.
324   - * @param[in] zenith Zenith angle in Earth system (rad). Not used.
325   - * @param[in] azimuth Azimuth angle in Earth system (rad). Not used.
  324 + * @param[in] logEsrc Log10 of the true photon energy (\f$\log_{10}\f$ TeV).
  325 + * @param[in] theta Offset angle in camera system (radians). Not used.
  326 + * @param[in] phi Azimuth angle in camera system (radians). Not used.
  327 + * @param[in] zenith Zenith angle in Earth system (radians). Not used.
  328 + * @param[in] azimuth Azimuth angle in Earth system (radians). Not used.
  329 + * @return Energy.
326 330 *
327 331 * Draws observed energy value from a normal distribution of width
328 332 * m_par_sigma around @p logE.
... ... @@ -352,11 +356,12 @@ GEnergy GCTAEdispPerfTable::mc(GRan&amp; ran,
352 356 /***********************************************************************//**
353 357 * @brief Return observed energy interval that contains the energy dispersion.
354 358 *
355   - * @param[in] logEsrc Log10 of the true photon energy (TeV).
356   - * @param[in] theta Offset angle in camera system (rad). Not used.
357   - * @param[in] phi Azimuth angle in camera system (rad). Not used.
358   - * @param[in] zenith Zenith angle in Earth system (rad). Not used.
359   - * @param[in] azimuth Azimuth angle in Earth system (rad). Not used.
  359 + * @param[in] logEsrc Log10 of the true photon energy (\f$\log_{10}\f$ TeV).
  360 + * @param[in] theta Offset angle in camera system (radians). Not used.
  361 + * @param[in] phi Azimuth angle in camera system (radians). Not used.
  362 + * @param[in] zenith Zenith angle in Earth system (radians). Not used.
  363 + * @param[in] azimuth Azimuth angle in Earth system (radians). Not used.
  364 + * @return Boundaries of reconstruced energies.
360 365 *
361 366 * Returns the band of observed energies outside of which the energy
362 367 * dispersion becomes negligible for a given true energy @p logEsrc. This
... ... @@ -389,15 +394,16 @@ GEbounds GCTAEdispPerfTable::ebounds_obs(const double&amp; logEsrc,
389 394 /***********************************************************************//**
390 395 * @brief Return true energy interval that contains the energy dispersion.
391 396 *
392   - * @param[in] logEobs Log10 of the observed event energy (TeV).
393   - * @param[in] theta Offset angle in camera system (rad). Not used.
394   - * @param[in] phi Azimuth angle in camera system (rad). Not used.
395   - * @param[in] zenith Zenith angle in Earth system (rad). Not used.
396   - * @param[in] azimuth Azimuth angle in Earth system (rad). Not used.
  397 + * @param[in] logEobs Log10 of the observed event energy (\f$\log_{10}\f$ TeV).
  398 + * @param[in] theta Offset angle in camera system (radians). Not used.
  399 + * @param[in] phi Azimuth angle in camera system (radians). Not used.
  400 + * @param[in] zenith Zenith angle in Earth system (radians). Not used.
  401 + * @param[in] azimuth Azimuth angle in Earth system (radians). Not used.
  402 + * @return Boundaries of true energies.
397 403 *
398 404 * Returns the band of true photon energies outside of which the energy
399   - * dispersion becomes negligible for a given observed energy @p logEobs. This
400   - * band is set to \f$\pm 5 \times \sigma\f$, where \f$\sigma\f$ is the
  405 + * dispersion becomes negligible for a given observed energy @p logEobs.
  406 + * This band is set to \f$\pm 5 \times \sigma\f$, where \f$\sigma\f$ is the
401 407 * Gaussian width of the energy dispersion.
402 408 ***************************************************************************/
403 409 GEbounds GCTAEdispPerfTable::ebounds_src(const double& logEobs,
... ... @@ -430,8 +436,19 @@ GEbounds GCTAEdispPerfTable::ebounds_src(const double&amp; logEobs,
430 436 * @param[in] ereco_min Minimum of reconstructed energy interval.
431 437 * @param[in] ereco_max Maximum of reconstructed energy interval.
432 438 * @param[in] etrue True energy.
433   - * @param[in] theta Offset angle (not used).
  439 + * @param[in] theta Offset angle (radians). Not used.
434 440 * @return Integrated energy dispersion probability.
  441 + *
  442 + * Computes
  443 + *
  444 + * \f[
  445 + * \int_{E_{\rm reco}^{\rm min}}^{E_{\rm reco}^{\rm max}}
  446 + * E_{\rm disp}(E_{\rm true}, E_{\rm reco}) \, dE_{\rm reco}
  447 + * \f]
  448 + *
  449 + * where
  450 + * \f$E_{\rm reco}\f$ is the reconstructed energy and
  451 + * \f$E_{\rm true}\f$ is the true energy.
435 452 ***************************************************************************/
436 453 double GCTAEdispPerfTable::prob_erecobin(const GEnergy& ereco_min,
437 454 const GEnergy& ereco_max,
... ... @@ -556,11 +573,12 @@ void GCTAEdispPerfTable::free_members(void)
556 573 /***********************************************************************//**
557 574 * @brief Update energy dispersion parameter cache
558 575 *
559   - * @param[in] logE Log10 of the true photon energy (TeV).
  576 + * @param[in] logE Log10 of the true photon energy (\f$\log_{10}\f$ TeV).
560 577 *
561   - * This method updates the energy dispersion parameter cache.
562   - * As the performance table energy dispersion only depends on energy,
563   - * the only parameter on which the cache values depend is the energy.
  578 + * This method updates the energy dispersion parameter cache. As the
  579 + * performance table energy dispersion only depends on true photon energy,
  580 + * the only parameter on which the cache values depend is the true photon
  581 + * energy.
564 582 ***************************************************************************/
565 583 void GCTAEdispPerfTable::update(const double& logE) const
566 584 {
... ...
inst/cta/src/GCTAEdispRmf.cpp
... ... @@ -160,27 +160,27 @@ GCTAEdispRmf&amp; GCTAEdispRmf::operator=(const GCTAEdispRmf&amp; edisp)
160 160  
161 161  
162 162 /***********************************************************************//**
163   - * @brief Return energy dispersion in units of \f$(\log_{10} MeV)^{-1}\f$
  163 + * @brief Return energy dispersion in units of MeV\f$^{-1}\f$
164 164 *
165   - * @param[in] logEobs log10 of the observed photon energy (TeV).
166   - * @param[in] logEsrc log10 of the true photon energy (TeV).
167   - * @param[in] theta Offset angle in camera system (rad). Not used.
168   - * @param[in] phi Azimuth angle in camera system (rad). Not used.
169   - * @param[in] zenith Zenith angle in Earth system (rad). Not used.
170   - * @param[in] azimuth Azimuth angle in Earth system (rad). Not used.
171   - * @return Energy dispersion
  165 + * @param[in] logEobs Log10 of the observed photon energy (\f$\log_{10}\f$ TeV).
  166 + * @param[in] logEsrc Log10 of the true photon energy (\f$\log_{10}\f$ TeV).
  167 + * @param[in] theta Offset angle in camera system (radians). Not used.
  168 + * @param[in] phi Azimuth angle in camera system (radians). Not used.
  169 + * @param[in] zenith Zenith angle in Earth system (radians). Not used.
  170 + * @param[in] azimuth Azimuth angle in Earth system (radians). Not used.
  171 + * @return Energy dispersion (MeV\f$^{-1}\f$)
172 172 *
173 173 * Returns the energy dispersion
174 174 *
175 175 * \f[
176   - * E_{\rm disp}(\log_{10} E_{\rm reco} | \log_{10} E_{\rm true}) =
177   - * \frac{dP}{d\log_{10} E_{\rm reco}}
  176 + * E_{\rm disp}(E_{\rm true}, E_{\rm reco}) =
  177 + * \frac{E_{\rm disp}(E_{\rm true}, \log_{10} E_{\rm reco}, \theta)}
  178 + * {\log_{10} E_{\rm reco}}
178 179 * \f]
179 180 *
180   - * in units of \f$(\log_{10} MeV)^{-1}\f$ where
181   - * \f$\log_{10} E_{\rm reco}\f$ is the logarithm of the reconstructed energy
182   - * in TeV, and
183   - * \f$\log_{10} E_{\rm true}\f$ is the logarithm of the true energy in TeV.
  181 + * in units of MeV\f$^{-1}\f$ where
  182 + * \f$E_{\rm reco}\f$ is the reconstructed energy in units of MeV, and
  183 + * \f$E_{\rm true}\f$ is the true energy in units of MeV.
184 184 ***************************************************************************/
185 185 double GCTAEdispRmf::operator()(const double& logEobs,
186 186 const double& logEsrc,
... ... @@ -203,6 +203,11 @@ double GCTAEdispRmf::operator()(const double&amp; logEobs,
203 203 edisp = 0.0;
204 204 }
205 205  
  206 + // If the energy dispersion is positive, return it per MeV
  207 + else {
  208 + edisp /= (gammalib::ln10 * std::pow(10.0, logEobs+6.0));
  209 + }
  210 +
206 211 // Return energy dispersion
207 212 return edisp;
208 213 }
... ... @@ -278,11 +283,12 @@ void GCTAEdispRmf::load(const GFilename&amp; filename)
278 283 * @brief Simulate energy dispersion
279 284 *
280 285 * @param[in] ran Random number generator.
281   - * @param[in] logEsrc Log10 of the true photon energy (TeV).
282   - * @param[in] theta Offset angle in camera system (rad). Not used.
283   - * @param[in] phi Azimuth angle in camera system (rad). Not used.
284   - * @param[in] zenith Zenith angle in Earth system (rad). Not used.
285   - * @param[in] azimuth Azimuth angle in Earth system (rad). Not used.
  286 + * @param[in] logEsrc Log10 of the true photon energy (\f$\log_{10}\f$ TeV).
  287 + * @param[in] theta Offset angle in camera system (radians). Not used.
  288 + * @param[in] phi Azimuth angle in camera system (radians). Not used.
  289 + * @param[in] zenith Zenith angle in Earth system (radians). Not used.
  290 + * @param[in] azimuth Azimuth angle in Earth system (radians). Not used.
  291 + * @return Energy.
286 292 *
287 293 * Draws observed energy value from RMF matrix.
288 294 ***************************************************************************/
... ... @@ -323,11 +329,12 @@ GEnergy GCTAEdispRmf::mc(GRan&amp; ran,
323 329 /***********************************************************************//**
324 330 * @brief Return observed energy interval that contains the energy dispersion.
325 331 *
326   - * @param[in] logEsrc Log10 of the true photon energy (TeV).
327   - * @param[in] theta Offset angle in camera system (rad). Not used.
328   - * @param[in] phi Azimuth angle in camera system (rad). Not used.
329   - * @param[in] zenith Zenith angle in Earth system (rad). Not used.
330   - * @param[in] azimuth Azimuth angle in Earth system (rad). Not used.
  332 + * @param[in] logEsrc Log10 of the true photon energy (\f$\log_{10}\f$ TeV).
  333 + * @param[in] theta Offset angle in camera system (radians). Not used.
  334 + * @param[in] phi Azimuth angle in camera system (radians). Not used.
  335 + * @param[in] zenith Zenith angle in Earth system (radians). Not used.
  336 + * @param[in] azimuth Azimuth angle in Earth system (radians). Not used.
  337 + * @return Reconstructed energy boundaries.
331 338 *
332 339 * Returns the band of observed energies outside of which the energy
333 340 * dispersion becomes negligible for a given true energy @p logEsrc.
... ... @@ -383,11 +390,12 @@ GEbounds GCTAEdispRmf::ebounds_obs(const double&amp; logEsrc,
383 390 /***********************************************************************//**
384 391 * @brief Return true energy interval that contains the energy dispersion.
385 392 *
386   - * @param[in] logEobs Log10 of the observed event energy (TeV).
387   - * @param[in] theta Offset angle in camera system (rad). Not used.
388   - * @param[in] phi Azimuth angle in camera system (rad). Not used.
389   - * @param[in] zenith Zenith angle in Earth system (rad). Not used.
390   - * @param[in] azimuth Azimuth angle in Earth system (rad). Not used.
  393 + * @param[in] logEobs Log10 of the observed event energy (\f$\log_{10}\f$ TeV).
  394 + * @param[in] theta Offset angle in camera system (radians). Not used.
  395 + * @param[in] phi Azimuth angle in camera system (radians). Not used.
  396 + * @param[in] zenith Zenith angle in Earth system (radians). Not used.
  397 + * @param[in] azimuth Azimuth angle in Earth system (radians). Not used.
  398 + * @return True energy boundaries.
391 399 *
392 400 * Returns the band of true photon energies outside of which the energy
393 401 * dispersion becomes negligible for a given observed energy @p logEobs.
... ... @@ -447,8 +455,19 @@ GEbounds GCTAEdispRmf::ebounds_src(const double&amp; logEobs,
447 455 * @param[in] ereco_min Minimum of reconstructed energy interval.
448 456 * @param[in] ereco_max Maximum of reconstructed energy interval.
449 457 * @param[in] etrue True energy.
450   - * @param[in] theta Offset angle (not used).
  458 + * @param[in] theta Offset angle. Not used.
451 459 * @return Integrated energy dispersion probability.
  460 + *
  461 + * Computes
  462 + *
  463 + * \f[
  464 + * \int_{E_{\rm reco}^{\rm min}}^{E_{\rm reco}^{\rm max}}
  465 + * E_{\rm disp}(E_{\rm true}, E_{\rm reco}) \, dE_{\rm reco}
  466 + * \f]
  467 + *
  468 + * where
  469 + * \f$E_{\rm reco}\f$ is the reconstructed energy and
  470 + * \f$E_{\rm true}\f$ is the true energy.
452 471 ***************************************************************************/
453 472 double GCTAEdispRmf::prob_erecobin(const GEnergy& ereco_min,
454 473 const GEnergy& ereco_max,
... ... @@ -783,25 +802,25 @@ void GCTAEdispRmf::set_max_edisp(void) const
783 802 /***********************************************************************//**
784 803 * @brief Update cache
785 804 *
786   - * @param[in] etrue Log10 of true energy in TeV.
787   - * @param[in] emeasured Log10 of measured energy in TeV.
  805 + * @param[in] logEsrc Log10 of true energy (\f$\log_{10}\f$ TeV).
  806 + * @param[in] logEobs Log10 of reconstructed energy (\f$\log_{10}\f$ TeV).
788 807 *
789 808 * Updates the interpolation cache. The interpolation cache is composed
790 809 * of four indices and weights that define 4 data values of the RMF matrix
791 810 * that are used for bilinear interpolation.
792 811 ***************************************************************************/
793   -void GCTAEdispRmf::update(const double& etrue, const double& emeasured) const
  812 +void GCTAEdispRmf::update(const double& logEsrc, const double& logEobs) const
794 813 {
795 814 // Update cache only of arguments have changed
796   - if (etrue != m_last_etrue || emeasured != m_last_emeasured) {
  815 + if (logEsrc != m_last_etrue || logEobs != m_last_emeasured) {
797 816  
798 817 // Store actual values
799   - m_last_etrue = etrue;
800   - m_last_emeasured = emeasured;
  818 + m_last_etrue = logEsrc;
  819 + m_last_emeasured = logEobs;
801 820  
802 821 // Set values for node arrays
803   - m_etrue.set_value(etrue);
804   - m_emeasured.set_value(emeasured);
  822 + m_etrue.set_value(logEsrc);
  823 + m_emeasured.set_value(logEobs);
805 824  
806 825 // Set indices for bi-linear interpolation
807 826 m_itrue1 = m_etrue.inx_left();
... ... @@ -825,10 +844,10 @@ void GCTAEdispRmf::update(const double&amp; etrue, const double&amp; emeasured) const
825 844 /***********************************************************************//**
826 845 * @brief Compute ebounds_obs vector
827 846 *
828   - * @param[in] theta Offset angle in camera system (rad). Not used.
829   - * @param[in] phi Azimuth angle in camera system (rad). Not used.
830   - * @param[in] zenith Zenith angle in Earth system (rad). Not used.
831   - * @param[in] azimuth Azimuth angle in Earth system (rad). Not used.
  847 + * @param[in] theta Offset angle in camera system (radians). Not used.
  848 + * @param[in] phi Azimuth angle in camera system (radians). Not used.
  849 + * @param[in] zenith Zenith angle in Earth system (radians). Not used.
  850 + * @param[in] azimuth Azimuth angle in Earth system (radians). Not used.
832 851 ***************************************************************************/
833 852 void GCTAEdispRmf::compute_ebounds_obs(const double& theta,
834 853 const double& phi,
... ... @@ -861,10 +880,10 @@ void GCTAEdispRmf::compute_ebounds_obs(const double&amp; theta,
861 880 /***********************************************************************//**
862 881 * @brief Compute ebounds_src vector
863 882 *
864   - * @param[in] theta Offset angle in camera system (rad). Not used.
865   - * @param[in] phi Azimuth angle in camera system (rad). Not used.
866   - * @param[in] zenith Zenith angle in Earth system (rad). Not used.
867   - * @param[in] azimuth Azimuth angle in Earth system (rad). Not used.
  883 + * @param[in] theta Offset angle in camera system (radians). Not used.
  884 + * @param[in] phi Azimuth angle in camera system (radians). Not used.
  885 + * @param[in] zenith Zenith angle in Earth system (radians). Not used.
  886 + * @param[in] azimuth Azimuth angle in Earth system (radians). Not used.
868 887 ***************************************************************************/
869 888 void GCTAEdispRmf::compute_ebounds_src(const double& theta,
870 889 const double& phi,
... ...
inst/cta/src/GCTAModelSpatialGradient.cpp
... ... @@ -75,6 +75,28 @@ GCTAModelSpatialGradient::GCTAModelSpatialGradient(void) : GCTAModelSpatial()
75 75  
76 76  
77 77 /***********************************************************************//**
  78 + * @brief Gradient constructor
  79 + *
  80 + * @param[in] detx_gradient DETX gradient (degrees\f$^{-1}\f$).
  81 + * @param[in] dety_gradient DETY gradient (degrees\f$^{-1}\f$).
  82 + ***************************************************************************/
  83 +GCTAModelSpatialGradient::GCTAModelSpatialGradient(const double& detx_gradient,
  84 + const double& dety_gradient) :
  85 + GCTAModelSpatial()
  86 +{
  87 + // Initialise members
  88 + init_members();
  89 +
  90 + // Assign gradients
  91 + this->detx_gradient(detx_gradient);
  92 + this->dety_gradient(dety_gradient);
  93 +
  94 + // Return
  95 + return;
  96 +}
  97 +
  98 +
  99 +/***********************************************************************//**
78 100 * @brief XML constructor
79 101 *
80 102 * @param[in] xml XML element.
... ... @@ -318,7 +340,7 @@ void GCTAModelSpatialGradient::read(const GXmlElement&amp; xml)
318 340 *
319 341 * @param[in] xml XML element.
320 342 *
321   - * @exceptino GException::invalid_value
  343 + * @exception GException::invalid_value
322 344 * Spatial model is not of valid type.
323 345 *
324 346 * Write the gradient spatial model information into an XML element.
... ...
inst/cta/src/GCTAResponseIrf.cpp
... ... @@ -115,6 +115,7 @@
115 115 //#define G_DEBUG_IRF_RADIAL //!< Debug irf_radial method
116 116 //#define G_DEBUG_IRF_DIFFUSE //!< Debug irf_diffuse method
117 117 //#define G_DEBUG_IRF_ELLIPTICAL //!< Debug irf_elliptical method
  118 +//#define G_DEBUG_NROI_EDISP //!< Debug nroi energy dispersion computation
118 119 //#define G_DEBUG_NROI_RADIAL //!< Debug npred_radial method
119 120 //#define G_DEBUG_NROI_ELLIPTICAL //!< Debug npred_elliptical method
120 121 //#define G_DEBUG_NROI_DIFFUSE //!< Debug npred_diffuse method
... ... @@ -455,12 +456,12 @@ double GCTAResponseIrf::nroi(const GModelSky&amp; model,
455 456 // Loop over all boundaries
456 457 for (int i = 0; i < ebounds.size(); ++i) {
457 458  
458   - // Get boundaries in MeV
459   - double emin = ebounds.emin(i).MeV();
460   - double emax = ebounds.emax(i).MeV();
  459 + // Get true energy boundaries in log10 MeV
  460 + double log10_etrue_min = ebounds.emin(i).log10MeV();
  461 + double log10_etrue_max = ebounds.emax(i).log10MeV();
461 462  
462 463 // Continue only if valid
463   - if (emax > emin) {
  464 + if (log10_etrue_max > log10_etrue_min) {
464 465  
465 466 // Setup integration function
466 467 cta_nroi_kern integrand(this, &obs, &model, srcTime, obsEng, obsTime);
... ... @@ -470,14 +471,32 @@ double GCTAResponseIrf::nroi(const GModelSky&amp; model,
470 471 integral.fixed_iter(iter);
471 472  
472 473 // Do Romberg integration
473   - emin = std::log(emin);
474   - emax = std::log(emax);
475   - nroi += integral.romberg(emin, emax);
  474 + nroi += integral.romberg(log10_etrue_min, log10_etrue_max);
476 475  
477 476 } // endif: interval was valid
478 477  
479 478 } // endfor: looped over energy intervals
480 479  
  480 + // Debug energy dispersion computation
  481 + #if defined(G_DEBUG_NROI_EDISP)
  482 + apply_edisp(false);
  483 + const GEnergy& srcEng = obsEng;
  484 + double nroi_spatial = this->nroi(model, srcEng, srcTime, obsEng, obsTime, obs);
  485 + double nroi_spectral = model.spectral()->eval(srcEng, srcTime);
  486 + double nroi_temporal = model.temporal()->eval(srcTime);
  487 + double nroi_noedisp = nroi_spatial * nroi_spectral * nroi_temporal;
  488 + apply_edisp(true);
  489 + std::cout << "GCTAResponseIrf::nroi:";
  490 + std::cout << " obs_id=" << obs.id();
  491 + std::cout << " ereco=" << obsEng;
  492 + std::cout << " nroi(noedisp)=" << nroi_noedisp;
  493 + std::cout << " nroi(edisp)=" << nroi;
  494 + if (nroi_noedisp > 0.0) {
  495 + std::cout << " ratio(edisp/noedisp)=" << nroi/nroi_noedisp;
  496 + }
  497 + std::cout << std::endl;
  498 + #endif
  499 +
481 500 } // endif: energy dispersion requested
482 501  
483 502 // ... otherwise evaluate
... ... @@ -487,12 +506,12 @@ double GCTAResponseIrf::nroi(const GModelSky&amp; model,
487 506 const GEnergy& srcEng = obsEng;
488 507  
489 508 // Compute response components
490   - double npred_spatial = this->nroi(model, srcEng, srcTime, obsEng, obsTime, obs);
491   - double npred_spectral = model.spectral()->eval(srcEng, srcTime);
492   - double npred_temporal = model.temporal()->eval(srcTime);
  509 + double nroi_spatial = this->nroi(model, srcEng, srcTime, obsEng, obsTime, obs);
  510 + double nroi_spectral = model.spectral()->eval(srcEng, srcTime);
  511 + double nroi_temporal = model.temporal()->eval(srcTime);
493 512  
494 513 // Compute response
495   - nroi = npred_spatial * npred_spectral * npred_temporal;
  514 + nroi = nroi_spatial * nroi_spectral * nroi_temporal;
496 515  
497 516 } // endelse: no energy dispersion requested
498 517  
... ... @@ -1783,7 +1802,7 @@ double GCTAResponseIrf::psf_delta_max(const double&amp; theta,
1783 1802  
1784 1803  
1785 1804 /***********************************************************************//**
1786   - * @brief Return energy dispersion (in units of MeV^-1)
  1805 + * @brief Return energy dispersion (in units of MeV\f$^{-1}\f$)
1787 1806 *
1788 1807 * @param[in] obsEng Measured event energy.
1789 1808 * @param[in] theta Radial offset angle in camera (radians).
... ... @@ -1791,6 +1810,7 @@ double GCTAResponseIrf::psf_delta_max(const double&amp; theta,
1791 1810 * @param[in] zenith Zenith angle of telescope pointing (radians).
1792 1811 * @param[in] azimuth Azimuth angle of telescope pointing (radians).
1793 1812 * @param[in] srcLogEng Log10 of true photon energy (E/TeV).
  1813 + * @return Energy dispersion (MeV\f$^{-1}\f$).
1794 1814 ***************************************************************************/
1795 1815 double GCTAResponseIrf::edisp(const GEnergy& obsEng,
1796 1816 const double& theta,
... ... @@ -1799,13 +1819,11 @@ double GCTAResponseIrf::edisp(const GEnergy&amp; obsEng,
1799 1819 const double& azimuth,
1800 1820 const double& srcLogEng) const
1801 1821 {
1802   - // Compute log10 energy in TeV and linear energy in MeV
  1822 + // Compute log10 energy in TeV
1803 1823 double obsLogEng = obsEng.log10TeV();
1804   - double energy = obsEng.MeV();
1805 1824  
1806 1825 // Compute energy dispersion
1807   - double edisp = (*m_edisp)(obsLogEng, srcLogEng, theta, phi, zenith, azimuth) /
1808   - (gammalib::ln10 * energy);
  1826 + double edisp = (*m_edisp)(obsLogEng, srcLogEng, theta, phi, zenith, azimuth);
1809 1827  
1810 1828 // Return energy dispersion
1811 1829 return edisp;
... ... @@ -2707,11 +2725,12 @@ double GCTAResponseIrf::irf_diffuse(const GEvent&amp; event,
2707 2725 // Retrieve CTA pointing
2708 2726 const GCTAPointing& pnt = gammalib::cta_pnt(G_IRF_DIFFUSE, obs);
2709 2727  
2710   - // Try getting the IRF value from cache
  2728 + // Try getting the IRF value from cache. Do not do this for energy
  2729 + // dispersion since the the spectral model will change the IRF values
2711 2730 #if defined(G_USE_IRF_CACHE)
2712 2731 const GCTAEventList* list = dynamic_cast<const GCTAEventList*>(obs.events());
2713 2732 const GCTAEventAtom* atom = dynamic_cast<const GCTAEventAtom*>(&event);
2714   - if (list != NULL && atom != NULL) {
  2733 + if (list != NULL && atom != NULL && !use_edisp()) {
2715 2734 irf = list->irf_cache(source.name(), atom->index());
2716 2735 if (irf >= 0.0) {
2717 2736 has_irf = true;
... ... @@ -2826,9 +2845,11 @@ double GCTAResponseIrf::irf_diffuse(const GEvent&amp; event,
2826 2845 // Apply deadtime correction
2827 2846 irf *= obs.deadc(srcTime);
2828 2847  
2829   - // Put IRF value in cache
  2848 + // Put IRF value in cache. Only do this in no energy dispersion is
  2849 + // requested, since for energy dispersion the IRF values will
  2850 + // depend on the spectrum
2830 2851 #if defined(G_USE_IRF_CACHE)
2831   - if (list != NULL && atom != NULL) {
  2852 + if (list != NULL && atom != NULL && !use_edisp()) {
2832 2853 list->irf_cache(source.name(), atom->index(), irf);
2833 2854 }
2834 2855 #endif
... ... @@ -3315,9 +3336,11 @@ double GCTAResponseIrf::nroi_diffuse(const GModelSky&amp; model,
3315 3336 // Build unique identifier
3316 3337 std::string id = model.name() + "::" + obs.id();
3317 3338  
3318   - // Check if Nroi value is already in cache
  3339 + // Check if Nroi value is already in cache. Do not use the Npred cache
  3340 + // for energy dispersion since the actual value will depend on the
  3341 + // spectral model
3319 3342 #if defined(G_USE_NPRED_CACHE)
3320   - if (!m_npred_names.empty()) {
  3343 + if (!m_npred_names.empty() && !use_edisp()) {
3321 3344  
3322 3345 // Search for unique identifier, and if found, recover Npred value
3323 3346 // and break
... ... @@ -3407,18 +3430,23 @@ double GCTAResponseIrf::nroi_diffuse(const GModelSky&amp; model,
3407 3430 #if defined(G_DEBUG_NROI_DIFFUSE)
3408 3431 std::cout << "GCTAResponseIrf::nroi_diffuse:";
3409 3432 std::cout << " roi_psf_radius=" << roi_psf_radius;
3410   - std::cout << " nroi=" << npred;
  3433 + std::cout << " nroi=" << nroi;
  3434 + std::cout << " Etrue=" << srcEng;
  3435 + std::cout << " Ereco=" << obsEng;
3411 3436 std::cout << " id=" << id << std::endl;
3412 3437 #endif
3413 3438  
3414 3439 } // endif: offset angle range was valid
3415 3440  
3416   - // Store result in Npred cache
  3441 + // Store result in Npred cache in case that no energy dispersion
  3442 + // is requested
3417 3443 #if defined(G_USE_NPRED_CACHE)
3418   - m_npred_names.push_back(id);
3419   - m_npred_energies.push_back(srcEng);
3420   - m_npred_times.push_back(srcTime);
3421   - m_npred_values.push_back(nroi);
  3444 + if (!use_edisp()) {
  3445 + m_npred_names.push_back(id);
  3446 + m_npred_energies.push_back(srcEng);
  3447 + m_npred_times.push_back(srcTime);
  3448 + m_npred_values.push_back(nroi);
  3449 + }
3422 3450 #endif
3423 3451  
3424 3452 // Debug: Check for NaN
... ...
inst/cta/src/GCTAResponse_helpers.cpp
... ... @@ -133,7 +133,7 @@ cta_omega_intervals gammalib::limit_omega(const double&amp; min,
133 133 }
134 134 intervals.push_back(std::make_pair(interval_min,interval_max));
135 135 }
136   -
  136 +
137 137 // If the [min,max] interval overlaps with the [-domega,domega]
138 138 // interval shifted by -2pi then constrain the interval using the
139 139 // shifted interval
... ... @@ -339,7 +339,7 @@ double cta_npsf_kern_rad_azsym::eval(const double&amp; delta)
339 339 {
340 340 // Initialise PSF value
341 341 double value = 0.0;
342   -
  342 +
343 343 // Get arclength for given radius in radians
344 344 double phi = gammalib::cta_roi_arclength(delta,
345 345 m_psf,
... ... @@ -350,7 +350,7 @@ double cta_npsf_kern_rad_azsym::eval(const double&amp; delta)
350 350  
351 351 // If arclength is positive then compute the PSF value
352 352 if (phi > 0) {
353   -
  353 +
354 354 // Compute PSF value
355 355 value = m_rsp->psf(delta, m_theta, m_phi, m_zenith, m_azimuth, m_logE) *
356 356 phi * std::sin(delta);
... ... @@ -366,7 +366,7 @@ double cta_npsf_kern_rad_azsym::eval(const double&amp; delta)
366 366 std::cout << std::endl;
367 367 }
368 368 #endif
369   -
  369 +
370 370 } // endif: arclength was positive
371 371  
372 372 // Return
... ... @@ -375,25 +375,25 @@ double cta_npsf_kern_rad_azsym::eval(const double&amp; delta)
375 375  
376 376  
377 377 /***********************************************************************//**
378   - * @brief Integration kernel for nedisp() method
  378 + * @brief Integration kernel for GCTAResponseIrf::nroi method
379 379 *
380   - * @param[in] logEsrc Logarithm of true photon energy.
  380 + * @param[in] logEsrc Base 10 logarithm of true photon energy in MeV.
381 381 * @return Nroi.
382 382 ***************************************************************************/
383 383 double cta_nroi_kern::eval(const double& logEsrc)
384 384 {
385 385 // Set true energy
386 386 GEnergy srcEng;
387   - double expx = std::exp(logEsrc);
388   - srcEng.MeV(expx);
  387 + srcEng.log10MeV(logEsrc);
389 388  
390 389 // Compute response components
391 390 double nroi_spatial = m_rsp->nroi(*m_model, srcEng, m_srcTime, m_obsEng, m_obsTime, *m_obs);
392 391 double nroi_spectral = m_model->spectral()->eval(srcEng, m_srcTime);
393 392 double nroi_temporal = m_model->temporal()->eval(m_srcTime);
394   -
  393 +
395 394 // Compute response
396   - double nroi = nroi_spatial * nroi_spectral * nroi_temporal * expx;
  395 + double nroi = nroi_spatial * nroi_spectral * nroi_temporal *
  396 + gammalib::ln10 * m_obsEng.MeV();
397 397  
398 398 // Return response
399 399 return nroi;
... ... @@ -567,10 +567,10 @@ double cta_irf_radial_kern_omega::eval(const double&amp; omega)
567 567 {
568 568 // Compute PSF offset angle [radians]
569 569 double delta = std::acos(m_cos_psf + m_sin_psf * std::cos(omega));
570   -
  570 +
571 571 // Compute true photon offset angle in camera system [radians]
572 572 double offset = std::acos(m_cos_ph + m_sin_ph * std::cos(m_omega0 - omega));
573   -
  573 +
574 574 //TODO: Compute true photon azimuth angle in camera system [radians]
575 575 double azimuth = 0.0;
576 576  
... ... @@ -582,7 +582,7 @@ double cta_irf_radial_kern_omega::eval(const double&amp; omega)
582 582 if (m_rsp->use_edisp() && irf > 0.0) {
583 583 irf *= m_rsp->edisp(m_obsEng, offset, azimuth, m_zenith, m_azimuth, m_srcLogEng);
584 584 }
585   -
  585 +
586 586 // Compile option: Check for NaN/Inf
587 587 #if defined(G_NAN_CHECK)
588 588 if (gammalib::is_notanumber(irf) || gammalib::is_infinite(irf)) {
... ... @@ -944,7 +944,7 @@ double cta_irf_elliptical_kern_rho::eval(const double&amp; rho)
944 944 #endif
945 945  
946 946 } // endif: arc length was positive
947   -
  947 +
948 948 } // endif: rho was positive
949 949  
950 950 // Return result
... ... @@ -1035,7 +1035,7 @@ double cta_irf_elliptical_kern_omega::eval(const double&amp; omega)
1035 1035  
1036 1036 // Compute Psf offset angle [radians]
1037 1037 double delta = std::acos(m_cos_psf + m_sin_psf * std::cos(omega));
1038   -
  1038 +
1039 1039 // Compute true photon offset and azimuth angle in camera system
1040 1040 // [radians]
1041 1041 double theta = std::acos(m_cos_ph + m_sin_ph * std::cos(m_omega_pnt - omega));
... ... @@ -1242,7 +1242,7 @@ double cta_nroi_elliptical_kern_rho::eval(const double&amp; rho)
1242 1242 #endif
1243 1243  
1244 1244 } // endif: arc length was positive
1245   -
  1245 +
1246 1246 } // endif: rho was positive
1247 1247  
1248 1248 // Return Nroi
... ... @@ -1302,10 +1302,10 @@ double cta_nroi_elliptical_kern_omega::eval(const double&amp; omega)
1302 1302 std::cout << std::endl;
1303 1303 }
1304 1304 #endif
1305   -
  1305 +
1306 1306 // Continue only if model is positive
1307 1307 if (model > 0.0) {
1308   -
  1308 +
1309 1309 // Compute sky direction vector in native coordinates
1310 1310 double cos_omega = std::cos(omega_model);
1311 1311 double sin_omega = std::sin(omega_model);
... ... @@ -1339,7 +1339,7 @@ double cta_nroi_elliptical_kern_omega::eval(const double&amp; omega)
1339 1339 #endif
1340 1340  
1341 1341 } // endif: sky intensity was positive
1342   -
  1342 +
1343 1343 // Return Nroi
1344 1344 return nroi;
1345 1345 }
... ... @@ -2210,7 +2210,7 @@ double cta_psf_elliptical_kern_rho::eval(const double&amp; rho)
2210 2210 #endif
2211 2211  
2212 2212 } // endif: arc length was positive
2213   -
  2213 +
2214 2214 } // endif: rho was positive
2215 2215  
2216 2216 // Return result
... ...
inst/cta/test/test_CTA.cpp
... ... @@ -901,7 +901,7 @@ void TestGCTAResponse::test_response_edisp_PerfTable(void)
901 901 test_value(edisp2(std::log10(30.0), std::log10(1.0)), 0.0);
902 902  
903 903 // Test that diagonal element is non-zero
904   - test_value(edisp2(std::log10(30.0),std::log10(30.0)), 14.7025305813298);
  904 + test_value(edisp2(std::log10(30.0),std::log10(30.0)), 2.12840930049511e-07);
905 905  
906 906 // Test if non-diagonal element (above diagonal) is zero
907 907 test_value(edisp2(std::log10(1.0), std::log10(30.0)), 0.0);
... ... @@ -911,7 +911,7 @@ void TestGCTAResponse::test_response_edisp_PerfTable(void)
911 911 GEnergy etrue(1.0, "TeV");
912 912 GEnergy emin(0.1, "TeV");
913 913 GEnergy emax(10.0, "TeV");
914   - test_value(edisp2.prob_erecobin(emin, emax, etrue, 0.0), 1.0);
  914 + test_value(edisp2.prob_erecobin(emin, emax, etrue, 0.0), 1.0);
915 915 test_value(edisp2.prob_erecobin(etrue, emax, etrue, 0.0), 0.5);
916 916 test_value(edisp2.prob_erecobin(emin, etrue, etrue, 0.0), 0.5);
917 917  
... ... @@ -958,7 +958,7 @@ void TestGCTAResponse::test_response_edisp_RMF(void)
958 958 test_value(edisp2(std::log10(30.0), std::log10(1.0)), 0.0);
959 959  
960 960 // Test that diagonal element is non-zero
961   - test_value(edisp2(std::log10(30.0),std::log10(30.0)), 7.4480009344395);
  961 + test_value(edisp2(std::log10(30.0),std::log10(30.0)), 1.0785985662216e-07);
962 962  
963 963 // Test if non-diagonal element (above diagonal) is zero
964 964 test_value(edisp2(std::log10(1.0), std::log10(30.0)), 0.0);
... ... @@ -967,13 +967,13 @@ void TestGCTAResponse::test_response_edisp_RMF(void)
967 967 GEnergy etrue(1.0, "TeV");
968 968 GEnergy emin(0.1, "TeV");
969 969 GEnergy emax(10.0, "TeV");
970   - test_value(edisp2.prob_erecobin(emin, emax, etrue, 0.0), 0.999664174482241);
971   - test_value(edisp2.prob_erecobin(etrue, emax, etrue, 0.0), 0.494679101633454);
972   - test_value(edisp2.prob_erecobin(emin, etrue, etrue, 0.0), 0.504985072848788);
  970 + test_value(edisp2.prob_erecobin(emin, emax, etrue, 0.0), 1.00002576227557);
  971 + test_value(edisp2.prob_erecobin(etrue, emax, etrue, 0.0), 0.42065218271377);
  972 + test_value(edisp2.prob_erecobin(emin, etrue, etrue, 0.0), 0.43096191010834);
973 973  
974 974 // Test mc method
975 975 GRan ran;
976   - test_value(edisp2.mc(ran, 0.0).TeV(), 0.942937281664539);
  976 + test_value(edisp2.mc(ran, 0.0).TeV(), 0.930126476703161);
977 977  
978 978 // Test normalisation
979 979 test_edisp_integration(edisp2, 0.5, 10.0);
... ... @@ -1006,14 +1006,14 @@ void TestGCTAResponse::test_response_edisp_2D(void)
1006 1006 test_value(edisp2.ebounds_obs(1.0).emax().TeV(), 13.2011115155942);
1007 1007  
1008 1008 // Test ebounds_src method
1009   - test_value(edisp2.ebounds_src(1.0).emin().TeV(), 7.72198100249182);
1010   - test_value(edisp2.ebounds_src(1.0).emax().TeV(), 19.8348918834691);
  1009 + test_value(edisp2.ebounds_src(1.0).emin().TeV(), 7.7813808018878);
  1010 + test_value(edisp2.ebounds_src(1.0).emax().TeV(), 15.8059295273666);
1011 1011  
1012 1012 // Test if non-diagonal element (below diagonal) is zero
1013 1013 test_value(edisp2(std::log10(30.0), std::log10(1.0)), 0.0);
1014 1014  
1015 1015 // Test that diagonal element is non-zero
1016   - test_value(edisp2(std::log10(30.0),std::log10(30.0)), 11.7884028286084);
  1016 + test_value(edisp2(std::log10(30.0),std::log10(30.0)), 1.74200329848547e-07);
1017 1017  
1018 1018 // Test if non-diagonal element (above diagonal) is zero
1019 1019 test_value(edisp2(std::log10(1.0), std::log10(30.0)), 0.0);
... ... @@ -1022,13 +1022,13 @@ void TestGCTAResponse::test_response_edisp_2D(void)
1022 1022 GEnergy etrue(1.0, "TeV");
1023 1023 GEnergy emin(0.1, "TeV");
1024 1024 GEnergy emax(10.0, "TeV");
1025   - test_value(edisp2.prob_erecobin(emin, emax, etrue, 0.0), 1.00008878464952);
1026   - test_value(edisp2.prob_erecobin(etrue, emax, etrue, 0.0), 0.448839360815326);
1027   - test_value(edisp2.prob_erecobin(emin, etrue, etrue, 0.0), 0.551249618415791);
  1025 + test_value(edisp2.prob_erecobin(emin, emax, etrue, 0.0), 1.00002101900895);
  1026 + test_value(edisp2.prob_erecobin(etrue, emax, etrue, 0.0), 0.480451962185927);
  1027 + test_value(edisp2.prob_erecobin(emin, etrue, etrue, 0.0), 0.519569056823021);
1028 1028  
1029 1029 // Test mc method
1030 1030 GRan ran;
1031   - test_value(edisp2.mc(ran, 0.0).TeV(), 0.97532927330811);
  1031 + test_value(edisp2.mc(ran, 0.0).TeV(), 0.872387491302767);
1032 1032  
1033 1033 // Test normalisation
1034 1034 test_edisp_integration(edisp2);
... ... @@ -1411,9 +1411,9 @@ void TestGCTAResponse::test_response_edispcube(void)
1411 1411 /***********************************************************************//**
1412 1412 * @brief Utility function for energy dispersion tests
1413 1413 *
1414   - * @param[in] rsp Response
1415   - * @param[in] e_src_min Minimum true energy (TeV) (default: 0.1)
1416   - * @param[in] e_src_max Maximum true energy (TeV) (default: 10.0)
  1414 + * @param[in] rsp Response.
  1415 + * @param[in] e_src_min Minimum true energy (TeV).
  1416 + * @param[in] e_src_max Maximum true energy (TeV).
1417 1417 ***************************************************************************/
1418 1418 void TestGCTAResponse::test_response_edisp_integration(const GCTAResponseIrf& rsp,
1419 1419 const double& e_src_min,
... ... @@ -1429,40 +1429,29 @@ void TestGCTAResponse::test_response_edisp_integration(const GCTAResponseIrf&amp; rs
1429 1429 double log10_e_src = std::log10(e_src);
1430 1430  
1431 1431 // Retrieve boundaries in observed energy
1432   - GEbounds ebounds = rsp.edisp()->ebounds_obs(log10_e_src);
1433   - GEnergy emin = ebounds.emin();
1434   - GEnergy emax = ebounds.emax();
1435   - double logE_min = std::log10(emin.TeV());
1436   - double logE_max = std::log10(emax.TeV());
1437   -
1438   - // Compute step size for numerical integration
1439   - const int steps = 1000;
1440   - double dlogE = (logE_max-logE_min)/steps;
  1432 + GEbounds ebounds = rsp.edisp()->ebounds_obs(log10_e_src);
  1433 + double emin = ebounds.emin().MeV();
  1434 + double emax = ebounds.emax().MeV();
1441 1435  
1442 1436 // Perform numerical integration by summing
1443   - double sum = 0.0;
1444   - double logE_obs = logE_min;
1445   - for (int i = 0; i < steps; ++i) {
1446   - double dp_dlogE = (*rsp.edisp())(logE_obs, log10_e_src);
1447   - sum += dp_dlogE * dlogE;
1448   - logE_obs += dlogE;
1449   - }
1450   - GEnergy eng(e_src, "TeV");
1451   - test_value(sum, 1.0, 0.001, "Energy Dispersion integration for "+eng.print());
1452   -
1453   - // And now in linear energies, test GCTAResponse::edisp() method
1454 1437 const int nE = 10000;
1455   - double E_min = emin.MeV();
1456   - double E_max = emax.MeV();
1457   - double dE = (E_max-E_min)/nE;
  1438 + double dE = (emax-emin)/nE;
  1439 + double sum1 = 0.0;
1458 1440 double sum2 = 0.0;
1459   - double E_obs = E_min;
1460   - for (int i = 0; i < nE; ++i) {
1461   - double dp_dE = rsp.edisp(GEnergy(E_obs,"MeV"), 0.0, 0.0, 0.0, 0.0, log10_e_src);
1462   - sum2 += dp_dE * dE;
1463   - E_obs += dE;
  1441 + double E_obs = emin;
  1442 + for (int i = 0; i < nE; ++i, E_obs += dE) {
  1443 + GEnergy ereco(E_obs,"MeV");
  1444 + double dp1 = (*rsp.edisp())(ereco.log10TeV(), log10_e_src);
  1445 + double dp2 = rsp.edisp(ereco, 0.0, 0.0, 0.0, 0.0, log10_e_src);
  1446 + sum1 += dp1 * dE;
  1447 + sum2 += dp2 * dE;
1464 1448 }
1465   - test_value(sum2, 1.0, 0.001, "Energy Dispersion integration for "+eng.print());
  1449 + GEnergy eng(e_src, "TeV");
  1450 + test_value(sum1, 1.0, 0.001, "Energy dispersion integration using "+
  1451 + rsp.edisp()->classname()+"::operator() "
  1452 + "for "+eng.print());
  1453 + test_value(sum2, 1.0, 0.001, "Energy dispersion integration using "
  1454 + "GCTAResponseIrf::edisp() for "+eng.print());
1466 1455 }
1467 1456  
1468 1457 } // endif: energy dispersion was available
... ... @@ -1475,15 +1464,17 @@ void TestGCTAResponse::test_response_edisp_integration(const GCTAResponseIrf&amp; rs
1475 1464 /***********************************************************************//**
1476 1465 * @brief Utility function for energy dispersion integration tests
1477 1466 *
1478   - * @param[in] edisp Energy dispersion
1479   - * @param[in] e_src_min Minimum true energy (TeV) (default: 0.1)
1480   - * @param[in] e_src_max Maximum true energy (TeV) (default: 10.0)
  1467 + * @param[in] edisp Energy dispersion instance.
  1468 + * @param[in] e_src_min Minimum true energy (TeV).
  1469 + * @param[in] e_src_max Maximum true energy (TeV).
  1470 + *
  1471 + * Tests whether the energy dispersion integrated over observed energies
  1472 + * gives unity.
1481 1473 ***************************************************************************/
1482   -void TestGCTAResponse::test_edisp_integration(const GCTAEdisp& edisp,
1483   - const double& e_src_min,
1484   - const double& e_src_max)
  1474 +void TestGCTAResponse::test_edisp_integration(const GCTAEdisp& edisp,
  1475 + const double& e_src_min,
  1476 + const double& e_src_max)
1485 1477 {
1486   -
1487 1478 // Loop over source energies
1488 1479 for (double e_src = e_src_min; e_src <= e_src_max; e_src *= 2.0) {
1489 1480  
... ... @@ -1498,30 +1489,22 @@ void TestGCTAResponse::test_edisp_integration(const GCTAEdisp&amp; edisp,
1498 1489 continue;
1499 1490 }
1500 1491  
1501   - // Set measured energy range
1502   - GEnergy emin = ebounds.emin();
1503   - GEnergy emax = ebounds.emax();
1504   - double logE_min = std::log10(emin.TeV());
1505   - double logE_max = std::log10(emax.TeV());
1506   -
1507   - // Compute step size for numerical integration
1508   - const int steps = 1000;
1509   - double dlogE = (logE_max-logE_min)/steps;
1510   -
1511 1492 // Perform numerical integration by summing
1512   - double sum = 0.0;
1513   - double logE_obs = logE_min;
1514   - for (int i = 0; i <= steps; ++i) {
1515   - double dp_dlogE = edisp(logE_obs, log10_e_src);
1516   - sum += dp_dlogE * dlogE;
1517   - logE_obs += dlogE;
  1493 + const int nE = 10000;
  1494 + double emin = ebounds.emin().MeV();
  1495 + double emax = ebounds.emax().MeV();
  1496 + double dE = (emax-emin)/nE;
  1497 + double sum = 0.0;
  1498 + double E_obs = emin;
  1499 + for (int i = 0; i < nE; ++i, E_obs += dE) {
  1500 + GEnergy ereco(E_obs,"MeV");
  1501 + double dp = edisp(ereco.log10TeV(), log10_e_src);
  1502 + sum += dp * dE;
1518 1503 }
1519   -
1520   - // Set message string
1521   - GEnergy eng(e_src, "TeV");
1522   - std::string msg = edisp.classname()+" integration for "+eng.print();
1523   - test_value(sum, 1.0, 0.01, msg);
1524   -
  1504 + GEnergy eng(e_src, "TeV");
  1505 + test_value(sum, 1.0, 0.001, "Energy dispersion integration using "+
  1506 + edisp.classname()+"::operator() for "+
  1507 + eng.print());
1525 1508 }
1526 1509  
1527 1510 // Return
... ...
inst/cta/test/test_CTA.hpp
... ... @@ -95,9 +95,9 @@ public:
95 95 void test_response_edisp_integration(const GCTAResponseIrf& rsp,
96 96 const double& e_src_min = 0.1,
97 97 const double& e_src_max = 10.0);
98   - void test_edisp_integration(const GCTAEdisp& edisp,
99   - const double& e_src_min = 0.1,
100   - const double& e_src_max = 10.0);
  98 + void test_edisp_integration(const GCTAEdisp& edisp,
  99 + const double& e_src_min = 0.1,
  100 + const double& e_src_max = 10.0);
101 101 };
102 102  
103 103  
... ...
inst/cta/test/test_CTA.py
... ... @@ -250,12 +250,12 @@ class Test(gammalib.GPythonTestSuite):
250 250 edisp = gammalib.GCTAEdispPerfTable(filename)
251 251  
252 252 # Test energy dispersion values
253   - self.test_value(edisp(0.0, 0.0), 9.99019627861, 1.0e-6)
254   - self.test_value(edisp(0.001, 0.0), 9.9870644077, 1.0e-6)
255   - self.test_value(edisp(0.01, 0.0), 9.68182, 1.0e-6)
256   - self.test_value(edisp(0.1, 0.0), 0.434382, 1.0e-6)
257   - self.test_value(edisp(1.0, 1.0), 18.064868197, 1.0e-6)
258   - self.test_value(edisp(1.001, 1.0, 0.0), 18.0463571212, 1.0e-6)
  253 + self.test_value(edisp(0.0, 0.0), 4.3386871e-06, 1.0e-6)
  254 + self.test_value(edisp(0.001, 0.0), 4.3273514e-06, 1.0e-6)
  255 + self.test_value(edisp(0.01, 0.0), 4.1090489e-06, 1.0e-6)
  256 + self.test_value(edisp(0.1, 0.0), 1.4984969e-07, 1.0e-6)
  257 + self.test_value(edisp(1.0, 1.0), 7.8454726e-07, 1.0e-6)
  258 + self.test_value(edisp(1.001, 1.0, 0.0), 7.8194077e-07, 1.0e-6)
259 259  
260 260 # Test GCTAResponseIrf file constructor
261 261 db = gammalib.GCaldb(self._caldb)
... ... @@ -397,6 +397,7 @@ class Test(gammalib.GPythonTestSuite):
397 397 test_support.pickeling(self, gammalib.GCTAEventCube())
398 398 test_support.pickeling(self, gammalib.GCTAEventList())
399 399 test_support.pickeling(self, gammalib.GCTAInstDir())
  400 + test_support.pickeling(self, gammalib.GCTAModelBackground())
400 401 test_support.pickeling(self, gammalib.GCTAModelAeffBackground())
401 402 test_support.pickeling(self, gammalib.GCTAModelCubeBackground())
402 403 test_support.pickeling(self, gammalib.GCTAModelIrfBackground())
... ... @@ -404,6 +405,8 @@ class Test(gammalib.GPythonTestSuite):
404 405 test_support.pickeling(self, gammalib.GCTAModelRadialGauss())
405 406 test_support.pickeling(self, gammalib.GCTAModelRadialPolynom())
406 407 test_support.pickeling(self, gammalib.GCTAModelRadialProfile())
  408 + test_support.pickeling(self, gammalib.GCTAModelSpatialGradient())
  409 + test_support.pickeling(self, gammalib.GCTAModelSpatialMultiplicative())
407 410 test_support.pickeling(self, gammalib.GCTAObservation())
408 411 test_support.pickeling(self, gammalib.GCTAOnOffObservation())
409 412 test_support.pickeling(self, gammalib.GCTAPointing())
... ... @@ -467,6 +470,9 @@ class Test(gammalib.GPythonTestSuite):
467 470 obs5 = gammalib.GCTAOnOffObservation(obs1, ptsrc, ebds, ebds, regs, regs)
468 471 radgauss = gammalib.GCTAModelRadialGauss(1.0)
469 472 radacc = gammalib.GCTAModelRadialAcceptance(radgauss, plaw)
  473 + multi = gammalib.GCTAModelSpatialMultiplicative()
  474 + multi.append(radgauss, 'Src1')
  475 + multi.append(radgauss, 'Src2')
470 476 kingname = self._caldb + '/prod1_king.fits'
471 477 hessaeff = self._data + '/irf_hess_aeff.fits.gz'
472 478 hesspsf = self._data + '/irf_hess_psf.fits.gz'
... ... @@ -498,6 +504,7 @@ class Test(gammalib.GPythonTestSuite):
498 504 test_support.pickeling(self, gammalib.GCTAEventCube(cube))
499 505 test_support.pickeling(self, gammalib.GCTAEventList(list))
500 506 test_support.pickeling(self, gammalib.GCTAInstDir(instdir))
  507 + test_support.pickeling(self, gammalib.GCTAModelBackground(radgauss,plaw))
501 508 test_support.pickeling(self, gammalib.GCTAModelAeffBackground(plaw))
502 509 test_support.pickeling(self, gammalib.GCTAModelCubeBackground(plaw))
503 510 test_support.pickeling(self, gammalib.GCTAModelIrfBackground(plaw))
... ... @@ -505,6 +512,8 @@ class Test(gammalib.GPythonTestSuite):
505 512 test_support.pickeling(self, gammalib.GCTAModelRadialGauss(1.0))
506 513 test_support.pickeling(self, gammalib.GCTAModelRadialPolynom([1.0,2.0]))
507 514 test_support.pickeling(self, gammalib.GCTAModelRadialProfile(1.0,2.0,3.0))
  515 + test_support.pickeling(self, gammalib.GCTAModelSpatialGradient(1.0,2.0))
  516 + test_support.pickeling(self, gammalib.GCTAModelSpatialMultiplicative(multi))
508 517 test_support.pickeling(self, gammalib.GCTAObservation(obs1))
509 518 test_support.pickeling(self, gammalib.GCTAObservation(obs2))
510 519 test_support.pickeling(self, gammalib.GCTAObservation(obs3))
... ...
src/model/GModelSpectralExponential.cpp
... ... @@ -423,11 +423,11 @@ GEnergy GModelSpectralExponential::mc(const GEnergy&amp; emin,
423 423 * Reads the spectral information from an XML element. The XML element shall
424 424 * have the following format
425 425 *
426   - * <spectrum type="Exponential">
427   - * <spectrum type="Constant">
428   - * <parameter name="Normalization" scale="1" value="1" min="0" max="2" free="1"/>
429   - * </spectrum>
430   - * </spectrum>
  426 + * <spectrum type="Exponential">
  427 + * <spectrum type="Constant">
  428 + * <parameter name="Normalization" scale="1" value="1" min="0" max="2" free="1"/>
  429 + * </spectrum>
  430 + * </spectrum>
431 431 *
432 432 ***************************************************************************/
433 433 void GModelSpectralExponential::read(const GXmlElement& xml)
... ... @@ -469,17 +469,17 @@ void GModelSpectralExponential::read(const GXmlElement&amp; xml)
469 469 * Writes the spectral information into an XML element. The XML element
470 470 * will have the following format:
471 471 *
472   - * <spectrum type="Exponential">
473   - * <spectrum type="Constant">
474   - * <parameter name="Normalization" scale="1" value="1" min="0" max="2" free="1"/>
475   - * </spectrum>
476   - * </spectrum>
  472 + * <spectrum type="Exponential">
  473 + * <spectrum type="Constant">
  474 + * <parameter name="Normalization" scale="1" value="1" min="0" max="2" free="1"/>
  475 + * </spectrum>
  476 + * </spectrum>
477 477 *
478 478 * If no exponential model component is defined the method writes the
479 479 * following XML structure
480 480 *
481   - * <spectrum type="Exponential">
482   - * </spectrum>
  481 + * <spectrum type="Exponential">
  482 + * </spectrum>
483 483 *
484 484 ***************************************************************************/
485 485 void GModelSpectralExponential::write(GXmlElement& xml) const
... ...
src/obs/GResponse.cpp
1 1 /***************************************************************************
2 2 * GResponse.cpp - Abstract response base class *
3 3 * ----------------------------------------------------------------------- *
4   - * copyright (C) 2008-2016 by Juergen Knoedlseder *
  4 + * copyright (C) 2008-2018 by Juergen Knoedlseder *
5 5 * ----------------------------------------------------------------------- *
6 6 * *
7 7 * This program is free software: you can redistribute it and/or modify *
... ... @@ -57,6 +57,7 @@
57 57 /* __ Coding definitions _________________________________________________ */
58 58  
59 59 /* __ Debug definitions __________________________________________________ */
  60 +#define G_DEBUG_CONVOLVE_EDISP //!< Debug convolve for energy dispersion
60 61  
61 62  
62 63 /*==========================================================================
... ... @@ -187,19 +188,19 @@ double GResponse::convolve(const GModelSky&amp; model,
187 188  
188 189 // Case A: Integration
189 190 if (use_edisp()) {
190   -
  191 +
191 192 // Retrieve true energy boundaries
192 193 GEbounds ebounds = this->ebounds(event.energy());
193   -
  194 +
194 195 // Loop over all boundaries
195 196 for (int i = 0; i < ebounds.size(); ++i) {
196 197  
197   - // Get boundaries in MeV
198   - double emin = ebounds.emin(i).MeV();
199   - double emax = ebounds.emax(i).MeV();
  198 + // Get true energy boundaries in log10 MeV
  199 + double log10_etrue_min = ebounds.emin(i).log10MeV();
  200 + double log10_etrue_max = ebounds.emax(i).log10MeV();
200 201  
201 202 // Continue only if valid
202   - if (emax > emin) {
  203 + if (log10_etrue_max > log10_etrue_min) {
203 204  
204 205 // Setup integration function
205 206 edisp_kern integrand(this, &obs, &model, &event, srcTime, grad);
... ... @@ -209,10 +210,8 @@ double GResponse::convolve(const GModelSky&amp; model,
209 210 integral.fixed_iter(iter);
210 211  
211 212 // Do Romberg integration
212   - emin = std::log(emin);
213   - emax = std::log(emax);
214   - prob += integral.romberg(emin, emax);
215   -
  213 + prob += integral.romberg(log10_etrue_min, log10_etrue_max);
  214 +
216 215 } // endif: interval was valid
217 216  
218 217 } // endfor: looped over intervals
... ... @@ -221,7 +220,7 @@ double GResponse::convolve(const GModelSky&amp; model,
221 220  
222 221 // Case B: No integration (assume no energy dispersion)
223 222 else {
224   -
  223 +
225 224 // Get source energy (no dispersion)
226 225 GEnergy srcEng = event.energy();
227 226  
... ... @@ -410,39 +409,34 @@ double GResponse::eval_prob(const GModelSky&amp; model,
410 409  
411 410  
412 411 /***********************************************************************//**
413   - * @brief Integration kernel for edisp_kern() method
  412 + * @brief Integration kernel for GResponse::edisp_kern() class
414 413 *
415   - * @param[in] x Function value.
  414 + * @param[in] logEsrc Base 10 logarithm of true photon energy in MeV.
416 415 *
417   - * This method implements the integration kernel needed for the edisp_kern()
418   - * method.
  416 + * This method implements the integration kernel needed for the
  417 + * GResponse::edisp_kern() class.
419 418 ***************************************************************************/
420   -double GResponse::edisp_kern::eval(const double& x)
  419 +double GResponse::edisp_kern::eval(const double& logEsrc)
421 420 {
422   - // Set energy
423   - GEnergy eng;
424   - double expx = std::exp(x);
425   - eng.MeV(expx);
  421 + // Set true energy
  422 + GEnergy srcEng;
  423 + srcEng.log10MeV(logEsrc);
426 424  
427 425 // Get function value
428   - double value = m_parent->eval_prob(*m_model, *m_event, eng, m_srcTime, *m_obs, m_grad);
429   -
430   - // Save value if needed
431   - #if defined(G_NAN_CHECK)
432   - double value_out = value;
433   - #endif
  426 + double value = m_parent->eval_prob(*m_model, *m_event,
  427 + srcEng, m_srcTime, *m_obs, m_grad);
434 428  
435   - // Correct for variable substitution
436   - value *= expx;
  429 + // Account for the fact that integration is done in log10 MeV and
  430 + // not in MeV
  431 + value *= gammalib::ln10 * m_event->energy().MeV();
437 432  
438 433 // Compile option: Check for NaN
439 434 #if defined(G_NAN_CHECK)
440 435 if (gammalib::is_notanumber(value) || gammalib::is_infinite(value)) {
441 436 std::cout << "*** ERROR: GResponse::edisp_kern::eval";
442   - std::cout << "(x=" << x << "): ";
  437 + std::cout << "(logEsrc=" << logEsrc << "): ";
443 438 std::cout << " NaN/Inf encountered";
444   - std::cout << " (value=" << value_out;
445   - std::cout << " exp(x)=" << expx;
  439 + std::cout << " (value=" << value;
446 440 std::cout << ")" << std::endl;
447 441 }
448 442 #endif
... ...