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 111 void free_members(void);
112 112 int cache_index(const std::string& name) const;
113 113 double psf_radial(const GModelSpatialRadial* model,
114   - const double& zeta,
  114 + const double& rho_obs,
115 115 const GSkyDir& obsDir,
116 116 const GEnergy& srcEng,
117 117 const GTime& srcTime) const;
... ...
inst/cta/src/GCTAResponseCube.cpp
... ... @@ -37,6 +37,7 @@
37 37 #include "GCTASourceCubeDiffuse.hpp"
38 38 #include "GModelSpatialPointSource.hpp"
39 39 #include "GModelSpatialRadial.hpp"
  40 +#include "GModelSpatialRadialShell.hpp"
40 41 #include "GModelSpatialElliptical.hpp"
41 42 #include "GModelSpatialDiffuse.hpp"
42 43 #include "GPhoton.hpp"
... ... @@ -63,8 +64,6 @@
63 64 /* __ Macros _____________________________________________________________ */
64 65  
65 66 /* __ Coding definitions _________________________________________________ */
66   -#define G_NEW_PSF_RADIAL
67   -#define G_NEW_PSF_ELLIPTICAL
68 67  
69 68 /* __ Debug definitions __________________________________________________ */
70 69  
... ... @@ -464,11 +463,11 @@ double GCTAResponseCube::irf_radial(const GEvent& event,
464 463  
465 464 // Compute angle between model centre and measured photon direction
466 465 // (radians)
467   - double zeta = model->dir().dist(obsDir);
  466 + double rho_obs = model->dir().dist(obsDir);
468 467  
469 468 // Continue only if we're sufficiently close to the model centre to get a
470 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 472 // Get exposure
474 473 irf = exposure()(obsDir, obsEng);
... ... @@ -476,7 +475,7 @@ double GCTAResponseCube::irf_radial(const GEvent&amp; event,
476 475 // Continue only if exposure is positive
477 476 if (irf > 0.0) {
478 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 479 irf *= obs.deadc(obsTime);
481 480 }
482 481  
... ... @@ -535,8 +534,8 @@ double GCTAResponseCube::irf_elliptical(const GEvent&amp; event,
535 534  
536 535 // Compute angle between model centre and measured photon direction and
537 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 540 // Continue only if we're sufficiently close to the model centre to get a
542 541 // non-zero response
... ... @@ -927,8 +926,8 @@ int GCTAResponseCube::cache_index(const std::string&amp; name) const
927 926 * @brief Integrate PSF over radial model
928 927 *
929 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 931 * @param[in] obsDir Observed event direction.
933 932 * @param[in] srcEng True photon energy.
934 933 * @param[in] srcTime True photon arrival time.
... ... @@ -949,9 +948,8 @@ int GCTAResponseCube::cache_index(const std::string&amp; name) const
949 948 * \f${\rm PSF}(\delta)\f$ is the azimuthally symmetric point spread
950 949 * function.
951 950 ***************************************************************************/
952   -#if defined(G_NEW_PSF_ELLIPTICAL)
953 951 double GCTAResponseCube::psf_radial(const GModelSpatialRadial* model,
954   - const double& rho_obs, // was zeta
  952 + const double& rho_obs,
955 953 const GSkyDir& obsDir,
956 954 const GEnergy& srcEng,
957 955 const GTime& srcTime) const
... ... @@ -968,7 +966,7 @@ double GCTAResponseCube::psf_radial(const GModelSpatialRadial* model,
968 966 // Get maximum PSF radius (radians)
969 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 970 double rho_min = (rho_obs > delta_max) ? rho_obs - delta_max : 0.0;
973 971 double rho_max = rho_obs + delta_max;
974 972 double src_max = model->theta_max();
... ... @@ -1016,6 +1014,17 @@ double GCTAResponseCube::psf_radial(const GModelSpatialRadial* model,
1016 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 1028 // Integrate kernel
1020 1029 irf = integral.romberg(bounds, iter_rho);
1021 1030  
... ... @@ -1036,49 +1045,6 @@ double GCTAResponseCube::psf_radial(const GModelSpatialRadial* model,
1036 1045 // Return PSF
1037 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 1090 * \f${\rm PSF}(\delta)\f$ is the azimuthally symmetric point spread
1125 1091 * function.
1126 1092 ***************************************************************************/
1127   -#if defined(G_NEW_PSF_ELLIPTICAL)
1128 1093 double GCTAResponseCube::psf_elliptical(const GModelSpatialElliptical* model,
1129 1094 const double& rho_obs,
1130 1095 const double& posangle_obs,
... ... @@ -1201,16 +1166,6 @@ double GCTAResponseCube::psf_elliptical(const GModelSpatialElliptical* model,
1201 1166 bounds.push_back(rho_min);
1202 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 1169 // If the integration range includes the semiminor boundary, then
1215 1170 // add an integration boundary at that location
1216 1171 if (semiminor > rho_min && semiminor < rho_max) {
... ... @@ -1237,47 +1192,3 @@ double GCTAResponseCube::psf_elliptical(const GModelSpatialElliptical* model,
1237 1192 // Return PSF
1238 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 1282 * @exception GCTAException::bad_model_type
1283 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 1285 * Integrates the product of the model and the Irf over the true photon
1454 1286 * arrival direction using
1455 1287 *
... ... @@ -1617,7 +1449,7 @@ double GCTAResponseIrf::irf_radial(const GEvent&amp; event,
1617 1449 // location
1618 1450 const GModelSpatialRadialShell* shell = dynamic_cast<const GModelSpatialRadialShell*>(model);
1619 1451 if (shell != NULL) {
1620   - double shell_radius = shell->radius();
  1452 + double shell_radius = shell->radius() * gammalib::deg2rad;
1621 1453 if (shell_radius > rho_min && shell_radius < rho_max) {
1622 1454 bounds.push_back(shell_radius);
1623 1455 }
... ... @@ -1663,7 +1495,6 @@ double GCTAResponseIrf::irf_radial(const GEvent&amp; event,
1663 1495 // Return IRF value
1664 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 1655 bounds.push_back(rho_min);
1825 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 1658 // If the integration range includes the semiminor boundary, then
1838 1659 // add an integration boundary at that location
1839 1660 if (semiminor > rho_min && semiminor < rho_max) {
... ... @@ -2224,7 +2045,7 @@ double GCTAResponseIrf::npred_radial(const GSource&amp; source,
2224 2045 // location
2225 2046 const GModelSpatialRadialShell* shell = dynamic_cast<const GModelSpatialRadialShell*>(model);
2226 2047 if (shell != NULL) {
2227   - double shell_radius = shell->radius();
  2048 + double shell_radius = shell->radius() * gammalib::deg2rad;
2228 2049 if (shell_radius > rho_min && shell_radius < rho_max) {
2229 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 283 double omega_min = -domega;
284 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 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 296 // Debug: test if model is non positive
290 297 #if defined(G_DEBUG_MODEL_ZERO)
... ... @@ -292,7 +299,7 @@ double cta_irf_radial_kern_rho::eval(const double&amp; rho)
292 299 std::cout << "*** WARNING: cta_irf_radial_kern_rho::eval";
293 300 std::cout << " zero model for (rho)=(";
294 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 303 std::cout << " radians" << std::endl;
297 304 }
298 305 #endif
... ... @@ -474,8 +481,15 @@ double cta_npred_radial_kern_rho::eval(const double&amp; rho)
474 481 double omega_min = m_omega0 - domega;
475 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 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 494 // Debug: test if model is non positive
481 495 #if defined(G_DEBUG_MODEL_ZERO)
... ... @@ -483,7 +497,7 @@ double cta_npred_radial_kern_rho::eval(const double&amp; rho)
483 497 std::cout << "*** WARNING: cta_npred_radial_kern_rho::eval";
484 498 std::cout << " zero model for (rho)=(";
485 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 501 std::cout << " radians" << std::endl;
488 502 }
489 503 #endif
... ... @@ -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 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 1565 if (domega > 0.0) {
1778 1566  
1779 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 1569 double rho_kluge = rho - 1.0e-12;
1783 1570 if (rho_kluge < 0.0) {
1784 1571 rho_kluge = 0.0;
... ... @@ -1793,7 +1580,7 @@ double cta_psf_radial_kern_rho::eval(const double&amp; rho)
1793 1580 std::cout << "*** WARNING: cta_psf_radial_kern_rho::eval";
1794 1581 std::cout << " zero model for (rho)=(";
1795 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 1584 std::cout << " radians" << std::endl;
1798 1585 }
1799 1586 #endif
... ... @@ -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 1702 * @brief Kernel for elliptical model integration over zenith angle
2043 1703 *
2044 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 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 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 1976 * not included in the azimuthally integration, but just multiplied on the
2327 1977 * azimuthally integrated model. The method returns thus
2328 1978 *
2329 1979 * \f[
2330   - * {\rm Psf}(\delta) \times
  1980 + * {\rm PSF}(\delta) \times
2331 1981 * \int_0^{2\pi} {\rm M}(\delta, \phi) \sin \delta {\rm d}\phi
2332 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 1985 * system of the point spread function, defined by the angle \f$\delta\f$
2336 1986 * between the true and the measured photon direction and the azimuth angle
2337 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 1991 // Initialise value
2342 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 1995 // term. We thus only integrate for positive deltas.
2346 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 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 2020 } // endif: delta was positive
2409 2021  
2410 2022 // Debug: Check for NaN
2411 2023 #if defined(G_NAN_CHECK)
2412 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 2026 std::cout << "(delta=" << delta << "):";
2415 2027 std::cout << " NaN/Inf encountered";
2416 2028 std::cout << " (value=" << value;
... ... @@ -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 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 2069 // Debug: Check for NaN
2497 2070 #if defined(G_NAN_CHECK)
2498 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 2073 std::cout << "(phi=" << phi << "):";
2501 2074 std::cout << " NaN/Inf encountered";
2502 2075 std::cout << " (value=" << value;
... ...
inst/cta/src/GCTAResponse_helpers.hpp
... ... @@ -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 985 * @class cta_irf_radial_kern_rho
1122 986 *
1123 987 * @brief Kernel for radial model zenith angle integration
... ... @@ -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 1094 * @class cta_irf_elliptical_kern_rho
1308 1095 *
1309 1096 * @brief Kernel for elliptical model zenith angle integration
... ... @@ -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 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 1246 double eval(const double& delta);
1475 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 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 1279 double eval(const double& phi);
1531 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 1289 #endif /* GCTARESPONSE_HELPERS_HPP */
... ...