Commit f54cd3020f284c6d6262c9fa3e05ff72faaa04cc

Authored by Jürgen Knödlseder
1 parent 92d74e59

Adjust the interface for On/Off observations

inst/cta/include/GCTAOnOffObservation.hpp
@@ -40,20 +40,33 @@ @@ -40,20 +40,33 @@
40 class GModels; 40 class GModels;
41 class GOptimizerPars; 41 class GOptimizerPars;
42 class GCTAObservation; 42 class GCTAObservation;
  43 +class GBounds;
43 44
44 45
45 /***********************************************************************//** 46 /***********************************************************************//**
46 * @class GCTAOnOffObservation 47 * @class GCTAOnOffObservation
47 * 48 *
48 * @brief CTA On/Off observation class 49 * @brief CTA On/Off observation class
  50 + *
  51 + * This class defines a CTA On/Off observation. An On/Off observation is
  52 + * defined by two spectra, one for an On region including the source of
  53 + * interest, and one for an Off region including only background. The
  54 + * response of an On/Off observation is given by the Auxiliary Response File
  55 + * (ARF) and the Redistribution Matrix File (RMF).
  56 + *
  57 + * The class uses GPha objects to store the On and Off spectra, and GArf and
  58 + * GRmf objects to store the response information.
49 ***************************************************************************/ 59 ***************************************************************************/
50 class GCTAOnOffObservation : public GObservation { 60 class GCTAOnOffObservation : public GObservation {
51 61
52 public: 62 public:
53 // Constructors and destructors 63 // Constructors and destructors
54 GCTAOnOffObservation(void); 64 GCTAOnOffObservation(void);
55 - GCTAOnOffObservation(const GEbounds& ereco, const GSkyRegions& on,  
56 - const GSkyRegions& off); 65 + GCTAOnOffObservation(const GCTAObservation& obs,
  66 + const GEbounds& etrue,
  67 + const GEbounds& ereco,
  68 + const GSkyRegions& on,
  69 + const GSkyRegions& off);
57 GCTAOnOffObservation(const GCTAOnOffObservation& obs); 70 GCTAOnOffObservation(const GCTAOnOffObservation& obs);
58 virtual ~GCTAOnOffObservation(void); 71 virtual ~GCTAOnOffObservation(void);
59 72
@@ -82,28 +95,28 @@ public: @@ -82,28 +95,28 @@ public:
82 virtual int nobserved(void) const; 95 virtual int nobserved(void) const;
83 96
84 // Other methods 97 // Other methods
85 - void instrument(const std::string& instrument);  
86 - void on_regions(const GSkyRegions& regions);  
87 - void off_regions(const GSkyRegions& regions);  
88 - const GPha& on_spec(void) const;  
89 - const GPha& off_spec(void) const;  
90 - const GArf& arf(void) const;  
91 - const GRmf& rmf(void) const;  
92 - void fill(const GCTAObservation& obs);  
93 - void compute_response(const GCTAObservation& obs,  
94 - const GEbounds& etrue); 98 + void instrument(const std::string& instrument);
  99 + void on_regions(const GSkyRegions& regions);
  100 + void off_regions(const GSkyRegions& regions);
  101 + const GSkyRegions& on_regions(void) const;
  102 + const GSkyRegions& off_regions(void) const;
  103 + const GPha& on_spec(void) const;
  104 + const GPha& off_spec(void) const;
  105 + const GArf& arf(void) const;
  106 + const GRmf& rmf(void) const;
95 107
96 protected: 108 protected:
97 // Protected methods 109 // Protected methods
98 void init_members(void); 110 void init_members(void);
99 void copy_members(const GCTAOnOffObservation& obs); 111 void copy_members(const GCTAOnOffObservation& obs);
100 void free_members(void); 112 void free_members(void);
101 - void compute_alpha(const GCTAObservation& obs); 113 + void set(const GCTAObservation& obs);
102 void compute_arf(const GCTAObservation& obs); 114 void compute_arf(const GCTAObservation& obs);
103 void compute_bgd(const GCTAObservation& obs); 115 void compute_bgd(const GCTAObservation& obs);
104 - void compute_rmf(const GCTAObservation& obs, const GEbounds& etrue);  
105 - double model_on(const GModels& models, int ibin, GVector* mod_grad) const;  
106 - double model_off(const GModels& models, int ibin, GVector* mod_grad) const; 116 + void compute_alpha(const GCTAObservation& obs);
  117 + void compute_rmf(const GCTAObservation& obs);
  118 + double N_gamma(const GModels& models, const int& ibin, GVector* grad) const;
  119 + double N_bgd(const GModels& models, const int& ibin, GVector* grad) const;
107 120
108 // Protected data members 121 // Protected data members
109 std::string m_instrument; //!< Instrument name 122 std::string m_instrument; //!< Instrument name
@@ -228,9 +241,33 @@ void GCTAOnOffObservation::off_regions(const GSkyRegions&amp; regions) @@ -228,9 +241,33 @@ void GCTAOnOffObservation::off_regions(const GSkyRegions&amp; regions)
228 241
229 242
230 /***********************************************************************//** 243 /***********************************************************************//**
231 - * @brief Return ON spectrum 244 + * @brief Return On regions
  245 + *
  246 + * @return On regions.
  247 + ***************************************************************************/
  248 +inline
  249 +const GSkyRegions& GCTAOnOffObservation::on_regions(void) const
  250 +{
  251 + return (m_on_regions);
  252 +}
  253 +
  254 +
  255 +/***********************************************************************//**
  256 + * @brief Return Off regions
  257 + *
  258 + * @return Off regions.
  259 + ***************************************************************************/
  260 +inline
  261 +const GSkyRegions& GCTAOnOffObservation::off_regions(void) const
  262 +{
  263 + return (m_off_regions);
  264 +}
  265 +
  266 +
  267 +/***********************************************************************//**
  268 + * @brief Return On spectrum
232 * 269 *
233 - * @return ON spectrum. 270 + * @return On spectrum.
234 ***************************************************************************/ 271 ***************************************************************************/
235 inline 272 inline
236 const GPha& GCTAOnOffObservation::on_spec(void) const 273 const GPha& GCTAOnOffObservation::on_spec(void) const
@@ -240,9 +277,9 @@ const GPha&amp; GCTAOnOffObservation::on_spec(void) const @@ -240,9 +277,9 @@ const GPha&amp; GCTAOnOffObservation::on_spec(void) const
240 277
241 278
242 /***********************************************************************//** 279 /***********************************************************************//**
243 - * @brief Return OFF spectrum 280 + * @brief Return Off spectrum
244 * 281 *
245 - * @return OFF spectrum. 282 + * @return Off spectrum.
246 ***************************************************************************/ 283 ***************************************************************************/
247 inline 284 inline
248 const GPha& GCTAOnOffObservation::off_spec(void) const 285 const GPha& GCTAOnOffObservation::off_spec(void) const
inst/cta/pyext/GCTAOnOffObservation.i
@@ -63,8 +63,11 @@ class GCTAOnOffObservation : public GObservation { @@ -63,8 +63,11 @@ class GCTAOnOffObservation : public GObservation {
63 public: 63 public:
64 // Constructors and destructors 64 // Constructors and destructors
65 GCTAOnOffObservation(void); 65 GCTAOnOffObservation(void);
66 - GCTAOnOffObservation(const GEbounds& ereco, const GSkyRegions& on,  
67 - const GSkyRegions& off); 66 + GCTAOnOffObservation(const GCTAObservation& obs,
  67 + const GEbounds& etrue,
  68 + const GEbounds& ereco,
  69 + const GSkyRegions& on,
  70 + const GSkyRegions& off);
68 GCTAOnOffObservation(const GCTAOnOffObservation& obs); 71 GCTAOnOffObservation(const GCTAOnOffObservation& obs);
69 virtual ~GCTAOnOffObservation(void); 72 virtual ~GCTAOnOffObservation(void);
70 73
@@ -89,16 +92,15 @@ public: @@ -89,16 +92,15 @@ public:
89 virtual int nobserved(void) const; 92 virtual int nobserved(void) const;
90 93
91 // Other methods 94 // Other methods
92 - void instrument(const std::string& instrument);  
93 - void on_regions(const GSkyRegions& regions);  
94 - void off_regions(const GSkyRegions& regions);  
95 - const GPha& on_spec(void) const;  
96 - const GPha& off_spec(void) const;  
97 - const GArf& arf(void) const;  
98 - const GRmf& rmf(void) const;  
99 - void fill(const GCTAObservation& obs);  
100 - void compute_response(const GCTAObservation& obs,  
101 - const GEbounds& etrue); 95 + void instrument(const std::string& instrument);
  96 + void on_regions(const GSkyRegions& regions);
  97 + void off_regions(const GSkyRegions& regions);
  98 + const GSkyRegions& on_regions(void) const;
  99 + const GSkyRegions& off_regions(void) const;
  100 + const GPha& on_spec(void) const;
  101 + const GPha& off_spec(void) const;
  102 + const GArf& arf(void) const;
  103 + const GRmf& rmf(void) const;
102 }; 104 };
103 105
104 106
inst/cta/src/GCTAOnOffObservation.cpp
@@ -55,16 +55,15 @@ const GObservationRegistry g_onoff_obs_cta_registry(&amp;g_onoff_obs_cta_seed); @@ -55,16 +55,15 @@ const GObservationRegistry g_onoff_obs_cta_registry(&amp;g_onoff_obs_cta_seed);
55 #define G_RESPONSE_GET "GCTAOnOffObservation::response()" 55 #define G_RESPONSE_GET "GCTAOnOffObservation::response()"
56 #define G_WRITE "GCTAOnOffObservation::write(GXmlElement&)" 56 #define G_WRITE "GCTAOnOffObservation::write(GXmlElement&)"
57 #define G_READ "GCTAOnOffObservation::read(GXmlElement&)" 57 #define G_READ "GCTAOnOffObservation::read(GXmlElement&)"
58 -#define G_FILL "GCTAOnOffObservation::fill(GCTAObservation&)"  
59 -#define G_COMPUTE_ALPHA "GCTAOnOffObservation::compute_alpha("\  
60 - "GCTAObservation&)"  
61 -#define G_COMPUTE_ARF "GCTAOnOffObservation::compute_arf(GCTAObservation&)"  
62 -#define G_COMPUTE_BGD "GCTAOnOffObservation::compute_bgd(GCTAObservation&)"  
63 -#define G_COMPUTE_RMF "GCTAOnOffObservation::compute_rmf(GCTAObservation&,"\  
64 - " GEbounds&)"  
65 #define G_LIKELIHOOD "GCTAOnOffObservation::likelihood(GModels&, "\ 58 #define G_LIKELIHOOD "GCTAOnOffObservation::likelihood(GModels&, "\
66 "GOptimizerPars&, GMatrixSparse&, "\ 59 "GOptimizerPars&, GMatrixSparse&, "\
67 "GVector&, double&, double&)" 60 "GVector&, double&, double&)"
  61 +#define G_SET "GCTAOnOffObservation::set(GCTAObservation&)"
  62 +#define G_COMPUTE_ARF "GCTAOnOffObservation::compute_arf(GCTAObservation&)"
  63 +#define G_COMPUTE_BGD "GCTAOnOffObservation::compute_bgd(GCTAObservation&)"
  64 +#define G_COMPUTE_ALPHA "GCTAOnOffObservation::compute_alpha("\
  65 + "GCTAObservation&)"
  66 +#define G_COMPUTE_RMF "GCTAOnOffObservation::compute_rmf(GCTAObservation&)"
68 67
69 /* __ Constants __________________________________________________________ */ 68 /* __ Constants __________________________________________________________ */
70 const double minmod = 1.0e-100; //!< Minimum model value 69 const double minmod = 1.0e-100; //!< Minimum model value
@@ -86,6 +85,8 @@ const double minerr = 1.0e-100; //!&lt; Minimum statistical error @@ -86,6 +85,8 @@ const double minerr = 1.0e-100; //!&lt; Minimum statistical error
86 85
87 /***********************************************************************//** 86 /***********************************************************************//**
88 * @brief Void constructor 87 * @brief Void constructor
  88 + *
  89 + * Constructs empty On/Off observation.
89 ***************************************************************************/ 90 ***************************************************************************/
90 GCTAOnOffObservation::GCTAOnOffObservation(void) 91 GCTAOnOffObservation::GCTAOnOffObservation(void)
91 { 92 {
@@ -100,7 +101,7 @@ GCTAOnOffObservation::GCTAOnOffObservation(void) @@ -100,7 +101,7 @@ GCTAOnOffObservation::GCTAOnOffObservation(void)
100 /***********************************************************************//** 101 /***********************************************************************//**
101 * @brief Copy constructor 102 * @brief Copy constructor
102 * 103 *
103 - * @param[in] obs CTA On/Off observation. 104 + * @param[in] obs On/Off observation.
104 ***************************************************************************/ 105 ***************************************************************************/
105 GCTAOnOffObservation::GCTAOnOffObservation(const GCTAOnOffObservation& obs) 106 GCTAOnOffObservation::GCTAOnOffObservation(const GCTAOnOffObservation& obs)
106 { 107 {
@@ -116,15 +117,25 @@ GCTAOnOffObservation::GCTAOnOffObservation(const GCTAOnOffObservation&amp; obs) @@ -116,15 +117,25 @@ GCTAOnOffObservation::GCTAOnOffObservation(const GCTAOnOffObservation&amp; obs)
116 117
117 118
118 /***********************************************************************//** 119 /***********************************************************************//**
119 - * @brief Parameter constructor 120 + * @brief CTA observation constructor
120 * 121 *
121 - * @param[in] ereco Reconstructed energy bins. 122 + * @param[in] obs CTA observation.
  123 + * @param[in] etrue True energy boundaries.
  124 + * @param[in] ereco Reconstructed energy boundaries.
122 * @param[in] on On regions. 125 * @param[in] on On regions.
123 * @param[in] off Off regions. 126 * @param[in] off Off regions.
  127 + *
  128 + * Constructs On/Off observation a CTA observation by filling the On and Off
  129 + * spectra and computing the Auxiliary Response File (ARF) and Redistribution
  130 + * Matrix File (RMF). The method requires the specification of the true and
  131 + * reconstructed energy boundaries, as well as the definition of On and Off
  132 + * regions.
124 ***************************************************************************/ 133 ***************************************************************************/
125 -GCTAOnOffObservation::GCTAOnOffObservation(const GEbounds& ereco,  
126 - const GSkyRegions& on,  
127 - const GSkyRegions& off) 134 +GCTAOnOffObservation::GCTAOnOffObservation(const GCTAObservation& obs,
  135 + const GEbounds& etrue,
  136 + const GEbounds& ereco,
  137 + const GSkyRegions& on,
  138 + const GSkyRegions& off)
128 { 139 {
129 // Initialise private 140 // Initialise private
130 init_members(); 141 init_members();
@@ -133,10 +144,17 @@ GCTAOnOffObservation::GCTAOnOffObservation(const GEbounds&amp; ereco, @@ -133,10 +144,17 @@ GCTAOnOffObservation::GCTAOnOffObservation(const GEbounds&amp; ereco,
133 m_on_spec = GPha(ereco); 144 m_on_spec = GPha(ereco);
134 m_off_spec = GPha(ereco); 145 m_off_spec = GPha(ereco);
135 146
  147 + // Initialise response information
  148 + m_arf = GArf(ereco);
  149 + m_rmf = GRmf(etrue, ereco);
  150 +
136 // Store regions 151 // Store regions
137 m_on_regions = on; 152 m_on_regions = on;
138 m_off_regions = off; 153 m_off_regions = off;
139 154
  155 + // Set On/Off observation from CTA observation
  156 + set(obs);
  157 +
140 // Return 158 // Return
141 return; 159 return;
142 } 160 }
@@ -164,10 +182,10 @@ GCTAOnOffObservation::~GCTAOnOffObservation(void) @@ -164,10 +182,10 @@ GCTAOnOffObservation::~GCTAOnOffObservation(void)
164 /***********************************************************************//** 182 /***********************************************************************//**
165 * @brief Assignment operator 183 * @brief Assignment operator
166 * 184 *
167 - * @param[in] obs CTA On/Off observation.  
168 - * @return CTA On/Off observation. 185 + * @param[in] obs On/Off observation.
  186 + * @return On/Off observation.
169 * 187 *
170 - * Assigns one CTA On/Off observation to another On/Off observation object. 188 + * Assigns one On/Off observation to another On/Off observation object.
171 ***************************************************************************/ 189 ***************************************************************************/
172 GCTAOnOffObservation& GCTAOnOffObservation::operator=(const GCTAOnOffObservation& obs) 190 GCTAOnOffObservation& GCTAOnOffObservation::operator=(const GCTAOnOffObservation& obs)
173 { 191 {
@@ -237,7 +255,7 @@ GCTAOnOffObservation* GCTAOnOffObservation::clone(void) const @@ -237,7 +255,7 @@ GCTAOnOffObservation* GCTAOnOffObservation::clone(void) const
237 * @exception GException::invalid_argument 255 * @exception GException::invalid_argument
238 * Invalid response class specified. 256 * Invalid response class specified.
239 * 257 *
240 - * Sets the response function for the observation. 258 + * Sets the response function for the On/Off observation.
241 ***************************************************************************/ 259 ***************************************************************************/
242 void GCTAOnOffObservation::response(const GResponse& rsp) 260 void GCTAOnOffObservation::response(const GResponse& rsp)
243 { 261 {
@@ -291,17 +309,12 @@ const GCTAResponse* GCTAOnOffObservation::response(void) const @@ -291,17 +309,12 @@ const GCTAResponse* GCTAOnOffObservation::response(void) const
291 309
292 310
293 /***********************************************************************//** 311 /***********************************************************************//**
294 - * @brief Read ON/OFFobservation from an xml element 312 + * @brief Read On/Off observation from an XML element
295 * 313 *
296 * @param[in] xml XML element. 314 * @param[in] xml XML element.
297 * 315 *
298 - * @exception GException::xml_invalid_parnum  
299 - * Invalid number of parameters found in XML element.  
300 - * @exception GException::xml_invalid_parnames  
301 - * Invalid parameter names found in XML element.  
302 - *  
303 - * Reads information for a CTA On/Off observation from an XML element.  
304 - * The expected format of the XML element is 316 + * Reads information for an On/Off observation from an XML element. The
  317 + * expected format of the XML element is
305 * 318 *
306 * <observation name="..." id="..." instrument="..."> 319 * <observation name="..." id="..." instrument="...">
307 * <parameter name="Pha_on" file="..."/> 320 * <parameter name="Pha_on" file="..."/>
@@ -321,109 +334,21 @@ void GCTAOnOffObservation::read(const GXmlElement&amp; xml) @@ -321,109 +334,21 @@ void GCTAOnOffObservation::read(const GXmlElement&amp; xml)
321 // Extract instrument name 334 // Extract instrument name
322 m_instrument = xml.attribute("instrument"); 335 m_instrument = xml.attribute("instrument");
323 336
324 - // Determine number of parameter nodes in XML element  
325 - int npars = xml.elements("parameter");  
326 -  
327 - // Verify that XML element has exactly 6 parameters  
328 - if (xml.elements() != 6 || npars != 6) {  
329 - throw GException::xml_invalid_parnum(G_READ, xml,  
330 - "CTA observation requires exactly 6 parameters.");  
331 - }  
332 -  
333 - // Extract parameters  
334 - int npar[] = {0, 0, 0, 0, 0, 0};  
335 - for (int i = 0; i < npars; ++i) {  
336 -  
337 - // Get parameter element  
338 - const GXmlElement* par = xml.element("parameter", i);  
339 -  
340 - // Handle Pha_on  
341 - if (par->attribute("name") == "Pha_on") {  
342 -  
343 - // Read pha_on file name  
344 - std::string filename = par->attribute("file");  
345 -  
346 - // Load pha_on  
347 - m_on_spec.load(filename);  
348 -  
349 - // Increment parameter counter  
350 - npar[0]++;  
351 - }  
352 -  
353 - // Handle Pha_off  
354 - else if (par->attribute("name") == "Pha_off") {  
355 -  
356 - // Read countsmap file name  
357 - std::string filename = par->attribute("file");  
358 -  
359 - // Load pha_off  
360 - m_off_spec.load(filename);  
361 -  
362 - // Increment parameter counter  
363 - npar[1]++;  
364 - }  
365 -  
366 - // Handle on regions  
367 - else if(par->attribute("name") == "Regions_on") {  
368 -  
369 - // Get filename  
370 - std::string filename = par->attribute("file");  
371 -  
372 - // Load on regions  
373 - m_on_regions.load(filename);  
374 -  
375 - // Increase number of parameters  
376 - npar[2]++;  
377 - }  
378 -  
379 - // Handle off regions  
380 - else if(par->attribute("name") == "Regions_off") {  
381 -  
382 - // Get filename  
383 - std::string filename = par->attribute("file");  
384 -  
385 - // load off regions  
386 - m_on_regions.load(filename);  
387 -  
388 - // Increase number of parameters  
389 - npar[3]++;  
390 - }  
391 -  
392 - // Handle Arf  
393 - else if(par->attribute("name") == "Arf") {  
394 -  
395 - // Get filename  
396 - std::string filename = par->attribute("file");  
397 -  
398 - // Load arf  
399 - m_arf.load(filename);  
400 -  
401 - // Increase number of parameters  
402 - npar[4]++;  
403 - }  
404 -  
405 - // Handle Rmf  
406 - else if(par->attribute("name") == "Rmf") {  
407 -  
408 - // Get filename  
409 - std::string filename = par->attribute("file");  
410 -  
411 - // Load Rmf  
412 - m_rmf.load(filename);  
413 -  
414 - // Increase number of parameters  
415 - npar[5]++;  
416 - }  
417 -  
418 - } // endfor: looped over all parameters  
419 -  
420 - // Verify that all parameters were found  
421 - if (npar[0] != 1 || npar[1] != 1 || npar[2] != 1 ||  
422 - npar[3] != 1 || npar[4] != 1 || npar[5] != 1) {  
423 - throw GException::xml_invalid_parnames(G_READ, xml,  
424 - "Require \"Pha_on\" or \"Pha_off\" and \"Regions_on\""  
425 - ", \"Regions_off\",\"Arf\" and \"Rmf\" parameters.");  
426 - } 337 + // Get parameters
  338 + std::string pha_on = gammalib::xml_get_attr(G_READ, xml, "Pha_on", "file");
  339 + std::string pha_off = gammalib::xml_get_attr(G_READ, xml, "Pha_off", "file");
  340 + std::string reg_on = gammalib::xml_get_attr(G_READ, xml, "Regions_on", "file");
  341 + std::string reg_off = gammalib::xml_get_attr(G_READ, xml, "Regions_off", "file");
  342 + std::string arf = gammalib::xml_get_attr(G_READ, xml, "Arf", "file");
  343 + std::string rmf = gammalib::xml_get_attr(G_READ, xml, "Rmf", "file");
  344 +
  345 + // Load files
  346 + m_on_spec.load(pha_on);
  347 + m_off_spec.load(pha_off);
  348 + m_on_regions.load(reg_on);
  349 + m_off_regions.load(reg_off);
  350 + m_arf.load(arf);
  351 + m_rmf.load(rmf);
427 352
428 // Return 353 // Return
429 return; 354 return;
@@ -435,12 +360,7 @@ void GCTAOnOffObservation::read(const GXmlElement&amp; xml) @@ -435,12 +360,7 @@ void GCTAOnOffObservation::read(const GXmlElement&amp; xml)
435 * 360 *
436 * @param[in] xml XML element. 361 * @param[in] xml XML element.
437 * 362 *
438 - * @exception GException::xml_invalid_parnum  
439 - * Invalid number of parameters found in XML element.  
440 - * @exception GException::xml_invalid_parnames  
441 - * Invalid parameter names found in XML element.  
442 - *  
443 - * Writes information for a CTA On/Off observation into an XML element. The 363 + * Writes information for an On/Off observation into an XML element. The
444 * expected format of the XML element is 364 * expected format of the XML element is
445 * 365 *
446 * <observation name="..." id="..." instrument="..."> 366 * <observation name="..." id="..." instrument="...">
@@ -452,150 +372,36 @@ void GCTAOnOffObservation::read(const GXmlElement&amp; xml) @@ -452,150 +372,36 @@ void GCTAOnOffObservation::read(const GXmlElement&amp; xml)
452 * <parameter name="Rmf" file="..."/> 372 * <parameter name="Rmf" file="..."/>
453 * </observation> 373 * </observation>
454 * 374 *
  375 + * The actual files described in the XML elements are not written.
455 ***************************************************************************/ 376 ***************************************************************************/
456 void GCTAOnOffObservation::write(GXmlElement& xml) const 377 void GCTAOnOffObservation::write(GXmlElement& xml) const
457 { 378 {
458 - // If XML element has 0 nodes then append 6 parameter nodes  
459 - if (xml.elements() == 0) {  
460 - xml.append(GXmlElement("parameter name=\"Pha_on\""));  
461 - xml.append(GXmlElement("parameter name=\"Pha_off\""));  
462 - xml.append(GXmlElement("parameter name=\"Regions_on\""));  
463 - xml.append(GXmlElement("parameter name=\"Regions_off\""));  
464 - xml.append(GXmlElement("parameter name=\"Arf\""));  
465 - xml.append(GXmlElement("parameter name=\"Rmf\""));  
466 - }  
467 -  
468 - // Verify that XML element has exactly 4 parameters  
469 - if (xml.elements() != 6 || xml.elements("parameter") != 6) {  
470 - throw GException::xml_invalid_parnum(G_WRITE, xml,  
471 - "CTAOnOffObservation requires exactly 6 parameters.");  
472 - } 379 + // Allocate XML element pointer
  380 + GXmlElement* par;
473 381
474 - // Set or update parameter attributes  
475 - int npar[] = {0, 0, 0, 0, 0, 0};  
476 - for (int i = 0; i < 6; ++i) { 382 + // Set Pha_on parameter
  383 + par = gammalib::xml_need_par(G_WRITE, xml, "Pha_on");
  384 + par->attribute("file", m_on_spec.filename());
477 385
478 - // Get parameter element  
479 - GXmlElement* par = xml.element("parameter", i);  
480 -  
481 - // Handle on counts  
482 - if (par->attribute("name") == "Pha_on") {  
483 - par->attribute("file", m_on_spec.filename());  
484 - npar[0]++;  
485 - }  
486 -  
487 - // Handle off counts  
488 - else if (par->attribute("name") == "Pha_off") {  
489 - par->attribute("file", m_off_spec.filename());  
490 - npar[1]++;  
491 - }  
492 -  
493 - // Handle on regions  
494 - else if (par->attribute("name") == "Regions_on") {  
495 - par->attribute("file", m_on_regions.filename());  
496 - npar[2]++;  
497 - } 386 + // Set Pha_off parameter
  387 + par = gammalib::xml_need_par(G_WRITE, xml, "Pha_off");
  388 + par->attribute("file", m_off_spec.filename());
498 389
499 - // Handle off regions  
500 - else if (par->attribute("name") == "Regions_off") {  
501 - par->attribute("file", m_off_regions.filename());  
502 - npar[3]++;  
503 - } 390 + // Set Regions_on parameter
  391 + par = gammalib::xml_need_par(G_WRITE, xml, "Regions_on");
  392 + par->attribute("file", m_on_regions.filename());
504 393
505 - // Handle effective area  
506 - else if (par->attribute("name") == "Arf") {  
507 - par->attribute("file", m_arf.filename());  
508 - npar[4]++;  
509 - } 394 + // Set Regions_off parameter
  395 + par = gammalib::xml_need_par(G_WRITE, xml, "Regions_off");
  396 + par->attribute("file", m_off_regions.filename());
510 397
511 - // Handle energy resolution  
512 - else if (par->attribute("name") == "Rmf") {  
513 - par->attribute("file", m_rmf.filename());  
514 - npar[5]++;  
515 - }  
516 - } 398 + // Set Arf parameter
  399 + par = gammalib::xml_need_par(G_WRITE, xml, "Arf");
  400 + par->attribute("file", m_arf.filename());
517 401
518 - // Verify that all required parameters are present  
519 - if (npar[0] != 1 || npar[1] != 1 || npar[2] != 1 ||  
520 - npar[3] != 1 || npar[4] != 1 || npar[5] != 1) {  
521 - throw GException::xml_invalid_parnames(G_WRITE, xml,  
522 - "Require \"Pha_on\" or \"Pha_off\" and \"Regions_on\","  
523 - " \"Regions_off\", \"Arf\", and \"Rmf\" parameters.");  
524 - }  
525 -  
526 - // Return  
527 - return;  
528 -}  
529 -  
530 -  
531 -/***********************************************************************//**  
532 - * @brief Fill events in the On and Off spectra  
533 - *  
534 - * @param[in] obs CTA observation.  
535 - *  
536 - * @exception GException::invalid_value  
537 - * No CTA event list found in CTA observation.  
538 - *  
539 - * Fills events from an observation into the On and Off spectra and cumulates  
540 - * their livetime. This method adds to any existing content in the On and  
541 - * Off spectra.  
542 - ***************************************************************************/  
543 -void GCTAOnOffObservation::fill(const GCTAObservation& obs)  
544 -{  
545 - // Get CTA event list pointer  
546 - const GCTAEventList* events = dynamic_cast<const GCTAEventList*>(obs.events());  
547 - if (events == NULL) {  
548 - std::string msg = "No event list found in CTA observation \""+  
549 - obs.name()+"\" (ID="+obs.id()+"). ON/OFF observation "  
550 - "can only be filled from event list.";  
551 - throw GException::invalid_value(G_FILL, msg);  
552 - }  
553 -  
554 - // Loop over all events  
555 - for (int i = 0; i < events->size(); ++i) {  
556 -  
557 - // Get measured event direction  
558 - const GCTAEventAtom* atom = (*events)[i];  
559 - GSkyDir dir = atom->dir().dir();  
560 -  
561 - // Fill in spectrum according to region containment  
562 - if (m_on_regions.contains(dir)) {  
563 - m_on_spec.fill(atom->energy());  
564 - }  
565 - if (m_off_regions.contains(dir)) {  
566 - m_off_spec.fill(atom->energy());  
567 - }  
568 -  
569 - } // endfor: looped over all events  
570 -  
571 - // Store the livetime as exposures of the spectra  
572 - m_on_spec.exposure(obs.livetime());  
573 - m_off_spec.exposure(obs.livetime());  
574 -  
575 - // Store the ontime, livetime and deadtime correction in the observation  
576 - m_ontime = obs.ontime();  
577 - m_livetime = obs.livetime();  
578 - m_deadc = obs.deadc();  
579 -  
580 - // Return  
581 - return;  
582 -}  
583 -  
584 -  
585 -/***********************************************************************//**  
586 - * @brief Compute response for On/Off observation  
587 - *  
588 - * @param[in] obs CTA observation.  
589 - * @param[in] etrue True energy boundaries.  
590 - ***************************************************************************/  
591 -void GCTAOnOffObservation::compute_response(const GCTAObservation& obs,  
592 - const GEbounds& etrue)  
593 -{  
594 - // Compute response components  
595 - compute_arf(obs);  
596 - compute_bgd(obs);  
597 - compute_rmf(obs, etrue);  
598 - compute_alpha(obs); 402 + // Set Rmf parameter
  403 + par = gammalib::xml_need_par(G_WRITE, xml, "Rmf");
  404 + par->attribute("file", m_rmf.filename());
599 405
600 // Return 406 // Return
601 return; 407 return;
@@ -622,8 +428,8 @@ void GCTAOnOffObservation::compute_response(const GCTAObservation&amp; obs, @@ -622,8 +428,8 @@ void GCTAOnOffObservation::compute_response(const GCTAObservation&amp; obs,
622 * \f$N_{\gamma}\f$ and background events \f$N_{\rm bgd}\f$ are 428 * \f$N_{\gamma}\f$ and background events \f$N_{\rm bgd}\f$ are
623 * computed from the spectral models of the relevant components in the 429 * computed from the spectral models of the relevant components in the
624 * model container (spatial and temporal components are ignored so far). 430 * model container (spatial and temporal components are ignored so far).
625 - * See the model_on() and model_off() methods for details about the  
626 - * model computations. 431 + * See the N_gamma() and N_bgd() methods for details about the model
  432 + * computations.
627 * 433 *
628 * The log-likelihood is given by 434 * The log-likelihood is given by
629 * 435 *
@@ -758,8 +564,8 @@ double GCTAOnOffObservation::likelihood(const GModels&amp; models, @@ -758,8 +564,8 @@ double GCTAOnOffObservation::likelihood(const GModels&amp; models,
758 564
759 // Get number of gamma and background events (and corresponding 565 // Get number of gamma and background events (and corresponding
760 // spectral model gradients) 566 // spectral model gradients)
761 - double ngam = model_on(models, i, &sky_grad);  
762 - double nbgd = model_off(models, i, &bgd_grad); 567 + double ngam = N_gamma(models, i, &sky_grad);
  568 + double nbgd = N_bgd(models, i, &bgd_grad);
763 569
764 // Skip bin if model is too small (avoids -Inf or NaN gradients) 570 // Skip bin if model is too small (avoids -Inf or NaN gradients)
765 double nonpred = ngam + alpha[i] * nbgd; 571 double nonpred = ngam + alpha[i] * nbgd;
@@ -1028,119 +834,62 @@ void GCTAOnOffObservation::free_members(void) @@ -1028,119 +834,62 @@ void GCTAOnOffObservation::free_members(void)
1028 834
1029 835
1030 /***********************************************************************//** 836 /***********************************************************************//**
1031 - * @brief Compute vector of alpha parameters 837 + * @brief Set On/Off observation from a CTA observation
1032 * 838 *
1033 * @param[in] obs CTA observation. 839 * @param[in] obs CTA observation.
1034 * 840 *
1035 * @exception GException::invalid_value 841 * @exception GException::invalid_value
1036 - * No CTA response found in CTA observation.  
1037 - *  
1038 - * Compute the alpha parameters for all energy bins. The alpha parameter  
1039 - * gives the ratio between the On and Off region background acceptance  
1040 - * multiplied by the ratio between On and Off region solid angles. 842 + * No CTA event list found in CTA observation.
1041 * 843 *
1042 - * @todo Implement for general sky regions, not only circles  
1043 - * @todo Integrate over the sky region instead of taking the values at the  
1044 - * sky region centre 844 + * Sets an On/Off observation from a CTA observation by filling the events
  845 + * that fall in the On and Off regions into the PHA spectra and by computing
  846 + * the corresponding ARF and RMF response functions.
1045 ***************************************************************************/ 847 ***************************************************************************/
1046 -void GCTAOnOffObservation::compute_alpha(const GCTAObservation& obs) 848 +void GCTAOnOffObservation::set(const GCTAObservation& obs)
1047 { 849 {
1048 - // Get energy boundaries from On spectrum  
1049 - GEbounds ereco = m_on_spec.ebounds();  
1050 -  
1051 - // Continue only if there are spectral bins  
1052 - int nreco = ereco.size();  
1053 - if (nreco > 0) { 850 + // Get CTA event list pointer
  851 + const GCTAEventList* events = dynamic_cast<const GCTAEventList*>(obs.events());
  852 + if (events == NULL) {
  853 + std::string msg = "No event list found in CTA observation \""+
  854 + obs.name()+"\" (ID="+obs.id()+"). ON/OFF observation "
  855 + "can only be filled from event list.";
  856 + throw GException::invalid_value(G_SET, msg);
  857 + }
1054 858
1055 - // Initialise On/Off exposure ratios  
1056 - std::vector<double> alpha(nreco, 0.0);  
1057 -  
1058 - // Get CTA response pointer. Throw an exception if no response is found  
1059 - const GCTAResponseIrf* response =  
1060 - dynamic_cast<const GCTAResponseIrf*>(obs.response());  
1061 - if (response == NULL) {  
1062 - std::string msg = "Response in CTA observation \""+obs.name()+"\" "  
1063 - "(ID="+obs.id()+") is not of the GCTAResponseIrf "  
1064 - "type.";  
1065 - throw GException::invalid_value(G_COMPUTE_ALPHA, msg);  
1066 - }  
1067 -  
1068 - // Get CTA observation pointing direction, zenith, and azimuth  
1069 - GCTAPointing obspnt = obs.pointing();  
1070 - GSkyDir obsdir = obspnt.dir(); 859 + // Loop over all events
  860 + for (int i = 0; i < events->size(); ++i) {
1071 861
1072 - // Loop over reconstructed energies  
1073 - for (int i = 0; i < nreco; ++i) { 862 + // Get measured event direction
  863 + const GCTAEventAtom* atom = (*events)[i];
  864 + GSkyDir dir = atom->dir().dir();
  865 +
  866 + // Fill in spectrum according to region containment
  867 + if (m_on_regions.contains(dir)) {
  868 + m_on_spec.fill(atom->energy());
  869 + }
  870 + if (m_off_regions.contains(dir)) {
  871 + m_off_spec.fill(atom->energy());
  872 + }
1074 873
1075 - // Get mean log10 energy in TeV of bin  
1076 - double logEreco = ereco.elogmean(i).log10TeV();  
1077 -  
1078 - // Initialise effective area totals  
1079 - double aon(0.0);  
1080 - double aoff(0.0);  
1081 -  
1082 - // Loop over ON sky regions  
1083 - for (int m = 0; m < m_on_regions.size(); ++m) {  
1084 -  
1085 - // If region is of type GSkyRegionCircle  
1086 - const GSkyRegionCircle* skyreg =  
1087 - dynamic_cast<const GSkyRegionCircle*>(m_on_regions[m]);  
1088 - if (skyreg != NULL) {  
1089 -  
1090 - // Get centre of circular region  
1091 - GSkyDir centreg = skyreg->centre();  
1092 -  
1093 - // Compute instrument direction from region centre  
1094 - GCTAInstDir instdir(obspnt.instdir(centreg)); 874 + } // endfor: looped over all events
1095 875
1096 - // Add up acceptance  
1097 - aon += (*response->background())(logEreco,  
1098 - instdir.detx(),  
1099 - instdir.dety()) *  
1100 - skyreg->solidangle();  
1101 -  
1102 - } // endif: sky region is of type GSkyRegionCircle  
1103 -  
1104 - } // Looped over ON regions  
1105 -  
1106 - // Loop over OFF sky regions  
1107 - for (int m = 0; m < m_off_regions.size(); ++m) {  
1108 -  
1109 - // If region is of type GSkyRegionCircle  
1110 - const GSkyRegionCircle* skyreg =  
1111 - dynamic_cast<const GSkyRegionCircle*>(m_off_regions[m]);  
1112 - if (skyreg != NULL) {  
1113 -  
1114 - // Get centre of circular region  
1115 - GSkyDir centreg = skyreg->centre();  
1116 -  
1117 - // Compute instrument direction from region centre  
1118 - GCTAInstDir instdir(obspnt.instdir(centreg)); 876 + // Store the livetime as exposures of the spectra
  877 + m_on_spec.exposure(obs.livetime());
  878 + m_off_spec.exposure(obs.livetime());
1119 879
1120 - // Add up acceptance  
1121 - aoff += (*response->background())(logEreco,  
1122 - instdir.detx(),  
1123 - instdir.dety()) *  
1124 - skyreg->solidangle();  
1125 -  
1126 - } // endif: sky region is of type GSkyRegionCircle  
1127 -  
1128 - } // endfor: looped over OFF regions  
1129 -  
1130 - // Compute alpha for this energy bin  
1131 - if (aoff > 0.0) {  
1132 - alpha[i] = aon/aoff;  
1133 - }  
1134 -  
1135 - } // endfor: looped over reconstructed energies 880 + // Store the ontime, livetime and deadtime correction in the observation
  881 + m_ontime = obs.ontime();
  882 + m_livetime = obs.livetime();
  883 + m_deadc = obs.deadc();
1136 884
1137 - // Append alpha vector to ARF  
1138 - m_arf.append("ALPHA", alpha); 885 + // Compute response components
  886 + compute_arf(obs);
  887 + compute_bgd(obs);
  888 + compute_alpha(obs);
  889 + compute_rmf(obs);
1139 890
1140 - } // endif: there were energy bins  
1141 -  
1142 - // Return  
1143 - return; 891 + // Return
  892 + return;
1144 } 893 }
1145 894
1146 895
@@ -1158,11 +907,11 @@ void GCTAOnOffObservation::compute_alpha(const GCTAObservation&amp; obs) @@ -1158,11 +907,11 @@ void GCTAOnOffObservation::compute_alpha(const GCTAObservation&amp; obs)
1158 ***************************************************************************/ 907 ***************************************************************************/
1159 void GCTAOnOffObservation::compute_arf(const GCTAObservation& obs) 908 void GCTAOnOffObservation::compute_arf(const GCTAObservation& obs)
1160 { 909 {
1161 - // Get energy boundaries from on spectrum  
1162 - GEbounds ereco = m_on_spec.ebounds();  
1163 -  
1164 - // Continue only if there are spectral bins  
1165 - int nreco = ereco.size(); 910 + // Get reconstructed energy boundaries from on ARF
  911 + GEbounds ereco = m_arf.ebounds();
  912 + int nreco = ereco.size();
  913 +
  914 + // Continue only if there are ARF bins
1166 if (nreco > 0) { 915 if (nreco > 0) {
1167 916
1168 // Get CTA response pointer. Throw an exception if no response is found 917 // Get CTA response pointer. Throw an exception if no response is found
@@ -1181,9 +930,6 @@ void GCTAOnOffObservation::compute_arf(const GCTAObservation&amp; obs) @@ -1181,9 +930,6 @@ void GCTAOnOffObservation::compute_arf(const GCTAObservation&amp; obs)
1181 double zenith = obspnt.zenith(); 930 double zenith = obspnt.zenith();
1182 double azimuth = obspnt.azimuth(); 931 double azimuth = obspnt.azimuth();
1183 932
1184 - // Initialize ARF  
1185 - m_arf = GArf(ereco);  
1186 -  
1187 // Loop over reconstructed energies 933 // Loop over reconstructed energies
1188 for (int i = 0; i < nreco; ++i) { 934 for (int i = 0; i < nreco; ++i) {
1189 935
@@ -1202,11 +948,11 @@ void GCTAOnOffObservation::compute_arf(const GCTAObservation&amp; obs) @@ -1202,11 +948,11 @@ void GCTAOnOffObservation::compute_arf(const GCTAObservation&amp; obs)
1202 if (skyreg != NULL) { 948 if (skyreg != NULL) {
1203 949
1204 // Get centre of circular region 950 // Get centre of circular region
1205 - GSkyDir centreg(skyreg->centre()); 951 + GSkyDir centre(skyreg->centre());
1206 952
1207 // Compute position of region centre in instrument coordinates 953 // Compute position of region centre in instrument coordinates
1208 - double theta = obsdir.dist(centreg);  
1209 - double phi = obsdir.posang(centreg); 954 + double theta = obsdir.dist(centre);
  955 + double phi = obsdir.posang(centre);
1210 956
1211 // Add up effective area 957 // Add up effective area
1212 m_arf[i] += response->aeff(theta, 958 m_arf[i] += response->aeff(theta,
@@ -1229,33 +975,35 @@ void GCTAOnOffObservation::compute_arf(const GCTAObservation&amp; obs) @@ -1229,33 +975,35 @@ void GCTAOnOffObservation::compute_arf(const GCTAObservation&amp; obs)
1229 975
1230 976
1231 /***********************************************************************//** 977 /***********************************************************************//**
1232 - * @brief Compute background rate in Off observations 978 + * @brief Compute background rate in Off region
1233 * 979 *
1234 * @param[in] obs CTA observation. 980 * @param[in] obs CTA observation.
1235 * 981 *
1236 * @exception GException::invalid_argument 982 * @exception GException::invalid_argument
1237 - * Specified observation does not contain relevant response or  
1238 - * background information 983 + * Observation does not contain relevant response or background
  984 + * information
  985 + *
  986 + * Compute the background rate in units of events/s/MeV in all Off regions
  987 + * and stores the background rate as additional column with name
  988 + * "BACKGROUND" in the Auxiliary Response File (ARF).
1239 * 989 *
1240 * @todo Should implement integration of background model over region. 990 * @todo Should implement integration of background model over region.
1241 * @todo Allow general region, not only circles. 991 * @todo Allow general region, not only circles.
1242 ***************************************************************************/ 992 ***************************************************************************/
1243 void GCTAOnOffObservation::compute_bgd(const GCTAObservation& obs) 993 void GCTAOnOffObservation::compute_bgd(const GCTAObservation& obs)
1244 { 994 {
1245 - // Get energy boundaries from on spectrum  
1246 - GEbounds ereco = m_on_spec.ebounds(); 995 + // Get reconstructed energy boundaries from on ARF
  996 + GEbounds ereco = m_arf.ebounds();
  997 + int nreco = ereco.size();
1247 998
1248 - // Continue only if there are spectral bins  
1249 - int nreco = ereco.size(); 999 + // Continue only if there are ARF bins
1250 if (nreco > 0) { 1000 if (nreco > 0) {
1251 1001
1252 - // Initialise background rayes 1002 + // Initialise background rates to zero
1253 std::vector<double> background(nreco, 0.0); 1003 std::vector<double> background(nreco, 0.0);
1254 1004
1255 - // Get CTA observation pointing direction, zenith, and azimuth  
1256 - GCTAPointing obspnt(obs.pointing());  
1257 - double zenith = obspnt.zenith();  
1258 - double azimuth = obspnt.azimuth(); 1005 + // Get CTA observation pointing direction
  1006 + GCTAPointing obspnt = obs.pointing();
1259 1007
1260 // Get pointer on CTA IRF response 1008 // Get pointer on CTA IRF response
1261 const GCTAResponseIrf* rsp = 1009 const GCTAResponseIrf* rsp =
@@ -1266,7 +1014,7 @@ void GCTAOnOffObservation::compute_bgd(const GCTAObservation&amp; obs) @@ -1266,7 +1014,7 @@ void GCTAOnOffObservation::compute_bgd(const GCTAObservation&amp; obs)
1266 throw GException::invalid_argument(G_COMPUTE_BGD, msg); 1014 throw GException::invalid_argument(G_COMPUTE_BGD, msg);
1267 } 1015 }
1268 1016
1269 - // Retrieve pointer to CTA background 1017 + // Get pointer to CTA background
1270 const GCTABackground* bgd = rsp->background(); 1018 const GCTABackground* bgd = rsp->background();
1271 if (bgd == NULL) { 1019 if (bgd == NULL) {
1272 std::string msg = "Specified observation contains no " 1020 std::string msg = "Specified observation contains no "
@@ -1280,24 +1028,24 @@ void GCTAOnOffObservation::compute_bgd(const GCTAObservation&amp; obs) @@ -1280,24 +1028,24 @@ void GCTAOnOffObservation::compute_bgd(const GCTAObservation&amp; obs)
1280 // Get region centre direction, fall through if region is not 1028 // Get region centre direction, fall through if region is not
1281 // of type GSkyRegionCircle 1029 // of type GSkyRegionCircle
1282 const GSkyRegionCircle* skyreg = 1030 const GSkyRegionCircle* skyreg =
1283 - dynamic_cast<const GSkyRegionCircle*>(m_off_regions[m]); 1031 + dynamic_cast<const GSkyRegionCircle*>(m_off_regions[m]);
1284 if (skyreg == NULL) { 1032 if (skyreg == NULL) {
1285 continue; 1033 continue;
1286 } 1034 }
1287 1035
1288 // Get centre of circular region 1036 // Get centre of circular region
1289 - GSkyDir centreg = skyreg->centre(); 1037 + GSkyDir centre = skyreg->centre();
1290 1038
1291 - // Set direction to centre of the region in instrument coordinates  
1292 - GCTAInstDir offdir = obspnt.instdir(centreg); 1039 + // Get instrument direction of region centre
  1040 + GCTAInstDir offdir = obspnt.instdir(centre);
1293 1041
1294 // Loop over energy bins 1042 // Loop over energy bins
1295 for (int i = 0; i < nreco; ++i) { 1043 for (int i = 0; i < nreco; ++i) {
1296 1044
1297 - // Get log energy bin mean 1045 + // Get log10(E/TeV) of mean reconstructed bin energy
1298 double logEreco = m_on_spec.ebounds().elogmean(i).log10TeV(); 1046 double logEreco = m_on_spec.ebounds().elogmean(i).log10TeV();
1299 1047
1300 - // Get rate in events/s/MeV 1048 + // Get background rate in events/s/MeV
1301 background[i] += (*bgd)(logEreco, 1049 background[i] += (*bgd)(logEreco,
1302 offdir.detx(), 1050 offdir.detx(),
1303 offdir.dety()) * skyreg->solidangle(); 1051 offdir.dety()) * skyreg->solidangle();
@@ -1317,27 +1065,146 @@ void GCTAOnOffObservation::compute_bgd(const GCTAObservation&amp; obs) @@ -1317,27 +1065,146 @@ void GCTAOnOffObservation::compute_bgd(const GCTAObservation&amp; obs)
1317 1065
1318 1066
1319 /***********************************************************************//** 1067 /***********************************************************************//**
  1068 + * @brief Compute vector of alpha parameters
  1069 + *
  1070 + * @param[in] obs CTA observation.
  1071 + *
  1072 + * @exception GException::invalid_value
  1073 + * No CTA response found in CTA observation.
  1074 + *
  1075 + * Compute the alpha parameters for all energy bins. The alpha parameter
  1076 + * gives the ratio between the On and Off region background acceptance
  1077 + * multiplied by the ratio between On and Off region solid angles.
  1078 + *
  1079 + * @todo Implement for general sky regions, not only circles
  1080 + * @todo Integrate over the sky region instead of taking the values at the
  1081 + * sky region centre
  1082 + ***************************************************************************/
  1083 +void GCTAOnOffObservation::compute_alpha(const GCTAObservation& obs)
  1084 +{
  1085 + // Get reconstructed energy boundaries from on ARF
  1086 + GEbounds ereco = m_arf.ebounds();
  1087 + int nreco = ereco.size();
  1088 +
  1089 + // Continue only if there are ARF bins
  1090 + if (nreco > 0) {
  1091 +
  1092 + // Initialise On/Off exposure ratios
  1093 + std::vector<double> alpha(nreco, 0.0);
  1094 +
  1095 + // Get CTA response pointer. Throw an exception if no response is found
  1096 + const GCTAResponseIrf* response =
  1097 + dynamic_cast<const GCTAResponseIrf*>(obs.response());
  1098 + if (response == NULL) {
  1099 + std::string msg = "Response in CTA observation \""+obs.name()+"\" "
  1100 + "(ID="+obs.id()+") is not of the GCTAResponseIrf "
  1101 + "type.";
  1102 + throw GException::invalid_value(G_COMPUTE_ALPHA, msg);
  1103 + }
  1104 +
  1105 + // Get CTA observation pointing direction, zenith, and azimuth
  1106 + GCTAPointing obspnt = obs.pointing();
  1107 + GSkyDir obsdir = obspnt.dir();
  1108 +
  1109 + // Loop over reconstructed energies
  1110 + for (int i = 0; i < nreco; ++i) {
  1111 +
  1112 + // Get mean log10 energy in TeV of bin
  1113 + double logEreco = ereco.elogmean(i).log10TeV();
  1114 +
  1115 + // Initialise effective area totals
  1116 + double aon(0.0);
  1117 + double aoff(0.0);
  1118 +
  1119 + // Loop over ON sky regions
  1120 + for (int m = 0; m < m_on_regions.size(); ++m) {
  1121 +
  1122 + // If region is of type GSkyRegionCircle
  1123 + const GSkyRegionCircle* skyreg =
  1124 + dynamic_cast<const GSkyRegionCircle*>(m_on_regions[m]);
  1125 + if (skyreg != NULL) {
  1126 +
  1127 + // Get centre of circular region
  1128 + GSkyDir centreg = skyreg->centre();
  1129 +
  1130 + // Compute instrument direction from region centre
  1131 + GCTAInstDir instdir(obspnt.instdir(centreg));
  1132 +
  1133 + // Add up acceptance
  1134 + aon += (*response->background())(logEreco,
  1135 + instdir.detx(),
  1136 + instdir.dety()) *
  1137 + skyreg->solidangle();
  1138 +
  1139 + } // endif: sky region is of type GSkyRegionCircle
  1140 +
  1141 + } // Looped over ON regions
  1142 +
  1143 + // Loop over OFF sky regions
  1144 + for (int m = 0; m < m_off_regions.size(); ++m) {
  1145 +
  1146 + // If region is of type GSkyRegionCircle
  1147 + const GSkyRegionCircle* skyreg =
  1148 + dynamic_cast<const GSkyRegionCircle*>(m_off_regions[m]);
  1149 + if (skyreg != NULL) {
  1150 +
  1151 + // Get centre of circular region
  1152 + GSkyDir centreg = skyreg->centre();
  1153 +
  1154 + // Compute instrument direction from region centre
  1155 + GCTAInstDir instdir(obspnt.instdir(centreg));
  1156 +
  1157 + // Add up acceptance
  1158 + aoff += (*response->background())(logEreco,
  1159 + instdir.detx(),
  1160 + instdir.dety()) *
  1161 + skyreg->solidangle();
  1162 +
  1163 + } // endif: sky region is of type GSkyRegionCircle
  1164 +
  1165 + } // endfor: looped over OFF regions
  1166 +
  1167 + // Compute alpha for this energy bin
  1168 + if (aoff > 0.0) {
  1169 + alpha[i] = aon/aoff;
  1170 + }
  1171 +
  1172 + } // endfor: looped over reconstructed energies
  1173 +
  1174 + // Append alpha vector to ARF
  1175 + m_arf.append("ALPHA", alpha);
  1176 +
  1177 + } // endif: there were energy bins
  1178 +
  1179 + // Return
  1180 + return;
  1181 +}
  1182 +
  1183 +
  1184 +/***********************************************************************//**
1320 * @brief Compute RMF of On/Off observation 1185 * @brief Compute RMF of On/Off observation
1321 * 1186 *
1322 * @param[in] obs CTA observation. 1187 * @param[in] obs CTA observation.
1323 - * @param[in] etrue True energy boundaries.  
1324 * 1188 *
1325 * @exception GException::invalid_value 1189 * @exception GException::invalid_value
1326 * Observation does not contain IRF response 1190 * Observation does not contain IRF response
1327 * 1191 *
  1192 + * Compute the energy redistribution matrix for an On/Off observation. The
  1193 + * method requires that the RMF energy axes have been defined before.
  1194 + *
1328 * @todo Check if we really should use reconstructed energy in the Aeff 1195 * @todo Check if we really should use reconstructed energy in the Aeff
1329 * access. 1196 * access.
1330 * @todo Generalise to any kind of sky region 1197 * @todo Generalise to any kind of sky region
1331 ***************************************************************************/ 1198 ***************************************************************************/
1332 -void GCTAOnOffObservation::compute_rmf(const GCTAObservation& obs,  
1333 - const GEbounds& etrue) 1199 +void GCTAOnOffObservation::compute_rmf(const GCTAObservation& obs)
1334 { 1200 {
1335 - // Get reconstructed energy boundaries from on spectrum  
1336 - GEbounds ereco = m_on_spec.ebounds();  
1337 -  
1338 - // Continue only if there are spectral bins  
1339 - int ntrue = etrue.size();  
1340 - int nreco = ereco.size(); 1201 + // Get true and reconstructed energy boundaries from RMF
  1202 + GEbounds etrue = m_rmf.etrue();
  1203 + GEbounds ereco = m_rmf.emeasured();
  1204 + int ntrue = etrue.size();
  1205 + int nreco = ereco.size();
  1206 +
  1207 + // Continue only if there are RMF bins
1341 if (ntrue > 0 && nreco > 0) { 1208 if (ntrue > 0 && nreco > 0) {
1342 1209
1343 // Get CTA response pointer 1210 // Get CTA response pointer
@@ -1356,9 +1223,6 @@ void GCTAOnOffObservation::compute_rmf(const GCTAObservation&amp; obs, @@ -1356,9 +1223,6 @@ void GCTAOnOffObservation::compute_rmf(const GCTAObservation&amp; obs,
1356 double zenith = obspnt.zenith(); 1223 double zenith = obspnt.zenith();
1357 double azimuth = obspnt.azimuth(); 1224 double azimuth = obspnt.azimuth();
1358 1225
1359 - // Initialize RMF  
1360 - m_rmf = GRmf(etrue, ereco);  
1361 -  
1362 // Loop over reconstructed energy 1226 // Loop over reconstructed energy
1363 for (int ireco = 0; ireco < nreco; ++ireco) { 1227 for (int ireco = 0; ireco < nreco; ++ireco) {
1364 1228
@@ -1367,6 +1231,9 @@ void GCTAOnOffObservation::compute_rmf(const GCTAObservation&amp; obs, @@ -1367,6 +1231,9 @@ void GCTAOnOffObservation::compute_rmf(const GCTAObservation&amp; obs,
1367 1231
1368 // Loop over true energy 1232 // Loop over true energy
1369 for (int itrue = 0; itrue < ntrue; ++itrue) { 1233 for (int itrue = 0; itrue < ntrue; ++itrue) {
  1234 +
  1235 + // Initialise RMF element
  1236 + m_rmf(itrue, ireco) = 0.0;
1370 1237
1371 // Compute true energy and initialise weight for this bin 1238 // Compute true energy and initialise weight for this bin
1372 double logEtrue = etrue.elogmean(itrue).log10TeV(); 1239 double logEtrue = etrue.elogmean(itrue).log10TeV();
@@ -1424,33 +1291,46 @@ void GCTAOnOffObservation::compute_rmf(const GCTAObservation&amp; obs, @@ -1424,33 +1291,46 @@ void GCTAOnOffObservation::compute_rmf(const GCTAObservation&amp; obs,
1424 1291
1425 1292
1426 /*********************************************************************** 1293 /***********************************************************************
1427 - * @brief Evaluate model for sky contribution and fill model gradients 1294 + * @brief Compute \f$N_{\gamma}\f$ value and model parameter gradients
1428 * 1295 *
1429 * @param[in] models Model container. 1296 * @param[in] models Model container.
1430 - * @param[in,out] ibin Energy bin number.  
1431 - * @param[in,out] mod_grad Model gradient array. 1297 + * @param[in] ibin Energy bin number.
  1298 + * @param[in,out] grad Model gradient vector.
1432 * 1299 *
1433 - * Computes the number of expected gamma-ray events in an On region for a  
1434 - * given energy bin, and returns it. Also computes the gradients of the  
1435 - * sky model spectral components. The method assumes that  
1436 - * parameters are stored in the order spatial-spectral-temporal. 1300 + * Returns the predicted number of source events \f$N_{\gamma}\f$
  1301 + * in the On regions for a given energy bin. The method computes also
  1302 + *
  1303 + * \f[
  1304 + * \left( \frac{\partial N_{\gamma}}{\partial p_{\rm sky}} \right)
  1305 + * \f]
  1306 + *
  1307 + * which are the gradients in the predicted number of source events with
  1308 + * respect to all model parameters.
  1309 + *
  1310 + * The method assumes that parameters are stored in the order
  1311 + * spatial-spectral-temporal.
1437 * 1312 *
1438 * @todo I think this method only works for point sources. What happens 1313 * @todo I think this method only works for point sources. What happens
1439 * for an extended source? 1314 * for an extended source?
1440 ***********************************************************************/ 1315 ***********************************************************************/
1441 -double GCTAOnOffObservation::model_on(const GModels& models,  
1442 - int ibin,  
1443 - GVector* mod_grad) const 1316 +double GCTAOnOffObservation::N_gamma(const GModels& models,
  1317 + const int& ibin,
  1318 + GVector* grad) const
1444 { 1319 {
1445 // Get total number of model parameters 1320 // Get total number of model parameters
1446 int npars = models.npars(); 1321 int npars = models.npars();
1447 -  
1448 - // Initialize variables (vector has 0.0 values) 1322 +
  1323 + // Initialize results
1449 double value = 0.0; 1324 double value = 0.0;
1450 - int ipar = 0; 1325 + for (int i = 0; i < npars; ++i) {
  1326 + (*grad)[i] = 0.0;
  1327 + }
1451 1328
1452 // Continue only if bin number is in range and there are model parameters 1329 // Continue only if bin number is in range and there are model parameters
1453 - if ((ibin < m_on_spec.size()) && (npars > 0)) { 1330 + if ((ibin >= 0) && (ibin < m_on_spec.size()) && (npars > 0)) {
  1331 +
  1332 + // Initialise parameter index
  1333 + int ipar = 0;
1454 1334
1455 // Get energy bin bounds 1335 // Get energy bin bounds
1456 const GEnergy emin = m_on_spec.ebounds().emin(ibin); 1336 const GEnergy emin = m_on_spec.ebounds().emin(ibin);
@@ -1460,8 +1340,8 @@ double GCTAOnOffObservation::model_on(const GModels&amp; models, @@ -1460,8 +1340,8 @@ double GCTAOnOffObservation::model_on(const GModels&amp; models,
1460 1340
1461 // Compute normalisation factors 1341 // Compute normalisation factors
1462 double exposure = m_on_spec.exposure(); 1342 double exposure = m_on_spec.exposure();
1463 - double norm_flux = m_arf[ibin] * exposure;  
1464 - double norm_grad = norm_flux * ewidth; 1343 + double norm_flux = m_arf[ibin] * exposure; // cm2 s
  1344 + double norm_grad = norm_flux * ewidth; // cm2 s MeV
1465 1345
1466 // Loop over models 1346 // Loop over models
1467 for (int j = 0; j < models.size(); ++j) { 1347 for (int j = 0; j < models.size(); ++j) {
@@ -1500,7 +1380,7 @@ double GCTAOnOffObservation::model_on(const GModels&amp; models, @@ -1500,7 +1380,7 @@ double GCTAOnOffObservation::model_on(const GModels&amp; models,
1500 // computing the flux over the energy bin in ph/cm2/s 1380 // computing the flux over the energy bin in ph/cm2/s
1501 // and multiplying this flux by the effective area (cm2) 1381 // and multiplying this flux by the effective area (cm2)
1502 // and the livetime (s) 1382 // and the livetime (s)
1503 - value += spectral->flux(emin,emax) * norm_flux; 1383 + value += spectral->flux(emin, emax) * norm_flux;
1504 1384
1505 // Determine the model gradients at the current energy 1385 // Determine the model gradients at the current energy
1506 spectral->eval_gradients(emean); 1386 spectral->eval_gradients(emean);
@@ -1509,7 +1389,7 @@ double GCTAOnOffObservation::model_on(const GModels&amp; models, @@ -1509,7 +1389,7 @@ double GCTAOnOffObservation::model_on(const GModels&amp; models,
1509 for (int k = 0; k < spectral->size(); ++k, ++ipar) { 1389 for (int k = 0; k < spectral->size(); ++k, ++ipar) {
1510 GModelPar& par = (*spectral)[k]; 1390 GModelPar& par = (*spectral)[k];
1511 if (par.is_free() && ipar < npars) { 1391 if (par.is_free() && ipar < npars) {
1512 - (*mod_grad)[ipar] = par.factor_gradient() * norm_grad; 1392 + (*grad)[ipar] = par.factor_gradient() * norm_grad;
1513 } 1393 }
1514 } 1394 }
1515 1395
@@ -1531,47 +1411,58 @@ double GCTAOnOffObservation::model_on(const GModels&amp; models, @@ -1531,47 +1411,58 @@ double GCTAOnOffObservation::model_on(const GModels&amp; models,
1531 1411
1532 1412
1533 /*********************************************************************** 1413 /***********************************************************************
1534 - * @brief Evaluate model for bgd contribution and fill model gradients 1414 + * @brief Compute \f$N_{\rm bgd}\f$ value and model parameter gradients
1535 * 1415 *
1536 * @param[in] models Model container. 1416 * @param[in] models Model container.
1537 - * @param[in,out] ibin Energy bin number.  
1538 - * @param[in,out] mod_grad Model gradient array.  
1539 - * @return Number of expected background events. 1417 + * @param[in] ibin Energy bin index.
  1418 + * @param[in,out] grad Model gradient vector.
  1419 + * @return Predicted number of background events in Off regions.
1540 * 1420 *
1541 - * Returns the number of expected background events in the Off spectrum  
1542 - * for a given energy bin. The method computes also the gradients of the  
1543 - * background model spectral components. 1421 + * Returns the predicted number of background events \f$N_{\rm bgd}\f$
  1422 + * in the Off regions for a given energy bin. The method computes also
  1423 + *
  1424 + * \f[
  1425 + * \left( \frac{\partial N_{\rm bgd}}{\partial p_{\rm bgd}} \right)
  1426 + * \f]
  1427 + *
  1428 + * which are the gradients in the predicted number of background events
  1429 + * with respect to all model parameters.
1544 * 1430 *
1545 * The method assumes that the model parameters are stored in the order 1431 * The method assumes that the model parameters are stored in the order
1546 - * spatial-spectral-temporal. 1432 + * spectral-temporal.
1547 * 1433 *
1548 * @todo So far only handles the GCTAModelIrfBackground background model; 1434 * @todo So far only handles the GCTAModelIrfBackground background model;
1549 * Should be more generic, but there is maybe for the moment no 1435 * Should be more generic, but there is maybe for the moment no
1550 * other choice than implementing something for all possible 1436 * other choice than implementing something for all possible
1551 * background models; that's not very satisfactory ... 1437 * background models; that's not very satisfactory ...
1552 ***********************************************************************/ 1438 ***********************************************************************/
1553 -double GCTAOnOffObservation::model_off(const GModels& models,  
1554 - int ibin,  
1555 - GVector* mod_grad) const 1439 +double GCTAOnOffObservation::N_bgd(const GModels& models,
  1440 + const int& ibin,
  1441 + GVector* grad) const
1556 { 1442 {
1557 // Get total number of model parameters 1443 // Get total number of model parameters
1558 int npars = models.npars(); 1444 int npars = models.npars();
1559 1445
1560 - // Initialize variables  
1561 - double value = 0.0;  
1562 - int ipar = 0;  
1563 -  
1564 - // Continue only if bin number is in range and if there are parameters  
1565 - if ((ibin < m_off_spec.size()) && (npars > 0)) { 1446 + // Initialize results
  1447 + double value = 0.0;
  1448 + for (int i = 0; i < npars; ++i) {
  1449 + (*grad)[i] = 0.0;
  1450 + }
  1451 +
  1452 + // Continue only if bin number is valid and if there are model parameters
  1453 + if ((ibin >= 0) && (ibin < m_off_spec.size()) && (npars > 0)) {
1566 1454
1567 - // Get reference to background rates 1455 + // Initialise parameter index
  1456 + int ipar = 0;
  1457 +
  1458 + // Get reference to background rates (events/MeV/s)
1568 const std::vector<double>& background = m_arf["BACKGROUND"]; 1459 const std::vector<double>& background = m_arf["BACKGROUND"];
1569 1460
1570 // Get energy bin mean and width 1461 // Get energy bin mean and width
1571 const GEnergy emean = m_off_spec.ebounds().elogmean(ibin); 1462 const GEnergy emean = m_off_spec.ebounds().elogmean(ibin);
1572 const double ewidth = m_off_spec.ebounds().ewidth(ibin).MeV(); 1463 const double ewidth = m_off_spec.ebounds().ewidth(ibin).MeV();
1573 1464
1574 - // Compute normalisation factor 1465 + // Compute normalisation factor (events)
1575 double exposure = m_off_spec.exposure(); 1466 double exposure = m_off_spec.exposure();
1576 double norm = background[ibin] * exposure * ewidth; 1467 double norm = background[ibin] * exposure * ewidth;
1577 1468
@@ -1591,7 +1482,7 @@ double GCTAOnOffObservation::model_off(const GModels&amp; models, @@ -1591,7 +1482,7 @@ double GCTAOnOffObservation::model_off(const GModels&amp; models,
1591 continue; 1482 continue;
1592 } 1483 }
1593 1484
1594 - // Fall through if model is not a IRF background component 1485 + // Fall through if model is not an IRF background component
1595 const GCTAModelIrfBackground* bgd = 1486 const GCTAModelIrfBackground* bgd =
1596 dynamic_cast<const GCTAModelIrfBackground*>(mptr); 1487 dynamic_cast<const GCTAModelIrfBackground*>(mptr);
1597 if (bgd == NULL) { 1488 if (bgd == NULL) {
@@ -1604,16 +1495,17 @@ double GCTAOnOffObservation::model_off(const GModels&amp; models, @@ -1604,16 +1495,17 @@ double GCTAOnOffObservation::model_off(const GModels&amp; models,
1604 if (spectral != NULL) { 1495 if (spectral != NULL) {
1605 1496
1606 // Determine the number of background events in model by 1497 // Determine the number of background events in model by
1607 - // computing the rate at the energy bin in events/s/MeV and  
1608 - // by multiplying the rate with the off livetime (s), the  
1609 - // energy bin width (MeV) and the spectral normalisation 1498 + // computing the model normalization at the mean bin energy bin
  1499 + // and multiplying the normalisation with the number of
  1500 + // background events
1610 value += spectral->eval_gradients(emean) * norm; 1501 value += spectral->eval_gradients(emean) * norm;
1611 1502
1612 - // Loop over spectral model parameters 1503 + // Compute the parameter gradients for all spectral model
  1504 + // parameters
1613 for (int k = 0; k < spectral->size(); ++k, ++ipar) { 1505 for (int k = 0; k < spectral->size(); ++k, ++ipar) {
1614 - GModelPar& par=(*spectral)[k]; 1506 + GModelPar& par = (*spectral)[k];
1615 if (par.is_free() && ipar < npars) { 1507 if (par.is_free() && ipar < npars) {
1616 - (*mod_grad)[ipar] = par.factor_gradient() * norm; 1508 + (*grad)[ipar] += par.factor_gradient() * norm;
1617 } 1509 }
1618 } 1510 }
1619 1511
inst/cta/test/test_CTA.py
@@ -265,15 +265,12 @@ class Test(gammalib.GPythonTestSuite): @@ -265,15 +265,12 @@ class Test(gammalib.GPythonTestSuite):
265 ereco = gammalib.GEbounds(10, gammalib.GEnergy(0.1, 'TeV'), 265 ereco = gammalib.GEbounds(10, gammalib.GEnergy(0.1, 'TeV'),
266 gammalib.GEnergy(10.0, 'TeV')) 266 gammalib.GEnergy(10.0, 'TeV'))
267 267
268 - # Create On/Off observations by filling all events found in  
269 - # the observation container and computing the response 268 + # Create On/Off observations from CTA observations
270 filename = self.caldb + '/../data/irf_unbinned.xml' 269 filename = self.caldb + '/../data/irf_unbinned.xml'
271 inobs = gammalib.GObservations(filename) 270 inobs = gammalib.GObservations(filename)
272 outobs = gammalib.GObservations() 271 outobs = gammalib.GObservations()
273 for run in inobs: 272 for run in inobs:
274 - onoff = gammalib.GCTAOnOffObservation(ereco, on, off)  
275 - onoff.fill(run)  
276 - onoff.compute_response(run, etrue) 273 + onoff = gammalib.GCTAOnOffObservation(run, etrue, ereco, on, off)
277 outobs.append(onoff) 274 outobs.append(onoff)
278 275
279 # Load model container and attach it to the observations 276 # Load model container and attach it to the observations