Commit 8f76e1ff53e85a2cb1fbe90afc2e956538ce5f55
1 parent
006278f6
Implement rejection method for Monte Carlo also for HealPix maps.
Showing
1 changed file
with
82 additions
and
27 deletions
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 { | ... | ... |