Commit 44d3f497eb672a29adbe814218ff208dd1035bf0

Authored by Jürgen Knödlseder
1 parent f4cec345

Use CTA response cache in GCTAResponse and GCTAResponseIRF and remove former dif…

…fuse model cache (#2715)
@@ -312,7 +312,12 @@ None @@ -312,7 +312,12 @@ None
312 20. CTA interface 312 20. CTA interface
313 ----------------- 313 -----------------
314 The GCTAResponseCache class was added to implement a general response cache for 314 The GCTAResponseCache class was added to implement a general response cache for
315 -CTA (#2715). 315 +CTA. The response cache is now used in GCTAResponse::irf(GEvent&, GSource&,
  316 +GObservation&) and in GCTAResponseIrf::nroi(GModelSky&, GEnergy&, GTime&,
  317 +GEnergy&, GTime&, GObservation&) in case that the spatial model has no free
  318 +parameters. This means that now all other spatial models with frozen parameters
  319 +will be cached. The use of the cache can be toggled by internal data members
  320 +(#2715).
316 321
317 GCTABackground3D::init_mc_cache now pre-computes the Monte Carlo cache at energy 322 GCTABackground3D::init_mc_cache now pre-computes the Monte Carlo cache at energy
318 nodes of the background cube, and in addition, inserts 8 logarithmically spaced 323 nodes of the background cube, and in addition, inserts 8 logarithmically spaced
inst/cta/include/GCTAResponse.hpp
@@ -30,6 +30,7 @@ @@ -30,6 +30,7 @@
30 /* __ Includes ___________________________________________________________ */ 30 /* __ Includes ___________________________________________________________ */
31 #include <string> 31 #include <string>
32 #include "GResponse.hpp" 32 #include "GResponse.hpp"
  33 +#include "GCTAResponseCache.hpp"
33 34
34 /* __ Type definitions ___________________________________________________ */ 35 /* __ Type definitions ___________________________________________________ */
35 36
@@ -113,6 +114,12 @@ protected: @@ -113,6 +114,12 @@ protected:
113 virtual double irf_composite(const GEvent& event, 114 virtual double irf_composite(const GEvent& event,
114 const GSource& source, 115 const GSource& source,
115 const GObservation& obs) const; 116 const GObservation& obs) const;
  117 +
  118 + // Protected members
  119 + bool m_use_irf_cache; //!< Control usage of IRF cache
  120 +
  121 + // Cache for irf(GEvent&, GSource&, GObservation&) computation
  122 + mutable GCTAResponseCache m_irf_cache;
116 }; 123 };
117 124
118 #endif /* GCTARESPONSE_HPP */ 125 #endif /* GCTARESPONSE_HPP */
inst/cta/include/GCTAResponseIrf.hpp
@@ -34,6 +34,7 @@ @@ -34,6 +34,7 @@
34 #include "GTime.hpp" 34 #include "GTime.hpp"
35 #include "GCaldb.hpp" 35 #include "GCaldb.hpp"
36 #include "GCTAResponse.hpp" 36 #include "GCTAResponse.hpp"
  37 +#include "GCTAResponseCache.hpp"
37 #include "GCTAAeff.hpp" 38 #include "GCTAAeff.hpp"
38 #include "GCTAPsf.hpp" 39 #include "GCTAPsf.hpp"
39 #include "GCTAEdisp.hpp" 40 #include "GCTAEdisp.hpp"
@@ -218,16 +219,15 @@ private: @@ -218,16 +219,15 @@ private:
218 mutable bool m_apply_edisp; //!< Apply energy dispersion 219 mutable bool m_apply_edisp; //!< Apply energy dispersion
219 double m_lo_save_thres; //!< Save low energy threshold 220 double m_lo_save_thres; //!< Save low energy threshold
220 double m_hi_save_thres; //!< Save high energy threshold 221 double m_hi_save_thres; //!< Save high energy threshold
  222 + bool m_use_nroi_cache; //!< Control usage of nroi cache
221 223
222 // XML response filename 224 // XML response filename
223 std::string m_xml_caldb; //!< Calibration database string in XML file 225 std::string m_xml_caldb; //!< Calibration database string in XML file
224 std::string m_xml_rspname; //!< Response name in XML file 226 std::string m_xml_rspname; //!< Response name in XML file
225 227
226 - // Npred cache  
227 - mutable std::vector<std::string> m_npred_names; //!< Model names  
228 - mutable std::vector<GEnergy> m_npred_energies; //!< Model energy  
229 - mutable std::vector<GTime> m_npred_times; //!< Model time  
230 - mutable std::vector<double> m_npred_values; //!< Model values 228 + // Cache for nroi(GModelSky&, GEnergy&, GTime&, GEnergy&, GTime&, GObservation&)
  229 + // computation
  230 + mutable GCTAResponseCache m_nroi_cache;
231 }; 231 };
232 232
233 233
inst/cta/src/GCTAResponse.cpp
@@ -34,6 +34,7 @@ @@ -34,6 +34,7 @@
34 #include "GModelSpatial.hpp" 34 #include "GModelSpatial.hpp"
35 #include "GModelSpatialComposite.hpp" 35 #include "GModelSpatialComposite.hpp"
36 #include "GCTAResponse.hpp" 36 #include "GCTAResponse.hpp"
  37 +#include "GCTAResponse.hpp"
37 38
38 /* __ Method name definitions ____________________________________________ */ 39 /* __ Method name definitions ____________________________________________ */
39 40
@@ -166,25 +167,48 @@ double GCTAResponse::irf(const GEvent&amp; event, @@ -166,25 +167,48 @@ double GCTAResponse::irf(const GEvent&amp; event,
166 // Initialise IRF value 167 // Initialise IRF value
167 double irf = 0.0; 168 double irf = 0.0;
168 169
169 - // Select IRF depending on the spatial model type  
170 - switch (source.model()->code()) {  
171 - case GMODEL_SPATIAL_POINT_SOURCE:  
172 - irf = irf_ptsrc(event, source, obs);  
173 - break;  
174 - case GMODEL_SPATIAL_RADIAL:  
175 - irf = irf_radial(event, source, obs);  
176 - break;  
177 - case GMODEL_SPATIAL_ELLIPTICAL:  
178 - irf = irf_elliptical(event, source, obs);  
179 - break;  
180 - case GMODEL_SPATIAL_DIFFUSE:  
181 - irf = irf_diffuse(event, source, obs);  
182 - break;  
183 - case GMODEL_SPATIAL_COMPOSITE:  
184 - irf = irf_composite(event, source, obs);  
185 - break;  
186 - default:  
187 - break; 170 + // Set IRF value attributes
  171 + std::string name = obs.id() + "::" + source.name();
  172 + const GEnergy& ereco = event.energy();
  173 + const GEnergy& etrue = source.energy();
  174 +
  175 + // Signal if spatial model has free parameters
  176 + bool has_free_pars = source.model()->has_free_pars();
  177 +
  178 + // If the spatial model component has free parameters, or the response
  179 + // cache should not be used, or the cache does not contain the requested
  180 + // IRF value then compute the IRF value for the spatial model.
  181 + if (has_free_pars ||
  182 + !m_use_irf_cache ||
  183 + !m_irf_cache.contains(name, ereco, etrue, &irf)) {
  184 +
  185 + // Compute IRF for spatial model
  186 + switch (source.model()->code()) {
  187 + case GMODEL_SPATIAL_POINT_SOURCE:
  188 + irf = irf_ptsrc(event, source, obs);
  189 + break;
  190 + case GMODEL_SPATIAL_RADIAL:
  191 + irf = irf_radial(event, source, obs);
  192 + break;
  193 + case GMODEL_SPATIAL_ELLIPTICAL:
  194 + irf = irf_elliptical(event, source, obs);
  195 + break;
  196 + case GMODEL_SPATIAL_DIFFUSE:
  197 + irf = irf_diffuse(event, source, obs);
  198 + break;
  199 + case GMODEL_SPATIAL_COMPOSITE:
  200 + irf = irf_composite(event, source, obs);
  201 + break;
  202 + default:
  203 + break;
  204 + }
  205 +
  206 + } // endif: computed spatial model
  207 +
  208 + // If the spatial model has no free parameters and the response cache
  209 + // should be used then put the IRF value in the response cache.
  210 + if (!has_free_pars && m_use_irf_cache) {
  211 + m_irf_cache.set(name, ereco, etrue, irf);
188 } 212 }
189 213
190 // Return IRF value 214 // Return IRF value
@@ -203,6 +227,10 @@ double GCTAResponse::irf(const GEvent&amp; event, @@ -203,6 +227,10 @@ double GCTAResponse::irf(const GEvent&amp; event,
203 ***************************************************************************/ 227 ***************************************************************************/
204 void GCTAResponse::init_members(void) 228 void GCTAResponse::init_members(void)
205 { 229 {
  230 + // Initialize members
  231 + m_use_irf_cache = true;
  232 + m_irf_cache.clear();
  233 +
206 // Return 234 // Return
207 return; 235 return;
208 } 236 }
@@ -215,6 +243,10 @@ void GCTAResponse::init_members(void) @@ -215,6 +243,10 @@ void GCTAResponse::init_members(void)
215 ***************************************************************************/ 243 ***************************************************************************/
216 void GCTAResponse::copy_members(const GCTAResponse& rsp) 244 void GCTAResponse::copy_members(const GCTAResponse& rsp)
217 { 245 {
  246 + // Copy members
  247 + m_use_irf_cache = rsp.m_use_irf_cache;
  248 + m_irf_cache = rsp.m_irf_cache;
  249 +
218 // Return 250 // Return
219 return; 251 return;
220 } 252 }
inst/cta/src/GCTAResponseIrf.cpp
@@ -107,8 +107,6 @@ @@ -107,8 +107,6 @@
107 /* __ Macros _____________________________________________________________ */ 107 /* __ Macros _____________________________________________________________ */
108 108
109 /* __ Coding definitions _________________________________________________ */ 109 /* __ Coding definitions _________________________________________________ */
110 -#define G_USE_IRF_CACHE //!< Use IRF cache in irf_diffuse method  
111 -#define G_USE_NPRED_CACHE //!< Use Npred cache in npred_diffuse method  
112 //#define G_USE_PSF_SYSTEM //!< Do radial Irf integrations in Psf system 110 //#define G_USE_PSF_SYSTEM //!< Do radial Irf integrations in Psf system
113 111
114 /* __ Debug definitions __________________________________________________ */ 112 /* __ Debug definitions __________________________________________________ */
@@ -1604,8 +1602,8 @@ std::string GCTAResponseIrf::print(const GChatter&amp; chatter) const @@ -1604,8 +1602,8 @@ std::string GCTAResponseIrf::print(const GChatter&amp; chatter) const
1604 } 1602 }
1605 } 1603 }
1606 1604
1607 - // Append save energy threshold information  
1608 - result.append("\n"+gammalib::parformat("Save energy range")); 1605 + // Append safe energy threshold information
  1606 + result.append("\n"+gammalib::parformat("Safe energy range"));
1609 if (m_lo_save_thres > 0.0 && m_hi_save_thres) { 1607 if (m_lo_save_thres > 0.0 && m_hi_save_thres) {
1610 result.append(gammalib::str(m_lo_save_thres)); 1608 result.append(gammalib::str(m_lo_save_thres));
1611 result.append(" - "); 1609 result.append(" - ");
@@ -1626,9 +1624,11 @@ std::string GCTAResponseIrf::print(const GChatter&amp; chatter) const @@ -1626,9 +1624,11 @@ std::string GCTAResponseIrf::print(const GChatter&amp; chatter) const
1626 result.append("undefined"); 1624 result.append("undefined");
1627 } 1625 }
1628 1626
1629 - // Append detailed information 1627 + // Get reduced chatter level
1630 GChatter reduced_chatter = gammalib::reduce(chatter); 1628 GChatter reduced_chatter = gammalib::reduce(chatter);
1631 - if (reduced_chatter > SILENT) { 1629 +
  1630 + // Append detailed information
  1631 + if (chatter >= NORMAL) {
1632 1632
1633 // Append calibration database 1633 // Append calibration database
1634 result.append("\n"+m_caldb.print(reduced_chatter)); 1634 result.append("\n"+m_caldb.print(reduced_chatter));
@@ -1655,19 +1655,11 @@ std::string GCTAResponseIrf::print(const GChatter&amp; chatter) const @@ -1655,19 +1655,11 @@ std::string GCTAResponseIrf::print(const GChatter&amp; chatter) const
1655 1655
1656 } 1656 }
1657 1657
1658 - // EXPLICIT: Append Npred cache information 1658 + // Append cache information
1659 if (chatter >= EXPLICIT) { 1659 if (chatter >= EXPLICIT) {
1660 - if (!m_npred_names.empty()) {  
1661 - for (int i = 0; i < m_npred_names.size(); ++i) {  
1662 - result.append("\n"+gammalib::parformat("Npred cache " +  
1663 - gammalib::str(i)));  
1664 - result.append(m_npred_names[i]+", ");  
1665 - result.append(m_npred_energies[i].print()+", ");  
1666 - result.append(m_npred_times[i].print()+" = ");  
1667 - result.append(gammalib::str(m_npred_values[i]));  
1668 - }  
1669 - }  
1670 - } // endif: chatter was explicit 1660 + result.append("\n"+m_irf_cache.print(reduced_chatter));
  1661 + result.append("\n"+m_nroi_cache.print(reduced_chatter));
  1662 + }
1671 1663
1672 } // endif: chatter was not silent 1664 } // endif: chatter was not silent
1673 1665
@@ -1864,25 +1856,46 @@ double GCTAResponseIrf::nroi(const GModelSky&amp; model, @@ -1864,25 +1856,46 @@ double GCTAResponseIrf::nroi(const GModelSky&amp; model,
1864 // Initialise response value 1856 // Initialise response value
1865 double nroi = 0.0; 1857 double nroi = 0.0;
1866 1858
1867 - // Select method depending on the spatial model type  
1868 - switch (model.spatial()->code()) {  
1869 - case GMODEL_SPATIAL_POINT_SOURCE:  
1870 - nroi = nroi_ptsrc(model, srcEng, srcTime, obsEng, obsTime, obs);  
1871 - break;  
1872 - case GMODEL_SPATIAL_RADIAL:  
1873 - nroi = nroi_radial(model, srcEng, srcTime, obsEng, obsTime, obs);  
1874 - break;  
1875 - case GMODEL_SPATIAL_ELLIPTICAL:  
1876 - nroi = nroi_elliptical(model, srcEng, srcTime, obsEng, obsTime, obs);  
1877 - break;  
1878 - case GMODEL_SPATIAL_DIFFUSE:  
1879 - nroi = nroi_diffuse(model, srcEng, srcTime, obsEng, obsTime, obs);  
1880 - break;  
1881 - case GMODEL_SPATIAL_COMPOSITE:  
1882 - nroi = nroi_composite(model, srcEng, srcTime, obsEng, obsTime, obs);  
1883 - break;  
1884 - default:  
1885 - break; 1859 + // Set response value attributes
  1860 + std::string name = obs.id() + "::" + model.name();
  1861 +
  1862 + // Signal if spatial model has free parameters
  1863 + bool has_free_pars = model.spatial()->has_free_pars();
  1864 +
  1865 + // If the spatial model component has free parameters, or the response
  1866 + // cache should not be used, or the cache does not contain the requested
  1867 + // IRF value then compute the IRF value for the spatial model.
  1868 + if (has_free_pars ||
  1869 + !m_use_nroi_cache ||
  1870 + !m_nroi_cache.contains(name, obsEng, srcEng, &nroi)) {
  1871 +
  1872 + // Select method depending on the spatial model type
  1873 + switch (model.spatial()->code()) {
  1874 + case GMODEL_SPATIAL_POINT_SOURCE:
  1875 + nroi = nroi_ptsrc(model, srcEng, srcTime, obsEng, obsTime, obs);
  1876 + break;
  1877 + case GMODEL_SPATIAL_RADIAL:
  1878 + nroi = nroi_radial(model, srcEng, srcTime, obsEng, obsTime, obs);
  1879 + break;
  1880 + case GMODEL_SPATIAL_ELLIPTICAL:
  1881 + nroi = nroi_elliptical(model, srcEng, srcTime, obsEng, obsTime, obs);
  1882 + break;
  1883 + case GMODEL_SPATIAL_DIFFUSE:
  1884 + nroi = nroi_diffuse(model, srcEng, srcTime, obsEng, obsTime, obs);
  1885 + break;
  1886 + case GMODEL_SPATIAL_COMPOSITE:
  1887 + nroi = nroi_composite(model, srcEng, srcTime, obsEng, obsTime, obs);
  1888 + break;
  1889 + default:
  1890 + break;
  1891 + }
  1892 +
  1893 + } // endif: computed spatial model
  1894 +
  1895 + // If the spatial model has no free parameters and the response cache
  1896 + // should be used then put the IRF value in the response cache.
  1897 + if (!has_free_pars && m_use_nroi_cache) {
  1898 + m_nroi_cache.set(name, obsEng, srcEng, nroi);
1886 } 1899 }
1887 1900
1888 // Return response value 1901 // Return response value
@@ -2105,23 +2118,21 @@ void GCTAResponseIrf::init_members(void) @@ -2105,23 +2118,21 @@ void GCTAResponseIrf::init_members(void)
2105 // Initialise members 2118 // Initialise members
2106 m_caldb.clear(); 2119 m_caldb.clear();
2107 m_rspname.clear(); 2120 m_rspname.clear();
2108 - m_aeff = NULL;  
2109 - m_psf = NULL;  
2110 - m_edisp = NULL;  
2111 - m_background = NULL;  
2112 - m_apply_edisp = false; //!< Switched off by default  
2113 - m_lo_save_thres = 0.0;  
2114 - m_hi_save_thres = 0.0; 2121 + m_aeff = NULL;
  2122 + m_psf = NULL;
  2123 + m_edisp = NULL;
  2124 + m_background = NULL;
  2125 + m_apply_edisp = false; //!< Switched off by default
  2126 + m_lo_save_thres = 0.0;
  2127 + m_hi_save_thres = 0.0;
  2128 + m_use_nroi_cache = true; //!< Switched on by default
2115 2129
2116 // XML response filenames 2130 // XML response filenames
2117 m_xml_caldb.clear(); 2131 m_xml_caldb.clear();
2118 m_xml_rspname.clear(); 2132 m_xml_rspname.clear();
2119 2133
2120 - // Initialise Npred cache  
2121 - m_npred_names.clear();  
2122 - m_npred_energies.clear();  
2123 - m_npred_times.clear();  
2124 - m_npred_values.clear(); 2134 + // Initialise nroi cache
  2135 + m_nroi_cache.clear();
2125 2136
2126 // Return 2137 // Return
2127 return; 2138 return;
@@ -2136,21 +2147,19 @@ void GCTAResponseIrf::init_members(void) @@ -2136,21 +2147,19 @@ void GCTAResponseIrf::init_members(void)
2136 void GCTAResponseIrf::copy_members(const GCTAResponseIrf& rsp) 2147 void GCTAResponseIrf::copy_members(const GCTAResponseIrf& rsp)
2137 { 2148 {
2138 // Copy members 2149 // Copy members
2139 - m_caldb = rsp.m_caldb;  
2140 - m_rspname = rsp.m_rspname;  
2141 - m_apply_edisp = rsp.m_apply_edisp;  
2142 - m_lo_save_thres = rsp.m_lo_save_thres;  
2143 - m_hi_save_thres = rsp.m_hi_save_thres; 2150 + m_caldb = rsp.m_caldb;
  2151 + m_rspname = rsp.m_rspname;
  2152 + m_apply_edisp = rsp.m_apply_edisp;
  2153 + m_lo_save_thres = rsp.m_lo_save_thres;
  2154 + m_hi_save_thres = rsp.m_hi_save_thres;
  2155 + m_use_nroi_cache = rsp.m_use_nroi_cache;
2144 2156
2145 // Copy response filenames 2157 // Copy response filenames
2146 m_xml_caldb = rsp.m_xml_caldb; 2158 m_xml_caldb = rsp.m_xml_caldb;
2147 m_xml_rspname = rsp.m_xml_rspname; 2159 m_xml_rspname = rsp.m_xml_rspname;
2148 2160
2149 - // Copy cache  
2150 - m_npred_names = rsp.m_npred_names;  
2151 - m_npred_energies = rsp.m_npred_energies;  
2152 - m_npred_times = rsp.m_npred_times;  
2153 - m_npred_values = rsp.m_npred_values; 2161 + // Copy nroi cache
  2162 + m_nroi_cache = rsp.m_nroi_cache;
2154 2163
2155 // Clone members 2164 // Clone members
2156 m_aeff = (rsp.m_aeff != NULL) ? rsp.m_aeff->clone() : NULL; 2165 m_aeff = (rsp.m_aeff != NULL) ? rsp.m_aeff->clone() : NULL;
@@ -2715,152 +2724,117 @@ double GCTAResponseIrf::irf_diffuse(const GEvent&amp; event, @@ -2715,152 +2724,117 @@ double GCTAResponseIrf::irf_diffuse(const GEvent&amp; event,
2715 static const int max_iter_phi = 8; 2724 static const int max_iter_phi = 8;
2716 2725
2717 // Initialise IRF value 2726 // Initialise IRF value
2718 - bool has_irf = false;  
2719 - double irf = 0.0; 2727 + double irf = 0.0;
2720 2728
2721 // Retrieve CTA pointing 2729 // Retrieve CTA pointing
2722 const GCTAPointing& pnt = gammalib::cta_pnt(G_IRF_DIFFUSE, obs); 2730 const GCTAPointing& pnt = gammalib::cta_pnt(G_IRF_DIFFUSE, obs);
2723 2731
2724 - // Try getting the IRF value from cache. Do not do this for energy  
2725 - // dispersion since the the spectral model will change the IRF values  
2726 - #if defined(G_USE_IRF_CACHE)  
2727 - const GCTAEventList* list = dynamic_cast<const GCTAEventList*>(obs.events());  
2728 - const GCTAEventAtom* atom = dynamic_cast<const GCTAEventAtom*>(&event);  
2729 - if (list != NULL && atom != NULL && !use_edisp()) {  
2730 - irf = list->irf_cache(source.name(), atom->index());  
2731 - if (irf >= 0.0) {  
2732 - has_irf = true;  
2733 - #if defined(G_DEBUG_IRF_DIFFUSE)  
2734 - std::cout << "GCTAResponseIrf::irf_diffuse:";  
2735 - std::cout << " cached irf=" << irf << std::endl;  
2736 - #endif  
2737 - }  
2738 - else {  
2739 - irf = 0.0;  
2740 - }  
2741 - }  
2742 - #endif  
2743 -  
2744 - // Continue only if we have no IRF value  
2745 - if (!has_irf) { 2732 + // Get CTA instrument direction
  2733 + const GCTAInstDir& dir = gammalib::cta_dir(G_IRF_ELLIPTICAL, event);
2746 2734
2747 - // Get CTA instrument direction  
2748 - const GCTAInstDir& dir = gammalib::cta_dir(G_IRF_ELLIPTICAL, event);  
2749 -  
2750 - // Get pointer on spatial model  
2751 - const GModelSpatial* model =  
2752 - dynamic_cast<const GModelSpatial*>(source.model());  
2753 - if (model == NULL) {  
2754 - throw GCTAException::bad_model_type(G_IRF_DIFFUSE);  
2755 - }  
2756 -  
2757 - // Get resolution of spatial model  
2758 - double resolution = gammalib::resolution(model);  
2759 -  
2760 - // Get event attributes  
2761 - const GEnergy& obsEng = event.energy(); 2735 + // Get pointer on spatial model
  2736 + const GModelSpatial* model =
  2737 + dynamic_cast<const GModelSpatial*>(source.model());
  2738 + if (model == NULL) {
  2739 + throw GCTAException::bad_model_type(G_IRF_DIFFUSE);
  2740 + }
2762 2741
2763 - // Get source attributes  
2764 - const GEnergy& srcEng = source.energy();  
2765 - const GTime& srcTime = source.time(); 2742 + // Get resolution of spatial model
  2743 + double resolution = gammalib::resolution(model);
2766 2744
2767 - // Get pointing direction zenith angle and azimuth [radians]  
2768 - double zenith = pnt.zenith();  
2769 - double azimuth = pnt.azimuth(); 2745 + // Get event attributes
  2746 + const GEnergy& obsEng = event.energy();
2770 2747
2771 - // Determine angular distance between measured photon direction and  
2772 - // pointing direction [radians]  
2773 - double eta = pnt.dir().dist(dir.dir()); 2748 + // Get source attributes
  2749 + const GEnergy& srcEng = source.energy();
  2750 + const GTime& srcTime = source.time();
2774 2751
2775 - // Get log10(E/TeV) of true photon energy  
2776 - double srcLogEng = srcEng.log10TeV(); 2752 + // Get pointing direction zenith angle and azimuth [radians]
  2753 + double zenith = pnt.zenith();
  2754 + double azimuth = pnt.azimuth();
2777 2755
2778 - // Assign the observed theta angle (eta) as the true theta angle  
2779 - // between the source and the pointing directions. This is a (not  
2780 - // too bad) approximation which helps to speed up computations.  
2781 - // If we want to do this correctly, however, we would need to move  
2782 - // the psf_dummy_sigma down to the integration kernel, and we would  
2783 - // need to make sure that psf_delta_max really gives the absolute  
2784 - // maximum (this is certainly less critical)  
2785 - double theta = eta;  
2786 - double phi = 0.0; //TODO: Implement Phi dependence 2756 + // Determine angular distance between measured photon direction and
  2757 + // pointing direction [radians]
  2758 + double eta = pnt.dir().dist(dir.dir());
2787 2759
2788 - // Get maximum PSF radius in radians  
2789 - double delta_max = psf_delta_max(theta, phi, zenith, azimuth, srcLogEng); 2760 + // Get log10(E/TeV) of true photon energy
  2761 + double srcLogEng = srcEng.log10TeV();
2790 2762
2791 - // Perform zenith angle integration if interval is valid  
2792 - if (delta_max > 0.0) { 2763 + // Assign the observed theta angle (eta) as the true theta angle
  2764 + // between the source and the pointing directions. This is a (not
  2765 + // too bad) approximation which helps to speed up computations.
  2766 + // If we want to do this correctly, however, we would need to move
  2767 + // the psf_dummy_sigma down to the integration kernel, and we would
  2768 + // need to make sure that psf_delta_max really gives the absolute
  2769 + // maximum (this is certainly less critical)
  2770 + double theta = eta;
  2771 + double phi = 0.0; //TODO: Implement Phi dependence
2793 2772
2794 - // Compute rotation matrix to convert from coordinates (theta,phi)  
2795 - // in the reference frame of the observed arrival direction into  
2796 - // celestial coordinates  
2797 - GMatrix ry;  
2798 - GMatrix rz;  
2799 - ry.eulery(dir.dir().dec_deg() - 90.0);  
2800 - rz.eulerz(-dir.dir().ra_deg());  
2801 - GMatrix rot = (ry * rz).transpose(); 2773 + // Get maximum PSF radius in radians
  2774 + double delta_max = psf_delta_max(theta, phi, zenith, azimuth, srcLogEng);
2802 2775
2803 - // Setup integration kernel  
2804 - cta_irf_diffuse_kern_theta integrand(this,  
2805 - model,  
2806 - &rot,  
2807 - theta,  
2808 - phi,  
2809 - zenith,  
2810 - azimuth,  
2811 - srcEng,  
2812 - srcTime,  
2813 - srcLogEng,  
2814 - obsEng,  
2815 - eta,  
2816 - min_iter_phi,  
2817 - max_iter_phi,  
2818 - resolution);  
2819 -  
2820 - // Set number of radial integration iterations  
2821 - int iter = gammalib::iter_rho(delta_max, resolution,  
2822 - min_iter_rho, max_iter_rho);  
2823 -  
2824 - // Integrate over Psf delta angle  
2825 - GIntegral integral(&integrand);  
2826 - integral.fixed_iter(iter);  
2827 - irf = integral.romberg(0.0, delta_max); 2776 + // Perform zenith angle integration if interval is valid
  2777 + if (delta_max > 0.0) {
2828 2778
2829 - // Compile option: Check for NaN/Inf  
2830 - #if defined(G_NAN_CHECK)  
2831 - if (gammalib::is_notanumber(irf) || gammalib::is_infinite(irf)) {  
2832 - std::cout << "*** ERROR: GCTAResponseIrf::irf_diffuse:";  
2833 - std::cout << " NaN/Inf encountered";  
2834 - std::cout << " (irf=" << irf;  
2835 - std::cout << ", delta_max=" << delta_max << ")";  
2836 - std::cout << std::endl;  
2837 - }  
2838 - #endif  
2839 - } 2779 + // Compute rotation matrix to convert from coordinates (theta,phi)
  2780 + // in the reference frame of the observed arrival direction into
  2781 + // celestial coordinates
  2782 + GMatrix ry;
  2783 + GMatrix rz;
  2784 + ry.eulery(dir.dir().dec_deg() - 90.0);
  2785 + rz.eulerz(-dir.dir().ra_deg());
  2786 + GMatrix rot = (ry * rz).transpose();
2840 2787
2841 - // Apply deadtime correction  
2842 - irf *= obs.deadc(srcTime); 2788 + // Setup integration kernel
  2789 + cta_irf_diffuse_kern_theta integrand(this,
  2790 + model,
  2791 + &rot,
  2792 + theta,
  2793 + phi,
  2794 + zenith,
  2795 + azimuth,
  2796 + srcEng,
  2797 + srcTime,
  2798 + srcLogEng,
  2799 + obsEng,
  2800 + eta,
  2801 + min_iter_phi,
  2802 + max_iter_phi,
  2803 + resolution);
  2804 +
  2805 + // Set number of radial integration iterations
  2806 + int iter = gammalib::iter_rho(delta_max, resolution,
  2807 + min_iter_rho, max_iter_rho);
  2808 +
  2809 + // Integrate over Psf delta angle
  2810 + GIntegral integral(&integrand);
  2811 + integral.fixed_iter(iter);
  2812 + irf = integral.romberg(0.0, delta_max);
2843 2813
2844 - // Put IRF value in cache. Only do this in no energy dispersion is  
2845 - // requested, since for energy dispersion the IRF values will  
2846 - // depend on the spectrum  
2847 - #if defined(G_USE_IRF_CACHE)  
2848 - if (list != NULL && atom != NULL && !use_edisp()) {  
2849 - list->irf_cache(source.name(), atom->index(), irf); 2814 + // Compile option: Check for NaN/Inf
  2815 + #if defined(G_NAN_CHECK)
  2816 + if (gammalib::is_notanumber(irf) || gammalib::is_infinite(irf)) {
  2817 + std::cout << "*** ERROR: GCTAResponseIrf::irf_diffuse:";
  2818 + std::cout << " NaN/Inf encountered";
  2819 + std::cout << " (irf=" << irf;
  2820 + std::cout << ", delta_max=" << delta_max << ")";
  2821 + std::cout << std::endl;
2850 } 2822 }
2851 #endif 2823 #endif
  2824 + }
2852 2825
2853 - // Compile option: Show integration results  
2854 - #if defined(G_DEBUG_IRF_DIFFUSE)  
2855 - std::cout << "GCTAResponseIrf::irf_diffuse:";  
2856 - std::cout << " srcLogEng=" << srcLogEng;  
2857 - std::cout << " obsLogEng=" << obsLogEng;  
2858 - std::cout << " eta=" << eta;  
2859 - std::cout << " delta_max=" << delta_max;  
2860 - std::cout << " irf=" << irf << std::endl;  
2861 - #endif 2826 + // Apply deadtime correction
  2827 + irf *= obs.deadc(srcTime);
2862 2828
2863 - } // endif: has no IRF 2829 + // Compile option: Show integration results
  2830 + #if defined(G_DEBUG_IRF_DIFFUSE)
  2831 + std::cout << "GCTAResponseIrf::irf_diffuse:";
  2832 + std::cout << " srcLogEng=" << srcLogEng;
  2833 + std::cout << " obsLogEng=" << obsLogEng;
  2834 + std::cout << " eta=" << eta;
  2835 + std::cout << " delta_max=" << delta_max;
  2836 + std::cout << " irf=" << irf << std::endl;
  2837 + #endif
2864 2838
2865 // Return IRF value 2839 // Return IRF value
2866 return irf; 2840 return irf;
@@ -3326,137 +3300,91 @@ double GCTAResponseIrf::nroi_diffuse(const GModelSky&amp; model, @@ -3326,137 +3300,91 @@ double GCTAResponseIrf::nroi_diffuse(const GModelSky&amp; model,
3326 static const int iter_phi = 9; 3300 static const int iter_phi = 9;
3327 3301
3328 // Initialise Nroi value 3302 // Initialise Nroi value
3329 - double nroi = 0.0;  
3330 - bool has_nroi = false;  
3331 -  
3332 - // Build unique identifier  
3333 - std::string id = model.name() + "::" + obs.id();  
3334 -  
3335 - // Check if Nroi value is already in cache. Do not use the Npred cache  
3336 - // for energy dispersion since the actual value will depend on the  
3337 - // spectral model  
3338 - #if defined(G_USE_NPRED_CACHE)  
3339 - if (!m_npred_names.empty() && !use_edisp()) {  
3340 -  
3341 - // Search for unique identifier, and if found, recover Npred value  
3342 - // and break  
3343 - for (int i = 0; i < m_npred_names.size(); ++i) {  
3344 - if (m_npred_names[i] == id &&  
3345 - m_npred_energies[i] == srcEng &&  
3346 - m_npred_times[i] == srcTime) {  
3347 - nroi = m_npred_values[i];  
3348 - has_nroi = true;  
3349 - #if defined(G_DEBUG_NROI_DIFFUSE)  
3350 - std::cout << "GCTAResponseIrf::nroi_diffuse:";  
3351 - std::cout << " cache=" << i;  
3352 - std::cout << " nroi=" << nroi << std::endl;  
3353 - #endif  
3354 - break;  
3355 - }  
3356 - }  
3357 -  
3358 - } // endif: there were values in the Npred cache  
3359 - #endif  
3360 -  
3361 - // Continue only if no Npred cache value was found  
3362 - if (!has_nroi) { 3303 + double nroi = 0.0;
3363 3304
3364 - // Retrieve CTA observation, ROI and pointing  
3365 - const GCTAObservation& cta = gammalib::cta_obs(G_NROI_DIFFUSE, obs);  
3366 - const GCTARoi& roi = gammalib::cta_event_list(G_NROI_DIFFUSE, obs).roi();  
3367 - const GCTAPointing& pnt = cta.pointing(); 3305 + // Retrieve CTA observation, ROI and pointing
  3306 + const GCTAObservation& cta = gammalib::cta_obs(G_NROI_DIFFUSE, obs);
  3307 + const GCTARoi& roi = gammalib::cta_event_list(G_NROI_DIFFUSE, obs).roi();
  3308 + const GCTAPointing& pnt = cta.pointing();
3368 3309
3369 - // Get pointer on spatial model  
3370 - const GModelSpatial* spatial =  
3371 - dynamic_cast<const GModelSpatial*>(model.spatial());  
3372 - if (spatial == NULL) {  
3373 - throw GCTAException::bad_model_type(G_NROI_DIFFUSE);  
3374 - } 3310 + // Get pointer on spatial model
  3311 + const GModelSpatial* spatial =
  3312 + dynamic_cast<const GModelSpatial*>(model.spatial());
  3313 + if (spatial == NULL) {
  3314 + throw GCTAException::bad_model_type(G_NROI_DIFFUSE);
  3315 + }
3375 3316
3376 - // Get pointing direction zenith angle and azimuth [radians]  
3377 - double zenith = pnt.zenith();  
3378 - double azimuth = pnt.azimuth(); 3317 + // Get pointing direction zenith angle and azimuth [radians]
  3318 + double zenith = pnt.zenith();
  3319 + double azimuth = pnt.azimuth();
3379 3320
3380 - // Get log10(E/TeV) of true photon energy  
3381 - double srcLogEng = srcEng.log10TeV(); 3321 + // Get log10(E/TeV) of true photon energy
  3322 + double srcLogEng = srcEng.log10TeV();
3382 3323
3383 - // Get maximum PSF radius (radians). We do this for the onaxis PSF only,  
3384 - // as this allows us doing this computation in the outer loop. This  
3385 - // should be sufficient here, unless the offaxis PSF becomes much worse  
3386 - // than the onaxis PSF. In this case, we may add a safety factor here  
3387 - // to make sure we encompass the entire PSF.  
3388 - double psf_max_radius = psf_delta_max(0.0, 0.0, zenith, azimuth, srcLogEng); 3324 + // Get maximum PSF radius (radians). We do this for the onaxis PSF only,
  3325 + // as this allows us doing this computation in the outer loop. This
  3326 + // should be sufficient here, unless the offaxis PSF becomes much worse
  3327 + // than the onaxis PSF. In this case, we may add a safety factor here
  3328 + // to make sure we encompass the entire PSF.
  3329 + double psf_max_radius = psf_delta_max(0.0, 0.0, zenith, azimuth, srcLogEng);
3389 3330
3390 - // Extract ROI radius (radians)  
3391 - double roi_radius = roi.radius() * gammalib::deg2rad; 3331 + // Extract ROI radius (radians)
  3332 + double roi_radius = roi.radius() * gammalib::deg2rad;
3392 3333
3393 - // Compute the ROI radius plus maximum PSF radius (radians). Any photon  
3394 - // coming from beyond this radius will not make it in the dataspace and  
3395 - // thus can be neglected.  
3396 - double roi_psf_radius = roi_radius + psf_max_radius; 3334 + // Compute the ROI radius plus maximum PSF radius (radians). Any photon
  3335 + // coming from beyond this radius will not make it in the dataspace and
  3336 + // thus can be neglected.
  3337 + double roi_psf_radius = roi_radius + psf_max_radius;
3397 3338
3398 - // Perform offset angle integration only if interval is valid  
3399 - if (roi_psf_radius > 0.0) { 3339 + // Perform offset angle integration only if interval is valid
  3340 + if (roi_psf_radius > 0.0) {
3400 3341
3401 - // Compute rotation matrix to convert from native ROI coordinates,  
3402 - // given by (theta,phi), into celestial coordinates.  
3403 - GMatrix ry;  
3404 - GMatrix rz;  
3405 - ry.eulery(roi.centre().dir().dec_deg() - 90.0);  
3406 - rz.eulerz(-roi.centre().dir().ra_deg());  
3407 - GMatrix rot = (ry * rz).transpose(); 3342 + // Compute rotation matrix to convert from native ROI coordinates,
  3343 + // given by (theta,phi), into celestial coordinates.
  3344 + GMatrix ry;
  3345 + GMatrix rz;
  3346 + ry.eulery(roi.centre().dir().dec_deg() - 90.0);
  3347 + rz.eulerz(-roi.centre().dir().ra_deg());
  3348 + GMatrix rot = (ry * rz).transpose();
3408 3349
3409 - // Setup integration kernel  
3410 - cta_nroi_diffuse_kern_theta integrand(this,  
3411 - &cta,  
3412 - spatial,  
3413 - &rot,  
3414 - srcEng,  
3415 - srcTime,  
3416 - obsEng,  
3417 - obsTime,  
3418 - iter_phi);  
3419 -  
3420 - // Integrate over model's zenith angle  
3421 - GIntegral integral(&integrand);  
3422 - integral.fixed_iter(iter_rho);  
3423 - nroi = integral.romberg(0.0, roi_psf_radius);  
3424 -  
3425 - // Compile option: Show integration results  
3426 - #if defined(G_DEBUG_NROI_DIFFUSE)  
3427 - std::cout << "GCTAResponseIrf::nroi_diffuse:";  
3428 - std::cout << " roi_psf_radius=" << roi_psf_radius;  
3429 - std::cout << " nroi=" << nroi;  
3430 - std::cout << " Etrue=" << srcEng;  
3431 - std::cout << " Ereco=" << obsEng;  
3432 - std::cout << " id=" << id << std::endl;  
3433 - #endif 3350 + // Setup integration kernel
  3351 + cta_nroi_diffuse_kern_theta integrand(this,
  3352 + &cta,
  3353 + spatial,
  3354 + &rot,
  3355 + srcEng,
  3356 + srcTime,
  3357 + obsEng,
  3358 + obsTime,
  3359 + iter_phi);
3434 3360
3435 - } // endif: offset angle range was valid 3361 + // Integrate over model's zenith angle
  3362 + GIntegral integral(&integrand);
  3363 + integral.fixed_iter(iter_rho);
  3364 + nroi = integral.romberg(0.0, roi_psf_radius);
3436 3365
3437 - // Store result in Npred cache in case that no energy dispersion  
3438 - // is requested  
3439 - #if defined(G_USE_NPRED_CACHE)  
3440 - if (!use_edisp()) {  
3441 - m_npred_names.push_back(id);  
3442 - m_npred_energies.push_back(srcEng);  
3443 - m_npred_times.push_back(srcTime);  
3444 - m_npred_values.push_back(nroi);  
3445 - } 3366 + // Compile option: Show integration results
  3367 + #if defined(G_DEBUG_NROI_DIFFUSE)
  3368 + std::cout << "GCTAResponseIrf::nroi_diffuse:";
  3369 + std::cout << " roi_psf_radius=" << roi_psf_radius;
  3370 + std::cout << " nroi=" << nroi;
  3371 + std::cout << " Etrue=" << srcEng;
  3372 + std::cout << " Ereco=" << obsEng;
  3373 + std::cout << " id=" << id << std::endl;
3446 #endif 3374 #endif
3447 3375
3448 - // Debug: Check for NaN  
3449 - #if defined(G_NAN_CHECK)  
3450 - if (gammalib::is_notanumber(nroi) || gammalib::is_infinite(nroi)) {  
3451 - std::cout << "*** ERROR: GCTAResponseIrf::nroi_diffuse:";  
3452 - std::cout << " NaN/Inf encountered";  
3453 - std::cout << " (nroi=" << nroi;  
3454 - std::cout << ", roi_psf_radius=" << roi_psf_radius;  
3455 - std::cout << ")" << std::endl;  
3456 - }  
3457 - #endif 3376 + } // endif: offset angle range was valid
3458 3377
3459 - } // endif: Nroi computation required 3378 + // Debug: Check for NaN
  3379 + #if defined(G_NAN_CHECK)
  3380 + if (gammalib::is_notanumber(nroi) || gammalib::is_infinite(nroi)) {
  3381 + std::cout << "*** ERROR: GCTAResponseIrf::nroi_diffuse:";
  3382 + std::cout << " NaN/Inf encountered";
  3383 + std::cout << " (nroi=" << nroi;
  3384 + std::cout << ", roi_psf_radius=" << roi_psf_radius;
  3385 + std::cout << ")" << std::endl;
  3386 + }
  3387 + #endif
3460 3388
3461 // Return Nroi 3389 // Return Nroi
3462 return nroi; 3390 return nroi;