Commit f522be3ac0b17b470e5a56a3e9875ce320c54fbb

Authored by Jürgen Knödlseder
1 parent ab4461b6

Finalize response optimization.

inst/cta/src/GCTAResponseCube.cpp
... ... @@ -64,6 +64,7 @@
64 64 /* __ Macros _____________________________________________________________ */
65 65  
66 66 /* __ Coding definitions _________________________________________________ */
  67 +#define G_RADIAL_PSF_BASED //!< Use Psf-based integration for radial model
67 68  
68 69 /* __ Debug definitions __________________________________________________ */
69 70  
... ... @@ -237,11 +238,12 @@ GCTAResponseCube* GCTAResponseCube::clone(void) const
237 238  
238 239  
239 240 /***********************************************************************//**
240   - * @brief Return value of instrument response function
  241 + * @brief Return instrument response
241 242 *
242 243 * @param[in] event Observed event.
243 244 * @param[in] photon Incident photon.
244 245 * @param[in] obs Observation.
  246 + * @return Instrument response.
245 247 ***************************************************************************/
246 248 double GCTAResponseCube::irf(const GEvent& event,
247 249 const GPhoton& photon,
... ... @@ -308,14 +310,14 @@ double GCTAResponseCube::irf(const GEvent&amp; event,
308 310  
309 311  
310 312 /***********************************************************************//**
311   - * @brief Return value of instrument response function
  313 + * @brief Return instrument response
312 314 *
313 315 * @param[in] event Event.
314 316 * @param[in] source Source.
315 317 * @param[in] obs Observation.
  318 + * @return Instrument response.
316 319 *
317   - * Returns the instrument response function for a given event, source and
318   - * observation.
  320 + * Returns the instrument response for a given event, source and observation.
319 321 ***************************************************************************/
320 322 double GCTAResponseCube::irf(const GEvent& event,
321 323 const GSource& source,
... ... @@ -348,15 +350,14 @@ double GCTAResponseCube::irf(const GEvent&amp; event,
348 350  
349 351  
350 352 /***********************************************************************//**
351   - * @brief Return value of point source instrument response function
  353 + * @brief Return instrument response to point source
352 354 *
353 355 * @param[in] event Observed event.
354 356 * @param[in] source Source.
355 357 * @param[in] obs Observation.
356   - * @return Value of instrument response function for a point source.
  358 + * @return Instrument response to point source.
357 359 *
358   - * This method returns the value of the instrument response function for a
359   - * point source.
  360 + * Returns the instrument response to a specified point source.
360 361 ***************************************************************************/
361 362 double GCTAResponseCube::irf_ptsrc(const GEvent& event,
362 363 const GSource& source,
... ... @@ -426,15 +427,14 @@ double GCTAResponseCube::irf_ptsrc(const GEvent&amp; event,
426 427  
427 428  
428 429 /***********************************************************************//**
429   - * @brief Return value of radial source instrument response function
  430 + * @brief Return instrument response to radial source
430 431 *
431 432 * @param[in] event Observed event.
432 433 * @param[in] source Source.
433 434 * @param[in] obs Observation.
434   - * @return Value of instrument response function for a radial source.
  435 + * @return Instrument response to radial source.
435 436 *
436   - * This method returns the value of the instrument response function for a
437   - * radial source. Response values are computed on the fly.
  437 + * Returns the instrument response to a specified radial source.
438 438 ***************************************************************************/
439 439 double GCTAResponseCube::irf_radial(const GEvent& event,
440 440 const GSource& source,
... ... @@ -497,15 +497,14 @@ double GCTAResponseCube::irf_radial(const GEvent&amp; event,
497 497  
498 498  
499 499 /***********************************************************************//**
500   - * @brief Return value of elliptical source instrument response function
  500 + * @brief Return instrument response to elliptical source
501 501 *
502 502 * @param[in] event Observed event.
503 503 * @param[in] source Source.
504 504 * @param[in] obs Observation.
505   - * @return Value of instrument response function for an elliptical source.
  505 + * @return Instrument response to elliptical source.
506 506 *
507   - * This method returns the value of the instrument response function for an
508   - * elliptical source. Response values are computed on the fly.
  507 + * Returns the instrument response to a specified elliptical source.
509 508 ***************************************************************************/
510 509 double GCTAResponseCube::irf_elliptical(const GEvent& event,
511 510 const GSource& source,
... ... @@ -569,21 +568,20 @@ double GCTAResponseCube::irf_elliptical(const GEvent&amp; event,
569 568  
570 569  
571 570 /***********************************************************************//**
572   - * @brief Return value of extended source instrument response function
  571 + * @brief Return instrument response to diffuse source
573 572 *
574 573 * @param[in] event Observed event.
575 574 * @param[in] source Source.
576 575 * @param[in] obs Observation.
577   - * @return Value of instrument response function for an extended source.
  576 + * @return Instrument response to diffuse source.
578 577 *
579   - * This method returns the value of the instrument response function for an
580   - * extended source. It uses a pre-computation cache to store the IRF for
581   - * the spatial model component.
  578 + * Returns the instrument response to a specified diffuse source.
582 579 *
583   - * The pre-computation cache is initialised if no cache has yet been
584   - * allocated, or if at the beginning of a scan over the events, the model
585   - * parameters have changed. The beginning of a scan is defined by an event
586   - * bin index of 0.
  580 + * The method uses a pre-computation cache to store the instrument response
  581 + * for the spatial model component. The pre-computation cache is initialised
  582 + * if no cache has yet been allocated, or if at the beginning of a scan over
  583 + * the events, the model parameters have changed. The beginning of a scan is
  584 + * defined by an event bin index of 0.
587 585 ***************************************************************************/
588 586 double GCTAResponseCube::irf_diffuse(const GEvent& event,
589 587 const GSource& source,
... ... @@ -652,9 +650,13 @@ double GCTAResponseCube::irf_diffuse(const GEvent&amp; event,
652 650 *
653 651 * @param[in] photon Incident photon.
654 652 * @param[in] obs Observation.
  653 + * @return Spatial integral of point spread function.
655 654 *
656 655 * @exception GException::feature_not_implemented
657 656 * Method not implemented.
  657 + *
  658 + * This method is a dummy method that is required for a class derived from
  659 + * GResponse but that needs not to be implemented for a binned response.
658 660 ***************************************************************************/
659 661 double GCTAResponseCube::npred(const GPhoton& photon,
660 662 const GObservation& obs) const
... ... @@ -669,21 +671,21 @@ double GCTAResponseCube::npred(const GPhoton&amp; photon,
669 671  
670 672  
671 673 /***********************************************************************//**
672   - * @brief Read response
  674 + * @brief Read response information from Xml element
673 675 *
674 676 * @param[in] xml XML element.
675 677 *
676 678 * @exception GException::xml_invalid_parnames
677 679 * Invalid parameter names found in XML element.
678 680 *
679   - * Reads information for a CTA observation from an XML element. The exposure
680   - * and PSF cubes are specified using
  681 + * Reads response information from an Xml element. The Exposure and Psf cubes
  682 + * are specified using
681 683 *
682   - * <observation name="..." id="..." instrument="...">
683   - * ...
684   - * <parameter name="ExposureCube" file="..."/>
685   - * <parameter name="PsfCube" file="..."/>
686   - * </observation>
  684 + * <observation name="..." id="..." instrument="...">
  685 + * ...
  686 + * <parameter name="ExposureCube" file="..."/>
  687 + * <parameter name="PsfCube" file="..."/>
  688 + * </observation>
687 689 *
688 690 ***************************************************************************/
689 691 void GCTAResponseCube::read(const GXmlElement& xml)
... ... @@ -738,18 +740,18 @@ void GCTAResponseCube::read(const GXmlElement&amp; xml)
738 740  
739 741  
740 742 /***********************************************************************//**
741   - * @brief Write response
  743 + * @brief Write response information into Xml element
742 744 *
743 745 * @param[in] xml XML element.
744 746 *
745   - * Writes information for a CTA observation into an XML element. The exposure
746   - * and PSF cubes are specified using
  747 + * Writes response information into an Xml element. The Exposure and Psf
  748 + * cubes are specified using
747 749 *
748   - * <observation name="..." id="..." instrument="...">
749   - * ...
750   - * <parameter name="ExposureCube" file="..."/>
751   - * <parameter name="PsfCube" file="..."/>
752   - * </observation>
  750 + * <observation name="..." id="..." instrument="...">
  751 + * ...
  752 + * <parameter name="ExposureCube" file="..."/>
  753 + * <parameter name="PsfCube" file="..."/>
  754 + * </observation>
753 755 *
754 756 ***************************************************************************/
755 757 void GCTAResponseCube::write(GXmlElement& xml) const
... ... @@ -774,10 +776,10 @@ void GCTAResponseCube::write(GXmlElement&amp; xml) const
774 776  
775 777  
776 778 /***********************************************************************//**
777   - * @brief Print CTA response information
  779 + * @brief Print response information
778 780 *
779 781 * @param[in] chatter Chattiness (defaults to NORMAL).
780   - * @return String containing CTA response information.
  782 + * @return String containing response information.
781 783 ***************************************************************************/
782 784 std::string GCTAResponseCube::print(const GChatter& chatter) const
783 785 {
... ... @@ -819,20 +821,6 @@ std::string GCTAResponseCube::print(const GChatter&amp; chatter) const
819 821  
820 822 /*==========================================================================
821 823 = =
822   - = Model type dependent CTA response methods =
823   - = =
824   - ==========================================================================*/
825   -
826   -
827   -/*==========================================================================
828   - = =
829   - = Low-level CTA response methods =
830   - = =
831   - ==========================================================================*/
832   -
833   -
834   -/*==========================================================================
835   - = =
836 824 = Private methods =
837 825 = =
838 826 ==========================================================================*/
... ... @@ -858,7 +846,7 @@ void GCTAResponseCube::init_members(void)
858 846 /***********************************************************************//**
859 847 * @brief Copy class members
860 848 *
861   - * @param[in] rsp Response to be copied
  849 + * @param[in] rsp Response.
862 850 ***************************************************************************/
863 851 void GCTAResponseCube::copy_members(const GCTAResponseCube& rsp)
864 852 {
... ... @@ -894,10 +882,12 @@ void GCTAResponseCube::free_members(void)
894 882  
895 883  
896 884 /***********************************************************************//**
897   - * @brief Determines the cache index for a given model name
  885 + * @brief Determine cache index for model
898 886 *
899 887 * @param[in] name Model name.
900 888 * @return Cache index (-1 if model has not been found).
  889 + *
  890 + * Determines the cache index for a given model @p name.
901 891 ***************************************************************************/
902 892 int GCTAResponseCube::cache_index(const std::string& name) const
903 893 {
... ... @@ -922,8 +912,114 @@ int GCTAResponseCube::cache_index(const std::string&amp; name) const
922 912 }
923 913  
924 914  
  915 +#if defined(G_RADIAL_PSF_BASED)
925 916 /***********************************************************************//**
926   - * @brief Integrate PSF over radial model
  917 + * @brief Integrate Psf over radial model
  918 + *
  919 + * @param[in] model Radial model.
  920 + * @param[in] delta_mod Angle between model centre and measured photon
  921 + * direction (radians).
  922 + * @param[in] obsDir Observed event direction.
  923 + * @param[in] srcEng True photon energy.
  924 + * @param[in] srcTime True photon arrival time.
  925 + *
  926 + * Integrates the product of the spatial model and the point spread
  927 + * function over the true photon arrival direction using
  928 + *
  929 + * \f[
  930 + * \int_{\delta_{\rm min}}^{\delta_{\rm max}}
  931 + * \sin \delta \times {\rm Psf}(\delta) \times
  932 + * \int_{\phi_{\rm min}}^{\phi_{\rm max}}
  933 + * S_{\rm p}(\delta, \phi | E, t) d\phi d\delta
  934 + * \f]
  935 + *
  936 + * where
  937 + * \f$S_{\rm p}(\delta, \phi | E, t)\f$ is the radial spatial model,
  938 + * \f${\rm Psf}(\delta)\f$ is the point spread function,
  939 + * \f$\delta\f$ is angular distance between the true and the measured
  940 + * photon direction, and
  941 + * \f$\phi\f$ is the position angle around the observed photon direction
  942 + * measured counterclockwise from the connecting line between the model
  943 + * centre and the observed photon arrival direction.
  944 + ***************************************************************************/
  945 +double GCTAResponseCube::psf_radial(const GModelSpatialRadial* model,
  946 + const double& delta_mod,
  947 + const GSkyDir& obsDir,
  948 + const GEnergy& srcEng,
  949 + const GTime& srcTime) const
  950 +{
  951 + // Set number of iterations for Romberg integration.
  952 + // These values have been determined after careful testing, see
  953 + // https://cta-redmine.irap.omp.eu/issues/1291
  954 + static const int iter_delta = 5;
  955 + static const int iter_phi = 6;
  956 +
  957 + // Initialise value
  958 + double value = 0.0;
  959 +
  960 + // Get maximum Psf radius (radians)
  961 + double psf_max = psf().delta_max();
  962 +
  963 + // Get maximum model radius (radians)
  964 + double theta_max = model->theta_max();
  965 +
  966 + // Set offset angle integration range (radians)
  967 + double delta_min = (delta_mod > theta_max) ? delta_mod - theta_max : 0.0;
  968 + double delta_max = delta_mod + theta_max;
  969 + if (delta_max > psf_max) {
  970 + delta_max = psf_max;
  971 + }
  972 +
  973 + // Setup integration kernel. We take here the observed photon arrival
  974 + // direction as the true photon arrival direction because the PSF does
  975 + // not vary significantly over a small region.
  976 + cta_psf_radial_kern_delta integrand(this,
  977 + model,
  978 + obsDir,
  979 + srcEng,
  980 + srcTime,
  981 + delta_mod,
  982 + theta_max,
  983 + iter_phi);
  984 +
  985 + // Integrate over model's zenith angle
  986 + GIntegral integral(&integrand);
  987 + integral.fixed_iter(iter_delta);
  988 +
  989 + // Setup integration boundaries
  990 + std::vector<double> bounds;
  991 + bounds.push_back(delta_min);
  992 + bounds.push_back(delta_max);
  993 +
  994 + // If the integration range includes a transition between full
  995 + // containment of Psf within model and partial containment, then
  996 + // add a boundary at this location
  997 + double transition_point = theta_max - delta_mod;
  998 + if (transition_point > delta_min && transition_point < delta_max) {
  999 + bounds.push_back(transition_point);
  1000 + }
  1001 +
  1002 + // Integrate kernel
  1003 + value = integral.romberg(bounds, iter_delta);
  1004 +
  1005 + // Compile option: Check for NaN/Inf
  1006 + #if defined(G_NAN_CHECK)
  1007 + if (gammalib::is_notanumber(value) || gammalib::is_infinite(value)) {
  1008 + std::cout << "*** ERROR: GCTAResponseCube::psf_radial:";
  1009 + std::cout << " NaN/Inf encountered";
  1010 + std::cout << " (value=" << value;
  1011 + std::cout << ", delta_min=" << delta_min;
  1012 + std::cout << ", delta_max=" << delta_max << ")";
  1013 + std::cout << std::endl;
  1014 + }
  1015 + #endif
  1016 +
  1017 + // Return integral
  1018 + return value;
  1019 +}
  1020 +#else
  1021 +/***********************************************************************//**
  1022 + * @brief Integrate Psf over radial model
927 1023 *
928 1024 * @param[in] model Radial model.
929 1025 * @param[in] rho_obs Angle between model centre and measured photon direction
... ... @@ -932,21 +1028,23 @@ int GCTAResponseCube::cache_index(const std::string&amp; name) const
932 1028 * @param[in] srcEng True photon energy.
933 1029 * @param[in] srcTime True photon arrival time.
934 1030 *
935   - * Computes the integral
  1031 + * Integrates the product of the spatial model and the point spread
  1032 + * function over the true photon arrival direction using
936 1033 *
937 1034 * \f[
938   - * \int_0^{\delta_{\rm max}}
939   - * {\rm PSF}(\delta) \times
940   - * \int_0^{2\pi} {\rm M}(\delta, \phi) \sin \delta
941   - * {\rm d}\phi {\rm d}\delta
  1035 + * \int_{\rho_{\rm min}}^{\rho_{\rm max}}
  1036 + * \sin \rho \times S_{\rm p}(\rho | E, t) \times
  1037 + * \int_{\omega_{\rm min}}^{\omega_{\rm max}}
  1038 + * {\rm Psf}(\rho, \omega) d\omega d\rho
942 1039 * \f]
943 1040 *
944   - * where \f${\rm M}(\delta, \phi)\f$ is a radial model in the coordinate
945   - * system of the point spread function, defined by the angle \f$\delta\f$
946   - * between the true and the measured photon direction and the azimuth angle
947   - * \f$\phi\f$ around the measured photon direction.
948   - * \f${\rm PSF}(\delta)\f$ is the azimuthally symmetric point spread
949   - * function.
  1041 + * where
  1042 + * \f$S_{\rm p}(\rho | E, t)\f$ is the radial spatial model,
  1043 + * \f${\rm Psf}(\rho, \omega)\f$ is the point spread function,
  1044 + * \f$\rho\f$ is the radial distance from the model centre, and
  1045 + * \f$\omega\f$ is the position angle around the model centre measured
  1046 + * counterclockwise from the connecting line between the model centre and
  1047 + * the observed photon arrival direction.
950 1048 ***************************************************************************/
951 1049 double GCTAResponseCube::psf_radial(const GModelSpatialRadial* model,
952 1050 const double& rho_obs,
... ... @@ -1006,14 +1104,6 @@ double GCTAResponseCube::psf_radial(const GModelSpatialRadial* model,
1006 1104 bounds.push_back(transition_point);
1007 1105 }
1008 1106  
1009   - // If the integration range includes a transition between full
1010   - // containment of Psf within model and partial containment, then
1011   - // add a boundary at this location
1012   - transition_point = src_max - delta_max;
1013   - if (transition_point > rho_min && transition_point < rho_max) {
1014   - bounds.push_back(transition_point);
1015   - }
1016   -
1017 1107 // If we have a shell model then add an integration boundary for the
1018 1108 // shell radius as a function discontinuity will occur at this
1019 1109 // location
... ... @@ -1045,10 +1135,11 @@ double GCTAResponseCube::psf_radial(const GModelSpatialRadial* model,
1045 1135 // Return PSF
1046 1136 return irf;
1047 1137 }
  1138 +#endif
1048 1139  
1049 1140  
1050 1141 /***********************************************************************//**
1051   - * @brief Integrate PSF over elliptical model
  1142 + * @brief Integrate Psf over elliptical model
1052 1143 *
1053 1144 * @param[in] model Elliptical model.
1054 1145 * @param[in] rho_obs Angle between model centre and measured photon direction
... ... @@ -1059,36 +1150,24 @@ double GCTAResponseCube::psf_radial(const GModelSpatialRadial* model,
1059 1150 * @param[in] srcEng True photon energy.
1060 1151 * @param[in] srcTime True photon arrival time.
1061 1152 *
1062   - * Computes the integral
  1153 + * Integrates the product of the spatial model and the point spread
  1154 + * function over the true photon arrival direction using
1063 1155 *
1064 1156 * \f[
1065 1157 * \int_{\rho_{\rm min}}^{\rho_{\rm max}}
1066   - * \sin \rho \times
  1158 + * \sin \rho \times
1067 1159 * \int_{\omega}
1068   - * S_{\rm p}(\rho, \omega | E, t) \, PSF(\rho, \omega) d\omega d\rho
  1160 + * S_{\rm p}(\rho, \omega | E, t) \times
  1161 + * {\rm Psf}(\rho, \omega) d\omega d\rho
1069 1162 * \f]
1070 1163 *
1071 1164 * where
1072   - * \f$S_{\rm p}(\rho, \omega | E, t)\f$ is the elliptical model,
1073   - * \f$PSF(\rho, \omega)\f$ is the point spread function,
1074   - * \f$\rho\f$ is the distance from the model centre, and
1075   - * \f$\omega\f$ is the position angle with respect to the connecting line
1076   - * between the model centre and the observed photon arrival direction.
1077   - ***************************************************************************/
1078   -/*
1079   - * \f[
1080   - * \int_0^{\delta_{\rm max}}
1081   - * {\rm PSF}(\delta) \times
1082   - * \int_0^{2\pi} {\rm M}(\delta, \phi) \sin \delta
1083   - * {\rm d}\phi {\rm d}\delta
1084   - * \f]
1085   - *
1086   - * where \f${\rm M}(\delta, \phi)\f$ is an elliptical model in the coordinate
1087   - * system of the point spread function, defined by the angle \f$\delta\f$
1088   - * between the true and the measured photon direction and the azimuth angle
1089   - * \f$\phi\f$ around the measured photon direction.
1090   - * \f${\rm PSF}(\delta)\f$ is the azimuthally symmetric point spread
1091   - * function.
  1165 + * \f$S_{\rm p}(\rho, \omega | E, t)\f$ is the elliptical spatial model,
  1166 + * \f${\rm Psf}(\rho, \omega)\f$ is the point spread function,
  1167 + * \f$\rho\f$ is the radial distance from the model centre, and
  1168 + * \f$\omega\f$ is the position angle around the model centre measured
  1169 + * counterclockwise from the connecting line between the model centre and
  1170 + * the observed photon arrival direction.
1092 1171 ***************************************************************************/
1093 1172 double GCTAResponseCube::psf_elliptical(const GModelSpatialElliptical* model,
1094 1173 const double& rho_obs,
... ...
inst/cta/src/GCTAResponseIrf.cpp
... ... @@ -1268,7 +1268,7 @@ std::string GCTAResponseIrf::print(const GChatter&amp; chatter) const
1268 1268  
1269 1269 /*==========================================================================
1270 1270 = =
1271   - = Model type dependent CTA response methods =
  1271 + = Model type dependent CTA response methods =
1272 1272 = =
1273 1273 ==========================================================================*/
1274 1274  
... ... @@ -1282,24 +1282,22 @@ 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
  1285 + * Integrates the product of the spatial model and the instrument response
  1286 + * function over the true photon arrival direction using
1287 1287 *
1288 1288 * \f[
1289 1289 * \int_{\rho_{\rm min}}^{\rho_{\rm max}}
1290 1290 * \sin \rho \times S_{\rm p}(\rho | E, t) \times
1291 1291 * \int_{\omega_{\rm min}}^{\omega_{\rm max}}
1292   - * Irf(\rho, \omega) d\omega d\rho
  1292 + * {\rm Irf}(\rho, \omega) d\omega d\rho
1293 1293 * \f]
1294 1294 *
1295 1295 * where
1296   - *
1297   - * \f$S_{\rm p}(\rho | E, t)\f$ is the radial model,
1298   - * \f$Irf(\rho, \omega)\f$ is the IRF
1299   - * \f$\rho\f$ is the distance from the model centre, and
1300   - * \f$\omega\f$ is the azimuth angle is the position angle with respect
1301   - * to the connecting line between the model centre and the observed
1302   - * photon arrival direction.
  1296 + * \f$S_{\rm p}(\rho | E, t)\f$ is the radial spatial model,
  1297 + * \f${\rm Irf}(\rho, \omega)\f$ is the instrument response function,
  1298 + * \f$\rho\f$ is the radial distance from the model centre, and
  1299 + * \f$\omega\f$ is the position angle with respect to the connecting line
  1300 + * between the model centre and the observed photon arrival direction.
1303 1301 *
1304 1302 * The integration is performed in the coordinate system of the source
1305 1303 * model spanned by \f$\rho\f$ and \f$\omega\f$ which allows to benefit
... ... @@ -1436,14 +1434,6 @@ double GCTAResponseIrf::irf_radial(const GEvent&amp; event,
1436 1434 bounds.push_back(transition_point);
1437 1435 }
1438 1436  
1439   - // If the integration range includes a transition between full
1440   - // containment of Psf within model and partial containment, then
1441   - // add a boundary at this location
1442   - transition_point = src_max - delta_max;
1443   - if (transition_point > rho_min && transition_point < rho_max) {
1444   - bounds.push_back(transition_point);
1445   - }
1446   -
1447 1437 // If we have a shell model then add an integration boundary for the
1448 1438 // shell radius as a function discontinuity will occur at this
1449 1439 // location
... ... @@ -1498,7 +1488,7 @@ double GCTAResponseIrf::irf_radial(const GEvent&amp; event,
1498 1488  
1499 1489  
1500 1490 /***********************************************************************//**
1501   - * @brief Return IRF value for elliptical source model
  1491 + * @brief Return Irf value for elliptical source model
1502 1492 *
1503 1493 * @param[in] event Observed event.
1504 1494 * @param[in] source Source.
... ...
inst/cta/src/GCTAResponse_helpers.cpp
... ... @@ -692,7 +692,7 @@ double cta_irf_elliptical_kern_rho::eval(const double&amp; rho)
692 692 // If the radius rho is not larger than the semiminor axis
693 693 // boundary, the circle with that radius is fully contained in
694 694 // the ellipse and we can just integrate over the relevant arc
695   - if (rho < m_semiminor) {
  695 + if (rho <= m_semiminor) {
696 696  
697 697 // Compute omega integration range
698 698 double omega_min = -domega;
... ... @@ -984,7 +984,7 @@ double cta_npred_elliptical_kern_rho::eval(const double&amp; rho)
984 984 // If the radius rho is not larger than the semiminor axis
985 985 // boundary, the circle with that radius is fully contained in
986 986 // the ellipse and we can just integrate over the relevant arc
987   - if (rho < m_semiminor) {
  987 + if (rho <= m_semiminor) {
988 988  
989 989 // Compute omega integration range
990 990 double omega_min = -domega;
... ... @@ -1699,6 +1699,165 @@ double cta_psf_radial_kern_omega::eval(const double&amp; omega)
1699 1699  
1700 1700  
1701 1701 /***********************************************************************//**
  1702 + * @brief Kernel for PSF integration of radial model
  1703 + *
  1704 + * @param[in] delta PSF offset angle (radians).
  1705 + * @return Azimuthally integrated product between PSF and radial model.
  1706 + *
  1707 + * Computes the azimuthally integrated product of point spread function and
  1708 + * the radial model intensity. As the PSF is azimuthally symmetric, it is
  1709 + * not included in the azimuthally integration, but just multiplied on the
  1710 + * azimuthally integrated model. The method returns thus
  1711 + *
  1712 + * \f[
  1713 + * {\rm PSF}(\delta) \times
  1714 + * \int_0^{2\pi} {\rm M}(\delta, \phi) \sin \delta {\rm d}\phi
  1715 + * \f]
  1716 + *
  1717 + * where \f${\rm M}(\delta, \phi)\f$ is the radial model in the coordinate
  1718 + * system of the point spread function, defined by the angle \f$\delta\f$
  1719 + * between the true and the measured photon direction and the azimuth angle
  1720 + * \f$\phi\f$ around the measured photon direction.
  1721 + ***************************************************************************/
  1722 +double cta_psf_radial_kern_delta::eval(const double& delta)
  1723 +{
  1724 + // Initialise value
  1725 + double value = 0.0;
  1726 +
  1727 + // If we're at the Psf peak the model is zero (due to the sin(delta)
  1728 + // term. We thus only integrate for positive deltas.
  1729 + if (delta > 0.0) {
  1730 +
  1731 + // Get Psf for this delta
  1732 + double psf = m_rsp->psf()(m_srcDir, delta, m_srcEng);
  1733 +
  1734 + // Continue only if Psf is positive
  1735 + if (psf > 0.0) {
  1736 +
  1737 + // Compute half length of the arc (in radians) from a circle with
  1738 + // radius delta that intersects with the model, defined as a circle
  1739 + // with maximum radius m_theta_max
  1740 + double dphi = 0.5 * gammalib::cta_roi_arclength(delta,
  1741 + m_delta_mod,
  1742 + m_cos_delta_mod,
  1743 + m_sin_delta_mod,
  1744 + m_theta_max,
  1745 + m_cos_theta_max);
  1746 +
  1747 + // Continue only if arc length is positive
  1748 + if (dphi > 0.0) {
  1749 +
  1750 + // Compute phi integration range
  1751 + double phi_min = -dphi;
  1752 + double phi_max = +dphi;
  1753 +
  1754 + // Precompute cosine and sine terms for azimuthal integration
  1755 + double sin_delta = std::sin(delta);
  1756 + double cos_delta = std::cos(delta);
  1757 + double sin_fact = sin_delta * m_sin_delta_mod;
  1758 + double cos_fact = cos_delta * m_cos_delta_mod;
  1759 +
  1760 + // Setup kernel for azimuthal integration of the spatial model
  1761 + cta_psf_radial_kern_phi integrand(m_model,
  1762 + m_srcEng,
  1763 + m_srcTime,
  1764 + sin_fact,
  1765 + cos_fact);
  1766 +
  1767 + // Setup integrator
  1768 + GIntegral integral(&integrand);
  1769 + integral.fixed_iter(m_iter);
  1770 +
  1771 + // Integrate over azimuth
  1772 + value = integral.romberg(phi_min, phi_max, m_iter) *
  1773 + psf * sin_delta;
  1774 +
  1775 + // Debug: Check for NaN
  1776 + #if defined(G_NAN_CHECK)
  1777 + if (gammalib::is_notanumber(value) || gammalib::is_infinite(value)) {
  1778 + std::cout << "*** ERROR: cta_psf_radial_kern_delta::eval";
  1779 + std::cout << "(delta=" << delta << "):";
  1780 + std::cout << " NaN/Inf encountered";
  1781 + std::cout << " (value=" << value;
  1782 + std::cout << ")" << std::endl;
  1783 + }
  1784 + #endif
  1785 +
  1786 + } // endif: Psf value was positive
  1787 +
  1788 + } // endif: arc length was positive
  1789 +
  1790 + } // endif: delta was positive
  1791 +
  1792 + // Return kernel value
  1793 + return value;
  1794 +}
  1795 +
  1796 +
  1797 +/***********************************************************************//**
  1798 + * @brief Kernel for azimuthal radial model integration
  1799 + *
  1800 + * @param[in] phi Azimuth angle (radians).
  1801 + * @return Radial model value.
  1802 + *
  1803 + * Computes the value of the radial model at the position \f$(\delta,\phi)\f$
  1804 + * given in point spread function coordinates. The \f$\theta\f$ angle of the
  1805 + * radial model is computed using
  1806 + *
  1807 + * \f[
  1808 + * \theta = \arccos \left( \cos \delta \cos \zeta +
  1809 + * \sin \delta \sin \zeta \cos \phi \right)
  1810 + * \f]
  1811 + *
  1812 + * where \f$\delta\f$ is the angle between true and measured photon
  1813 + * direction, \f$\zeta\f$ is the angle between model centre and measured
  1814 + * photon direction, and \f$\phi\f$ is the azimuth angle with respect to the
  1815 + * measured photon direction, where \f$\phi=0\f$ corresponds to the
  1816 + * connecting line between model centre and measured photon direction.
  1817 + ***************************************************************************/
  1818 +double cta_psf_radial_kern_phi::eval(const double& phi)
  1819 +{
  1820 + // Compute radial model theta angle
  1821 + double theta = std::acos(m_cos_fact + m_sin_fact * std::cos(phi));
  1822 +
  1823 + // Reduce theta by an infinite amount to avoid rounding errors at the
  1824 + // boundary of a sharp edged model
  1825 + double theta_kluge = theta - 1.0e-12;
  1826 + if (theta_kluge < 0.0) {
  1827 + theta_kluge = 0.0;
  1828 + }
  1829 +
  1830 + // Get radial model value
  1831 + double value = m_model->eval(theta_kluge, m_srcEng, m_srcTime);
  1832 +
  1833 + // Debug: test if model is non positive
  1834 + #if defined(G_DEBUG_MODEL_ZERO)
  1835 + if (value <= 0.0) {
  1836 + std::cout << "*** WARNING: cta_psf_radial_kern_phi::eval";
  1837 + std::cout << " zero model for (phi)=(";
  1838 + std::cout << phi*gammalib::rad2deg << ")";
  1839 + std::cout << " theta-r_model=" << (theta-m_model->theta_max());
  1840 + std::cout << " radians" << std::endl;
  1841 + }
  1842 + #endif
  1843 +
  1844 + // Debug: Check for NaN
  1845 + #if defined(G_NAN_CHECK)
  1846 + if (gammalib::is_notanumber(value) || gammalib::is_infinite(value)) {
  1847 + std::cout << "*** ERROR: cta_psf_radial_kern_phi::eval";
  1848 + std::cout << "(phi=" << phi << "):";
  1849 + std::cout << " NaN/Inf encountered";
  1850 + std::cout << " (value=" << value;
  1851 + std::cout << ")" << std::endl;
  1852 + }
  1853 + #endif
  1854 +
  1855 + // Return kernel value
  1856 + return value;
  1857 +}
  1858 +
  1859 +
  1860 +/***********************************************************************//**
1702 1861 * @brief Kernel for elliptical model integration over zenith angle
1703 1862 *
1704 1863 * @param[in] rho Radial distance from model centre (radians).
... ... @@ -1777,7 +1936,7 @@ double cta_psf_elliptical_kern_rho::eval(const double&amp; rho)
1777 1936 // If the radius rho is not larger than the semiminor axis
1778 1937 // boundary, the circle with that radius is fully contained in
1779 1938 // the ellipse and we can just integrate over the relevant arc
1780   - if (rho < m_semiminor) {
  1939 + if (rho <= m_semiminor) {
1781 1940  
1782 1941 // Compute omega integration range
1783 1942 double omega_min = -domega;
... ...
inst/cta/src/GCTAResponse_helpers.hpp
... ... @@ -1091,6 +1091,75 @@ public:
1091 1091  
1092 1092  
1093 1093 /***********************************************************************//**
  1094 + * @class cta_psf_radial_kern_delta
  1095 + *
  1096 + * @brief Kernel for Psf delta angle integration used for stacked analysis
  1097 + ***************************************************************************/
  1098 +class cta_psf_radial_kern_delta : public GFunction {
  1099 +public:
  1100 + cta_psf_radial_kern_delta(const GCTAResponseCube* rsp,
  1101 + const GModelSpatialRadial* model,
  1102 + const GSkyDir& srcDir,
  1103 + const GEnergy& srcEng,
  1104 + const GTime& srcTime,
  1105 + const double& delta_mod,
  1106 + const double& theta_max,
  1107 + const int& iter) :
  1108 + m_rsp(rsp),
  1109 + m_model(model),
  1110 + m_srcDir(srcDir),
  1111 + m_srcEng(srcEng),
  1112 + m_srcTime(srcTime),
  1113 + m_delta_mod(delta_mod),
  1114 + m_cos_delta_mod(std::cos(delta_mod)),
  1115 + m_sin_delta_mod(std::sin(delta_mod)),
  1116 + m_theta_max(theta_max),
  1117 + m_cos_theta_max(std::cos(theta_max)),
  1118 + m_iter(iter) { }
  1119 + double eval(const double& delta);
  1120 +protected:
  1121 + const GCTAResponseCube* m_rsp; //!< Response cube
  1122 + const GModelSpatialRadial* m_model; //!< Radial model
  1123 + const GSkyDir& m_srcDir; //!< True photon arrival direction
  1124 + const GEnergy& m_srcEng; //!< True photon energy
  1125 + const GTime& m_srcTime; //!< True photon arrival time
  1126 + const double& m_delta_mod; //!< Distance of model from Psf
  1127 + double m_cos_delta_mod; //!< Cosine of m_delta_mod
  1128 + double m_sin_delta_mod; //!< Sine of m_delta_mod
  1129 + const double& m_theta_max; //!< Maximum model radius
  1130 + double m_cos_theta_max; //!< Cosine of m_theta_max
  1131 + const int& m_iter; //!< Integration iterations
  1132 +};
  1133 +
  1134 +
  1135 +/***********************************************************************//**
  1136 + * @class cta_psf_radial_kern_phi
  1137 + *
  1138 + * @brief Kernel for Psf phi angle integration used for stacked analysis
  1139 + ***************************************************************************/
  1140 +class cta_psf_radial_kern_phi : public GFunction {
  1141 +public:
  1142 + cta_psf_radial_kern_phi(const GModelSpatialRadial* model,
  1143 + const GEnergy& srcEng,
  1144 + const GTime& srcTime,
  1145 + const double& sin_fact,
  1146 + const double& cos_fact) :
  1147 + m_model(model),
  1148 + m_srcEng(srcEng),
  1149 + m_srcTime(srcTime),
  1150 + m_sin_fact(sin_fact),
  1151 + m_cos_fact(cos_fact) { }
  1152 + double eval(const double& phi);
  1153 +protected:
  1154 + const GModelSpatialRadial* m_model; //!< Radial model
  1155 + const GEnergy& m_srcEng; //!< True photon energy
  1156 + const GTime& m_srcTime; //!< True photon arrival time
  1157 + const double& m_sin_fact; //!< sin(delta)*sin(delta_mod)
  1158 + const double& m_cos_fact; //!< cos(delta)*cos(delta_mod)
  1159 +};
  1160 +
  1161 +
  1162 +/***********************************************************************//**
1094 1163 * @class cta_irf_elliptical_kern_rho
1095 1164 *
1096 1165 * @brief Kernel for elliptical model zenith angle integration
... ...
inst/cta/test/test_irf_radial.py
... ... @@ -210,18 +210,19 @@ if __name__ == &#39;__main__&#39;:
210 210 sys.exit()
211 211  
212 212 # Set Psf size
213   - delta_max = 0.6
  213 + delta_max = 0.8
214 214  
215 215 # Set model parameters
216 216 centre = gammalib.GSkyDir()
217 217 centre.radec_deg(0.0, 0.0)
218   - radius = 0.8
  218 + radius = 1.0
219 219  
220 220 # Set observed photon direction
221 221 obsDir = gammalib.GSkyDir()
222   - obsDir.radec_deg(0.4, -0.3)
  222 + #obsDir.radec_deg(0.4, -0.3)
223 223 #obsDir.radec_deg(-0.4, -0.3)
224 224 #obsDir.radec_deg(0.4, 0.4)
  225 + obsDir.radec_deg(0.0, 0.3)
225 226  
226 227 # Show elliptical response geometry
227 228 show_response(centre, radius, obsDir, delta_max)
... ...
inst/cta/test/test_likelihood_profile.py
... ... @@ -82,7 +82,7 @@ def show_likelihood_profile(result):
82 82 # ========================== #
83 83 # Compute likelihood profile #
84 84 # ========================== #
85   -def compute_likelihood_profile(obs, parname="RA", scale=1.0e-4, steps=10):
  85 +def compute_likelihood_profile(obs, parname="Radius", scale=1.0e-5, steps=10):
86 86 """
87 87 Compute the log-likelihood profile for a given parameter.
88 88  
... ... @@ -93,12 +93,12 @@ def compute_likelihood_profile(obs, parname=&quot;RA&quot;, scale=1.0e-4, steps=10):
93 93 """
94 94 # Free parameter for which profile is to be determined
95 95 obs.models()[0][parname].free()
96   - obs.models()[0]["DEC"].free()
97 96  
98 97 # Allocate Levenberg-Marquardt optimizer
99 98 log = gammalib.GLog()
100 99 log.cout(True)
101 100 opt = gammalib.GOptimizerLM(log)
  101 + opt.eps(5.0e-3)
102 102  
103 103 # Optimize model parameters
104 104 obs.optimize(opt)
... ... @@ -256,8 +256,8 @@ if __name__ == &#39;__main__&#39;:
256 256  
257 257 # Set test
258 258 #test = "point"
259   - test = "gauss"
260   - #test = "disk"
  259 + #test = "gauss"
  260 + test = "disk"
261 261 #test = "ldisk"
262 262 #test = "shell"
263 263 #test = "ellipse"
... ... @@ -309,10 +309,10 @@ if __name__ == &#39;__main__&#39;:
309 309 cntref = 337.5 # +/- 1.8 (from ctobssim)
310 310  
311 311 # Perform unbinned computation
312   - original_likelihood_profile(model, events, irf, gammalib.GCaldb(caldb), cntref)
  312 + #original_likelihood_profile(model, events, irf, gammalib.GCaldb(caldb), cntref)
313 313  
314 314 # Perform binned computation
315 315 #original_likelihood_profile(model, cntmap, irf, gammalib.GCaldb(caldb), cntref)
316 316  
317 317 # Perform stacked computation
318   - #stacked_likelihood_profile(model, cntmap, expcube, psfcube, cntref)
  318 + stacked_likelihood_profile(model, cntmap, expcube, psfcube, cntref)
... ...
inst/cta/test/test_npred_elliptical.py
... ... @@ -279,7 +279,7 @@ def show_response(centre, semimajor, semiminor, posang, roiDir, pntDir, roi_radi
279 279 if (domega > 0.0):
280 280  
281 281 # Full containment within ellipse
282   - if (rho < bound_minor):
  282 + if (rho <= bound_minor):
283 283  
284 284 # Compute phi integration range
285 285 omega_min = -domega
... ... @@ -400,17 +400,16 @@ if __name__ == &#39;__main__&#39;:
400 400 # Set model parameters
401 401 centre = gammalib.GSkyDir()
402 402 centre.radec_deg(0.0, 0.0)
403   - #semimajor = 1.0
404   - #semiminor = 0.50
405   - semiminor = 1.0
406   - semimajor = 0.50
407   - posang = 310.0
  403 + semimajor = 1.0
  404 + semiminor = 0.50
  405 + posang = 30.0
408 406  
409 407 # Set ROI direction
410 408 roiDir = gammalib.GSkyDir()
411   - #roiDir.radec_deg(-0.4, -0.3)
412   - roiDir.radec_deg(+0.4, -0.3)
  409 + roiDir.radec_deg(-0.4, -0.3)
  410 + #roiDir.radec_deg(+0.4, -0.3)
413 411 #roiDir.radec_deg(+0.4, +0.3)
  412 + #roiDir.radec_deg(0.0, 0.1)
414 413  
415 414 # Set pointing direction
416 415 pntDir = gammalib.GSkyDir()
... ...