Commit 2a30f21b4a5c9561ae4887cd77560d7136de101c
1 parent
f573d219
Optimized and parallelized ctmodel (#2147)
Optimized computation intensive parts of ctmodel to reduce computation time. Implemented openmp parallelization of ctmodel to reduce computation time. Updated information logging (#2147) Updated writing information to log file in order to prevent messy log file messages. This doesnt appear to impact the computation time. Integrate code without modification of GammaLib (#2147) Implement efficient model computation (#2147)
Showing
2 changed files
with
165 additions
and
61 deletions
src/ctmodel/ctmodel.cpp
| 1 | 1 | /*************************************************************************** |
| 2 | 2 | * ctmodel - Model cube generation tool * |
| 3 | 3 | * ----------------------------------------------------------------------- * |
| 4 | - * copyright (C) 2012-2016 by Juergen Knoedlseder * | |
| 4 | + * copyright (C) 2012-2017 by Juergen Knoedlseder * | |
| 5 | 5 | * ----------------------------------------------------------------------- * |
| 6 | 6 | * * |
| 7 | 7 | * This program is free software: you can redistribute it and/or modify * |
| ... | ... | @@ -218,6 +218,9 @@ void ctmodel::run(void) |
| 218 | 218 | // Get task parameters |
| 219 | 219 | get_parameters(); |
| 220 | 220 | |
| 221 | + // Extract cube properties | |
| 222 | + extract_cube_properties(); | |
| 223 | + | |
| 221 | 224 | // Set energy dispersion flags of all CTA observations and save old |
| 222 | 225 | // values in save_edisp vector |
| 223 | 226 | std::vector<bool> save_edisp = set_edisp(m_obs, m_apply_edisp); |
| ... | ... | @@ -232,43 +235,44 @@ void ctmodel::run(void) |
| 232 | 235 | log_header1(TERSE, "Generate model cube"); |
| 233 | 236 | |
| 234 | 237 | // Loop over all observations in the container |
| 238 | + #pragma omp parallel for | |
| 235 | 239 | for (int i = 0; i < m_obs.size(); ++i) { |
| 236 | - | |
| 237 | - // Write header for the current observation | |
| 238 | - log_header3(TERSE, get_obs_header(m_obs[i])); | |
| 239 | - | |
| 240 | + | |
| 240 | 241 | // Get CTA observation |
| 241 | 242 | GCTAObservation* obs = dynamic_cast<GCTAObservation*>(m_obs[i]); |
| 242 | 243 | |
| 243 | 244 | // Skip observation if it's not CTA |
| 244 | 245 | if (obs == NULL) { |
| 246 | + log_header3(TERSE, get_obs_header(m_obs[i])); | |
| 245 | 247 | std::string msg = " Skipping "+m_obs[i]->instrument()+ |
| 246 | 248 | " observation"; |
| 247 | 249 | log_string(NORMAL, msg); |
| 248 | - continue; | |
| 249 | 250 | } |
| 250 | 251 | |
| 251 | - // Fill cube and leave loop if we are binned mode (meaning we | |
| 252 | + // Fill cube and leave loop if we are binned mode (meaning we | |
| 252 | 253 | // only have one binned observation) |
| 253 | - if (m_binned) { | |
| 254 | + else if (m_binned) { | |
| 254 | 255 | fill_cube(obs); |
| 255 | - break; | |
| 256 | + i = m_obs.size(); | |
| 256 | 257 | } |
| 257 | 258 | |
| 258 | 259 | // Skip observation if we have a binned observation |
| 259 | - if (obs->eventtype() == "CountsCube") { | |
| 260 | + else if (obs->eventtype() == "CountsCube") { | |
| 261 | + log_header3(TERSE, get_obs_header(m_obs[i])); | |
| 260 | 262 | std::string msg = " Skipping binned "+m_obs[i]->instrument()+ |
| 261 | 263 | " observation"; |
| 262 | 264 | log_string(NORMAL, msg); |
| 263 | - continue; | |
| 264 | 265 | } |
| 265 | 266 | |
| 266 | - // Fill the cube | |
| 267 | - fill_cube(obs); | |
| 268 | - | |
| 269 | - // Dispose events to free memory if event file exists on disk | |
| 270 | - if (obs->eventfile().length() > 0 && obs->eventfile().exists()) { | |
| 271 | - obs->dispose_events(); | |
| 267 | + // Otherwise, everything seems to be fine, so fill the cube from obs | |
| 268 | + else { | |
| 269 | + // Fill the cube | |
| 270 | + fill_cube(obs); | |
| 271 | + | |
| 272 | + // Dispose events to free memory if event file exists on disk | |
| 273 | + if (obs->eventfile().length() > 0 && obs->eventfile().exists()) { | |
| 274 | + obs->dispose_events(); | |
| 275 | + } | |
| 272 | 276 | } |
| 273 | 277 | |
| 274 | 278 | } // endfor: looped over observations |
| ... | ... | @@ -396,6 +400,12 @@ void ctmodel::init_members(void) |
| 396 | 400 | m_has_cube = false; |
| 397 | 401 | m_append_cube = false; |
| 398 | 402 | m_binned = false; |
| 403 | + m_dir.clear(); | |
| 404 | + m_solidangle.clear(); | |
| 405 | + m_energy.clear(); | |
| 406 | + m_ewidth.clear(); | |
| 407 | + m_time.clear(); | |
| 408 | + m_ontime = 0.0; | |
| 399 | 409 | |
| 400 | 410 | // Return |
| 401 | 411 | return; |
| ... | ... | @@ -421,6 +431,12 @@ void ctmodel::copy_members(const ctmodel& app) |
| 421 | 431 | m_has_cube = app.m_has_cube; |
| 422 | 432 | m_append_cube = app.m_append_cube; |
| 423 | 433 | m_binned = app.m_binned; |
| 434 | + m_dir = app.m_dir; | |
| 435 | + m_solidangle = app.m_solidangle; | |
| 436 | + m_energy = app.m_energy; | |
| 437 | + m_ewidth = app.m_ewidth; | |
| 438 | + m_time = app.m_time; | |
| 439 | + m_ontime = app.m_ontime; | |
| 424 | 440 | |
| 425 | 441 | // Return |
| 426 | 442 | return; |
| ... | ... | @@ -478,7 +494,6 @@ void ctmodel::get_parameters(void) |
| 478 | 494 | // Set cube from binned observation |
| 479 | 495 | GCTAEventCube* evtcube = dynamic_cast<GCTAEventCube*> |
| 480 | 496 | (const_cast<GEvents*>(obs->events())); |
| 481 | - | |
| 482 | 497 | cube(*evtcube); |
| 483 | 498 | |
| 484 | 499 | // Signal that cube has been set |
| ... | ... | @@ -714,6 +729,60 @@ void ctmodel::get_obs(void) |
| 714 | 729 | |
| 715 | 730 | |
| 716 | 731 | /***********************************************************************//** |
| 732 | + * @brief Extract cube properties in data members | |
| 733 | + ***************************************************************************/ | |
| 734 | +void ctmodel::extract_cube_properties(void) | |
| 735 | +{ | |
| 736 | + // Clear cube properties | |
| 737 | + m_dir.clear(); | |
| 738 | + m_solidangle.clear(); | |
| 739 | + m_energy.clear(); | |
| 740 | + m_ewidth.clear(); | |
| 741 | + | |
| 742 | + // Get number of spatial pixels and energy layers in counts cube | |
| 743 | + int npix = m_cube.npix(); | |
| 744 | + int nebins = m_cube.ebins(); | |
| 745 | + | |
| 746 | + // Reserve capacity in vectors | |
| 747 | + m_dir.reserve(npix); | |
| 748 | + m_solidangle.reserve(npix); | |
| 749 | + m_energy.reserve(nebins); | |
| 750 | + m_ewidth.reserve(nebins); | |
| 751 | + | |
| 752 | + // Loop over all the spatial bins and extract spatial cube properties | |
| 753 | + for (int i = 0; i < npix; ++i) { | |
| 754 | + | |
| 755 | + // Get cube bin | |
| 756 | + GCTAEventBin* bin = m_cube[i]; | |
| 757 | + | |
| 758 | + // Extract sky direction and solid angle | |
| 759 | + m_dir.push_back(bin->dir()); | |
| 760 | + m_solidangle.push_back(bin->solidangle()); | |
| 761 | + | |
| 762 | + } // endfor: looped over spatial bins | |
| 763 | + | |
| 764 | + // Loop over all the spectral bins and extract spectral cube properties | |
| 765 | + for (int iebin = 0, ibin = 0; iebin < nebins; ++iebin, ibin += npix) { | |
| 766 | + | |
| 767 | + // Get cube bin | |
| 768 | + GCTAEventBin* bin = m_cube[ibin]; | |
| 769 | + | |
| 770 | + // Extract energy and energy width | |
| 771 | + m_energy.push_back(bin->energy()); | |
| 772 | + m_ewidth.push_back(bin->ewidth()); | |
| 773 | + | |
| 774 | + } // endfor: looped over spectral bins | |
| 775 | + | |
| 776 | + // Set time and ontime | |
| 777 | + m_time = m_cube[0]->time(); | |
| 778 | + m_ontime = m_cube[0]->ontime(); | |
| 779 | + | |
| 780 | + // Return | |
| 781 | + return; | |
| 782 | +} | |
| 783 | + | |
| 784 | + | |
| 785 | +/***********************************************************************//** | |
| 717 | 786 | * @brief Fill model into model cube |
| 718 | 787 | * |
| 719 | 788 | * @param[in] obs CTA observation. |
| ... | ... | @@ -725,6 +794,10 @@ void ctmodel::get_obs(void) |
| 725 | 794 | ***************************************************************************/ |
| 726 | 795 | void ctmodel::fill_cube(const GCTAObservation* obs) |
| 727 | 796 | { |
| 797 | + // Get number of spatial and spectral bins in counts cube | |
| 798 | + int npix = m_cube.npix(); | |
| 799 | + int nebins = m_cube.ebins(); | |
| 800 | + | |
| 728 | 801 | // Get references to GTI and energy boundaries for the event list |
| 729 | 802 | const GGti& gti = obs->events()->gti(); |
| 730 | 803 | const GEbounds& obs_ebounds = obs->ebounds(); |
| ... | ... | @@ -748,64 +821,88 @@ void ctmodel::fill_cube(const GCTAObservation* obs) |
| 748 | 821 | int num_outside_ebds = 0; |
| 749 | 822 | int num_outside_roi = 0; |
| 750 | 823 | |
| 751 | - // Setup cube GTIs for this observation | |
| 752 | - m_cube.gti(obs->events()->gti()); | |
| 824 | + // Extract copy of models from observation container | |
| 825 | + GModels models(m_obs.models()); | |
| 753 | 826 | |
| 754 | - // Loop over all cube bins | |
| 755 | - for (int i = 0; i < m_cube.size(); ++i) { | |
| 827 | + // Get pointer to event cube pixels | |
| 828 | + double* pixels = const_cast<double*>(m_cube.counts().pixels()); | |
| 756 | 829 | |
| 757 | - // Get cube bin | |
| 758 | - GCTAEventBin* bin = m_cube[i]; | |
| 830 | + // Initialise event bin | |
| 831 | + GCTAEventBin bin; | |
| 759 | 832 | |
| 760 | - // Determine counts cube energy bin | |
| 761 | - int iebin = cube_ebounds.index(bin->energy()); | |
| 833 | + // Set event bin attributes that are constant for a counts cube | |
| 834 | + bin.time(m_time); | |
| 835 | + bin.ontime(m_ontime); | |
| 836 | + bin.weight(1.0); | |
| 762 | 837 | |
| 763 | - // Skip bin if the corresponding counts cube energy bin is not fully | |
| 764 | - // contained in the event list energy range. This avoids having | |
| 765 | - // partially filled bins. | |
| 766 | - if (!usage[iebin]) { | |
| 767 | - num_outside_ebds++; | |
| 768 | - continue; | |
| 769 | - } | |
| 838 | + // Loop over all the spatial bins | |
| 839 | + for (int i = 0; i < npix; ++i) { | |
| 770 | 840 | |
| 771 | 841 | // If RoI is valid then skip bin if it is outside the RoI of the |
| 772 | 842 | // observation |
| 773 | - if (roi.is_valid() && !roi.contains(*bin)) { | |
| 774 | - num_outside_roi++; | |
| 843 | + if (roi.is_valid() && !roi.contains(m_dir[i])) { | |
| 844 | + num_outside_roi += nebins; | |
| 775 | 845 | continue; |
| 776 | 846 | } |
| 777 | 847 | |
| 778 | - // Get actual bin value | |
| 779 | - double value = bin->counts(); | |
| 780 | - | |
| 781 | - // Compute model value for cube bin | |
| 782 | - double model = m_obs.models().eval(*bin, *obs) * bin->size(); | |
| 848 | + // Set instrument direction and solid angle of bin | |
| 849 | + bin.dir(m_dir[i]); | |
| 850 | + bin.solidangle(m_solidangle[i]); | |
| 851 | + | |
| 852 | + // Loop over all of the energy bins of the cube | |
| 853 | + for (int iebin = 0, ibin = i; iebin < nebins; ++iebin, ibin += npix) { | |
| 783 | 854 | |
| 784 | - // Add model to actual value | |
| 785 | - value += model; | |
| 786 | - sum += model; | |
| 855 | + // Skip bin if the corresponding counts cube energy bin is not fully | |
| 856 | + // contained in the event list energy range. This avoids having | |
| 857 | + // partially filled bins. | |
| 858 | + if (!usage[iebin]) { | |
| 859 | + num_outside_ebds++; | |
| 860 | + continue; | |
| 861 | + } | |
| 787 | 862 | |
| 788 | - // Store value | |
| 789 | - bin->counts(value); | |
| 863 | + // Set energy and energy width of bin | |
| 864 | + bin.energy(m_energy[iebin]); | |
| 865 | + bin.ewidth(m_ewidth[iebin]); | |
| 790 | 866 | |
| 791 | - } // endfor: looped over all cube bins | |
| 867 | + // Compute model value for cube bin | |
| 868 | + double model = models.eval(bin, *obs) * bin.size(); | |
| 792 | 869 | |
| 793 | - // Append GTIs of observation to list of GTIs | |
| 794 | - m_gti.extend(gti); | |
| 870 | + // Sum model | |
| 871 | + sum += model; | |
| 872 | + | |
| 873 | + // Store value | |
| 874 | + #pragma omp critical(ctmodel_fill_cube) | |
| 875 | + pixels[ibin] += model; | |
| 876 | + | |
| 877 | + } // endfor: looped over all spatial bins | |
| 878 | + | |
| 879 | + } // endfor: looped over all cube bins | |
| 795 | 880 | |
| 796 | 881 | // Update GTIs |
| 797 | - m_cube.gti(m_gti); | |
| 882 | + #pragma omp critical(ctmodel_fill_cube) | |
| 883 | + { | |
| 884 | + // Append GTIs of observation to list of GTIs | |
| 885 | + m_gti.extend(gti); | |
| 886 | + | |
| 887 | + // Update GTIs | |
| 888 | + m_cube.gti(m_gti); | |
| 889 | + } | |
| 798 | 890 | |
| 799 | 891 | // Log filling results |
| 800 | - log_value(NORMAL, "Model events in cube", sum); | |
| 801 | - log_value(NORMAL, "Bins outside energy range", num_outside_ebds); | |
| 802 | - log_value(NORMAL, "Bins outside RoI", num_outside_roi); | |
| 892 | + #pragma omp critical(ctmodel_fill_cube) | |
| 893 | + { | |
| 894 | + log_header3(TERSE, get_obs_header(obs)); | |
| 895 | + log_value(NORMAL, "Model events in cube", sum); | |
| 896 | + log_value(NORMAL, "Bins outside energy range", num_outside_ebds); | |
| 897 | + log_value(NORMAL, "Bins outside RoI", num_outside_roi); | |
| 898 | + } | |
| 803 | 899 | |
| 804 | 900 | // Write model cube into header |
| 805 | - log_header2(EXPLICIT, "Model cube"); | |
| 806 | - log_string(EXPLICIT, m_cube.print(m_chatter)); | |
| 901 | + if (m_chatter >= EXPLICIT) { | |
| 902 | + log_header2(EXPLICIT, "Model cube"); | |
| 903 | + log_string(EXPLICIT, m_cube.print(m_chatter)); | |
| 904 | + } | |
| 807 | 905 | |
| 808 | 906 | // Return |
| 809 | 907 | return; |
| 810 | 908 | } |
| 811 | - | ... | ... |
src/ctmodel/ctmodel.hpp
| 1 | 1 | /*************************************************************************** |
| 2 | 2 | * ctmodel - Model cube generation tool * |
| 3 | 3 | * ----------------------------------------------------------------------- * |
| 4 | - * copyright (C) 2012-2016 by Juergen Knoedlseder * | |
| 4 | + * copyright (C) 2012-2017 by Juergen Knoedlseder * | |
| 5 | 5 | * ----------------------------------------------------------------------- * |
| 6 | 6 | * * |
| 7 | 7 | * This program is free software: you can redistribute it and/or modify * |
| ... | ... | @@ -79,6 +79,7 @@ protected: |
| 79 | 79 | void free_members(void); |
| 80 | 80 | void get_parameters(void); |
| 81 | 81 | void get_obs(void); |
| 82 | + void extract_cube_properties(void); | |
| 82 | 83 | void fill_cube(const GCTAObservation* obs); |
| 83 | 84 | bool has_cube(void) const; |
| 84 | 85 | |
| ... | ... | @@ -89,11 +90,17 @@ protected: |
| 89 | 90 | GChatter m_chatter; //!< Chattiness |
| 90 | 91 | |
| 91 | 92 | // Protected members |
| 92 | - GCTAEventCube m_cube; //!< Model cube | |
| 93 | - GGti m_gti; //!< Model cube GTIs | |
| 94 | - bool m_has_cube; //!< Signal if cube has been set or loaded | |
| 95 | - bool m_append_cube; //!< Signal that cube should be appended | |
| 96 | - bool m_binned; //!< Signals that we are in binned mode | |
| 93 | + GCTAEventCube m_cube; //!< Model cube | |
| 94 | + GGti m_gti; //!< Model cube GTIs | |
| 95 | + bool m_has_cube; //!< Signal if cube exists | |
| 96 | + bool m_append_cube; //!< Signal to append cube | |
| 97 | + bool m_binned; //!< Signal binned mode | |
| 98 | + std::vector<GCTAInstDir> m_dir; //!< Cube directions | |
| 99 | + std::vector<double> m_solidangle; //!< Cube solid angles | |
| 100 | + std::vector<GEnergy> m_energy; //!< Cube energies | |
| 101 | + std::vector<GEnergy> m_ewidth; //!< Cube energy widths | |
| 102 | + GTime m_time; //!< Cube time | |
| 103 | + double m_ontime; //!< Cube ontime | |
| 97 | 104 | }; |
| 98 | 105 | |
| 99 | 106 | ... | ... |