Commit f522be3ac0b17b470e5a56a3e9875ce320c54fbb
1 parent
ab4461b6
Finalize response optimization.
Showing
7 changed files
with
440 additions
and
143 deletions
inst/cta/src/GCTAResponseCube.cpp
| @@ -64,6 +64,7 @@ | @@ -64,6 +64,7 @@ | ||
| 64 | /* __ Macros _____________________________________________________________ */ | 64 | /* __ Macros _____________________________________________________________ */ |
| 65 | 65 | ||
| 66 | /* __ Coding definitions _________________________________________________ */ | 66 | /* __ Coding definitions _________________________________________________ */ |
| 67 | +#define G_RADIAL_PSF_BASED //!< Use Psf-based integration for radial model | ||
| 67 | 68 | ||
| 68 | /* __ Debug definitions __________________________________________________ */ | 69 | /* __ Debug definitions __________________________________________________ */ |
| 69 | 70 | ||
| @@ -237,11 +238,12 @@ GCTAResponseCube* GCTAResponseCube::clone(void) const | @@ -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 | * @param[in] event Observed event. | 243 | * @param[in] event Observed event. |
| 243 | * @param[in] photon Incident photon. | 244 | * @param[in] photon Incident photon. |
| 244 | * @param[in] obs Observation. | 245 | * @param[in] obs Observation. |
| 246 | + * @return Instrument response. | ||
| 245 | ***************************************************************************/ | 247 | ***************************************************************************/ |
| 246 | double GCTAResponseCube::irf(const GEvent& event, | 248 | double GCTAResponseCube::irf(const GEvent& event, |
| 247 | const GPhoton& photon, | 249 | const GPhoton& photon, |
| @@ -308,14 +310,14 @@ double GCTAResponseCube::irf(const GEvent& event, | @@ -308,14 +310,14 @@ double GCTAResponseCube::irf(const GEvent& event, | ||
| 308 | 310 | ||
| 309 | 311 | ||
| 310 | /***********************************************************************//** | 312 | /***********************************************************************//** |
| 311 | - * @brief Return value of instrument response function | 313 | + * @brief Return instrument response |
| 312 | * | 314 | * |
| 313 | * @param[in] event Event. | 315 | * @param[in] event Event. |
| 314 | * @param[in] source Source. | 316 | * @param[in] source Source. |
| 315 | * @param[in] obs Observation. | 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 | double GCTAResponseCube::irf(const GEvent& event, | 322 | double GCTAResponseCube::irf(const GEvent& event, |
| 321 | const GSource& source, | 323 | const GSource& source, |
| @@ -348,15 +350,14 @@ double GCTAResponseCube::irf(const GEvent& event, | @@ -348,15 +350,14 @@ double GCTAResponseCube::irf(const GEvent& 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 | * @param[in] event Observed event. | 355 | * @param[in] event Observed event. |
| 354 | * @param[in] source Source. | 356 | * @param[in] source Source. |
| 355 | * @param[in] obs Observation. | 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 | double GCTAResponseCube::irf_ptsrc(const GEvent& event, | 362 | double GCTAResponseCube::irf_ptsrc(const GEvent& event, |
| 362 | const GSource& source, | 363 | const GSource& source, |
| @@ -426,15 +427,14 @@ double GCTAResponseCube::irf_ptsrc(const GEvent& event, | @@ -426,15 +427,14 @@ double GCTAResponseCube::irf_ptsrc(const GEvent& 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 | * @param[in] event Observed event. | 432 | * @param[in] event Observed event. |
| 432 | * @param[in] source Source. | 433 | * @param[in] source Source. |
| 433 | * @param[in] obs Observation. | 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 | double GCTAResponseCube::irf_radial(const GEvent& event, | 439 | double GCTAResponseCube::irf_radial(const GEvent& event, |
| 440 | const GSource& source, | 440 | const GSource& source, |
| @@ -497,15 +497,14 @@ double GCTAResponseCube::irf_radial(const GEvent& event, | @@ -497,15 +497,14 @@ double GCTAResponseCube::irf_radial(const GEvent& 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 | * @param[in] event Observed event. | 502 | * @param[in] event Observed event. |
| 503 | * @param[in] source Source. | 503 | * @param[in] source Source. |
| 504 | * @param[in] obs Observation. | 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 | double GCTAResponseCube::irf_elliptical(const GEvent& event, | 509 | double GCTAResponseCube::irf_elliptical(const GEvent& event, |
| 511 | const GSource& source, | 510 | const GSource& source, |
| @@ -569,21 +568,20 @@ double GCTAResponseCube::irf_elliptical(const GEvent& event, | @@ -569,21 +568,20 @@ double GCTAResponseCube::irf_elliptical(const GEvent& 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 | * @param[in] event Observed event. | 573 | * @param[in] event Observed event. |
| 575 | * @param[in] source Source. | 574 | * @param[in] source Source. |
| 576 | * @param[in] obs Observation. | 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 | double GCTAResponseCube::irf_diffuse(const GEvent& event, | 586 | double GCTAResponseCube::irf_diffuse(const GEvent& event, |
| 589 | const GSource& source, | 587 | const GSource& source, |
| @@ -652,9 +650,13 @@ double GCTAResponseCube::irf_diffuse(const GEvent& event, | @@ -652,9 +650,13 @@ double GCTAResponseCube::irf_diffuse(const GEvent& event, | ||
| 652 | * | 650 | * |
| 653 | * @param[in] photon Incident photon. | 651 | * @param[in] photon Incident photon. |
| 654 | * @param[in] obs Observation. | 652 | * @param[in] obs Observation. |
| 653 | + * @return Spatial integral of point spread function. | ||
| 655 | * | 654 | * |
| 656 | * @exception GException::feature_not_implemented | 655 | * @exception GException::feature_not_implemented |
| 657 | * Method not implemented. | 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 | double GCTAResponseCube::npred(const GPhoton& photon, | 661 | double GCTAResponseCube::npred(const GPhoton& photon, |
| 660 | const GObservation& obs) const | 662 | const GObservation& obs) const |
| @@ -669,21 +671,21 @@ double GCTAResponseCube::npred(const GPhoton& photon, | @@ -669,21 +671,21 @@ double GCTAResponseCube::npred(const GPhoton& photon, | ||
| 669 | 671 | ||
| 670 | 672 | ||
| 671 | /***********************************************************************//** | 673 | /***********************************************************************//** |
| 672 | - * @brief Read response | 674 | + * @brief Read response information from Xml element |
| 673 | * | 675 | * |
| 674 | * @param[in] xml XML element. | 676 | * @param[in] xml XML element. |
| 675 | * | 677 | * |
| 676 | * @exception GException::xml_invalid_parnames | 678 | * @exception GException::xml_invalid_parnames |
| 677 | * Invalid parameter names found in XML element. | 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 | void GCTAResponseCube::read(const GXmlElement& xml) | 691 | void GCTAResponseCube::read(const GXmlElement& xml) |
| @@ -738,18 +740,18 @@ void GCTAResponseCube::read(const GXmlElement& xml) | @@ -738,18 +740,18 @@ void GCTAResponseCube::read(const GXmlElement& xml) | ||
| 738 | 740 | ||
| 739 | 741 | ||
| 740 | /***********************************************************************//** | 742 | /***********************************************************************//** |
| 741 | - * @brief Write response | 743 | + * @brief Write response information into Xml element |
| 742 | * | 744 | * |
| 743 | * @param[in] xml XML element. | 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 | void GCTAResponseCube::write(GXmlElement& xml) const | 757 | void GCTAResponseCube::write(GXmlElement& xml) const |
| @@ -774,10 +776,10 @@ void GCTAResponseCube::write(GXmlElement& xml) const | @@ -774,10 +776,10 @@ void GCTAResponseCube::write(GXmlElement& xml) const | ||
| 774 | 776 | ||
| 775 | 777 | ||
| 776 | /***********************************************************************//** | 778 | /***********************************************************************//** |
| 777 | - * @brief Print CTA response information | 779 | + * @brief Print response information |
| 778 | * | 780 | * |
| 779 | * @param[in] chatter Chattiness (defaults to NORMAL). | 781 | * @param[in] chatter Chattiness (defaults to NORMAL). |
| 780 | - * @return String containing CTA response information. | 782 | + * @return String containing response information. |
| 781 | ***************************************************************************/ | 783 | ***************************************************************************/ |
| 782 | std::string GCTAResponseCube::print(const GChatter& chatter) const | 784 | std::string GCTAResponseCube::print(const GChatter& chatter) const |
| 783 | { | 785 | { |
| @@ -819,20 +821,6 @@ std::string GCTAResponseCube::print(const GChatter& chatter) const | @@ -819,20 +821,6 @@ std::string GCTAResponseCube::print(const GChatter& 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 | = Private methods = | 824 | = Private methods = |
| 837 | = = | 825 | = = |
| 838 | ==========================================================================*/ | 826 | ==========================================================================*/ |
| @@ -858,7 +846,7 @@ void GCTAResponseCube::init_members(void) | @@ -858,7 +846,7 @@ void GCTAResponseCube::init_members(void) | ||
| 858 | /***********************************************************************//** | 846 | /***********************************************************************//** |
| 859 | * @brief Copy class members | 847 | * @brief Copy class members |
| 860 | * | 848 | * |
| 861 | - * @param[in] rsp Response to be copied | 849 | + * @param[in] rsp Response. |
| 862 | ***************************************************************************/ | 850 | ***************************************************************************/ |
| 863 | void GCTAResponseCube::copy_members(const GCTAResponseCube& rsp) | 851 | void GCTAResponseCube::copy_members(const GCTAResponseCube& rsp) |
| 864 | { | 852 | { |
| @@ -894,10 +882,12 @@ void GCTAResponseCube::free_members(void) | @@ -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 | * @param[in] name Model name. | 887 | * @param[in] name Model name. |
| 900 | * @return Cache index (-1 if model has not been found). | 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 | int GCTAResponseCube::cache_index(const std::string& name) const | 892 | int GCTAResponseCube::cache_index(const std::string& name) const |
| 903 | { | 893 | { |
| @@ -922,8 +912,114 @@ int GCTAResponseCube::cache_index(const std::string& name) const | @@ -922,8 +912,114 @@ int GCTAResponseCube::cache_index(const std::string& 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 | * @param[in] model Radial model. | 1024 | * @param[in] model Radial model. |
| 929 | * @param[in] rho_obs Angle between model centre and measured photon direction | 1025 | * @param[in] rho_obs Angle between model centre and measured photon direction |
| @@ -932,21 +1028,23 @@ int GCTAResponseCube::cache_index(const std::string& name) const | @@ -932,21 +1028,23 @@ int GCTAResponseCube::cache_index(const std::string& name) const | ||
| 932 | * @param[in] srcEng True photon energy. | 1028 | * @param[in] srcEng True photon energy. |
| 933 | * @param[in] srcTime True photon arrival time. | 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 | * \f[ | 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 | * \f] | 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 | double GCTAResponseCube::psf_radial(const GModelSpatialRadial* model, | 1049 | double GCTAResponseCube::psf_radial(const GModelSpatialRadial* model, |
| 952 | const double& rho_obs, | 1050 | const double& rho_obs, |
| @@ -1006,14 +1104,6 @@ double GCTAResponseCube::psf_radial(const GModelSpatialRadial* model, | @@ -1006,14 +1104,6 @@ double GCTAResponseCube::psf_radial(const GModelSpatialRadial* model, | ||
| 1006 | bounds.push_back(transition_point); | 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 | // If we have a shell model then add an integration boundary for the | 1107 | // If we have a shell model then add an integration boundary for the |
| 1018 | // shell radius as a function discontinuity will occur at this | 1108 | // shell radius as a function discontinuity will occur at this |
| 1019 | // location | 1109 | // location |
| @@ -1045,10 +1135,11 @@ double GCTAResponseCube::psf_radial(const GModelSpatialRadial* model, | @@ -1045,10 +1135,11 @@ double GCTAResponseCube::psf_radial(const GModelSpatialRadial* model, | ||
| 1045 | // Return PSF | 1135 | // Return PSF |
| 1046 | return irf; | 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 | * @param[in] model Elliptical model. | 1144 | * @param[in] model Elliptical model. |
| 1054 | * @param[in] rho_obs Angle between model centre and measured photon direction | 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,36 +1150,24 @@ double GCTAResponseCube::psf_radial(const GModelSpatialRadial* model, | ||
| 1059 | * @param[in] srcEng True photon energy. | 1150 | * @param[in] srcEng True photon energy. |
| 1060 | * @param[in] srcTime True photon arrival time. | 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 | * \f[ | 1156 | * \f[ |
| 1065 | * \int_{\rho_{\rm min}}^{\rho_{\rm max}} | 1157 | * \int_{\rho_{\rm min}}^{\rho_{\rm max}} |
| 1066 | - * \sin \rho \times | 1158 | + * \sin \rho \times |
| 1067 | * \int_{\omega} | 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 | * \f] | 1162 | * \f] |
| 1070 | * | 1163 | * |
| 1071 | * where | 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 | double GCTAResponseCube::psf_elliptical(const GModelSpatialElliptical* model, | 1172 | double GCTAResponseCube::psf_elliptical(const GModelSpatialElliptical* model, |
| 1094 | const double& rho_obs, | 1173 | const double& rho_obs, |
inst/cta/src/GCTAResponseIrf.cpp
| @@ -1268,7 +1268,7 @@ std::string GCTAResponseIrf::print(const GChatter& chatter) const | @@ -1268,7 +1268,7 @@ std::string GCTAResponseIrf::print(const GChatter& 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& chatter) const | @@ -1282,24 +1282,22 @@ std::string GCTAResponseIrf::print(const GChatter& chatter) const | ||
| 1282 | * @exception GCTAException::bad_model_type | 1282 | * @exception GCTAException::bad_model_type |
| 1283 | * Model is not a radial model. | 1283 | * Model is not a radial model. |
| 1284 | * | 1284 | * |
| 1285 | - * Integrates the product of the model and the Irf over the true photon | ||
| 1286 | - * arrival direction using | 1285 | + * Integrates the product of the spatial model and the instrument response |
| 1286 | + * function over the true photon arrival direction using | ||
| 1287 | * | 1287 | * |
| 1288 | * \f[ | 1288 | * \f[ |
| 1289 | * \int_{\rho_{\rm min}}^{\rho_{\rm max}} | 1289 | * \int_{\rho_{\rm min}}^{\rho_{\rm max}} |
| 1290 | * \sin \rho \times S_{\rm p}(\rho | E, t) \times | 1290 | * \sin \rho \times S_{\rm p}(\rho | E, t) \times |
| 1291 | * \int_{\omega_{\rm min}}^{\omega_{\rm max}} | 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 | * \f] | 1293 | * \f] |
| 1294 | * | 1294 | * |
| 1295 | * where | 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 | * The integration is performed in the coordinate system of the source | 1302 | * The integration is performed in the coordinate system of the source |
| 1305 | * model spanned by \f$\rho\f$ and \f$\omega\f$ which allows to benefit | 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& event, | @@ -1436,14 +1434,6 @@ double GCTAResponseIrf::irf_radial(const GEvent& event, | ||
| 1436 | bounds.push_back(transition_point); | 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 | // If we have a shell model then add an integration boundary for the | 1437 | // If we have a shell model then add an integration boundary for the |
| 1448 | // shell radius as a function discontinuity will occur at this | 1438 | // shell radius as a function discontinuity will occur at this |
| 1449 | // location | 1439 | // location |
| @@ -1498,7 +1488,7 @@ double GCTAResponseIrf::irf_radial(const GEvent& event, | @@ -1498,7 +1488,7 @@ double GCTAResponseIrf::irf_radial(const GEvent& 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 | * @param[in] event Observed event. | 1493 | * @param[in] event Observed event. |
| 1504 | * @param[in] source Source. | 1494 | * @param[in] source Source. |
inst/cta/src/GCTAResponse_helpers.cpp
| @@ -692,7 +692,7 @@ double cta_irf_elliptical_kern_rho::eval(const double& rho) | @@ -692,7 +692,7 @@ double cta_irf_elliptical_kern_rho::eval(const double& rho) | ||
| 692 | // If the radius rho is not larger than the semiminor axis | 692 | // If the radius rho is not larger than the semiminor axis |
| 693 | // boundary, the circle with that radius is fully contained in | 693 | // boundary, the circle with that radius is fully contained in |
| 694 | // the ellipse and we can just integrate over the relevant arc | 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 | // Compute omega integration range | 697 | // Compute omega integration range |
| 698 | double omega_min = -domega; | 698 | double omega_min = -domega; |
| @@ -984,7 +984,7 @@ double cta_npred_elliptical_kern_rho::eval(const double& rho) | @@ -984,7 +984,7 @@ double cta_npred_elliptical_kern_rho::eval(const double& rho) | ||
| 984 | // If the radius rho is not larger than the semiminor axis | 984 | // If the radius rho is not larger than the semiminor axis |
| 985 | // boundary, the circle with that radius is fully contained in | 985 | // boundary, the circle with that radius is fully contained in |
| 986 | // the ellipse and we can just integrate over the relevant arc | 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 | // Compute omega integration range | 989 | // Compute omega integration range |
| 990 | double omega_min = -domega; | 990 | double omega_min = -domega; |
| @@ -1699,6 +1699,165 @@ double cta_psf_radial_kern_omega::eval(const double& omega) | @@ -1699,6 +1699,165 @@ double cta_psf_radial_kern_omega::eval(const double& 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 | * @brief Kernel for elliptical model integration over zenith angle | 1861 | * @brief Kernel for elliptical model integration over zenith angle |
| 1703 | * | 1862 | * |
| 1704 | * @param[in] rho Radial distance from model centre (radians). | 1863 | * @param[in] rho Radial distance from model centre (radians). |
| @@ -1777,7 +1936,7 @@ double cta_psf_elliptical_kern_rho::eval(const double& rho) | @@ -1777,7 +1936,7 @@ double cta_psf_elliptical_kern_rho::eval(const double& rho) | ||
| 1777 | // If the radius rho is not larger than the semiminor axis | 1936 | // If the radius rho is not larger than the semiminor axis |
| 1778 | // boundary, the circle with that radius is fully contained in | 1937 | // boundary, the circle with that radius is fully contained in |
| 1779 | // the ellipse and we can just integrate over the relevant arc | 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 | // Compute omega integration range | 1941 | // Compute omega integration range |
| 1783 | double omega_min = -domega; | 1942 | double omega_min = -domega; |
inst/cta/src/GCTAResponse_helpers.hpp
| @@ -1091,6 +1091,75 @@ public: | @@ -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 | * @class cta_irf_elliptical_kern_rho | 1163 | * @class cta_irf_elliptical_kern_rho |
| 1095 | * | 1164 | * |
| 1096 | * @brief Kernel for elliptical model zenith angle integration | 1165 | * @brief Kernel for elliptical model zenith angle integration |
inst/cta/test/test_irf_radial.py
| @@ -210,18 +210,19 @@ if __name__ == '__main__': | @@ -210,18 +210,19 @@ if __name__ == '__main__': | ||
| 210 | sys.exit() | 210 | sys.exit() |
| 211 | 211 | ||
| 212 | # Set Psf size | 212 | # Set Psf size |
| 213 | - delta_max = 0.6 | 213 | + delta_max = 0.8 |
| 214 | 214 | ||
| 215 | # Set model parameters | 215 | # Set model parameters |
| 216 | centre = gammalib.GSkyDir() | 216 | centre = gammalib.GSkyDir() |
| 217 | centre.radec_deg(0.0, 0.0) | 217 | centre.radec_deg(0.0, 0.0) |
| 218 | - radius = 0.8 | 218 | + radius = 1.0 |
| 219 | 219 | ||
| 220 | # Set observed photon direction | 220 | # Set observed photon direction |
| 221 | obsDir = gammalib.GSkyDir() | 221 | obsDir = gammalib.GSkyDir() |
| 222 | - obsDir.radec_deg(0.4, -0.3) | 222 | + #obsDir.radec_deg(0.4, -0.3) |
| 223 | #obsDir.radec_deg(-0.4, -0.3) | 223 | #obsDir.radec_deg(-0.4, -0.3) |
| 224 | #obsDir.radec_deg(0.4, 0.4) | 224 | #obsDir.radec_deg(0.4, 0.4) |
| 225 | + obsDir.radec_deg(0.0, 0.3) | ||
| 225 | 226 | ||
| 226 | # Show elliptical response geometry | 227 | # Show elliptical response geometry |
| 227 | show_response(centre, radius, obsDir, delta_max) | 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,7 +82,7 @@ def show_likelihood_profile(result): | ||
| 82 | # ========================== # | 82 | # ========================== # |
| 83 | # Compute likelihood profile # | 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 | Compute the log-likelihood profile for a given parameter. | 87 | Compute the log-likelihood profile for a given parameter. |
| 88 | 88 | ||
| @@ -93,12 +93,12 @@ def compute_likelihood_profile(obs, parname="RA", scale=1.0e-4, steps=10): | @@ -93,12 +93,12 @@ def compute_likelihood_profile(obs, parname="RA", scale=1.0e-4, steps=10): | ||
| 93 | """ | 93 | """ |
| 94 | # Free parameter for which profile is to be determined | 94 | # Free parameter for which profile is to be determined |
| 95 | obs.models()[0][parname].free() | 95 | obs.models()[0][parname].free() |
| 96 | - obs.models()[0]["DEC"].free() | ||
| 97 | 96 | ||
| 98 | # Allocate Levenberg-Marquardt optimizer | 97 | # Allocate Levenberg-Marquardt optimizer |
| 99 | log = gammalib.GLog() | 98 | log = gammalib.GLog() |
| 100 | log.cout(True) | 99 | log.cout(True) |
| 101 | opt = gammalib.GOptimizerLM(log) | 100 | opt = gammalib.GOptimizerLM(log) |
| 101 | + opt.eps(5.0e-3) | ||
| 102 | 102 | ||
| 103 | # Optimize model parameters | 103 | # Optimize model parameters |
| 104 | obs.optimize(opt) | 104 | obs.optimize(opt) |
| @@ -256,8 +256,8 @@ if __name__ == '__main__': | @@ -256,8 +256,8 @@ if __name__ == '__main__': | ||
| 256 | 256 | ||
| 257 | # Set test | 257 | # Set test |
| 258 | #test = "point" | 258 | #test = "point" |
| 259 | - test = "gauss" | ||
| 260 | - #test = "disk" | 259 | + #test = "gauss" |
| 260 | + test = "disk" | ||
| 261 | #test = "ldisk" | 261 | #test = "ldisk" |
| 262 | #test = "shell" | 262 | #test = "shell" |
| 263 | #test = "ellipse" | 263 | #test = "ellipse" |
| @@ -309,10 +309,10 @@ if __name__ == '__main__': | @@ -309,10 +309,10 @@ if __name__ == '__main__': | ||
| 309 | cntref = 337.5 # +/- 1.8 (from ctobssim) | 309 | cntref = 337.5 # +/- 1.8 (from ctobssim) |
| 310 | 310 | ||
| 311 | # Perform unbinned computation | 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 | # Perform binned computation | 314 | # Perform binned computation |
| 315 | #original_likelihood_profile(model, cntmap, irf, gammalib.GCaldb(caldb), cntref) | 315 | #original_likelihood_profile(model, cntmap, irf, gammalib.GCaldb(caldb), cntref) |
| 316 | 316 | ||
| 317 | # Perform stacked computation | 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,7 +279,7 @@ def show_response(centre, semimajor, semiminor, posang, roiDir, pntDir, roi_radi | ||
| 279 | if (domega > 0.0): | 279 | if (domega > 0.0): |
| 280 | 280 | ||
| 281 | # Full containment within ellipse | 281 | # Full containment within ellipse |
| 282 | - if (rho < bound_minor): | 282 | + if (rho <= bound_minor): |
| 283 | 283 | ||
| 284 | # Compute phi integration range | 284 | # Compute phi integration range |
| 285 | omega_min = -domega | 285 | omega_min = -domega |
| @@ -400,17 +400,16 @@ if __name__ == '__main__': | @@ -400,17 +400,16 @@ if __name__ == '__main__': | ||
| 400 | # Set model parameters | 400 | # Set model parameters |
| 401 | centre = gammalib.GSkyDir() | 401 | centre = gammalib.GSkyDir() |
| 402 | centre.radec_deg(0.0, 0.0) | 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 | # Set ROI direction | 407 | # Set ROI direction |
| 410 | roiDir = gammalib.GSkyDir() | 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 | #roiDir.radec_deg(+0.4, +0.3) | 411 | #roiDir.radec_deg(+0.4, +0.3) |
| 412 | + #roiDir.radec_deg(0.0, 0.1) | ||
| 414 | 413 | ||
| 415 | # Set pointing direction | 414 | # Set pointing direction |
| 416 | pntDir = gammalib.GSkyDir() | 415 | pntDir = gammalib.GSkyDir() |