GammaLib 2.0.0
Loading...
Searching...
No Matches
GCTAResponse_helpers.cpp
Go to the documentation of this file.
1/***************************************************************************
2 * GCTAResponse_helpers.cpp - CTA response helper classes *
3 * ----------------------------------------------------------------------- *
4 * copyright (C) 2012-2020 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 GCTAResponse_helpers.cpp
23 * @brief CTA response helper classes implementation
24 * @author Juergen Knoedlseder
25 */
26
27/* __ Includes ___________________________________________________________ */
28#ifdef HAVE_CONFIG_H
29#include <config.h>
30#endif
31#include <cmath>
32#include "GTools.hpp"
33#include "GMath.hpp"
34#include "GIntegral.hpp"
35#include "GIntegrals.hpp"
36#include "GVector.hpp"
38#include "GCTAEdisp.hpp"
39#include "GCTASupport.hpp"
43#include "GWcs.hpp"
44
45/* __ Method name definitions ____________________________________________ */
46
47/* __ Macros _____________________________________________________________ */
48
49/* __ Coding definitions _________________________________________________ */
50//#define G_USE_OBSDIR_FOR_AEFF //!< Use event offset for Aeff computation
51
52/* __ Debug definitions __________________________________________________ */
53//#define G_DEBUG_INTEGRAL //!< Debug integration
54//#define G_DEBUG_MODEL_ZERO //!< Debug check for zero model values
55
56/* __ Constants __________________________________________________________ */
57const double g_kludge_radius = 1.0e-12; //!< Tiny angle (radians)
58const double g_ellipse_kludge_radius = 1.0e-6; //!< About 0.2 arc seconds
59 // A larger radius is used
60 // as the ellipse is defined
61 // in cartesian coordinates
62 // while distances are
63 // computed in spherical
64 // trigonometry
65
66
67/*==========================================================================
68 = =
69 = Helper functions =
70 = =
71 ==========================================================================*/
72
73/***********************************************************************//**
74 * @brief Limit omega interval
75 *
76 * @param[in] min Interval minimum (radians).
77 * @param[in] max Interval maximum (radians).
78 * @param[in] domega Half length of interval (radians).
79 * @return Vector of intervals.
80 *
81 * Limits an omega interval [@p min,@p max] to the interval specified by
82 * [-@p domega,@p domega]. This may lead to a split of [@p min,@p max] in
83 * several intervals. The method thus returns a vector of intervals that
84 * overlap with [-@p domega,@p domega]. If there is no overlap with the
85 * interval, the method returns an empty vector.
86 *
87 * The method takes care of wrap arounds. It is assumed that on input
88 * [@p min,@p max] is contained within [-2pi,+2pi].
89 ***************************************************************************/
91 const double& max,
92 const double& domega)
93{
94 // Allocate intervals
95 cta_omega_intervals intervals;
96
97 // Continue only if domega is smaller than pi
98 if (domega < gammalib::pi) {
99
100 // Set limiting intervals. To take care of a possible wrap around in
101 // omega we consider also intervals that are shifted by +/-2pi
102 double omega_min = -domega;
103 double omega_max = +domega;
104 double omega_min_plus = omega_min + gammalib::twopi;
105 double omega_max_plus = omega_max + gammalib::twopi;
106 double omega_min_minus = omega_min - gammalib::twopi;
107 double omega_max_minus = omega_max - gammalib::twopi;
108
109 // If the [min,max] interval overlaps with the unshifted
110 // [-domega,domega] interval then constrain the interval
111 if (max > omega_min && min < omega_max) {
112 double interval_min = min;
113 double interval_max = max;
114 if (interval_min < omega_min) {
115 interval_min = omega_min;
116 }
117 if (interval_max > omega_max) {
118 interval_max = omega_max;
119 }
120 intervals.push_back(std::make_pair(interval_min,interval_max));
121 }
122
123 // If the [min,max] interval overlaps with the [-domega,domega]
124 // interval shifted by +2pi then constrain the interval using the
125 // shifted interval
126 if (max > omega_min_plus && min < omega_max_plus) {
127 double interval_min = min;
128 double interval_max = max;
129 if (interval_min < omega_min_plus) {
130 interval_min = omega_min_plus;
131 }
132 if (interval_max > omega_max_plus) {
133 interval_max = omega_max_plus;
134 }
135 intervals.push_back(std::make_pair(interval_min,interval_max));
136 }
137
138 // If the [min,max] interval overlaps with the [-domega,domega]
139 // interval shifted by -2pi then constrain the interval using the
140 // shifted interval
141 if (max > omega_min_minus && min < omega_max_minus) {
142 double interval_min = min;
143 double interval_max = max;
144 if (interval_min < omega_min_minus) {
145 interval_min = omega_min_minus;
146 }
147 if (interval_max > omega_max_minus) {
148 interval_max = omega_max_minus;
149 }
150 intervals.push_back(std::make_pair(interval_min,interval_max));
151 }
152
153 } // endif: interval was not the full circle
154
155 // ... otherwise append the provided interval
156 else {
157 intervals.push_back(std::make_pair(min,max));
158 }
159
160 // Return intervals
161 return intervals;
162}
163
164
165/***********************************************************************//**
166 * @brief Determine resolution of spatial model
167 *
168 * @param[in] model Pointer to spatial model.
169 * @return Resolution of spatial model (radians).
170 *
171 * Determine the resolution of a spatial model. So far the method only works
172 * for a spatial map or cube model holding a WCS projection. If a constant
173 * spatial model is encountered a resolution of 180 deg is returned.
174 *
175 * If the resolution of the model could not be determined, the method returns
176 * a resolution of 0.01 deg.
177 ***************************************************************************/
179{
180 // Initialise resolution to default resolution
181 double resolution = 0.01 * gammalib::deg2rad;
182
183 // Extract pointer to spatial map. The pointer will be NULL if no spatial
184 // map exists.
185 const GSkyMap* map = NULL;
186 const GModelSpatialDiffuseMap* pmap =
187 dynamic_cast<const GModelSpatialDiffuseMap*>(model);
188 if (pmap != NULL) {
189 map = &(pmap->map());
190 }
191 else {
192 const GModelSpatialDiffuseCube* pcube =
193 dynamic_cast<const GModelSpatialDiffuseCube*>(model);
194 if (pcube != NULL) {
195 map = &(pcube->cube());
196 }
197 else {
198 const GModelSpatialDiffuseConst* pconst =
199 dynamic_cast<const GModelSpatialDiffuseConst*>(model);
200 if (pconst != NULL) {
202 }
203 }
204 }
205
206 // If a spatial map exists then get it's resolution. This so far only
207 // works for WCS maps.
208 if (map != NULL) {
209 const GWcs* wcs = dynamic_cast<const GWcs*>(map->projection());
210 if (wcs != NULL) {
211 double dx = std::abs(wcs->cdelt(0));
212 double dy = std::abs(wcs->cdelt(1));
213 resolution = ((dx < dy) ? dx : dy) * gammalib::deg2rad;
214 }
215 }
216
217 // Return resolution
218 return resolution;
219}
220
221
222/*==========================================================================
223 = =
224 = Helper class methods for response computation =
225 = =
226 ==========================================================================*/
227
228/***********************************************************************//**
229 * @brief Integration kernel for npsf() method
230 *
231 * @param[in] delta Distance from PSF centre (radians).
232 * @return Azimuthally integrated PSF.
233 *
234 * Computes
235 *
236 * \f[
237 * \int_{0}^{\phi} PSF(\delta) d\phi
238 * \f]
239 *
240 * for a given offset angle \f$\delta\f$.
241 *
242 * The azimuthal integration is performed over an arclength given by
243 * \f$\phi\f$. The method actually assumes that the PSF is azimuthally
244 * symmetric, hence it just multiplies the PSF value by the arclength times
245 * the sinus of the offset angle.
246 ***************************************************************************/
247double cta_npsf_kern_rad_azsym::eval(const double& delta)
248{
249 // Initialise PSF value
250 double value = 0.0;
251
252 // Get arclength for given radius in radians
253 double phi = gammalib::roi_arclength(delta,
254 m_psf,
255 m_cospsf,
256 m_sinpsf,
257 m_roi,
258 m_cosroi);
259
260 // If arclength is positive then compute the PSF value
261 if (phi > 0) {
262
263 // Compute PSF value
264 value = m_rsp->psf(delta, m_theta, m_phi, m_zenith, m_azimuth, m_logE) *
265 phi * std::sin(delta);
266
267 // Compile option: Check for NaN/Inf
268 #if defined(G_NAN_CHECK)
269 if (gammalib::is_notanumber(value) || gammalib::is_infinite(value)) {
270 std::cout << "*** ERROR: cta_npsf_kern_rad_azsym::eval";
271 std::cout << " NaN/Inf encountered";
272 std::cout << " (value=" << value;
273 std::cout << ", delta=" << delta;
274 std::cout << ", phi=" << phi << ")";
275 std::cout << std::endl;
276 }
277 #endif
278
279 } // endif: arclength was positive
280
281 // Return
282 return value;
283}
284
285
286/***********************************************************************//**
287 * @brief Integration kernel for GCTAResponseIrf::nroi method
288 *
289 * @param[in] etrue True photon energy in MeV.
290 * @return Nroi.
291 ***************************************************************************/
292double cta_nroi_kern::eval(const double& etrue)
293{
294 // Set true energy
295 GEnergy srcEng;
296 srcEng.MeV(etrue);
297
298 // Compute response components
299 double nroi_spatial = m_rsp->nroi(*m_model, srcEng, m_srcTime, m_obsEng, m_obsTime, *m_obs);
300 double nroi_spectral = m_model->spectral()->eval(srcEng, m_srcTime);
301 double nroi_temporal = m_model->temporal()->eval(m_srcTime);
302
303 // Compute response
304 double nroi = nroi_spatial * nroi_spectral * nroi_temporal;
305
306 // Return response
307 return nroi;
308}
309
310
311/*==========================================================================
312 = =
313 = Helper class methods for radial models =
314 = =
315 ==========================================================================*/
316
317/***********************************************************************//**
318 * @brief Kernel for radial model zenith angle integration of Irf
319 *
320 * @param[in] rho Zenith angle with respect to model centre [radians].
321 *
322 * Computes the kernel
323 *
324 * \f[
325 * K(\rho | E, t) = \sin \rho \times S_{\rm p}(\rho | E, t) \times
326 * \int_{\omega_{\rm min}}^{\omega_{\rm max}}
327 * IRF(\rho, \omega) d\omega
328 * \f]
329 *
330 * for the zenith angle integration of radial models.
331 ***************************************************************************/
332double cta_irf_radial_kern_rho::eval(const double& rho)
333{
334 // Initialise result
335 double irf = 0.0;
336
337 // Continue only if rho is positive (otherwise the integral will be
338 // zero)
339 if (rho > 0.0) {
340
341 // Compute half length of arc that lies within PSF validity circle
342 // (in radians)
343 double domega = 0.5 * gammalib::roi_arclength(rho,
344 m_zeta,
349
350 // Continue only if arc length is positive
351 if (domega > 0.0) {
352
353 // Compute omega integration range
354 double omega_min = -domega;
355 double omega_max = +domega;
356
357 // Reduce rho by an infinite amount to avoid rounding errors
358 // at the boundary of a sharp edged model
359 double rho_kludge = rho - g_kludge_radius;
360 if (rho_kludge < 0.0) {
361 rho_kludge = 0.0;
362 }
363
364 // Evaluate sky model
365 double model = m_model->eval(rho_kludge, m_srcEng, m_srcTime);
366
367 // Debug: test if model is non positive
368 #if defined(G_DEBUG_MODEL_ZERO)
369 if (model <= 0.0) {
370 std::cout << "*** WARNING: cta_irf_radial_kern_rho::eval";
371 std::cout << " zero model for (rho)=(";
372 std::cout << rho*gammalib::rad2deg << ")";
373 std::cout << " rho-r_model=" << (rho-m_model.theta_max());
374 std::cout << " radians" << std::endl;
375 }
376 #endif
377
378 // Continue only if model is positive
379 if (model > 0.0) {
380
381 // Precompute cosine and sine terms for azimuthal
382 // integration
383 double cos_rho = std::cos(rho);
384 double sin_rho = std::sin(rho);
385 double cos_psf = cos_rho*m_cos_zeta;
386 double sin_psf = sin_rho*m_sin_zeta;
387 double cos_ph = cos_rho*m_cos_lambda;
388 double sin_ph = sin_rho*m_sin_lambda;
389
390 // Setup integration kernel
392 m_zenith,
393 m_azimuth,
394 m_srcEng,
395 m_obsEng,
396 m_zeta,
397 m_lambda,
398 m_omega0,
399 rho,
400 cos_psf,
401 sin_psf,
402 cos_ph,
403 sin_ph);
404
405 // Integrate over phi
406 GIntegral integral(&integrand);
407 integral.fixed_iter(m_iter);
408 irf = integral.romberg(omega_min, omega_max, m_iter) *
409 model * sin_rho;
410
411 // Compile option: Check for NaN/Inf
412 #if defined(G_NAN_CHECK)
414 std::cout << "*** ERROR: cta_irf_radial_kern_rho";
415 std::cout << "(rho=" << rho << "):";
416 std::cout << " NaN/Inf encountered";
417 std::cout << " (irf=" << irf;
418 std::cout << ", domega=" << domega;
419 std::cout << ", model=" << model;
420 std::cout << ", sin_rho=" << sin_rho << ")";
421 std::cout << std::endl;
422 }
423 #endif
424
425 } // endif: model was positive
426
427 } // endif: arclength was positive
428
429 } // endif: rho was positive
430
431 // Return result
432 return irf;
433}
434
435
436/***********************************************************************//**
437 * @brief Kernel for radial model azimuth angle IRF integration
438 *
439 * @param[in] omega Azimuth angle (radians).
440 *
441 * Computes the kernel
442 *
443 * \f[
444 * IRF(\rho,\omega)
445 * \f]
446 *
447 * for the azimuth angle integration of radial models.
448 *
449 * From the model coordinates \f$(\rho,\omega)\f$, the method computes the
450 * angle between the true (\f$\vec{p}\f$) and observed (\f$\vec{p'}\f$)
451 * photon arrival direction using
452 *
453 * \f[\delta = \arccos(\cos \rho \cos \zeta +
454 * \sin \rho \sin \zeta \cos \omega)\f]
455 *
456 * where
457 * \f$\zeta\f$ is the angular distance between the observed photon arrival
458 * direction \f$\vec{p'}\f$ and the model centre \f$\vec{m}\f$. This angle
459 * \f$\delta\f$ is used to compute the \f$PSF(\delta)\f$ value.
460 *
461 * The method computes also the angle \f$\theta\f$ between the observed
462 * photon arrival direction \f$\vec{p'}\f$ and the camera pointing
463 * \f$\vec{d}\f$ using
464 *
465 * \f[\theta = \arccos(\cos \rho \cos \lambda +
466 * \sin \rho \sin \lambda \cos \omega_0 - \omega)\f]
467 *
468 * where
469 * \f$\lambda\f$ is the angular distance between the model centre
470 * \f$\vec{m}\f$ and the camera pointing direction \f$\vec{d}\f$.
471 * The angle \f$\theta\f$ is used in the computation of the IRF (no
472 * azimuthal dependence is so far implemented for the IRF computation).
473 ***************************************************************************/
474double cta_irf_radial_kern_omega::eval(const double& omega)
475{
476 // Get log10(E/TeV) of true photon energy
477 double srcLogEng = m_srcEng.log10TeV();
478
479 // Compute PSF offset angle [radians]
480 double delta = std::acos(m_cos_psf + m_sin_psf * std::cos(omega));
481
482 // Compute true photon offset angle in camera system [radians]
483 double offset = std::acos(m_cos_ph + m_sin_ph * std::cos(m_omega0 - omega));
484
485 //TODO: Compute true photon azimuth angle in camera system [radians]
486 double azimuth = 0.0;
487
488 // Evaluate IRF
489 double irf = m_rsp->aeff(offset, azimuth, m_zenith, m_azimuth, srcLogEng) *
490 m_rsp->psf(delta, offset, azimuth, m_zenith, m_azimuth, srcLogEng);
491
492 // Optionally take energy dispersion into account
493 if (m_rsp->use_edisp() && irf > 0.0) {
494 irf *= m_rsp->edisp(m_obsEng, m_srcEng, offset, azimuth, m_zenith, m_azimuth);
495 }
496
497 // Compile option: Check for NaN/Inf
498 #if defined(G_NAN_CHECK)
500 std::cout << "*** ERROR: cta_irf_radial_kern_omega::eval";
501 std::cout << "(omega=" << omega << "):";
502 std::cout << " NaN/Inf encountered";
503 std::cout << " (irf=" << irf;
504 std::cout << ", delta=" << delta;
505 std::cout << ", offset=" << offset;
506 std::cout << ", azimuth=" << azimuth << ")";
507 std::cout << std::endl;
508 }
509 #endif
510
511 // Return
512 return irf;
513}
514
515
516/***********************************************************************//**
517 * @brief Kernel for zenith angle Nroi integration or radial model
518 *
519 * @param[in] rho Radial model zenith angle (radians).
520 *
521 * Computes
522 *
523 * \f[
524 * K(\rho | E, t) = \sin \rho \times S_{\rm p}(\rho | E, t) \times
525 * \int_{\omega_{\rm min}}^{\omega_{\rm max}}
526 * N_{\rm pred}(\rho,\omega) d\omega
527 * \f]
528 *
529 * The azimuth angle integration range
530 * \f$[\omega_{\rm min}, \omega_{\rm max}]\f$
531 * is limited to an arc around the vector connecting the model centre to
532 * the ROI centre. This limitation assures that the integration converges
533 * properly.
534 ***************************************************************************/
535double cta_nroi_radial_kern_rho::eval(const double& rho)
536{
537 // Initialise Nroi value
538 double nroi = 0.0;
539
540 // Continue only if rho is positive
541 if (rho > 0.0) {
542
543 // Compute half length of arc that lies within ROI+PSF radius (radians)
544 double domega = 0.5 * gammalib::roi_arclength(rho,
545 m_dist,
548 m_radius,
550
551 // Continue only if arc length is positive
552 if (domega > 0.0) {
553
554 // Compute omega integration range
555 double omega_min = m_omega0 - domega;
556 double omega_max = m_omega0 + domega;
557
558 // Reduce rho by an infinite amount to avoid rounding errors
559 // at the boundary of a sharp edged model
560 double rho_kludge = rho - g_kludge_radius;
561 if (rho_kludge < 0.0) {
562 rho_kludge = 0.0;
563 }
564
565 // Get radial model value
566 double model = m_model->eval(rho_kludge, m_srcEng, m_srcTime);
567
568 // Debug: test if model is non positive
569 #if defined(G_DEBUG_MODEL_ZERO)
570 if (model <= 0.0) {
571 std::cout << "*** WARNING: cta_nroi_radial_kern_rho::eval";
572 std::cout << " zero model for (rho)=(";
573 std::cout << rho*gammalib::rad2deg << ")";
574 std::cout << " rho-r_model=" << (rho-m_model.theta_max());
575 std::cout << " radians" << std::endl;
576 }
577 #endif
578
579 // Continue only if we have a positive model value
580 if (model > 0.0) {
581
582 // Compute sine and cosine of offset angle
583 double sin_rho = std::sin(rho);
584 double cos_rho = std::cos(rho);
585
586 // Setup phi integration kernel
588 m_obs,
589 m_rot,
590 m_srcEng,
591 m_srcTime,
592 m_obsEng,
593 m_obsTime,
594 sin_rho,
595 cos_rho);
596
597 // Integrate over phi
598 GIntegral integral(&integrand);
599 integral.fixed_iter(m_iter);
600 nroi = integral.romberg(omega_min, omega_max, m_iter) *
601 sin_rho * model;
602
603 // Debug: Check for NaN
604 #if defined(G_NAN_CHECK)
606 std::cout << "*** ERROR: cta_nroi_radial_kern_rho::eval";
607 std::cout << "(rho=" << rho << "):";
608 std::cout << " NaN/Inf encountered";
609 std::cout << " (nroi=" << nroi;
610 std::cout << ", model=" << model;
611 std::cout << ", omega=[" << omega_min << "," << omega_max << "]";
612 std::cout << ", sin_rho=" << sin_rho;
613 std::cout << ")" << std::endl;
614 }
615 #endif
616
617 } // endif: model was positive
618
619 } // endif: arc length was positive
620
621 } // endif: rho was positive
622
623 // Return Nroi
624 return nroi;
625}
626
627
628/***********************************************************************//**
629 * @brief Kernel for azimuth angle Nroi integration of radial model
630 *
631 * @param[in] omega Azimuth angle (radians).
632 *
633 * @todo Re-consider formula for possible simplification (dumb matrix
634 * multiplication is definitely not the fastest way to do that
635 * computation).
636 ***************************************************************************/
637double cta_nroi_radial_kern_omega::eval(const double& omega)
638{
639 // Compute sky direction vector in native coordinates
640 double cos_omega = std::cos(omega);
641 double sin_omega = std::sin(omega);
642 GVector native(-cos_omega*m_sin_rho, sin_omega*m_sin_rho, m_cos_rho);
643
644 // Rotate from native into celestial system
645 GVector cel = *m_rot * native;
646
647 // Set sky direction
648 GSkyDir srcDir;
649 srcDir.celvector(cel);
650
651 // Set Photon
652 GPhoton photon(srcDir, m_srcEng, m_srcTime);
653
654 // Compute point source Nroi for this sky direction
655 double nroi = m_rsp->nirf(photon, m_obsEng, m_obsTime, *m_obs);
656
657 // Debug: Check for NaN
658 #if defined(G_NAN_CHECK)
660 std::cout << "*** ERROR: cta_nroi_radial_kern_omega::eval";
661 std::cout << "(omega=" << omega << "):";
662 std::cout << " NaN/Inf encountered";
663 std::cout << " (nroi=" << nroi;
664 std::cout << ", cos_omega=" << cos_omega;
665 std::cout << ", sin_omega=" << sin_omega;
666 std::cout << ")" << std::endl;
667 }
668 #endif
669
670 // Return Nroi
671 return nroi;
672}
673
674
675/*==========================================================================
676 = =
677 = Helper class methods for elliptical models =
678 = =
679 ==========================================================================*/
680
681/***********************************************************************//**
682 * @brief Kernel for elliptical model integration over model's zenith angle
683 *
684 * @param[in] rho Radial distance from model centre [radians].
685 * @return Radial IRF integration kernel.
686 *
687 * Computes
688 *
689 * \f[
690 * K(\rho | E, t) = \sin \rho \times
691 * \int_{\omega_{\rm min}}^{\omega_{\rm max}}
692 * S_{\rm p}(\rho, \omega | E, t) \, IRF(\rho, \omega)
693 * d\omega
694 * \f]
695 *
696 * where
697 * \f$\omega\f$ is the azimuth angle with respect to the model centre,
698 * counted counterclockwise from the vector connecting the model centre
699 * to the observed photon direction, and
700 * \f$\rho\f$ is the radial distance from the model centre.
701 * \f$S_{\rm p}(\rho, \omega | E, t)\f$ is the elliptical source model
702 * for a given true photon energy and photon arrival time,
703 * \f$IRF(\rho, \omega)\f$ is the instrument response function.
704 *
705 * The method performs the required coordinate transformations from the
706 * model system, spanned by \f$(\rho, \omega)\f$, to the system needed for
707 * IRF computations. Furthermore, the method limits the integration range
708 * to area where the ellipse intersects the IRF.
709 ***************************************************************************/
710double cta_irf_elliptical_kern_rho::eval(const double& rho)
711{
712 // Initialise result
713 double irf = 0.0;
714
715 // Continue only if rho is positive
716 if (rho > 0.0) {
717
718 // Compute half length of the arc (in radians) from a circle with
719 // radius rho that intersects with the point spread function, defined
720 // as a circle with maximum radius m_delta_max
721 double domega = 0.5 * gammalib::roi_arclength(rho,
722 m_rho_obs,
727
728 // Continue only if arc length is positive
729 if (domega > 0.0) {
730
731 // Precompute cosine and sine terms for azimuthal integration
732 double cos_rho = std::cos(rho);
733 double sin_rho = std::sin(rho);
734 double cos_psf = cos_rho * m_cos_rho_obs;
735 double sin_psf = sin_rho * m_sin_rho_obs;
736 double cos_ph = cos_rho * m_cos_rho_pnt;
737 double sin_ph = sin_rho * m_sin_rho_pnt;
738
739 // Reduce rho by an infinite amount to avoid rounding errors
740 // at the boundary of a sharp edged model (e.g. an elliptical
741 // disk model)
742 double rho_kludge = rho - g_ellipse_kludge_radius;
743 if (rho_kludge < 0.0) {
744 rho_kludge = 0.0;
745 }
746
747 // Setup integration kernel
749 m_model,
750 m_zenith,
751 m_azimuth,
752 m_srcEng,
753 m_srcTime,
754 m_obsEng,
757 rho_kludge,
758 cos_psf,
759 sin_psf,
760 cos_ph,
761 sin_ph);
762
763 // Setup integrator
764 GIntegral integral(&integrand);
765 integral.fixed_iter(m_iter);
766
767 // If the radius rho is not larger than the semiminor axis
768 // boundary, the circle with that radius is fully contained in
769 // the ellipse and we can just integrate over the relevant arc
770 if (rho <= m_semiminor) {
771
772 // Compute omega integration range
773 double omega_min = -domega;
774 double omega_max = +domega;
775
776 // Integrate over omega
777 irf = integral.romberg(omega_min, omega_max, m_iter) *
778 sin_rho;
779
780 } // endif: circle comprised in ellipse
781
782 // ... otherwise there are arcs that intersect with the Psf circle
783 else {
784
785 // Compute half the arc length (in radians) of a circle of
786 // radius rho, centred on the model, that intersects with
787 // the ellipse boundary
788 double arg1 = 1.0 - (m_semiminor*m_semiminor) / (rho*rho);
789 double arg2 = 1.0 - (m_semiminor*m_semiminor) /
791 double omega_width = std::acos(std::sqrt(arg1/arg2));
792
793 // Continue only if the arclength is positive
794 if (omega_width > 0.0) {
795
796 // Compute azimuth angle difference between ellipse
797 // position angle and position angle of observed
798 // photon in the model system. This angle will define
799 // the reference point around which the circle arc. Make
800 // sure that omega_0 is within [-pi,pi] thus that the omega
801 // intervals are between [-2pi,2pi]
802 double omega_0 = m_posangle - m_posangle_obs;
803 if (omega_0 > gammalib::pi) {
804 omega_0 -= gammalib::pi;
805 }
806 else if (omega_0 < -gammalib::pi) {
807 omega_0 += gammalib::pi;
808 }
809
810 // Compute azimuth angle intervals
811 double omega1_min = omega_0 - omega_width;
812 double omega1_max = omega_0 + omega_width;
813 double omega2_min = omega1_min + gammalib::pi;
814 double omega2_max = omega1_max + gammalib::pi;
815
816 // Limit intervals to the intersection of the ellipse with
817 // the Psf circle. This may lead to a split of intervals,
818 // and we gather all these intervals in a special interval
819 // pair containers
820 cta_omega_intervals intervals1 =
821 gammalib::limit_omega(omega1_min, omega1_max, domega);
822 cta_omega_intervals intervals2 =
823 gammalib::limit_omega(omega2_min, omega2_max, domega);
824
825 // Integrate over all intervals for omega1
826 for (int i = 0; i < intervals1.size(); ++i) {
827 double min = intervals1[i].first;
828 double max = intervals1[i].second;
829 irf += integral.romberg(min, max, m_iter) * sin_rho;
830 }
831
832 // Integrate over all intervals for omega2
833 for (int i = 0; i < intervals2.size(); ++i) {
834 double min = intervals2[i].first;
835 double max = intervals2[i].second;
836 irf += integral.romberg(min, max, m_iter) * sin_rho;
837 }
838
839 } // endif: arc length was positive
840
841 } // endelse: circle was not comprised in ellipse
842
843 // Compile option: Check for NaN/Inf
844 #if defined(G_NAN_CHECK)
846 std::cout << "*** ERROR: cta_irf_elliptical_kern_rho";
847 std::cout << "(rho=" << rho << "):";
848 std::cout << " NaN/Inf encountered";
849 std::cout << " (irf=" << irf;
850 std::cout << ", domega=" << domega;
851 std::cout << ", sin_rho=" << sin_rho << ")";
852 std::cout << std::endl;
853 }
854 #endif
855
856 } // endif: arc length was positive
857
858 } // endif: rho was positive
859
860 // Return result
861 return irf;
862}
863
864
865/***********************************************************************//**
866 * @brief Kernel for elliptical model integration over model's azimuth angle
867 *
868 * @param[in] omega Azimuth angle (radians).
869 *
870 * Computes
871 *
872 * \f[
873 * S_{\rm p}(\omega | \rho, E, t) \, IRF(\omega | \rho)
874 * \f]
875 *
876 * where
877 * \f$\omega\f$ is the azimuth angle with respect to the model centre,
878 * counted counterclockwise from the vector connecting the model centre
879 * to the observed photon direction, and
880 * \f$\rho\f$ is the radial distance from the model centre.
881 *
882 * From the coordinates \f$(\rho,\omega)\f$ in the model system, the method
883 * computes the angle between the true (\f$\vec{p}\f$) and observed
884 * (\f$\vec{p'}\f$) photon arrival direction using
885 *
886 * \f[\delta = \arccos(\cos \rho \cos \zeta +
887 * \sin \rho \sin \zeta \cos \omega)\f]
888 *
889 * where
890 * \f$\zeta\f$ is the angular distance between the observed photon arrival
891 * direction \f$\vec{p'}\f$ and the model centre \f$\vec{m}\f$. This angle
892 * \f$\delta\f$ is used to compute the \f$PSF(\delta)\f$ value.
893 *
894 * The method computes also the angle \f$\theta\f$ between the observed
895 * photon arrival direction \f$\vec{p'}\f$ and the camera pointing
896 * \f$\vec{d}\f$ using
897 *
898 * \f[\theta = \arccos(\cos \rho \cos \lambda +
899 * \sin \rho \sin \lambda \cos \omega_0 - \omega)\f]
900 *
901 * where
902 * \f$\lambda\f$ is the angular distance between the model centre
903 * \f$\vec{m}\f$ and the camera pointing direction \f$\vec{d}\f$.
904 * The angle \f$\theta\f$ is used in the computation of the IRF (no
905 * azimuthal dependence is so far implemented for the IRF computation).
906 ***************************************************************************/
907double cta_irf_elliptical_kern_omega::eval(const double& omega)
908{
909 // Initialise IRF value
910 double irf = 0.0;
911
912 // Compute azimuth angle in model coordinate system (radians)
913 double omega_model = omega + m_posangle_obs;
914
915 // Evaluate sky model
916 double model = m_model->eval(m_rho, omega_model, m_srcEng, m_srcTime);
917
918 // Debug: test if model is non positive
919 #if defined(G_DEBUG_MODEL_ZERO)
920 if (model <= 0.0) {
921 double m_semiminor_rad = m_model->semiminor() * gammalib::deg2rad;
922 double m_semimajor_rad = m_model->semimajor() * gammalib::deg2rad;
923 double diff_angle = omega_model - m_model->posangle() * gammalib::deg2rad;
924 double cosinus = std::cos(diff_angle);
925 double sinus = std::sin(diff_angle);
926 double arg1 = m_semiminor_rad * cosinus;
927 double arg2 = m_semimajor_rad * sinus;
928 double r_ellipse = m_semiminor_rad * m_semimajor_rad /
929 std::sqrt(arg1*arg1 + arg2*arg2);
930 std::cout << "*** WARNING: cta_irf_elliptical_kern_omega::eval:";
931 std::cout << " zero model for (rho,omega)=(";
932 std::cout << m_rho*gammalib::rad2deg << ",";
933 std::cout << omega*gammalib::rad2deg << ")";
934 std::cout << " semiminor=" << m_semiminor_rad;
935 std::cout << " semimajor=" << m_semimajor_rad;
936 std::cout << " posang=" << m_model->posangle() * gammalib::deg2rad;
937 std::cout << " rel_posang=" << diff_angle * gammalib::deg2rad;
938 std::cout << " rho/r_ellipse=" << (m_rho/r_ellipse);
939 std::cout << std::endl;
940 }
941 #endif
942
943 // Continue only if model is positive
944 if (model > 0.0) {
945
946 // Get log10(E/TeV) of true photon energy
947 double srcLogEng = m_srcEng.log10TeV();
948
949 // Compute Psf offset angle [radians]
950 double delta = std::acos(m_cos_psf + m_sin_psf * std::cos(omega));
951
952 // Compute true photon offset and azimuth angle in camera system
953 // [radians]
954 double theta = std::acos(m_cos_ph + m_sin_ph * std::cos(m_omega_pnt - omega));
955 double phi = 0.0; //TODO: Implement IRF Phi dependence
956
957 // Evaluate IRF * model
958 irf = m_rsp->aeff(theta, phi, m_zenith, m_azimuth, srcLogEng) *
959 m_rsp->psf(delta, theta, phi, m_zenith, m_azimuth, srcLogEng) *
960 model;
961
962 // Optionally take energy dispersion into account
963 if (m_rsp->use_edisp() && irf > 0.0) {
964 irf *= m_rsp->edisp(m_obsEng, m_srcEng, theta, phi, m_zenith, m_azimuth);
965 }
966
967 // Compile option: Check for NaN/Inf
968 #if defined(G_NAN_CHECK)
970 std::cout << "*** ERROR: cta_irf_elliptical_kern_omega::eval";
971 std::cout << "(omega=" << omega << "):";
972 std::cout << " NaN/Inf encountered";
973 std::cout << " (irf=" << irf;
974 std::cout << ", model=" << model;
975 std::cout << ", delta=" << delta;
976 std::cout << ", theta=" << theta;
977 std::cout << ", phi=" << phi << ")";
978 std::cout << std::endl;
979 }
980 #endif
981
982 } // endif: model is positive
983
984 // Return
985 return irf;
986}
987
988
989/***********************************************************************//**
990 * @brief Kernel for zenith angle Nroi integration of elliptical model
991 *
992 * @param[in] rho Elliptical model offset angle (radians).
993 *
994 * Computes
995 *
996 * \f[
997 * K(\rho | E, t) = \sin \rho \times
998 * \int_{\omega_{\rm min}}^{\omega_{\rm max}}
999 * S_{\rm p}(\rho,\omega | E, t) \,
1000 * N_{\rm pred}(\rho,\omega) d\omega
1001 * \f]
1002 *
1003 * where
1004 * \f$\omega\f$ is the azimuth angle with respect to the model centre,
1005 * counted counterclockwise from the vector connecting the model centre
1006 * to the ROI centre, and
1007 * \f$\rho\f$ is the radial distance from the model centre.
1008 * \f$S_{\rm p}(\rho, \omega | E, t)\f$ is the elliptical source model
1009 * for a given true photon energy and photon arrival time,
1010 * \f$N_{\rm pred}(\rho,\omega)\f$ is the data space integral over the
1011 * response function.
1012 *
1013 * The method performs the required coordinate transformations from the
1014 * model system, spanned by \f$(\rho, \omega)\f$, to the system needed for
1015 * Npred computations. Furthermore, the method limits the integration range
1016 * to area where the ellipse intersects the ROI.
1017 ***************************************************************************/
1018double cta_nroi_elliptical_kern_rho::eval(const double& rho)
1019{
1020 // Initialise Nroi value
1021 double nroi = 0.0;
1022
1023 // Continue only if rho is positive
1024 if (rho > 0.0) {
1025
1026 // Compute half length of arc that lies within ROI+PSF radius (radians)
1027 double domega = 0.5 * gammalib::roi_arclength(rho,
1028 m_rho_roi,
1033
1034 // Continue only if arc length is positive
1035 if (domega > 0.0) {
1036
1037 // Compute sine and cosine terms for azimuthal integration
1038 double sin_rho = std::sin(rho);
1039 double cos_rho = std::cos(rho);
1040
1041 // Reduce rho by an infinite amount to avoid rounding errors
1042 // at the boundary of a sharp edged model (e.g. an elliptical
1043 // disk model)
1044 double rho_kludge = rho - g_ellipse_kludge_radius;
1045 if (rho_kludge < 0.0) {
1046 rho_kludge = 0.0;
1047 }
1048
1049 // Setup phi integration kernel
1051 m_obs,
1052 m_model,
1053 m_rot,
1054 m_srcEng,
1055 m_srcTime,
1056 m_obsEng,
1057 m_obsTime,
1058 rho_kludge,
1059 sin_rho,
1060 cos_rho,
1062
1063 // Setup integrator
1064 GIntegral integral(&integrand);
1065 integral.fixed_iter(m_iter);
1066
1067 // If the radius rho is not larger than the semiminor axis
1068 // boundary, the circle with that radius is fully contained in
1069 // the ellipse and we can just integrate over the relevant arc
1070 if (rho <= m_semiminor) {
1071
1072 // Compute omega integration range
1073 double omega_min = -domega;
1074 double omega_max = +domega;
1075
1076 // Integrate over omega
1077 nroi = integral.romberg(omega_min, omega_max, m_iter) *
1078 sin_rho;
1079
1080 } // endif: circle comprised in ellipse
1081
1082 // ... otherwise there are arcs that intersect with the ROI
1083 // circle
1084 else {
1085
1086 // Compute half the arc length (in radians) of a circle of
1087 // radius rho, centred on the model, that intersects with
1088 // the ellipse boundary
1089 double arg1 = 1.0 - (m_semiminor*m_semiminor) / (rho*rho);
1090 double arg2 = 1.0 - (m_semiminor*m_semiminor) /
1092 double omega_width = std::acos(std::sqrt(arg1/arg2));
1093
1094 // Continue only if the arclength is positive
1095 if (omega_width > 0.0) {
1096
1097 // Compute azimuth angle difference between ellipse
1098 // position angle and position angle of ROI in the model
1099 // system. This angle will define the reference point for
1100 // the circle arcs. Make sure that omega_0 is within [-pi,pi]
1101 // thus that the omega intervals are between [-2pi,2pi]
1102 double omega_0 = m_posangle - m_posangle_roi;
1103 if (omega_0 > gammalib::pi) {
1104 omega_0 -= gammalib::pi;
1105 }
1106 else if (omega_0 < -gammalib::pi) {
1107 omega_0 += gammalib::pi;
1108 }
1109
1110 // Compute azimuth angle intervals
1111 double omega1_min = omega_0 - omega_width;
1112 double omega1_max = omega_0 + omega_width;
1113 double omega2_min = omega1_min + gammalib::pi;
1114 double omega2_max = omega1_max + gammalib::pi;
1115
1116 // Limit intervals to the intersection of the ellipse with
1117 // the Psf circle. This may lead to a split of intervals,
1118 // and we gather all these intervals in a special interval
1119 // pair containers
1120 cta_omega_intervals intervals1 =
1121 gammalib::limit_omega(omega1_min, omega1_max, domega);
1122 cta_omega_intervals intervals2 =
1123 gammalib::limit_omega(omega2_min, omega2_max, domega);
1124
1125 // Integrate over all intervals for omega1
1126 for (int i = 0; i < intervals1.size(); ++i) {
1127 double min = intervals1[i].first;
1128 double max = intervals1[i].second;
1129 nroi += integral.romberg(min, max, m_iter) * sin_rho;
1130 }
1131
1132 // Integrate over all intervals for omega2
1133 for (int i = 0; i < intervals2.size(); ++i) {
1134 double min = intervals2[i].first;
1135 double max = intervals2[i].second;
1136 nroi += integral.romberg(min, max, m_iter) * sin_rho;
1137 }
1138
1139 } // endif: arc length was positive
1140
1141 } // endelse: circle was not comprised in ellipse
1142
1143 // Debug: Check for NaN
1144 #if defined(G_NAN_CHECK)
1146 std::cout << "*** ERROR: cta_nroi_elliptical_kern_rho::eval";
1147 std::cout << "(rho=" << rho << "):";
1148 std::cout << " NaN/Inf encountered";
1149 std::cout << " (nroi=" << nroi;
1150 std::cout << ", sin_rho=" << sin_rho;
1151 std::cout << ", cos_rho=" << cos_rho;
1152 std::cout << ")" << std::endl;
1153 }
1154 #endif
1155
1156 } // endif: arc length was positive
1157
1158 } // endif: rho was positive
1159
1160 // Return Nroi
1161 return nroi;
1162}
1163
1164
1165/***********************************************************************//**
1166 * @brief Kernel for azimuth angle Nroi integration of elliptical model
1167 *
1168 * @param[in] omega Azimuth angle (radians).
1169 *
1170 * Computes
1171 *
1172 * \f[
1173 * S_{\rm p}(\omega | \rho, E, t) \, N_{\rm pred}(\omega | \rho)
1174 * \f]
1175 *
1176 * where
1177 * \f$\omega\f$ is the azimuth angle with respect to the model centre,
1178 * counted counterclockwise from the vector connecting the model centre
1179 * to the centre of the Region of Interest (ROI), and
1180 * \f$\rho\f$ is the radial distance from the model centre.
1181 *
1182 * @todo Npred computation goes over sky coordinates. This can maybe be
1183 * optimized to reduce the number of coordinate transformations.
1184 * @todo Check whether the Npred omega argument is the right one.
1185 ***************************************************************************/
1186double cta_nroi_elliptical_kern_omega::eval(const double& omega)
1187{
1188 // Initialise Nroi value
1189 double nroi = 0.0;
1190
1191 // Compute azimuth angle in model coordinate system (radians)
1192 double omega_model = omega + m_posangle_roi;
1193
1194 // Evaluate sky model
1195 double model = m_model->eval(m_rho, omega_model, m_srcEng, m_srcTime);
1196
1197 // Debug: test if model is non positive
1198 #if defined(G_DEBUG_MODEL_ZERO)
1199 if (model <= 0.0) {
1200 double m_semiminor_rad = m_model->semiminor() * gammalib::deg2rad;
1201 double m_semimajor_rad = m_model->semimajor() * gammalib::deg2rad;
1202 double diff_angle = omega_model - m_model->posangle() * gammalib::deg2rad;
1203 double cosinus = std::cos(diff_angle);
1204 double sinus = std::sin(diff_angle);
1205 double arg1 = m_semiminor_rad * cosinus;
1206 double arg2 = m_semimajor_rad * sinus;
1207 double r_ellipse = m_semiminor_rad * m_semimajor_rad /
1208 std::sqrt(arg1*arg1 + arg2*arg2);
1209 std::cout << "*** WARNING: cta_nroi_elliptical_kern_omega::eval";
1210 std::cout << " zero model for (rho,omega)=(";
1211 std::cout << m_rho*gammalib::rad2deg << ",";
1212 std::cout << omega*gammalib::rad2deg << ")";
1213 std::cout << " rho-r_ellipse=" << (m_rho-r_ellipse) << " radians";
1214 std::cout << std::endl;
1215 }
1216 #endif
1217
1218 // Continue only if model is positive
1219 if (model > 0.0) {
1220
1221 // Compute sky direction vector in native coordinates
1222 double cos_omega = std::cos(omega_model);
1223 double sin_omega = std::sin(omega_model);
1224 GVector native(-cos_omega*m_sin_rho, sin_omega*m_sin_rho, m_cos_rho);
1225
1226 // Rotate from native into celestial system
1227 GVector cel = *m_rot * native;
1228
1229 // Set sky direction
1230 GSkyDir srcDir;
1231 srcDir.celvector(cel);
1232
1233 // Set Photon
1234 GPhoton photon(srcDir, m_srcEng, m_srcTime);
1235
1236 // Compute Nroi for this sky direction
1237 nroi = m_rsp->nirf(photon, m_obsEng, m_obsTime, *m_obs) * model;
1238
1239 // Debug: Check for NaN
1240 #if defined(G_NAN_CHECK)
1242 std::cout << "*** ERROR: cta_nroi_elliptical_kern_omega::eval";
1243 std::cout << "(omega=" << omega << "):";
1244 std::cout << " NaN/Inf encountered";
1245 std::cout << " (nroi=" << nroi;
1246 std::cout << ", model=" << model;
1247 std::cout << ", cos_omega=" << cos_omega;
1248 std::cout << ", sin_omega=" << sin_omega;
1249 std::cout << ")" << std::endl;
1250 }
1251 #endif
1252
1253 } // endif: sky intensity was positive
1254
1255 // Return Nroi
1256 return nroi;
1257}
1258
1259
1260/*==========================================================================
1261 = =
1262 = Helper class methods for diffuse models =
1263 = =
1264 ==========================================================================*/
1265
1266/***********************************************************************//**
1267 * @brief Kernel for IRF offest angle integration of the diffuse source model
1268 *
1269 * @param[in] theta Offset angle with respect to observed photon direction
1270 * (radians).
1271 *
1272 * Computes
1273 *
1274 * \f[
1275 * K(\theta | E, t) = \sin \theta \times PSF(\theta)
1276 * \int_{0}^{2\pi}
1277 * S_{\rm p}(\theta, \phi | E, t) \,
1278 * Aeff(\theta, \phi) \,
1279 * Edisp(\theta, \phi) d\phi
1280 * \f]
1281 *
1282 * The PSF is assumed to be azimuthally symmetric, hence the PSF is computed
1283 * outside the azimuthal integration.
1284 *
1285 * Note that the integration is only performed for \f$\theta>0\f$. Otherwise
1286 * zero is returned.
1287 ***************************************************************************/
1288double cta_irf_diffuse_kern_theta::eval(const double& theta)
1289{
1290 // Initialise result
1291 double irf = 0.0;
1292
1293 // Continue only if offset angle is positive
1294 if (theta > 0.0) {
1295
1296 // Get PSF value. We can do this externally to the azimuthal
1297 // integration as the PSF is so far azimuthally symmetric. Once
1298 // we introduce asymmetries, we have to move this done into the
1299 // Phi kernel method/
1300 double psf = m_rsp->psf(theta, m_theta, m_phi, m_zenith, m_azimuth, m_srcLogEng);
1301
1302 // Continue only if PSF is positive
1303 if (psf > 0.0) {
1304
1305 // Precompute terms needed for the computation of the angular
1306 // distance between the true photon direction and the pointing
1307 // direction (i.e. the camera centre)
1308 double sin_theta = std::sin(theta);
1309 double cos_theta = std::cos(theta);
1310 double sin_ph = sin_theta * m_sin_eta;
1311 double cos_ph = cos_theta * m_cos_eta;
1312
1313 // Setup kernel for azimuthal integration
1315 m_model,
1316 m_rot,
1317 m_zenith,
1318 m_azimuth,
1319 m_srcEng,
1320 m_srcTime,
1322 m_obsEng,
1323 sin_theta,
1324 cos_theta,
1325 sin_ph,
1326 cos_ph);
1327
1328 // Set number of azimuthal integration iterations
1329 int iter = gammalib::iter_phi(theta, m_resolution,
1331
1332 // Integrate over phi
1333 GIntegral integral(&integrand);
1334 integral.fixed_iter(iter);
1335 irf = integral.romberg(0.0, gammalib::twopi) * psf * sin_theta;
1336
1337 // Compile option: Debugging
1338 #if defined(G_DEBUG_INTEGRAL)
1339 if (!integral.isvalid()) {
1340 std::cout << "cta_irf_diffuse_kern_theta(theta=";
1341 std::cout << theta*gammalib::rad2deg << " deg) psf=";
1342 std::cout << psf << ":" << std::endl;
1343 std::cout << integral.print() << std::endl;
1344 }
1345 #endif
1346
1347 // Compile option: Check for NaN/Inf
1348 #if defined(G_NAN_CHECK)
1350 std::cout << "*** ERROR: cta_irf_diffuse_kern_theta";
1351 std::cout << "(theta=" << theta << "):";
1352 std::cout << " NaN/Inf encountered";
1353 std::cout << " (irf=" << irf;
1354 std::cout << ", psf=" << psf;
1355 std::cout << ", sin_theta=" << sin_theta;
1356 std::cout << ", cos_theta=" << cos_theta;
1357 std::cout << ", sin_ph=" << sin_ph;
1358 std::cout << ", cos_ph=" << cos_ph;
1359 std::cout << ")";
1360 std::cout << std::endl;
1361 }
1362 #endif
1363
1364 } // endif: PSF was positive
1365
1366 } // endif: offset angle was positive
1367
1368 // Return result
1369 return irf;
1370}
1371
1372
1373/***********************************************************************//**
1374 * @brief Kernel for IRF azimuth angle integration of the diffuse source model
1375 *
1376 * @param[in] phi Azimuth angle around observed photon direction (radians).
1377 *
1378 * Computes
1379 *
1380 * \f[
1381 * S_{\rm p}(\theta, \phi | E, t) \,
1382 * Aeff(\theta, \phi) \,
1383 * Edisp(\theta, \phi)
1384 * \f]
1385 *
1386 * As the coordinates \f$(\theta, \phi)\f$ are given in the reference frame
1387 * of the observed photon direction, some coordinate transformations have
1388 * to be performed.
1389 *
1390 * First, \f$(\theta, \phi)\f$ are transformed into the celestial reference
1391 * frame using the rotation matrix.
1392 *
1393 * Then, the offset angle of the true photon direction is computed in the
1394 * camera system (so far we do not compute the azimuth angle as we assume an
1395 * azimuthally symmetric response).
1396 *
1397 * @todo Optimize computation of sky direction in native coordinates
1398 * @todo Implement azimuth angle computation of true photon in camera
1399 * @todo Replace (theta,phi) by (delta,alpha)
1400 ***************************************************************************/
1401double cta_irf_diffuse_kern_phi::eval(const double& phi)
1402{
1403 // Initialise result
1404 double irf = 0.0;
1405
1406 // Compute sine and cosine of azimuth angle
1407 double sin_phi = std::sin(phi);
1408 double cos_phi = std::cos(phi);
1409
1410 // Compute sky direction vector in native coordinates
1411 m_native[0] = -cos_phi * m_sin_theta;
1412 m_native[1] = sin_phi * m_sin_theta;
1413 m_native[2] = m_cos_theta;
1414
1415 // Rotate from native into celestial system
1416 GVector cel = *m_rot * m_native;
1417
1418 // Set sky direction
1419 GSkyDir srcDir;
1420 srcDir.celvector(cel);
1421
1422 // Set photon
1423 m_photon.dir(srcDir);
1426
1427 // Get sky intensity for photon
1428 double intensity = m_model->eval(m_photon);
1429
1430 // Continue only if sky intensity is positive
1431 if (intensity > 0.0) {
1432
1433 // Compute true photon offset angle in camera system [radians]
1434 double offset = std::acos(m_cos_ph + m_sin_ph * cos_phi);
1435
1436 //TODO: Compute true photon azimuth angle in camera system [radians]
1437 double azimuth = 0.0;
1438
1439 // Evaluate model times the effective area
1440 irf = intensity *
1441 m_rsp->aeff(offset, azimuth, m_zenith, m_azimuth, m_srcLogEng);
1442
1443 // Optionally take energy dispersion into account
1444 if (m_rsp->use_edisp() && irf > 0.0) {
1445 irf *= m_rsp->edisp(m_obsEng, m_srcEng, offset, azimuth,
1447 }
1448
1449 // Compile option: Check for NaN/Inf
1450 #if defined(G_NAN_CHECK)
1452 std::cout << "*** ERROR: cta_irf_diffuse_kern_phi::eval";
1453 std::cout << "(phi=" << phi << "):";
1454 std::cout << " NaN/Inf encountered";
1455 std::cout << " (irf=" << irf;
1456 std::cout << ", intensity=" << intensity;
1457 std::cout << ", offset=" << offset;
1458 std::cout << ", azimuth=" << azimuth;
1459 std::cout << ")";
1460 std::cout << std::endl;
1461 }
1462 #endif
1463
1464 } // endif: sky intensity was positive
1465
1466 // Return
1467 return irf;
1468}
1469
1470
1471/***********************************************************************//**
1472 * @brief Kernel for Nroi offest angle integration of diffuse model
1473 *
1474 * @param[in] theta Offset angle with respect to ROI centre (radians).
1475 *
1476 * Computes
1477 *
1478 * \f[
1479 * K(\theta | E, t) = \sin \theta \times
1480 * \int_{0}^{2\pi}
1481 * S_{\rm p}(\theta, \phi | E, t) \,
1482 * N_{\rm pred}(\theta, \phi) d\phi
1483 * \f]
1484 *
1485 * This method integrates a diffuse model for a given offset angle with
1486 * respect to the ROI centre over all azimuth angles (from 0 to 2pi). The
1487 * integration is only performed for positive offset angles, otherwise 0 is
1488 * returned.
1489 *
1490 * Integration is done using the Romberg algorithm. The integration kernel
1491 * is defined by the helper class cta_npred_diffuse_kern_phi.
1492 *
1493 * Note that the integration precision was adjusted trading-off between
1494 * computation time and computation precision. A value of 1e-4 was judged
1495 * appropriate.
1496 ***************************************************************************/
1497double cta_nroi_diffuse_kern_theta::eval(const double& theta)
1498{
1499 // Initialise Nroi value
1500 double nroi = 0.0;
1501
1502 // Continue only if offset angle is positive
1503 if (theta > 0.0) {
1504
1505 // Compute sine of offset angle
1506 double sin_theta = std::sin(theta);
1507
1508 // Setup phi integration kernel
1510 m_obs,
1511 m_model,
1512 m_rot,
1513 m_srcEng,
1514 m_srcTime,
1515 m_obsEng,
1516 m_obsTime,
1517 theta,
1518 sin_theta);
1519
1520 // Integrate over phi
1521 GIntegral integral(&integrand);
1522 integral.fixed_iter(m_iter);
1523 nroi = integral.romberg(0.0, gammalib::twopi, m_iter) * sin_theta;
1524 #if defined(G_DEBUG_INTEGRAL)
1525 if (!integral.isvalid()) {
1526 std::cout << "cta_nroi_diffuse_kern_theta(theta=";
1527 std::cout << theta*gammalib::rad2deg << " deg):" << std::endl;
1528 std::cout << integral.print() << std::endl;
1529 }
1530 #endif
1531
1532 // Debug: Check for NaN
1533 #if defined(G_NAN_CHECK)
1535 std::cout << "*** ERROR: cta_nroi_radial_kern_theta::eval";
1536 std::cout << "(theta=" << theta << "):";
1537 std::cout << " NaN/Inf encountered";
1538 std::cout << " (nroi=" << nroi;
1539 std::cout << ", sin_theta=" << sin_theta;
1540 std::cout << ")" << std::endl;
1541 }
1542 #endif
1543
1544 } // endif: offset angle was positive
1545
1546 // Return Nroi
1547 return nroi;
1548}
1549
1550
1551/***********************************************************************//**
1552 * @brief Kernel for Nroi azimuth angle integration of diffuse model
1553 *
1554 * @param[in] phi Azimuth angle with respect to ROI centre (radians).
1555 *
1556 * Computes
1557 *
1558 * \f[
1559 * S_{\rm p}(\theta, \phi | E, t) \, N_{\rm pred}(\theta, \phi)
1560 * \f]
1561 *
1562 * @todo Re-consider formula for possible simplification (dumb matrix
1563 * multiplication is definitely not the fastest way to do that
1564 * computation).
1565 ***************************************************************************/
1566double cta_nroi_diffuse_kern_phi::eval(const double& phi)
1567{
1568 // Initialise Nroi value
1569 double nroi = 0.0;
1570
1571 // Compute sky direction vector in native coordinates
1572 double cos_phi = std::cos(phi);
1573 double sin_phi = std::sin(phi);
1574 GVector native(-cos_phi*m_sin_theta, sin_phi*m_sin_theta, m_cos_theta);
1575
1576 // Rotate from native into celestial system
1577 GVector cel = *m_rot * native;
1578
1579 // Set sky direction
1580 GSkyDir srcDir;
1581 srcDir.celvector(cel);
1582
1583 // Set Photon
1584 GPhoton photon(srcDir, m_srcEng, m_srcTime);
1585
1586 // Get sky intensity for this photon
1587 double intensity = m_model->eval(photon);
1588
1589 // Continue only if sky intensity is positive
1590 if (intensity > 0.0) {
1591
1592 // Compute Nroi for this sky direction
1593 nroi = m_rsp->nirf(photon, m_obsEng, m_obsTime, *m_obs) * intensity;
1594
1595 // Debug: Check for NaN
1596 #if defined(G_NAN_CHECK)
1598 std::cout << "*** ERROR: cta_nroi_diffuse_kern_phi::eval";
1599 std::cout << "(phi=" << phi << "):";
1600 std::cout << " NaN/Inf encountered";
1601 std::cout << " (nroi=" << nroi;
1602 std::cout << ", intensity=" << intensity;
1603 std::cout << ", cos_phi=" << cos_phi;
1604 std::cout << ", sin_phi=" << sin_phi;
1605 std::cout << ")" << std::endl;
1606 }
1607 #endif
1608
1609 } // endif: sky intensity was positive
1610
1611 // Return Nroi
1612 return nroi;
1613}
1614
1615
1616/*==========================================================================
1617 = =
1618 = Helper class methods for stacked analysis =
1619 = =
1620 ==========================================================================*/
1621
1622/***********************************************************************//**
1623 * @brief Kernel for radial model integration over zenith angle
1624 *
1625 * @param[in] rho Radial distance from model centre (radians).
1626 * @return Integration kernel.
1627 *
1628 * Computes the integration kernel
1629 *
1630 * \f[
1631 * K(\rho | E, t) = \sin \rho \times
1632 * S_{\rm p}(\rho | E, t) \times
1633 * \int_{\omega} PSF(\rho, \omega) d\omega
1634 * \f]
1635 *
1636 * where
1637 * \f$\omega\f$ is the azimuth angle with respect to the model centre,
1638 * counted counterclockwise from the vector connecting the model centre
1639 * to the observed photon direction, and
1640 * \f$\rho\f$ is the radial distance from the model centre.
1641 * \f$S_{\rm p}(\rho | E, t)\f$ is the radial source model
1642 * for a given true photon energy and photon arrival time,
1643 * \f$PSF(\rho, \omega)\f$ is the point spread function.
1644 ***************************************************************************/
1645double cta_psf_radial_kern_rho::eval(const double& rho)
1646{
1647 // Initialise result
1648 double irf = 0.0;
1649
1650 // Continue only if rho is positive
1651 if (rho > 0.0) {
1652
1653 // Compute half length of the arc (in radians) from a circle with
1654 // radius rho that intersects with the point spread function, defined
1655 // as a circle with maximum radius m_delta_max
1656 double domega = 0.5 * gammalib::roi_arclength(rho,
1657 m_rho_obs,
1662
1663 // Continue only if arc length is positive
1664 if (domega > 0.0) {
1665
1666 // Reduce rho by an infinite amount to avoid rounding errors
1667 // at the boundary of a sharp edged model
1668 double rho_kludge = rho - g_kludge_radius;
1669 if (rho_kludge < 0.0) {
1670 rho_kludge = 0.0;
1671 }
1672
1673 // Evaluate sky model
1674 double model = m_model->eval(rho_kludge, m_srcEng, m_srcTime);
1675
1676 // Debug: test if model is non positive
1677 #if defined(G_DEBUG_MODEL_ZERO)
1678 if (model <= 0.0) {
1679 std::cout << "*** WARNING: cta_psf_radial_kern_rho::eval";
1680 std::cout << " zero model for (rho)=(";
1681 std::cout << rho*gammalib::rad2deg << ")";
1682 std::cout << " rho-r_model=" << (rho-m_model->theta_max());
1683 std::cout << " radians" << std::endl;
1684 }
1685 #endif
1686
1687 // Continue only if model is positive
1688 if (model > 0.0) {
1689
1690 // Compute omega integration range
1691 double omega_min = -domega;
1692 double omega_max = +domega;
1693
1694 // Precompute cosine and sine terms for azimuthal integration
1695 double cos_rho = std::cos(rho);
1696 double sin_rho = std::sin(rho);
1697 double cos_psf = cos_rho * m_cos_rho_obs;
1698 double sin_psf = sin_rho * m_sin_rho_obs;
1699
1700 // Setup integration kernel
1702 m_model,
1703 m_srcDir,
1704 m_srcEng,
1705 m_srcTime,
1706 cos_psf,
1707 sin_psf);
1708
1709 // Setup integrator
1710 GIntegral integral(&integrand);
1711 integral.fixed_iter(m_iter);
1712
1713 // Integrate over omega
1714 irf = integral.romberg(omega_min, omega_max, m_iter) *
1715 model * sin_rho;
1716
1717 // Compile option: Check for NaN/Inf
1718 #if defined(G_NAN_CHECK)
1720 std::cout << "*** ERROR: cta_psf_radial_kern_rho";
1721 std::cout << "(rho=" << rho << "):";
1722 std::cout << " NaN/Inf encountered";
1723 std::cout << " (irf=" << irf;
1724 std::cout << ", domega=" << domega;
1725 std::cout << ", model=" << model;
1726 std::cout << ", sin_rho=" << sin_rho << ")";
1727 std::cout << std::endl;
1728 }
1729 #endif
1730
1731 } // endif: model was positive
1732
1733 } // endif: arclength was positive
1734
1735 } // endif: rho was positive
1736
1737 // Return result
1738 return irf;
1739}
1740
1741
1742/***********************************************************************//**
1743 * @brief Kernel for radial model integration over azimuth angle
1744 *
1745 * @param[in] omega Azimuth angle (radians).
1746 *
1747 * Computes
1748 *
1749 * \f[
1750 * K(\omega | \rho, E, t) = PSF(\omega | \rho)
1751 * \f]
1752 *
1753 * where
1754 * \f$\omega\f$ is the azimuth angle with respect to the model centre,
1755 * counted counterclockwise from the vector connecting the model centre
1756 * to the observed photon direction, and
1757 * \f$\rho\f$ is the radial distance from the model centre.
1758 *
1759 * From the coordinates \f$(\rho,\omega)\f$ in the model system, the method
1760 * computes the angle between the true (\f$\vec{p}\f$) and observed
1761 * (\f$\vec{p'}\f$) photon arrival direction using
1762 *
1763 * \f[\delta = \arccos(\cos \rho \cos \rho_{\rm obs} +
1764 * \sin \rho \sin \rho_{\rm obs} \cos \omega)\f]
1765 *
1766 * where
1767 * \f$\rho_{\rm obs}\f$ is the angular distance between the observed photon
1768 * arrival direction \f$\vec{p'}\f$ and the model centre \f$\vec{m}\f$.
1769 * \f$\delta\f$ is used to compute the value of the point spread function.
1770 ***************************************************************************/
1771double cta_psf_radial_kern_omega::eval(const double& omega)
1772{
1773 // Compute Psf offset angle (radians)
1774 double delta = std::acos(m_cos_psf + m_sin_psf * std::cos(omega));
1775
1776 // Evaluate Psf * model for this delta
1777 double irf = m_rsp->psf()(m_srcDir, delta, m_srcEng);
1778
1779 // Compile option: Check for NaN/Inf
1780 #if defined(G_NAN_CHECK)
1782 std::cout << "*** ERROR: cta_psf_radial_kern_omega::eval";
1783 std::cout << "(omega=" << omega << "):";
1784 std::cout << " NaN/Inf encountered";
1785 std::cout << " (irf=" << irf;
1786 std::cout << ", delta=" << delta;
1787 std::cout << ", omega=" << omega << ")";
1788 std::cout << std::endl;
1789 }
1790 #endif
1791
1792 // Return
1793 return irf;
1794}
1795
1796
1797/***********************************************************************//**
1798 * @brief Kernel for PSF integration of radial model
1799 *
1800 * @param[in] delta PSF offset angle (radians).
1801 * @return Azimuthally integrated product between PSF and radial model.
1802 *
1803 * Computes the azimuthally integrated product of point spread function and
1804 * the radial model intensity. As the PSF is azimuthally symmetric, it is
1805 * not included in the azimuthally integration, but just multiplied on the
1806 * azimuthally integrated model. The method returns thus
1807 *
1808 * \f[
1809 * {\rm PSF}(\delta) \times
1810 * \int_0^{2\pi} {\rm M}(\delta, \phi) \sin \delta {\rm d}\phi
1811 * \f]
1812 *
1813 * where \f${\rm M}(\delta, \phi)\f$ is the radial model in the coordinate
1814 * system of the point spread function, defined by the angle \f$\delta\f$
1815 * between the true and the measured photon direction and the azimuth angle
1816 * \f$\phi\f$ around the measured photon direction.
1817 ***************************************************************************/
1818double cta_psf_radial_kern_delta::eval(const double& delta)
1819{
1820 // Initialise value
1821 double value = 0.0;
1822
1823 // If we're at the Psf peak the model is zero (due to the sin(delta)
1824 // term. We thus only integrate for positive deltas.
1825 if (delta > 0.0) {
1826
1827 // Get Psf for this delta
1828 double psf = m_rsp->psf()(m_srcDir, delta, m_srcEng);
1829
1830 // Continue only if Psf is positive
1831 if (psf > 0.0) {
1832
1833 // Compute half length of the arc (in radians) from a circle with
1834 // radius delta that intersects with the model, defined as a circle
1835 // with maximum radius m_theta_max
1836 double dphi = 0.5 * gammalib::roi_arclength(delta,
1842
1843 // Continue only if arc length is positive
1844 if (dphi > 0.0) {
1845
1846 // Compute phi integration range
1847 double phi_min = -dphi;
1848 double phi_max = +dphi;
1849
1850 // Precompute cosine and sine terms for azimuthal integration
1851 double sin_delta = std::sin(delta);
1852 double cos_delta = std::cos(delta);
1853 double sin_fact = sin_delta * m_sin_delta_mod;
1854 double cos_fact = cos_delta * m_cos_delta_mod;
1855
1856 // Setup kernel for azimuthal integration of the spatial model
1858 m_srcEng,
1859 m_srcTime,
1860 sin_fact,
1861 cos_fact);
1862
1863 // Setup integrator
1864 GIntegral integral(&integrand);
1865 integral.fixed_iter(m_iter);
1866
1867 // Integrate over azimuth
1868 value = integral.romberg(phi_min, phi_max, m_iter) *
1869 psf * sin_delta;
1870
1871 // Debug: Check for NaN
1872 #if defined(G_NAN_CHECK)
1873 if (gammalib::is_notanumber(value) || gammalib::is_infinite(value)) {
1874 std::cout << "*** ERROR: cta_psf_radial_kern_delta::eval";
1875 std::cout << "(delta=" << delta << "):";
1876 std::cout << " NaN/Inf encountered";
1877 std::cout << " (value=" << value;
1878 std::cout << ")" << std::endl;
1879 }
1880 #endif
1881
1882 } // endif: Psf value was positive
1883
1884 } // endif: arc length was positive
1885
1886 } // endif: delta was positive
1887
1888 // Return kernel value
1889 return value;
1890}
1891
1892
1893/***********************************************************************//**
1894 * @brief Kernel for azimuthal radial model integration
1895 *
1896 * @param[in] phi Azimuth angle (radians).
1897 * @return Radial model value.
1898 *
1899 * Computes the value of the radial model at the position \f$(\delta,\phi)\f$
1900 * given in point spread function coordinates. The \f$\theta\f$ angle of the
1901 * radial model is computed using
1902 *
1903 * \f[
1904 * \theta = \arccos \left( \cos \delta \cos \zeta +
1905 * \sin \delta \sin \zeta \cos \phi \right)
1906 * \f]
1907 *
1908 * where \f$\delta\f$ is the angle between true and measured photon
1909 * direction, \f$\zeta\f$ is the angle between model centre and measured
1910 * photon direction, and \f$\phi\f$ is the azimuth angle with respect to the
1911 * measured photon direction, where \f$\phi=0\f$ corresponds to the
1912 * connecting line between model centre and measured photon direction.
1913 ***************************************************************************/
1914double cta_psf_radial_kern_phi::eval(const double& phi)
1915{
1916 // Compute radial model theta angle
1917 double theta = std::acos(m_cos_fact + m_sin_fact * std::cos(phi));
1918
1919 // Reduce theta by an infinite amount to avoid rounding errors at the
1920 // boundary of a sharp edged model
1921 double theta_kluge = theta - 1.0e-12;
1922 if (theta_kluge < 0.0) {
1923 theta_kluge = 0.0;
1924 }
1925
1926 // Get radial model value
1927 double value = m_model->eval(theta_kluge, m_srcEng, m_srcTime);
1928
1929 // Debug: test if model is non positive
1930 #if defined(G_DEBUG_MODEL_ZERO)
1931 if (value <= 0.0) {
1932 std::cout << "*** WARNING: cta_psf_radial_kern_phi::eval";
1933 std::cout << " zero model for (phi)=(";
1934 std::cout << phi*gammalib::rad2deg << ")";
1935 std::cout << " theta-r_model=" << (theta-m_model->theta_max());
1936 std::cout << " radians" << std::endl;
1937 }
1938 #endif
1939
1940 // Debug: Check for NaN
1941 #if defined(G_NAN_CHECK)
1942 if (gammalib::is_notanumber(value) || gammalib::is_infinite(value)) {
1943 std::cout << "*** ERROR: cta_psf_radial_kern_phi::eval";
1944 std::cout << "(phi=" << phi << "):";
1945 std::cout << " NaN/Inf encountered";
1946 std::cout << " (value=" << value;
1947 std::cout << ")" << std::endl;
1948 }
1949 #endif
1950
1951 // Return kernel value
1952 return value;
1953}
1954
1955
1956/***********************************************************************//**
1957 * @brief Kernel for elliptical model integration over zenith angle
1958 *
1959 * @param[in] rho Radial distance from model centre (radians).
1960 * @return Integration kernel.
1961 *
1962 * Computes the integration kernel
1963 *
1964 * \f[
1965 * K(\rho | E, t) = \sin \rho \times
1966 * \int_{\omega}
1967 * S_{\rm p}(\rho, \omega | E, t) \, PSF(\rho, \omega)
1968 * d\omega
1969 * \f]
1970 *
1971 * where
1972 * \f$\omega\f$ is the azimuth angle with respect to the model centre,
1973 * counted counterclockwise from the vector connecting the model centre
1974 * to the observed photon direction, and
1975 * \f$\rho\f$ is the radial distance from the model centre.
1976 * \f$S_{\rm p}(\rho, \omega | E, t)\f$ is the elliptical source model
1977 * for a given true photon energy and photon arrival time,
1978 * \f$PSF(\rho, \omega)\f$ is the point spread function.
1979 * The integration is over the \f$\omega\f$ values that are comprised within
1980 * the ellipse boundaries.
1981 ***************************************************************************/
1982double cta_psf_elliptical_kern_rho::eval(const double& rho)
1983{
1984 // Initialise result
1985 double irf = 0.0;
1986
1987 // Continue only if rho is positive
1988 if (rho > 0.0) {
1989
1990 // Compute half length of the arc (in radians) from a circle with
1991 // radius rho that intersects with the point spread function, defined
1992 // as a circle with maximum radius m_delta_max
1993 double domega = 0.5 * gammalib::roi_arclength(rho,
1994 m_rho_obs,
1999
2000 // Continue only if arc length is positive
2001 if (domega > 0.0) {
2002
2003 // Precompute cosine and sine terms for azimuthal integration
2004 double cos_rho = std::cos(rho);
2005 double sin_rho = std::sin(rho);
2006 double cos_psf = cos_rho * m_cos_rho_obs;
2007 double sin_psf = sin_rho * m_sin_rho_obs;
2008
2009 // Reduce rho by an infinite amount to avoid rounding errors
2010 // at the boundary of a sharp edged model (e.g. an elliptical
2011 // disk model)
2012 double rho_kludge = rho - g_ellipse_kludge_radius;
2013 if (rho_kludge < 0.0) {
2014 rho_kludge = 0.0;
2015 }
2016
2017 // Setup integration kernel
2019 m_model,
2020 m_srcDir,
2021 m_srcEng,
2022 m_srcTime,
2024 rho_kludge,
2025 cos_psf,
2026 sin_psf);
2027
2028 // Setup integrator
2029 GIntegral integral(&integrand);
2030 integral.fixed_iter(m_iter);
2031
2032 // If the radius rho is not larger than the semiminor axis
2033 // boundary, the circle with that radius is fully contained in
2034 // the ellipse and we can just integrate over the relevant arc
2035 if (rho <= m_semiminor) {
2036
2037 // Compute omega integration range
2038 double omega_min = -domega;
2039 double omega_max = +domega;
2040
2041 // Integrate over omega
2042 irf = integral.romberg(omega_min, omega_max, m_iter) *
2043 sin_rho;
2044
2045 } // endif: circle comprised in ellipse
2046
2047 // ... otherwise there are arcs that intersect with the Psf circle
2048 else {
2049
2050 // Compute half the arc length (in radians) of a circle of
2051 // radius rho, centred on the model, that intersects with
2052 // the ellipse boundary
2053 double arg1 = 1.0 - (m_semiminor*m_semiminor) / (rho*rho);
2054 double arg2 = 1.0 - (m_semiminor*m_semiminor) /
2056 double omega_width = std::acos(std::sqrt(arg1/arg2));
2057
2058 // Continue only if the arclength is positive
2059 if (omega_width > 0.0) {
2060
2061 // Compute azimuth angle difference between ellipse
2062 // position angle and position angle of observed
2063 // photon in the model system. This angle will define
2064 // the reference point around which the circle arc. Make
2065 // sure that omega_0 is within [-pi,pi] thus that the omega
2066 // intervals are between [-2pi,2pi]
2067 double omega_0 = m_posangle - m_posangle_obs;
2068 if (omega_0 > gammalib::pi) {
2069 omega_0 -= gammalib::pi;
2070 }
2071 else if (omega_0 < -gammalib::pi) {
2072 omega_0 += gammalib::pi;
2073 }
2074
2075 // Compute azimuth angle intervals
2076 double omega1_min = omega_0 - omega_width;
2077 double omega1_max = omega_0 + omega_width;
2078 double omega2_min = omega1_min + gammalib::pi;
2079 double omega2_max = omega1_max + gammalib::pi;
2080
2081 // Limit intervals to the intersection of the ellipse with
2082 // the Psf circle. This may lead to a split of intervals,
2083 // and we gather all these intervals in a special interval
2084 // pair containers
2085 cta_omega_intervals intervals1 =
2086 gammalib::limit_omega(omega1_min, omega1_max, domega);
2087 cta_omega_intervals intervals2 =
2088 gammalib::limit_omega(omega2_min, omega2_max, domega);
2089
2090 // Integrate over all intervals for omega1
2091 for (int i = 0; i < intervals1.size(); ++i) {
2092 double min = intervals1[i].first;
2093 double max = intervals1[i].second;
2094 irf += integral.romberg(min, max, m_iter) * sin_rho;
2095 }
2096
2097 // Integrate over all intervals for omega2
2098 for (int i = 0; i < intervals2.size(); ++i) {
2099 double min = intervals2[i].first;
2100 double max = intervals2[i].second;
2101 irf += integral.romberg(min, max, m_iter) * sin_rho;
2102 }
2103
2104 } // endif: arc length was positive
2105
2106 } // endelse: circle was not comprised in ellipse
2107
2108 // Compile option: Check for NaN/Inf
2109 #if defined(G_NAN_CHECK)
2111 std::cout << "*** ERROR: cta_psf_elliptical_kern_rho";
2112 std::cout << "(rho=" << rho << "):";
2113 std::cout << " NaN/Inf encountered";
2114 std::cout << " (irf=" << irf;
2115 std::cout << ", domega=" << domega;
2116 std::cout << ", sin_rho=" << sin_rho << ")";
2117 std::cout << std::endl;
2118 }
2119 #endif
2120
2121 } // endif: arc length was positive
2122
2123 } // endif: rho was positive
2124
2125 // Return result
2126 return irf;
2127}
2128
2129
2130/***********************************************************************//**
2131 * @brief Kernel for elliptical model integration over azimuth angle
2132 *
2133 * @param[in] omega Azimuth angle (radians).
2134 *
2135 * Computes
2136 *
2137 * \f[
2138 * K(\omega | \rho, E, t) = S_{\rm p}(\omega | \rho, E, t) \,
2139 * PSF(\omega | \rho)
2140 * \f]
2141 *
2142 * where
2143 * \f$\omega\f$ is the azimuth angle with respect to the model centre,
2144 * counted counterclockwise from the vector connecting the model centre
2145 * to the observed photon direction, and
2146 * \f$\rho\f$ is the radial distance from the model centre.
2147 *
2148 * From the coordinates \f$(\rho,\omega)\f$ in the model system, the method
2149 * computes the angle between the true (\f$\vec{p}\f$) and observed
2150 * (\f$\vec{p'}\f$) photon arrival direction using
2151 *
2152 * \f[\delta = \arccos(\cos \rho \cos \rho_{\rm obs} +
2153 * \sin \rho \sin \rho_{\rm obs} \cos \omega)\f]
2154 *
2155 * where
2156 * \f$\rho_{\rm obs}\f$ is the angular distance between the observed photon
2157 * arrival direction \f$\vec{p'}\f$ and the model centre \f$\vec{m}\f$.
2158 * \f$\delta\f$ is used to compute the value of the point spread function.
2159 ***************************************************************************/
2160double cta_psf_elliptical_kern_omega::eval(const double& omega)
2161{
2162 // Initialise Irf value
2163 double irf = 0.0;
2164
2165 // Compute azimuth angle in model coordinate system (radians)
2166 double omega_model = omega + m_posangle_obs;
2167
2168 // Evaluate sky model
2169 double model = m_model->eval(m_rho, omega_model, m_srcEng, m_srcTime);
2170
2171 // Debug: test if model is non positive
2172 #if defined(G_DEBUG_MODEL_ZERO)
2173 if (model <= 0.0) {
2174 double semiminor_rad = m_model->semiminor() * gammalib::deg2rad;
2175 double semimajor_rad = m_model->semimajor() * gammalib::deg2rad;
2176 double diff_angle = omega_model - m_model->posangle() * gammalib::deg2rad;
2177 double cosinus = std::cos(diff_angle);
2178 double sinus = std::sin(diff_angle);
2179 double arg1 = semiminor_rad * cosinus;
2180 double arg2 = semimajor_rad * sinus;
2181 double r_ellipse = semiminor_rad * semimajor_rad /
2182 std::sqrt(arg1*arg1 + arg2*arg2);
2183 std::cout << "*** WARNING: cta_psf_elliptical_kern_omega::eval";
2184 std::cout << " zero model for (rho,omega)=";
2185 std::cout << m_rho*gammalib::rad2deg << ",";
2186 std::cout << omega*gammalib::rad2deg << ")";
2187 std::cout << " rho-r_ellipse=" << (m_rho-r_ellipse) << " radians";
2188 std::cout << std::endl;
2189 }
2190 #endif
2191
2192 // Continue only if model is positive
2193 if (model > 0.0) {
2194
2195 // Compute Psf offset angle (radians)
2196 double delta = std::acos(m_cos_psf + m_sin_psf * std::cos(omega));
2197
2198 // Evaluate Psf * model for this delta
2199 irf = m_rsp->psf()(m_srcDir, delta, m_srcEng) * model;
2200
2201 // Compile option: Check for NaN/Inf
2202 #if defined(G_NAN_CHECK)
2204 std::cout << "*** ERROR: cta_psf_elliptical_kern_omega::eval";
2205 std::cout << "(omega=" << omega << "):";
2206 std::cout << " NaN/Inf encountered";
2207 std::cout << " (irf=" << irf;
2208 std::cout << ", model=" << model;
2209 std::cout << ", delta=" << delta;
2210 std::cout << ", rho=" << m_rho;
2211 std::cout << ", omega=" << omega << ")";
2212 std::cout << std::endl;
2213 }
2214 #endif
2215
2216 } // endif: model is positive
2217
2218 // Return
2219 return irf;
2220}
2221
2222
2223/***********************************************************************//**
2224 * @brief Kernel for PSF integration of spatial model
2225 *
2226 * @param[in] delta PSF offset angle (radians).
2227 * @return Azimuthally integrated product between PSF and model.
2228 *
2229 * Computes the azimuthally integrated product of point spread function and
2230 * the spatial model intensity. As the PSF is azimuthally symmetric, it is
2231 * not included in the azimuthally integration, but just multiplied on the
2232 * azimuthally integrated model. The method returns thus
2233 *
2234 * \f[
2235 * {\rm PSF}(\delta) \times
2236 * \int_0^{2\pi} {\rm M}(\delta, \phi) \sin \delta {\rm d}\phi
2237 * \f]
2238 *
2239 * where \f${\rm M}(\delta, \phi)\f$ is the spatial model in the coordinate
2240 * system of the point spread function, defined by the angle \f$\delta\f$
2241 * between the true and the measured photon direction and the azimuth angle
2242 * \f$\phi\f$ around the measured photon direction.
2243 ***************************************************************************/
2244double cta_psf_diffuse_kern_delta::eval(const double& delta)
2245{
2246 // Initialise value
2247 double value = 0.0;
2248
2249 // If we're at the PSF peak the model is zero (due to the sin(delta)
2250 // term. We thus only integrate for positive deltas.
2251 if (delta > 0.0) {
2252
2253 // Get PSF for this delta
2254 value = m_rsp->psf()(m_srcDir, delta, m_srcEng);
2255
2256 // Continue only if PSF is positive
2257 if (value > 0.0) {
2258
2259 // Compute sine and cosine of delta
2260 double sin_delta = std::sin(delta);
2261 double cos_delta = std::cos(delta);
2262
2263 // Setup kernel for azimuthal integration of the diffuse model
2266 sin_delta, cos_delta);
2267
2268 // Set number of azimuthal integration iterations
2269 int iter = gammalib::iter_phi(delta, m_resolution,
2271
2272 // Setup untegration
2273 GIntegral integral(&integrand);
2274
2275 // Set fixed number of iterations
2276 integral.fixed_iter(iter);
2277
2278 // Azimuthally integrate model
2279 value *= integral.romberg(0.0, gammalib::twopi) * sin_delta;
2280
2281 } // endif: PSF value was positive
2282
2283 } // endif: delta was positive
2284
2285 // Debug: Check for NaN
2286 #if defined(G_NAN_CHECK)
2287 if (gammalib::is_notanumber(value) || gammalib::is_infinite(value)) {
2288 std::cout << "*** ERROR: cta_psf_diffuse_kern_delta::eval";
2289 std::cout << "(delta=" << delta << "):";
2290 std::cout << " NaN/Inf encountered";
2291 std::cout << " (value=" << value;
2292 std::cout << ")" << std::endl;
2293 }
2294 #endif
2295
2296 // Return kernel value
2297 return value;
2298}
2299
2300
2301/***********************************************************************//**
2302 * @brief Kernel for map integration of spatial model
2303 *
2304 * @param[in] phi Azimuth angle (radians).
2305 * @return Spatial model value.
2306 *
2307 * Computes the value of the spatial model at the position (delta,phi) given
2308 * in point spread function coordinates. The transformation from point
2309 * spread function coordinates into sky coordinates is done using a rotation
2310 * matrix that is pre-computed on entry.
2311 ***************************************************************************/
2312double cta_psf_diffuse_kern_phi::eval(const double& phi)
2313{
2314 // Compute sky direction vector in native coordinates
2315 double cos_phi = std::cos(phi);
2316 double sin_phi = std::sin(phi);
2317 GVector native(-cos_phi*m_sin_delta, sin_phi*m_sin_delta, m_cos_delta);
2318
2319 // Rotate from native into celestial system
2320 GVector cel = *m_rot * native;
2321
2322 // Set sky direction
2323 GSkyDir srcDir;
2324 srcDir.celvector(cel);
2325
2326 // Set photon
2327 GPhoton photon(srcDir, m_srcEng, m_srcTime);
2328
2329 // Compute map value for this sky direction
2330 double value = m_model->eval(photon);
2331
2332 // Debug: Check for NaN
2333 #if defined(G_NAN_CHECK)
2334 if (gammalib::is_notanumber(value) || gammalib::is_infinite(value)) {
2335 std::cout << "*** ERROR: cta_psf_diffuse_kern_phi::eval";
2336 std::cout << "(phi=" << phi << "):";
2337 std::cout << " NaN/Inf encountered";
2338 std::cout << " (value=" << value;
2339 std::cout << ")" << std::endl;
2340 }
2341 #endif
2342
2343 // Return kernel value
2344 return value;
2345}
Abstract CTA energy dispersion base class definition.
const double g_ellipse_kludge_radius
About 0.2 arc seconds.
const double g_kludge_radius
Tiny angle (radians)
CTA response helper classes definition.
std::vector< std::pair< double, double > > cta_omega_intervals
Definition of support function used by CTA classes.
Integration class interface definition.
Integration class for set of functions interface definition.
Mathematical function definitions.
Isotropic spatial model class interface definition.
Spatial map cube model class interface definition.
Spatial map model class interface definition.
const double g_kludge_radius
Tiny angle (radians)
Gammalib tools definition.
double min(const GVector &vector)
Computes minimum vector element.
Definition GVector.cpp:886
double max(const GVector &vector)
Computes maximum vector element.
Definition GVector.cpp:915
Vector class interface definition.
Abstract world coordinate system base class definition.
const GCTACubePsf & psf(void) const
Return cube analysis point spread function.
const GCTAAeff * aeff(void) const
Return pointer to effective area response.
virtual bool use_edisp(void) const
Signal if response uses energy dispersion.
const GCTAPsf * psf(void) const
Return pointer to point spread function.
virtual double nroi(const GModelSky &model, const GEnergy &obsEng, const GTime &obsTime, const GObservation &obs) const
Return integral of event probability for a given sky model over ROI.
double nirf(const GPhoton &photon, const GEnergy &obsEng, const GTime &obsTime, const GObservation &obs) const
Return spatial integral of Instrument Response Function.
const GCTAEdisp * edisp(void) const
Return pointer to energy dispersion.
Class that handles energies in a unit independent way.
Definition GEnergy.hpp:48
double log10TeV(void) const
Return log10 of energy in TeV.
Definition GEnergy.cpp:457
double MeV(void) const
Return energy in MeV.
Definition GEnergy.cpp:321
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.
std::string print(const GChatter &chatter=NORMAL) const
Print integral information.
GModelSpectral * spectral(void) const
Return spectral model component.
GModelTemporal * temporal(void) const
Return temporal model component.
const GSkyMap & cube(void) const
Get map cube.
const GSkyMap & map(void) const
Get map.
double semimajor(void) const
Return semi-major axis of ellipse.
virtual double eval(const double &theta, const double &posangle, const GEnergy &energy, const GTime &time, const bool &gradients=false) const =0
double posangle(void) const
Return Position Angle of model.
double semiminor(void) const
Return semi-minor axis of ellipse.
virtual double theta_max(void) const =0
virtual double eval(const double &theta, const GEnergy &energy, const GTime &time, const bool &gradients=false) const =0
Abstract spatial model base class.
virtual double eval(const GPhoton &photon, const bool &gradients=false) const =0
virtual double eval(const GEnergy &srcEng, const GTime &srcTime=GTime(), const bool &gradients=false) const =0
virtual double eval(const GTime &srcTime, const bool &gradients=false) const =0
Class that handles photons.
Definition GPhoton.hpp:47
const GTime & time(void) const
Return photon time.
Definition GPhoton.hpp:134
const GSkyDir & dir(void) const
Return photon sky direction.
Definition GPhoton.hpp:110
const GEnergy & energy(void) const
Return photon energy.
Definition GPhoton.hpp:122
Sky direction class.
Definition GSkyDir.hpp:62
void celvector(const GVector &vector)
Set sky direction from 3D vector in celestial coordinates.
Definition GSkyDir.cpp:313
Sky map class.
Definition GSkyMap.hpp:89
const GSkyProjection * projection(void) const
Returns pointer to sky projection.
Definition GSkyMap.hpp:463
Vector class.
Definition GVector.hpp:46
Abstract world coordinate system base class.
Definition GWcs.hpp:51
double cdelt(const int &inx) const
Return pixel size.
Definition GWcs.cpp:935
Kernel for IRF azimuth angle integration of the diffuse source model.
GTime m_srcTime
True photon arrival time.
GVector m_native
Pre-allocate memory for native coordinates.
GPhoton m_photon
Pre-allocate memory for a photon object.
double m_cos_ph
Cosine term in angular distance equation.
double m_sin_theta
Sine of offset angle.
double m_srcLogEng
True photon log energy.
GEnergy m_obsEng
Measured event energy.
const GModelSpatial * m_model
Spatial model.
GEnergy m_srcEng
True photon energy.
const GMatrix * m_rot
Rotation matrix.
double m_sin_ph
Sine term in angular distance equation.
double m_cos_theta
Cosine of offset angle.
double eval(const double &phi)
Kernel for IRF azimuth angle integration of the diffuse source model.
const GCTAResponseIrf * m_rsp
CTA response.
const GCTAResponseIrf * m_rsp
CTA response.
double m_azimuth
Pointing azimuth angle.
double m_sin_eta
Sine of angular distance between.
double m_srcLogEng
True photon log energy.
double m_theta
Photon offset angle.
double m_phi
Photon azimuth angle.
GEnergy m_srcEng
True photon energy.
const GModelSpatial * m_model
Spatial model.
const GMatrix * m_rot
Rotation matrix.
double m_zenith
Pointing zenith angle.
double eval(const double &theta)
Kernel for IRF offest angle integration of the diffuse source model.
GTime m_srcTime
True photon arrival time.
double m_cos_eta
Cosine of angular distance between.
GEnergy m_obsEng
Measured event energy.
Kernel for ellitpical model azimuth angle IRF integration.
double m_sin_psf
Sine term for PSF offset angle computation.
GEnergy m_srcEng
True photon energy.
double m_omega_pnt
Azimuth of pointing in model system.
const GCTAResponseIrf * m_rsp
CTA response.
double m_cos_psf
Cosine term for PSF offset angle computation.
const GModelSpatialElliptical * m_model
Spatial model.
GEnergy m_obsEng
Measured event energy.
double m_posangle_obs
Measured photon position angle from model centre.
double m_sin_ph
Sine term for photon offset angle computation.
double eval(const double &omega)
Kernel for elliptical model integration over model's azimuth angle.
double m_cos_ph
Cosine term for photon offset angle computation.
double m_rho_obs
Distance of model centre from measured photon.
double m_posangle_obs
Photon position angle measured from model centre.
double m_semiminor
Ellipse boundary semiminor axis.
const GCTAResponseIrf * m_rsp
CTA response.
double m_sin_rho_pnt
Sine of m_rho_pnt.
GEnergy m_obsEng
Measured event energy.
GEnergy m_srcEng
True photon energy.
double m_sin_rho_obs
Sine of m_rho_obs.
double m_cos_delta_max
Cosine of maximum PSF radius.
double m_semimajor
Ellipse boundary semimajor axis.
double m_posangle
Ellipse boundary position angle.
double m_omega_pnt
Azimuth of pointing in model system.
int m_iter
Integration iterations.
const GModelSpatialElliptical * m_model
Elliptical model.
double eval(const double &rho)
Kernel for elliptical model integration over model's zenith angle.
double m_cos_rho_pnt
Cosine of m_rho_pnt.
double m_delta_max
Maximum PSF radius.
double m_cos_rho_obs
Cosine of m_rho_obs.
Kernel for radial model azimuth angle IRF integration.
double m_sin_ph
Sine term for photon offset angle computation.
GEnergy m_srcEng
True photon energy.
double m_sin_psf
Sine term for PSF offset angle computation.
double m_cos_psf
Cosine term for PSF offset angle computation.
const GCTAResponseIrf * m_rsp
CTA response.
GEnergy m_obsEng
Measured event energy.
double m_cos_ph
Cosine term for photon offset angle computation.
double eval(const double &omega)
Kernel for radial model azimuth angle IRF integration.
double m_omega0
Azimuth of pointing in model system.
double m_cos_zeta
Cosine of zeta.
const GCTAResponseIrf * m_rsp
CTA response.
double m_sin_lambda
Sine of lambda.
double m_cos_lambda
Cosine of lambda.
GEnergy m_obsEng
Measured event energy.
double m_delta_max
Maximum PSF radius.
GTime m_srcTime
True photon time.
double m_zeta
Distance model centre - measured photon.
int m_iter
Integration iterations.
GEnergy m_srcEng
True photon energy.
double m_lambda
Distance model centre - pointing.
double eval(const double &rho)
Kernel for radial model zenith angle integration of Irf.
const GModelSpatialRadial * m_model
Radial spatial model.
double m_cos_delta_max
Cosine of maximum PSF radius.
double m_omega0
Azimuth of pointing in model system.
double m_cospsf
Cosine of PSF-ROI centre distance.
double m_theta
Offset angle of source in camera system.
double m_roi
ROI radius in radians.
double m_logE
Log10 of true photon energy (E/TeV).
double m_sinpsf
Sine of PSF-ROI centre distance.
double m_cosroi
Cosine of ROI radius.
double m_zenith
Zenith angle of source in Earth system.
double m_azimuth
Azimuth angle of source in Earth system.
const GCTAResponseIrf * m_rsp
CTA response function.
double m_phi
Azimuth angle of source in camera system.
double eval(const double &delta)
Integration kernel for npsf() method.
double m_psf
PSF-ROI centre distance in radians.
Kernel for Nroi azimuth angle integration of diffuse model.
const GCTAResponseIrf * m_rsp
CTA response.
const GModelSpatial * m_model
Spatial model.
const GMatrix * m_rot
Rotation matrix.
const GCTAObservation * m_obs
CTA observation.
double eval(const double &phi)
Kernel for Nroi azimuth angle integration of diffuse model.
GTime m_obsTime
Observed photon arrival time.
double m_sin_theta
Sine of offset angle.
GEnergy m_obsEng
Observed photon energy.
GTime m_srcTime
True photon arrival time.
GEnergy m_srcEng
True photon energy.
double m_cos_theta
Cosine of offset angle.
GEnergy m_srcEng
True photon energy.
double eval(const double &theta)
Kernel for Nroi offest angle integration of diffuse model.
const GMatrix * m_rot
Rotation matrix.
const GModelSpatial * m_model
Spatial model.
const GCTAObservation * m_obs
CTA observation.
GTime m_srcTime
True photon arrival time.
const GCTAResponseIrf * m_rsp
CTA response.
GEnergy m_obsEng
Observed photon energy.
GTime m_obsTime
Observed photon arrival time.
int m_iter
Integration iterations.
Kernel for azimuth angle Nroi integration of elliptical model.
const GMatrix * m_rot
Rotation matrix.
GEnergy m_srcEng
True photon energy.
GEnergy m_obsEng
Observed photon energy.
double m_cos_rho
Cosine of offset angle.
const GCTAObservation * m_obs
CTA observation.
const GCTAResponseIrf * m_rsp
CTA response.
GTime m_obsTime
Observed photon arrival time.
double eval(const double &omega)
Kernel for azimuth angle Nroi integration of elliptical model.
GTime m_srcTime
True photon arrival time.
double m_posangle_roi
Position angle of ROI.
double m_sin_rho
Sine of offset angle.
const GModelSpatialElliptical * m_model
Elliptical model.
const GModelSpatialElliptical * m_model
Elliptical model.
const GMatrix * m_rot
Rotation matrix.
GTime m_srcTime
True photon arrival time.
double m_posangle
Ellipse boundary position angle.
GTime m_obsTime
Observed photon arrival time.
int m_iter
Integration iterations.
const GCTAResponseIrf * m_rsp
CTA response.
double m_cos_radius_roi
Cosine of m_radius_roi.
GEnergy m_srcEng
True photon energy.
double m_rho_roi
Distance between model and ROI centre.
double m_semiminor
Ellipse boundary semiminor axis.
double m_sin_rho_roi
Sine of m_rho_roi.
double m_posangle_roi
Position angle of ROI.
double m_semimajor
Ellipse boundary semimajor axis.
const GCTAObservation * m_obs
CTA observation.
double eval(const double &rho)
Kernel for zenith angle Nroi integration of elliptical model.
GEnergy m_obsEng
Observed photon energy.
double m_cos_rho_roi
Cosine of m_rho_roi.
const GModelSky * m_model
Sky model.
GTime m_obsTime
Measured arrival time.
const GCTAResponseIrf * m_rsp
CTA response function.
GEnergy m_obsEng
Measured energy.
GTime m_srcTime
True arrival time.
const GObservation * m_obs
Observation.
double eval(const double &etrue)
Integration kernel for GCTAResponseIrf::nroi method.
GEnergy m_srcEng
True photon energy.
GTime m_srcTime
True photon arrival time.
GEnergy m_obsEng
Observed photon energy.
const GCTAResponseIrf * m_rsp
CTA response.
const GCTAObservation * m_obs
CTA observation.
const GMatrix * m_rot
Rotation matrix.
GTime m_obsTime
Observed photon arrival time.
double m_cos_rho
Cosine of offset angle.
double eval(const double &omega)
Kernel for azimuth angle Nroi integration of radial model.
double m_sin_rho
Sine of offset angle.
const GModelSpatialRadial * m_model
Radial spatial model.
double m_dist
Distance model-ROI centre.
GTime m_obsTime
Observed photon arrival time.
GEnergy m_obsEng
Observed photon energy.
const GCTAObservation * m_obs
CTA observation.
int m_iter
Integration iterations.
const GMatrix * m_rot
Rotation matrix.
GTime m_srcTime
True photon arrival time.
double m_cos_dist
Cosine of distance model-ROI centre.
double eval(const double &rho)
Kernel for zenith angle Nroi integration or radial model.
double m_cos_radius
Cosine of ROI+PSF radius.
double m_sin_dist
Sine of distance model-ROI centre.
GEnergy m_srcEng
True photon energy.
const GCTAResponseIrf * m_rsp
CTA response.
double m_omega0
Position angle of ROI.
int m_max_iter
Maximum number of Romberg iterations.
const GMatrix * m_rot
Rotation matrix.
GSkyDir m_srcDir
True photon arrival direction.
const GCTAResponseCube * m_rsp
Response cube.
double m_resolution
Spatial map resolution.
GTime m_srcTime
True photon arrival time.
int m_min_iter
Minimum number of Romberg iterations.
const GModelSpatial * m_model
Spatial model.
GEnergy m_srcEng
True photon energy.
double eval(const double &delta)
Kernel for PSF integration of spatial model.
Kernel for Psf phi angle integration used for stacked analysis.
const GMatrix * m_rot
Rotation matrix.
double eval(const double &phi)
Kernel for map integration of spatial model.
GTime m_srcTime
True photon arrival time.
const GModelSpatial * m_model
Spatial model.
GEnergy m_srcEng
True photon energy.
double eval(const double &omega)
Kernel for elliptical model integration over azimuth angle.
GSkyDir m_srcDir
True photon sky direction.
const GModelSpatialElliptical * m_model
Elliptical model.
double m_posangle_obs
Measured photon position angle from model centre.
GEnergy m_srcEng
True photon energy.
double m_sin_psf
Sine term for PSF offset angle computation.
const GCTAResponseCube * m_rsp
CTA response.
double m_cos_psf
Cosine term for PSF offset angle computation.
GEnergy m_srcEng
True photon energy.
double m_rho_obs
Distance of model centre from measured photon.
double eval(const double &rho)
Kernel for elliptical model integration over zenith angle.
double m_cos_delta_max
Cosine of maximum PSF radius.
double m_semimajor
Ellipse boundary semimajor axis.
GSkyDir m_srcDir
True photon arrival direction.
double m_posangle
Ellipse boundary position angle.
double m_semiminor
Ellipse boundary semiminor axis.
double m_delta_max
Maximum PSF radius.
double m_sin_rho_obs
Sine of m_rho_obs.
const GCTAResponseCube * m_rsp
CTA response.
int m_iter
Integration iterations.
const GModelSpatialElliptical * m_model
Elliptical model.
double m_cos_rho_obs
Cosine of m_rho_obs.
double m_posangle_obs
Photon position angle measured from model centre.
double m_cos_theta_max
Cosine of m_theta_max.
double eval(const double &delta)
Kernel for PSF integration of radial model.
const GCTAResponseCube * m_rsp
Response cube.
GSkyDir m_srcDir
True photon arrival direction.
GTime m_srcTime
True photon arrival time.
GEnergy m_srcEng
True photon energy.
double m_cos_delta_mod
Cosine of m_delta_mod.
double m_delta_mod
Distance of model from Psf.
double m_sin_delta_mod
Sine of m_delta_mod.
int m_iter
Integration iterations.
double m_theta_max
Maximum model radius.
const GModelSpatialRadial * m_model
Radial model.
Kernel for radial model azimuth angle integration.
GEnergy m_srcEng
True photon energy.
const GCTAResponseCube * m_rsp
CTA response.
double m_sin_psf
Sine term for PSF offset angle computation.
double m_cos_psf
Cosine term for PSF offset angle computation.
GSkyDir m_srcDir
True photon sky direction.
double eval(const double &omega)
Kernel for radial model integration over azimuth angle.
Kernel for Psf phi angle integration used for stacked analysis.
double eval(const double &phi)
Kernel for azimuthal radial model integration.
GEnergy m_srcEng
True photon energy.
GTime m_srcTime
True photon arrival time.
const GModelSpatialRadial * m_model
Radial model.
double m_sin_fact
sin(delta)*sin(delta_mod)
double m_cos_fact
cos(delta)*cos(delta_mod)
const GCTAResponseCube * m_rsp
CTA response.
GEnergy m_srcEng
True photon energy.
double eval(const double &rho)
Kernel for radial model integration over zenith angle.
int m_iter
Integration iterations.
double m_cos_delta_max
Cosine of maximum PSF radius.
GTime m_srcTime
True photon time.
double m_rho_obs
Distance of model centre from measured photon.
GSkyDir m_srcDir
True photon arrival direction.
const GModelSpatialRadial * m_model
Radial model.
double m_cos_rho_obs
Cosine of m_rho_obs.
double m_delta_max
Maximum PSF radius.
double m_sin_rho_obs
Sine of m_rho_obs.
bool is_infinite(const double &x)
Signal if argument is infinite.
Definition GTools.hpp:184
bool is_notanumber(const double &x)
Signal if argument is not a number.
Definition GTools.hpp:201
const double pi
Definition GMath.hpp:35
double roi_arclength(const double &rad, const double &dist, const double &cosdist, const double &sindist, const double &roi, const double &cosroi)
Returns length of circular arc within circular ROI.
Definition GTools.cpp:2107
const double deg2rad
Definition GMath.hpp:43
int iter_phi(const double &rho, const double &resolution, const int &iter_min, const int &iter_max)
Determine number of azimuthal Romberg iterations.
double resolution(const GModelSpatial *model)
Determine resolution of spatial model.
cta_omega_intervals limit_omega(const double &min, const double &max, const double &domega)
Limit omega interval.
const double twopi
Definition GMath.hpp:36
const double rad2deg
Definition GMath.hpp:44