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 119 GNodeArray m_logE; //!< Log10(energy) values of the maps
120 120 GEbounds m_ebounds; //!< Energy bounds of the maps
121 121 std::vector<double> m_mc_cache; //!< Monte Carlo cache
  122 + std::vector<double> m_mc_max; //!< Maximum values for MC
122 123 GModelSpectralNodes m_mc_spectrum; //!< Map cube spectrum
123 124 GSkyDir m_mc_cone_dir; //!< Monte Carlo simulation cone centre
124 125 double m_mc_cone_rad; //!< Monte Carlo simulation cone radius
... ...
src/model/GModelSpatialDiffuseCube.cpp
... ... @@ -31,6 +31,7 @@
31 31 #include "GException.hpp"
32 32 #include "GTools.hpp"
33 33 #include "GMath.hpp"
  34 +#include "GHealpix.hpp"
34 35 #include "GModelSpatialDiffuseCube.hpp"
35 36 #include "GModelSpatialRegistry.hpp"
36 37 #include "GFitsTable.hpp"
... ... @@ -454,16 +455,90 @@ GSkyDir GModelSpatialDiffuseCube::mc(const GEnergy&amp; energy,
454 455 low = mid;
455 456 }
456 457 }
  458 + int index = low - offset;
457 459  
458 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 543 } // endif: there were pixels in sky map
469 544  
... ... @@ -787,6 +862,7 @@ void GModelSpatialDiffuseCube::set_mc_cone(const GSkyDir&amp; centre,
787 862 {
788 863 // Initialise cache
789 864 m_mc_cache.clear();
  865 + m_mc_max.clear();
790 866 m_mc_spectrum.clear();
791 867  
792 868 // Fetch cube
... ... @@ -801,6 +877,7 @@ void GModelSpatialDiffuseCube::set_mc_cone(const GSkyDir&amp; centre,
801 877  
802 878 // Reserve space for all pixels in cache
803 879 m_mc_cache.reserve((npix+1)*nmaps);
  880 + m_mc_max.reserve(npix*nmaps);
804 881  
805 882 // Loop over all maps
806 883 for (int i = 0; i < nmaps; ++i) {
... ... @@ -833,7 +910,7 @@ void GModelSpatialDiffuseCube::set_mc_cone(const GSkyDir&amp; centre,
833 910 // simulated event is contained in the simulation cone.
834 911 double distance = centre.dist_deg(m_cube.pix2dir(k));
835 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 914 if (flux > 0.0) {
838 915 total_flux += flux;
839 916 }
... ... @@ -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 1008 } // endfor: looped over all maps
870 1009  
871 1010 // Dump cache values for debugging
... ... @@ -1000,6 +1139,7 @@ void GModelSpatialDiffuseCube::init_members(void)
1000 1139  
1001 1140 // Initialise MC cache
1002 1141 m_mc_cache.clear();
  1142 + m_mc_max.clear();
1003 1143 m_mc_spectrum.clear();
1004 1144 m_mc_cone_dir.clear();
1005 1145 m_mc_cone_rad = 0.0;
... ... @@ -1026,6 +1166,7 @@ void GModelSpatialDiffuseCube::copy_members(const GModelSpatialDiffuseCube&amp; mode
1026 1166  
1027 1167 // Copy MC cache
1028 1168 m_mc_cache = model.m_mc_cache;
  1169 + m_mc_max = model.m_mc_max;
1029 1170 m_mc_spectrum = model.m_mc_spectrum;
1030 1171 m_mc_cone_dir = model.m_mc_cone_dir;
1031 1172 m_mc_cone_rad = model.m_mc_cone_rad;
... ...