Commit 89b294ab64795e6f8bef3a3ef480109b940183c9
1 parent
1ee6318e
Improve elliptical Irf and Npred computation.
The binned analysis looks okay, but unbinned shows a strange behaviour for the semiminor axis parameter which results in a strong dependency of Npred on the semiminor axis value. This seems to prevent a nice convergence in unbinned mode. There is also an open issue to be solved related to the possibility that the minor axis is larger than the major axis. The actual code assumes that minor axis < major axis. If this is not fulfilled, the omega arc computations will be wrong.
Showing
7 changed files
with
172 additions
and
57 deletions
include/GModelSpatialEllipticalDisk.hpp
| 1 | 1 | /*************************************************************************** |
| 2 | 2 | * GModelSpatialEllipticalDisk.hpp - Elliptical disk source model class * |
| 3 | 3 | * ----------------------------------------------------------------------- * |
| 4 | - * copyright (C) 2013 by Michael Mayer * | |
| 4 | + * copyright (C) 2013-2014 by Michael Mayer * | |
| 5 | 5 | * ----------------------------------------------------------------------- * |
| 6 | 6 | * * |
| 7 | 7 | * This program is free software: you can redistribute it and/or modify * | ... | ... |
inst/cta/src/GCTAResponseIrf.cpp
| ... | ... | @@ -1755,10 +1755,21 @@ double GCTAResponseIrf::irf_elliptical(const GEvent& event, |
| 1755 | 1755 | double delta_max = psf_delta_max(theta, phi, zenith, azimuth, srcLogEng); |
| 1756 | 1756 | |
| 1757 | 1757 | // Get semimajor and semiminor ellipse boundaries [radians] |
| 1758 | - double aspect_ratio = (model->semiminor() > 0) ? | |
| 1759 | - model->semimajor() / model->semiminor() : 0.0; | |
| 1758 | + /* | |
| 1759 | + double aspect_ratio = (model->semimajor() > 0) ? | |
| 1760 | + model->semiminor() / model->semimajor() : 0.0; | |
| 1760 | 1761 | double semimajor = model->theta_max(); |
| 1761 | 1762 | double semiminor = semimajor * aspect_ratio; |
| 1763 | + */ | |
| 1764 | + double semimajor = model->semimajor() * gammalib::deg2rad; | |
| 1765 | + double semiminor = model->semiminor() * gammalib::deg2rad; | |
| 1766 | + | |
| 1767 | + //TEST TEST TEST | |
| 1768 | + if (semimajor < semiminor) { | |
| 1769 | + std::cout << "Warning:"; | |
| 1770 | + std::cout << " semiminor=" << semiminor * gammalib::rad2deg; | |
| 1771 | + std::cout << " semimajor=" << semimajor * gammalib::rad2deg; | |
| 1772 | + } | |
| 1762 | 1773 | |
| 1763 | 1774 | // Set zenith angle integration range for elliptical model |
| 1764 | 1775 | double rho_min = (rho_obs > delta_max) ? rho_obs - delta_max : 0.0; |
| ... | ... | @@ -2326,10 +2337,21 @@ double GCTAResponseIrf::npred_elliptical(const GSource& source, |
| 2326 | 2337 | double rho_roi = roi.centre().dir().dist(centre); |
| 2327 | 2338 | |
| 2328 | 2339 | // Get semimajor and semiminor ellipse boundaries [radians] |
| 2329 | - double aspect_ratio = (model->semiminor() > 0) ? | |
| 2330 | - model->semimajor() / model->semiminor() : 0.0; | |
| 2340 | + /* | |
| 2341 | + double aspect_ratio = (model->semimajor() > 0) ? | |
| 2342 | + model->semiminor() / model->semimajor() : 0.0; | |
| 2331 | 2343 | double semimajor = model->theta_max(); |
| 2332 | 2344 | double semiminor = semimajor * aspect_ratio; |
| 2345 | + */ | |
| 2346 | + double semimajor = model->semimajor() * gammalib::deg2rad; | |
| 2347 | + double semiminor = model->semiminor() * gammalib::deg2rad; | |
| 2348 | + | |
| 2349 | + //TEST TEST TEST | |
| 2350 | + if (semimajor < semiminor) { | |
| 2351 | + std::cout << "Warning:"; | |
| 2352 | + std::cout << " semiminor=" << semiminor * gammalib::rad2deg; | |
| 2353 | + std::cout << " semimajor=" << semimajor * gammalib::rad2deg; | |
| 2354 | + } | |
| 2333 | 2355 | |
| 2334 | 2356 | // Set offset angle integration range. We take here the ROI+PSF into |
| 2335 | 2357 | // account to make no integrations beyond the point where the |
| ... | ... | @@ -2361,8 +2383,8 @@ double GCTAResponseIrf::npred_elliptical(const GSource& source, |
| 2361 | 2383 | *model, |
| 2362 | 2384 | semimajor, |
| 2363 | 2385 | semiminor, |
| 2364 | - source.energy(), | |
| 2365 | - source.time(), | |
| 2386 | + srcEng, | |
| 2387 | + srcTime, | |
| 2366 | 2388 | cta, |
| 2367 | 2389 | rot, |
| 2368 | 2390 | rho_roi, | ... | ... |
inst/cta/src/GCTAResponse_helpers.cpp
| ... | ... | @@ -46,6 +46,7 @@ |
| 46 | 46 | |
| 47 | 47 | /* __ Debug definitions __________________________________________________ */ |
| 48 | 48 | //#define G_DEBUG_INTEGRAL //!< Debug integration |
| 49 | +#define G_DEBUG_MODEL_ZERO //!< Debug check for zero model values | |
| 49 | 50 | |
| 50 | 51 | /* __ Constants __________________________________________________________ */ |
| 51 | 52 | |
| ... | ... | @@ -623,6 +624,14 @@ double cta_irf_elliptical_kern_rho::eval(const double& rho) |
| 623 | 624 | double cos_ph = cos_rho * m_cos_rho_pnt; |
| 624 | 625 | double sin_ph = sin_rho * m_sin_rho_pnt; |
| 625 | 626 | |
| 627 | + // Reduce rho by an infinite amount to avoid rounding errors | |
| 628 | + // at the boundary of a sharp edged model (e.g. an elliptical | |
| 629 | + // disk model) | |
| 630 | + double rho_kluge = rho - 1.0e-12; | |
| 631 | + if (rho_kluge < 0.0) { | |
| 632 | + rho_kluge = 0.0; | |
| 633 | + } | |
| 634 | + | |
| 626 | 635 | // Setup integration kernel |
| 627 | 636 | cta_irf_elliptical_kern_omega integrand(m_rsp, |
| 628 | 637 | m_model, |
| ... | ... | @@ -634,7 +643,7 @@ double cta_irf_elliptical_kern_rho::eval(const double& rho) |
| 634 | 643 | m_obsEng, |
| 635 | 644 | m_posangle_obs, |
| 636 | 645 | m_omega_pnt, |
| 637 | - rho, | |
| 646 | + rho_kluge, | |
| 638 | 647 | cos_psf, |
| 639 | 648 | sin_psf, |
| 640 | 649 | cos_ph, |
| ... | ... | @@ -790,8 +799,32 @@ double cta_irf_elliptical_kern_omega::eval(const double& omega) |
| 790 | 799 | // Initialise IRF value |
| 791 | 800 | double irf = 0.0; |
| 792 | 801 | |
| 802 | + // Compute azimuth angle in model coordinate system (radians) | |
| 803 | + double omega_model = omega + m_posangle_obs; | |
| 804 | + | |
| 793 | 805 | // Evaluate sky model |
| 794 | - double model = m_model.eval(m_rho, omega + m_posangle_obs, m_srcEng, m_srcTime); | |
| 806 | + double model = m_model.eval(m_rho, omega_model, m_srcEng, m_srcTime); | |
| 807 | + | |
| 808 | + // Debug: test if model is non positive | |
| 809 | + #if defined(G_DEBUG_MODEL_ZERO) | |
| 810 | + if (model <= 0.0) { | |
| 811 | + double m_semiminor_rad = m_model.semiminor() * gammalib::deg2rad; | |
| 812 | + double m_semimajor_rad = m_model.semimajor() * gammalib::deg2rad; | |
| 813 | + double diff_angle = omega_model - m_model.posangle() * gammalib::deg2rad; | |
| 814 | + double cosinus = std::cos(diff_angle); | |
| 815 | + double sinus = std::sin(diff_angle); | |
| 816 | + double arg1 = m_semiminor_rad * cosinus; | |
| 817 | + double arg2 = m_semimajor_rad * sinus; | |
| 818 | + double r_ellipse = m_semiminor_rad * m_semimajor_rad / | |
| 819 | + std::sqrt(arg1*arg1 + arg2*arg2); | |
| 820 | + std::cout << "*** WARNING: cta_irf_elliptical_kern_omega::eval"; | |
| 821 | + std::cout << " zero model for (rho,omega)="; | |
| 822 | + std::cout << m_rho*gammalib::rad2deg << ","; | |
| 823 | + std::cout << omega*gammalib::rad2deg << ")"; | |
| 824 | + std::cout << " rho-r_ellipse=" << (m_rho-r_ellipse) << " radians"; | |
| 825 | + std::cout << std::endl; | |
| 826 | + } | |
| 827 | + #endif | |
| 795 | 828 | |
| 796 | 829 | // Continue only if model is positive |
| 797 | 830 | if (model > 0.0) { |
| ... | ... | @@ -851,10 +884,20 @@ double cta_irf_elliptical_kern_omega::eval(const double& omega) |
| 851 | 884 | * N_{\rm pred}(\rho,\omega) d\omega |
| 852 | 885 | * \f] |
| 853 | 886 | * |
| 854 | - * The azimuth angle integration range | |
| 855 | - * \f$[\omega_{\rm min}, \omega_{\rm max}\f$ | |
| 856 | - * is limited to an arc around the vector connecting the model centre to | |
| 857 | - * the ROI centre. This limitation assures proper converges properly. | |
| 887 | + * where | |
| 888 | + * \f$\omega\f$ is the azimuth angle with respect to the model centre, | |
| 889 | + * counted counterclockwise from the vector connecting the model centre | |
| 890 | + * to the ROI centre, and | |
| 891 | + * \f$\rho\f$ is the radial distance from the model centre. | |
| 892 | + * \f$S_{\rm p}(\rho, \omega | E, t)\f$ is the elliptical source model | |
| 893 | + * for a given true photon energy and photon arrival time, | |
| 894 | + * \f$N_{\rm pred}(\rho,\omega)\f$ is the data space integral over the | |
| 895 | + * response function. | |
| 896 | + * | |
| 897 | + * The method performs the required coordinate transformations from the | |
| 898 | + * model system, spanned by \f$(\rho, \omega)\f$, to the system needed for | |
| 899 | + * Npred computations. Furthermore, the method limits the integration range | |
| 900 | + * to area where the ellipse intersects the ROI. | |
| 858 | 901 | ***************************************************************************/ |
| 859 | 902 | double cta_npred_elliptical_kern_rho::eval(const double& rho) |
| 860 | 903 | { |
| ... | ... | @@ -875,14 +918,18 @@ double cta_npred_elliptical_kern_rho::eval(const double& rho) |
| 875 | 918 | // Continue only if arc length is positive |
| 876 | 919 | if (domega > 0.0) { |
| 877 | 920 | |
| 878 | - // Compute phi integration range | |
| 879 | - double omega_min = m_posangle_roi - domega; | |
| 880 | - double omega_max = m_posangle_roi + domega; | |
| 881 | - | |
| 882 | - // Compute sine and cosine of offset angle | |
| 921 | + // Compute sine and cosine terms for azimuthal integration | |
| 883 | 922 | double sin_rho = std::sin(rho); |
| 884 | 923 | double cos_rho = std::cos(rho); |
| 885 | 924 | |
| 925 | + // Reduce rho by an infinite amount to avoid rounding errors | |
| 926 | + // at the boundary of a sharp edged model (e.g. an elliptical | |
| 927 | + // disk model) | |
| 928 | + double rho_kluge = rho - 1.0e-12; | |
| 929 | + if (rho_kluge < 0.0) { | |
| 930 | + rho_kluge = 0.0; | |
| 931 | + } | |
| 932 | + | |
| 886 | 933 | // Setup phi integration kernel |
| 887 | 934 | cta_npred_elliptical_kern_omega integrand(m_rsp, |
| 888 | 935 | m_model, |
| ... | ... | @@ -890,8 +937,10 @@ double cta_npred_elliptical_kern_rho::eval(const double& rho) |
| 890 | 937 | m_srcTime, |
| 891 | 938 | m_obs, |
| 892 | 939 | m_rot, |
| 940 | + rho_kluge, | |
| 893 | 941 | sin_rho, |
| 894 | - cos_rho); | |
| 942 | + cos_rho, | |
| 943 | + m_posangle_roi); | |
| 895 | 944 | |
| 896 | 945 | // Setup integrator |
| 897 | 946 | GIntegral integral(&integrand); |
| ... | ... | @@ -928,11 +977,10 @@ double cta_npred_elliptical_kern_rho::eval(const double& rho) |
| 928 | 977 | if (omega_width > 0.0) { |
| 929 | 978 | |
| 930 | 979 | // Compute azimuth angle difference between ellipse |
| 931 | - // position angle and position angle of observed | |
| 932 | - // photon in the model system. This angle will define | |
| 933 | - // the reference point around which the circle arc. Make | |
| 934 | - // sure that omega_0 is within [-pi,pi] thus that the omega | |
| 935 | - // intervals are between [-2pi,2pi] | |
| 980 | + // position angle and position angle of ROI in the model | |
| 981 | + // system. This angle will define the reference point for | |
| 982 | + // the circle arcs. Make sure that omega_0 is within [-pi,pi] | |
| 983 | + // thus that the omega intervals are between [-2pi,2pi] | |
| 936 | 984 | double omega_0 = m_model.posangle() * gammalib::deg2rad - |
| 937 | 985 | m_posangle_roi; |
| 938 | 986 | if (omega_0 > gammalib::pi) { |
| ... | ... | @@ -1005,34 +1053,68 @@ double cta_npred_elliptical_kern_rho::eval(const double& rho) |
| 1005 | 1053 | * Computes |
| 1006 | 1054 | * |
| 1007 | 1055 | * \f[ |
| 1008 | - * S_{\rm p}(\rho,\omega | E, t) \, N_{\rm pred}(\rho,\omega) | |
| 1056 | + * S_{\rm p}(\omega | \rho, E, t) \, N_{\rm pred}(\omega | \rho) | |
| 1009 | 1057 | * \f] |
| 1058 | + * | |
| 1059 | + * where | |
| 1060 | + * \f$\omega\f$ is the azimuth angle with respect to the model centre, | |
| 1061 | + * counted counterclockwise from the vector connecting the model centre | |
| 1062 | + * to the centre of the Region of Interest (ROI), and | |
| 1063 | + * \f$\rho\f$ is the radial distance from the model centre. | |
| 1064 | + * | |
| 1065 | + * @todo Npred computation goes over sky coordinates. This can maybe be | |
| 1066 | + * optimized to reduce the number of coordinate transformations. | |
| 1067 | + * @todo Check whether the Npred omega argument is the right one. | |
| 1010 | 1068 | ***************************************************************************/ |
| 1011 | 1069 | double cta_npred_elliptical_kern_omega::eval(const double& omega) |
| 1012 | 1070 | { |
| 1013 | 1071 | // Initialise Npred value |
| 1014 | 1072 | double npred = 0.0; |
| 1015 | 1073 | |
| 1016 | - // Compute sky direction vector in native coordinates | |
| 1017 | - double cos_omega = std::cos(omega); | |
| 1018 | - double sin_omega = std::sin(omega); | |
| 1019 | - GVector native(-cos_omega*m_sin_rho, sin_omega*m_sin_rho, m_cos_rho); | |
| 1020 | - | |
| 1021 | - // Rotate from native into celestial system | |
| 1022 | - GVector cel = m_rot * native; | |
| 1074 | + // Compute azimuth angle in model coordinate system (radians) | |
| 1075 | + double omega_model = omega + m_posangle_roi; | |
| 1023 | 1076 | |
| 1024 | - // Set sky direction | |
| 1025 | - GSkyDir srcDir; | |
| 1026 | - srcDir.celvector(cel); | |
| 1077 | + // Evaluate sky model | |
| 1078 | + double model = m_model.eval(m_rho, omega_model, m_srcEng, m_srcTime); | |
| 1079 | + | |
| 1080 | + // Debug: test if model is non positive | |
| 1081 | + #if defined(G_DEBUG_MODEL_ZERO) | |
| 1082 | + if (model <= 0.0) { | |
| 1083 | + double m_semiminor_rad = m_model.semiminor() * gammalib::deg2rad; | |
| 1084 | + double m_semimajor_rad = m_model.semimajor() * gammalib::deg2rad; | |
| 1085 | + double diff_angle = omega_model - m_model.posangle() * gammalib::deg2rad; | |
| 1086 | + double cosinus = std::cos(diff_angle); | |
| 1087 | + double sinus = std::sin(diff_angle); | |
| 1088 | + double arg1 = m_semiminor_rad * cosinus; | |
| 1089 | + double arg2 = m_semimajor_rad * sinus; | |
| 1090 | + double r_ellipse = m_semiminor_rad * m_semimajor_rad / | |
| 1091 | + std::sqrt(arg1*arg1 + arg2*arg2); | |
| 1092 | + std::cout << "*** WARNING: cta_npred_elliptical_kern_omega::eval"; | |
| 1093 | + std::cout << " zero model for (rho,omega)="; | |
| 1094 | + std::cout << m_rho*gammalib::rad2deg << ","; | |
| 1095 | + std::cout << omega*gammalib::rad2deg << ")"; | |
| 1096 | + std::cout << " rho-r_ellipse=" << (m_rho-r_ellipse) << " radians"; | |
| 1097 | + std::cout << std::endl; | |
| 1098 | + } | |
| 1099 | + #endif | |
| 1100 | + | |
| 1101 | + // Continue only if model is positive | |
| 1102 | + if (model > 0.0) { | |
| 1103 | + | |
| 1104 | + // Compute sky direction vector in native coordinates | |
| 1105 | + double cos_omega = std::cos(omega_model); | |
| 1106 | + double sin_omega = std::sin(omega_model); | |
| 1107 | + GVector native(-cos_omega*m_sin_rho, sin_omega*m_sin_rho, m_cos_rho); | |
| 1027 | 1108 | |
| 1028 | - // Set Photon | |
| 1029 | - GPhoton photon(srcDir, m_srcEng, m_srcTime); | |
| 1109 | + // Rotate from native into celestial system | |
| 1110 | + GVector cel = m_rot * native; | |
| 1030 | 1111 | |
| 1031 | - // Get model value for this photon | |
| 1032 | - double model = m_model.eval(photon); | |
| 1112 | + // Set sky direction | |
| 1113 | + GSkyDir srcDir; | |
| 1114 | + srcDir.celvector(cel); | |
| 1033 | 1115 | |
| 1034 | - // Continue only if sky intensity is positive | |
| 1035 | - if (model > 0.0) { | |
| 1116 | + // Set Photon | |
| 1117 | + GPhoton photon(srcDir, m_srcEng, m_srcTime); | |
| 1036 | 1118 | |
| 1037 | 1119 | // Compute Npred for this sky direction |
| 1038 | 1120 | npred = m_rsp.npred(photon, m_obs) * model; | ... | ... |
inst/cta/src/GCTAResponse_helpers.hpp
| ... | ... | @@ -687,26 +687,32 @@ public: |
| 687 | 687 | const GTime& srcTime, |
| 688 | 688 | const GCTAObservation& obs, |
| 689 | 689 | const GMatrix& rot, |
| 690 | + const double& rho, | |
| 690 | 691 | const double& sin_rho, |
| 691 | - const double& cos_rho) : | |
| 692 | + const double& cos_rho, | |
| 693 | + const double& posangle_roi) : | |
| 692 | 694 | m_rsp(rsp), |
| 693 | 695 | m_model(model), |
| 694 | 696 | m_srcEng(srcEng), |
| 695 | 697 | m_srcTime(srcTime), |
| 696 | 698 | m_obs(obs), |
| 697 | 699 | m_rot(rot), |
| 700 | + m_rho(rho), | |
| 698 | 701 | m_sin_rho(sin_rho), |
| 699 | - m_cos_rho(cos_rho) { } | |
| 702 | + m_cos_rho(cos_rho), | |
| 703 | + m_posangle_roi(posangle_roi) { } | |
| 700 | 704 | double eval(const double& omega); |
| 701 | 705 | protected: |
| 702 | - const GCTAResponseIrf& m_rsp; //!< CTA response | |
| 703 | - const GModelSpatialElliptical& m_model; //!< Model | |
| 704 | - const GEnergy& m_srcEng; //!< True photon energy | |
| 705 | - const GTime& m_srcTime; //!< True photon arrival time | |
| 706 | - const GCTAObservation& m_obs; //!< Pointer to observation | |
| 707 | - const GMatrix& m_rot; //!< Rotation matrix | |
| 708 | - const double& m_sin_rho; //!< Sine of offset angle | |
| 709 | - const double& m_cos_rho; //!< Cosine of offset angle | |
| 706 | + const GCTAResponseIrf& m_rsp; //!< CTA response | |
| 707 | + const GModelSpatialElliptical& m_model; //!< Model | |
| 708 | + const GEnergy& m_srcEng; //!< True photon energy | |
| 709 | + const GTime& m_srcTime; //!< True photon arrival time | |
| 710 | + const GCTAObservation& m_obs; //!< Pointer to observation | |
| 711 | + const GMatrix& m_rot; //!< Rotation matrix | |
| 712 | + const double& m_rho; | |
| 713 | + const double& m_sin_rho; //!< Sine of offset angle | |
| 714 | + const double& m_cos_rho; //!< Cosine of offset angle | |
| 715 | + const double& m_posangle_roi; //!< Position angle of ROI | |
| 710 | 716 | }; |
| 711 | 717 | |
| 712 | 718 | ... | ... |
inst/cta/test/test_irf_elliptical.py
| ... | ... | @@ -413,8 +413,10 @@ if __name__ == '__main__': |
| 413 | 413 | # Set model parameters |
| 414 | 414 | centre = gammalib.GSkyDir() |
| 415 | 415 | centre.radec_deg(0.0, 0.0) |
| 416 | - semimajor = 1.0 | |
| 417 | - semiminor = 0.50 | |
| 416 | + #semimajor = 1.0 | |
| 417 | + #semiminor = 0.50 | |
| 418 | + semimajor = 0.5 | |
| 419 | + semiminor = 1.0 | |
| 418 | 420 | posang = 120.0 |
| 419 | 421 | |
| 420 | 422 | # Set observed photon direction | ... | ... |
inst/cta/test/test_npred_elliptical.py
| ... | ... | @@ -180,9 +180,12 @@ def show_response(centre, semimajor, semiminor, posang, roiDir, pntDir, roi_radi |
| 180 | 180 | |
| 181 | 181 | # Rho limits |
| 182 | 182 | rho_min = 0.0 |
| 183 | - rho_max = theta_max | |
| 183 | + #rho_max = theta_max | |
| 184 | + rho_max = rho_roi + radius_roi | |
| 184 | 185 | if (rho_roi > radius_roi): |
| 185 | 186 | rho_min = rho_roi - radius_roi |
| 187 | + if (rho_max > theta_max): | |
| 188 | + rho_max = theta_max | |
| 186 | 189 | |
| 187 | 190 | # Setup ellipse as vector |
| 188 | 191 | x = [] |
| ... | ... | @@ -370,8 +373,8 @@ if __name__ == '__main__': |
| 370 | 373 | sys.exit() |
| 371 | 374 | |
| 372 | 375 | # Set Parameters |
| 373 | - roi_radius = 0.5 | |
| 374 | - delta_max = 0.2 | |
| 376 | + roi_radius = 1.0 | |
| 377 | + delta_max = 0.4 | |
| 375 | 378 | |
| 376 | 379 | # Set model parameters |
| 377 | 380 | centre = gammalib.GSkyDir() | ... | ... |
src/model/GModelSpatialEllipticalDisk.cpp
| ... | ... | @@ -692,7 +692,7 @@ void GModelSpatialEllipticalDisk::free_members(void) |
| 692 | 692 | ***************************************************************************/ |
| 693 | 693 | void GModelSpatialEllipticalDisk::update() const |
| 694 | 694 | { |
| 695 | - // Update if one axis has changed has changed | |
| 695 | + // Update if one axis has changed | |
| 696 | 696 | if (m_last_semiminor != semiminor() || m_last_semimajor != semimajor()) { |
| 697 | 697 | |
| 698 | 698 | // Store last values | ... | ... |