Commit c4afb3f9a203456cab9afaf435680bc4e5dcf0ff
1 parent
8f76e1ff
Implement HealPix support and rejection method for Monte Carlo simulations.
Showing
2 changed files
with
149 additions
and
7 deletions
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& 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& 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& 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& 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& 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& 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; | ... | ... |