GCTAModelBackground.cpp
1 |
/***************************************************************************
|
---|---|
2 |
* GCTAModelBackground.cpp - generic CTA background model class *
|
3 |
* ----------------------------------------------------------------------- *
|
4 |
* copyright (C) 2011-2013 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 GCTABackgroundModel.cpp
|
23 |
* @brief generic CTA background model class implementation
|
24 |
* @author Michael Mayer
|
25 |
*/
|
26 |
|
27 |
/* __ Includes ___________________________________________________________ */
|
28 |
#ifdef HAVE_CONFIG_H
|
29 |
#include <config.h> |
30 |
#endif
|
31 |
#include "GException.hpp" |
32 |
#include "GTools.hpp" |
33 |
#include "GMath.hpp" |
34 |
#include "GModelRegistry.hpp" |
35 |
#include "GModelSpectralRegistry.hpp" |
36 |
#include "GModelSpatialRegistry.hpp" |
37 |
#include "GModelTemporalRegistry.hpp" |
38 |
#include "GModelTemporalConst.hpp" |
39 |
#include "GIntegral.hpp" |
40 |
#include "GCTAModelBackground.hpp" |
41 |
#include "GModelSpatialRegistry.hpp" |
42 |
#include "GModelSpatial.hpp" |
43 |
#include "GCTAObservation.hpp" |
44 |
#include "GCTAPointing.hpp" |
45 |
#include "GCTAInstDir.hpp" |
46 |
#include "GCTARoi.hpp" |
47 |
#include "GCTAException.hpp" |
48 |
#include "GCTASupport.hpp" |
49 |
|
50 |
/* __ Constants __________________________________________________________ */
|
51 |
|
52 |
/* __ Globals ____________________________________________________________ */
|
53 |
const GCTAModelBackground g_cta_model_background_seed;
|
54 |
const GModelRegistry g_cta_model_background_registry(&g_cta_model_background_seed);
|
55 |
|
56 |
/* __ Method name definitions ____________________________________________ */
|
57 |
#define G_ACCESS "GCTAModelBackground::operator() (int)" |
58 |
#define G_EVAL "GCTAModelBackground::eval(GEvent&,"\ |
59 |
" GObservation&)"
|
60 |
#define G_EVAL_GRADIENTS "GCTAModelBackground::eval_gradients(GEvent&,"\ |
61 |
" GObservation&)"
|
62 |
#define G_NPRED "GCTAModelBackground::npred(GEnergy&, GTime&,"\ |
63 |
" GObservation&)"
|
64 |
#define G_MC "GCTAModelBackground::mc(GObservation&, GRan&)" |
65 |
#define G_XML_SPATIAL "GCTAModelBackground::xml_spatial(GXmlElement&)" |
66 |
#define G_XML_SPECTRAL "GCTAModelBackground::xml_spectral(GXmlElement&)" |
67 |
#define G_XML_TEMPORAL "GCTAModelBackground::xml_temporal(GXmlElement&)" |
68 |
|
69 |
/* __ Macros _____________________________________________________________ */
|
70 |
|
71 |
/* __ Coding definitions _________________________________________________ */
|
72 |
|
73 |
/* __ Debug definitions __________________________________________________ */
|
74 |
//#define G_DUMP_MC //!< Dump MC information
|
75 |
|
76 |
|
77 |
/*==========================================================================
|
78 |
= =
|
79 |
= Constructors/destructors =
|
80 |
= =
|
81 |
==========================================================================*/
|
82 |
|
83 |
/***********************************************************************//** |
84 |
* @brief Void constructor
|
85 |
*
|
86 |
* Constructs an empty CTA background model.
|
87 |
***************************************************************************/
|
88 |
GCTAModelBackground::GCTAModelBackground(void) : GModelData()
|
89 |
{ |
90 |
// Initialise members
|
91 |
init_members(); |
92 |
|
93 |
// Return
|
94 |
return;
|
95 |
} |
96 |
|
97 |
|
98 |
/***********************************************************************//** |
99 |
* @brief Constructor
|
100 |
*
|
101 |
* @param[in] xml XML element.
|
102 |
*
|
103 |
* Constructs a CTA background model from the information that is
|
104 |
* found in a XML element. Please refer to the method
|
105 |
* GCTAModelBackground::read
|
106 |
* to learn more about the information that is expected in the XML element.
|
107 |
***************************************************************************/
|
108 |
GCTAModelBackground::GCTAModelBackground(const GXmlElement& xml) :
|
109 |
GModelData(xml) |
110 |
{ |
111 |
// Initialise members
|
112 |
init_members(); |
113 |
|
114 |
// Read XML
|
115 |
read(xml); |
116 |
|
117 |
// Set parameter pointers
|
118 |
set_pointers(); |
119 |
|
120 |
// Return
|
121 |
return;
|
122 |
} |
123 |
|
124 |
|
125 |
/***********************************************************************//** |
126 |
* @brief Construct from spatial and spectral components
|
127 |
*
|
128 |
* @param[in] spatial Spatial background model component.
|
129 |
* @param[in] spectral Spectral model component.
|
130 |
*
|
131 |
* Constructs a CTA background model from a spatial and a spectral
|
132 |
* model component. The temporal component is assumed to be constant.
|
133 |
* Please refer to the classes GModelSpatial and GModelSpectral to learn
|
134 |
* more about the definition of the spatial and spectral components.
|
135 |
***************************************************************************/
|
136 |
GCTAModelBackground::GCTAModelBackground(const GModelSpatial& spatial,
|
137 |
const GModelSpectral& spectral)
|
138 |
: GModelData() |
139 |
{ |
140 |
// Initialise members
|
141 |
init_members(); |
142 |
|
143 |
// Allocate temporal constant model
|
144 |
GModelTemporalConst temporal; |
145 |
|
146 |
// Clone model components
|
147 |
m_spatial = spatial.clone(); |
148 |
m_spectral = spectral.clone(); |
149 |
m_temporal = temporal.clone(); |
150 |
|
151 |
// Set parameter pointers
|
152 |
set_pointers(); |
153 |
|
154 |
// Return
|
155 |
return;
|
156 |
} |
157 |
|
158 |
|
159 |
/***********************************************************************//** |
160 |
* @brief Copy constructor
|
161 |
*
|
162 |
* @param[in] model CTA background model.
|
163 |
*
|
164 |
* Constructs a CTA background model by copying information from an
|
165 |
* existing model. Note that the copy is a deep copy, so the original object
|
166 |
* can be destroyed after the copy without any loss of information.
|
167 |
***************************************************************************/
|
168 |
GCTAModelBackground::GCTAModelBackground(const GCTAModelBackground& model) :
|
169 |
GModelData(model) |
170 |
{ |
171 |
// Initialise private members for clean destruction
|
172 |
init_members(); |
173 |
|
174 |
// Copy members
|
175 |
copy_members(model); |
176 |
|
177 |
// Set parameter pointers
|
178 |
set_pointers(); |
179 |
|
180 |
// Return
|
181 |
return;
|
182 |
} |
183 |
|
184 |
|
185 |
/***********************************************************************//** |
186 |
* @brief Destructor
|
187 |
*
|
188 |
* Destroys a CTA background model.
|
189 |
***************************************************************************/
|
190 |
GCTAModelBackground::~GCTAModelBackground(void)
|
191 |
{ |
192 |
// Free members
|
193 |
free_members(); |
194 |
|
195 |
// Return
|
196 |
return;
|
197 |
} |
198 |
|
199 |
|
200 |
/*==========================================================================
|
201 |
= =
|
202 |
= Operators =
|
203 |
= =
|
204 |
==========================================================================*/
|
205 |
|
206 |
/***********************************************************************//** |
207 |
* @brief Assignment operator
|
208 |
*
|
209 |
* @param[in] model CTA background model.
|
210 |
*
|
211 |
* Assigns the information from a CTA background model to the actual
|
212 |
* object. Note that a deep copy of the information is performed, so the
|
213 |
* original object can be destroyed after the assignment without any loss of
|
214 |
* information.
|
215 |
***************************************************************************/
|
216 |
GCTAModelBackground& GCTAModelBackground::operator= (const GCTAModelBackground& model) |
217 |
{ |
218 |
// Execute only if object is not identical
|
219 |
if (this != &model) { |
220 |
|
221 |
// Copy base class members
|
222 |
this->GModelData::operator=(model); |
223 |
|
224 |
// Free members
|
225 |
free_members(); |
226 |
|
227 |
// Initialise members
|
228 |
init_members(); |
229 |
|
230 |
// Copy members (this method also sets the parameter pointers)
|
231 |
copy_members(model); |
232 |
|
233 |
} // endif: object was not identical
|
234 |
|
235 |
// Return
|
236 |
return *this; |
237 |
} |
238 |
|
239 |
|
240 |
/*==========================================================================
|
241 |
= =
|
242 |
= Public methods =
|
243 |
= =
|
244 |
==========================================================================*/
|
245 |
|
246 |
/***********************************************************************//** |
247 |
* @brief Clear instance
|
248 |
*
|
249 |
* Resets the object to a clean initial state. All information that resided
|
250 |
* in the object will be lost.
|
251 |
***************************************************************************/
|
252 |
void GCTAModelBackground::clear(void) |
253 |
{ |
254 |
// Free class members (base and derived classes, derived class first)
|
255 |
free_members(); |
256 |
this->GModelData::free_members();
|
257 |
this->GModel::free_members();
|
258 |
|
259 |
// Initialise members
|
260 |
this->GModel::init_members();
|
261 |
this->GModelData::init_members();
|
262 |
init_members(); |
263 |
|
264 |
// Return
|
265 |
return;
|
266 |
} |
267 |
|
268 |
|
269 |
/***********************************************************************//** |
270 |
* @brief Clone instance
|
271 |
*
|
272 |
* Clone a CTA background model. Cloning performs a deep copy of the
|
273 |
* information, so the original object can be destroyed after cloning without
|
274 |
* any loss of information.
|
275 |
***************************************************************************/
|
276 |
GCTAModelBackground* GCTAModelBackground::clone(void) const |
277 |
{ |
278 |
return new GCTAModelBackground(*this); |
279 |
} |
280 |
|
281 |
|
282 |
/***********************************************************************//** |
283 |
* @brief Evaluate function
|
284 |
*
|
285 |
* @param[in] event Observed event.
|
286 |
* @param[in] obs Observation.
|
287 |
*
|
288 |
* Evaluates tha CTA background model which is a factorization of a
|
289 |
* spatial, spectral and temporal model component. This method also applies
|
290 |
* a deadtime correction factor, so that the normalization of the model is
|
291 |
* a real rate (counts/exposure time).
|
292 |
*
|
293 |
* @todo Add bookkeeping of last value and evaluate only if argument
|
294 |
* changed
|
295 |
* @todo Verify that CTA instrument direction pointer is valid, or better,
|
296 |
* add an offset method to GCTAPointing. Ideally, we should precompute
|
297 |
* all offset angles (for an event cube this may only be done easily
|
298 |
* if the pointing has been fixed; otherwise we need a structure
|
299 |
* similar to the Fermi/LAT livetime cube that provides the effective
|
300 |
* sky exposure as function of offset angle).
|
301 |
***************************************************************************/
|
302 |
double GCTAModelBackground::eval(const GEvent& event, |
303 |
const GObservation& obs) const |
304 |
{ |
305 |
// Extract CTA pointing direction
|
306 |
GCTAPointing* pnt = dynamic_cast<GCTAPointing*>(obs.pointing());
|
307 |
if (pnt == NULL) { |
308 |
throw GCTAException::no_pointing(G_EVAL);
|
309 |
} |
310 |
|
311 |
// Get instrument direction
|
312 |
const GInstDir* inst_dir = &(event.dir());
|
313 |
const GCTAInstDir* cta_dir = dynamic_cast<const GCTAInstDir*>(inst_dir); |
314 |
|
315 |
// Create a Photon from the event
|
316 |
// We need the GPhoton to evaluate the spatial model.
|
317 |
// For the background, GEvent and GPhoton are identical
|
318 |
// since the IRFs are not folded in
|
319 |
GPhoton photon(cta_dir->dir(), event.energy(),event.time()); |
320 |
|
321 |
// Evaluate function and gradients
|
322 |
double spat = (spatial() != NULL) |
323 |
? spatial()->eval(photon) : 1.0; |
324 |
double spec = (spectral() != NULL) |
325 |
? spectral()->eval(event.energy(), event.time()) : 1.0; |
326 |
double temp = (temporal() != NULL) |
327 |
? temporal()->eval(event.time()) : 1.0; |
328 |
|
329 |
// Compute value
|
330 |
double value = spat * spec * temp;
|
331 |
|
332 |
// Apply deadtime correction
|
333 |
value *= obs.deadc(event.time()); |
334 |
|
335 |
// Return
|
336 |
return value;
|
337 |
} |
338 |
|
339 |
|
340 |
/***********************************************************************//** |
341 |
* @brief Evaluate function and gradients
|
342 |
*
|
343 |
* @param[in] event Observed event.
|
344 |
* @param[in] obs Observation (not used).
|
345 |
*
|
346 |
* @exception GCTAException::no_pointing
|
347 |
* No valid CTA pointing found in observation
|
348 |
*
|
349 |
* Evaluates tha CTA background model and parameter gradients. The CTA
|
350 |
* background model is a factorization of a spatial, spectral and
|
351 |
* temporal model component. This method also applies a deadtime correction
|
352 |
* factor, so that the normalization of the model is a real rate
|
353 |
* (counts/exposure time).
|
354 |
*
|
355 |
* @todo Add bookkeeping of last value and evaluate only if argument
|
356 |
* changed
|
357 |
* @todo Verify that CTA instrument direction pointer is valid, or better,
|
358 |
* add an offset method to GCTAPointing. Ideally, we should precompute
|
359 |
* all offset angles (for an event cube this may only be done easily
|
360 |
* if the pointing has been fixed; otherwise we need a structure
|
361 |
* similar to the Fermi/LAT livetime cube that provides the effective
|
362 |
* sky exposure as function of offset angle).
|
363 |
***************************************************************************/
|
364 |
double GCTAModelBackground::eval_gradients(const GEvent& event, |
365 |
const GObservation& obs) const |
366 |
{ |
367 |
// Extract CTA pointing direction
|
368 |
GCTAPointing* pnt = dynamic_cast<GCTAPointing*>(obs.pointing());
|
369 |
if (pnt == NULL) { |
370 |
throw GCTAException::no_pointing(G_EVAL_GRADIENTS);
|
371 |
} |
372 |
|
373 |
// Get instrument direction
|
374 |
const GInstDir* inst_dir = &(event.dir());
|
375 |
const GCTAInstDir* cta_dir = dynamic_cast<const GCTAInstDir*>(inst_dir); |
376 |
|
377 |
// Create a Photon from the event
|
378 |
// We need the photon to evaluate the spatial model
|
379 |
// For the background, GEvent and GPhoton are identical
|
380 |
// since the IRFs are not folded in
|
381 |
GPhoton photon = GPhoton(cta_dir->dir(), event.energy(),event.time()); |
382 |
|
383 |
// Evaluate function and gradients
|
384 |
double spat = (spatial() != NULL) |
385 |
? spatial()->eval_gradients(photon) : 1.0; |
386 |
double spec = (spectral() != NULL) |
387 |
? spectral()->eval_gradients(event.energy(), event.time()) : 1.0; |
388 |
double temp = (temporal() != NULL) |
389 |
? temporal()->eval_gradients(event.time()) : 1.0; |
390 |
|
391 |
// Compute value
|
392 |
double value = spat * spec * temp;
|
393 |
|
394 |
// Apply deadtime correction
|
395 |
double deadc = obs.deadc(event.time());
|
396 |
value *= deadc; |
397 |
|
398 |
// Multiply factors to spatial gradients
|
399 |
if (spatial() != NULL) { |
400 |
double fact = spec * temp * deadc;
|
401 |
if (fact != 1.0) { |
402 |
for (int i = 0; i < spatial()->size(); ++i) |
403 |
(*spatial())[i].factor_gradient( (*spatial())[i].factor_gradient() * fact ); |
404 |
} |
405 |
} |
406 |
|
407 |
// Multiply factors to spectral gradients
|
408 |
if (spectral() != NULL) { |
409 |
double fact = spat * temp * deadc;
|
410 |
if (fact != 1.0) { |
411 |
for (int i = 0; i < spectral()->size(); ++i) |
412 |
(*spectral())[i].factor_gradient( (*spectral())[i].factor_gradient() * fact ); |
413 |
} |
414 |
} |
415 |
|
416 |
// Multiply factors to temporal gradients
|
417 |
if (temporal() != NULL) { |
418 |
double fact = spat * spec * deadc;
|
419 |
if (fact != 1.0) { |
420 |
for (int i = 0; i < temporal()->size(); ++i) |
421 |
(*temporal())[i].factor_gradient( (*temporal())[i].factor_gradient() * fact ); |
422 |
} |
423 |
} |
424 |
|
425 |
// Return value
|
426 |
return value;
|
427 |
} |
428 |
|
429 |
|
430 |
/***********************************************************************//** |
431 |
* @brief Return spatially integrated data model
|
432 |
*
|
433 |
* @param[in] obsEng Measured event energy.
|
434 |
* @param[in] obsTime Measured event time.
|
435 |
* @param[in] obs Observation.
|
436 |
*
|
437 |
* @exception GException::no_list
|
438 |
* No valid CTA event list found in observation
|
439 |
* @exception GCTAException::no_pointing
|
440 |
* No valid CTA pointing found in observation
|
441 |
*
|
442 |
* Spatially integrates the data model for a given measured event energy and
|
443 |
* event time. This method also applies a deadtime correction factor, so that
|
444 |
* the normalization of the model is a real rate (counts/exposure time).
|
445 |
***************************************************************************/
|
446 |
double GCTAModelBackground::npred(const GEnergy& obsEng, |
447 |
const GTime& obsTime,
|
448 |
const GObservation& obs) const |
449 |
{ |
450 |
// Initialise result
|
451 |
double npred = 0.0; |
452 |
|
453 |
// Evaluate only if model is valid
|
454 |
if (valid_model()) {
|
455 |
|
456 |
// Get pointer on CTA events list
|
457 |
const GCTAEventList* events = dynamic_cast<const GCTAEventList*>(obs.events()); |
458 |
if (events == NULL) { |
459 |
throw GException::no_list(G_NPRED);
|
460 |
} |
461 |
|
462 |
//todo we need to think about if we always want to always integrate
|
463 |
// starting from the pointing direction or instead take
|
464 |
// the center of gravity of the model
|
465 |
// For now we use the pointing position as integration start
|
466 |
|
467 |
// Get CTA pointing direction
|
468 |
GCTAPointing* pnt = dynamic_cast<GCTAPointing*>(obs.pointing());
|
469 |
if (pnt == NULL) { |
470 |
throw GCTAException::no_pointing(G_NPRED);
|
471 |
} |
472 |
|
473 |
// Get ROI radius in radians
|
474 |
double roi_radius = events->roi().radius() * gammalib::deg2rad;
|
475 |
|
476 |
// Get distance from ROI centre in radians
|
477 |
double roi_distance = events->roi().centre().dist(pnt->dir());
|
478 |
|
479 |
GMatrix ry; |
480 |
GMatrix rz; |
481 |
ry.eulery(events->roi().centre().dec_deg() - 90.0); |
482 |
rz.eulerz(-events->roi().centre().ra_deg()); |
483 |
GMatrix rot = (ry * rz).transpose(); |
484 |
|
485 |
// Compute position angle of ROI centre with respect to model
|
486 |
// centre (radians)
|
487 |
double omega0 = pnt->dir().posang(events->roi().centre().dir());
|
488 |
|
489 |
// Setup integration function
|
490 |
GCTAModelBackground::npred_roi_kern_theta integrand(spatial(), obsEng,obsTime,rot, roi_radius, roi_distance,omega0); |
491 |
|
492 |
// Setup integrator
|
493 |
GIntegral integral(&integrand); |
494 |
|
495 |
// Setup integration boundaries
|
496 |
double rmin = (roi_distance > roi_radius) ? roi_distance-roi_radius : 0.0; |
497 |
double rmax = roi_radius + roi_distance;
|
498 |
|
499 |
// Spatially integrate radial component
|
500 |
npred = integral.romb(rmin, rmax); |
501 |
|
502 |
// Multiply in spectral and temporal components
|
503 |
npred *= spectral()->eval(obsEng, obsTime); |
504 |
npred *= temporal()->eval(obsTime); |
505 |
|
506 |
// Apply deadtime correction
|
507 |
npred *= obs.deadc(obsTime); |
508 |
|
509 |
} // endif: model was valid
|
510 |
|
511 |
// Return
|
512 |
return npred;
|
513 |
} |
514 |
|
515 |
|
516 |
/***********************************************************************//** |
517 |
* @brief Return simulated list of events
|
518 |
*
|
519 |
* @param[in] obs Observation.
|
520 |
* @param[in] ran Random number generator.
|
521 |
*
|
522 |
* @exception GCTAException::no_pointing
|
523 |
* No CTA pointing found in observation.
|
524 |
*
|
525 |
* Draws a sample of events from the background model using a Monte
|
526 |
* Carlo simulation. The pointing information, the energy boundaries and the
|
527 |
* good time interval for the sampling will be extracted from the observation
|
528 |
* argument that is passed to the method. The method also requires a random
|
529 |
* number generator of type GRan which is passed by reference, hence the
|
530 |
* state of the random number generator will be changed by the method.
|
531 |
*
|
532 |
* The method also applies a deadtime correction using a Monte Carlo process,
|
533 |
* taking into account temporal deadtime variations. For this purpose, the
|
534 |
* method makes use of the time dependent GObservation::deadc method.
|
535 |
***************************************************************************/
|
536 |
GCTAEventList* GCTAModelBackground::mc(const GObservation& obs,
|
537 |
GRan& ran) const
|
538 |
{ |
539 |
// Initialise new event list
|
540 |
GCTAEventList* list = new GCTAEventList;
|
541 |
|
542 |
// Continue only if model is valid)
|
543 |
if (valid_model()) {
|
544 |
|
545 |
// Extract CTA pointing direction at beginning of observation
|
546 |
GCTAPointing* pnt = dynamic_cast<GCTAPointing*>(obs.pointing());
|
547 |
if (pnt == NULL) { |
548 |
throw GCTAException::no_pointing(G_MC);
|
549 |
} |
550 |
|
551 |
// Convert CTA pointing direction in instrument system
|
552 |
GCTAInstDir pnt_dir(pnt->dir()); |
553 |
|
554 |
// Loop over all energy boundaries
|
555 |
for (int ieng = 0; ieng < obs.events()->ebounds().size(); ++ieng) { |
556 |
|
557 |
// Compute the on-axis background rate in model within the
|
558 |
// energy boundaries from spectral component (units: cts/s/sr)
|
559 |
double flux = spectral()->flux(obs.events()->ebounds().emin(ieng),
|
560 |
obs.events()->ebounds().emax(ieng)); |
561 |
|
562 |
// Compute solid angle used for normalization
|
563 |
// todo Find a way to calculate the solid angle of the model
|
564 |
double area = 1.0; |
565 |
|
566 |
// Derive expecting rate (units: cts/s). Note that the time here
|
567 |
// is good time. Deadtime correction will be done later.
|
568 |
double rate = flux * area;
|
569 |
|
570 |
// Debug option: dump rate
|
571 |
#if defined(G_DUMP_MC)
|
572 |
std::cout << "GCTAModelBackground::mc(\"" << name() << "\": "; |
573 |
std::cout << "flux=" << flux << " cts/s/sr, "; |
574 |
std::cout << "area=" << area << " sr, "; |
575 |
std::cout << "rate=" << rate << " cts/s)" << std::endl; |
576 |
#endif
|
577 |
|
578 |
// Loop over all good time intervals
|
579 |
for (int itime = 0; itime < obs.events()->gti().size(); ++itime) { |
580 |
|
581 |
// Get event arrival times from temporal model
|
582 |
GTimes times = m_temporal->mc(rate, |
583 |
obs.events()->gti().tstart(itime), |
584 |
obs.events()->gti().tstop(itime), |
585 |
ran); |
586 |
|
587 |
// Get number of events
|
588 |
int n_events = times.size();
|
589 |
|
590 |
// Reserve space for events
|
591 |
if (n_events > 0) { |
592 |
list->reserve(n_events); |
593 |
} |
594 |
|
595 |
// Loop over events
|
596 |
for (int i = 0; i < n_events; ++i) { |
597 |
|
598 |
// Apply deadtime correction
|
599 |
double deadc = obs.deadc(times[i]);
|
600 |
if (deadc < 1.0) { |
601 |
if (ran.uniform() > deadc) {
|
602 |
continue;
|
603 |
} |
604 |
} |
605 |
|
606 |
|
607 |
|
608 |
// Set event energy
|
609 |
GEnergy energy = spectral()->mc(obs.events()->ebounds().emin(ieng), |
610 |
obs.events()->ebounds().emax(ieng), |
611 |
times[i], |
612 |
ran); |
613 |
|
614 |
// Set event direction
|
615 |
GSkyDir dir = spatial()->mc(energy,times[i], ran); |
616 |
GCTAInstDir cta_dir(dir);// = spatial()->mc(energy,times[i], ran);
|
617 |
|
618 |
// Allocate event
|
619 |
GCTAEventAtom event; |
620 |
|
621 |
// Set event attributes
|
622 |
event.dir(cta_dir); |
623 |
event.energy(energy); |
624 |
event.time(times[i]); |
625 |
|
626 |
// Append event to list
|
627 |
list->append(event); |
628 |
|
629 |
} // endfor: looped over all events
|
630 |
|
631 |
} // endfor: looped over all GTIs
|
632 |
|
633 |
} // endfor: looped over all energy boundaries
|
634 |
|
635 |
} // endif: model was valid
|
636 |
|
637 |
// Return
|
638 |
return list;
|
639 |
} |
640 |
|
641 |
|
642 |
/***********************************************************************//** |
643 |
* @brief Read model from XML element
|
644 |
*
|
645 |
* @param[in] xml XML element.
|
646 |
*
|
647 |
* The model is composed of a spectrum component ('spectral'), a spatial
|
648 |
* component ('spatialModel'), and, optionally, of a temporal component
|
649 |
* ('lightcurve'). If no temporal component is found a constant model is
|
650 |
* assumed.
|
651 |
***************************************************************************/
|
652 |
void GCTAModelBackground::read(const GXmlElement& xml) |
653 |
{ |
654 |
// Clear model
|
655 |
clear(); |
656 |
|
657 |
// Initialise XML elements
|
658 |
const GXmlElement* spat = NULL; |
659 |
const GXmlElement* spec = NULL; |
660 |
const GXmlElement* temp = NULL; |
661 |
|
662 |
// Get pointers on spectrum and radial model
|
663 |
spat = xml.element("spatialModel", 0); |
664 |
spec = xml.element("spectrum", 0); |
665 |
|
666 |
// Clone radial and spectral models
|
667 |
m_spatial = xml_spatial(*spat); |
668 |
m_spectral = xml_spectral(*spec); |
669 |
|
670 |
// Optionally get temporal model
|
671 |
try {
|
672 |
temp = xml.element("lightcurve", 0); |
673 |
m_temporal = xml_temporal(*temp); |
674 |
} |
675 |
catch (GException::xml_name_not_found &e) {
|
676 |
GModelTemporalConst temporal; |
677 |
m_temporal = temporal.clone(); |
678 |
} |
679 |
|
680 |
// Set model name
|
681 |
name(xml.attribute("name"));
|
682 |
|
683 |
// Set instruments
|
684 |
instruments(xml.attribute("instrument"));
|
685 |
|
686 |
// Set observation identifiers
|
687 |
ids(xml.attribute("id"));
|
688 |
|
689 |
// Set parameter pointers
|
690 |
set_pointers(); |
691 |
|
692 |
// Return
|
693 |
return;
|
694 |
} |
695 |
|
696 |
|
697 |
/***********************************************************************//** |
698 |
* @brief Write model into XML element
|
699 |
*
|
700 |
* @param[in] xml XML element.
|
701 |
*
|
702 |
* @todo Document method.
|
703 |
***************************************************************************/
|
704 |
void GCTAModelBackground::write(GXmlElement& xml) const |
705 |
{ |
706 |
// Initialise pointer on source
|
707 |
GXmlElement* src = NULL;
|
708 |
|
709 |
// Search corresponding source
|
710 |
int n = xml.elements("source"); |
711 |
for (int k = 0; k < n; ++k) { |
712 |
GXmlElement* element = xml.element("source", k);
|
713 |
if (element->attribute("name") == name()) { |
714 |
src = element; |
715 |
break;
|
716 |
} |
717 |
} |
718 |
|
719 |
// If no source with corresponding name was found then append one
|
720 |
if (src == NULL) { |
721 |
src = xml.append("source");
|
722 |
if (spectral() != NULL) src->append(GXmlElement("spectrum")); |
723 |
if (spatial() != NULL) src->append(GXmlElement("spatialModel")); |
724 |
} |
725 |
|
726 |
// Set model type, name and optionally instruments
|
727 |
src->attribute("name", name());
|
728 |
src->attribute("type", type());
|
729 |
if (instruments().length() > 0) { |
730 |
src->attribute("instrument", instruments());
|
731 |
} |
732 |
|
733 |
// Write spectral model
|
734 |
if (spectral() != NULL) { |
735 |
GXmlElement* spec = src->element("spectrum", 0); |
736 |
spectral()->write(*spec); |
737 |
} |
738 |
|
739 |
// Write radial model
|
740 |
if (spatial()) {
|
741 |
GXmlElement* spat = src->element("spatialModel", 0); |
742 |
spatial()->write(*spat); |
743 |
} |
744 |
|
745 |
// Write temporal model
|
746 |
if (temporal()) {
|
747 |
if (dynamic_cast<GModelTemporalConst*>(temporal()) == NULL) { |
748 |
GXmlElement* temp = src->element("lightcurve", 0); |
749 |
temporal()->write(*temp); |
750 |
} |
751 |
} |
752 |
|
753 |
// Return
|
754 |
return;
|
755 |
} |
756 |
|
757 |
|
758 |
/***********************************************************************//** |
759 |
* @brief Print model information
|
760 |
*
|
761 |
* @param[in] chatter Chattiness (defaults to NORMAL).
|
762 |
* @return String containing model information.
|
763 |
***************************************************************************/
|
764 |
std::string GCTAModelBackground::print(const GChatter& chatter) const |
765 |
{ |
766 |
// Initialise result string
|
767 |
std::string result;
|
768 |
|
769 |
// Continue only if chatter is not silent
|
770 |
if (chatter != SILENT) {
|
771 |
|
772 |
// Append header
|
773 |
result.append("=== GCTAModelBackground ===");
|
774 |
|
775 |
// Determine number of parameters per type
|
776 |
int n_radial = (spatial() != NULL) ? spatial()->size() : 0; |
777 |
int n_spectral = (spectral() != NULL) ? spectral()->size() : 0; |
778 |
int n_temporal = (temporal() != NULL) ? temporal()->size() : 0; |
779 |
|
780 |
// Append attributes
|
781 |
result.append("\n"+print_attributes());
|
782 |
|
783 |
// Append model type
|
784 |
result.append("\n"+gammalib::parformat("Model type")); |
785 |
if (n_radial > 0) { |
786 |
result.append("\""+spatial()->type()+"\""); |
787 |
if (n_spectral > 0 || n_temporal > 0) { |
788 |
result.append(" * ");
|
789 |
} |
790 |
} |
791 |
if (n_spectral > 0) { |
792 |
result.append("\""+spectral()->type()+"\""); |
793 |
if (n_temporal > 0) { |
794 |
result.append(" * ");
|
795 |
} |
796 |
} |
797 |
if (n_temporal > 0) { |
798 |
result.append("\""+temporal()->type()+"\""); |
799 |
} |
800 |
|
801 |
// Append parameters
|
802 |
result.append("\n"+gammalib::parformat("Number of parameters") + |
803 |
gammalib::str(size())); |
804 |
result.append("\n"+gammalib::parformat("Number of spatial par's") + |
805 |
gammalib::str(n_radial)); |
806 |
for (int i = 0; i < n_radial; ++i) { |
807 |
result.append("\n"+(*spatial())[i].print());
|
808 |
} |
809 |
result.append("\n"+gammalib::parformat("Number of spectral par's") + |
810 |
gammalib::str(n_spectral)); |
811 |
for (int i = 0; i < n_spectral; ++i) { |
812 |
result.append("\n"+(*spectral())[i].print());
|
813 |
} |
814 |
result.append("\n"+gammalib::parformat("Number of temporal par's") + |
815 |
gammalib::str(n_temporal)); |
816 |
for (int i = 0; i < n_temporal; ++i) { |
817 |
result.append("\n"+(*temporal())[i].print());
|
818 |
} |
819 |
|
820 |
} // endif: chatter was not silent
|
821 |
|
822 |
// Return result
|
823 |
return result;
|
824 |
} |
825 |
|
826 |
|
827 |
/*==========================================================================
|
828 |
= =
|
829 |
= Private methods =
|
830 |
= =
|
831 |
==========================================================================*/
|
832 |
|
833 |
/***********************************************************************//** |
834 |
* @brief Initialise class members
|
835 |
*
|
836 |
* @todo Document method.
|
837 |
***************************************************************************/
|
838 |
void GCTAModelBackground::init_members(void) |
839 |
{ |
840 |
// Initialise members
|
841 |
m_spatial = NULL;
|
842 |
m_spectral = NULL;
|
843 |
m_temporal = NULL;
|
844 |
|
845 |
// Return
|
846 |
return;
|
847 |
} |
848 |
|
849 |
|
850 |
/***********************************************************************//** |
851 |
* @brief Copy class members
|
852 |
*
|
853 |
* @param[in] model Model.
|
854 |
*
|
855 |
* @todo Document method.
|
856 |
***************************************************************************/
|
857 |
void GCTAModelBackground::copy_members(const GCTAModelBackground& model) |
858 |
{ |
859 |
// Clone radial, spectral and temporal model components
|
860 |
m_spatial = (model.m_spatial != NULL) ? model.m_spatial->clone() : NULL; |
861 |
m_spectral = (model.m_spectral != NULL) ? model.m_spectral->clone() : NULL; |
862 |
m_temporal = (model.m_temporal != NULL) ? model.m_temporal->clone() : NULL; |
863 |
|
864 |
// Set parameter pointers
|
865 |
set_pointers(); |
866 |
|
867 |
// Return
|
868 |
return;
|
869 |
} |
870 |
|
871 |
|
872 |
/***********************************************************************//** |
873 |
* @brief Delete class members
|
874 |
*
|
875 |
* @todo Document method.
|
876 |
***************************************************************************/
|
877 |
void GCTAModelBackground::free_members(void) |
878 |
{ |
879 |
// Free memory
|
880 |
if (m_spatial != NULL) delete m_spatial; |
881 |
if (m_spectral != NULL) delete m_spectral; |
882 |
if (m_temporal != NULL) delete m_temporal; |
883 |
|
884 |
// Signal free pointers
|
885 |
m_spatial = NULL;
|
886 |
m_spectral = NULL;
|
887 |
m_temporal = NULL;
|
888 |
|
889 |
// Return
|
890 |
return;
|
891 |
} |
892 |
|
893 |
|
894 |
/***********************************************************************//** |
895 |
* @brief Set pointers
|
896 |
*
|
897 |
* Set pointers to all model parameters. The pointers are stored in a vector
|
898 |
* that is member of the GModelData base class.
|
899 |
***************************************************************************/
|
900 |
void GCTAModelBackground::set_pointers(void) |
901 |
{ |
902 |
// Clear parameters
|
903 |
m_pars.clear(); |
904 |
|
905 |
// Determine the number of parameters
|
906 |
int n_radial = (spatial() != NULL) ? spatial()->size() : 0; |
907 |
int n_spectral = (spectral() != NULL) ? spectral()->size() : 0; |
908 |
int n_temporal = (temporal() != NULL) ? temporal()->size() : 0; |
909 |
int n_pars = n_radial + n_spectral + n_temporal;
|
910 |
|
911 |
// Continue only if there are parameters
|
912 |
if (n_pars > 0) { |
913 |
|
914 |
// Gather radial parameter pointers
|
915 |
for (int i = 0; i < n_radial; ++i) { |
916 |
m_pars.push_back(&((*spatial())[i])); |
917 |
} |
918 |
|
919 |
// Gather spectral parameters
|
920 |
for (int i = 0; i < n_spectral; ++i) { |
921 |
m_pars.push_back(&((*spectral())[i])); |
922 |
} |
923 |
|
924 |
// Gather temporal parameters
|
925 |
for (int i = 0; i < n_temporal; ++i) { |
926 |
m_pars.push_back(&((*temporal())[i])); |
927 |
} |
928 |
|
929 |
} |
930 |
|
931 |
// Return
|
932 |
return;
|
933 |
} |
934 |
|
935 |
|
936 |
/***********************************************************************//** |
937 |
* @brief Verifies if model has all components
|
938 |
*
|
939 |
* Returns 'true' if models has a spatial, a spectral and a temporal
|
940 |
* component. Otherwise returns 'false'.
|
941 |
***************************************************************************/
|
942 |
bool GCTAModelBackground::valid_model(void) const |
943 |
{ |
944 |
// Set result
|
945 |
bool result = ((spatial() != NULL) && |
946 |
(spectral() != NULL) &&
|
947 |
(temporal() != NULL));
|
948 |
|
949 |
// Return result
|
950 |
return result;
|
951 |
} |
952 |
|
953 |
|
954 |
/***********************************************************************//** |
955 |
* @brief Construct spatial model from XML element
|
956 |
*
|
957 |
* @param[in] spatial XML element containing spatial model information.
|
958 |
*
|
959 |
* @exception GException::model_invalid_spatial
|
960 |
* Invalid spatial model type encountered.
|
961 |
*
|
962 |
* Returns pointer to a spatial model that is defined in an XML element.
|
963 |
***************************************************************************/
|
964 |
GModelSpatial* GCTAModelBackground::xml_spatial(const GXmlElement& spatial) const |
965 |
{ |
966 |
// Get radial model type
|
967 |
std::string type = spatial.attribute("type"); |
968 |
|
969 |
// Get radial model
|
970 |
GModelSpatialRegistry registry; |
971 |
GModelSpatial* ptr = registry.alloc(type); |
972 |
|
973 |
// If model if valid then read model from XML file
|
974 |
if (ptr != NULL) { |
975 |
ptr->read(spatial); |
976 |
} |
977 |
|
978 |
// ... otherwise throw an exception
|
979 |
else {
|
980 |
throw GException::model_invalid_spatial(G_XML_SPATIAL, type);
|
981 |
} |
982 |
|
983 |
// Return pointer
|
984 |
return ptr;
|
985 |
} |
986 |
|
987 |
|
988 |
/***********************************************************************//** |
989 |
* @brief Construct spectral model from XML element
|
990 |
*
|
991 |
* @param[in] spectral XML element containing spectral model information.
|
992 |
*
|
993 |
* @exception GException::model_invalid_spectral
|
994 |
* Invalid spectral model type encountered.
|
995 |
*
|
996 |
* Returns pointer to a spectral model that is defined in an XML element.
|
997 |
***************************************************************************/
|
998 |
GModelSpectral* GCTAModelBackground::xml_spectral(const GXmlElement& spectral) const |
999 |
{ |
1000 |
// Get spectral model type
|
1001 |
std::string type = spectral.attribute("type"); |
1002 |
|
1003 |
// Get spectral model
|
1004 |
GModelSpectralRegistry registry; |
1005 |
GModelSpectral* ptr = registry.alloc(type); |
1006 |
|
1007 |
// If model if valid then read model from XML file
|
1008 |
if (ptr != NULL) { |
1009 |
ptr->read(spectral); |
1010 |
} |
1011 |
|
1012 |
// ... otherwise throw an exception
|
1013 |
else {
|
1014 |
throw GException::model_invalid_spectral(G_XML_SPECTRAL, type);
|
1015 |
} |
1016 |
|
1017 |
// Return pointer
|
1018 |
return ptr;
|
1019 |
} |
1020 |
|
1021 |
|
1022 |
/***********************************************************************//** |
1023 |
* @brief Construct temporal model from XML element
|
1024 |
*
|
1025 |
* @param[in] temporal XML element containing temporal model information.
|
1026 |
*
|
1027 |
* @exception GException::model_invalid_temporal
|
1028 |
* Invalid temporal model type encountered.
|
1029 |
*
|
1030 |
* Returns pointer to a temporal model that is defined in an XML element.
|
1031 |
***************************************************************************/
|
1032 |
GModelTemporal* GCTAModelBackground::xml_temporal(const GXmlElement& temporal) const |
1033 |
{ |
1034 |
// Get temporal model type
|
1035 |
std::string type = temporal.attribute("type"); |
1036 |
|
1037 |
// Get temporal model
|
1038 |
GModelTemporalRegistry registry; |
1039 |
GModelTemporal* ptr = registry.alloc(type); |
1040 |
|
1041 |
// If model if valid then read model from XML file
|
1042 |
if (ptr != NULL) { |
1043 |
ptr->read(temporal); |
1044 |
} |
1045 |
|
1046 |
// ... otherwise throw an exception
|
1047 |
else {
|
1048 |
throw GException::model_invalid_temporal(G_XML_TEMPORAL, type);
|
1049 |
} |
1050 |
|
1051 |
// Return pointer
|
1052 |
return ptr;
|
1053 |
} |
1054 |
|
1055 |
|
1056 |
|
1057 |
/***********************************************************************//** |
1058 |
* @brief Kernel for zenith angle Npred integration of background model
|
1059 |
*
|
1060 |
* @param[in] theta offset angle from integration center (pointing direction) (radians).
|
1061 |
*
|
1062 |
* Computes
|
1063 |
*
|
1064 |
* \f[
|
1065 |
* K(\rho | E, t) = \sin \rho \times
|
1066 |
* \int_{\omega_{\rm min}}^{\omega_{\rm max}}
|
1067 |
* S_{\rm p}(\rho,\omega | E, t) \,
|
1068 |
* N_{\rm pred}(\rho,\omega) d\omega
|
1069 |
* \f]
|
1070 |
*
|
1071 |
* The azimuth angle integration range
|
1072 |
* \f$[\omega_{\rm min}, \omega_{\rm max}\f$
|
1073 |
* is limited to an arc around the vector connecting the model centre to
|
1074 |
* the ROI centre. This limitation assures proper converges properly.
|
1075 |
***************************************************************************/
|
1076 |
double GCTAModelBackground::npred_roi_kern_theta::eval(double theta) |
1077 |
{ |
1078 |
// Initialise value
|
1079 |
double value = 0.0; |
1080 |
|
1081 |
// Continue only if offset angle is positive
|
1082 |
if (theta > 0.0) { |
1083 |
|
1084 |
// Compute sine of offset angle
|
1085 |
double sin_theta = std::sin(theta);
|
1086 |
|
1087 |
double domega = 0.5 * gammalib::cta_roi_arclength(theta, |
1088 |
m_dist, |
1089 |
m_cosdist, |
1090 |
m_sindist, |
1091 |
m_roi, |
1092 |
m_cosroi); |
1093 |
|
1094 |
// Continue only if arc length is positive
|
1095 |
if (domega > 0.0) { |
1096 |
|
1097 |
// Compute phi integration range
|
1098 |
double omega_min = m_omega0 - domega;
|
1099 |
double omega_max = m_omega0 + domega;
|
1100 |
|
1101 |
// Setup phi integration kernel
|
1102 |
GCTAModelBackground::npred_roi_kern_phi integrand(m_model, |
1103 |
m_obsEng, |
1104 |
m_obsTime, |
1105 |
m_rot, |
1106 |
theta, |
1107 |
sin_theta); |
1108 |
|
1109 |
// Integrate over phi
|
1110 |
GIntegral integral(&integrand); |
1111 |
integral.eps(1.0e-4); |
1112 |
value = integral.romb(omega_min,omega_max) * sin_theta; |
1113 |
|
1114 |
// Debug: Check for NaN
|
1115 |
#if defined(G_NAN_CHECK)
|
1116 |
if (gammalib::isnotanumber(value) || gammalib::isinfinite(value)) {
|
1117 |
std::cout << "*** ERROR: GCTAModelBackground::npred_roi_kern_theta::eval";
|
1118 |
std::cout << "(theta=" << theta << "):"; |
1119 |
std::cout << " NaN/Inf encountered";
|
1120 |
std::cout << " (value=" << value;
|
1121 |
std::cout << ", sin_theta=" << sin_theta;
|
1122 |
std::cout << ")" << std::endl;
|
1123 |
} |
1124 |
#endif
|
1125 |
|
1126 |
} // endif: arc length was positive
|
1127 |
|
1128 |
} // endif: offset angle was positive
|
1129 |
|
1130 |
// Return value
|
1131 |
return value;
|
1132 |
} |
1133 |
|
1134 |
|
1135 |
/***********************************************************************//** |
1136 |
* @brief Kernel for Npred azimuth angle integration of background model
|
1137 |
*
|
1138 |
* @param[in] phi Azimuth angle with respect to ROI centre (radians).
|
1139 |
*
|
1140 |
* Computes
|
1141 |
*
|
1142 |
* \f[
|
1143 |
* S_{\rm p}(\theta, \phi | E, t) \, N_{\rm pred}(\theta, \phi)
|
1144 |
* \f]
|
1145 |
*
|
1146 |
* @todo Re-consider formula for possible simplification (dumb matrix
|
1147 |
* multiplication is definitely not the fastest way to do that
|
1148 |
* computation).
|
1149 |
***************************************************************************/
|
1150 |
double GCTAModelBackground::npred_roi_kern_phi::eval(double phi) |
1151 |
{ |
1152 |
// Initialise Npred value
|
1153 |
double npred = 0.0; |
1154 |
|
1155 |
// Compute sky direction vector in native coordinates
|
1156 |
double cos_phi = std::cos(phi);
|
1157 |
double sin_phi = std::sin(phi);
|
1158 |
GVector native(-cos_phi*m_sin_theta, sin_phi*m_sin_theta, m_cos_theta); |
1159 |
|
1160 |
// Rotate from native into celestial system
|
1161 |
GVector cel = m_rot * native; |
1162 |
|
1163 |
// Set sky direction
|
1164 |
GSkyDir obsDir; |
1165 |
obsDir.celvector(cel); |
1166 |
|
1167 |
// Set Photon
|
1168 |
GPhoton photon(obsDir, m_obsEng, m_obsTime); |
1169 |
|
1170 |
// Get sky intensity for this photon
|
1171 |
double value = m_model->eval(photon);
|
1172 |
|
1173 |
// Debug: Check for NaN
|
1174 |
#if defined(G_NAN_CHECK)
|
1175 |
if (gammalib::isnotanumber(value) || gammalib::isinfinite(value)) {
|
1176 |
std::cout << "*** ERROR: GCTAModelBackground::npred_roi_kern_phi::eval";
|
1177 |
std::cout << "(phi=" << phi << "):"; |
1178 |
std::cout << " NaN/Inf encountered";
|
1179 |
std::cout << " (value=" << value;
|
1180 |
std::cout << ", cos_phi=" << cos_phi;
|
1181 |
std::cout << ", sin_phi=" << sin_phi;
|
1182 |
std::cout << ")" << std::endl;
|
1183 |
} |
1184 |
#endif
|
1185 |
|
1186 |
// Return Npred
|
1187 |
return value;
|
1188 |
} |
1189 |
|
1190 |
|
1191 |
/*==========================================================================
|
1192 |
= =
|
1193 |
= Friends =
|
1194 |
= =
|
1195 |
==========================================================================*/
|