Commit c4afb3f9a203456cab9afaf435680bc4e5dcf0ff

Authored by Jürgen Knödlseder
1 parent 8f76e1ff

Implement HealPix support and rejection method for Monte Carlo simulations.

include/GModelSpatialDiffuseCube.hpp
@@ -119,6 +119,7 @@ protected: @@ -119,6 +119,7 @@ protected:
119 GNodeArray m_logE; //!< Log10(energy) values of the maps 119 GNodeArray m_logE; //!< Log10(energy) values of the maps
120 GEbounds m_ebounds; //!< Energy bounds of the maps 120 GEbounds m_ebounds; //!< Energy bounds of the maps
121 std::vector<double> m_mc_cache; //!< Monte Carlo cache 121 std::vector<double> m_mc_cache; //!< Monte Carlo cache
  122 + std::vector<double> m_mc_max; //!< Maximum values for MC
122 GModelSpectralNodes m_mc_spectrum; //!< Map cube spectrum 123 GModelSpectralNodes m_mc_spectrum; //!< Map cube spectrum
123 GSkyDir m_mc_cone_dir; //!< Monte Carlo simulation cone centre 124 GSkyDir m_mc_cone_dir; //!< Monte Carlo simulation cone centre
124 double m_mc_cone_rad; //!< Monte Carlo simulation cone radius 125 double m_mc_cone_rad; //!< Monte Carlo simulation cone radius
src/model/GModelSpatialDiffuseCube.cpp
@@ -31,6 +31,7 @@ @@ -31,6 +31,7 @@
31 #include "GException.hpp" 31 #include "GException.hpp"
32 #include "GTools.hpp" 32 #include "GTools.hpp"
33 #include "GMath.hpp" 33 #include "GMath.hpp"
  34 +#include "GHealpix.hpp"
34 #include "GModelSpatialDiffuseCube.hpp" 35 #include "GModelSpatialDiffuseCube.hpp"
35 #include "GModelSpatialRegistry.hpp" 36 #include "GModelSpatialRegistry.hpp"
36 #include "GFitsTable.hpp" 37 #include "GFitsTable.hpp"
@@ -454,16 +455,90 @@ GSkyDir GModelSpatialDiffuseCube::mc(const GEnergy&amp; energy, @@ -454,16 +455,90 @@ GSkyDir GModelSpatialDiffuseCube::mc(const GEnergy&amp; energy,
454 low = mid; 455 low = mid;
455 } 456 }
456 } 457 }
  458 + int index = low - offset;
457 459
458 // Convert sky map index to sky map pixel 460 // Convert sky map index to sky map pixel
459 - GSkyPixel pixel = m_cube.inx2pix(low-offset); 461 + GSkyPixel pixel = m_cube.inx2pix(index);
460 462
461 - // Randomize pixel  
462 - pixel.x(pixel.x() + ran.uniform() - 0.5);  
463 - pixel.y(pixel.y() + ran.uniform() - 0.5); 463 + // If we have a 2D pixel then randomize pixel values and convert them
  464 + // into a sky direction
  465 + if (pixel.is_2D()) {
464 466
465 - // Get sky direction  
466 - dir = m_cube.pix2dir(pixel); 467 + // Use a rejection method to find a sky direction
  468 + while (true) {
  469 +
  470 + // Draw random sky pixel
  471 + GSkyPixel test(pixel.x() + ran.uniform() - 0.5,
  472 + pixel.y() + ran.uniform() - 0.5);
  473 +
  474 + // Derive sky direction
  475 + dir = m_cube.pix2dir(test);
  476 +
  477 + // Get map value at that sky direction
  478 + double value = m_cube(dir);
  479 +
  480 + // Get uniform random number up to the maximum
  481 + double uniform = ran.uniform() * m_mc_max[low];
  482 +
  483 + // Exit loop if we're not larger than the map value
  484 + if (uniform <= value) {
  485 + break;
  486 + }
  487 +
  488 + } // endwhile: rejection method
  489 +
  490 + } // endif: had a 2D pixel
  491 +
  492 + // ... otherwise convert pixel into sky direction and randomize
  493 + // position. We use a while loop to randomize the position within a
  494 + // circle that enclosed the pixel and retain only directions that
  495 + // result in the same pixel index and that are compatible with the
  496 + // density distribution.
  497 + else {
  498 +
  499 + // Get pointer on HealPix projection
  500 + const GHealpix* healpix =
  501 + static_cast<const GHealpix*>(m_cube.projection());
  502 +
  503 + // Get enclosing radius
  504 + double radius = healpix->max_pixrad();
  505 +
  506 + // Initialize pixel centre
  507 + dir = m_cube.pix2dir(pixel);
  508 +
  509 + // Get randomized pixel
  510 + GSkyDir randomized_dir;
  511 + double cosrad = std::cos(radius);
  512 + while (true) {
  513 +
  514 + // Get randomized sky direction
  515 + randomized_dir = dir;
  516 + double theta = std::acos(1.0 - ran.uniform() * (1.0 - cosrad)) * gammalib::rad2deg;
  517 + double phi = 360.0 * ran.uniform();
  518 + randomized_dir.rotate_deg(phi, theta);
  519 +
  520 + // Skip if we are not in the actual pixel
  521 + if (m_cube.dir2inx(randomized_dir) != index) {
  522 + continue;
  523 + }
  524 +
  525 + // Get map value at that sky direction
  526 + double value = m_cube(randomized_dir);
  527 +
  528 + // Get uniform random number up to the maximum
  529 + double uniform = ran.uniform() * m_mc_max[low];
  530 +
  531 + // Exit loop if we're not larger than the map value
  532 + if (uniform <= value) {
  533 + break;
  534 + }
  535 +
  536 + } // endwhile
  537 +
  538 + // Store randomize sky position
  539 + dir = randomized_dir;
  540 +
  541 + } // endelse: we had a HealPix map
467 542
468 } // endif: there were pixels in sky map 543 } // endif: there were pixels in sky map
469 544
@@ -787,6 +862,7 @@ void GModelSpatialDiffuseCube::set_mc_cone(const GSkyDir&amp; centre, @@ -787,6 +862,7 @@ void GModelSpatialDiffuseCube::set_mc_cone(const GSkyDir&amp; centre,
787 { 862 {
788 // Initialise cache 863 // Initialise cache
789 m_mc_cache.clear(); 864 m_mc_cache.clear();
  865 + m_mc_max.clear();
790 m_mc_spectrum.clear(); 866 m_mc_spectrum.clear();
791 867
792 // Fetch cube 868 // Fetch cube
@@ -801,6 +877,7 @@ void GModelSpatialDiffuseCube::set_mc_cone(const GSkyDir&amp; centre, @@ -801,6 +877,7 @@ void GModelSpatialDiffuseCube::set_mc_cone(const GSkyDir&amp; centre,
801 877
802 // Reserve space for all pixels in cache 878 // Reserve space for all pixels in cache
803 m_mc_cache.reserve((npix+1)*nmaps); 879 m_mc_cache.reserve((npix+1)*nmaps);
  880 + m_mc_max.reserve(npix*nmaps);
804 881
805 // Loop over all maps 882 // Loop over all maps
806 for (int i = 0; i < nmaps; ++i) { 883 for (int i = 0; i < nmaps; ++i) {
@@ -833,7 +910,7 @@ void GModelSpatialDiffuseCube::set_mc_cone(const GSkyDir&amp; centre, @@ -833,7 +910,7 @@ void GModelSpatialDiffuseCube::set_mc_cone(const GSkyDir&amp; centre,
833 // simulated event is contained in the simulation cone. 910 // simulated event is contained in the simulation cone.
834 double distance = centre.dist_deg(m_cube.pix2dir(k)); 911 double distance = centre.dist_deg(m_cube.pix2dir(k));
835 if (distance <= radius+pixel_radius) { 912 if (distance <= radius+pixel_radius) {
836 - double flux = m_cube(k,i) * m_cube.solidangle(k); 913 + double flux = m_cube.flux(k,i);
837 if (flux > 0.0) { 914 if (flux > 0.0) {
838 total_flux += flux; 915 total_flux += flux;
839 } 916 }
@@ -866,6 +943,68 @@ void GModelSpatialDiffuseCube::set_mc_cone(const GSkyDir&amp; centre, @@ -866,6 +943,68 @@ void GModelSpatialDiffuseCube::set_mc_cone(const GSkyDir&amp; centre,
866 943
867 } 944 }
868 945
  946 + // Do we have a HealPix map?
  947 + if (m_cube.projection()->code() == "HPX") {
  948 +
  949 + // Get pointer on HealPix projection
  950 + const GHealpix* healpix =
  951 + static_cast<const GHealpix*>(m_cube.projection());
  952 +
  953 + // Compute maximum value that may occur from bilinear
  954 + // interpolation within this pixel and push this value on the
  955 + // stack. We do this by checking values of all neighbours.
  956 + for (int k = 0; k < npix; ++k) {
  957 +
  958 + // Get neighbours
  959 + std::vector<int> neighbours = healpix->neighbours(k);
  960 +
  961 + // Loop over neighbours
  962 + double max = m_cube(k,i);
  963 + for (int j = 0; j < neighbours.size(); ++j) {
  964 + if (neighbours[j] != -1) {
  965 + double value = m_cube(neighbours[j],i);
  966 + if (value > max) {
  967 + max = value;
  968 + }
  969 + }
  970 + }
  971 +
  972 + // Store maximum
  973 + m_mc_max.push_back(max);
  974 +
  975 + } // endfor: looped over pixels
  976 +
  977 + } // endif: Healpix projection
  978 +
  979 + // ... no, then we have a WCS map
  980 + else {
  981 +
  982 + // Compute maximum value that may occur from bilinear
  983 + // interpolation within this pixel and push this value on the
  984 + // stack. We do this by checking the map values at the corners
  985 + // and the centre of each edge.
  986 + for (int k = 0; k < npix; ++k) {
  987 + GSkyPixel pixel = m_cube.inx2pix(k);
  988 + double max = m_cube(pixel,i);
  989 + for (int ix = -1; ix < 2; ++ix) {
  990 + for (int iy = -1; iy < 2; ++iy) {
  991 + if (ix != 0 || iy != 0) {
  992 + GSkyPixel edge(pixel.x()+ix*0.5, pixel.y()+iy*0.5);
  993 + if (m_cube.contains(edge)) {
  994 + GSkyDir dir = m_cube.pix2dir(edge);
  995 + double value = m_cube(dir,i);
  996 + if (value > max) {
  997 + max = value;
  998 + }
  999 + }
  1000 + }
  1001 + }
  1002 + }
  1003 + m_mc_max.push_back(max);
  1004 + }
  1005 +
  1006 + } // endelse: computed maximum pixel value
  1007 +
869 } // endfor: looped over all maps 1008 } // endfor: looped over all maps
870 1009
871 // Dump cache values for debugging 1010 // Dump cache values for debugging
@@ -1000,6 +1139,7 @@ void GModelSpatialDiffuseCube::init_members(void) @@ -1000,6 +1139,7 @@ void GModelSpatialDiffuseCube::init_members(void)
1000 1139
1001 // Initialise MC cache 1140 // Initialise MC cache
1002 m_mc_cache.clear(); 1141 m_mc_cache.clear();
  1142 + m_mc_max.clear();
1003 m_mc_spectrum.clear(); 1143 m_mc_spectrum.clear();
1004 m_mc_cone_dir.clear(); 1144 m_mc_cone_dir.clear();
1005 m_mc_cone_rad = 0.0; 1145 m_mc_cone_rad = 0.0;
@@ -1026,6 +1166,7 @@ void GModelSpatialDiffuseCube::copy_members(const GModelSpatialDiffuseCube&amp; mode @@ -1026,6 +1166,7 @@ void GModelSpatialDiffuseCube::copy_members(const GModelSpatialDiffuseCube&amp; mode
1026 1166
1027 // Copy MC cache 1167 // Copy MC cache
1028 m_mc_cache = model.m_mc_cache; 1168 m_mc_cache = model.m_mc_cache;
  1169 + m_mc_max = model.m_mc_max;
1029 m_mc_spectrum = model.m_mc_spectrum; 1170 m_mc_spectrum = model.m_mc_spectrum;
1030 m_mc_cone_dir = model.m_mc_cone_dir; 1171 m_mc_cone_dir = model.m_mc_cone_dir;
1031 m_mc_cone_rad = model.m_mc_cone_rad; 1172 m_mc_cone_rad = model.m_mc_cone_rad;