Commit 640e93f42ca416c210985765b098239e4b2c8930

Authored by Jürgen Knödlseder
1 parent bd7183cb

Remove diffuse model caching for stacked analysis (#2722)

ChangeLog
1   -2018-11-09
  1 +2018-11-12
2 2  
3 3 * Version 1.6.0 released
4 4 ========================
5 5  
  6 + Remove diffuse response cube caching (#2722)
6 7 Revise GCTAEdispCube::operator() interface and disable stacked response
7 8 cutting in true energy (#2721)
8 9 Rename GCTAResponseIrf::lo_save_thres() and GCTAResponseIrf::hi_save_thres()
... ...
1 1 New Features and Important Changes in GammaLib 1.6.0
2 2  
3   -9 November 2018
  3 +12 November 2018
4 4  
5 5  
6 6 1. Introduction
... ... @@ -21,7 +21,8 @@ The following classes have been added:
21 21 - GCTAModelSpatialRegistry
22 22  
23 23 The following classes have been removed:
24   -- none
  24 +- GCTACubeSourceDiffuse
  25 +- GCTACubeSourcePoint
25 26  
26 27 The following classes have been renamed:
27 28 - none
... ... @@ -314,6 +315,10 @@ None
314 315  
315 316 20. CTA interface
316 317 -----------------
  318 +The classes GCTACubeSourcePoint and GCTACubeSourceDiffuse were removed since
  319 +the response caching is now performed at the GCTAResponse level, hence no
  320 +dedicated response cache classes are needed anymore (#2722).
  321 +
317 322 The arguments of the GCTACubeEdisp::operator() were changed from, passing
318 323 now reconstructed and true energy objects, followed by a true sky direction.
319 324 This is more consistent with the GCTAEdisp interface. The stacked response
... ...
inst/cta/Makefile.am
... ... @@ -70,8 +70,6 @@ sources = src/GCTASupport.cpp \
70 70 src/GCTACubePsf.cpp \
71 71 src/GCTACubeEdisp.cpp \
72 72 src/GCTACubeSource.cpp \
73   - src/GCTACubeSourcePoint.cpp \
74   - src/GCTACubeSourceDiffuse.cpp \
75 73 src/GCTAInstDir.cpp \
76 74 src/GCTARoi.cpp \
77 75 src/GCTAPointing.cpp \
... ... @@ -129,8 +127,6 @@ pkginclude_HEADERS = include/GCTATypemaps.hpp \
129 127 include/GCTACubeEdisp.hpp \
130 128 include/GCTACubeBackground.hpp \
131 129 include/GCTACubeSource.hpp \
132   - include/GCTACubeSourcePoint.hpp \
133   - include/GCTACubeSourceDiffuse.hpp \
134 130 include/GCTAModelBackground.hpp \
135 131 include/GCTAModelSpatial.hpp \
136 132 include/GCTAModelSpatialGradient.hpp \
... ...
inst/cta/include/GCTAResponseCube.hpp
1 1 /***************************************************************************
2 2 * GCTAResponseCube.hpp - CTA cube analysis response function class *
3 3 * ----------------------------------------------------------------------- *
4   - * copyright (C) 2014-2016 by Juergen Knoedlseder *
  4 + * copyright (C) 2014-2018 by Juergen Knoedlseder *
5 5 * ----------------------------------------------------------------------- *
6 6 * *
7 7 * This program is free software: you can redistribute it and/or modify *
... ... @@ -124,6 +124,10 @@ private:
124 124 const GSkyDir& obsDir,
125 125 const GEnergy& srcEng,
126 126 const GTime& srcTime) const;
  127 + double psf_diffuse(const GModelSpatial* model,
  128 + const GSkyDir& obsDir,
  129 + const GEnergy& srcEng,
  130 + const GTime& srcTime) const;
127 131 double irf_ptsrc(const GEvent& event,
128 132 const GSource& source,
129 133 const GObservation& obs) const;
... ...
inst/cta/src/GCTAResponseCube.cpp
... ... @@ -33,7 +33,6 @@
33 33 #include "GTools.hpp"
34 34 #include "GCTAResponseCube.hpp"
35 35 #include "GCTAResponse_helpers.hpp"
36   -#include "GCTACubeSourceDiffuse.hpp"
37 36 #include "GCTAInstDir.hpp"
38 37 #include "GCTAEventBin.hpp"
39 38 #include "GCTASupport.hpp"
... ... @@ -1089,6 +1088,89 @@ double GCTAResponseCube::psf_elliptical(const GModelSpatialElliptical* model,
1089 1088  
1090 1089  
1091 1090 /***********************************************************************//**
  1091 + * @brief Integrate PSF over diffuse model
  1092 + *
  1093 + * @param[in] model Diffuse spatial model.
  1094 + * @param[in] obsDir Observed event direction.
  1095 + * @param[in] srcEng True photon energy.
  1096 + * @param[in] srcTime True photon arrival time.
  1097 + *
  1098 + * Computes the integral
  1099 + *
  1100 + * \f[
  1101 + * \int_0^{\delta_{\rm max}}
  1102 + * {\rm PSF}(\delta) \times
  1103 + * \int_0^{2\pi} {\rm Map}(\delta, \phi) \sin \delta
  1104 + * {\rm d}\phi {\rm d}\delta
  1105 + * \f]
  1106 + *
  1107 + * where \f${\rm Map}(\delta, \phi)\f$ is the diffuse map in the coordinate
  1108 + * system of the point spread function, defined by the angle \f$\delta\f$
  1109 + * between the true and the measured photon direction and the azimuth angle
  1110 + * \f$\phi\f$ around the measured photon direction.
  1111 + * \f${\rm PSF}(\delta)\f$ is the azimuthally symmetric point spread
  1112 + * function.
  1113 + ***************************************************************************/
  1114 +double GCTAResponseCube::psf_diffuse(const GModelSpatial* model,
  1115 + const GSkyDir& obsDir,
  1116 + const GEnergy& srcEng,
  1117 + const GTime& srcTime) const
  1118 +{
  1119 + // Set minimum and maximum number of iterations for Romberg integration.
  1120 + // These values have been determined after careful testing, see
  1121 + // https://cta-redmine.irap.omp.eu/issues/2458
  1122 + static const int min_iter_delta = 5;
  1123 + static const int min_iter_phi = 5;
  1124 + static const int max_iter_delta = 8;
  1125 + static const int max_iter_phi = 8;
  1126 +
  1127 + // Initialise PSF
  1128 + double psf = 0.0;
  1129 +
  1130 + // Compute rotation matrix to convert from PSF centred coordinate system
  1131 + // spanned by delta and phi into the reference frame of the observed
  1132 + // arrival direction given in Right Ascension and Declination.
  1133 + GMatrix ry;
  1134 + GMatrix rz;
  1135 + ry.eulery(obsDir.dec_deg() - 90.0);
  1136 + rz.eulerz(-obsDir.ra_deg());
  1137 + GMatrix rot = (ry * rz).transpose();
  1138 +
  1139 + // Get offset angle integration interval in radians
  1140 + double delta_min = 0.0;
  1141 + double delta_max = 1.1 * this->psf().delta_max();
  1142 +
  1143 + // Get resolution of spatial model
  1144 + double resolution = gammalib::resolution(model);
  1145 +
  1146 +
  1147 + // Setup integration kernel. We take here the observed photon arrival
  1148 + // direction as the true photon arrival direction because the PSF does
  1149 + // not vary significantly over a small region.
  1150 + cta_psf_diffuse_kern_delta integrand(this, model, &rot,
  1151 + obsDir, srcEng, srcTime,
  1152 + min_iter_phi, max_iter_phi,
  1153 + resolution);
  1154 +
  1155 + // Set number of radial integration iterations
  1156 + int iter = gammalib::iter_rho(delta_max, resolution,
  1157 + min_iter_delta, max_iter_delta);
  1158 +
  1159 + // Setup integration
  1160 + GIntegral integral(&integrand);
  1161 +
  1162 + // Set fixed number of iterations
  1163 + integral.fixed_iter(iter);
  1164 +
  1165 + // Integrate over PSF delta angle
  1166 + psf = integral.romberg(delta_min, delta_max);
  1167 +
  1168 + // Return PSF
  1169 + return psf;
  1170 +}
  1171 +
  1172 +
  1173 +/***********************************************************************//**
1092 1174 * @brief Return instrument response to point source
1093 1175 *
1094 1176 * @param[in] event Observed event.
... ... @@ -1319,7 +1401,8 @@ double GCTAResponseCube::irf_elliptical(const GEvent& event,
1319 1401 GEnergy srcEng = source.energy();
1320 1402  
1321 1403 // Get pointer to elliptical model
1322   - const GModelSpatialElliptical* model = static_cast<const GModelSpatialElliptical*>(source.model());
  1404 + const GModelSpatialElliptical* model =
  1405 + static_cast<const GModelSpatialElliptical*>(source.model());
1323 1406  
1324 1407 // Compute angle between model centre and measured photon direction and
1325 1408 // position angle (radians)
... ... @@ -1415,46 +1498,67 @@ double GCTAResponseCube::irf_diffuse(const GEvent&amp; event,
1415 1498 }
1416 1499 const GCTAEventBin* bin = static_cast<const GCTAEventBin*>(&event);
1417 1500  
1418   - // Get pointer to source cache. We first search the cache for a model
1419   - // with the source name. If no model was found we initialise a new
1420   - // cache entry for that model. Otherwise, we simply return the actual
1421   - // cache entry.
1422   - GCTACubeSourceDiffuse* cache(NULL);
1423   - int index = cache_index(source.name());
1424   - if (index == -1) {
  1501 + // Get event attribute references
  1502 + const GSkyDir& obsDir = bin->dir().dir();
  1503 + const GEnergy& obsEng = bin->energy();
  1504 + const GTime& obsTime = bin->time();
1425 1505  
1426   - // No cache entry was found, thus allocate and initialise a new one
1427   - cache = new GCTACubeSourceDiffuse;
1428   - cache->set(source.name(), *source.model(), obs);
1429   - m_cache.push_back(cache);
  1506 + // Get energy of source model
  1507 + GEnergy srcEng = source.energy();
1430 1508  
1431   - } // endif: no cache entry was found
1432   - else {
  1509 + // Get pointer to elliptical model
  1510 + const GModelSpatialDiffuse* model =
  1511 + static_cast<const GModelSpatialDiffuse*>(source.model());
1433 1512  
1434   - // Check that the cache entry is of the expected type
1435   - if (m_cache[index]->code() != GCTA_CUBE_SOURCE_DIFFUSE) {
1436   - std::string msg = "Cached model \""+source.name()+"\" is not "
1437   - "an extended source model. This method only "
1438   - "applies to extended source models.";
1439   - throw GException::invalid_value(G_IRF_DIFFUSE, msg);
1440   - }
1441   - cache = static_cast<GCTACubeSourceDiffuse*>(m_cache[index]);
  1513 + // Get pointer on CTA response cube
  1514 + //const GCTAResponseCube* rsp = gammalib::cta_rsp_cube(G_IRF_DIFFUSE, obs);
1442 1515  
1443   - } // endelse: there was a cache entry for this model
  1516 + // Get livetime (in seconds) and deadtime correction factor
  1517 + double livetime = exposure().livetime();
  1518 + double deadc = exposure().deadc();
1444 1519  
1445   - // If energy dispersion is available then compute IRF using the true
1446   - // photon energy and multiply in the energy dispersion ...
1447   - if (use_edisp()) {
1448   - irf = cache->irf(bin->ipix(), source.energy());
1449   - if (irf > 0.0) {
1450   - irf *= edisp()(bin->energy(), source.energy(), bin->dir().dir());
1451   - }
1452   - }
  1520 + // Get Psf radius (in degrees)
  1521 + double delta_max = psf().delta_max() * gammalib::rad2deg;
1453 1522  
1454   - // ... otherwise compute the IRF at the observed energy
1455   - else {
1456   - irf = cache->irf(bin->ipix(), bin->ieng());
1457   - }
  1523 + // Continue only if livetime is >0 and model contains reconstructed
  1524 + // sky direction
  1525 + if (livetime > 0.0 && model->contains(obsDir, delta_max)) {
  1526 +
  1527 + // Get exposure
  1528 + //
  1529 + // The current code assumes that the exposure at the observed and
  1530 + // true event direction does not vary significantly. In other words,
  1531 + // the code assumes that the exposure is constant over the size of
  1532 + // the PSF.
  1533 + irf = exposure()(obsDir, srcEng);
  1534 +
  1535 + // Continue only if exposure is positive
  1536 + if (irf > 0.0) {
  1537 +
  1538 + // Recover effective area from exposure
  1539 + irf /= livetime;
  1540 +
  1541 + // Compute product of PSF and diffuse map, integrated over the
  1542 + // relevant PSF area. We assume no energy dispersion and thus
  1543 + // compute the product using the observed energy.
  1544 + irf *= psf_diffuse(model, obsDir, srcEng, obsTime);
  1545 +
  1546 + // Multiply-in energy dispersion
  1547 + //
  1548 + // The current code assumes that the energy dispersion at the
  1549 + // observed and true event direction does not vary
  1550 + // significantly. In other words, the code assumes that the
  1551 + // energy dispersion is constant over the size of the PSF.
  1552 + if (use_edisp() && irf > 0.0) {
  1553 + irf *= edisp()(bin->energy(), srcEng, obsDir);
  1554 + }
  1555 +
  1556 + // Apply deadtime correction
  1557 + irf *= exposure().deadc();
  1558 +
  1559 + } // endif: exposure was positive
  1560 +
  1561 + } // endif: livetime was positive
1458 1562  
1459 1563 // Compile option: Check for NaN/Inf
1460 1564 #if defined(G_NAN_CHECK)
... ...