src/model/GModelSpatialDiffuseCube.cpp

Go to the documentation of this file.
00001 /***************************************************************************
00002  *       GModelSpatialDiffuseCube.cpp - Spatial map cube model class       *
00003  * ----------------------------------------------------------------------- *
00004  *  copyright (C) 2010-2016 by Juergen Knoedlseder                         *
00005  * ----------------------------------------------------------------------- *
00006  *                                                                         *
00007  *  This program is free software: you can redistribute it and/or modify   *
00008  *  it under the terms of the GNU General Public License as published by   *
00009  *  the Free Software Foundation, either version 3 of the License, or      *
00010  *  (at your option) any later version.                                    *
00011  *                                                                         *
00012  *  This program is distributed in the hope that it will be useful,        *
00013  *  but WITHOUT ANY WARRANTY; without even the implied warranty of         *
00014  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          *
00015  *  GNU General Public License for more details.                           *
00016  *                                                                         *
00017  *  You should have received a copy of the GNU General Public License      *
00018  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.  *
00019  *                                                                         *
00020  ***************************************************************************/
00021 /**
00022  * @file GModelSpatialDiffuseCube.cpp
00023  * @brief Spatial map cube model class implementation
00024  * @author Juergen Knoedlseder
00025  */
00026 
00027 /* __ Includes ___________________________________________________________ */
00028 #ifdef HAVE_CONFIG_H
00029 #include <config.h>
00030 #endif
00031 #include "GException.hpp"
00032 #include "GTools.hpp"
00033 #include "GMath.hpp"
00034 #include "GHealpix.hpp"
00035 #include "GEnergies.hpp"
00036 #include "GFits.hpp"
00037 #include "GFitsTable.hpp"
00038 #include "GFitsTableCol.hpp"
00039 #include "GXmlElement.hpp"
00040 #include "GModelSpatialRegistry.hpp"
00041 #include "GModelSpatialDiffuseCube.hpp"
00042 
00043 /* __ Constants __________________________________________________________ */
00044 
00045 /* __ Globals ____________________________________________________________ */
00046 const GModelSpatialDiffuseCube g_spatial_cube_seed;
00047 const GModelSpatialRegistry    g_spatial_cube_registry(&g_spatial_cube_seed);
00048 #if defined(G_LEGACY_XML_FORMAT)
00049 const GModelSpatialDiffuseCube g_spatial_cube_legacy_seed(true, "MapCubeFunction");
00050 const GModelSpatialRegistry    g_spatial_cube_legacy_registry(&g_spatial_cube_legacy_seed);
00051 #endif
00052 
00053 /* __ Method name definitions ____________________________________________ */
00054 #define G_MC          "GModelSpatialDiffuseCube::mc(GEnergy&, GTime&, GRan&)"
00055 #define G_READ                 "GModelSpatialDiffuseCube::read(GXmlElement&)"
00056 #define G_WRITE               "GModelSpatialDiffuseCube::write(GXmlElement&)"
00057 #define G_ENERGIES           "GModelSpatialDiffuseCube::energies(GEnergies&)"
00058 #define G_LOAD_CUBE         "GModelSpatialDiffuseCube::load_cube(GFilename&)"
00059 
00060 /* __ Macros _____________________________________________________________ */
00061 
00062 /* __ Coding definitions _________________________________________________ */
00063 
00064 /* __ Debug definitions __________________________________________________ */
00065 //#define G_DEBUG_MC                                     //!< Debug MC method
00066 //#define G_DEBUG_MC_CACHE                               //!< Debug MC cache
00067 
00068 
00069 /*==========================================================================
00070  =                                                                         =
00071  =                        Constructors/destructors                         =
00072  =                                                                         =
00073  ==========================================================================*/
00074 
00075 /***********************************************************************//**
00076  * @brief Void constructor
00077  *
00078  * Constructs empty map cube model.
00079  ***************************************************************************/
00080 GModelSpatialDiffuseCube::GModelSpatialDiffuseCube(void) :
00081                           GModelSpatialDiffuse()
00082 {
00083     // Initialise members
00084     init_members();
00085 
00086     // Return
00087     return;
00088 }
00089 
00090 
00091 /***********************************************************************//**
00092  * @brief Model type constructor
00093  *
00094  * @param[in] dummy Dummy flag.
00095  * @param[in] type Model type.
00096  *
00097  * Constructs empty map cube model by specifying a model @p type.
00098  ***************************************************************************/
00099 GModelSpatialDiffuseCube::GModelSpatialDiffuseCube(const bool&        dummy,
00100                                                    const std::string& type) :
00101                           GModelSpatialDiffuse()
00102 {
00103     // Initialise members
00104     init_members();
00105 
00106     // Set model type
00107     m_type = type;
00108 
00109     // Return
00110     return;
00111 }
00112 
00113 
00114 /***********************************************************************//**
00115  * @brief XML constructor
00116  *
00117  * @param[in] xml XML element.
00118  *
00119  * Constructs map cube model by extracting information from an XML element.
00120  * See the read() method for more information about the expected structure
00121  * of the XML element.
00122  ***************************************************************************/
00123 GModelSpatialDiffuseCube::GModelSpatialDiffuseCube(const GXmlElement& xml) :
00124                           GModelSpatialDiffuse()
00125 {
00126     // Initialise members
00127     init_members();
00128 
00129     // Read information from XML element
00130     read(xml);
00131 
00132     // Return
00133     return;
00134 }
00135 
00136 
00137 /***********************************************************************//**
00138  * @brief Filename constructor
00139  *
00140  * @param[in] filename File name.
00141  * @param[in] value Normalization factor (defaults to 1).
00142  *
00143  * Constructs map cube model by loading a map cube from @p filename and by
00144  * assigning the normalization @p value.
00145  ***************************************************************************/
00146 GModelSpatialDiffuseCube::GModelSpatialDiffuseCube(const GFilename& filename,
00147                                                    const double&    value) :
00148                           GModelSpatialDiffuse()
00149 {
00150     // Initialise members
00151     init_members();
00152 
00153     // Set parameter
00154     m_value.value(value);
00155 
00156     // Perform autoscaling of parameter
00157     autoscale();
00158 
00159     // Store filename
00160     m_filename = filename;
00161 
00162     // Return
00163     return;
00164 }
00165 
00166 
00167 /***********************************************************************//**
00168  * @brief Sky map constructor
00169  *
00170  * @param[in] cube Sky map cube.
00171  * @param[in] energies Sky map energies.
00172  * @param[in] value Normalization factor (defaults to 1).
00173  *
00174  * Constructs map cube model by extracting a @p cube from a sky map. The
00175  * constructor also assigns the energy values for all maps and sets the
00176  * scaling @p value. The filename will remain blank.
00177  ***************************************************************************/
00178 GModelSpatialDiffuseCube::GModelSpatialDiffuseCube(const GSkyMap&   cube,
00179                                                    const GEnergies& energies,
00180                                                    const double&    value) :
00181                           GModelSpatialDiffuse()
00182 {
00183     // Initialise members
00184     init_members();
00185 
00186     // Set parameter
00187     m_value.value(value);
00188 
00189     // Perform autoscaling of parameter
00190     autoscale();
00191 
00192     // Set map cube
00193     this->cube(cube);
00194 
00195     // Set energies
00196     this->energies(energies);
00197 
00198     // Return
00199     return;
00200 }
00201 
00202 
00203 /***********************************************************************//**
00204  * @brief Copy constructor
00205  *
00206  * @param[in] model Map cube model.
00207  ***************************************************************************/
00208 GModelSpatialDiffuseCube::GModelSpatialDiffuseCube(const GModelSpatialDiffuseCube& model) :
00209                           GModelSpatialDiffuse(model)
00210 {
00211     // Initialise members
00212     init_members();
00213 
00214     // Copy members
00215     copy_members(model);
00216 
00217     // Return
00218     return;
00219 }
00220 
00221 
00222 /***********************************************************************//**
00223  * @brief Destructor
00224  ***************************************************************************/
00225 GModelSpatialDiffuseCube::~GModelSpatialDiffuseCube(void)
00226 {
00227     // Free members
00228     free_members();
00229 
00230     // Return
00231     return;
00232 }
00233 
00234 
00235 /*==========================================================================
00236  =                                                                         =
00237  =                               Operators                                 =
00238  =                                                                         =
00239  ==========================================================================*/
00240 
00241 /***********************************************************************//**
00242  * @brief Assignment operator
00243  *
00244  * @param[in] model Map cube model.
00245  * @return Map cube model.
00246  ***************************************************************************/
00247 GModelSpatialDiffuseCube& GModelSpatialDiffuseCube::operator=(const GModelSpatialDiffuseCube& model)
00248 {
00249     // Execute only if object is not identical
00250     if (this != &model) {
00251 
00252         // Copy base class members
00253         this->GModelSpatialDiffuse::operator=(model);
00254 
00255         // Free members
00256         free_members();
00257 
00258         // Initialise members
00259         init_members();
00260 
00261         // Copy members
00262         copy_members(model);
00263 
00264     } // endif: object was not identical
00265 
00266     // Return
00267     return *this;
00268 }
00269 
00270 
00271 /*==========================================================================
00272  =                                                                         =
00273  =                            Public methods                               =
00274  =                                                                         =
00275  ==========================================================================*/
00276 
00277 /***********************************************************************//**
00278  * @brief Clear map cube model
00279  ***************************************************************************/
00280 void GModelSpatialDiffuseCube::clear(void)
00281 {
00282     // Free class members (base and derived classes, derived class first)
00283     free_members();
00284     this->GModelSpatialDiffuse::free_members();
00285     this->GModelSpatial::free_members();
00286 
00287     // Initialise members
00288     this->GModelSpatial::init_members();
00289     this->GModelSpatialDiffuse::init_members();
00290     init_members();
00291 
00292     // Return
00293     return;
00294 }
00295 
00296 
00297 /***********************************************************************//**
00298  * @brief Clone map cube model
00299  *
00300  * @return Pointer to deep copy of map cube model.
00301  ***************************************************************************/
00302 GModelSpatialDiffuseCube* GModelSpatialDiffuseCube::clone(void) const
00303 {
00304     // Clone map cube model
00305     return new GModelSpatialDiffuseCube(*this);
00306 }
00307 
00308 
00309 /***********************************************************************//**
00310  * @brief Evaluate function
00311  *
00312  * @param[in] photon Incident photon.
00313  * @param[in] gradients Compute gradients?
00314  * @return Sky map intensity (\f$\mbox{ph cm}^{-2}\mbox{sr}^{-1}\mbox{s}^{-1}\f$)
00315  *
00316  * Computes the spatial diffuse model as function of photon parameters.
00317  *
00318  * If the @p gradients flag is true the method will also evaluate the partial
00319  * derivatives of the model.
00320  ***************************************************************************/
00321 double GModelSpatialDiffuseCube::eval(const GPhoton& photon,
00322                                       const bool&    gradients) const
00323 {
00324     // Get log-log interpolated cube intensity
00325     double intensity = cube_intensity(photon);
00326 
00327     // Set the intensity times the scaling factor as model value
00328     double value = intensity * m_value.value();
00329 
00330     // Optionally compute partial derivatives
00331     if (gradients) {
00332 
00333         // Compute partial derivatives of the parameter value. In case that
00334         // the value is negative set the gradient to zero.
00335         double g_value = (m_value.is_free()) ? intensity * m_value.scale() : 0.0;
00336         if (value < 0.0) {
00337             g_value = 0.0;
00338         }
00339 
00340         // Set gradient
00341         m_value.factor_gradient(g_value);
00342 
00343     } // endif: computed partial derivatives
00344 
00345     // Make sure that value is not negative
00346     if (value < 0.0) {
00347         value = 0.0;
00348     }
00349 
00350     // Return value
00351     return value;
00352 }
00353 
00354 
00355 /***********************************************************************//**
00356  * @brief Returns MC sky direction
00357  *
00358  * @param[in] energy Photon energy.
00359  * @param[in] time Photon arrival time.
00360  * @param[in,out] ran Random number generator.
00361  * @return Sky direction.
00362  *
00363  * @exception GException::invalid_value
00364  *            No map cube defined.
00365  *            No energy boundaries defined.
00366  *            Simulation cone not defined or does not overlap with map cube.
00367  *
00368  * Returns a random sky direction according to the intensity distribution of
00369  * the model sky map and the specified energy. The method uses a rejection
00370  * method to determine the sky direction.
00371  ***************************************************************************/
00372 GSkyDir GModelSpatialDiffuseCube::mc(const GEnergy& energy,
00373                                      const GTime&   time,
00374                                      GRan&          ran) const
00375 {
00376     // Fetch cube
00377     fetch_cube();
00378 
00379     // Determine number of skymap pixels
00380     int npix = pixels();
00381 
00382     // Throw an exception if there are no sky map pixels
00383     if (npix <= 0) {
00384         std::string msg = "No map cube defined. Please specify a valid map cube.";
00385         throw GException::invalid_value(G_MC, msg);
00386     }
00387 
00388     // Throw an exception if no energy boundaries are defined
00389     if (m_ebounds.size() < 1) {
00390         std::string msg = "The energy boundaries of the maps in the cube have "
00391                           "not been defined. Maybe the map cube file is missing "
00392                           "the \"ENERGIES\" extension which defines the energy "
00393                           "of each map in the cube. Please provide the energy "
00394                           "information.";
00395         throw GException::invalid_value(G_MC, msg);
00396     }
00397 
00398     // Set energy for interpolation
00399     m_logE.set_value(energy.log10MeV());
00400 
00401     // Compute maximum map value within simulation cone by log-log interpolation
00402     // of the maximum values that are stored for each map
00403     double max       = 0.0;
00404     double max_left  = m_mc_max[m_logE.inx_left()];
00405     double max_right = m_mc_max[m_logE.inx_right()];
00406     if (max_left > 0.0 && max_right > 0.0) {
00407         double max_log = m_logE.wgt_left()  * std::log(max_left) +
00408                          m_logE.wgt_right() * std::log(max_right);
00409         max = std::exp(max_log);
00410     }
00411     else if (max_left > 0.0) {
00412         max = max_left;
00413     }
00414     else if (max_right > 0.0) {
00415         max = max_right;
00416     }
00417 
00418     // Throw an exception if the maximum MC intensity is not positive. This
00419     // can be the case because the simulation cone has not been defined or
00420     // because it does not overlap with the sky map
00421     if (max <= 0.0) {
00422         std::string msg = "Simulation cone has not been defined or does not "
00423                           "overlap with the sky map. Please specify a valid "
00424                           "simulation cone.";
00425         throw GException::invalid_value(G_MC, msg);
00426     }
00427 
00428     // Allocate sky direction
00429     GSkyDir dir;
00430 
00431     // Debug option: initialise counter
00432     #if defined(G_DEBUG_MC)
00433     int num_iterations = 0;
00434     #endif
00435 
00436     // Get sky direction
00437     while (true) {
00438 
00439         // Debug option: increment counter
00440         #if defined(G_DEBUG_MC)
00441         num_iterations++;
00442         #endif
00443 
00444         // Simulate random sky direction within Monte Carlo simulation cone
00445         double theta = std::acos(1.0 - ran.uniform() * m_mc_one_minus_cosrad) *
00446                        gammalib::rad2deg;
00447         double phi   = 360.0 * ran.uniform();
00448         dir = m_mc_centre;
00449         dir.rotate_deg(phi, theta);
00450 
00451         // Get map value at simulated sky direction
00452         double value       = 0.0;
00453         double value_left  = m_cube(dir, m_logE.inx_left());
00454         double value_right = m_cube(dir, m_logE.inx_right());
00455         if (value_left > 0.0 && value_right > 0.0) {
00456             double value_log = m_logE.wgt_left()  * std::log(value_left) +
00457                                m_logE.wgt_right() * std::log(value_right);
00458             value = std::exp(value_log);
00459         }
00460         else if (value_left > 0.0) {
00461             value = value_left;
00462         }
00463         else if (value_right > 0.0) {
00464             value = value_right;
00465         }
00466 
00467         // If map value is non-positive then simulate a new sky direction
00468         if (value <= 0.0) {
00469             continue;
00470         }
00471 
00472         // Get uniform random number
00473         double uniform = ran.uniform() * max;
00474 
00475         // Exit loop if the random number is not larger than the map cube value
00476         if (uniform <= value) {
00477             break;
00478         }
00479 
00480     } // endwhile: loop until sky direction was accepted
00481 
00482     // Debug option: log counter
00483     #if defined(G_DEBUG_MC)
00484     std::cout << num_iterations << " ";
00485     #endif
00486 
00487     // Return sky direction
00488     return dir;
00489 }
00490 
00491 
00492 /***********************************************************************//**
00493  * @brief Signals whether model contains sky direction
00494  *
00495  * @param[in] dir Sky direction.
00496  * @param[in] margin Margin to be added to sky direction (degrees)
00497  * @return True if the model contains the sky direction.
00498  *
00499  * Signals whether a sky direction falls within the bounding circle of
00500  * the diffuse cube.
00501  *
00502  * @todo To be implemented.
00503  ***************************************************************************/
00504 bool GModelSpatialDiffuseCube::contains(const GSkyDir& dir,
00505                                         const double&  margin) const
00506 {
00507     return (true);
00508 }
00509 
00510 
00511 /***********************************************************************//**
00512  * @brief Read model from XML element
00513  *
00514  * @param[in] xml XML element.
00515  *
00516  * @exception GException::invalid_value
00517  *            Model parameters not found in XML element.
00518  *
00519  * Read the map cube information from an XML element. The XML element should
00520  * have the format
00521  *
00522  *     <spatialModel type="DiffuseMapCube" file="test_file.fits">
00523  *       <parameter name="Normalization" ../>
00524  *     </spatialModel>
00525  ***************************************************************************/
00526 void GModelSpatialDiffuseCube::read(const GXmlElement& xml)
00527 {
00528     // If "Normalization" parameter exists then read parameter from this
00529     // XML element
00530     if (gammalib::xml_has_par(xml, "Normalization")) {
00531         const GXmlElement* par = gammalib::xml_get_par(G_READ, xml, "Normalization");
00532         m_value.read(*par);
00533     }
00534 
00535     // ... otherwise try reading parameter from "Value" parameter
00536     #if defined(G_LEGACY_XML_FORMAT)
00537     else if (gammalib::xml_has_par(xml, "Value")) {
00538         const GXmlElement* par = gammalib::xml_get_par(G_READ, xml, "Value");
00539         m_value.read(*par);
00540     }
00541     #endif
00542 
00543     // ... otherwise throw an exception
00544     else {
00545         #if defined(G_LEGACY_XML_FORMAT)
00546         std::string msg = "Diffuse map cube model requires either "
00547                           "\"Normalization\" or \"Value\" parameter.";
00548         #else
00549         std::string msg = "Diffuse map cube model requires \"Normalization\" "
00550                           "parameter.";
00551         #endif
00552         throw GException::invalid_value(G_READ, msg);
00553     }
00554 
00555     // Save filename
00556     m_filename = gammalib::xml_file_expand(xml, xml.attribute("file"));
00557 
00558     // Return
00559     return;
00560 }
00561 
00562 
00563 /***********************************************************************//**
00564  * @brief Write model into XML element
00565  *
00566  * @param[in] xml XML element.
00567  *
00568  * @exception GException::model_invalid_spatial
00569  *            Existing XML element is not of type "MapCubeFunction"
00570  * @exception GException::model_invalid_parnum
00571  *            Invalid number of model parameters found in XML element.
00572  * @exception GException::model_invalid_parnames
00573  *            Invalid model parameter names found in XML element.
00574  *
00575  * Write the map cube information into an XML element. The XML element will
00576  * have either the format
00577  *
00578  *     <spatialModel type="DiffuseMapCube" file="test_file.fits">
00579  *       <parameter name="Normalization" scale="1" value="1" min="0.1" max="10" free="0"/>
00580  *     </spatialModel>
00581  *
00582  * or alternatively
00583  *
00584  *     <spatialModel type="DiffuseMapCube" file="test_file.fits">
00585  *       <parameter name="Value" scale="1" value="1" min="0.1" max="10" free="0"/>
00586  *     </spatialModel>
00587  *
00588  * The latter format is the default for newly written XML elements. 
00589  ***************************************************************************/
00590 void GModelSpatialDiffuseCube::write(GXmlElement& xml) const
00591 {
00592     // Set model type
00593     if (xml.attribute("type") == "") {
00594         xml.attribute("type", type());
00595     }
00596 
00597     // Verify model type
00598     if (xml.attribute("type") != type()) {
00599         throw GException::model_invalid_spatial(G_WRITE, xml.attribute("type"),
00600               "Spatial model is not of type \""+type()+"\".");
00601     }
00602 
00603     // If XML element has 0 nodes then append parameter node. The name
00604     // of the node is "Normalization" as this is the Fermi-LAT standard.
00605     // We thus assure that the XML files will be compatible with
00606     // Fermi-LAT.
00607     if (xml.elements() == 0) {
00608         xml.append(GXmlElement("parameter name=\"Normalization\""));
00609     }
00610 
00611     // Verify that XML element has exactly 1 parameter
00612     if (xml.elements() != 1 || xml.elements("parameter") != 1) {
00613         throw GException::model_invalid_parnum(G_WRITE, xml,
00614               "Map cube spatial model requires exactly 1 parameter.");
00615     }
00616 
00617     // Get pointers on model parameter
00618     GXmlElement* par = xml.element("parameter", 0);
00619 
00620     // Set or update parameter
00621     if (par->attribute("name") == "Normalization" ||
00622         par->attribute("name") == "Value") {
00623         m_value.write(*par);
00624     }
00625     else {
00626         throw GException::model_invalid_parnames(G_WRITE, xml,
00627               "Map cube spatial model requires either \"Value\" or"
00628               " \"Normalization\" parameter.");
00629     }
00630 
00631     // Set filename
00632     //xml.attribute("file", m_filename);
00633     xml.attribute("file", gammalib::xml_file_reduce(xml, m_filename));
00634 
00635     // Return
00636     return;
00637 }
00638 
00639 
00640 /***********************************************************************//**
00641  * @brief Set map cube
00642  *
00643  * @param[in] cube Sky map.
00644  *
00645  * Set the map cube of the spatial map cube model.
00646  ***************************************************************************/
00647 void GModelSpatialDiffuseCube::cube(const GSkyMap& cube)
00648 {
00649     // Clear filename and signal map as unloaded
00650     m_filename.clear();
00651     m_loaded = false;
00652 
00653     // Assign map
00654     m_cube = cube;
00655 
00656     // Update Monte Carlo cache
00657     update_mc_cache();
00658 
00659     // Return
00660     return;
00661 }
00662 
00663 
00664 /***********************************************************************//**
00665  * @brief Set energies for map cube
00666  *
00667  * @param[in] energies Sky map energies.
00668  *
00669  * @exception GException::invalid_argument
00670  *            Specified sky map energies incompatible with map cube.
00671  *
00672  * Sets the energies for the map cube.
00673  ***************************************************************************/
00674 void GModelSpatialDiffuseCube::energies(const GEnergies& energies)
00675 {
00676     // Initialise energies
00677     m_logE.clear();
00678 
00679     // Fetch cube
00680     fetch_cube();
00681 
00682     // Extract number of energies in vector
00683     int num = energies.size();
00684 
00685     // Check if energy binning is consistent with number of maps in the cube
00686     if (num != m_cube.nmaps() ) {
00687         std::string msg = "Number of specified energies ("+gammalib::str(num)+")"
00688                           " does not match the number of maps ("
00689                           ""+gammalib::str(m_cube.nmaps())+" in the map cube.\n"
00690                           "The energies argument shall provide a vector of length"
00691                           " "+gammalib::str(m_cube.nmaps())+".";
00692         throw GException::invalid_argument(G_ENERGIES, msg);
00693     }
00694 
00695     // Set log10(energy) nodes, where energy is in units of MeV
00696     for (int i = 0; i < num; ++i) {
00697         m_logE.append(energies[i].log10MeV());
00698     }
00699 
00700     // Set energy boundaries
00701     set_energy_boundaries();
00702 
00703     // Update MC cache
00704     update_mc_cache();
00705 
00706     // Return
00707     return;
00708 }
00709 
00710 
00711 /***********************************************************************//**
00712  * @brief Returns map cube energies
00713  *
00714  * @return Map cube energies.
00715  *
00716  * Returns the energies for the map cube in a vector.
00717  ***************************************************************************/
00718 GEnergies GModelSpatialDiffuseCube::energies(void)
00719 {
00720     // Initialise energies container
00721     GEnergies energies;
00722 
00723     // Fetch cube
00724     fetch_cube();
00725 
00726     // Get number of map energies
00727     int num = m_logE.size();
00728 
00729     // Continue only if there are maps in the cube
00730     if (num > 0) {
00731 
00732         // Reserve space for all energies
00733         energies.reserve(num);
00734 
00735         // Set log10(energy) nodes, where energy is in units of MeV
00736         for (int i = 0; i < num; ++i) {
00737             GEnergy energy;
00738             energy.log10MeV(m_logE[i]);
00739             energies.append(energy);
00740         }
00741 
00742     } // endif: there were maps in the cube
00743 
00744     // Return energies
00745     return energies;
00746 }
00747 
00748 
00749 /***********************************************************************//**
00750  * @brief Set Monte Carlo simulation cone
00751  *
00752  * @param[in] centre Simulation cone centre.
00753  * @param[in] radius Simulation cone radius (degrees).
00754  *
00755  * Sets the simulation cone centre and radius that defines the directions
00756  * that will be simulated using the mc() method and pre-computes the maximum
00757  * intensity and the spatially integrated flux of each map within the
00758  * simulation cone region.
00759  ***************************************************************************/
00760 void GModelSpatialDiffuseCube::set_mc_cone(const GSkyDir& centre,
00761                                            const double&  radius) const
00762 {
00763     // Continue only if the simulation cone has changed
00764     if ((centre != m_mc_centre) || (radius != m_mc_radius)) {
00765 
00766         // Save simulation cone definition
00767         m_mc_centre = centre;
00768         m_mc_radius = radius;
00769 
00770         // Pre-compute 1 - cosine of radius
00771         m_mc_one_minus_cosrad = 1.0 - std::cos(m_mc_radius*gammalib::deg2rad);
00772 
00773         // Initialise Monte Carlo cache
00774         m_mc_max.clear();
00775         m_mc_spectrum.clear();
00776 
00777         // Fetch cube
00778         fetch_cube();
00779 
00780         // Determine number of cube pixels and maps
00781         int npix  = pixels();
00782         int nmaps = maps();
00783 
00784         // Continue only if there are pixels and maps
00785         if (npix > 0 && nmaps > 0) {
00786 
00787             // Reserve space in cache
00788             m_mc_max.reserve(nmaps);
00789             m_mc_spectrum.reserve(nmaps);
00790 
00791             // Loop over all maps
00792             for (int i = 0; i < nmaps; ++i) {
00793 
00794                 // Compute flux and maximum map intensity within the
00795                 // simulation cone
00796                 double total_flux    = 0.0;
00797                 double max_intensity = 0.0;
00798                 for (int k = 0; k < npix; ++k) {
00799                     double distance = centre.dist_deg(m_cube.pix2dir(k));
00800                     if (distance <= radius) {
00801                         double flux = m_cube.flux(k,i);
00802                         if (flux > 0.0) {
00803                             total_flux += flux;
00804                         }
00805                         double value = m_cube(k,i);
00806                         if (value > max_intensity) {
00807                             max_intensity = value;
00808                         }
00809                     }
00810                 }
00811 
00812                 // Store maximum map intensity
00813                 m_mc_max.push_back(max_intensity);
00814 
00815                 // Store flux as spectral node
00816                 if (m_logE.size() == nmaps) {
00817 
00818                     // Set map energy
00819                     GEnergy energy;
00820                     energy.log10MeV(m_logE[i]);
00821 
00822                     // Only append node if the integrated flux is positive
00823                     // (as we do a log-log interpolation)
00824                     if (total_flux > 0.0) {
00825                         m_mc_spectrum.append(energy, total_flux);
00826                     }
00827 
00828                 }
00829 
00830             } // endfor: looped over all maps
00831 
00832             // Log maximum intensity and total flux for debugging
00833             #if defined(G_DEBUG_MC_CACHE)
00834             std::cout << "GModelSpatialDiffuseCube::set_mc_cone:" << std::endl;
00835             std::cout << "  Maximum map intensity:" << std::endl;
00836             for (int i = 0; i < m_mc_max.size(); ++i) {
00837                 GEnergy energy;
00838                 energy.log10MeV(m_logE[i]);
00839                 std::cout << "  " << i;
00840                 std::cout << " " << energy;
00841                 std::cout << " " << m_mc_max[i] << " ph/cm2/s/sr/MeV";
00842                 std::cout << std::endl;
00843             }
00844             std::cout << "  Spatially integrated flux:" << std::endl;
00845             for (int i = 0; i < m_mc_spectrum.nodes(); ++i) {
00846                 std::cout << "  " << i;
00847                 std::cout << " " << m_mc_spectrum.energy(i);
00848                 std::cout << " " << m_mc_spectrum.intensity(i);
00849                 std::cout << " ph/cm2/s/MeV" << std::endl;
00850             }
00851             #endif
00852 
00853         } // endif: there were cube pixels and maps
00854 
00855     } // endif: simulation cone has changed
00856 
00857     // Return
00858     return;
00859 }
00860 
00861 
00862 /***********************************************************************//**
00863  * @brief Load cube into the model class
00864  *
00865  * @param[in] filename cube file.
00866  *
00867  * Loads cube into the model class.
00868  ***************************************************************************/
00869 void GModelSpatialDiffuseCube::load(const GFilename& filename)
00870 {
00871     // Load cube
00872     load_cube(filename);
00873 
00874     // Update Monte Carlo cache
00875     update_mc_cache();
00876 
00877     // Return
00878     return;
00879 }
00880 
00881 
00882 /***********************************************************************//**
00883  * @brief Save cube into FITS file
00884  *
00885  * @param[in] filename Cube FITS file name.
00886  * @param[in] clobber Overwrite existing FITS file (default: false).
00887  *
00888  * Saves spatial cube model into a FITS file.
00889  ***************************************************************************/
00890 void GModelSpatialDiffuseCube::save(const GFilename& filename,
00891                                     const bool&      clobber) const
00892 {
00893     // Create empty FITS file
00894     GFits fits;
00895 
00896     // Write event cube
00897     write(fits);
00898 
00899     // Save FITS file
00900     fits.saveto(filename, clobber);
00901 
00902     // Return
00903     return;
00904 }
00905 
00906 
00907 /***********************************************************************//**
00908  * @brief Write skymap into FITS file
00909  *
00910  * @param[in] file FITS file pointer.
00911  ***************************************************************************/
00912 void GModelSpatialDiffuseCube::write(GFits& file) const
00913 {
00914     // Write cube
00915     m_cube.write(file);
00916 
00917     // Create energy intervals
00918     GEnergies energies;
00919     for (int i = 0; i < m_logE.size(); ++i) {
00920         double energy = std::pow(10.0, m_logE[i]);
00921         energies.append(GEnergy(energy, "MeV"));
00922     }
00923 
00924     // Write energy intervals
00925     energies.write(file);
00926 
00927     // Return
00928     return;
00929 }
00930 
00931 
00932 /***********************************************************************//**
00933  * @brief Print map cube information
00934  *
00935  * @param[in] chatter Chattiness (defaults to NORMAL).
00936  * @return String containing model information.
00937  ***************************************************************************/
00938 std::string GModelSpatialDiffuseCube::print(const GChatter& chatter) const
00939 {
00940     // Initialise result string
00941     std::string result;
00942 
00943     // Continue only if chatter is not silent
00944     if (chatter != SILENT) {
00945 
00946         // Append header
00947         result.append("=== GModelSpatialDiffuseCube ===");
00948 
00949         // Append parameters
00950         result.append("\n"+gammalib::parformat("Map cube file"));
00951         result.append(m_filename);
00952         if (m_loaded) {
00953             result.append(" [loaded]");
00954         }
00955         result.append("\n"+gammalib::parformat("Number of parameters"));
00956         result.append(gammalib::str(size()));
00957         for (int i = 0; i < size(); ++i) {
00958             result.append("\n"+m_pars[i]->print(chatter));
00959         }
00960 
00961         // Append detailed information only if a map cube exists
00962         if (m_cube.npix() > 0) {
00963 
00964             // NORMAL: Append sky map
00965             if (chatter >= NORMAL) {
00966                 result.append("\n"+m_cube.print(chatter));
00967             }
00968 
00969             // EXPLICIT: Append energy nodes
00970             if (chatter >= EXPLICIT && m_logE.size() > 0) {
00971                 result.append("\n"+gammalib::parformat("Map energy values"));
00972                 if (m_logE.size() > 0) {
00973                     for (int i = 0; i < m_logE.size(); ++i) {
00974                         result.append("\n"+gammalib::parformat("  Map "+gammalib::str(i+1)));
00975                         result.append(gammalib::str(std::pow(10.0, m_logE[i])));
00976                         result.append(" MeV (log10E=");
00977                         result.append(gammalib::str(m_logE[i]));
00978                         result.append(")");
00979                         if (m_ebounds.size() == m_logE.size()) {
00980                             result.append(" [");
00981                             result.append(m_ebounds.emin(i).print());
00982                             result.append(", ");
00983                             result.append(m_ebounds.emax(i).print());
00984                             result.append("]");
00985                         }
00986                     }
00987                 }
00988                 else {
00989                     result.append("not specified");
00990                 }
00991             }
00992 
00993             // VERBOSE: Append MC cache
00994             if (chatter >= VERBOSE) {
00995                 result.append("\n"+gammalib::parformat("Map flux"));
00996                 if (m_mc_spectrum.nodes() > 0) {
00997                     for (int i = 0; i < m_mc_spectrum.nodes(); ++i) {
00998                         result.append("\n"+gammalib::parformat("  Map "+gammalib::str(i+1)));
00999                         result.append(gammalib::str(m_mc_spectrum.intensity(i)));
01000                     }
01001                 }
01002                 else {
01003                     result.append("not specified");
01004                 }
01005             }
01006 
01007         } // endif: map cube exists
01008 
01009     } // endif: chatter was not silent
01010 
01011     // Return result
01012     return result;
01013 }
01014 
01015 
01016 /*==========================================================================
01017  =                                                                         =
01018  =                            Private methods                              =
01019  =                                                                         =
01020  ==========================================================================*/
01021 
01022 /***********************************************************************//**
01023  * @brief Initialise class members
01024  ***************************************************************************/
01025 void GModelSpatialDiffuseCube::init_members(void)
01026 {
01027     // Initialise model type
01028     m_type = "DiffuseMapCube";
01029 
01030     // Initialise Value
01031     m_value.clear();
01032     m_value.name("Normalization");
01033     m_value.value(1.0);
01034     m_value.scale(1.0);
01035     m_value.range(0.001, 1000.0);
01036     m_value.gradient(0.0);
01037     m_value.fix();
01038     m_value.has_grad(true);
01039 
01040     // Set parameter pointer(s)
01041     m_pars.clear();
01042     m_pars.push_back(&m_value);
01043 
01044     // Initialise other members
01045     m_filename.clear();
01046     m_cube.clear();
01047     m_logE.clear();
01048     m_ebounds.clear();
01049     m_loaded = false;
01050     m_region.clear();
01051 
01052     // Initialise MC cache
01053     m_mc_centre.clear();
01054     m_mc_radius           = -1.0;    //!< Signal that initialisation is needed
01055     m_mc_one_minus_cosrad =  1.0;
01056     m_mc_max.clear();
01057     m_mc_spectrum.clear();
01058 
01059     // Return
01060     return;
01061 }
01062 
01063 
01064 /***********************************************************************//**
01065  * @brief Copy class members
01066  *
01067  * @param[in] model Spatial map cube model.
01068  ***************************************************************************/
01069 void GModelSpatialDiffuseCube::copy_members(const GModelSpatialDiffuseCube& model)
01070 {
01071     // Copy members
01072     m_type     = model.m_type;
01073     m_value    = model.m_value;
01074     m_filename = model.m_filename;
01075     m_cube     = model.m_cube;
01076     m_logE     = model.m_logE;
01077     m_ebounds  = model.m_ebounds;
01078     m_loaded   = model.m_loaded;
01079     m_region   = model.m_region;
01080 
01081     // Copy MC cache
01082     m_mc_centre           = model.m_mc_centre;
01083     m_mc_radius           = model.m_mc_radius;
01084     m_mc_one_minus_cosrad = model.m_mc_one_minus_cosrad;
01085     m_mc_max              = model.m_mc_max;
01086     m_mc_spectrum         = model.m_mc_spectrum;
01087 
01088     // Set parameter pointer(s)
01089     m_pars.clear();
01090     m_pars.push_back(&m_value);
01091 
01092     // Return
01093     return;
01094 }
01095 
01096 
01097 /***********************************************************************//**
01098  * @brief Delete class members
01099  ***************************************************************************/
01100 void GModelSpatialDiffuseCube::free_members(void)
01101 {
01102     // Return
01103     return;
01104 }
01105 
01106 
01107 /***********************************************************************//**
01108  * @brief Fetch cube
01109  *
01110  * Load diffuse cube if it is not yet loaded. The loading is thread save.
01111  ***************************************************************************/
01112 void GModelSpatialDiffuseCube::fetch_cube(void) const
01113 {
01114     // Load cube if it is not yet loaded
01115     if (!m_loaded && !m_filename.is_empty()) {
01116 
01117         // Put in a OMP critical zone
01118         #pragma omp critical(GModelSpatialDiffuseCube_fetch_cube)
01119         {
01120             const_cast<GModelSpatialDiffuseCube*>(this)->load_cube(m_filename);
01121         } // end of pragma
01122 
01123     } // endif: file not
01124 
01125     // Return
01126     return;
01127 }
01128 
01129 
01130 /***********************************************************************//**
01131  * @brief Load cube
01132  *
01133  * @param[in] filename Cube file.
01134  *
01135  * @exception GException::invalid_value
01136  *            Number of maps in cube mismatches number of energy bins.
01137  *
01138  * Load diffuse cube.
01139  ***************************************************************************/
01140 void GModelSpatialDiffuseCube::load_cube(const GFilename& filename)
01141 {
01142     // Initialise skymap
01143     m_cube.clear();
01144     m_logE.clear();
01145 
01146     // Store filename of cube (for XML writing). Note that we do
01147     // not expand any environment variable at this level, so that
01148     // if we write back the XML element we write the filepath with
01149     // the environment variables
01150     m_filename = filename;
01151 
01152     // Load cube
01153     m_cube.load(filename);
01154 
01155     // Load energies
01156     GEnergies energies(filename);
01157 
01158     // Extract number of energy bins
01159     int num = energies.size();
01160 
01161     // Check if energy binning is consistent with primary image hdu
01162     if (num != m_cube.nmaps() ) {
01163         std::string msg = "Number of energies in \"ENERGIES\""
01164                           " extension ("+gammalib::str(num)+")"
01165                           " does not match the number of maps ("+
01166                           gammalib::str(m_cube.nmaps())+" in the"
01167                           " map cube.\n"
01168                           "The \"ENERGIES\" extension table shall"
01169                           " provide one enegy value for each map"
01170                           " in the cube.";
01171         throw GException::invalid_value(G_LOAD_CUBE, msg);
01172     }
01173 
01174     // Set log10(energy) nodes, where energy is in units of MeV
01175     for (int i = 0; i < num; ++i) {
01176         m_logE.append(energies[i].log10MeV());
01177     }
01178 
01179     // Signal that cube has been loaded
01180     m_loaded = true;
01181 
01182     // Set energy boundaries
01183     set_energy_boundaries();
01184 
01185     // Return
01186     return;
01187 }
01188 
01189 
01190 /***********************************************************************//**
01191  * @brief Set energy boundaries
01192  *
01193  * Computes energy boundaries from the energy nodes. The boundaries will be
01194  * computed as the centre in log10(energy) between the nodes. If only a
01195  * single map exists, no boundaries will be computed.
01196  ***************************************************************************/
01197 void GModelSpatialDiffuseCube::set_energy_boundaries(void)
01198 {
01199     // Initialise energy boundaries
01200     m_ebounds.clear();
01201 
01202     // Determine number of energy bins
01203     int num = m_logE.size();
01204 
01205     // Continue only if there are at least two energy nodes
01206     if (num > 1) {
01207 
01208         // Loop over all nodes
01209         for (int i = 0; i < num; ++i) {
01210 
01211             // Calculate minimum energy
01212             double e_min = (i == 0) ? m_logE[i] - 0.5 * (m_logE[i+1] - m_logE[i])
01213                                     : m_logE[i] - 0.5 * (m_logE[i] - m_logE[i-1]);
01214 
01215             // Calculate maximum energy
01216             double e_max = (i < num-1) ? m_logE[i] + 0.5 * (m_logE[i+1] - m_logE[i])
01217                                        : m_logE[i] + 0.5 * (m_logE[i] - m_logE[i-1]);
01218 
01219             // Set energy boundaries
01220             GEnergy emin;
01221             GEnergy emax;
01222             emin.log10MeV(e_min);
01223             emax.log10MeV(e_max);
01224 
01225             // Append energy bin to energy boundary arra
01226             m_ebounds.append(emin, emax);
01227 
01228         } // endfor: looped over energy nodes
01229 
01230     } // endif: there were at least two energy nodes
01231 
01232     // Return
01233     return;
01234 }
01235 
01236 
01237 /***********************************************************************//**
01238  * @brief Update Monte Carlo cache
01239  *
01240  * Initialise the cache for Monte Carlo sampling of the map cube. The Monte
01241  * Carlo cache consists of a linear array that maps a value between 0 and 1
01242  * into the skymap pixel for all maps in the cube.
01243  ***************************************************************************/
01244 void GModelSpatialDiffuseCube::update_mc_cache(void)
01245 {
01246     // Set centre and radius to all sky
01247     GSkyDir centre;
01248     double  radius = 360.0;
01249 
01250     // Compute cache
01251     set_mc_cone(centre, radius);
01252 
01253     // Return
01254     return;
01255 }
01256 
01257 
01258 /***********************************************************************//**
01259  * @brief Compute cube intensity by log-log interpolation
01260  *
01261  * @param[in] photon Incident photon.
01262  * @return Cube intensity value.
01263  ***************************************************************************/
01264 double GModelSpatialDiffuseCube::cube_intensity(const GPhoton& photon) const
01265 {
01266     // Fetch cube
01267     fetch_cube();
01268 
01269     // Initialise intensity value
01270     double intensity = 0.0;
01271 
01272     // Continue only if there is energy information for the map cube
01273     if (m_logE.size() > 0) {
01274 
01275         // Set energy for interpolation in log-energy
01276         m_logE.set_value(photon.energy().log10MeV());
01277 
01278         // Compute map cube intensity for the left and right map
01279         double left_intensity  = m_cube(photon.dir(), m_logE.inx_left());
01280         double right_intensity = m_cube(photon.dir(), m_logE.inx_right());
01281 
01282         // Perform log-log interpolation
01283         if (left_intensity > 0.0 && right_intensity > 0.0) {
01284             double log_intensity = m_logE.wgt_left()  * std::log(left_intensity) +
01285                                    m_logE.wgt_right() * std::log(right_intensity);
01286             intensity = std::exp(log_intensity);
01287         }
01288         else if (left_intensity > 0.0) {
01289             intensity = left_intensity;
01290         }
01291         else if (right_intensity > 0.0) {
01292             intensity = right_intensity;
01293         }
01294 
01295     } // endif: energy information was available
01296 
01297     // Return intensity
01298     return intensity;
01299 }
01300 
01301 
01302 /***********************************************************************//**
01303  * @brief Set boundary sky region
01304  *
01305  * @todo Implement determination of the cube boundary circle
01306  ***************************************************************************/
01307 void GModelSpatialDiffuseCube::set_region(void) const
01308 {
01309     // Set sky region centre to (0,0)
01310     m_region.centre(0.0, 0.0);
01311 
01312     // Set sky region radius to 180 degrees (all points included)
01313     m_region.radius(180.0);
01314 
01315     // Return
01316     return;
01317 }

Generated on Tue Jan 24 12:37:23 2017 for GammaLib by  doxygen 1.4.7