Commit c238a196d2e5259c529643eee2963543d33d169e

Authored by Jürgen Knödlseder
1 parent 7764d813

Correct transition point for shell model (was in degrees but the method expected…

… radians; this led to convergence problems with the shell model).
inst/cta/include/GCTAResponseCube.hpp
@@ -111,7 +111,7 @@ private: @@ -111,7 +111,7 @@ private:
111 void free_members(void); 111 void free_members(void);
112 int cache_index(const std::string& name) const; 112 int cache_index(const std::string& name) const;
113 double psf_radial(const GModelSpatialRadial* model, 113 double psf_radial(const GModelSpatialRadial* model,
114 - const double& zeta, 114 + const double& rho_obs,
115 const GSkyDir& obsDir, 115 const GSkyDir& obsDir,
116 const GEnergy& srcEng, 116 const GEnergy& srcEng,
117 const GTime& srcTime) const; 117 const GTime& srcTime) const;
inst/cta/src/GCTAResponseCube.cpp
@@ -37,6 +37,7 @@ @@ -37,6 +37,7 @@
37 #include "GCTASourceCubeDiffuse.hpp" 37 #include "GCTASourceCubeDiffuse.hpp"
38 #include "GModelSpatialPointSource.hpp" 38 #include "GModelSpatialPointSource.hpp"
39 #include "GModelSpatialRadial.hpp" 39 #include "GModelSpatialRadial.hpp"
  40 +#include "GModelSpatialRadialShell.hpp"
40 #include "GModelSpatialElliptical.hpp" 41 #include "GModelSpatialElliptical.hpp"
41 #include "GModelSpatialDiffuse.hpp" 42 #include "GModelSpatialDiffuse.hpp"
42 #include "GPhoton.hpp" 43 #include "GPhoton.hpp"
@@ -63,8 +64,6 @@ @@ -63,8 +64,6 @@
63 /* __ Macros _____________________________________________________________ */ 64 /* __ Macros _____________________________________________________________ */
64 65
65 /* __ Coding definitions _________________________________________________ */ 66 /* __ Coding definitions _________________________________________________ */
66 -#define G_NEW_PSF_RADIAL  
67 -#define G_NEW_PSF_ELLIPTICAL  
68 67
69 /* __ Debug definitions __________________________________________________ */ 68 /* __ Debug definitions __________________________________________________ */
70 69
@@ -464,11 +463,11 @@ double GCTAResponseCube::irf_radial(const GEvent& event, @@ -464,11 +463,11 @@ double GCTAResponseCube::irf_radial(const GEvent& event,
464 463
465 // Compute angle between model centre and measured photon direction 464 // Compute angle between model centre and measured photon direction
466 // (radians) 465 // (radians)
467 - double zeta = model->dir().dist(obsDir); 466 + double rho_obs = model->dir().dist(obsDir);
468 467
469 // Continue only if we're sufficiently close to the model centre to get a 468 // Continue only if we're sufficiently close to the model centre to get a
470 // non-zero response 469 // non-zero response
471 - if (zeta <= model->theta_max()+psf().delta_max()) { 470 + if (rho_obs <= model->theta_max()+psf().delta_max()) {
472 471
473 // Get exposure 472 // Get exposure
474 irf = exposure()(obsDir, obsEng); 473 irf = exposure()(obsDir, obsEng);
@@ -476,7 +475,7 @@ double GCTAResponseCube::irf_radial(const GEvent&amp; event, @@ -476,7 +475,7 @@ double GCTAResponseCube::irf_radial(const GEvent&amp; event,
476 // Continue only if exposure is positive 475 // Continue only if exposure is positive
477 if (irf > 0.0) { 476 if (irf > 0.0) {
478 irf /= obs.livetime(); //!< Recover effective area from exposure 477 irf /= obs.livetime(); //!< Recover effective area from exposure
479 - irf *= psf_radial(model, zeta, obsDir, obsEng, obsTime); 478 + irf *= psf_radial(model, rho_obs, obsDir, obsEng, obsTime);
480 irf *= obs.deadc(obsTime); 479 irf *= obs.deadc(obsTime);
481 } 480 }
482 481
@@ -535,8 +534,8 @@ double GCTAResponseCube::irf_elliptical(const GEvent&amp; event, @@ -535,8 +534,8 @@ double GCTAResponseCube::irf_elliptical(const GEvent&amp; event,
535 534
536 // Compute angle between model centre and measured photon direction and 535 // Compute angle between model centre and measured photon direction and
537 // position angle (radians) 536 // position angle (radians)
538 - double rho_obs = model->dir().dist(obsDir); // was zeta  
539 - double posangle_obs = model->dir().posang(obsDir); // was omega 537 + double rho_obs = model->dir().dist(obsDir);
  538 + double posangle_obs = model->dir().posang(obsDir);
540 539
541 // Continue only if we're sufficiently close to the model centre to get a 540 // Continue only if we're sufficiently close to the model centre to get a
542 // non-zero response 541 // non-zero response
@@ -927,8 +926,8 @@ int GCTAResponseCube::cache_index(const std::string&amp; name) const @@ -927,8 +926,8 @@ int GCTAResponseCube::cache_index(const std::string&amp; name) const
927 * @brief Integrate PSF over radial model 926 * @brief Integrate PSF over radial model
928 * 927 *
929 * @param[in] model Radial model. 928 * @param[in] model Radial model.
930 - * @param[in] zeta Angle between model centre and measured photon direction  
931 - * (radians). 929 + * @param[in] rho_obs Angle between model centre and measured photon direction
  930 + * (radians).
932 * @param[in] obsDir Observed event direction. 931 * @param[in] obsDir Observed event direction.
933 * @param[in] srcEng True photon energy. 932 * @param[in] srcEng True photon energy.
934 * @param[in] srcTime True photon arrival time. 933 * @param[in] srcTime True photon arrival time.
@@ -949,9 +948,8 @@ int GCTAResponseCube::cache_index(const std::string&amp; name) const @@ -949,9 +948,8 @@ int GCTAResponseCube::cache_index(const std::string&amp; name) const
949 * \f${\rm PSF}(\delta)\f$ is the azimuthally symmetric point spread 948 * \f${\rm PSF}(\delta)\f$ is the azimuthally symmetric point spread
950 * function. 949 * function.
951 ***************************************************************************/ 950 ***************************************************************************/
952 -#if defined(G_NEW_PSF_ELLIPTICAL)  
953 double GCTAResponseCube::psf_radial(const GModelSpatialRadial* model, 951 double GCTAResponseCube::psf_radial(const GModelSpatialRadial* model,
954 - const double& rho_obs, // was zeta 952 + const double& rho_obs,
955 const GSkyDir& obsDir, 953 const GSkyDir& obsDir,
956 const GEnergy& srcEng, 954 const GEnergy& srcEng,
957 const GTime& srcTime) const 955 const GTime& srcTime) const
@@ -968,7 +966,7 @@ double GCTAResponseCube::psf_radial(const GModelSpatialRadial* model, @@ -968,7 +966,7 @@ double GCTAResponseCube::psf_radial(const GModelSpatialRadial* model,
968 // Get maximum PSF radius (radians) 966 // Get maximum PSF radius (radians)
969 double delta_max = psf().delta_max(); 967 double delta_max = psf().delta_max();
970 968
971 - // Set zenith angle integration range for elliptical model (radians) 969 + // Set zenith angle integration range for radial model (radians)
972 double rho_min = (rho_obs > delta_max) ? rho_obs - delta_max : 0.0; 970 double rho_min = (rho_obs > delta_max) ? rho_obs - delta_max : 0.0;
973 double rho_max = rho_obs + delta_max; 971 double rho_max = rho_obs + delta_max;
974 double src_max = model->theta_max(); 972 double src_max = model->theta_max();
@@ -1016,6 +1014,17 @@ double GCTAResponseCube::psf_radial(const GModelSpatialRadial* model, @@ -1016,6 +1014,17 @@ double GCTAResponseCube::psf_radial(const GModelSpatialRadial* model,
1016 bounds.push_back(transition_point); 1014 bounds.push_back(transition_point);
1017 } 1015 }
1018 1016
  1017 + // If we have a shell model then add an integration boundary for the
  1018 + // shell radius as a function discontinuity will occur at this
  1019 + // location
  1020 + const GModelSpatialRadialShell* shell = dynamic_cast<const GModelSpatialRadialShell*>(model);
  1021 + if (shell != NULL) {
  1022 + double shell_radius = shell->radius() * gammalib::deg2rad;
  1023 + if (shell_radius > rho_min && shell_radius < rho_max) {
  1024 + bounds.push_back(shell_radius);
  1025 + }
  1026 + }
  1027 +
1019 // Integrate kernel 1028 // Integrate kernel
1020 irf = integral.romberg(bounds, iter_rho); 1029 irf = integral.romberg(bounds, iter_rho);
1021 1030
@@ -1036,49 +1045,6 @@ double GCTAResponseCube::psf_radial(const GModelSpatialRadial* model, @@ -1036,49 +1045,6 @@ double GCTAResponseCube::psf_radial(const GModelSpatialRadial* model,
1036 // Return PSF 1045 // Return PSF
1037 return irf; 1046 return irf;
1038 } 1047 }
1039 -#else  
1040 -double GCTAResponseCube::psf_radial(const GModelSpatialRadial* model,  
1041 - const double& zeta,  
1042 - const GSkyDir& obsDir,  
1043 - const GEnergy& srcEng,  
1044 - const GTime& srcTime) const  
1045 -{  
1046 - // Set integration precision and Romberg order. These values have been  
1047 - // determined after careful testing, see  
1048 - // https://cta-redmine.irap.omp.eu/issues/1291  
1049 - const double eps_delta = 1.0e-2;  
1050 - const int order_delta = 5;  
1051 - const double eps_phi = 1.0e-2;  
1052 - const int order_phi = 5;  
1053 -  
1054 - // Initialise value  
1055 - double value = 0.0;  
1056 -  
1057 - // Compute sin and cosine of angle between model centre and measured  
1058 - // photon direction  
1059 - double sin_zeta = std::sin(zeta);  
1060 - double cos_zeta = std::cos(zeta);  
1061 -  
1062 - // Get offset angle integration interval in radians  
1063 - double delta_min = 0.0;  
1064 - double delta_max = psf().delta_max();  
1065 -  
1066 - // Setup integration kernel. We take here the observed photon arrival  
1067 - // direction as the true photon arrival direction because the PSF does  
1068 - // not vary significantly over a small region.  
1069 - cta_psf_radial_kern_delta integrand(this, model, obsDir, srcEng, srcTime,  
1070 - sin_zeta, cos_zeta,  
1071 - eps_phi, order_phi);  
1072 -  
1073 - // Integrate over PSF delta angle  
1074 - GIntegral integral(&integrand);  
1075 - integral.eps(eps_delta);  
1076 - value = integral.romberg(delta_min, delta_max, order_delta);  
1077 -  
1078 - // Return PSF  
1079 - return value;  
1080 -}  
1081 -#endif  
1082 1048
1083 1049
1084 /***********************************************************************//** 1050 /***********************************************************************//**
@@ -1124,7 +1090,6 @@ double GCTAResponseCube::psf_radial(const GModelSpatialRadial* model, @@ -1124,7 +1090,6 @@ double GCTAResponseCube::psf_radial(const GModelSpatialRadial* model,
1124 * \f${\rm PSF}(\delta)\f$ is the azimuthally symmetric point spread 1090 * \f${\rm PSF}(\delta)\f$ is the azimuthally symmetric point spread
1125 * function. 1091 * function.
1126 ***************************************************************************/ 1092 ***************************************************************************/
1127 -#if defined(G_NEW_PSF_ELLIPTICAL)  
1128 double GCTAResponseCube::psf_elliptical(const GModelSpatialElliptical* model, 1093 double GCTAResponseCube::psf_elliptical(const GModelSpatialElliptical* model,
1129 const double& rho_obs, 1094 const double& rho_obs,
1130 const double& posangle_obs, 1095 const double& posangle_obs,
@@ -1201,16 +1166,6 @@ double GCTAResponseCube::psf_elliptical(const GModelSpatialElliptical* model, @@ -1201,16 +1166,6 @@ double GCTAResponseCube::psf_elliptical(const GModelSpatialElliptical* model,
1201 bounds.push_back(rho_min); 1166 bounds.push_back(rho_min);
1202 bounds.push_back(rho_max); 1167 bounds.push_back(rho_max);
1203 1168
1204 - // If the integration range includes a transition between full  
1205 - // containment and partial containment, then add a boundary at  
1206 - // this location  
1207 - /*  
1208 - double transition_point = delta_max - rho_obs;  
1209 - if (transition_point > rho_min && transition_point < rho_max) {  
1210 - bounds.push_back(transition_point);  
1211 - }  
1212 - */  
1213 -  
1214 // If the integration range includes the semiminor boundary, then 1169 // If the integration range includes the semiminor boundary, then
1215 // add an integration boundary at that location 1170 // add an integration boundary at that location
1216 if (semiminor > rho_min && semiminor < rho_max) { 1171 if (semiminor > rho_min && semiminor < rho_max) {
@@ -1237,47 +1192,3 @@ double GCTAResponseCube::psf_elliptical(const GModelSpatialElliptical* model, @@ -1237,47 +1192,3 @@ double GCTAResponseCube::psf_elliptical(const GModelSpatialElliptical* model,
1237 // Return PSF 1192 // Return PSF
1238 return irf; 1193 return irf;
1239 } 1194 }
1240 -#else  
1241 -double GCTAResponseCube::psf_elliptical(const GModelSpatialElliptical* model,  
1242 - const double& rho_obs,  
1243 - const double& posangle_obs,  
1244 - const GSkyDir& obsDir,  
1245 - const GEnergy& srcEng,  
1246 - const GTime& srcTime) const  
1247 -{  
1248 - // Set integration precision and Romberg order. These values have been  
1249 - // determined after careful testing, see  
1250 - // https://cta-redmine.irap.omp.eu/issues/1291  
1251 - const double eps_delta = 1.0e-2;  
1252 - const int order_delta = 5;  
1253 - const double eps_phi = 1.0e-2;  
1254 - const int order_phi = 5;  
1255 -  
1256 - // Initialise value  
1257 - double value = 0.0;  
1258 -  
1259 - // Compute sin and cosine of angle between model centre and measured  
1260 - // photon direction  
1261 - double sin_rho_obs = std::sin(rho_obs);  
1262 - double cos_rho_obs = std::cos(rho_obs);  
1263 -  
1264 - // Get offset angle integration interval in radians  
1265 - double delta_min = 0.0;  
1266 - double delta_max = psf().delta_max();  
1267 -  
1268 - // Setup integration kernel. We take here the observed photon arrival  
1269 - // direction as the true photon arrival direction because the PSF does  
1270 - // not vary significantly over a small region.  
1271 - cta_psf_elliptical_kern_delta integrand(this, model, obsDir, srcEng, srcTime,  
1272 - sin_rho_obs, cos_rho_obs, posangle_obs,  
1273 - eps_phi, order_phi);  
1274 -  
1275 - // Integrate over PSF delta angle  
1276 - GIntegral integral(&integrand);  
1277 - integral.eps(eps_delta);  
1278 - value = integral.romberg(delta_min, delta_max, order_delta);  
1279 -  
1280 - // Return PSF  
1281 - return value;  
1282 -}  
1283 -#endif  
inst/cta/src/GCTAResponseIrf.cpp
@@ -1282,174 +1282,6 @@ std::string GCTAResponseIrf::print(const GChatter&amp; chatter) const @@ -1282,174 +1282,6 @@ std::string GCTAResponseIrf::print(const GChatter&amp; chatter) const
1282 * @exception GCTAException::bad_model_type 1282 * @exception GCTAException::bad_model_type
1283 * Model is not a radial model. 1283 * Model is not a radial model.
1284 * 1284 *
1285 - * Integrates the product of the model and the IRF over the true photon  
1286 - * arrival direction using  
1287 - *  
1288 - * \f[  
1289 - * \int_{\rho_{\rm min}}^{\rho_{\rm max}}  
1290 - * \sin \rho \times S_{\rm p}(\rho | E, t) \times  
1291 - * \int_{\omega_{\rm min}}^{\omega_{\rm max}}  
1292 - * IRF(\rho, \omega) d\omega d\rho  
1293 - * \f]  
1294 - *  
1295 - * where  
1296 - * - \f$S_{\rm p}(\rho | E, t)\f$ is the radial model,  
1297 - * - \f$IRF(\rho, \omega)\f$ is the IRF  
1298 - * - \f$\rho\f$ is the distance from the model centre, and  
1299 - * - \f$\omega\f$ is the azimuth angle is the position angle with respect to  
1300 - * the connecting line between the model centre and the observed photon  
1301 - * arrival direction.  
1302 - *  
1303 - * The integration is performed in the coordinate system of the source  
1304 - * model spanned by \f$\rho\f$ and \f$\omega\f$ which allows to benefit  
1305 - * from the symmetry of the source model.  
1306 - *  
1307 - * The source centre is located at \f$\vec{m}\f$, and a spherical system  
1308 - * is defined around this location with \f$(\omega,\rho)\f$ being the  
1309 - * azimuth and zenith angles, respectively. \f$\omega=0\f$ is defined  
1310 - * by the direction that connects the source centre \f$\vec{m}\f$ to the  
1311 - * measured photon direction \f$\vec{p'}\f$, and \f$\omega\f$ increases  
1312 - * counterclockwise.  
1313 - *  
1314 - * Note that this method approximates the true theta angle (angle between  
1315 - * incident photon and pointing direction) by the measured theta angle  
1316 - * (angle between the measured photon arrival direction and the pointing  
1317 - * direction). Given the slow variation of the PSF shape over the field of  
1318 - * view, this approximation should be fine. It helps in fact a lot in  
1319 - * speeding up the computations.  
1320 - ***************************************************************************/  
1321 -#if defined(G_USE_PSF_SYSTEM)  
1322 -double GCTAResponseIrf::irf_radial(const GEvent& event,  
1323 - const GSource& source,  
1324 - const GObservation& obs) const  
1325 -{  
1326 - // Set integration precision and Romberg order. These values have been  
1327 - // determined after careful testing, see  
1328 - // https://cta-redmine.irap.omp.eu/issues/1299  
1329 - static const int iter_delta = 5;  
1330 - static const int iter_phi = 5;  
1331 -  
1332 - // Set a tiny angle (in radians) to be removed from the model boundary  
1333 - // to avoid requesting values beyond the boundary due to round-off  
1334 - // errors (this could produce a discontinuity that the integrator does  
1335 - // not like)  
1336 - static const double tiny = 1.0e-9;  
1337 -  
1338 - // Initialise Irf  
1339 - double irf = 0.0;  
1340 -  
1341 - // Retrieve CTA pointing  
1342 - const GCTAPointing& pnt = retrieve_pnt(G_IRF_RADIAL, obs);  
1343 - const GCTAInstDir& dir = retrieve_dir(G_IRF_RADIAL, event);  
1344 -  
1345 - // Get pointer to radial model  
1346 - const GModelSpatialRadial* model = static_cast<const GModelSpatialRadial*>(source.model());  
1347 -  
1348 - // Get event attributes  
1349 - const GSkyDir& obsDir = dir.dir();  
1350 - const GEnergy& obsEng = event.energy();  
1351 -  
1352 - // Get source attributes  
1353 - const GEnergy& srcEng = source.energy();  
1354 - const GTime& srcTime = source.time();  
1355 - double srcLogEng = srcEng.log10TeV();  
1356 -  
1357 - // Angle between measured photon and model centre (radians)  
1358 - double zeta = obsDir.dist(model->dir());  
1359 -  
1360 - // Angle between measured photon and pointing (radians)  
1361 - double obsOffset = obsDir.dist(pnt.dir());  
1362 -  
1363 - // Compute position angle of pointing in Psf system (radians).  
1364 - // The angle Phi=0 of the Psf system is given by the connecting line  
1365 - // between the measured photon direction and the model centre  
1366 - double phi0 = obsDir.posang(pnt.dir()) - obsDir.posang(model->dir());  
1367 -  
1368 - // Get Psf delta integration interval [delta_min, delta_max] (radians)  
1369 - double theta_max = model->theta_max() - tiny;  
1370 - double delta_min = (zeta > theta_max) ? zeta - theta_max : 0.0;  
1371 - double delta_max = psf_delta_max(obsOffset, 0.0,  
1372 - pnt.zenith(), pnt.azimuth(),  
1373 - srcLogEng);  
1374 - double distance = zeta + theta_max;  
1375 - if (distance < delta_max) {  
1376 - delta_max = distance;  
1377 - }  
1378 -  
1379 - // Continue only if interval has positive length  
1380 - if (delta_max > delta_min) {  
1381 -  
1382 - // Setup integration kernel.  
1383 - cta_irf_radial_kern_delta integrand(this, model, pnt,  
1384 - srcEng, srcLogEng, srcTime,  
1385 - obsOffset, obsEng,  
1386 - zeta, phi0, theta_max,  
1387 - iter_phi);  
1388 -  
1389 - // Setup integration over Psf delta angle  
1390 - GIntegral integral(&integrand);  
1391 - integral.fixed_iter(iter_delta);  
1392 -  
1393 - // Setup integration boundaries  
1394 - std::vector<double> bounds;  
1395 - bounds.push_back(delta_min);  
1396 - bounds.push_back(delta_max);  
1397 -  
1398 - // If the integration range includes a transition between full  
1399 - // containment and partial containment, then add a boundary at  
1400 - // this location  
1401 - double transition_point = theta_max - zeta;  
1402 - if (transition_point > delta_min && transition_point < delta_max) {  
1403 - bounds.push_back(transition_point);  
1404 - }  
1405 -  
1406 - // Integrate kernel  
1407 - irf = integral.romberg(bounds, iter_delta);  
1408 -  
1409 - // Compile option: Check for NaN/Inf  
1410 - #if defined(G_NAN_CHECK)  
1411 - if (gammalib::is_notanumber(irf) || gammalib::is_infinite(irf)) {  
1412 - std::cout << "*** ERROR: GCTAResponseIrf::irf_radial:";  
1413 - std::cout << " NaN/Inf encountered";  
1414 - std::cout << " (irf=" << irf;  
1415 - std::cout << ", delta_min=" << delta_min;  
1416 - std::cout << ", delta_max=" << delta_max << ")";  
1417 - std::cout << std::endl;  
1418 - }  
1419 - #endif  
1420 -  
1421 - // Apply deadtime correction  
1422 - irf *= obs.deadc(srcTime);  
1423 -  
1424 - } // endif: delta interval has positive length  
1425 -  
1426 - // Compile option: Show integration results  
1427 - #if defined(G_DEBUG_IRF_RADIAL)  
1428 - std::cout << "GCTAResponseIrf::irf_radial:";  
1429 - std::cout << " delta=[" << delta_min*gammalib::rad2deg;  
1430 - std::cout << ", " << delta_max*gammalib::rad2deg << " deg;";  
1431 - std::cout << " zeta=" << zeta*gammalib::rad2deg << " deg;";  
1432 - std::cout << " phi0=" << phi0*gammalib::rad2deg << " deg;";  
1433 - std::cout << " theta_max=" << theta_max*gammalib::rad2deg << " deg;";  
1434 - std::cout << " obsDir=" << obsDir << ";";  
1435 - std::cout << " modelDir=" << model->dir() << ";";  
1436 - std::cout << " irf=" << irf << std::endl;  
1437 - #endif  
1438 -  
1439 - // Return Irf value  
1440 - return irf;  
1441 -}  
1442 -#else  
1443 -/***********************************************************************//**  
1444 - * @brief Return IRF value for radial source model  
1445 - *  
1446 - * @param[in] event Observed event.  
1447 - * @param[in] source Source.  
1448 - * @param[in] obs Observation.  
1449 - *  
1450 - * @exception GCTAException::bad_model_type  
1451 - * Model is not a radial model.  
1452 - *  
1453 * Integrates the product of the model and the Irf over the true photon 1285 * Integrates the product of the model and the Irf over the true photon
1454 * arrival direction using 1286 * arrival direction using
1455 * 1287 *
@@ -1617,7 +1449,7 @@ double GCTAResponseIrf::irf_radial(const GEvent&amp; event, @@ -1617,7 +1449,7 @@ double GCTAResponseIrf::irf_radial(const GEvent&amp; event,
1617 // location 1449 // location
1618 const GModelSpatialRadialShell* shell = dynamic_cast<const GModelSpatialRadialShell*>(model); 1450 const GModelSpatialRadialShell* shell = dynamic_cast<const GModelSpatialRadialShell*>(model);
1619 if (shell != NULL) { 1451 if (shell != NULL) {
1620 - double shell_radius = shell->radius(); 1452 + double shell_radius = shell->radius() * gammalib::deg2rad;
1621 if (shell_radius > rho_min && shell_radius < rho_max) { 1453 if (shell_radius > rho_min && shell_radius < rho_max) {
1622 bounds.push_back(shell_radius); 1454 bounds.push_back(shell_radius);
1623 } 1455 }
@@ -1663,7 +1495,6 @@ double GCTAResponseIrf::irf_radial(const GEvent&amp; event, @@ -1663,7 +1495,6 @@ double GCTAResponseIrf::irf_radial(const GEvent&amp; event,
1663 // Return IRF value 1495 // Return IRF value
1664 return irf; 1496 return irf;
1665 } 1497 }
1666 -#endif  
1667 1498
1668 1499
1669 /***********************************************************************//** 1500 /***********************************************************************//**
@@ -1824,16 +1655,6 @@ double GCTAResponseIrf::irf_elliptical(const GEvent&amp; event, @@ -1824,16 +1655,6 @@ double GCTAResponseIrf::irf_elliptical(const GEvent&amp; event,
1824 bounds.push_back(rho_min); 1655 bounds.push_back(rho_min);
1825 bounds.push_back(rho_max); 1656 bounds.push_back(rho_max);
1826 1657
1827 - // If the integration range includes a transition between full  
1828 - // containment and partial containment, then add a boundary at  
1829 - // this location  
1830 - /*  
1831 - double transition_point = delta_max - rho_obs;  
1832 - if (transition_point > rho_min && transition_point < rho_max) {  
1833 - bounds.push_back(transition_point);  
1834 - }  
1835 - */  
1836 -  
1837 // If the integration range includes the semiminor boundary, then 1658 // If the integration range includes the semiminor boundary, then
1838 // add an integration boundary at that location 1659 // add an integration boundary at that location
1839 if (semiminor > rho_min && semiminor < rho_max) { 1660 if (semiminor > rho_min && semiminor < rho_max) {
@@ -2224,7 +2045,7 @@ double GCTAResponseIrf::npred_radial(const GSource&amp; source, @@ -2224,7 +2045,7 @@ double GCTAResponseIrf::npred_radial(const GSource&amp; source,
2224 // location 2045 // location
2225 const GModelSpatialRadialShell* shell = dynamic_cast<const GModelSpatialRadialShell*>(model); 2046 const GModelSpatialRadialShell* shell = dynamic_cast<const GModelSpatialRadialShell*>(model);
2226 if (shell != NULL) { 2047 if (shell != NULL) {
2227 - double shell_radius = shell->radius(); 2048 + double shell_radius = shell->radius() * gammalib::deg2rad;
2228 if (shell_radius > rho_min && shell_radius < rho_max) { 2049 if (shell_radius > rho_min && shell_radius < rho_max) {
2229 bounds.push_back(shell_radius); 2050 bounds.push_back(shell_radius);
2230 } 2051 }
inst/cta/src/GCTAResponse_helpers.cpp
@@ -283,8 +283,15 @@ double cta_irf_radial_kern_rho::eval(const double&amp; rho) @@ -283,8 +283,15 @@ double cta_irf_radial_kern_rho::eval(const double&amp; rho)
283 double omega_min = -domega; 283 double omega_min = -domega;
284 double omega_max = +domega; 284 double omega_max = +domega;
285 285
  286 + // Reduce rho by an infinite amount to avoid rounding errors
  287 + // at the boundary of a sharp edged model
  288 + double rho_kluge = rho - 1.0e-12;
  289 + if (rho_kluge < 0.0) {
  290 + rho_kluge = 0.0;
  291 + }
  292 +
286 // Evaluate sky model 293 // Evaluate sky model
287 - double model = m_model.eval(rho, m_srcEng, m_srcTime); 294 + double model = m_model.eval(rho_kluge, m_srcEng, m_srcTime);
288 295
289 // Debug: test if model is non positive 296 // Debug: test if model is non positive
290 #if defined(G_DEBUG_MODEL_ZERO) 297 #if defined(G_DEBUG_MODEL_ZERO)
@@ -292,7 +299,7 @@ double cta_irf_radial_kern_rho::eval(const double&amp; rho) @@ -292,7 +299,7 @@ double cta_irf_radial_kern_rho::eval(const double&amp; rho)
292 std::cout << "*** WARNING: cta_irf_radial_kern_rho::eval"; 299 std::cout << "*** WARNING: cta_irf_radial_kern_rho::eval";
293 std::cout << " zero model for (rho)=("; 300 std::cout << " zero model for (rho)=(";
294 std::cout << rho*gammalib::rad2deg << ")"; 301 std::cout << rho*gammalib::rad2deg << ")";
295 - std::cout << " rho-r_model" << (rho-m_model.theta_max()); 302 + std::cout << " rho-r_model=" << (rho-m_model.theta_max());
296 std::cout << " radians" << std::endl; 303 std::cout << " radians" << std::endl;
297 } 304 }
298 #endif 305 #endif
@@ -474,8 +481,15 @@ double cta_npred_radial_kern_rho::eval(const double&amp; rho) @@ -474,8 +481,15 @@ double cta_npred_radial_kern_rho::eval(const double&amp; rho)
474 double omega_min = m_omega0 - domega; 481 double omega_min = m_omega0 - domega;
475 double omega_max = m_omega0 + domega; 482 double omega_max = m_omega0 + domega;
476 483
  484 + // Reduce rho by an infinite amount to avoid rounding errors
  485 + // at the boundary of a sharp edged model
  486 + double rho_kluge = rho - 1.0e-12;
  487 + if (rho_kluge < 0.0) {
  488 + rho_kluge = 0.0;
  489 + }
  490 +
477 // Get radial model value 491 // Get radial model value
478 - double model = m_model.eval(rho, m_srcEng, m_srcTime); 492 + double model = m_model.eval(rho_kluge, m_srcEng, m_srcTime);
479 493
480 // Debug: test if model is non positive 494 // Debug: test if model is non positive
481 #if defined(G_DEBUG_MODEL_ZERO) 495 #if defined(G_DEBUG_MODEL_ZERO)
@@ -483,7 +497,7 @@ double cta_npred_radial_kern_rho::eval(const double&amp; rho) @@ -483,7 +497,7 @@ double cta_npred_radial_kern_rho::eval(const double&amp; rho)
483 std::cout << "*** WARNING: cta_npred_radial_kern_rho::eval"; 497 std::cout << "*** WARNING: cta_npred_radial_kern_rho::eval";
484 std::cout << " zero model for (rho)=("; 498 std::cout << " zero model for (rho)=(";
485 std::cout << rho*gammalib::rad2deg << ")"; 499 std::cout << rho*gammalib::rad2deg << ")";
486 - std::cout << " rho-r_model" << (rho-m_model.theta_max()); 500 + std::cout << " rho-r_model=" << (rho-m_model.theta_max());
487 std::cout << " radians" << std::endl; 501 std::cout << " radians" << std::endl;
488 } 502 }
489 #endif 503 #endif
@@ -1500,237 +1514,11 @@ double cta_npred_diffuse_kern_phi::eval(const double&amp; phi) @@ -1500,237 +1514,11 @@ double cta_npred_diffuse_kern_phi::eval(const double&amp; phi)
1500 } 1514 }
1501 1515
1502 1516
1503 -/***********************************************************************//**  
1504 - * @brief Kernel for PSF integration of spatial model  
1505 - *  
1506 - * @param[in] delta PSF offset angle (radians).  
1507 - * @return Azimuthally integrated product between PSF and model.  
1508 - *  
1509 - * Computes the azimuthally integrated product of point spread function and  
1510 - * the spatial model intensity. As the PSF is azimuthally symmetric, it is  
1511 - * not included in the azimuthally integration, but just multiplied on the  
1512 - * azimuthally integrated model. The method returns thus  
1513 - *  
1514 - * \f[  
1515 - * {\rm PSF}(\delta) \times  
1516 - * \int_0^{2\pi} {\rm M}(\delta, \phi) \sin \delta {\rm d}\phi  
1517 - * \f]  
1518 - *  
1519 - * where \f${\rm M}(\delta, \phi)\f$ is the spatial model in the coordinate  
1520 - * system of the point spread function, defined by the angle \f$\delta\f$  
1521 - * between the true and the measured photon direction and the azimuth angle  
1522 - * \f$\phi\f$ around the measured photon direction.  
1523 - ***************************************************************************/  
1524 -double cta_psf_diffuse_kern_delta::eval(const double& delta)  
1525 -{  
1526 - // Initialise value  
1527 - double value = 0.0;  
1528 -  
1529 - // If we're at the PSF peak the model is zero (due to the sin(delta)  
1530 - // term. We thus only integrate for positive deltas.  
1531 - if (delta > 0.0) {  
1532 -  
1533 - // Get PSF for this delta  
1534 - value = m_rsp->psf()(m_srcDir, delta, m_srcEng);  
1535 -  
1536 - // Continue only if PSF is positive  
1537 - if (value > 0.0) {  
1538 -  
1539 - // Compute sine and cosine of delta  
1540 - double sin_delta = std::sin(delta);  
1541 - double cos_delta = std::cos(delta);  
1542 -  
1543 - // Setup kernel for azimuthal integration of the diffuse model  
1544 - cta_psf_diffuse_kern_phi integrand(m_model, m_srcEng, m_srcTime,  
1545 - m_rot,  
1546 - sin_delta, cos_delta);  
1547 -  
1548 - // Azimuthally integrate model  
1549 - GIntegral integral(&integrand);  
1550 - integral.eps(m_eps);  
1551 - value *= integral.romberg(0.0, gammalib::twopi, m_order) * sin_delta;  
1552 -  
1553 - } // endif: PSF value was positive  
1554 -  
1555 - } // endif: delta was positive  
1556 -  
1557 - // Debug: Check for NaN  
1558 - #if defined(G_NAN_CHECK)  
1559 - if (gammalib::is_notanumber(value) || gammalib::is_infinite(value)) {  
1560 - std::cout << "*** ERROR: cta_psf_diffuse_kern_delta::eval";  
1561 - std::cout << "(delta=" << delta << "):";  
1562 - std::cout << " NaN/Inf encountered";  
1563 - std::cout << " (value=" << value;  
1564 - std::cout << ")" << std::endl;  
1565 - }  
1566 - #endif  
1567 -  
1568 - // Return kernel value  
1569 - return value;  
1570 -}  
1571 -  
1572 -  
1573 -/***********************************************************************//**  
1574 - * @brief Kernel for map integration of spatial model  
1575 - *  
1576 - * @param[in] phi Azimuth angle (radians).  
1577 - * @return Spatial model value.  
1578 - *  
1579 - * Computes the value of the spatial model at the position (delta,phi) given  
1580 - * in point spread function coordinates. The transformation from point  
1581 - * spread function coordinates into sky coordinates is done using a rotation  
1582 - * matrix that is pre-computed on entry.  
1583 - ***************************************************************************/  
1584 -double cta_psf_diffuse_kern_phi::eval(const double& phi)  
1585 -{  
1586 - // Compute sky direction vector in native coordinates  
1587 - double cos_phi = std::cos(phi);  
1588 - double sin_phi = std::sin(phi);  
1589 - GVector native(-cos_phi*m_sin_delta, sin_phi*m_sin_delta, m_cos_delta);  
1590 -  
1591 - // Rotate from native into celestial system  
1592 - GVector cel = m_rot * native;  
1593 -  
1594 - // Set sky direction  
1595 - GSkyDir srcDir;  
1596 - srcDir.celvector(cel);  
1597 -  
1598 - // Set photon  
1599 - GPhoton photon(srcDir, m_srcEng, m_srcTime);  
1600 -  
1601 - // Compute map value this sky direction  
1602 - double value = m_model->eval(photon);  
1603 -  
1604 - // Debug: Check for NaN  
1605 - #if defined(G_NAN_CHECK)  
1606 - if (gammalib::is_notanumber(value) || gammalib::is_infinite(value)) {  
1607 - std::cout << "*** ERROR: cta_psf_diffuse_kern_phi::eval";  
1608 - std::cout << "(phi=" << phi << "):";  
1609 - std::cout << " NaN/Inf encountered";  
1610 - std::cout << " (value=" << value;  
1611 - std::cout << ")" << std::endl;  
1612 - }  
1613 - #endif  
1614 -  
1615 - // Return kernel value  
1616 - return value;  
1617 -}  
1618 -  
1619 -  
1620 -/***********************************************************************//**  
1621 - * @brief Kernel for PSF integration of radial model  
1622 - *  
1623 - * @param[in] delta PSF offset angle (radians).  
1624 - * @return Azimuthally integrated product between PSF and radial model.  
1625 - *  
1626 - * Computes the azimuthally integrated product of point spread function and  
1627 - * the radial model intensity. As the PSF is azimuthally symmetric, it is  
1628 - * not included in the azimuthally integration, but just multiplied on the  
1629 - * azimuthally integrated model. The method returns thus  
1630 - *  
1631 - * \f[  
1632 - * {\rm PSF}(\delta) \times  
1633 - * \int_0^{2\pi} {\rm M}(\delta, \phi) \sin \delta {\rm d}\phi  
1634 - * \f]  
1635 - *  
1636 - * where \f${\rm M}(\delta, \phi)\f$ is the radial model in the coordinate  
1637 - * system of the point spread function, defined by the angle \f$\delta\f$  
1638 - * between the true and the measured photon direction and the azimuth angle  
1639 - * \f$\phi\f$ around the measured photon direction.  
1640 - ***************************************************************************/  
1641 -double cta_psf_radial_kern_delta::eval(const double& delta)  
1642 -{  
1643 - // Initialise value  
1644 - double value = 0.0;  
1645 -  
1646 - // If we're at the PSF peak the model is zero (due to the sin(delta)  
1647 - // term. We thus only integrate for positive deltas.  
1648 - if (delta > 0.0) {  
1649 -  
1650 - // Get PSF for this delta  
1651 - value = m_rsp->psf()(m_srcDir, delta, m_srcEng);  
1652 -  
1653 - // Continue only if PSF is positive  
1654 - if (value > 0.0) {  
1655 -  
1656 - // Compute sin(delta), sin(delta)*sin(zeta), cos(delta)*cos(zeta)  
1657 - double sin_delta = std::sin(delta);  
1658 - double sin_fact = sin_delta * m_sin_zeta;  
1659 - double cos_fact = std::cos(delta)* m_cos_zeta;  
1660 -  
1661 - // Setup kernel for azimuthal integration of the spatial model  
1662 - cta_psf_radial_kern_phi integrand(m_model, m_srcEng, m_srcTime,  
1663 - sin_fact, cos_fact);  
1664 -  
1665 - // Azimuthally integrate model  
1666 - GIntegral integral(&integrand);  
1667 - integral.eps(m_eps);  
1668 - value *= integral.romberg(0.0, gammalib::twopi, m_order) * sin_delta;  
1669 -  
1670 - } // endif: PSF value was positive  
1671 -  
1672 - } // endif: delta was positive  
1673 -  
1674 - // Debug: Check for NaN  
1675 - #if defined(G_NAN_CHECK)  
1676 - if (gammalib::is_notanumber(value) || gammalib::is_infinite(value)) {  
1677 - std::cout << "*** ERROR: cta_psf_radial_kern_delta::eval";  
1678 - std::cout << "(delta=" << delta << "):";  
1679 - std::cout << " NaN/Inf encountered";  
1680 - std::cout << " (value=" << value;  
1681 - std::cout << ")" << std::endl;  
1682 - }  
1683 - #endif  
1684 -  
1685 - // Return kernel value  
1686 - return value;  
1687 -}  
1688 -  
1689 -  
1690 -/***********************************************************************//**  
1691 - * @brief Kernel for azimuthal radial model integration  
1692 - *  
1693 - * @param[in] phi Azimuth angle (radians).  
1694 - * @return Radial model value.  
1695 - *  
1696 - * Computes the value of the radial model at the position \f$(\delta,\phi)\f$  
1697 - * given in point spread function coordinates. The \f$\theta\f$ angle of the  
1698 - * radial model is computed using  
1699 - *  
1700 - * \f[  
1701 - * \theta = \arccos \left( \cos \delta \cos \zeta +  
1702 - * \sin \delta \sin \zeta \cos \phi  
1703 - * \f]  
1704 - *  
1705 - * where \f$\delta\f$ is the angle between true and measured photon  
1706 - * direction, \f$\zeta\f$ is the angle between model centre and measured  
1707 - * photon direction, and \f$\phi\f$ is the azimuth angle with respect to the  
1708 - * measured photon direction, where \f$\phi=0\f$ corresponds to the  
1709 - * connecting line between model centre and measured photon direction.  
1710 - ***************************************************************************/  
1711 -double cta_psf_radial_kern_phi::eval(const double& phi)  
1712 -{  
1713 - // Compute radial model theta angle  
1714 - double theta = std::acos(m_cos_fact + m_sin_fact * std::cos(phi));  
1715 -  
1716 - // Get radial model value  
1717 - double value = m_model->eval(theta, m_srcEng, m_srcTime);  
1718 -  
1719 - // Debug: Check for NaN  
1720 - #if defined(G_NAN_CHECK)  
1721 - if (gammalib::is_notanumber(value) || gammalib::is_infinite(value)) {  
1722 - std::cout << "*** ERROR: cta_psf_radial_kern_phi::eval";  
1723 - std::cout << "(phi=" << phi << "):";  
1724 - std::cout << " NaN/Inf encountered";  
1725 - std::cout << " (value=" << value;  
1726 - std::cout << ")" << std::endl;  
1727 - }  
1728 - #endif  
1729 -  
1730 - // Return kernel value  
1731 - return value;  
1732 -}  
1733 - 1517 +/*==========================================================================
  1518 + = =
  1519 + = Helper class methods for stacked analysis =
  1520 + = =
  1521 + ==========================================================================*/
1734 1522
1735 /***********************************************************************//** 1523 /***********************************************************************//**
1736 * @brief Kernel for radial model integration over zenith angle 1524 * @brief Kernel for radial model integration over zenith angle
@@ -1777,8 +1565,7 @@ double cta_psf_radial_kern_rho::eval(const double&amp; rho) @@ -1777,8 +1565,7 @@ double cta_psf_radial_kern_rho::eval(const double&amp; rho)
1777 if (domega > 0.0) { 1565 if (domega > 0.0) {
1778 1566
1779 // Reduce rho by an infinite amount to avoid rounding errors 1567 // Reduce rho by an infinite amount to avoid rounding errors
1780 - // at the boundary of a sharp edged model (e.g. an elliptical  
1781 - // disk model) 1568 + // at the boundary of a sharp edged model
1782 double rho_kluge = rho - 1.0e-12; 1569 double rho_kluge = rho - 1.0e-12;
1783 if (rho_kluge < 0.0) { 1570 if (rho_kluge < 0.0) {
1784 rho_kluge = 0.0; 1571 rho_kluge = 0.0;
@@ -1793,7 +1580,7 @@ double cta_psf_radial_kern_rho::eval(const double&amp; rho) @@ -1793,7 +1580,7 @@ double cta_psf_radial_kern_rho::eval(const double&amp; rho)
1793 std::cout << "*** WARNING: cta_psf_radial_kern_rho::eval"; 1580 std::cout << "*** WARNING: cta_psf_radial_kern_rho::eval";
1794 std::cout << " zero model for (rho)=("; 1581 std::cout << " zero model for (rho)=(";
1795 std::cout << rho*gammalib::rad2deg << ")"; 1582 std::cout << rho*gammalib::rad2deg << ")";
1796 - std::cout << " rho-r_model" << (rho-m_model->theta_max()); 1583 + std::cout << " rho-r_model=" << (rho-m_model->theta_max());
1797 std::cout << " radians" << std::endl; 1584 std::cout << " radians" << std::endl;
1798 } 1585 }
1799 #endif 1586 #endif
@@ -1912,133 +1699,6 @@ double cta_psf_radial_kern_omega::eval(const double&amp; omega) @@ -1912,133 +1699,6 @@ double cta_psf_radial_kern_omega::eval(const double&amp; omega)
1912 1699
1913 1700
1914 /***********************************************************************//** 1701 /***********************************************************************//**
1915 - * @brief Kernel for PSF integration of elliptical model  
1916 - *  
1917 - * @param[in] delta PSF offset angle (radians).  
1918 - * @return Azimuthally integrated product between PSF and elliptical model.  
1919 - *  
1920 - * Computes the azimuthally integrated product of point spread function and  
1921 - * the elliptical model intensity. As the PSF is azimuthally symmetric, it is  
1922 - * not included in the azimuthally integration, but just multiplied on the  
1923 - * azimuthally integrated model. The method returns thus  
1924 - *  
1925 - * \f[  
1926 - * {\rm PSF}(\delta) \times  
1927 - * \int_0^{2\pi} {\rm M}(\delta, \phi) \sin \delta {\rm d}\phi  
1928 - * \f]  
1929 - *  
1930 - * where \f${\rm M}(\delta, \phi)\f$ is the elliptical model in the coordinate  
1931 - * system of the point spread function, defined by the angle \f$\delta\f$  
1932 - * between the true and the measured photon direction and the azimuth angle  
1933 - * \f$\phi\f$ around the measured photon direction.  
1934 - ***************************************************************************/  
1935 -double cta_psf_elliptical_kern_delta::eval(const double& delta)  
1936 -{  
1937 - // Initialise value  
1938 - double value = 0.0;  
1939 -  
1940 - // If we're at the PSF peak the model is zero (due to the sin(delta)  
1941 - // term. We thus only integrate for positive deltas.  
1942 - if (delta > 0.0) {  
1943 -  
1944 - // Get PSF for this delta  
1945 - value = m_rsp->psf()(m_srcDir, delta, m_srcEng);  
1946 -  
1947 - // Continue only if PSF is positive  
1948 - if (value > 0.0) {  
1949 -  
1950 - // Compute sin(delta), sin(delta)*sin(zeta), cos(delta)*cos(zeta)  
1951 - double sin_delta = std::sin(delta);  
1952 - double sin_fact = sin_delta * m_sin_zeta;  
1953 - double cos_fact = std::cos(delta)* m_cos_zeta;  
1954 -  
1955 - // Setup kernel for azimuthal integration of the elliptical model  
1956 - cta_psf_elliptical_kern_phi integrand(m_model, m_srcEng, m_srcTime,  
1957 - m_omega,  
1958 - sin_delta, sin_fact, cos_fact);  
1959 -  
1960 - // Azimuthally integrate model  
1961 - GIntegral integral(&integrand);  
1962 - integral.eps(m_eps);  
1963 - value *= integral.romberg(0.0, gammalib::twopi, m_order) * sin_delta;  
1964 -  
1965 - } // endif: PSF value was positive  
1966 -  
1967 - } // endif: delta was positive  
1968 -  
1969 - // Debug: Check for NaN  
1970 - #if defined(G_NAN_CHECK)  
1971 - if (gammalib::is_notanumber(value) || gammalib::is_infinite(value)) {  
1972 - std::cout << "*** ERROR: cta_psf_elliptical_kern_delta::eval";  
1973 - std::cout << "(delta=" << delta << "):";  
1974 - std::cout << " NaN/Inf encountered";  
1975 - std::cout << " (value=" << value;  
1976 - std::cout << ")" << std::endl;  
1977 - }  
1978 - #endif  
1979 -  
1980 - // Return kernel value  
1981 - return value;  
1982 -}  
1983 -  
1984 -  
1985 -/***********************************************************************//**  
1986 - * @brief Kernel for azimuthal elliptical model integration  
1987 - *  
1988 - * @param[in] phi Azimuth angle (radians).  
1989 - * @return Radial model value.  
1990 - *  
1991 - * Computes the value of the elliptical model at the position \f$(\delta,\phi)\f$  
1992 - * given in point spread function coordinates. The \f$\theta\f$ angle of the  
1993 - * elliptical model is computed using  
1994 - *  
1995 - * \f[  
1996 - * \theta = \arccos \left( \cos \delta \cos \zeta +  
1997 - * \sin \delta \sin \zeta \cos \phi \right)  
1998 - * \f]  
1999 - *  
2000 - * where \f$\delta\f$ is the angle between true and measured photon  
2001 - * direction, \f$\zeta\f$ is the angle between model centre and measured  
2002 - * photon direction, and \f$\phi\f$ is the azimuth angle with respect to the  
2003 - * measured photon direction, where \f$\phi=0\f$ corresponds to the  
2004 - * connecting line between model centre and measured photon direction.  
2005 - *  
2006 - * The position angle of the elliptical model is computed using  
2007 - *  
2008 - * \f[  
2009 - * {\rm posangle} = \arcsin \left( \frac{\sin \phi \sin \delta}  
2010 - * {\sin \theta} \right)  
2011 - * \f]  
2012 - ***************************************************************************/  
2013 -double cta_psf_elliptical_kern_phi::eval(const double& phi)  
2014 -{  
2015 - // Compute radial model theta angle  
2016 - double theta = std::acos(m_cos_fact + m_sin_fact * std::cos(phi));  
2017 -  
2018 - // Compute radial model position angle  
2019 - double posangle = std::asin((std::sin(phi) * m_sin_delta)/std::sin(theta)) +  
2020 - m_omega;  
2021 -  
2022 - // Get radial model value  
2023 - double value = m_model->eval(theta, posangle, m_srcEng, m_srcTime);  
2024 -  
2025 - // Debug: Check for NaN  
2026 - #if defined(G_NAN_CHECK)  
2027 - if (gammalib::is_notanumber(value) || gammalib::is_infinite(value)) {  
2028 - std::cout << "*** ERROR: cta_psf_elliptical_kern_phi::eval";  
2029 - std::cout << "(phi=" << phi << "):";  
2030 - std::cout << " NaN/Inf encountered";  
2031 - std::cout << " (value=" << value;  
2032 - std::cout << ")" << std::endl;  
2033 - }  
2034 - #endif  
2035 -  
2036 - // Return kernel value  
2037 - return value;  
2038 -}  
2039 -  
2040 -  
2041 -/***********************************************************************//**  
2042 * @brief Kernel for elliptical model integration over zenith angle 1702 * @brief Kernel for elliptical model integration over zenith angle
2043 * 1703 *
2044 * @param[in] rho Radial distance from model centre (radians). 1704 * @param[in] rho Radial distance from model centre (radians).
@@ -2305,112 +1965,64 @@ double cta_psf_elliptical_kern_omega::eval(const double&amp; omega) @@ -2305,112 +1965,64 @@ double cta_psf_elliptical_kern_omega::eval(const double&amp; omega)
2305 } 1965 }
2306 1966
2307 1967
2308 -/*==========================================================================  
2309 - = =  
2310 - = Helper class methods for response computation testing =  
2311 - = =  
2312 - = WARNING: These helper classes are for testing purposes only and not for =  
2313 - = production. These classes may be removed at any moment without =  
2314 - = any warning. =  
2315 - ==========================================================================*/  
2316 -  
2317 -  
2318 /***********************************************************************//** 1968 /***********************************************************************//**
2319 - * @brief Kernel for integration of radial model in Psf system 1969 + * @brief Kernel for PSF integration of spatial model
2320 * 1970 *
2321 - * @param[in] delta Psf offset angle (radians).  
2322 - * @return Azimuthally integrated product between Psf and radial model. 1971 + * @param[in] delta PSF offset angle (radians).
  1972 + * @return Azimuthally integrated product between PSF and model.
2323 * 1973 *
2324 * Computes the azimuthally integrated product of point spread function and 1974 * Computes the azimuthally integrated product of point spread function and
2325 - * the radial model intensity. As the Psf is azimuthally symmetric, it is 1975 + * the spatial model intensity. As the PSF is azimuthally symmetric, it is
2326 * not included in the azimuthally integration, but just multiplied on the 1976 * not included in the azimuthally integration, but just multiplied on the
2327 * azimuthally integrated model. The method returns thus 1977 * azimuthally integrated model. The method returns thus
2328 * 1978 *
2329 * \f[ 1979 * \f[
2330 - * {\rm Psf}(\delta) \times 1980 + * {\rm PSF}(\delta) \times
2331 * \int_0^{2\pi} {\rm M}(\delta, \phi) \sin \delta {\rm d}\phi 1981 * \int_0^{2\pi} {\rm M}(\delta, \phi) \sin \delta {\rm d}\phi
2332 * \f] 1982 * \f]
2333 * 1983 *
2334 - * where \f${\rm M}(\delta, \phi)\f$ is the radial model in the coordinate 1984 + * where \f${\rm M}(\delta, \phi)\f$ is the spatial model in the coordinate
2335 * system of the point spread function, defined by the angle \f$\delta\f$ 1985 * system of the point spread function, defined by the angle \f$\delta\f$
2336 * between the true and the measured photon direction and the azimuth angle 1986 * between the true and the measured photon direction and the azimuth angle
2337 * \f$\phi\f$ around the measured photon direction. 1987 * \f$\phi\f$ around the measured photon direction.
2338 ***************************************************************************/ 1988 ***************************************************************************/
2339 -double cta_irf_radial_kern_delta::eval(const double& delta) 1989 +double cta_psf_diffuse_kern_delta::eval(const double& delta)
2340 { 1990 {
2341 // Initialise value 1991 // Initialise value
2342 double value = 0.0; 1992 double value = 0.0;
2343 1993
2344 - // If we're at the Psf peak the model is zero (due to the sin(delta) 1994 + // If we're at the PSF peak the model is zero (due to the sin(delta)
2345 // term. We thus only integrate for positive deltas. 1995 // term. We thus only integrate for positive deltas.
2346 if (delta > 0.0) { 1996 if (delta > 0.0) {
2347 1997
2348 - // Get Psf for this delta. We use here the event offset instead of  
2349 - // the true photon offset because the Psf varies little over the  
2350 - // dimension of the Psf, hence it should basically be invariant  
2351 - // over such small scales  
2352 - value = m_rsp->psf(delta, m_obsOffset, 0.0, m_pnt.zenith(), m_pnt.azimuth(), m_srcLogEng);  
2353 -  
2354 - // Compile option: Get Aeff now using the event offset instead of  
2355 - // computing Aeff later in the azimuthal integration kernel where  
2356 - // the true photon offset is available. This is an approximation and  
2357 - // assumes that Aeff varies little over the dimension of the Psf.  
2358 - // This option is only for testing purposes and should not be used  
2359 - // for production.  
2360 - #if defined(G_USE_OBSDIR_FOR_AEFF)  
2361 - value *= m_rsp->aeff(m_obsOffset, 0.0, m_pnt.zenith(), m_pnt.azimuth(), m_srcLogEng);  
2362 - #endif 1998 + // Get PSF for this delta
  1999 + value = m_rsp->psf()(m_srcDir, delta, m_srcEng);
2363 2000
2364 - // Continue only if Psf is positive 2001 + // Continue only if PSF is positive
2365 if (value > 0.0) { 2002 if (value > 0.0) {
2366 2003
2367 - // Compute half length of arc that lies within model (radians)  
2368 - double dphi = 0.5 * gammalib::cta_roi_arclength(delta,  
2369 - m_zeta,  
2370 - m_cos_zeta,  
2371 - m_sin_zeta,  
2372 - m_theta_max,  
2373 - m_cos_theta_max);  
2374 -  
2375 - // Continue only if arc length is positive  
2376 - if (dphi > 0.0) {  
2377 -  
2378 - // Compute azimuth integration range [phi_min,phi_max].  
2379 - double phi_min = -dphi;  
2380 - double phi_max = +dphi;  
2381 -  
2382 - // Pre-compute terms for angular distance computation in  
2383 - // azimuth integration kernel.  
2384 - double sin_delta = std::sin(delta);  
2385 - double cos_delta = std::cos(delta);  
2386 - double sin_fact_model = sin_delta * m_sin_zeta;  
2387 - double cos_fact_model = cos_delta * m_cos_zeta;  
2388 - double sin_fact_inst = sin_delta * m_sin_obs_offset;  
2389 - double cos_fact_inst = cos_delta * m_cos_obs_offset;  
2390 -  
2391 - // Setup kernel for azimuthal integration of the spatial model  
2392 - cta_irf_radial_kern_phi integrand(m_rsp, m_model, m_pnt,  
2393 - m_srcEng, m_srcLogEng, m_srcTime,  
2394 - m_obsEng,  
2395 - m_phi0,  
2396 - sin_fact_model, cos_fact_model,  
2397 - sin_fact_inst, cos_fact_inst);  
2398 -  
2399 - // Integrate kernel  
2400 - GIntegral integral(&integrand);  
2401 - integral.fixed_iter(m_iter);  
2402 - value *= integral.romberg(phi_min, phi_max, m_iter) * sin_delta; 2004 + // Compute sine and cosine of delta
  2005 + double sin_delta = std::sin(delta);
  2006 + double cos_delta = std::cos(delta);
  2007 +
  2008 + // Setup kernel for azimuthal integration of the diffuse model
  2009 + cta_psf_diffuse_kern_phi integrand(m_model, m_srcEng, m_srcTime,
  2010 + m_rot,
  2011 + sin_delta, cos_delta);
2403 2012
2404 - } // endif: arc length was positive 2013 + // Azimuthally integrate model
  2014 + GIntegral integral(&integrand);
  2015 + integral.eps(m_eps);
  2016 + value *= integral.romberg(0.0, gammalib::twopi, m_order) * sin_delta;
2405 2017
2406 - } // endif: Psf value was positive 2018 + } // endif: PSF value was positive
2407 2019
2408 } // endif: delta was positive 2020 } // endif: delta was positive
2409 2021
2410 // Debug: Check for NaN 2022 // Debug: Check for NaN
2411 #if defined(G_NAN_CHECK) 2023 #if defined(G_NAN_CHECK)
2412 if (gammalib::is_notanumber(value) || gammalib::is_infinite(value)) { 2024 if (gammalib::is_notanumber(value) || gammalib::is_infinite(value)) {
2413 - std::cout << "*** ERROR: cta_irf_radial_kern_delta::eval"; 2025 + std::cout << "*** ERROR: cta_psf_diffuse_kern_delta::eval";
2414 std::cout << "(delta=" << delta << "):"; 2026 std::cout << "(delta=" << delta << "):";
2415 std::cout << " NaN/Inf encountered"; 2027 std::cout << " NaN/Inf encountered";
2416 std::cout << " (value=" << value; 2028 std::cout << " (value=" << value;
@@ -2424,79 +2036,40 @@ double cta_irf_radial_kern_delta::eval(const double&amp; delta) @@ -2424,79 +2036,40 @@ double cta_irf_radial_kern_delta::eval(const double&amp; delta)
2424 2036
2425 2037
2426 /***********************************************************************//** 2038 /***********************************************************************//**
2427 - * @brief Kernel for azimuthal radial model integration 2039 + * @brief Kernel for map integration of spatial model
2428 * 2040 *
2429 * @param[in] phi Azimuth angle (radians). 2041 * @param[in] phi Azimuth angle (radians).
2430 - * @return Radial model value.  
2431 - *  
2432 - * Computes the value of the radial model at the position \f$(\delta,\phi)\f$  
2433 - * given in point spread function coordinates. The \f$\theta\f$ angle of the  
2434 - * radial model is computed using  
2435 - *  
2436 - * \f[  
2437 - * \theta = \arccos \left( \cos \delta \cos \zeta +  
2438 - * \sin \delta \sin \zeta \cos \phi  
2439 - * \f] 2042 + * @return Spatial model value.
2440 * 2043 *
2441 - * where \f$\delta\f$ is the angle between true and measured photon  
2442 - * direction, \f$\zeta\f$ is the angle between model centre and measured  
2443 - * photon direction, and \f$\phi\f$ is the azimuth angle with respect to the  
2444 - * measured photon direction, where \f$\phi=0\f$ corresponds to the  
2445 - * connecting line between model centre and measured photon direction. 2044 + * Computes the value of the spatial model at the position (delta,phi) given
  2045 + * in point spread function coordinates. The transformation from point
  2046 + * spread function coordinates into sky coordinates is done using a rotation
  2047 + * matrix that is pre-computed on entry.
2446 ***************************************************************************/ 2048 ***************************************************************************/
2447 -double cta_irf_radial_kern_phi::eval(const double& phi) 2049 +double cta_psf_diffuse_kern_phi::eval(const double& phi)
2448 { 2050 {
2449 - // Compute radial model theta angle  
2450 - double theta = gammalib::acos(m_cos_fact_model +  
2451 - m_sin_fact_model * std::cos(phi));  
2452 -  
2453 - // Get radial model value  
2454 - double value = m_model->eval(theta, m_srcEng, m_srcTime);  
2455 -  
2456 - // For debugging: Check boundary violation  
2457 - if (value == 0.0) {  
2458 - std::cout << "Model 0 at theta=" << theta*gammalib::rad2deg;  
2459 - std::cout << " deg; phi=";  
2460 - std::cout << phi*gammalib::rad2deg << " deg";  
2461 - std::cout << std::endl;  
2462 - throw;  
2463 - }  
2464 -  
2465 - // If model is positive the multiply it with the effective area and  
2466 - // optionally fold in energy dispersion  
2467 - #if defined(G_USE_OBSDIR_FOR_AEFF)  
2468 - if (m_rsp->use_edisp() && value > 0.0) {  
2469 -  
2470 - // Compute offset angle of true photon in camera system (radians)  
2471 - double offset = gammalib::acos(m_cos_fact_inst +  
2472 - m_sin_fact_inst * std::cos(m_phi0 - phi));  
2473 -  
2474 - // Take energy dispersion into account  
2475 - value *= m_rsp->edisp(m_obsEng, offset, 0.0, m_pnt.zenith(), m_pnt.azimuth(), m_srcLogEng); 2051 + // Compute sky direction vector in native coordinates
  2052 + double cos_phi = std::cos(phi);
  2053 + double sin_phi = std::sin(phi);
  2054 + GVector native(-cos_phi*m_sin_delta, sin_phi*m_sin_delta, m_cos_delta);
2476 2055
2477 - } // endif: model value was positive  
2478 - #else  
2479 - if (value > 0.0) { 2056 + // Rotate from native into celestial system
  2057 + GVector cel = m_rot * native;
2480 2058
2481 - // Compute offset angle of true photon in camera system (radians)  
2482 - double offset = gammalib::acos(m_cos_fact_inst +  
2483 - m_sin_fact_inst * std::cos(m_phi0 - phi)); 2059 + // Set sky direction
  2060 + GSkyDir srcDir;
  2061 + srcDir.celvector(cel);
2484 2062
2485 - // Multiply with effective area  
2486 - value *= m_rsp->aeff(offset, 0.0, m_pnt.zenith(), m_pnt.azimuth(), m_srcLogEng); 2063 + // Set photon
  2064 + GPhoton photon(srcDir, m_srcEng, m_srcTime);
2487 2065
2488 - // Optionally take energy dispersion into account  
2489 - if (m_rsp->use_edisp() && value > 0.0) {  
2490 - value *= m_rsp->edisp(m_obsEng, offset, 0.0, m_pnt.zenith(), m_pnt.azimuth(), m_srcLogEng);  
2491 - } 2066 + // Compute map value this sky direction
  2067 + double value = m_model->eval(photon);
2492 2068
2493 - } // endif: model value was positive  
2494 - #endif  
2495 -  
2496 // Debug: Check for NaN 2069 // Debug: Check for NaN
2497 #if defined(G_NAN_CHECK) 2070 #if defined(G_NAN_CHECK)
2498 if (gammalib::is_notanumber(value) || gammalib::is_infinite(value)) { 2071 if (gammalib::is_notanumber(value) || gammalib::is_infinite(value)) {
2499 - std::cout << "*** ERROR: cta_irf_radial_kern_phi::eval"; 2072 + std::cout << "*** ERROR: cta_psf_diffuse_kern_phi::eval";
2500 std::cout << "(phi=" << phi << "):"; 2073 std::cout << "(phi=" << phi << "):";
2501 std::cout << " NaN/Inf encountered"; 2074 std::cout << " NaN/Inf encountered";
2502 std::cout << " (value=" << value; 2075 std::cout << " (value=" << value;
inst/cta/src/GCTAResponse_helpers.hpp
@@ -982,142 +982,6 @@ protected: @@ -982,142 +982,6 @@ protected:
982 982
983 983
984 /***********************************************************************//** 984 /***********************************************************************//**
985 - * @class cta_psf_diffuse_kern_delta  
986 - *  
987 - * @brief Kernel for Psf delta angle integration used for stacked analysis  
988 - ***************************************************************************/  
989 -class cta_psf_diffuse_kern_delta : public GFunction {  
990 -public:  
991 - cta_psf_diffuse_kern_delta(const GCTAResponseCube* rsp,  
992 - const GModelSpatial* model,  
993 - const GSkyDir& srcDir,  
994 - const GEnergy& srcEng,  
995 - const GTime& srcTime,  
996 - const GMatrix& rot,  
997 - const double& eps,  
998 - const int& order) :  
999 - m_rsp(rsp),  
1000 - m_model(model),  
1001 - m_srcDir(srcDir),  
1002 - m_srcEng(srcEng),  
1003 - m_srcTime(srcTime),  
1004 - m_rot(rot),  
1005 - m_eps(eps),  
1006 - m_order (order),  
1007 - m_psf_max(rsp->psf()(srcDir, 0.0, srcEng)) { }  
1008 - double eval(const double& delta);  
1009 -protected:  
1010 - const GCTAResponseCube* m_rsp; //!< Response cube  
1011 - const GModelSpatial* m_model; //!< Spatial model  
1012 - const GSkyDir& m_srcDir; //!< True photon arrival direction  
1013 - const GEnergy& m_srcEng; //!< True photon energy  
1014 - const GTime& m_srcTime; //!< True photon arrival time  
1015 - const GMatrix& m_rot; //!< Rotation matrix  
1016 - const double& m_eps; //!< Integration precision  
1017 - const int& m_order; //!< Romberg order  
1018 - double m_psf_max; //!< Maximum PSF value  
1019 -};  
1020 -  
1021 -  
1022 -/***********************************************************************//**  
1023 - * @class cta_psf_diffuse_kern_phi  
1024 - *  
1025 - * @brief Kernel for Psf phi angle integration used for stacked analysis  
1026 - ***************************************************************************/  
1027 -class cta_psf_diffuse_kern_phi : public GFunction {  
1028 -public:  
1029 - cta_psf_diffuse_kern_phi(const GModelSpatial* model,  
1030 - const GEnergy& srcEng,  
1031 - const GTime& srcTime,  
1032 - const GMatrix& rot,  
1033 - const double& sin_delta,  
1034 - const double& cos_delta) :  
1035 - m_model(model),  
1036 - m_srcEng(srcEng),  
1037 - m_srcTime(srcTime),  
1038 - m_rot(rot),  
1039 - m_sin_delta(sin_delta),  
1040 - m_cos_delta(cos_delta) { }  
1041 - double eval(const double& phi);  
1042 -protected:  
1043 - const GModelSpatial* m_model; //!< Spatial model  
1044 - const GEnergy& m_srcEng; //!< True photon energy  
1045 - const GTime& m_srcTime; //!< True photon arrival time  
1046 - const GMatrix& m_rot; //!< Rotation matrix  
1047 - const double& m_sin_delta; //!< sin(delta)  
1048 - const double& m_cos_delta; //!< cos(delta)  
1049 -};  
1050 -  
1051 -  
1052 -/***********************************************************************//**  
1053 - * @class cta_psf_radial_kern_delta  
1054 - *  
1055 - * @brief Kernel for Psf delta angle integration used for stacked analysis  
1056 - ***************************************************************************/  
1057 -class cta_psf_radial_kern_delta : public GFunction {  
1058 -public:  
1059 - cta_psf_radial_kern_delta(const GCTAResponseCube* rsp,  
1060 - const GModelSpatialRadial* model,  
1061 - const GSkyDir& srcDir,  
1062 - const GEnergy& srcEng,  
1063 - const GTime& srcTime,  
1064 - const double& sin_zeta,  
1065 - const double& cos_zeta,  
1066 - const double& eps,  
1067 - const int& order) :  
1068 - m_rsp(rsp),  
1069 - m_model(model),  
1070 - m_srcDir(srcDir),  
1071 - m_srcEng(srcEng),  
1072 - m_srcTime(srcTime),  
1073 - m_sin_zeta(sin_zeta),  
1074 - m_cos_zeta(cos_zeta),  
1075 - m_eps(eps),  
1076 - m_order (order),  
1077 - m_psf_max(rsp->psf()(srcDir, 0.0, srcEng)) { }  
1078 - double eval(const double& delta);  
1079 -protected:  
1080 - const GCTAResponseCube* m_rsp; //!< Response cube  
1081 - const GModelSpatialRadial* m_model; //!< Radial model  
1082 - const GSkyDir& m_srcDir; //!< True photon arrival direction  
1083 - const GEnergy& m_srcEng; //!< True photon energy  
1084 - const GTime& m_srcTime; //!< True photon arrival time  
1085 - const double& m_sin_zeta; //!< sin(zeta)  
1086 - const double& m_cos_zeta; //!< cos(zeta)  
1087 - const double& m_eps; //!< Integration precision  
1088 - const int& m_order; //!< Romberg order  
1089 - double m_psf_max; //!< Maximum PSF value  
1090 -};  
1091 -  
1092 -  
1093 -/***********************************************************************//**  
1094 - * @class cta_psf_radial_kern_phi  
1095 - *  
1096 - * @brief Kernel for Psf phi angle integration used for stacked analysis  
1097 - ***************************************************************************/  
1098 -class cta_psf_radial_kern_phi : public GFunction {  
1099 -public:  
1100 - cta_psf_radial_kern_phi(const GModelSpatialRadial* model,  
1101 - const GEnergy& srcEng,  
1102 - const GTime& srcTime,  
1103 - const double& sin_fact,  
1104 - const double& cos_fact) :  
1105 - m_model(model),  
1106 - m_srcEng(srcEng),  
1107 - m_srcTime(srcTime),  
1108 - m_sin_fact(sin_fact),  
1109 - m_cos_fact(cos_fact) { }  
1110 - double eval(const double& phi);  
1111 -protected:  
1112 - const GModelSpatialRadial* m_model; //!< Radial model  
1113 - const GEnergy& m_srcEng; //!< True photon energy  
1114 - const GTime& m_srcTime; //!< True photon arrival time  
1115 - const double& m_sin_fact; //!< sin(delta)*sin(zeta)  
1116 - const double& m_cos_fact; //!< cos(delta)*cos(zeta)  
1117 -};  
1118 -  
1119 -  
1120 -/***********************************************************************//**  
1121 * @class cta_irf_radial_kern_rho 985 * @class cta_irf_radial_kern_rho
1122 * 986 *
1123 * @brief Kernel for radial model zenith angle integration 987 * @brief Kernel for radial model zenith angle integration
@@ -1227,83 +1091,6 @@ public: @@ -1227,83 +1091,6 @@ public:
1227 1091
1228 1092
1229 /***********************************************************************//** 1093 /***********************************************************************//**
1230 - * @class cta_psf_elliptical_kern_delta  
1231 - *  
1232 - * @brief Kernel for Psf delta angle integration used for stacked analysis  
1233 - ***************************************************************************/  
1234 -class cta_psf_elliptical_kern_delta : public GFunction {  
1235 -public:  
1236 - cta_psf_elliptical_kern_delta(const GCTAResponseCube* rsp,  
1237 - const GModelSpatialElliptical* model,  
1238 - const GSkyDir& srcDir,  
1239 - const GEnergy& srcEng,  
1240 - const GTime& srcTime,  
1241 - const double& sin_zeta,  
1242 - const double& cos_zeta,  
1243 - const double& omega,  
1244 - const double& eps,  
1245 - const int& order) :  
1246 - m_rsp(rsp),  
1247 - m_model(model),  
1248 - m_srcDir(srcDir),  
1249 - m_srcEng(srcEng),  
1250 - m_srcTime(srcTime),  
1251 - m_sin_zeta(sin_zeta),  
1252 - m_cos_zeta(cos_zeta),  
1253 - m_omega(omega),  
1254 - m_eps(eps),  
1255 - m_order (order),  
1256 - m_psf_max(rsp->psf()(srcDir, 0.0, srcEng)) { }  
1257 - double eval(const double& delta);  
1258 -protected:  
1259 - const GCTAResponseCube* m_rsp; //!< Response cube  
1260 - const GModelSpatialElliptical* m_model; //!< Elliptical model  
1261 - const GSkyDir& m_srcDir; //!< True photon arrival direction  
1262 - const GEnergy& m_srcEng; //!< True photon energy  
1263 - const GTime& m_srcTime; //!< True photon arrival time  
1264 - const double& m_sin_zeta; //!< sin(zeta)  
1265 - const double& m_cos_zeta; //!< cos(zeta)  
1266 - const double& m_omega; //!< Position angle of photon w/r model  
1267 - const double& m_eps; //!< Integration precision  
1268 - const int& m_order; //!< Romberg order  
1269 - double m_psf_max; //!< Maximum PSF value  
1270 -};  
1271 -  
1272 -  
1273 -/***********************************************************************//**  
1274 - * @class cta_psf_elliptical_kern_delta  
1275 - *  
1276 - * @brief Kernel for Psf phi angle integration used for stacked analysis  
1277 - ***************************************************************************/  
1278 -class cta_psf_elliptical_kern_phi : public GFunction {  
1279 -public:  
1280 - cta_psf_elliptical_kern_phi(const GModelSpatialElliptical* model,  
1281 - const GEnergy& srcEng,  
1282 - const GTime& srcTime,  
1283 - const double& omega,  
1284 - const double& sin_delta,  
1285 - const double& sin_fact,  
1286 - const double& cos_fact) :  
1287 - m_model(model),  
1288 - m_srcEng(srcEng),  
1289 - m_srcTime(srcTime),  
1290 - m_omega(omega),  
1291 - m_sin_delta(sin_delta),  
1292 - m_sin_fact(sin_fact),  
1293 - m_cos_fact(cos_fact) { }  
1294 - double eval(const double& phi);  
1295 -protected:  
1296 - const GModelSpatialElliptical* m_model; //!< Elliptical model  
1297 - const GEnergy& m_srcEng; //!< True photon energy  
1298 - const GTime& m_srcTime; //!< True photon arrival time  
1299 - const double& m_omega; //!< Position angle of photon w/r model  
1300 - const double& m_sin_delta; //!< sin(delta)  
1301 - const double& m_sin_fact; //!< sin(delta)*sin(zeta)  
1302 - const double& m_cos_fact; //!< cos(delta)*cos(zeta)  
1303 -};  
1304 -  
1305 -  
1306 -/***********************************************************************//**  
1307 * @class cta_irf_elliptical_kern_rho 1094 * @class cta_irf_elliptical_kern_rho
1308 * 1095 *
1309 * @brief Kernel for elliptical model zenith angle integration 1096 * @brief Kernel for elliptical model zenith angle integration
@@ -1433,114 +1220,70 @@ public: @@ -1433,114 +1220,70 @@ public:
1433 1220
1434 1221
1435 /***********************************************************************//** 1222 /***********************************************************************//**
1436 - * @class cta_irf_radial_kern_delta  
1437 - *  
1438 - * @brief Test kernel for Irf delta angle integration 1223 + * @class cta_psf_diffuse_kern_delta
1439 * 1224 *
1440 - * This class is used for Psf system based integration. It is only here for  
1441 - * testing and this class may be removed in the future. 1225 + * @brief Kernel for Psf delta angle integration used for stacked analysis
1442 ***************************************************************************/ 1226 ***************************************************************************/
1443 -class cta_irf_radial_kern_delta : public GFunction { 1227 +class cta_psf_diffuse_kern_delta : public GFunction {
1444 public: 1228 public:
1445 - cta_irf_radial_kern_delta(const GCTAResponseIrf* rsp,  
1446 - const GModelSpatialRadial* model,  
1447 - const GCTAPointing& pnt,  
1448 - const GEnergy& srcEng,  
1449 - const double& srcLogEng,  
1450 - const GTime& srcTime,  
1451 - const double& obsOffset,  
1452 - const GEnergy& obsEng,  
1453 - const double& zeta,  
1454 - const double& phi0,  
1455 - const double& theta_max,  
1456 - const int& iter) :  
1457 - m_rsp(rsp),  
1458 - m_model(model),  
1459 - m_pnt(pnt),  
1460 - m_srcEng(srcEng),  
1461 - m_srcLogEng(srcLogEng),  
1462 - m_srcTime(srcTime),  
1463 - m_obsOffset(obsOffset),  
1464 - m_sin_obs_offset(std::sin(obsOffset)),  
1465 - m_cos_obs_offset(std::cos(obsOffset)),  
1466 - m_obsEng(obsEng),  
1467 - m_zeta(zeta),  
1468 - m_phi0(phi0),  
1469 - m_sin_zeta(std::sin(zeta)),  
1470 - m_cos_zeta(std::cos(zeta)),  
1471 - m_theta_max(theta_max),  
1472 - m_cos_theta_max(std::cos(theta_max)),  
1473 - m_iter(iter) { } 1229 + cta_psf_diffuse_kern_delta(const GCTAResponseCube* rsp,
  1230 + const GModelSpatial* model,
  1231 + const GSkyDir& srcDir,
  1232 + const GEnergy& srcEng,
  1233 + const GTime& srcTime,
  1234 + const GMatrix& rot,
  1235 + const double& eps,
  1236 + const int& order) :
  1237 + m_rsp(rsp),
  1238 + m_model(model),
  1239 + m_srcDir(srcDir),
  1240 + m_srcEng(srcEng),
  1241 + m_srcTime(srcTime),
  1242 + m_rot(rot),
  1243 + m_eps(eps),
  1244 + m_order (order),
  1245 + m_psf_max(rsp->psf()(srcDir, 0.0, srcEng)) { }
1474 double eval(const double& delta); 1246 double eval(const double& delta);
1475 protected: 1247 protected:
1476 - const GCTAResponseIrf* m_rsp; //!< Response  
1477 - const GModelSpatialRadial* m_model; //!< Radial model  
1478 - const GCTAPointing& m_pnt; //!< CTA pointing  
1479 - const GEnergy& m_srcEng; //!< True photon energy  
1480 - const double& m_srcLogEng; //!< Log10(true photon energy)  
1481 - const GTime& m_srcTime; //!< True photon arrival time  
1482 - const double& m_obsOffset; //!< Observed photon offset angle  
1483 - const double m_sin_obs_offset;  
1484 - const double m_cos_obs_offset;  
1485 - const GEnergy& m_obsEng; //!< Measured event energy  
1486 - const double& m_zeta; //!< zeta  
1487 - const double& m_phi0; //!< phi0  
1488 - const double m_sin_zeta; //!< sin(zeta)  
1489 - const double m_cos_zeta; //!< cos(zeta)  
1490 - const double& m_theta_max; //!< Model radius  
1491 - const double m_cos_theta_max;  
1492 - const int& m_iter; //!< Integration iterations 1248 + const GCTAResponseCube* m_rsp; //!< Response cube
  1249 + const GModelSpatial* m_model; //!< Spatial model
  1250 + const GSkyDir& m_srcDir; //!< True photon arrival direction
  1251 + const GEnergy& m_srcEng; //!< True photon energy
  1252 + const GTime& m_srcTime; //!< True photon arrival time
  1253 + const GMatrix& m_rot; //!< Rotation matrix
  1254 + const double& m_eps; //!< Integration precision
  1255 + const int& m_order; //!< Romberg order
  1256 + double m_psf_max; //!< Maximum PSF value
1493 }; 1257 };
1494 1258
1495 1259
1496 /***********************************************************************//** 1260 /***********************************************************************//**
1497 - * @class cta_irf_radial_kern_phi  
1498 - *  
1499 - * @brief Test kernel for Irf phi angle integration 1261 + * @class cta_psf_diffuse_kern_phi
1500 * 1262 *
1501 - * This class is used for Psf system based integration. It is only here for  
1502 - * testing and this class may be removed in the future. 1263 + * @brief Kernel for Psf phi angle integration used for stacked analysis
1503 ***************************************************************************/ 1264 ***************************************************************************/
1504 -class cta_irf_radial_kern_phi : public GFunction { 1265 +class cta_psf_diffuse_kern_phi : public GFunction {
1505 public: 1266 public:
1506 - cta_irf_radial_kern_phi(const GCTAResponseIrf* rsp,  
1507 - const GModelSpatialRadial* model,  
1508 - const GCTAPointing& pnt,  
1509 - const GEnergy& srcEng,  
1510 - const double& srcLogEng,  
1511 - const GTime& srcTime,  
1512 - const GEnergy& obsEng,  
1513 - const double& phi0,  
1514 - const double& sin_fact_model,  
1515 - const double& cos_fact_model,  
1516 - const double& sin_fact_inst,  
1517 - const double& cos_fact_inst) :  
1518 - m_rsp(rsp),  
1519 - m_model(model),  
1520 - m_pnt(pnt),  
1521 - m_srcEng(srcEng),  
1522 - m_srcLogEng(srcLogEng),  
1523 - m_srcTime(srcTime),  
1524 - m_obsEng(obsEng),  
1525 - m_phi0(phi0),  
1526 - m_sin_fact_model(sin_fact_model),  
1527 - m_cos_fact_model(cos_fact_model),  
1528 - m_sin_fact_inst(sin_fact_inst),  
1529 - m_cos_fact_inst(cos_fact_inst) { } 1267 + cta_psf_diffuse_kern_phi(const GModelSpatial* model,
  1268 + const GEnergy& srcEng,
  1269 + const GTime& srcTime,
  1270 + const GMatrix& rot,
  1271 + const double& sin_delta,
  1272 + const double& cos_delta) :
  1273 + m_model(model),
  1274 + m_srcEng(srcEng),
  1275 + m_srcTime(srcTime),
  1276 + m_rot(rot),
  1277 + m_sin_delta(sin_delta),
  1278 + m_cos_delta(cos_delta) { }
1530 double eval(const double& phi); 1279 double eval(const double& phi);
1531 protected: 1280 protected:
1532 - const GCTAResponseIrf* m_rsp; //!< Response  
1533 - const GModelSpatialRadial* m_model; //!< Radial model  
1534 - const GCTAPointing& m_pnt; //!< CTA pointing  
1535 - const GEnergy& m_srcEng; //!< True photon energy  
1536 - const double& m_srcLogEng; //!< Log10(true photon energy)  
1537 - const GTime& m_srcTime; //!< True photon arrival time  
1538 - const GEnergy& m_obsEng; //!< Measured event energy  
1539 - const double& m_phi0; //!< phi0  
1540 - const double& m_sin_fact_model; //!< sin(delta)*sin(zeta)  
1541 - const double& m_cos_fact_model; //!< cos(delta)*cos(zeta)  
1542 - const double& m_sin_fact_inst; //!< sin(delta)*sin(obsOffset)  
1543 - const double& m_cos_fact_inst; //!< cos(delta)*cos(obsOffset) 1281 + const GModelSpatial* m_model; //!< Spatial model
  1282 + const GEnergy& m_srcEng; //!< True photon energy
  1283 + const GTime& m_srcTime; //!< True photon arrival time
  1284 + const GMatrix& m_rot; //!< Rotation matrix
  1285 + const double& m_sin_delta; //!< sin(delta)
  1286 + const double& m_cos_delta; //!< cos(delta)
1544 }; 1287 };
1545 1288
1546 #endif /* GCTARESPONSE_HELPERS_HPP */ 1289 #endif /* GCTARESPONSE_HELPERS_HPP */