GCTAModelBackground.cpp

source file - Mayer Michael, 10/10/2013 05:35 PM

Download (40.7 KB)

 
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
 ==========================================================================*/