Commit 3b85d07c344dae38bcadaa1ae43d20d46574f0d4
1 parent
2b014284
add source region phi range selection
Showing
2 changed files
with
168 additions
and
71 deletions
inst/cta/include/GCTAResponse.hpp
| ... | ... | @@ -161,6 +161,7 @@ public: |
| 161 | 161 | // Analytical PSF implementation |
| 162 | 162 | double psf_dummy(const double& delta, const double& sigma) const; |
| 163 | 163 | double psf_dummy_sigma(const double& srcLogEng) const; |
| 164 | + double psf_dummy_max(const double& sigma) const; | |
| 164 | 165 | |
| 165 | 166 | private: |
| 166 | 167 | // Private methods |
| ... | ... | @@ -178,6 +179,7 @@ private: |
| 178 | 179 | const GCTADir* obsCam, |
| 179 | 180 | const GCTADir* srcCam, |
| 180 | 181 | double delta_max, |
| 182 | + double src_max, | |
| 181 | 183 | double zenith, |
| 182 | 184 | double azimuth, |
| 183 | 185 | double srcLogEng, |
| ... | ... | @@ -190,6 +192,8 @@ private: |
| 190 | 192 | m_src_cam(srcCam), |
| 191 | 193 | m_delta_max(delta_max), |
| 192 | 194 | m_cos_delta_max(std::cos(delta_max)), |
| 195 | + m_src_max(src_max), | |
| 196 | + m_cos_src_max(std::cos(src_max)), | |
| 193 | 197 | m_zenith(zenith), |
| 194 | 198 | m_azimuth(azimuth), |
| 195 | 199 | m_srcLogEng(srcLogEng), |
| ... | ... | @@ -204,6 +208,8 @@ private: |
| 204 | 208 | const GCTADir* m_src_cam; //!< Pointer to source location in camera |
| 205 | 209 | double m_delta_max; //!< Maximum PSF angle delta_max |
| 206 | 210 | double m_cos_delta_max; //!< Cosine of delta_max |
| 211 | + double m_src_max; //!< Maximum source radius | |
| 212 | + double m_cos_src_max; //!< Cosine of source radius | |
| 207 | 213 | double m_zenith; //!< Telescope zenith |
| 208 | 214 | double m_azimuth; //!< Telescope azimuth |
| 209 | 215 | double m_srcLogEng; //!< True photon energy | ... | ... |
inst/cta/src/GCTAResponse.cpp
| ... | ... | @@ -558,6 +558,9 @@ double GCTAResponse::irf_ptsrc(const GCTAInstDir& obsDir, |
| 558 | 558 | * @todo The telescope zenith and azimuth angles as well as the radial offset |
| 559 | 559 | * and polar angles in the camera are not yet computed correctly (as |
| 560 | 560 | * the actual IRF implement does not need these values). |
| 561 | + * | |
| 562 | + * @todo The theta limits could still improved as only the theta range is | |
| 563 | + * relevant for which the source and the PSF circle overlap. | |
| 561 | 564 | ***************************************************************************/ |
| 562 | 565 | double GCTAResponse::irf_extended(const GCTAInstDir& obsDir, |
| 563 | 566 | const GEnergy& obsEng, |
| ... | ... | @@ -585,34 +588,54 @@ double GCTAResponse::irf_extended(const GCTAInstDir& obsDir, |
| 585 | 588 | // Get PSF sigma |
| 586 | 589 | double sigma = psf_dummy_sigma(srcLogEng); |
| 587 | 590 | |
| 588 | - // Get maximum angular separation for which PSF is significant | |
| 589 | - double delta_max = psf_delta_max(theta, phi, zenith, azimuth, srcLogEng); | |
| 590 | - | |
| 591 | 591 | // Determine camera direction of measured photon |
| 592 | 592 | GCTADir obsCam(obsDir, *pnt); |
| 593 | 593 | |
| 594 | 594 | // Determine camera direction of source model centre |
| 595 | 595 | GCTADir srcCam(model.dir(), *pnt); |
| 596 | 596 | |
| 597 | - // Setup integration kernel | |
| 598 | - GCTAResponse::irf_kern_theta integrand(this, | |
| 599 | - pnt, | |
| 600 | - model.radial(), | |
| 601 | - &obsCam, | |
| 602 | - &srcCam, | |
| 603 | - delta_max, | |
| 604 | - zenith, | |
| 605 | - azimuth, | |
| 606 | - srcLogEng, | |
| 607 | - obsLogEng, | |
| 608 | - sigma); | |
| 609 | - | |
| 610 | - // Integrate over theta | |
| 611 | - GIntegral integral(&integrand); | |
| 612 | - integral.eps(m_eps); | |
| 613 | - double theta_min = (obsCam.theta() > delta_max) ? obsCam.theta() - delta_max : 0.0; | |
| 614 | - double theta_max = obsCam.theta() + delta_max; | |
| 615 | - double irf = integral.romb(theta_min, theta_max); | |
| 597 | + // Get maximum angular separation for which PSF is significant (in radians). | |
| 598 | + // Set offset angle range that bounds PSF. | |
| 599 | + double delta_max = psf_delta_max(theta, phi, zenith, azimuth, srcLogEng); | |
| 600 | + double theta_psf_min = (obsCam.theta() > delta_max) ? obsCam.theta() - delta_max : 0.0; | |
| 601 | + double theta_psf_max = obsCam.theta() + delta_max; | |
| 602 | + | |
| 603 | + // Get maximum source radius in radians. | |
| 604 | + // Set offset angle range that bounds the source. | |
| 605 | + double src_max = model.radial()->theta_max(); | |
| 606 | + double theta_src_min = (srcCam.theta() > src_max) ? srcCam.theta() - src_max : 0.0; | |
| 607 | + double theta_src_max = srcCam.theta() + src_max; | |
| 608 | + | |
| 609 | + // Set overlapping offset angle range for integration (in radians). | |
| 610 | + double theta_min = std::max(theta_psf_min, theta_src_min); | |
| 611 | + double theta_max = std::min(theta_psf_max, theta_src_max); | |
| 612 | + | |
| 613 | + // Initialise IRF value | |
| 614 | + double irf = 0.0; | |
| 615 | + | |
| 616 | + // Perform offset angle integration if interval is valid | |
| 617 | + if (theta_max > theta_min) { | |
| 618 | + | |
| 619 | + // Setup integration kernel | |
| 620 | + GCTAResponse::irf_kern_theta integrand(this, | |
| 621 | + pnt, | |
| 622 | + model.radial(), | |
| 623 | + &obsCam, | |
| 624 | + &srcCam, | |
| 625 | + delta_max, | |
| 626 | + src_max, | |
| 627 | + zenith, | |
| 628 | + azimuth, | |
| 629 | + srcLogEng, | |
| 630 | + obsLogEng, | |
| 631 | + sigma); | |
| 632 | + | |
| 633 | + // Integrate over theta | |
| 634 | + GIntegral integral(&integrand); | |
| 635 | + integral.eps(m_eps); | |
| 636 | + irf = integral.romb(theta_min, theta_max); | |
| 637 | + | |
| 638 | + } | |
| 616 | 639 | |
| 617 | 640 | // Compile option: Show integration results |
| 618 | 641 | #if G_DEBUG_IRF_EXTENDED |
| ... | ... | @@ -856,7 +879,7 @@ double GCTAResponse::psf_delta_max(const double& theta, |
| 856 | 879 | double sigma = psf_dummy_sigma(srcLogEng); |
| 857 | 880 | |
| 858 | 881 | // Set maximum angular separation |
| 859 | - double delta_max = sigma * 5.0; | |
| 882 | + double delta_max = psf_dummy_max(sigma); | |
| 860 | 883 | |
| 861 | 884 | // Return PSF |
| 862 | 885 | return delta_max; |
| ... | ... | @@ -922,9 +945,7 @@ double GCTAResponse::npsf(const GSkyDir& srcDir, |
| 922 | 945 | double radroi = roi.radius() * deg2rad; |
| 923 | 946 | double psf = roi.centre().dist(srcDir); |
| 924 | 947 | double sigma = psf_dummy_sigma(srcLogEng); |
| 925 | - | |
| 926 | - // Get maximum PSF radius | |
| 927 | - double rmax = 5.0*sigma; | |
| 948 | + double rmax = psf_dummy_max(sigma); | |
| 928 | 949 | |
| 929 | 950 | // If PSF is sufficiently enclosed by ROI, skip the numerical integration |
| 930 | 951 | // and assume that the integral is 1.0 |
| ... | ... | @@ -1033,6 +1054,18 @@ double GCTAResponse::psf_dummy_sigma(const double& srcLogEng) const |
| 1033 | 1054 | } |
| 1034 | 1055 | |
| 1035 | 1056 | |
| 1057 | +/***********************************************************************//** | |
| 1058 | + * @brief Returns radius beyond which PSF is negligible (in radians) | |
| 1059 | + * | |
| 1060 | + * @param[in] sigma Width of point spread function (radians). | |
| 1061 | + ***************************************************************************/ | |
| 1062 | +double GCTAResponse::psf_dummy_max(const double& sigma) const | |
| 1063 | +{ | |
| 1064 | + // Return radius | |
| 1065 | + return (5.0*sigma); | |
| 1066 | +} | |
| 1067 | + | |
| 1068 | + | |
| 1036 | 1069 | /*========================================================================== |
| 1037 | 1070 | = = |
| 1038 | 1071 | = Private methods = |
| ... | ... | @@ -1042,8 +1075,20 @@ double GCTAResponse::psf_dummy_sigma(const double& srcLogEng) const |
| 1042 | 1075 | /***********************************************************************//** |
| 1043 | 1076 | * @brief Initialise class members |
| 1044 | 1077 | * |
| 1045 | - * We set the relative integration precision to 1e-4 as test images have | |
| 1046 | - * revealed smooth with this precision. | |
| 1078 | + * We set the relative integration precision to 1e-4 as test images are | |
| 1079 | + * pretty smooth with this precision and computations are reasonably fast. | |
| 1080 | + * We know that integration is not perfect, however, and the results are | |
| 1081 | + * still a little noisy. We could still improve a little more the current | |
| 1082 | + * theta integration scheme, but the mathematics have to be worked out | |
| 1083 | + * for this first. | |
| 1084 | + * | |
| 1085 | + * The following timing was obtained for a 0.4 deg disk model on a 100x100 | |
| 1086 | + * grid with 10 energy layers on a 64 Bit machine (fermi): | |
| 1087 | + * m_eps = 1e-4 : user 0m12.894s | |
| 1088 | + * m_eps = 5e-5 : user 0m21.929s | |
| 1089 | + * m_eps = 1e-5 : user 1m53.607s | |
| 1090 | + * So the time increases about linearly with an increased precision | |
| 1091 | + * requirement. | |
| 1047 | 1092 | ***************************************************************************/ |
| 1048 | 1093 | void GCTAResponse::init_members(void) |
| 1049 | 1094 | { |
| ... | ... | @@ -1108,7 +1153,7 @@ void GCTAResponse::free_members(void) |
| 1108 | 1153 | void GCTAResponse::read_performance_table(const std::string& filename) |
| 1109 | 1154 | { |
| 1110 | 1155 | // Allocate line buffer |
| 1111 | - const int n = 1000; | |
| 1156 | + const int n = 1000; | |
| 1112 | 1157 | char line[n]; |
| 1113 | 1158 | |
| 1114 | 1159 | // Open performance table readonly |
| ... | ... | @@ -1165,53 +1210,99 @@ void GCTAResponse::read_performance_table(const std::string& filename) |
| 1165 | 1210 | * |
| 1166 | 1211 | * This method provides the integration kernel for the radial offset angle |
| 1167 | 1212 | * IRF integration. The radial offset angle is given in the camera system. |
| 1213 | + * | |
| 1214 | + * @todo I tried to cover the wrap around properly, yet I have not | |
| 1215 | + * demonstrated mathematically that it works in all cases (but I did | |
| 1216 | + * many tests to check that it works). | |
| 1168 | 1217 | ***************************************************************************/ |
| 1169 | 1218 | double GCTAResponse::irf_kern_theta::eval(double theta) |
| 1170 | 1219 | { |
| 1171 | - // Compute half interval length (in radians) | |
| 1172 | - double delta_phi = 0.5 * cta_roi_arclength(theta, | |
| 1173 | - m_obs_cam->theta(), | |
| 1174 | - m_obs_cam->costheta(), | |
| 1175 | - m_obs_cam->sintheta(), | |
| 1176 | - m_delta_max, m_cos_delta_max); | |
| 1177 | - | |
| 1178 | - // Set phi interval (in radians) | |
| 1179 | - double phi_min = m_obs_cam->phi() - delta_phi; | |
| 1180 | - double phi_max = m_obs_cam->phi() + delta_phi; | |
| 1181 | - | |
| 1182 | - // Precompute cosine and sine terms for azimuthal integration | |
| 1183 | - double cos_theta = std::cos(theta); | |
| 1184 | - double sin_theta = std::sin(theta); | |
| 1185 | - double cos_obs = cos_theta * m_obs_cam->costheta(); | |
| 1186 | - double sin_obs = sin_theta * m_obs_cam->sintheta(); | |
| 1187 | - double cos_src = cos_theta * m_src_cam->costheta(); | |
| 1188 | - double sin_src = sin_theta * m_src_cam->sintheta(); | |
| 1189 | - | |
| 1190 | - // Setup integration kernel | |
| 1191 | - GCTAResponse::irf_kern_phi integrand(m_rsp, | |
| 1192 | - m_pnt, | |
| 1193 | - m_radial, | |
| 1194 | - m_obs_cam, | |
| 1195 | - m_src_cam, | |
| 1196 | - theta, | |
| 1197 | - m_zenith, | |
| 1198 | - m_azimuth, | |
| 1199 | - m_srcLogEng, | |
| 1200 | - m_obsLogEng, | |
| 1201 | - m_sigma, | |
| 1202 | - cos_obs, | |
| 1203 | - sin_obs, | |
| 1204 | - cos_src, | |
| 1205 | - sin_src); | |
| 1206 | - | |
| 1207 | - // Integrate over phi | |
| 1208 | - GIntegral integral(&integrand); | |
| 1209 | - integral.eps(m_rsp->m_eps); | |
| 1210 | - integral.silent(true); | |
| 1211 | - double value = integral.romb(phi_min, phi_max) * sin_theta; | |
| 1220 | + // Compute half interval length for PSF circle (in radians) | |
| 1221 | + double dphi_psf = 0.5 * cta_roi_arclength(theta, | |
| 1222 | + m_obs_cam->theta(), | |
| 1223 | + m_obs_cam->costheta(), | |
| 1224 | + m_obs_cam->sintheta(), | |
| 1225 | + m_delta_max, m_cos_delta_max); | |
| 1226 | + | |
| 1227 | + // Compute half interval length for model circle (in radians) | |
| 1228 | + double dphi_src = 0.5 * cta_roi_arclength(theta, | |
| 1229 | + m_src_cam->theta(), | |
| 1230 | + m_src_cam->costheta(), | |
| 1231 | + m_src_cam->sintheta(), | |
| 1232 | + m_src_max, m_cos_src_max); | |
| 1233 | + | |
| 1234 | + // Set the phi phase to common half space | |
| 1235 | + double phi_psf = m_obs_cam->phi(); | |
| 1236 | + double phi_src = m_src_cam->phi(); | |
| 1237 | + if (phi_src-phi_psf > pi) | |
| 1238 | + phi_src -= twopi; | |
| 1239 | + if (phi_src-phi_psf < -pi) | |
| 1240 | + phi_src += twopi; | |
| 1241 | + | |
| 1242 | + // Set phi interval for PSF and source region (in radians) | |
| 1243 | + double phi_psf_min = phi_psf - dphi_psf; | |
| 1244 | + double phi_psf_max = phi_psf + dphi_psf; | |
| 1245 | + double phi_src_min = phi_src - dphi_src; | |
| 1246 | + double phi_src_max = phi_src + dphi_src; | |
| 1247 | + | |
| 1248 | + // Set common phi interval. If one of the intervals covers the entire | |
| 1249 | + // azimuth range then just apply the other interval | |
| 1250 | + double phi_min; | |
| 1251 | + double phi_max; | |
| 1252 | + if (dphi_src >= pi) { | |
| 1253 | + phi_min = phi_psf_min; | |
| 1254 | + phi_max = phi_psf_max; | |
| 1255 | + } | |
| 1256 | + else if (dphi_psf >= pi) { | |
| 1257 | + phi_min = phi_src_min; | |
| 1258 | + phi_max = phi_src_max; | |
| 1259 | + } | |
| 1260 | + else { | |
| 1261 | + phi_min = std::max(phi_psf_min, phi_src_min); | |
| 1262 | + phi_max = std::min(phi_psf_max, phi_src_max); | |
| 1263 | + } | |
| 1264 | + | |
| 1265 | + // Initialise result | |
| 1266 | + double irf = 0.0; | |
| 1267 | + | |
| 1268 | + // Perform phi integration (only if interval is valid) | |
| 1269 | + if (phi_min < phi_max) { | |
| 1270 | + | |
| 1271 | + // Precompute cosine and sine terms for azimuthal integration | |
| 1272 | + double cos_theta = std::cos(theta); | |
| 1273 | + double sin_theta = std::sin(theta); | |
| 1274 | + double cos_obs = cos_theta * m_obs_cam->costheta(); | |
| 1275 | + double sin_obs = sin_theta * m_obs_cam->sintheta(); | |
| 1276 | + double cos_src = cos_theta * m_src_cam->costheta(); | |
| 1277 | + double sin_src = sin_theta * m_src_cam->sintheta(); | |
| 1278 | + | |
| 1279 | + // Setup integration kernel | |
| 1280 | + GCTAResponse::irf_kern_phi integrand(m_rsp, | |
| 1281 | + m_pnt, | |
| 1282 | + m_radial, | |
| 1283 | + m_obs_cam, | |
| 1284 | + m_src_cam, | |
| 1285 | + theta, | |
| 1286 | + m_zenith, | |
| 1287 | + m_azimuth, | |
| 1288 | + m_srcLogEng, | |
| 1289 | + m_obsLogEng, | |
| 1290 | + m_sigma, | |
| 1291 | + cos_obs, | |
| 1292 | + sin_obs, | |
| 1293 | + cos_src, | |
| 1294 | + sin_src); | |
| 1295 | + | |
| 1296 | + // Integrate over phi | |
| 1297 | + GIntegral integral(&integrand); | |
| 1298 | + integral.eps(m_rsp->m_eps); | |
| 1299 | + integral.silent(true); | |
| 1300 | + irf = integral.romb(phi_min, phi_max) * sin_theta; | |
| 1301 | + | |
| 1302 | + } // endif: phi intervals was valid | |
| 1212 | 1303 | |
| 1213 | 1304 | // Return result |
| 1214 | - return value; | |
| 1305 | + return irf; | |
| 1215 | 1306 | } |
| 1216 | 1307 | |
| 1217 | 1308 | ... | ... |