GammaLib 2.2.0.dev
Loading...
Searching...
No Matches
GResponse.cpp
Go to the documentation of this file.
1/***************************************************************************
2 * GResponse.cpp - Abstract response base class *
3 * ----------------------------------------------------------------------- *
4 * copyright (C) 2008-2025 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 GResponse.hpp
23 * @brief Abstract response base class interface definition
24 * @author Juergen Knoedlseder
25 */
26
27/* __ Includes ___________________________________________________________ */
28#ifdef HAVE_CONFIG_H
29#include <config.h>
30#endif
31#include <string>
32#include <unistd.h> // access() function
33#include "GException.hpp"
34#include "GTools.hpp"
35#include "GMath.hpp"
36#include "GVector.hpp"
37#include "GMatrix.hpp"
38#include "GMatrixSparse.hpp"
39#include "GIntegral.hpp"
40#include "GIntegrals.hpp"
41#include "GResponse.hpp"
42#include "GEvent.hpp"
43#include "GPhoton.hpp"
44#include "GEnergy.hpp"
45#include "GTime.hpp"
46#include "GSource.hpp"
47#include "GEbounds.hpp"
48#include "GObservation.hpp"
49#include "GSkyMap.hpp"
50#include "GModelSky.hpp"
59
60/* __ Method name definitions ____________________________________________ */
61#define G_CONVOLVE "GResponse::convolve(GModelSky&, GObservation&, "\
62 "GMatrixSparse*)"
63#define G_IRF_RADIAL "GResponse::irf_radial(GEvent&, GSource&,"\
64 " GObservation&)"
65#define G_IRF_ELLIPTICAL "GResponse::irf_elliptical(GEvent&, GSource&,"\
66 " GObservation&)"
67#define G_IRF_DIFFUSE "GResponse::irf_diffuse(GEvent&, GSource&,"\
68 " GObservation&)"
69
70/* __ Macros _____________________________________________________________ */
71
72/* __ Coding definitions _________________________________________________ */
73
74/* __ Debug definitions __________________________________________________ */
75
76
77/*==========================================================================
78 = =
79 = Constructors/destructors =
80 = =
81 ==========================================================================*/
82
83/***********************************************************************//**
84 * @brief Void constructor
85 ***************************************************************************/
87{
88 // Initialise class members for clean destruction
90
91 // Return
92 return;
93}
94
95
96/***********************************************************************//**
97 * @brief Copy constructor
98 *
99 * @param[in] rsp Response.
100 ***************************************************************************/
102{
103 // Initialise class members for clean destruction
104 init_members();
105
106 // Copy members
107 copy_members(rsp);
108
109 // Return
110 return;
111}
112
113
114/***********************************************************************//**
115 * @brief Destructor
116 ***************************************************************************/
118{
119 // Free members
120 free_members();
121
122 // Return
123 return;
124}
125
126
127/*==========================================================================
128 = =
129 = Operators =
130 = =
131 ==========================================================================*/
132
133/***********************************************************************//**
134 * @brief Assignment operator
135 *
136 * @param[in] rsp Response.
137 * @return Response.
138 ***************************************************************************/
140{
141 // Execute only if object is not identical
142 if (this != &rsp) {
143
144 // Free members
145 free_members();
146
147 // Initialise private members for clean destruction
148 init_members();
149
150 // Copy members
151 copy_members(rsp);
152
153 } // endif: object was not identical
154
155 // Return this object
156 return *this;
157}
158
159
160/*==========================================================================
161 = =
162 = Public methods =
163 = =
164 ==========================================================================*/
165
166/***********************************************************************//**
167 * @brief Convolve sky model with the instrument response
168 *
169 * @param[in] model Sky model.
170 * @param[in] event Event.
171 * @param[in] obs Observation.
172 * @param[in] grad Should model gradients be computed?
173 * @return Event probability.
174 *
175 * Computes the event probability
176 *
177 * \f[
178 * P(p',E',t') = \int \int \int
179 * S(p,E,t) \times R(p',E',t'|p,E,t) \, dp \, dE \, dt
180 * \f]
181 *
182 * without taking into account any time dispersion. Energy dispersion is
183 * correctly handled by this method. If time dispersion is indeed needed,
184 * an instrument specific method needs to be provided.
185 ***************************************************************************/
186double GResponse::convolve(const GModelSky& model,
187 const GEvent& event,
188 const GObservation& obs,
189 const bool& grad) const
190{
191 // Set number of iterations for Romberg integration.
192 static const int iter = 6;
193
194 // Initialise result
195 double prob = 0.0;
196
197 // Continue only if the model has a spatial component
198 if (model.spatial() != NULL) {
199
200 // Get source time (no dispersion)
201 GTime srcTime = event.time();
202
203 // Case A: Integration
204 if (use_edisp()) {
205
206 // Retrieve true energy boundaries
207 GEbounds ebounds = this->ebounds(event.energy());
208
209 // Retrieve model energy boundaries
210 GEbounds ebounds_model = this->ebounds_model(model);
211
212 // Determine size of result array
213 int size = size_edisp_vector(model, obs, grad);
214
215 // Initialise vector array
216 GVector array(size);
217
218 // Loop over all boundaries
219 for (int i = 0; i < ebounds.size(); ++i) {
220
221 // Get true energy boundaries in MeV
222 double etrue_min = ebounds.emin(i).MeV();
223 double etrue_max = ebounds.emax(i).MeV();
224
225 // Limit energy boundaries
226 if (ebounds_model.size() > 0) {
227 if (ebounds_model.emin(0).MeV() > etrue_min) {
228 etrue_min = ebounds_model.emin(0).MeV();
229 }
230 if (ebounds_model.emax(0).MeV() < etrue_max) {
231 etrue_max = ebounds_model.emax(0).MeV();
232 }
233 }
234
235 // Continue only if valid
236 if (etrue_max > etrue_min) {
237
238 // Setup integration function
239 edisp_kerns integrand(this, &obs, &model, &event, srcTime, grad);
240 GIntegrals integral(&integrand);
241
242 // Set number of iterations
243 integral.fixed_iter(iter);
244
245 // Do Romberg integration
246 array += integral.romberg(etrue_min, etrue_max);
247
248 } // endif: interval was valid
249
250 } // endfor: looped over intervals
251
252 // Initialise array index
253 int index = 0;
254
255 // Get probability
256 prob = array[index++];
257
258 // Set gradients
259 if (grad) {
260
261 // Set spectral gradients
262 if (model.spectral() != NULL) {
263 for (int i = 0; i < model.spectral()->size(); ++i) {
264 GModelPar& par = (*(model.spectral()))[i];
265 if (par.is_free() && par.has_grad()) {
266 par.factor_gradient(array[index++]);
267 }
268 }
269 }
270
271 // Set temporal gradients
272 if (model.temporal() != NULL) {
273 for (int i = 0; i < model.temporal()->size(); ++i) {
274 GModelPar& par = (*(model.temporal()))[i];
275 if (par.is_free() && par.has_grad()) {
276 par.factor_gradient(array[index++]);
277 }
278 }
279 }
280
281 // Optionally set scale gradient for instrument
282 if (model.has_scales()) {
283 for (int i = 0; i < model.scales(); ++i) {
284 const GModelPar& par = model.scale(i);
285 if (par.name() == obs.instrument()) {
286 if (par.is_free() && par.has_grad()) {
287 par.factor_gradient(array[index++]);
288 }
289 }
290 }
291 }
292
293 } // endif: set gradients
294
295 } // endif: Case A
296
297 // Case B: No integration (assume no energy dispersion)
298 else {
299
300 // Get source energy (no dispersion)
301 GEnergy srcEng = event.energy();
302
303 // Evaluate probability
304 prob = eval_prob(model, event, srcEng, srcTime, obs, grad);
305
306 } // endelse: Case B
307
308 // Compile option: Check for NaN/Inf
309 #if defined(G_NAN_CHECK)
311 std::cout << "*** ERROR: GResponse::convolve:";
312 std::cout << " NaN/Inf encountered";
313 std::cout << " (prob=" << prob;
314 std::cout << ", event=" << event;
315 std::cout << ", srcTime=" << srcTime;
316 std::cout << ")" << std::endl;
317 }
318 #endif
319
320 } // endif: spatial component valid
321
322 // Return probability
323 return prob;
324}
325
326
327/***********************************************************************//**
328 * @brief Convolve sky model with the instrument response
329 *
330 * @param[in] model Sky model.
331 * @param[in] obs Observation.
332 * @param[out] gradients Pointer to matrix of gradients.
333 * @return Vector of event probabilities.
334 *
335 * Computes the event probability
336 *
337 * \f[
338 * P(p',E',t') = \int \int \int
339 * S(p,E,t) \times R(p',E',t'|p,E,t) \, dp \, dE \, dt
340 * \f]
341 *
342 * without taking into account any time dispersion. Energy dispersion is
343 * correctly handled by this method. If time dispersion is indeed needed,
344 * an instrument specific method needs to be provided.
345 ***************************************************************************/
347 const GObservation& obs,
348 GMatrixSparse* gradients) const
349{
350 // Set number of iterations for Romberg integration.
351 static const int iter = 6;
352
353 // Get number of model parameters and number of events
354 int npars = model.size();
355 int nevents = obs.events()->size();
356
357 // Initialise gradients flag
358 bool grad = (gradients != NULL);
359
360 // Check matrix consistency
361 if (grad) {
362
363 // Check number of columns
364 int ncolumns = (nevents > 0) ? npars : 0;
365 if (gradients->columns() != ncolumns) {
366 std::string msg = "Number of "+gammalib::str(gradients->columns())+" "
367 "columns in gradient matrix differs from expected "
368 "number of "+gammalib::str(ncolumns)+". Please "
369 "specify compatible arguments.";
371 }
372
373 // Check number of rows
374 int nrows = (ncolumns > 0) ? nevents : 0;
375 if (gradients->rows() != nrows) {
376 std::string msg = "Number of "+gammalib::str(gradients->rows())+" "
377 "rows in gradient matrix differs from expected "
378 "number of "+gammalib::str(nrows)+". Please "
379 "specify compatible arguments.";
381 }
382
383 } // endif: gadient was requested
384
385 // Initialise result
386 GVector probs(nevents);
387
388 // Continue only if the model has a spatial component
389 if (model.spatial() != NULL) {
390
391 // Case A: Integration
392 if (use_edisp()) {
393
394 // Initialise temporary vectors to hold gradients
395 GVector* tmp_gradients = NULL;
396 if (grad) {
397 tmp_gradients = new GVector[npars];
398 for (int i = 0; i < npars; ++i) {
399 tmp_gradients[i] = GVector(nevents);
400 }
401 }
402
403 // Determine size of result array
404 int size = size_edisp_vector(model, obs, grad);
405
406 // Get model energy boundaries
407 GEbounds ebounds_model = this->ebounds_model(model);
408
409 // Loop over events
410 for (int k = 0; k < nevents; ++k) {
411
412 // Get reference to event
413 const GEvent& event = *((*obs.events())[k]);
414
415 // Get source time (no dispersion)
416 GTime srcTime = event.time();
417
418 // Retrieve true energy boundaries
419 GEbounds ebounds = this->ebounds(event.energy());
420
421 // Initialise vector array
422 GVector array(size);
423
424 // Loop over all boundaries
425 for (int i = 0; i < ebounds.size(); ++i) {
426
427 // Get true energy boundaries in MeV
428 double etrue_min = ebounds.emin(i).MeV();
429 double etrue_max = ebounds.emax(i).MeV();
430
431 // Limit energy boundaries
432 if (ebounds_model.size() > 0) {
433 if (ebounds_model.emin(0).MeV() > etrue_min) {
434 etrue_min = ebounds_model.emin(0).MeV();
435 }
436 if (ebounds_model.emax(0).MeV() < etrue_max) {
437 etrue_max = ebounds_model.emax(0).MeV();
438 }
439 }
440
441 // Continue only if valid
442 if (etrue_max > etrue_min) {
443
444 // Setup integration function
445 edisp_kerns integrand(this, &obs, &model, &event, srcTime, grad);
446 GIntegrals integral(&integrand);
447
448 // Set number of iterations
449 integral.fixed_iter(iter);
450
451 // Do Romberg integration
452 array += integral.romberg(etrue_min, etrue_max);
453
454 } // endif: interval was valid
455
456 } // endfor: looped over intervals
457
458 // Initialise array index
459 int index = 0;
460
461 // Get probability
462 probs[k] = array[index++];
463
464 // Set gradients
465 if (grad) {
466
467 // Get number of spatial, spectral and temporal parameters
468 int n_spat = model.spatial()->size();
469 int n_spec = model.spectral()->size();
470 int n_temp = model.temporal()->size();
471
472 // Set spectral gradients
473 if (model.spectral() != NULL) {
474 int offset = n_spat;
475 for (int i = 0; i < n_spec; ++i) {
476 const GModelPar& par = (*(model.spectral()))[i];
477 if (par.is_free() && par.has_grad()) {
478 tmp_gradients[offset+i][k] = array[index++];
479 }
480 }
481 }
482
483 // Set temporal gradients
484 if (model.temporal() != NULL) {
485 int offset = n_spat + n_spec;
486 for (int i = 0; i < n_temp; ++i) {
487 const GModelPar& par = (*(model.temporal()))[i];
488 if (par.is_free() && par.has_grad()) {
489 tmp_gradients[offset+i][k] = array[index++];
490 }
491 }
492 }
493
494 // Optionally set scale gradient for instrument
495 if (model.has_scales()) {
496 int offset = n_spat + n_spec + n_temp;
497 for (int i = 0; i < model.scales(); ++i) {
498 const GModelPar& par = model.scale(i);
499 if (par.name() == obs.instrument()) {
500 if (par.is_free() && par.has_grad()) {
501 tmp_gradients[offset+i][k] = array[index++];
502 }
503 }
504 }
505 }
506
507 } // endif: set gradients
508
509 // Compile option: Check for NaN/Inf
510 #if defined(G_NAN_CHECK)
511 if (gammalib::is_notanumber(probs[k]) || gammalib::is_infinite(probs[k])) {
512 std::cout << "*** ERROR: GResponse::convolve:";
513 std::cout << " NaN/Inf encountered";
514 std::cout << " (probs[" << k << "]=" << probs[k];
515 std::cout << ", event=" << event;
516 std::cout << ", srcTime=" << srcTime;
517 std::cout << ")" << std::endl;
518 }
519 #endif
520
521 } // endfor: looped over events
522
523 // Post-process gradients
524 if (grad) {
525
526 // Fill gradients into matrix
527 for (int i = 0; i < npars; ++i) {
528 gradients->column(i, tmp_gradients[i]);
529 }
530
531 // Delete temporal gradients
532 delete [] tmp_gradients;
533
534 } // endif: post-processed gradients
535
536 } // endif: Case A
537
538 // Case B: No integration (assume no energy dispersion)
539 else {
540
541 // Evaluate probability
542 probs = eval_probs(model, obs, gradients);
543
544 } // endelse: Case B
545
546 } // endif: spatial component valid
547
548 // Return probabilities
549 return probs;
550}
551
552
553/***********************************************************************//**
554 * @brief Return instrument response integrated over the spatial model
555 *
556 * @param[in] event Event.
557 * @param[in] source Source.
558 * @param[in] obs Observation.
559 * @return Instrument response to a spatial model.
560 *
561 * Returns the instrument response for a given event, source and observation
562 * integrated over the spatial model component. The method computes
563 *
564 * \f[
565 * {\tt irf}(p', E', t') = \int_p M_{\rm S}(p | E, t) \,
566 * R(p', E', t' | p, E, t) \, d\,p
567 * \f]
568 *
569 * where
570 * * \f$M_{\rm S}(p | E, t)\f$ is the spatial model component,
571 * * \f$R(p', E', t' | p, E, t)\f$ is the Instrument Response Function (IRF),
572 * * \f$p'\f$ is the measured instrument direction,
573 * * \f$E'\f$ is the measured or reconstructed energy,
574 * * \f$t'\f$ is the measured arrival time,
575 * * \f$p\f$ is the true photon arrival direction,
576 * * \f$E\f$ is the true photon energy, and
577 * * \f$t\f$ is the true trigger time.
578 *
579 * The integration is done over all relevant true sky directions \f$p\f$.
580 *
581 * Depending on the type of the source model the method branches to the
582 * following methods to perform the actual computations
583 *
584 * * irf_ptsrc() - for the handling of a point source
585 * * irf_radial() - for radial models
586 * * irf_elliptical() - for elliptical models
587 * * irf_diffuse() - for diffuse models
588 * * irf_composite() - for composite models
589 *
590 * The method implements a caching mechanism for spatial models that have all
591 * parameters fixed. For those models the instrument response for a given
592 * event and observation is only computed once and then stored in an internal
593 * cache from which it is fetched back in case that the method is called
594 * again for the same event and observation.
595 ***************************************************************************/
596double GResponse::irf_spatial(const GEvent& event,
597 const GSource& source,
598 const GObservation& obs) const
599{
600 // Initialise IRF value
601 double irf = 0.0;
602
603 // Set IRF value attributes
604 std::string name = obs.id() + ":" + source.name();
605 const GInstDir& dir = event.dir();
606 const GEnergy& ereco = event.energy();
607 const GEnergy& etrue = source.energy();
608
609 // Signal if spatial model has free parameters
610 bool has_free_pars = source.model()->has_free_pars();
611
612 // If the spatial model component has free parameters, or the response
613 // cache should not be used, or the cache does not contain the requested
614 // IRF value then compute the IRF value for the spatial model.
615 if (has_free_pars ||
617 !m_irf_cache.contains(name, dir, ereco, etrue, &irf)) {
618
619 // Compute IRF for spatial model
620 switch (source.model()->code()) {
622 irf = irf_ptsrc(event, source, obs);
623 break;
625 irf = irf_radial(event, source, obs);
626 break;
628 irf = irf_elliptical(event, source, obs);
629 break;
631 irf = irf_diffuse(event, source, obs);
632 break;
634 irf = irf_composite(event, source, obs);
635 break;
636 default:
637 break;
638 }
639
640 } // endif: computed spatial model
641
642 // If the spatial model has no free parameters and the response cache
643 // should be used then put the IRF value in the response cache.
644 if (!has_free_pars && m_use_irf_cache) {
645 m_irf_cache.set(name, dir, ereco, etrue, irf);
646 }
647
648 // Return IRF value
649 return irf;
650}
651
652
653/***********************************************************************//**
654 * @brief Return instrument response vector integrated over the spatial model
655 *
656 * @param[in] model Sky model.
657 * @param[in] obs Observation.
658 * @param[in] gradients Gradients matrix.
659 * @return Instrument response vector to a spatial model.
660 *
661 * Returns the instrument response to a sky model integrated over the spatial
662 * model component for all events in a given observation. The method computes
663 *
664 * \f[
665 * {\tt irf}(p', E', t') = \int_p M_{\rm S}(p | E, t) \,
666 * R(p', E', t' | p, E, t) \, d\,p
667 * \f]
668 *
669 * where
670 * * \f$M_{\rm S}(p | E, t)\f$ is the spatial model component,
671 * * \f$R(p', E', t' | p, E, t)\f$ is the Instrument Response Function (IRF),
672 * * \f$p'\f$ is the measured instrument direction,
673 * * \f$E'\f$ is the measured or reconstructed energy,
674 * * \f$t'\f$ is the measured arrival time,
675 * * \f$p\f$ is the true photon arrival direction,
676 * * \f$E\f$ is the true photon energy, and
677 * * \f$t\f$ is the true trigger time.
678 *
679 * The integration is done over all relevant true sky directions \f$p\f$.
680 *
681 * Depending on the type of the source model the method branches to the
682 * following methods to perform the actual computations
683 *
684 * * irf_ptsrc() - for the handling of a point source
685 * * irf_radial() - for radial models
686 * * irf_elliptical() - for elliptical models
687 * * irf_diffuse() - for diffuse models
688 * * irf_composite() - for composite models
689 *
690 * The method implements a caching mechanism for spatial models that have all
691 * parameters fixed. For those models the instrument response for a given
692 * event and observation is only computed once and then stored in an internal
693 * cache from which it is fetched back in case that the method is called
694 * again for the same event and observation.
695 ***************************************************************************/
697 const GObservation& obs,
698 GMatrix* gradients) const
699{
700 // Get number of model parameters and number of events
701 int npars = model.size();
702 int nevents = obs.events()->size();
703
704 // Initialise result
705 GVector irfs(nevents);
706
707 // Continue only if model has a spatial component
708 if (model.spatial() != NULL) {
709
710 // Set IRF cache identifier
711 std::string cache_id = obs.id() + ":" + model.name();
712
713 // Signal if spatial model has free parameters
714 bool has_free_pars = model.spatial()->has_free_pars();
715
716 // If the spatial model component has free parameters, or the response
717 // cache should not be used, or the cache does not contain the requested
718 // IRF value then compute the IRF value for the spatial model.
719 if (has_free_pars ||
721 !m_irf_vector_cache.contains(cache_id, &irfs)) {
722
723 // Compute IRF for spatial model
724 switch (model.spatial()->code()) {
726 irfs = irf_ptsrc(model, obs, gradients);
727 break;
729 irfs = irf_radial(model, obs, gradients);
730 break;
732 irfs = irf_elliptical(model, obs, gradients);
733 break;
735 irfs = irf_diffuse(model, obs, gradients);
736 break;
738 irfs = irf_composite(model, obs, gradients);
739 break;
740 default:
741 break;
742 }
743
744 // If the spatial model has no free parameters and the response cache
745 // should be used then put the IRF value in the response cache.
746 if (!has_free_pars && m_use_irf_cache) {
747 m_irf_vector_cache.set(cache_id, irfs);
748 }
749
750 } // endif: computed spatial model
751
752 } // endif: model had spatial component
753
754 // Return IRF values
755 return irfs;
756}
757
758
759/***********************************************************************//**
760 * @brief Remove response cache for specific observation and model
761 *
762 * @param[in] cache_id Response cache identifier.
763 *
764 * Remove response cache with @p cache_id. The response cache identifier
765 * @p cache_id is formed by a combination of observation identifier and
766 * model name using
767 *
768 * cache_id = obs.id() + ":" + model.name()
769 *
770 * Please make sure to respect the same format for the specified @p cache_id
771 * parameter.
772 ***************************************************************************/
773void GResponse::remove_response_cache(const std::string& cache_id)
774{
775 // Remove cache_id from response caches
776 m_irf_cache.remove(cache_id);
777 m_nroi_cache.remove(cache_id);
778 m_irf_vector_cache.remove(cache_id);
779
780 // Return
781 return;
782}
783
784
785/*==========================================================================
786 = =
787 = Protected methods =
788 = =
789 ==========================================================================*/
790
791/***********************************************************************//**
792 * @brief Initialise class members
793 ***************************************************************************/
795{
796 // Initialize members
797 m_use_irf_cache = true; //!< Switched on by default
798 m_use_nroi_cache = true; //!< Switched on by default
803 m_irf_diffuse_resolution = 0.1; //!< Angular resolution (deg)
804
805 // Clear cache
809
810 // Return
811 return;
812}
813
814
815/***********************************************************************//**
816 * @brief Copy class members
817 *
818 * @param[in] rsp Response.
819 ***************************************************************************/
837
838
839/***********************************************************************//**
840 * @brief Delete class members
841 ***************************************************************************/
843{
844 // Return
845 return;
846}
847
848
849/***********************************************************************//**
850 * @brief Return instrument response to point source
851 *
852 * @param[in] event Observed event.
853 * @param[in] source Source.
854 * @param[in] obs Observation.
855 * @return Instrument response to point source.
856 *
857 * Returns the instrument response to a point source.
858 ***************************************************************************/
859double GResponse::irf_ptsrc(const GEvent& event,
860 const GSource& source,
861 const GObservation& obs) const
862{
863 // Get pointer to point source model
864 const GModelSpatialPointSource* model =
865 static_cast<const GModelSpatialPointSource*>(source.model());
866
867 // Setup photon
868 GPhoton photon(model->dir(), source.energy(), source.time());
869
870 // Get IRF
871 double irf = this->irf(event, photon, obs);
872
873 // Return IRF value
874 return irf;
875}
876
877
878/***********************************************************************//**
879 * @brief Return instrument response to radial source
880 *
881 * @param[in] event Observed event.
882 * @param[in] source Source.
883 * @param[in] obs Observation.
884 * @return Instrument response to radial source.
885 *
886 * Returns the instrument response to a radial source for a given event,
887 * source and observation. The method computes
888 *
889 * \f[
890 * {\tt irf}(p', E', t') = \int_0^{\theta_{\rm max}}
891 * M_{\rm S}(\theta | E, t) \, sin \, \theta
892 * \int_0^{2\pi}
893 * R(p', E', t' | \theta, \phi, E, t) \,
894 * d\,\phi \, d\,\theta
895 * \f]
896 *
897 * where
898 * * \f$M_{\rm S}(\theta | E, t)\f$ is the radial model component,
899 * * \f$R(p', E', t' | \theta, \phi, E, t)\f$ is the Instrument Response
900 * Function (IRF),
901 * * \f$p'\f$ is the measured instrument direction,
902 * * \f$E'\f$ is the measured or reconstructed energy,
903 * * \f$t'\f$ is the measured arrival time,
904 * * \f$\theta\f$ is the radial distance from the model centre,
905 * * \f$\phi\f$ is the azimuthal angle around the model centre,
906 * * \f$E\f$ is the true photon energy, and
907 * * \f$t\f$ is the true trigger time.
908 *
909 * The azimuth integration is done over \f$2\pi\f$ using the
910 * irf_radial_kern_phi::eval() method.
911 * The radial integration is done out to a maximum angle
912 * \f$\theta_{\rm max}\f$, given by GModelSpatialRadial::theta_max(), by the
913 * irf_radial_kern_theta::eval() method.
914 ***************************************************************************/
915double GResponse::irf_radial(const GEvent& event,
916 const GSource& source,
917 const GObservation& obs) const
918{
919 // Initialise IRF
920 double irf = 0.0;
921
922 // Get pointer to radial model
923 const GModelSpatialRadial* model =
924 static_cast<const GModelSpatialRadial*>(source.model());
925
926 // Get source attributes
927 const GEnergy& srcEng = source.energy();
928 const GTime& srcTime = source.time();
929
930 // Set radial integration range (radians)
931 double theta_min = 0.0;
932 double theta_max = model->theta_max() - 1.0e-12; // Kludge to stay inside
933 // the boundary of a
934 // sharp edged model
935
936 // Integrate if interval is valid
937 if (theta_max > theta_min) {
938
939 // Allocate Euler and rotation matrices
940 GMatrix ry;
941 GMatrix rz;
942
943 // Set up rotation matrix to rotate from native model coordinates to
944 // celestial coordinates
945 ry.eulery( model->dir().dec_deg() - 90.0);
946 rz.eulerz(-model->dir().ra_deg());
947 GMatrix rot = (ry * rz).transpose();
948
949 // Setup integration kernel
950 irf_radial_kern_theta integrand(this,
951 &event,
952 &obs,
953 model,
954 &rot,
955 &srcEng,
956 &srcTime,
958
959 // Integrate over model's radial coordinate
960 GIntegral integral(&integrand);
962
963 // Integrate kernel
964 irf = integral.romberg(theta_min, theta_max, m_irf_radial_iter_theta);
965
966 // Compile option: Check for NaN/Inf
967 #if defined(G_NAN_CHECK)
969 std::cout << "*** ERROR: GResponse::irf_radial:";
970 std::cout << " NaN/Inf encountered";
971 std::cout << " (irf=" << irf;
972 std::cout << ", theta_min=" << theta_min;
973 std::cout << ", theta_max=" << theta_max << ")";
974 std::cout << std::endl;
975 }
976 #endif
977
978 } // endif: integration interval was valid
979
980 // Return IRF value
981 return irf;
982}
983
984
985/***********************************************************************//**
986 * @brief Return instrument response to elliptical source
987 *
988 * @param[in] event Observed event.
989 * @param[in] source Source.
990 * @param[in] obs Observation.
991 * @return Instrument response to elliptical source.
992 *
993 * Returns the instrument response to an elliptical source for a given event,
994 * source and observation. The method computes
995 *
996 * \f[
997 * {\tt irf}(p', E', t') = \int_0^{\theta_{\rm max}} \int_0^{2\pi}
998 * M_{\rm S}(\theta, \phi | E, t) \,
999 * R(p', E', t' | \theta, \phi, E, t) \,
1000 * sin \, \theta \,
1001 * d\,\phi \, d\,\theta
1002 * \f]
1003 *
1004 * where
1005 * * \f$M_{\rm S}(\theta, \phi | E, t)\f$ is the elliptical model component,
1006 * * \f$R(p', E', t' | \theta, \phi, E, t)\f$ is the Instrument Response
1007 * Function (IRF),
1008 * * \f$p'\f$ is the measured instrument direction,
1009 * * \f$E'\f$ is the measured or reconstructed energy,
1010 * * \f$t'\f$ is the measured arrival time,
1011 * * \f$\theta\f$ is the radial distance from the model centre,
1012 * * \f$\phi\f$ is the azimuthal angle around the model centre,
1013 * * \f$E\f$ is the true photon energy, and
1014 * * \f$t\f$ is the true trigger time.
1015 *
1016 * The azimuth integration is done over \f$2\pi\f$ using the
1017 * irf_elliptical_kern_phi::eval() method.
1018 * The radial integration is done out to a maximum angle
1019 * \f$\theta_{\rm max}\f$, given by GModelSpatialElliptical::theta_max(), by
1020 * the irf_elliptical_kern_theta::eval() method.
1021 ***************************************************************************/
1023 const GSource& source,
1024 const GObservation& obs) const
1025{
1026 // Initialise IRF
1027 double irf = 0.0;
1028
1029 // Get pointer to elliptical model
1030 const GModelSpatialElliptical* model =
1031 static_cast<const GModelSpatialElliptical*>(source.model());
1032
1033 // Get source attributes
1034 const GEnergy& srcEng = source.energy();
1035 const GTime& srcTime = source.time();
1036
1037 // Set radial integration range (radians)
1038 double theta_min = 0.0;
1039 double theta_max = model->theta_max() - 1.0e-6; // Kludge to stay inside
1040 // the boundary of a
1041 // sharp edged model
1042
1043 // Integrate if interval is valid
1044 if (theta_max > theta_min) {
1045
1046 // Allocate Euler and rotation matrices
1047 GMatrix ry;
1048 GMatrix rz;
1049
1050 // Set up rotation matrix to rotate from native model coordinates to
1051 // celestial coordinates
1052 ry.eulery( model->dir().dec_deg() - 90.0);
1053 rz.eulerz(-model->dir().ra_deg());
1054 GMatrix rot = (ry * rz).transpose();
1055
1056 // Setup integration kernel
1057 irf_elliptical_kern_theta integrand(this,
1058 &event,
1059 &obs,
1060 model,
1061 &rot,
1062 &srcEng,
1063 &srcTime,
1065
1066 // Integrate over model's radial coordinate
1067 GIntegral integral(&integrand);
1069
1070 // Integrate kernel
1071 irf = integral.romberg(theta_min, theta_max, m_irf_elliptical_iter_theta);
1072
1073 // Compile option: Check for NaN/Inf
1074 #if defined(G_NAN_CHECK)
1076 std::cout << "*** ERROR: GResponse::irf_elliptical:";
1077 std::cout << " NaN/Inf encountered";
1078 std::cout << " (irf=" << irf;
1079 std::cout << ", theta_min=" << theta_min;
1080 std::cout << ", theta_max=" << theta_max << ")";
1081 std::cout << std::endl;
1082 }
1083 #endif
1084
1085 } // endif: integration interval was valid
1086
1087 // Return IRF value
1088 return irf;
1089}
1090
1091
1092/***********************************************************************//**
1093 * @brief Return instrument response to diffuse source
1094 *
1095 * @param[in] event Observed event.
1096 * @param[in] source Source.
1097 * @param[in] obs Observation.
1098 * @return Instrument response to diffuse source.
1099 *
1100 * Returns the instrument response to a diffuse source for a given event,
1101 * source and observation. The method computes
1102 *
1103 * \f[
1104 * {\tt irf}(p', E', t') = \sum_p M_{\rm S}(p | E, t) \, \Delta(p)
1105 * R(p', E', t' | p, E, t)
1106 * \f]
1107 *
1108 * where
1109 * * \f$M_{\rm S}(p | E, t)\f$ is the diffuse model component,
1110 * * \f$\Delta(p)\f$ is the solid angle of the map pixel
1111 * * \f$R(p', E', t' | p, E, t)\f$ is the Instrument Response Function (IRF),
1112 * * \f$p'\f$ is the measured instrument direction,
1113 * * \f$E'\f$ is the measured or reconstructed energy,
1114 * * \f$t'\f$ is the measured arrival time,
1115 * * \f$p\f$ is the true photon arrival direction,
1116 * * \f$E\f$ is the true photon energy, and
1117 * * \f$t\f$ is the true trigger time.
1118 *
1119 * The method simply adds up all sky map pixels multiplied with the IRF for
1120 * all diffuse model pixels. For models that do not contain sky map pixels
1121 * (such as the GModelSpatialDiffuseConst model) the method allocates an
1122 * all-sky sky map with an angular resolution that is specified by the
1123 * m_irf_diffuse_resolution member.
1124 ***************************************************************************/
1125double GResponse::irf_diffuse(const GEvent& event,
1126 const GSource& source,
1127 const GObservation& obs) const
1128{
1129 // Initialise IRF
1130 double irf = 0.0;
1131
1132 // Get source attributes
1133 const GEnergy& srcEng = source.energy();
1134 const GTime& srcTime = source.time();
1135
1136 // If model is a diffuse map model then extract sky map
1137 const GModelSpatialDiffuseMap* map =
1138 dynamic_cast<const GModelSpatialDiffuseMap*>(source.model());
1139 if (map != NULL) {
1140
1141 // Get reference to sky map
1142 const GSkyMap& skymap = map->map();
1143
1144 // Loop over all sky map pixels
1145 for (int i = 0; i < skymap.npix(); ++i) {
1146
1147 // Get map value
1148 double value = skymap(i);
1149
1150 // Go to next pixel if map value is zero
1151 if (value == 0.0) {
1152 continue;
1153 }
1154
1155 // Get sky direction
1156 GSkyDir srcDir = skymap.inx2dir(i);
1157
1158 // Setup photon
1159 GPhoton photon(srcDir, srcEng, srcTime);
1160
1161 // Add IRF multiplied by flux in map pixel. Since the map value
1162 // is per steradian we have to multiply with the solid angle of
1163 // the map pixel
1164 irf += this->irf(event, photon, obs) * value * skymap.solidangle(i);
1165
1166 } // endfor: looped over all sky map pixels
1167
1168 } // endif: model was diffuse map
1169
1170 // ... otherwise if model is a cube map model then extract sky map
1171 else {
1172 const GModelSpatialDiffuseCube* cube =
1173 dynamic_cast<const GModelSpatialDiffuseCube*>(source.model());
1174 if (cube != NULL) {
1175
1176 // Get reference to sky cube
1177 const GSkyMap& skymap = cube->cube();
1178
1179 // Loop over all sky map pixels
1180 for (int i = 0; i < skymap.npix(); ++i) {
1181
1182 // Get sky direction
1183 GSkyDir srcDir = skymap.inx2dir(i);
1184
1185 // Setup photon
1186 GPhoton photon(srcDir, srcEng, srcTime);
1187
1188 // Get map value
1189 double value = cube->eval(photon);
1190
1191 // Go to next pixel if map value is not positive
1192 if (value <= 0.0) {
1193 continue;
1194 }
1195
1196 // Add IRF multiplied by flux in map pixel. Since the map
1197 // value is per steradian we have to multiply with the solid
1198 // angle of the map pixel
1199 irf += this->irf(event, photon, obs) * value * skymap.solidangle(i);
1200
1201 } // endfor: looped over all sky map pixels
1202
1203 } // endelse: model was diffuse cube
1204
1205 // ... otherwise allocate all sky map with specified resolution
1206 else {
1207
1208 // Allocate sky map
1209 int nx = int(360.0/m_irf_diffuse_resolution + 0.5);
1210 int ny = int(180.0/m_irf_diffuse_resolution + 0.5);
1211 double dx = 360.0 / double(nx);
1212 double dy = 180.0 / double(ny);
1213 GSkyMap skymap = GSkyMap("CAR", "CEL", 0.0, 0.0, -dx, dy, nx, ny);
1214
1215 // Get pointer to diffuse model
1216 const GModelSpatialDiffuse* model =
1217 static_cast<const GModelSpatialDiffuse*>(source.model());
1218
1219 // Loop over all sky map pixels
1220 for (int i = 0; i < skymap.npix(); ++i) {
1221
1222 // Get sky direction
1223 GSkyDir srcDir = skymap.inx2dir(i);
1224
1225 // Setup photon
1226 GPhoton photon(srcDir, srcEng, srcTime);
1227
1228 // Get model value
1229 double value = model->eval(photon);
1230
1231 // Go to next pixel if map value is not positive
1232 if (value <= 0.0) {
1233 continue;
1234 }
1235
1236 // Add IRF multiplied by flux in map pixel. Since the map
1237 // value is per steradian we have to multiply with the solid
1238 // angle of the map pixel
1239 irf += this->irf(event, photon, obs) * value * skymap.solidangle(i);
1240
1241 } // endfor: looped over sky map pixels
1242
1243 } // endelse: model was not a cube
1244
1245 } // endelse: model was not a map
1246
1247 // Return IRF value
1248 return irf;
1249}
1250
1251
1252/***********************************************************************//**
1253 * @brief Return instrument response to composite source
1254 *
1255 * @param[in] event Observed event.
1256 * @param[in] source Source.
1257 * @param[in] obs Observation.
1258 * @return Instrument response to composite source.
1259 *
1260 * Returns the instrument response to a specified composite source.
1261 ***************************************************************************/
1263 const GSource& source,
1264 const GObservation& obs) const
1265{
1266 // Initialise IRF
1267 double irf = 0.0;
1268
1269 // Get pointer to composite model
1270 const GModelSpatialComposite* composite =
1271 dynamic_cast<const GModelSpatialComposite*>(source.model());
1272
1273 // Loop over model components
1274 for (int i = 0; i < composite->components(); ++i) {
1275
1276 // Get pointer to spatial component
1277 GModelSpatial* spat = const_cast<GModelSpatial*>(composite->component(i));
1278
1279 // Set source name
1280 std::string name = source.name() + ":" + composite->name(i);
1281
1282 // Create new GSource object
1283 GSource src(name, spat, source.energy(), source.time());
1284
1285 // Compute irf value
1286 irf += irf_spatial(event, src, obs) * composite->scale(i)->value();
1287
1288 }
1289
1290 // Divide by number of model components
1291 double sum = composite->sum_of_scales();
1292 if (sum > 0.0) {
1293 irf /= sum;
1294 }
1295
1296 // Return IRF value
1297 return irf;
1298}
1299
1300
1301/***********************************************************************//**
1302 * @brief Return instrument response to point source sky model
1303 *
1304 * @param[in] model Sky model.
1305 * @param[in] obs Observation.
1306 * @param[in] gradients Gradients matrix.
1307 * @return Instrument response to point source sky model.
1308 *
1309 * Returns the instrument response to a point source sky model for all
1310 * events.
1311 ***************************************************************************/
1313 const GObservation& obs,
1314 GMatrix* gradients) const
1315{
1316 // Get number of events
1317 int nevents = obs.events()->size();
1318
1319 // Initialise result
1320 GVector irfs(nevents);
1321
1322 // Loop over events
1323 for (int k = 0; k < nevents; ++k) {
1324
1325 // Get reference to event
1326 const GEvent& event = *((*obs.events())[k]);
1327
1328 // Get source energy and time (no dispersion)
1329 GEnergy srcEng = event.energy();
1330 GTime srcTime = event.time();
1331
1332 // Setup source
1333 GSource source(model.name(), model.spatial(), srcEng, srcTime);
1334
1335 // Get IRF value for event
1336 irfs[k] = this->irf_ptsrc(event, source, obs);
1337
1338 } // endfor: looped over events
1339
1340 // Return IRF value
1341 return irfs;
1342}
1343
1344
1345/***********************************************************************//**
1346 * @brief Return instrument response to radial source sky model
1347 *
1348 * @param[in] model Sky model.
1349 * @param[in] obs Observation.
1350 * @param[in] gradients Gradients matrix.
1351 * @return Instrument response to radial source sky model.
1352 *
1353 * Returns the instrument response to a radial source sky model for all
1354 * events.
1355 ***************************************************************************/
1357 const GObservation& obs,
1358 GMatrix* gradients) const
1359{
1360 // Get number of events
1361 int nevents = obs.events()->size();
1362
1363 // Initialise result
1364 GVector irfs(nevents);
1365
1366 // Loop over events
1367 for (int k = 0; k < nevents; ++k) {
1368
1369 // Get reference to event
1370 const GEvent& event = *((*obs.events())[k]);
1371
1372 // Get source energy and time (no dispersion)
1373 GEnergy srcEng = event.energy();
1374 GTime srcTime = event.time();
1375
1376 // Setup source
1377 GSource source(model.name(), model.spatial(), srcEng, srcTime);
1378
1379 // Get IRF value for event
1380 irfs[k] = this->irf_radial(event, source, obs);
1381
1382 } // endfor: looped over events
1383
1384 // Return IRF value
1385 return irfs;
1386}
1387
1388
1389/***********************************************************************//**
1390 * @brief Return instrument response to ellipitical source sky model
1391 *
1392 * @param[in] model Sky model.
1393 * @param[in] obs Observation.
1394 * @param[in] gradients Gradients matrix.
1395 * @return Instrument response to ellipitical source sky model.
1396 *
1397 * Returns the instrument response to a ellipitical source sky model for all
1398 * events.
1399 ***************************************************************************/
1401 const GObservation& obs,
1402 GMatrix* gradients) const
1403{
1404 // Get number of events
1405 int nevents = obs.events()->size();
1406
1407 // Initialise result
1408 GVector irfs(nevents);
1409
1410 // Loop over events
1411 for (int k = 0; k < nevents; ++k) {
1412
1413 // Get reference to event
1414 const GEvent& event = *((*obs.events())[k]);
1415
1416 // Get source energy and time (no dispersion)
1417 GEnergy srcEng = event.energy();
1418 GTime srcTime = event.time();
1419
1420 // Setup source
1421 GSource source(model.name(), model.spatial(), srcEng, srcTime);
1422
1423 // Get IRF value for event
1424 irfs[k] = this->irf_elliptical(event, source, obs);
1425
1426 } // endfor: looped over events
1427
1428 // Return IRF value
1429 return irfs;
1430}
1431
1432
1433/***********************************************************************//**
1434 * @brief Return instrument response to diffuse source sky model
1435 *
1436 * @param[in] model Sky model.
1437 * @param[in] obs Observation.
1438 * @param[in] gradients Gradients matrix.
1439 * @return Instrument response to diffuse source sky model.
1440 *
1441 * Returns the instrument response to a diffuse source sky model for all
1442 * events.
1443 ***************************************************************************/
1445 const GObservation& obs,
1446 GMatrix* gradients) const
1447{
1448 // Get number of events
1449 int nevents = obs.events()->size();
1450
1451 // Initialise result
1452 GVector irfs(nevents);
1453
1454 // Get reference to normalisation parameter
1455 const GModelPar& value = (*model.spatial())[0];
1456
1457 // Loop over events
1458 for (int k = 0; k < nevents; ++k) {
1459
1460 // Get reference to event
1461 const GEvent& event = *((*obs.events())[k]);
1462
1463 // Get source energy and time (no dispersion)
1464 GEnergy srcEng = event.energy();
1465 GTime srcTime = event.time();
1466
1467 // Setup source
1468 GSource source(model.name(), model.spatial(), srcEng, srcTime);
1469
1470 // Get IRF value for event
1471 irfs[k] = this->irf_diffuse(event, source, obs);
1472
1473 // Optionally compute normalisation parameter gradient
1474 if (gradients != NULL) {
1475 (*gradients)(k,0) = (value.is_free()) ? irfs[k] * value.scale() : 0.0;
1476 }
1477
1478 } // endfor: looped over events
1479
1480 // Return IRF value
1481 return irfs;
1482}
1483
1484
1485/***********************************************************************//**
1486 * @brief Return instrument response to composite source sky model
1487 *
1488 * @param[in] model Sky model.
1489 * @param[in] obs Observation.
1490 * @param[in] gradients Gradients matrix.
1491 * @return Instrument response to composite source sky model.
1492 *
1493 * Returns the instrument response to a composite source sky model for all
1494 * events.
1495 ***************************************************************************/
1497 const GObservation& obs,
1498 GMatrix* gradients) const
1499{
1500 // Get number of events
1501 int nevents = obs.events()->size();
1502
1503 // Initialise result
1504 GVector irfs(nevents);
1505
1506 // Get pointer to composite spatial model
1507 const GModelSpatialComposite* composite = dynamic_cast<const GModelSpatialComposite*>(model.spatial());
1508
1509 // Create copy of sky model
1510 GModelSky sky = model;
1511
1512 // Initialise spatial gradient parameter index
1513 int ipar = 0;
1514
1515 // Compute sum of scales
1516 double sum = (composite->normalize()) ? composite->sum_of_scales() : 0.0;
1517
1518 // Loop over model components
1519 for (int i = 0; i < composite->components(); ++i) {
1520
1521 // Get spatial component
1522 const GModelSpatial* spatial = const_cast<GModelSpatial*>(composite->component(i));
1523
1524 // Set spatial component of sky model
1525 sky.spatial(spatial);
1526
1527 // Set model name
1528 sky.name(model.name()+":"+composite->name(i));
1529
1530 // Initialise matrix to hold spatial gradients
1531 GMatrix spat_gradients(nevents, spatial->size());
1532
1533 // Compute IRF values
1534 GVector irf = irf_spatial(sky, obs, &spat_gradients);
1535
1536 // Add IRF values
1537 irfs += irf * composite->scale(i)->value();
1538
1539 // Optionally compute gradients
1540 if (gradients != NULL) {
1541
1542 // Put spatial gradients in full matrix
1543 for (int j = 0; j < spatial->size(); ++j, ++ipar) {
1544 for (int k = 0; k < nevents; ++k) {
1545 (*gradients)(k,ipar) = spat_gradients(k,j);
1546 }
1547 }
1548
1549 // Compute normalised scale
1550 double scale = (composite->normalize() && sum != 0.0) ?
1551 composite->scale(i)->scale() *
1552 (1.0 / sum - composite->scale(i)->value() / (sum*sum)) :
1553 composite->scale(i)->scale();
1554
1555 // Compute scale gradient
1556 for (int k = 0; k < nevents; ++k) {
1557 (*gradients)(k,ipar) = (composite->scale(i)->is_free()) ? irf[k] * scale : 0.0;
1558 }
1559
1560 // Increment parameter index to account for scale gradient
1561 ipar++;
1562
1563 }
1564
1565 } // endfor: looped over all model components
1566
1567 // If composite model is normalised then divide result by number of model
1568 // components
1569 if (composite->normalize() && sum != 0.0) {
1570 irfs /= sum;
1571 }
1572
1573 // Return IRF values
1574 return irfs;
1575}
1576
1577
1578/***********************************************************************//**
1579 * @brief Convolve sky model with the instrument response
1580 *
1581 * @param[in] model Sky model.
1582 * @param[in] event Event.
1583 * @param[in] srcEng Source energy.
1584 * @param[in] srcTime Source time.
1585 * @param[in] obs Observation.
1586 * @param[in] grad Should model gradients be computed? (default: true)
1587 * @return Event probability.
1588 *
1589 * Computes the event probability
1590 *
1591 * \f[
1592 * P(p',E',t'|E,t) = \int S(p,E,t) \times R(p',E',t'|p,E,t) \, dp
1593 * \f]
1594 *
1595 * for a given true energy \f$E\f$ and time \f$t\f$.
1596 ***************************************************************************/
1598 const GEvent& event,
1599 const GEnergy& srcEng,
1600 const GTime& srcTime,
1601 const GObservation& obs,
1602 const bool& grad) const
1603{
1604 // Initialise result
1605 double prob = 0.0;
1606
1607 // Continue only if the model has a spatial component
1608 if (model.spatial() != NULL) {
1609
1610 // Set source
1611 GSource source(model.name(), model.spatial(), srcEng, srcTime);
1612
1613 // Compute IRF value
1614 double irf = irf_spatial(event, source, obs);
1615
1616 // Continue only if IRF value is positive
1617 if (irf > 0.0) {
1618
1619 // Optionally get scaling
1620 double scale = (model.has_scales())
1621 ? model.scale(obs.instrument()).value() : 1.0;
1622
1623 // Evaluate spectral and temporal components
1624 double spec = (model.spectral() != NULL)
1625 ? model.spectral()->eval(srcEng, srcTime, grad) : 1.0;
1626 double temp = (model.temporal() != NULL)
1627 ? model.temporal()->eval(srcTime, grad) : 1.0;
1628
1629 // Compute probability
1630 prob = spec * temp * irf * scale;
1631
1632 // Optionally compute partial derivatives
1633 if (grad) {
1634
1635 // Multiply factors to spectral gradients
1636 if (model.spectral() != NULL) {
1637 double fact = temp * irf * scale;
1638 if (fact != 1.0) {
1639 for (int i = 0; i < model.spectral()->size(); ++i) {
1640 (*model.spectral())[i].factor_gradient((*model.spectral())[i].factor_gradient() * fact);
1641 }
1642 }
1643 }
1644
1645 // Multiply factors to temporal gradients
1646 if (model.temporal() != NULL) {
1647 double fact = spec * irf * scale;
1648 if (fact != 1.0) {
1649 for (int i = 0; i < model.temporal()->size(); ++i) {
1650 (*model.temporal())[i].factor_gradient((*model.temporal())[i].factor_gradient() * fact);
1651 }
1652 }
1653 }
1654
1655 // Optionally compute scale gradient for instrument
1656 if (model.has_scales()) {
1657 for (int i = 0; i < model.scales(); ++i) {
1658 double g_scale = 0.0;
1659 if (model.scale(i).name() == obs.instrument()) {
1660 if (model.scale(i).is_free()) {
1661 g_scale = model.scale(i).scale() * spec * temp * irf;
1662 }
1663 }
1664 model.scale(i).factor_gradient(g_scale);
1665 }
1666 }
1667
1668 } // endif: computed partial derivatives
1669
1670 // Compile option: Check for NaN/Inf
1671 #if defined(G_NAN_CHECK)
1673 std::cout << "*** ERROR: GResponse::eval_prob:";
1674 std::cout << " NaN/Inf encountered";
1675 std::cout << " (prob=" << prob;
1676 std::cout << ", spec=" << spec;
1677 std::cout << ", temp=" << temp;
1678 std::cout << ", irf=" << irf;
1679 std::cout << ")" << std::endl;
1680 }
1681 #endif
1682
1683 } // endif: IRF value was positive
1684
1685 // ... otherwise if gradient computation is requested then set the
1686 // spectral and temporal gradients to zero
1687 else if (grad) {
1688
1689 // Reset spectral gradients
1690 if (model.spectral() != NULL) {
1691 for (int i = 0; i < model.spectral()->size(); ++i) {
1692 (*model.spectral())[i].factor_gradient(0.0);
1693 }
1694 }
1695
1696 // Reset temporal gradients
1697 if (model.temporal() != NULL) {
1698 for (int i = 0; i < model.temporal()->size(); ++i) {
1699 (*model.temporal())[i].factor_gradient(0.0);
1700 }
1701 }
1702
1703 // Optionally reset scale gradients
1704 if (model.has_scales()) {
1705 for (int i = 0; i < model.scales(); ++i) {
1706 model.scale(i).factor_gradient(0.0);
1707 }
1708 }
1709
1710 } // endelse: IRF value was not positive
1711
1712 } // endif: Gamma-ray source model had a spatial component
1713
1714 // Return event probability
1715 return prob;
1716}
1717
1718
1719/***********************************************************************//**
1720 * @brief Convolve sky model with the instrument response
1721 *
1722 * @param[in] model Sky model.
1723 * @param[in] obs Observation.
1724 * @param[out] gradients Pointer to matrix of gradients.
1725 * @return Event probabilities.
1726 *
1727 * Computes the event probability
1728 *
1729 * \f[
1730 * P(p',E',t'|E,t) = \int S(p,E,t) \times R(p',E',t'|p,E,t) \, dp
1731 * \f]
1732 *
1733 * for a all events.
1734 ***************************************************************************/
1736 const GObservation& obs,
1737 GMatrixSparse* gradients) const
1738{
1739 // Get number of model parameters and number of events
1740 int npars = model.size();
1741 int nevents = obs.events()->size();
1742
1743 // Initialise gradients flag
1744 bool grad = (gradients != NULL);
1745
1746 // Initialise result
1747 GVector probs(nevents);
1748
1749 // Continue only if the model has a spatial component
1750 if (model.spatial() != NULL) {
1751
1752 // Initialise matrix to hold spatial gradients
1753 GMatrix spat_gradients(nevents, model.spatial()->size());
1754
1755 // Compute IRF value
1756 probs = irf_spatial(model, obs, &spat_gradients);
1757
1758 // Get global model scaling
1759 double scale = (model.has_scales())
1760 ? model.scale(obs.instrument()).value() : 1.0;
1761
1762 // Initialise temporary vectors to hold gradients
1763 GVector* tmp_gradients = NULL;
1764 if (grad) {
1765 tmp_gradients = new GVector[npars];
1766 for (int i = 0; i < npars; ++i) {
1767 tmp_gradients[i] = GVector(nevents);
1768 }
1769 }
1770
1771 // Loop over events
1772 for (int k = 0; k < nevents; ++k) {
1773
1774 // Get probability
1775 double spat = probs[k];
1776
1777 // Continue only if spatial value is positive
1778 if (spat > 0.0) {
1779
1780 // Get reference to event
1781 const GEvent& event = *((*obs.events())[k]);
1782
1783 // Get source energy and time
1784 GEnergy srcEng = event.energy();
1785 GTime srcTime = event.time();
1786
1787 // Evaluate spectral and temporal components
1788 double spec = (model.spectral() != NULL)
1789 ? model.spectral()->eval(srcEng, srcTime, grad)
1790 : 1.0;
1791 double temp = (model.temporal() != NULL)
1792 ? model.temporal()->eval(srcTime, grad)
1793 : 1.0;
1794
1795 // Compute probability
1796 probs[k] = spat * spec * temp * scale;
1797
1798 // Optionally compute partial derivatives
1799 if (grad) {
1800
1801 // Get number of spatial, spectral and temporal parameters
1802 int n_spat = model.spatial()->size();
1803 int n_spec = model.spectral()->size();
1804 int n_temp = model.temporal()->size();
1805
1806 // Multiply factors to spatial gradients
1807 if (model.spatial() != NULL) {
1808 double fact = spec * temp * scale;
1809 for (int i = 0; i < n_spat; ++i) {
1810 tmp_gradients[i][k] = spat_gradients(k,i) * fact;
1811 }
1812 }
1813
1814 // Multiply factors to spectral gradients
1815 if (model.spectral() != NULL) {
1816 int offset = n_spat;
1817 double fact = spat * temp * scale;
1818 for (int i = 0; i < n_spec; ++i) {
1819 tmp_gradients[offset+i][k] =
1820 (*(model.spectral()))[i].factor_gradient() * fact;
1821 }
1822 }
1823
1824 // Multiply factors to temporal gradients
1825 if (model.temporal() != NULL) {
1826 int offset = n_spat + n_spec;
1827 double fact = spat * spec * scale;
1828 for (int i = 0; i < n_temp; ++i) {
1829 tmp_gradients[offset+i][k] =
1830 (*(model.temporal()))[i].factor_gradient() * fact;
1831 }
1832 }
1833
1834 // Optionally set scale gradient for instrument
1835 if (model.has_scales()) {
1836 int offset = n_spat + n_spec + n_temp;
1837 double fact = spat * spec * temp;
1838 for (int i = 0; i < model.scales(); ++i) {
1839 const GModelPar& par = model.scale(i);
1840 double g_scale = 0.0;
1841 if (par.name() == obs.instrument()) {
1842 if (par.is_free()) {
1843 tmp_gradients[offset+i][k] = par.scale() * fact;
1844 }
1845 }
1846 }
1847 }
1848
1849 } // endif: computed partial derivatives
1850
1851 } // endif: IRF spatial value was positive
1852
1853 // Compile option: Check for NaN/Inf
1854 #if defined(G_NAN_CHECK)
1855 if (gammalib::is_notanumber(probs[k]) || gammalib::is_infinite(probs[k])) {
1856 std::cout << "*** ERROR: GResponse::eval_prob:";
1857 std::cout << " NaN/Inf encountered";
1858 std::cout << " (probs[" << k << "]=" << probs[k];
1859 std::cout << ", spat=" << spat;
1860 std::cout << ")" << std::endl;
1861 }
1862 #endif
1863
1864 } // endfor: looped over events
1865
1866 // Post-process gradients
1867 if (grad) {
1868
1869 // Fill gradients into matrix
1870 for (int i = 0; i < npars; ++i) {
1871 gradients->column(i, tmp_gradients[i]);
1872 }
1873
1874 // Delete temporal gradients
1875 delete [] tmp_gradients;
1876
1877 } // endif: gradient post processing
1878
1879 } // endif: Gamma-ray source model had a spatial component
1880
1881 // Return event probabilities
1882 return probs;
1883}
1884
1885
1886/***********************************************************************//**
1887 * @brief Return size of vector for energy dispersion computation
1888 *
1889 * @param[in] model Sky model.
1890 * @param[in] obs Observation.
1891 * @param[out] grad Signals whether gradients should be computed.
1892 * @return Size of vector for energy dispersion computation.
1893 *
1894 * Computes the size of the vector that will be computed for the computation
1895 * of the energy dispersion.
1896 ***************************************************************************/
1898 const GObservation& obs,
1899 const bool& grad) const
1900{
1901 // Initialise vector size
1902 int size = 1;
1903
1904 // Determine size of gradient part
1905 if (grad) {
1906 if (model.spectral() != NULL) {
1907 for (int i = 0; i < model.spectral()->size(); ++i) {
1908 GModelPar& par = (*(model.spectral()))[i];
1909 if (par.is_free() && par.has_grad()) {
1910 size++;
1911 }
1912 }
1913 }
1914 if (model.temporal() != NULL) {
1915 for (int i = 0; i < model.temporal()->size(); ++i) {
1916 GModelPar& par = (*(model.temporal()))[i];
1917 if (par.is_free() && par.has_grad()) {
1918 size++;
1919 }
1920 }
1921 }
1922 if (model.has_scales()) {
1923 for (int i = 0; i < model.scales(); ++i) {
1924 GModelPar& par = const_cast<GModelPar&>(model.scale(i));
1925 if (par.name() == obs.instrument()) {
1926 if (par.is_free() && par.has_grad()) {
1927 size++;
1928 }
1929 }
1930 }
1931 }
1932 }
1933
1934 // Return size
1935 return size;
1936}
1937
1938
1939/***********************************************************************//**
1940 * @brief Constructor for energy dispersion integration kernels class
1941 *
1942 * @param[in] parent Pointer to response.
1943 * @param[in] obs Pointer to observation.
1944 * @param[in] model Pointer to sky model.
1945 * @param[in] event Pointer to event.
1946 * @param[in] srcTime True time.
1947 * @param[in] grad Evaluate gradients?
1948 *
1949 * This method constructs the integration kernel needed for the energy
1950 * dispersion computation.
1951 ***************************************************************************/
1953 const GObservation* obs,
1954 const GModelSky* model,
1955 const GEvent* event,
1956 const GTime& srcTime,
1957 const bool& grad)
1958{
1959 // Set members
1960 m_parent = parent;
1961 m_obs = obs;
1962 m_model = model;
1963 m_event = event;
1964 m_size = 1;
1965 m_srcTime = srcTime;
1966 m_grad = grad;
1967
1968 // If gradients are requested then put pointers to all relevant parameter
1969 // in the parameter pointer vector
1970 m_pars.clear();
1971 if (m_grad) {
1972
1973 // Add free spectral parameters with analytical gradients
1974 if (m_model->spectral() != NULL) {
1975 for (int i = 0; i < m_model->spectral()->size(); ++i) {
1976 GModelPar& par = (*(m_model->spectral()))[i];
1977 if (par.is_free() && par.has_grad()) {
1978 m_pars.push_back(&par);
1979 }
1980 }
1981 }
1982
1983 // Add free temporal parameters with analytical gradients
1984 if (m_model->temporal() != NULL) {
1985 for (int i = 0; i < m_model->temporal()->size(); ++i) {
1986 GModelPar& par = (*(m_model->temporal()))[i];
1987 if (par.is_free() && par.has_grad()) {
1988 m_pars.push_back(&par);
1989 }
1990 }
1991 }
1992
1993 // Add free scaling factors
1994 if (m_model->has_scales()) {
1995 for (int i = 0; i < m_model->scales(); ++i) {
1996 GModelPar& par = const_cast<GModelPar&>(m_model->scale(i));
1997 if (par.name() == m_obs->instrument()) {
1998 if (par.is_free() && par.has_grad()) {
1999 m_pars.push_back(&par);
2000 }
2001 }
2002 }
2003 }
2004
2005 // Update size
2006 m_size += m_pars.size();
2007
2008 } // endif: gradients were requested
2009
2010 // Return
2011 return;
2012}
2013
2014
2015/***********************************************************************//**
2016 * @brief Return true energy intervals for sky model
2017 *
2018 * @param[in] model Sky model.
2019 * @return True energy intervals.
2020 *
2021 * Returns the true energy intervals for a sky model. For all spectral
2022 * models other than the GModelSpectralGauss model the method will return
2023 * an empty energy boundaries structure. For the GModelSpectralGauss model
2024 * the method will return the interval [mean - 5 * sigma, mean + 5 * sigma].
2025 ***************************************************************************/
2027{
2028 // Initialise empty energy boundaries
2030
2031 // If the spectral model is a Gaussian line then return
2032 const GModelSpectralGauss* gauss =
2033 dynamic_cast<const GModelSpectralGauss*>(model.spectral());
2034 if (gauss != NULL) {
2035
2036 // Define width of energy interval
2037 const double width = 5.0;
2038
2039 // Compute energy boundaries
2040 GEnergy mean = gauss->mean();
2041 GEnergy sigma = gauss->sigma();
2042 GEnergy ebound_min = mean - width * sigma;
2043 GEnergy ebound_max = mean + width * sigma;
2044
2045 // Make sure that energies are not negative positive
2046 if (ebound_min.MeV() < 0.0) {
2047 ebound_min.MeV(0.0);
2048 }
2049 if (ebound_max.MeV() < 0.0) {
2050 ebound_max.MeV(0.0);
2051 }
2052
2053 // If energy boundaries define a positive interval then append them
2054 if (ebound_max > ebound_min) {
2055 ebounds.append(ebound_min, ebound_max);
2056 }
2057
2058 } // endif: spectral model was a Gaussian
2059
2060 // Return energy boudnaries
2061 return ebounds;
2062}
2063
2064
2065/***********************************************************************//**
2066 * @brief Evaluate energy dispersion integration kernel
2067 *
2068 * @param[in] etrue True photon energy in MeV.
2069 *
2070 * This method implements the integration kernel needed for the
2071 * GResponse::edisp_kern() class.
2072 ***************************************************************************/
2074{
2075 // Initialise result
2076 GVector kernels(m_size);
2077
2078 // Initialise Ndarray array index
2079 int index = 0;
2080
2081 // Set true energy
2082 GEnergy srcEng;
2083 srcEng.MeV(etrue);
2084
2085 // Get function value
2086 kernels[index++] = m_parent->eval_prob(*m_model, *m_event,
2087 srcEng, m_srcTime, *m_obs,
2088 m_grad);
2089
2090 // If gradients are requested then extract them and put them into the
2091 // array
2092 if (m_grad) {
2093 for (int i = 0; i < m_pars.size(); ++i, ++index) {
2094 kernels[index] = m_pars[i]->factor_gradient();
2095 }
2096 }
2097
2098 // Compile option: Check for NaN
2099 #if defined(G_NAN_CHECK)
2100 if (gammalib::is_notanumber(kernels[0]) || gammalib::is_infinite(kernels[0])) {
2101 std::cout << "*** ERROR: GResponse::edisp_kern::eval";
2102 std::cout << "(etrue=" << etrue << "): NaN/Inf encountered";
2103 std::cout << " (value=" << kernels[0] << ")" << std::endl;
2104 }
2105 #endif
2106
2107 // Return kernels
2108 return kernels;
2109}
2110
2111
2112/***********************************************************************//**
2113 * @brief Zenith angle integration kernel for radial model
2114 *
2115 * @param[in] theta Zenith angle (radians).
2116 * @return IRF value.
2117 *
2118 * Integrated the IRF multiplied by the model value for a given zenith angle
2119 * over all azimuth angles.
2120 ***************************************************************************/
2122{
2123 // Initialise result
2124 double irf = 0.0;
2125
2126 // Continue only for positive zenith angles (otherwise the integral will
2127 // be zero)
2128 if (theta > 0.0) {
2129
2130 // Evaluate sky model
2131 double model = m_model->eval(theta, *m_srcEng, *m_srcTime);
2132
2133 // Continue only if model is positive
2134 if (model > 0.0) {
2135
2136 // Set azimuthal integration range
2137 double phi_min = 0.0;
2138 double phi_max = gammalib::twopi;
2139
2140 // Setup integration kernel
2141 irf_radial_kern_phi integrand(m_rsp,
2142 m_event,
2143 m_obs,
2144 m_rot,
2145 theta,
2146 m_srcEng,
2147 m_srcTime);
2148
2149 // Setup integration
2150 GIntegral integral(&integrand);
2151 integral.fixed_iter(m_iter_phi);
2152
2153 // Integrate over phi
2154 irf = integral.romberg(phi_min, phi_max, m_iter_phi) *
2155 model * std::sin(theta);
2156
2157 } // endif: model was positive
2158
2159 } // endif: theta was positive
2160
2161 // Return result
2162 return irf;
2163}
2164
2165
2166/***********************************************************************//**
2167 * @brief Azimuth angle integration kernel for radial model
2168 *
2169 * @param[in] phi Azimuth angle (radians).
2170 * @return IRF value.
2171 *
2172 * Computes the IRF at a given zenith and azimuth angle with respect to a
2173 * specified centre.
2174 ***************************************************************************/
2176{
2177 // Set up native coordinate vector
2178 double cos_phi = std::cos(phi);
2179 double sin_phi = std::sin(phi);
2180 GVector native(-cos_phi*m_sin_theta, sin_phi*m_sin_theta, m_cos_theta);
2181
2182 // Rotate vector into celestial coordinates
2183 GVector vector = (*m_rot) * native;
2184
2185 // Convert vector into sky direction
2186 GSkyDir dir(vector);
2187
2188 // Setup photon
2189 GPhoton photon(dir, *m_srcEng, *m_srcTime);
2190
2191 // Evaluate IRF for photon
2192 double irf = m_rsp->irf(*m_event, photon, *m_obs);
2193
2194 // Return
2195 return irf;
2196}
2197
2198
2199/***********************************************************************//**
2200 * @brief Zenith angle integration kernel for elliptical model
2201 *
2202 * @param[in] theta Zenith angle (radians).
2203 * @return IRF value.
2204 *
2205 * Integrated the IRF multiplied by the model value for a given zenith angle
2206 * over all azimuth angles.
2207 ***************************************************************************/
2209{
2210 // Initialise result
2211 double irf = 0.0;
2212
2213 // Continue only for positive zenith angles (otherwise the integral will
2214 // be zero)
2215 if (theta > 0.0) {
2216
2217 // Set azimuthal integration range
2218 double phi_min = 0.0;
2219 double phi_max = gammalib::twopi;
2220
2221 // Setup integration kernel
2222 irf_elliptical_kern_phi integrand(m_rsp,
2223 m_event,
2224 m_obs,
2225 m_model,
2226 m_rot,
2227 theta,
2228 m_srcEng,
2229 m_srcTime);
2230
2231 // Setup integration
2232 GIntegral integral(&integrand);
2233 integral.fixed_iter(m_iter_phi);
2234
2235 // Integrate over phi
2236 irf = integral.romberg(phi_min, phi_max, m_iter_phi) * std::sin(theta);
2237
2238 } // endif: theta was positive
2239
2240 // Return result
2241 return irf;
2242}
2243
2244
2245/***********************************************************************//**
2246 * @brief Azimuth angle integration kernel for elliptical model
2247 *
2248 * @param[in] phi Azimuth angle (radians).
2249 * @return IRF value.
2250 *
2251 * Computes the IRF at a given zenith and azimuth angle with respect to a
2252 * specified centre.
2253 ***************************************************************************/
2255{
2256 // Initialise result
2257 double irf = 0.0;
2258
2259 // Evaluate sky model
2260 double model = m_model->eval(m_theta, phi, *m_srcEng, *m_srcTime);
2261
2262 // Continue only if model is positive
2263 if (model > 0.0) {
2264
2265 // Set up native coordinate vector
2266 double cos_phi = std::cos(phi);
2267 double sin_phi = std::sin(phi);
2268 GVector native(-cos_phi*m_sin_theta, sin_phi*m_sin_theta, m_cos_theta);
2269
2270 // Rotate vector into celestial coordinates
2271 GVector vector = (*m_rot) * native;
2272
2273 // Convert vector into sky direction
2274 GSkyDir dir(vector);
2275
2276 // Setup photon
2277 GPhoton photon(dir, *m_srcEng, *m_srcTime);
2278
2279 // Evaluate IRF for photon
2280 irf = m_rsp->irf(*m_event, photon, *m_obs) * model;
2281
2282 } // endif: model was positive
2283
2284 // Return
2285 return irf;
2286}
Energy boundaries class interface definition.
Energy value class definition.
Abstract event base class definition.
Exception handler interface definition.
Integration class interface definition.
Integration class for set of functions interface definition.
Mathematical function definitions.
Sparse matrix class definition.
Generic matrix class definition.
Sky model class interface definition.
Spatial composite model class interface definition.
Spatial map cube model class interface definition.
Spatial map model class interface definition.
Abstract diffuse spatial model base class interface definition.
Abstract elliptical spatial model base class interface definition.
Point source spatial model class interface definition.
Abstract radial spatial model base class interface definition.
Gaussian spectral model class interface definition.
Abstract observation base class interface definition.
Photon class definition.
#define G_CONVOLVE
Definition GResponse.cpp:61
Abstract response base class definition.
Sky map class definition.
Source class definition.
Time class interface definition.
Gammalib tools definition.
@ GMODEL_SPATIAL_ELLIPTICAL
Definition GTypemaps.hpp:45
@ GMODEL_SPATIAL_RADIAL
Definition GTypemaps.hpp:44
@ GMODEL_SPATIAL_COMPOSITE
Definition GTypemaps.hpp:47
@ GMODEL_SPATIAL_DIFFUSE
Definition GTypemaps.hpp:46
@ GMODEL_SPATIAL_POINT_SOURCE
Definition GTypemaps.hpp:43
double sum(const GVector &vector)
Computes vector sum.
Definition GVector.cpp:1012
Vector class interface definition.
Energy boundaries container class.
Definition GEbounds.hpp:60
const GEnergy & emax(void) const
Return maximum energy of all intervals.
Definition GEbounds.hpp:199
void append(const GEnergy &emin, const GEnergy &emax)
Append energy interval.
Definition GEbounds.cpp:301
int size(void) const
Return number of energy boundaries.
Definition GEbounds.hpp:163
const GEnergy & emin(void) const
Return minimum energy of all intervals.
Definition GEbounds.hpp:187
Class that handles energies in a unit independent way.
Definition GEnergy.hpp:48
double MeV(void) const
Return energy in MeV.
Definition GEnergy.cpp:342
Abstract interface for the event classes.
Definition GEvent.hpp:71
virtual const GEnergy & energy(void) const =0
virtual int size(void) const =0
Abstract instrument direction base class.
Definition GInstDir.hpp:51
GIntegral class interface definition.
Definition GIntegral.hpp:46
double romberg(std::vector< double > bounds, const int &order=5)
Perform Romberg integration.
void fixed_iter(const int &iter)
Set fixed number of iterations.
Integration class for set of functions.
GVector romberg(std::vector< double > bounds, const int &order=5)
Perform Romberg integration.
void fixed_iter(const int &iter)
Set fixed number of iterations.
const int & rows(void) const
Return number of matrix rows.
const int & columns(void) const
Return number of matrix columns.
Sparse matrix class interface definition.
virtual GVector column(const int &column) const
Extract column as vector from matrix.
Generic matrix class definition.
Definition GMatrix.hpp:79
void eulerz(const double &angle)
Set Euler rotation matrix around z axis.
Definition GMatrix.cpp:1194
void eulery(const double &angle)
Set Euler rotation matrix around y axis.
Definition GMatrix.cpp:1157
Model parameter class.
Definition GModelPar.hpp:87
Sky model class.
GModelSpectral * spectral(void) const
Return spectral model component.
GModelTemporal * temporal(void) const
Return temporal model component.
GModelSpatial * spatial(void) const
Return spatial model component.
Spatial composite model.
const GModelSpatial * component(const int &index) const
Returns pointer to spatial component element.
const GModelPar * scale(const int &index) const
Returns scale parameter of spatial component.
int components(void) const
Return number of model components.
double sum_of_scales(void) const
Returns sum of all model scales.
const bool & normalize(void) const
Return normalisation flag.
std::string name(const int &index) const
Returns names of spatial component.
const GSkyMap & cube(void) const
Get map cube.
virtual double eval(const GPhoton &photon, const bool &gradients=false) const
Evaluate function.
const GSkyMap & map(void) const
Get map.
Abstract diffuse spatial model base class.
virtual double eval(const GPhoton &photon, const bool &gradients=false) const =0
Abstract elliptical spatial model base class.
const GSkyDir & dir(void) const
Return position of elliptical spatial model.
virtual double theta_max(void) const =0
Point source spatial model.
const GSkyDir & dir(void) const
Return position of point source.
Abstract radial spatial model base class.
virtual double theta_max(void) const =0
const GSkyDir & dir(void) const
Return position of radial spatial model.
Abstract spatial model base class.
bool has_free_pars(void) const
Checks if the spatial model has free parameters.
virtual GClassCode code(void) const =0
int size(void) const
Return number of parameters.
Gaussian spectral model class.
GEnergy mean(void) const
Return mean energy.
GEnergy sigma(void) const
Return energy width.
virtual double eval(const GEnergy &srcEng, const GTime &srcTime=GTime(), const bool &gradients=false) const =0
int size(void) const
Return number of parameters.
virtual double eval(const GTime &srcTime, const bool &gradients=false) const =0
int size(void) const
Return number of parameters.
GModelPar & scale(const int &index)
Returns reference to scale parameter by index.
Definition GModel.cpp:384
bool has_scales(void) const
Signals that model has scales.
Definition GModel.hpp:359
int size(void) const
Return number of parameters in model.
Definition GModel.hpp:237
const std::string & name(void) const
Return parameter name.
Definition GModel.hpp:265
int scales(void) const
Return number of scale parameters in model.
Definition GModel.hpp:251
Abstract observation base class.
virtual GEvents * events(void)
Return events.
virtual std::string instrument(void) const =0
void id(const std::string &id)
Set observation identifier.
bool is_free(void) const
Signal if parameter is free.
const double & scale(void) const
Return parameter scale.
bool has_grad(void) const
Signal if parameter gradient is computed analytically.
const double & factor_gradient(void) const
Return parameter factor gradient.
double value(void) const
Return parameter value.
const std::string & name(void) const
Return parameter name.
Class that handles photons.
Definition GPhoton.hpp:47
bool contains(const std::string &name, const GEnergy &ereco, const GEnergy &etrue, double *value=NULL) const
Check if cache contains a value for specific parameters.
void remove(const std::string &name)
Remove cache for source.
void set(const std::string &name, const GEnergy &ereco, const GEnergy &etrue, const double &value)
Set cache value.
void clear(void)
Clear response cache.
bool contains(const std::string &cache_id, GVector *irfs=NULL) const
Check if cache contains a value for specific parameters.
void remove(const std::string &cache_id)
Remove cache.
void clear(void)
Clear response vector cache.
void set(const std::string &cache_id, const GVector &vector)
Set cache value.
const GEvent * m_event
Event.
const GResponse * m_parent
Response.
std::vector< GModelPar * > m_pars
Parameter pointers.
int size(void) const
GVector eval(const double &etrue)
Evaluate energy dispersion integration kernel.
bool m_grad
Gradient flag.
int m_size
Array of values and gradients.
const GModelSky * m_model
Sky model.
edisp_kerns(const GResponse *parent, const GObservation *obs, const GModelSky *model, const GEvent *event, const GTime &srcTime, const bool &grad)
Constructor for energy dispersion integration kernels class.
GTime m_srcTime
True arrival time.
const GObservation * m_obs
Observation.
double eval(const double &phi)
Azimuth angle integration kernel for elliptical model.
double eval(const double &phi)
Zenith angle integration kernel for elliptical model.
double eval(const double &phi)
Azimuth angle integration kernel for radial model.
double eval(const double &phi)
Zenith angle integration kernel for radial model.
Abstract instrument response base class.
Definition GResponse.hpp:77
GResponseCache m_irf_cache
GVector eval_probs(const GModelSky &model, const GObservation &obs, GMatrixSparse *gradients=NULL) const
Convolve sky model with the instrument response.
virtual double irf_spatial(const GEvent &event, const GSource &source, const GObservation &obs) const
Return instrument response integrated over the spatial model.
int m_irf_elliptical_iter_theta
Elliptical model integration theta iterations.
int m_irf_radial_iter_phi
Radial model integration phi iterations.
void copy_members(const GResponse &rsp)
Copy class members.
virtual ~GResponse(void)
Destructor.
virtual double irf_ptsrc(const GEvent &event, const GSource &source, const GObservation &obs) const
Return instrument response to point source.
GResponseCache m_nroi_cache
double eval_prob(const GModelSky &model, const GEvent &event, const GEnergy &srcEng, const GTime &srcTime, const GObservation &obs, const bool &grad) const
Convolve sky model with the instrument response.
virtual double irf(const GEvent &event, const GPhoton &photon, const GObservation &obs) const =0
GResponse(void)
Void constructor.
Definition GResponse.cpp:86
virtual double convolve(const GModelSky &model, const GEvent &event, const GObservation &obs, const bool &grad=true) const
Convolve sky model with the instrument response.
virtual double irf_radial(const GEvent &event, const GSource &source, const GObservation &obs) const
Return instrument response to radial source.
GEbounds ebounds_model(const GModelSky &model) const
Return true energy intervals for sky model.
int size_edisp_vector(const GModelSky &model, const GObservation &obs, const bool &grad) const
Return size of vector for energy dispersion computation.
virtual void remove_response_cache(const std::string &cache_id)
Remove response cache for specific observation and model.
int m_irf_elliptical_iter_phi
Elliptical model integration phi iterations.
bool m_use_nroi_cache
Control usage of nroi cache.
virtual GEbounds ebounds(const GEnergy &obsEng) const =0
virtual double irf_elliptical(const GEvent &event, const GSource &source, const GObservation &obs) const
Return instrument response to elliptical source.
virtual double irf_diffuse(const GEvent &event, const GSource &source, const GObservation &obs) const
Return instrument response to diffuse source.
virtual bool use_edisp(void) const =0
GResponseVectorCache m_irf_vector_cache
void init_members(void)
Initialise class members.
void free_members(void)
Delete class members.
bool m_use_irf_cache
Control usage of irf cache.
virtual double irf_composite(const GEvent &event, const GSource &source, const GObservation &obs) const
Return instrument response to composite source.
virtual GResponse & operator=(const GResponse &rsp)
Assignment operator.
double m_irf_diffuse_resolution
Angular resolution for diffuse model.
int m_irf_radial_iter_theta
Radial model integration theta iterations.
Sky direction class.
Definition GSkyDir.hpp:62
double dec_deg(void) const
Returns Declination in degrees.
Definition GSkyDir.hpp:258
double ra_deg(void) const
Returns Right Ascension in degrees.
Definition GSkyDir.hpp:231
Sky map class.
Definition GSkyMap.hpp:89
double solidangle(const int &index) const
Returns solid angle of pixel.
Definition GSkyMap.cpp:1858
GSkyDir inx2dir(const int &index) const
Returns sky direction of pixel.
Definition GSkyMap.cpp:1331
const int & npix(void) const
Returns number of pixels.
Definition GSkyMap.hpp:383
Class that handles gamma-ray sources.
Definition GSource.hpp:53
const GEnergy & energy(void) const
Return photon energy.
Definition GSource.hpp:142
const std::string & name(void) const
Return model name.
Definition GSource.hpp:114
const GTime & time(void) const
Return photon arrival time.
Definition GSource.hpp:156
const GModelSpatial * model(void) const
Return spatial model component.
Definition GSource.hpp:128
Time class.
Definition GTime.hpp:55
Vector class.
Definition GVector.hpp:46
bool is_infinite(const double &x)
Signal if argument is infinite.
Definition GTools.hpp:186
bool is_notanumber(const double &x)
Signal if argument is not a number.
Definition GTools.hpp:203
std::string str(const unsigned short int &value)
Convert unsigned short integer value into string.
Definition GTools.cpp:508
const double twopi
Definition GMath.hpp:36