GammaLib 2.1.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-2024 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 // Loop over events
1455 for (int k = 0; k < nevents; ++k) {
1456
1457 // Get reference to event
1458 const GEvent& event = *((*obs.events())[k]);
1459
1460 // Get source energy and time (no dispersion)
1461 GEnergy srcEng = event.energy();
1462 GTime srcTime = event.time();
1463
1464 // Setup source
1465 GSource source(model.name(), model.spatial(), srcEng, srcTime);
1466
1467 // Get IRF value for event
1468 irfs[k] = this->irf_diffuse(event, source, obs);
1469
1470 } // endfor: looped over events
1471
1472 // Return IRF value
1473 return irfs;
1474}
1475
1476
1477/***********************************************************************//**
1478 * @brief Return instrument response to composite source sky model
1479 *
1480 * @param[in] model Sky model.
1481 * @param[in] obs Observation.
1482 * @param[in] gradients Gradients matrix.
1483 * @return Instrument response to composite source sky model.
1484 *
1485 * Returns the instrument response to a composite source sky model for all
1486 * events.
1487 ***************************************************************************/
1489 const GObservation& obs,
1490 GMatrix* gradients) const
1491{
1492 // Get number of events
1493 int nevents = obs.events()->size();
1494
1495 // Initialise result
1496 GVector irfs(nevents);
1497
1498 // Get pointer to composite spatial model
1499 const GModelSpatialComposite* composite = dynamic_cast<const GModelSpatialComposite*>(model.spatial());
1500
1501 // Create copy of sky model
1502 GModelSky sky = model;
1503
1504 // Initialise spatial gradient parameter index
1505 int ipar = 0;
1506
1507 // Compute sum of scales
1508 double sum = (composite->normalize()) ? composite->sum_of_scales() : 0.0;
1509
1510 // Loop over model components
1511 for (int i = 0; i < composite->components(); ++i) {
1512
1513 // Get spatial component
1514 const GModelSpatial* spatial = const_cast<GModelSpatial*>(composite->component(i));
1515
1516 // Set spatial component of sky model
1517 sky.spatial(spatial);
1518
1519 // Set model name
1520 sky.name(model.name()+":"+composite->name(i));
1521
1522 // Initialise matrix to hold spatial gradients
1523 GMatrix spat_gradients(nevents, spatial->size());
1524
1525 // Compute IRF values
1526 GVector irf = irf_spatial(sky, obs, &spat_gradients);
1527
1528 // Add IRF values
1529 irfs += irf * composite->scale(i)->value();
1530
1531 // Put spatial gradients in full matrix
1532 for (int j = 0; j < spatial->size(); ++j, ++ipar) {
1533 for (int k = 0; k < nevents; ++k) {
1534 (*gradients)(k,ipar) = spat_gradients(k,j);
1535 }
1536 }
1537
1538 // Optionally compute scale gradient
1539 if (gradients != NULL) {
1540
1541 // Compute normalised scale
1542 double scale = (composite->normalize() && sum != 0.0) ?
1543 composite->scale(i)->scale() *
1544 (1.0 / sum - composite->scale(i)->value() / (sum*sum)) :
1545 composite->scale(i)->scale();
1546
1547 // Compute gradient
1548 for (int k = 0; k < nevents; ++k) {
1549 double g = (composite->scale(i)->is_free()) ? irf[k] * scale : 0.0;
1550 (*gradients)(k,ipar) = g;
1551 }
1552 }
1553
1554 // Increment parameter index
1555 ipar++;
1556
1557 } // endfor: looped over all model components
1558
1559 // If composite model is normalised then divide result by number of model
1560 // components
1561 if (composite->normalize() && sum != 0.0) {
1562 irfs /= sum;
1563 }
1564
1565 // Return IRF values
1566 return irfs;
1567}
1568
1569
1570/***********************************************************************//**
1571 * @brief Convolve sky model with the instrument response
1572 *
1573 * @param[in] model Sky model.
1574 * @param[in] event Event.
1575 * @param[in] srcEng Source energy.
1576 * @param[in] srcTime Source time.
1577 * @param[in] obs Observation.
1578 * @param[in] grad Should model gradients be computed? (default: true)
1579 * @return Event probability.
1580 *
1581 * Computes the event probability
1582 *
1583 * \f[
1584 * P(p',E',t'|E,t) = \int S(p,E,t) \times R(p',E',t'|p,E,t) \, dp
1585 * \f]
1586 *
1587 * for a given true energy \f$E\f$ and time \f$t\f$.
1588 ***************************************************************************/
1590 const GEvent& event,
1591 const GEnergy& srcEng,
1592 const GTime& srcTime,
1593 const GObservation& obs,
1594 const bool& grad) const
1595{
1596 // Initialise result
1597 double prob = 0.0;
1598
1599 // Continue only if the model has a spatial component
1600 if (model.spatial() != NULL) {
1601
1602 // Set source
1603 GSource source(model.name(), model.spatial(), srcEng, srcTime);
1604
1605 // Compute IRF value
1606 double irf = irf_spatial(event, source, obs);
1607
1608 // Continue only if IRF value is positive
1609 if (irf > 0.0) {
1610
1611 // Optionally get scaling
1612 double scale = (model.has_scales())
1613 ? model.scale(obs.instrument()).value() : 1.0;
1614
1615 // Evaluate spectral and temporal components
1616 double spec = (model.spectral() != NULL)
1617 ? model.spectral()->eval(srcEng, srcTime, grad) : 1.0;
1618 double temp = (model.temporal() != NULL)
1619 ? model.temporal()->eval(srcTime, grad) : 1.0;
1620
1621 // Compute probability
1622 prob = spec * temp * irf * scale;
1623
1624 // Optionally compute partial derivatives
1625 if (grad) {
1626
1627 // Multiply factors to spectral gradients
1628 if (model.spectral() != NULL) {
1629 double fact = temp * irf * scale;
1630 if (fact != 1.0) {
1631 for (int i = 0; i < model.spectral()->size(); ++i) {
1632 (*model.spectral())[i].factor_gradient((*model.spectral())[i].factor_gradient() * fact);
1633 }
1634 }
1635 }
1636
1637 // Multiply factors to temporal gradients
1638 if (model.temporal() != NULL) {
1639 double fact = spec * irf * scale;
1640 if (fact != 1.0) {
1641 for (int i = 0; i < model.temporal()->size(); ++i) {
1642 (*model.temporal())[i].factor_gradient((*model.temporal())[i].factor_gradient() * fact);
1643 }
1644 }
1645 }
1646
1647 // Optionally compute scale gradient for instrument
1648 if (model.has_scales()) {
1649 for (int i = 0; i < model.scales(); ++i) {
1650 double g_scale = 0.0;
1651 if (model.scale(i).name() == obs.instrument()) {
1652 if (model.scale(i).is_free()) {
1653 g_scale = model.scale(i).scale() * spec * temp * irf;
1654 }
1655 }
1656 model.scale(i).factor_gradient(g_scale);
1657 }
1658 }
1659
1660 } // endif: computed partial derivatives
1661
1662 // Compile option: Check for NaN/Inf
1663 #if defined(G_NAN_CHECK)
1665 std::cout << "*** ERROR: GResponse::eval_prob:";
1666 std::cout << " NaN/Inf encountered";
1667 std::cout << " (prob=" << prob;
1668 std::cout << ", spec=" << spec;
1669 std::cout << ", temp=" << temp;
1670 std::cout << ", irf=" << irf;
1671 std::cout << ")" << std::endl;
1672 }
1673 #endif
1674
1675 } // endif: IRF value was positive
1676
1677 // ... otherwise if gradient computation is requested then set the
1678 // spectral and temporal gradients to zero
1679 else if (grad) {
1680
1681 // Reset spectral gradients
1682 if (model.spectral() != NULL) {
1683 for (int i = 0; i < model.spectral()->size(); ++i) {
1684 (*model.spectral())[i].factor_gradient(0.0);
1685 }
1686 }
1687
1688 // Reset temporal gradients
1689 if (model.temporal() != NULL) {
1690 for (int i = 0; i < model.temporal()->size(); ++i) {
1691 (*model.temporal())[i].factor_gradient(0.0);
1692 }
1693 }
1694
1695 // Optionally reset scale gradients
1696 if (model.has_scales()) {
1697 for (int i = 0; i < model.scales(); ++i) {
1698 model.scale(i).factor_gradient(0.0);
1699 }
1700 }
1701
1702 } // endelse: IRF value was not positive
1703
1704 } // endif: Gamma-ray source model had a spatial component
1705
1706 // Return event probability
1707 return prob;
1708}
1709
1710
1711/***********************************************************************//**
1712 * @brief Convolve sky model with the instrument response
1713 *
1714 * @param[in] model Sky model.
1715 * @param[in] obs Observation.
1716 * @param[out] gradients Pointer to matrix of gradients.
1717 * @return Event probabilities.
1718 *
1719 * Computes the event probability
1720 *
1721 * \f[
1722 * P(p',E',t'|E,t) = \int S(p,E,t) \times R(p',E',t'|p,E,t) \, dp
1723 * \f]
1724 *
1725 * for a all events.
1726 ***************************************************************************/
1728 const GObservation& obs,
1729 GMatrixSparse* gradients) const
1730{
1731 // Get number of model parameters and number of events
1732 int npars = model.size();
1733 int nevents = obs.events()->size();
1734
1735 // Initialise gradients flag
1736 bool grad = (gradients != NULL);
1737
1738 // Initialise result
1739 GVector probs(nevents);
1740
1741 // Continue only if the model has a spatial component
1742 if (model.spatial() != NULL) {
1743
1744 // Initialise matrix to hold spatial gradients
1745 GMatrix spat_gradients(nevents, model.spatial()->size());
1746
1747 // Compute IRF value
1748 probs = irf_spatial(model, obs, &spat_gradients);
1749
1750 // Get global model scaling
1751 double scale = (model.has_scales())
1752 ? model.scale(obs.instrument()).value() : 1.0;
1753
1754 // Initialise temporary vectors to hold gradients
1755 GVector* tmp_gradients = NULL;
1756 if (grad) {
1757 tmp_gradients = new GVector[npars];
1758 for (int i = 0; i < npars; ++i) {
1759 tmp_gradients[i] = GVector(nevents);
1760 }
1761 }
1762
1763 // Loop over events
1764 for (int k = 0; k < nevents; ++k) {
1765
1766 // Get probability
1767 double spat = probs[k];
1768
1769 // Continue only if spatial value is positive
1770 if (spat > 0.0) {
1771
1772 // Get reference to event
1773 const GEvent& event = *((*obs.events())[k]);
1774
1775 // Get source energy and time
1776 GEnergy srcEng = event.energy();
1777 GTime srcTime = event.time();
1778
1779 // Evaluate spectral and temporal components
1780 double spec = (model.spectral() != NULL)
1781 ? model.spectral()->eval(srcEng, srcTime, grad)
1782 : 1.0;
1783 double temp = (model.temporal() != NULL)
1784 ? model.temporal()->eval(srcTime, grad)
1785 : 1.0;
1786
1787 // Compute probability
1788 probs[k] = spat * spec * temp * scale;
1789
1790 // Optionally compute partial derivatives
1791 if (grad) {
1792
1793 // Get number of spatial, spectral and temporal parameters
1794 int n_spat = model.spatial()->size();
1795 int n_spec = model.spectral()->size();
1796 int n_temp = model.temporal()->size();
1797
1798 // Multiply factors to spatial gradients
1799 if (model.spatial() != NULL) {
1800 double fact = spec * temp * scale;
1801 for (int i = 0; i < n_spat; ++i) {
1802 tmp_gradients[i][k] = spat_gradients(k,i) * fact;
1803 }
1804 }
1805
1806 // Multiply factors to spectral gradients
1807 if (model.spectral() != NULL) {
1808 int offset = n_spat;
1809 double fact = spat * temp * scale;
1810 for (int i = 0; i < n_spec; ++i) {
1811 tmp_gradients[offset+i][k] =
1812 (*(model.spectral()))[i].factor_gradient() * fact;
1813 }
1814 }
1815
1816 // Multiply factors to temporal gradients
1817 if (model.temporal() != NULL) {
1818 int offset = n_spat + n_spec;
1819 double fact = spat * spec * scale;
1820 for (int i = 0; i < n_temp; ++i) {
1821 tmp_gradients[offset+i][k] =
1822 (*(model.temporal()))[i].factor_gradient() * fact;
1823 }
1824 }
1825
1826 // Optionally set scale gradient for instrument
1827 if (model.has_scales()) {
1828 int offset = n_spat + n_spec + n_temp;
1829 double fact = spat * spec * temp;
1830 for (int i = 0; i < model.scales(); ++i) {
1831 const GModelPar& par = model.scale(i);
1832 double g_scale = 0.0;
1833 if (par.name() == obs.instrument()) {
1834 if (par.is_free()) {
1835 tmp_gradients[offset+i][k] = par.scale() * fact;
1836 }
1837 }
1838 }
1839 }
1840
1841 } // endif: computed partial derivatives
1842
1843 } // endif: IRF spatial value was positive
1844
1845 // Compile option: Check for NaN/Inf
1846 #if defined(G_NAN_CHECK)
1847 if (gammalib::is_notanumber(probs[k]) || gammalib::is_infinite(probs[k])) {
1848 std::cout << "*** ERROR: GResponse::eval_prob:";
1849 std::cout << " NaN/Inf encountered";
1850 std::cout << " (probs[" << k << "]=" << probs[k];
1851 std::cout << ", spat=" << spat;
1852 std::cout << ")" << std::endl;
1853 }
1854 #endif
1855
1856 } // endfor: looped over events
1857
1858 // Post-process gradients
1859 if (grad) {
1860
1861 // Fill gradients into matrix
1862 for (int i = 0; i < npars; ++i) {
1863 gradients->column(i, tmp_gradients[i]);
1864 }
1865
1866 // Delete temporal gradients
1867 delete [] tmp_gradients;
1868
1869 } // endif: gradient post processing
1870
1871 } // endif: Gamma-ray source model had a spatial component
1872
1873 // Return event probabilities
1874 return probs;
1875}
1876
1877
1878/***********************************************************************//**
1879 * @brief Return size of vector for energy dispersion computation
1880 *
1881 * @param[in] model Sky model.
1882 * @param[in] obs Observation.
1883 * @param[out] grad Signals whether gradients should be computed.
1884 * @return Size of vector for energy dispersion computation.
1885 *
1886 * Computes the size of the vector that will be computed for the computation
1887 * of the energy dispersion.
1888 ***************************************************************************/
1890 const GObservation& obs,
1891 const bool& grad) const
1892{
1893 // Initialise vector size
1894 int size = 1;
1895
1896 // Determine size of gradient part
1897 if (grad) {
1898 if (model.spectral() != NULL) {
1899 for (int i = 0; i < model.spectral()->size(); ++i) {
1900 GModelPar& par = (*(model.spectral()))[i];
1901 if (par.is_free() && par.has_grad()) {
1902 size++;
1903 }
1904 }
1905 }
1906 if (model.temporal() != NULL) {
1907 for (int i = 0; i < model.temporal()->size(); ++i) {
1908 GModelPar& par = (*(model.temporal()))[i];
1909 if (par.is_free() && par.has_grad()) {
1910 size++;
1911 }
1912 }
1913 }
1914 if (model.has_scales()) {
1915 for (int i = 0; i < model.scales(); ++i) {
1916 GModelPar& par = const_cast<GModelPar&>(model.scale(i));
1917 if (par.name() == obs.instrument()) {
1918 if (par.is_free() && par.has_grad()) {
1919 size++;
1920 }
1921 }
1922 }
1923 }
1924 }
1925
1926 // Return size
1927 return size;
1928}
1929
1930
1931/***********************************************************************//**
1932 * @brief Constructor for energy dispersion integration kernels class
1933 *
1934 * @param[in] parent Pointer to response.
1935 * @param[in] obs Pointer to observation.
1936 * @param[in] model Pointer to sky model.
1937 * @param[in] event Pointer to event.
1938 * @param[in] srcTime True time.
1939 * @param[in] grad Evaluate gradients?
1940 *
1941 * This method constructs the integration kernel needed for the energy
1942 * dispersion computation.
1943 ***************************************************************************/
1945 const GObservation* obs,
1946 const GModelSky* model,
1947 const GEvent* event,
1948 const GTime& srcTime,
1949 const bool& grad)
1950{
1951 // Set members
1952 m_parent = parent;
1953 m_obs = obs;
1954 m_model = model;
1955 m_event = event;
1956 m_size = 1;
1957 m_srcTime = srcTime;
1958 m_grad = grad;
1959
1960 // If gradients are requested then put pointers to all relevant parameter
1961 // in the parameter pointer vector
1962 m_pars.clear();
1963 if (m_grad) {
1964
1965 // Add free spectral parameters with analytical gradients
1966 if (m_model->spectral() != NULL) {
1967 for (int i = 0; i < m_model->spectral()->size(); ++i) {
1968 GModelPar& par = (*(m_model->spectral()))[i];
1969 if (par.is_free() && par.has_grad()) {
1970 m_pars.push_back(&par);
1971 }
1972 }
1973 }
1974
1975 // Add free temporal parameters with analytical gradients
1976 if (m_model->temporal() != NULL) {
1977 for (int i = 0; i < m_model->temporal()->size(); ++i) {
1978 GModelPar& par = (*(m_model->temporal()))[i];
1979 if (par.is_free() && par.has_grad()) {
1980 m_pars.push_back(&par);
1981 }
1982 }
1983 }
1984
1985 // Add free scaling factors
1986 if (m_model->has_scales()) {
1987 for (int i = 0; i < m_model->scales(); ++i) {
1988 GModelPar& par = const_cast<GModelPar&>(m_model->scale(i));
1989 if (par.name() == m_obs->instrument()) {
1990 if (par.is_free() && par.has_grad()) {
1991 m_pars.push_back(&par);
1992 }
1993 }
1994 }
1995 }
1996
1997 // Update size
1998 m_size += m_pars.size();
1999
2000 } // endif: gradients were requested
2001
2002 // Return
2003 return;
2004}
2005
2006
2007/***********************************************************************//**
2008 * @brief Return true energy intervals for sky model
2009 *
2010 * @param[in] model Sky model.
2011 * @return True energy intervals.
2012 *
2013 * Returns the true energy intervals for a sky model. For all spectral
2014 * models other than the GModelSpectralGauss model the method will return
2015 * an empty energy boundaries structure. For the GModelSpectralGauss model
2016 * the method will return the interval [mean - 5 * sigma, mean + 5 * sigma].
2017 ***************************************************************************/
2019{
2020 // Initialise empty energy boundaries
2022
2023 // If the spectral model is a Gaussian line then return
2024 const GModelSpectralGauss* gauss =
2025 dynamic_cast<const GModelSpectralGauss*>(model.spectral());
2026 if (gauss != NULL) {
2027
2028 // Define width of energy interval
2029 const double width = 5.0;
2030
2031 // Compute energy boundaries
2032 GEnergy mean = gauss->mean();
2033 GEnergy sigma = gauss->sigma();
2034 GEnergy ebound_min = mean - width * sigma;
2035 GEnergy ebound_max = mean + width * sigma;
2036
2037 // Make sure that energies are not negative positive
2038 if (ebound_min.MeV() < 0.0) {
2039 ebound_min.MeV(0.0);
2040 }
2041 if (ebound_max.MeV() < 0.0) {
2042 ebound_max.MeV(0.0);
2043 }
2044
2045 // If energy boundaries define a positive interval then append them
2046 if (ebound_max > ebound_min) {
2047 ebounds.append(ebound_min, ebound_max);
2048 }
2049
2050 } // endif: spectral model was a Gaussian
2051
2052 // Return energy boudnaries
2053 return ebounds;
2054}
2055
2056
2057/***********************************************************************//**
2058 * @brief Evaluate energy dispersion integration kernel
2059 *
2060 * @param[in] etrue True photon energy in MeV.
2061 *
2062 * This method implements the integration kernel needed for the
2063 * GResponse::edisp_kern() class.
2064 ***************************************************************************/
2066{
2067 // Initialise result
2068 GVector kernels(m_size);
2069
2070 // Initialise Ndarray array index
2071 int index = 0;
2072
2073 // Set true energy
2074 GEnergy srcEng;
2075 srcEng.MeV(etrue);
2076
2077 // Get function value
2078 kernels[index++] = m_parent->eval_prob(*m_model, *m_event,
2079 srcEng, m_srcTime, *m_obs,
2080 m_grad);
2081
2082 // If gradients are requested then extract them and put them into the
2083 // array
2084 if (m_grad) {
2085 for (int i = 0; i < m_pars.size(); ++i, ++index) {
2086 kernels[index] = m_pars[i]->factor_gradient();
2087 }
2088 }
2089
2090 // Compile option: Check for NaN
2091 #if defined(G_NAN_CHECK)
2092 if (gammalib::is_notanumber(kernels[0]) || gammalib::is_infinite(kernels[0])) {
2093 std::cout << "*** ERROR: GResponse::edisp_kern::eval";
2094 std::cout << "(etrue=" << etrue << "): NaN/Inf encountered";
2095 std::cout << " (value=" << kernels[0] << ")" << std::endl;
2096 }
2097 #endif
2098
2099 // Return kernels
2100 return kernels;
2101}
2102
2103
2104/***********************************************************************//**
2105 * @brief Zenith angle integration kernel for radial model
2106 *
2107 * @param[in] theta Zenith angle (radians).
2108 * @return IRF value.
2109 *
2110 * Integrated the IRF multiplied by the model value for a given zenith angle
2111 * over all azimuth angles.
2112 ***************************************************************************/
2114{
2115 // Initialise result
2116 double irf = 0.0;
2117
2118 // Continue only for positive zenith angles (otherwise the integral will
2119 // be zero)
2120 if (theta > 0.0) {
2121
2122 // Evaluate sky model
2123 double model = m_model->eval(theta, *m_srcEng, *m_srcTime);
2124
2125 // Continue only if model is positive
2126 if (model > 0.0) {
2127
2128 // Set azimuthal integration range
2129 double phi_min = 0.0;
2130 double phi_max = gammalib::twopi;
2131
2132 // Setup integration kernel
2133 irf_radial_kern_phi integrand(m_rsp,
2134 m_event,
2135 m_obs,
2136 m_rot,
2137 theta,
2138 m_srcEng,
2139 m_srcTime);
2140
2141 // Setup integration
2142 GIntegral integral(&integrand);
2143 integral.fixed_iter(m_iter_phi);
2144
2145 // Integrate over phi
2146 irf = integral.romberg(phi_min, phi_max, m_iter_phi) *
2147 model * std::sin(theta);
2148
2149 } // endif: model was positive
2150
2151 } // endif: theta was positive
2152
2153 // Return result
2154 return irf;
2155}
2156
2157
2158/***********************************************************************//**
2159 * @brief Azimuth angle integration kernel for radial model
2160 *
2161 * @param[in] phi Azimuth angle (radians).
2162 * @return IRF value.
2163 *
2164 * Computes the IRF at a given zenith and azimuth angle with respect to a
2165 * specified centre.
2166 ***************************************************************************/
2168{
2169 // Set up native coordinate vector
2170 double cos_phi = std::cos(phi);
2171 double sin_phi = std::sin(phi);
2172 GVector native(-cos_phi*m_sin_theta, sin_phi*m_sin_theta, m_cos_theta);
2173
2174 // Rotate vector into celestial coordinates
2175 GVector vector = (*m_rot) * native;
2176
2177 // Convert vector into sky direction
2178 GSkyDir dir(vector);
2179
2180 // Setup photon
2181 GPhoton photon(dir, *m_srcEng, *m_srcTime);
2182
2183 // Evaluate IRF for photon
2184 double irf = m_rsp->irf(*m_event, photon, *m_obs);
2185
2186 // Return
2187 return irf;
2188}
2189
2190
2191/***********************************************************************//**
2192 * @brief Zenith angle integration kernel for elliptical model
2193 *
2194 * @param[in] theta Zenith angle (radians).
2195 * @return IRF value.
2196 *
2197 * Integrated the IRF multiplied by the model value for a given zenith angle
2198 * over all azimuth angles.
2199 ***************************************************************************/
2201{
2202 // Initialise result
2203 double irf = 0.0;
2204
2205 // Continue only for positive zenith angles (otherwise the integral will
2206 // be zero)
2207 if (theta > 0.0) {
2208
2209 // Set azimuthal integration range
2210 double phi_min = 0.0;
2211 double phi_max = gammalib::twopi;
2212
2213 // Setup integration kernel
2214 irf_elliptical_kern_phi integrand(m_rsp,
2215 m_event,
2216 m_obs,
2217 m_model,
2218 m_rot,
2219 theta,
2220 m_srcEng,
2221 m_srcTime);
2222
2223 // Setup integration
2224 GIntegral integral(&integrand);
2225 integral.fixed_iter(m_iter_phi);
2226
2227 // Integrate over phi
2228 irf = integral.romberg(phi_min, phi_max, m_iter_phi) * std::sin(theta);
2229
2230 } // endif: theta was positive
2231
2232 // Return result
2233 return irf;
2234}
2235
2236
2237/***********************************************************************//**
2238 * @brief Azimuth angle integration kernel for elliptical model
2239 *
2240 * @param[in] phi Azimuth angle (radians).
2241 * @return IRF value.
2242 *
2243 * Computes the IRF at a given zenith and azimuth angle with respect to a
2244 * specified centre.
2245 ***************************************************************************/
2247{
2248 // Initialise result
2249 double irf = 0.0;
2250
2251 // Evaluate sky model
2252 double model = m_model->eval(m_theta, phi, *m_srcEng, *m_srcTime);
2253
2254 // Continue only if model is positive
2255 if (model > 0.0) {
2256
2257 // Set up native coordinate vector
2258 double cos_phi = std::cos(phi);
2259 double sin_phi = std::sin(phi);
2260 GVector native(-cos_phi*m_sin_theta, sin_phi*m_sin_theta, m_cos_theta);
2261
2262 // Rotate vector into celestial coordinates
2263 GVector vector = (*m_rot) * native;
2264
2265 // Convert vector into sky direction
2266 GSkyDir dir(vector);
2267
2268 // Setup photon
2269 GPhoton photon(dir, *m_srcEng, *m_srcTime);
2270
2271 // Evaluate IRF for photon
2272 irf = m_rsp->irf(*m_event, photon, *m_obs) * model;
2273
2274 } // endif: model was positive
2275
2276 // Return
2277 return irf;
2278}
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