Commit 8f76e1ff53e85a2cb1fbe90afc2e956538ce5f55

Authored by Jürgen Knödlseder
1 parent 006278f6

Implement rejection method for Monte Carlo also for HealPix maps.

src/model/GModelSpatialDiffuseMap.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 "GModelSpatialDiffuseMap.hpp"
35 36 #include "GModelSpatialRegistry.hpp"
36 37  
... ... @@ -322,18 +323,18 @@ double GModelSpatialDiffuseMap::eval_gradients(const GPhoton& photon) const
322 323 * @param[in,out] ran Random number generator.
323 324 * @return Sky direction.
324 325 *
325   - * Returns a random sky direction according to the intensity distribution of
326   - * the model sky map. It makes use of a cache array that contains the
327   - * normalized cumulative flux values of the skymap. Using a uniform random
328   - * number, this cache array is scanned using a bi-section method to determine
329   - * the skymap pixel for which the position should be returned. To avoid
330   - * binning problems, the exact position within the pixel is set by a uniform
331   - * random number generator. In case of a 2D map, this is done by randomizing
332   - * the skymap pixel values (neglecting thus pixel distortions). In case of a
333   - * HealPix map, this is done by selecing a random sky position within the
334   - * HealPix map.
335   - *
336   - * @todo: Improve HealPix randomization
  326 + * Draws a random sky direction from the intensity distribution of the model
  327 + * sky map.
  328 + *
  329 + * The method makes use of a cache array that contains the normalized
  330 + * cumulative flux values of the skymap. Using a uniform random number, this
  331 + * cache array is scanned using a bi-section method to determine the skymap
  332 + * pixel for which the position should be returned.
  333 + *
  334 + * Within that pixel, a rejection method is used to draw a sky direction
  335 + * that follows the intensity distribution that is obtained when calling the
  336 + * interpolation operator. This assures that even for coarse binning of the
  337 + * sky map the simulation corresponds to the model.
337 338 ***************************************************************************/
338 339 GSkyDir GModelSpatialDiffuseMap::mc(const GEnergy& energy,
339 340 const GTime& time,
... ... @@ -384,27 +385,54 @@ GSkyDir GModelSpatialDiffuseMap::mc(const GEnergy& energy,
384 385 } // endif: had a 2D pixel
385 386  
386 387 // ... otherwise convert pixel into sky direction and randomize
387   - // position. We use here a kluge to compute the radius that contains
388   - // a HealPix pixel by setting this radius to twice the half angle
389   - // subtended by the solid angle of the pixel. We then use a while
390   - // loop to randomize the position within that circle and to retain
391   - // only directions that result in the same pixel index.
  388 + // position. We use a while loop to randomize the position within a
  389 + // circle that enclosed the pixel and retain only directions that
  390 + // result in the same pixel index and that are compatible with the
  391 + // density distribution.
392 392 else {
  393 +
  394 + // Get pointer on HealPix projection
  395 + const GHealpix* healpix = static_cast<const GHealpix*>(m_map.projection());
  396 +
  397 + // Get enclosing radius
  398 + double radius = healpix->max_pixrad();
  399 +
  400 + // Initialize pixel centre
393 401 dir = m_map.pix2dir(pixel);
  402 +
  403 + // Get randomized pixel
394 404 GSkyDir randomized_dir;
395   - int randomized_index = -1;
396   - double radius = 2.0 * std::acos(1.0 - m_map.solidangle(index)/gammalib::twopi);
397   - double cosrad = std::cos(radius);
398   - while (randomized_index != index) {
  405 + double cosrad = std::cos(radius);
  406 + while (true) {
  407 +
  408 + // Get randomized sky direction
399 409 randomized_dir = dir;
400 410 double theta = std::acos(1.0 - ran.uniform() * (1.0 - cosrad)) * gammalib::rad2deg;
401 411 double phi = 360.0 * ran.uniform();
402 412 randomized_dir.rotate_deg(phi, theta);
403   - randomized_index = m_map.dir2inx(randomized_dir);
404   -//std::cout << radius << " " << randomized_index << " " << index << " " << randomized_dir << std::endl;
405   - }
  413 +
  414 + // Skip if we are not in the actual pixel
  415 + if (m_map.dir2inx(randomized_dir) != index) {
  416 + continue;
  417 + }
  418 +
  419 + // Get map value at that sky direction
  420 + double value = m_map(randomized_dir);
  421 +
  422 + // Get uniform random number up to the maximum
  423 + double uniform = ran.uniform() * m_mc_max[index];
  424 +
  425 + // Exit loop if we're not larger than the map value
  426 + if (uniform <= value) {
  427 + break;
  428 + }
  429 +
  430 + } // endwhile
  431 +
  432 + // Store randomize sky position
406 433 dir = randomized_dir;
407   - }
  434 +
  435 + } // endelse: we had a HealPix map
408 436  
409 437 } // endif: there were pixels in sky map
410 438  
... ... @@ -772,7 +800,6 @@ void GModelSpatialDiffuseMap::prepare_map(void)
772 800 // zero intensity in the skymap. Invalid pixels are also filtered.
773 801 double sum = 0.0;
774 802 for (int i = 0; i < npix; ++i) {
775   - //double flux = m_map(i) * m_map.solidangle(i);
776 803 double flux = m_map.flux(i);
777 804 if (flux < 0.0 ||
778 805 gammalib::is_notanumber(flux) ||
... ... @@ -808,10 +835,38 @@ void GModelSpatialDiffuseMap::prepare_map(void)
808 835 // If we have a HealPix map then set radius to 180 deg
809 836 if (m_map.projection()->code() == "HPX") {
810 837  
  838 + // Get pointer on HealPix projection
  839 + const GHealpix* healpix =
  840 + static_cast<const GHealpix*>(m_map.projection());
  841 +
811 842 // Set the map radius to full sky
812 843 m_radius = 180.0;
813 844  
814   - }
  845 + // Compute maximum value that may occur from bilinear
  846 + // interpolation within this pixel and push this value on the
  847 + // stack. We do this by checking values of all neighbours.
  848 + for (int i = 0; i < npix; ++i) {
  849 +
  850 + // Get neighbours
  851 + std::vector<int> neighbours = healpix->neighbours(i);
  852 +
  853 + // Loop over neighbours
  854 + double max = m_map(i);
  855 + for (int k = 0; k < neighbours.size(); ++k) {
  856 + if (neighbours[k] != -1) {
  857 + double value = m_map(neighbours[k]);
  858 + if (value > max) {
  859 + max = value;
  860 + }
  861 + }
  862 + }
  863 +
  864 + // Store maximum
  865 + m_mc_max.push_back(max);
  866 +
  867 + } // endfor: looped over pixels
  868 +
  869 + } // endif: Healpix projection
815 870  
816 871 // ... otherwise compute map centre and radius
817 872 else {
... ...