Commit 3a334a8c1ad43a639fd3602be4d3d18195e29094

Authored by Jürgen Knödlseder
2 parents 6ea53921 b84e41a8

Merge branch 'issue_1014_gradients' into integration

include/GModelSpectralGauss.hpp
@@ -96,7 +96,7 @@ public: @@ -96,7 +96,7 @@ public:
96 void mean(const GEnergy& mean); 96 void mean(const GEnergy& mean);
97 GEnergy sigma(void) const; 97 GEnergy sigma(void) const;
98 void sigma(const GEnergy& sigma); 98 void sigma(const GEnergy& sigma);
99 - 99 + void update_eval_cache(const GEnergy& energy) const;
100 100
101 101
102 protected: 102 protected:
@@ -104,6 +104,7 @@ protected: @@ -104,6 +104,7 @@ protected:
104 void init_members(void); 104 void init_members(void);
105 void copy_members(const GModelSpectralGauss& model); 105 void copy_members(const GModelSpectralGauss& model);
106 void free_members(void); 106 void free_members(void);
  107 + void update_eval_cache(const GEnergy& energy) const;
107 108
108 // Energy flux integration kernel 109 // Energy flux integration kernel
109 class eflux_kernel : public GFunction { 110 class eflux_kernel : public GFunction {
@@ -125,6 +126,10 @@ protected: @@ -125,6 +126,10 @@ protected:
125 GModelPar m_norm; //!< Normalization factor 126 GModelPar m_norm; //!< Normalization factor
126 GModelPar m_mean; //!< Gaussian mean energy 127 GModelPar m_mean; //!< Gaussian mean energy
127 GModelPar m_sigma; //!< Gaussian energy width 128 GModelPar m_sigma; //!< Gaussian energy width
  129 +
  130 + mutable double m_last_norm; //!< Last Normalization value
  131 + mutable double m_last_mean; //!< Last Mean value
  132 + mutable double m_last_sigma; //!< Last Sigma value
128 }; 133 };
129 134
130 135
@@ -218,4 +223,5 @@ void GModelSpectralGauss::sigma(const GEnergy&amp; sigma) @@ -218,4 +223,5 @@ void GModelSpectralGauss::sigma(const GEnergy&amp; sigma)
218 return; 223 return;
219 } 224 }
220 225
  226 +
221 #endif /* GMODELSPECTRALGAUSS_HPP */ 227 #endif /* GMODELSPECTRALGAUSS_HPP */
src/model/GModelSpectralGauss.cpp
@@ -240,8 +240,7 @@ GModelSpectralGauss* GModelSpectralGauss::clone(void) const @@ -240,8 +240,7 @@ GModelSpectralGauss* GModelSpectralGauss::clone(void) const
240 * Evaluates 240 * Evaluates
241 * 241 *
242 * \f[ 242 * \f[
243 - * \frac{dN}{dE} = \frac{\norm}{\sqrt{2\pi}\sigma}  
244 - * \exp(\frac{(E-\bar{E})^2}{2\sigma^2}) 243 + * \frac{dN}{dE}=\frac{\norm}{\sqrt{2\pi}\sigma}\exp(\frac{-(E-\bar{E})^2}{2\sigma^2})
245 * \f] 244 * \f]
246 ***************************************************************************/ 245 ***************************************************************************/
247 double GModelSpectralGauss::eval(const GEnergy& srcEng, 246 double GModelSpectralGauss::eval(const GEnergy& srcEng,
@@ -271,12 +270,62 @@ double GModelSpectralGauss::eval(const GEnergy&amp; srcEng, @@ -271,12 +270,62 @@ double GModelSpectralGauss::eval(const GEnergy&amp; srcEng,
271 * 270 *
272 * This method simply calls the eval() method as no analytical gradients will 271 * This method simply calls the eval() method as no analytical gradients will
273 * be computed. See the eval() method for details. 272 * be computed. See the eval() method for details.
  273 + *
  274 + * TODO: update docstring.
274 ***************************************************************************/ 275 ***************************************************************************/
275 double GModelSpectralGauss::eval_gradients(const GEnergy& srcEng, 276 double GModelSpectralGauss::eval_gradients(const GEnergy& srcEng,
276 const GTime& srcTime) 277 const GTime& srcTime)
277 { 278 {
278 - // Return  
279 - return eval(srcEng, srcTime); 279 + double energy = srcEng.MeV();
  280 + double norm = m_norm.value();
  281 + double mean = m_mean.value();
  282 + double sigma = m_sigma.value();
  283 +
  284 + // Update the evaluation cache
  285 + update_eval_cache(srcEng);
  286 +
  287 + // Compute function terms
  288 + double term1 = (norm / sigma) * gammalib::inv_sqrt2pi;
  289 + double term2 = (1 / sigma) * gammalib::inv_sqrt2pi;
  290 + double term3 = (energy - mean) * (energy - mean) / (2 * sigma * sigma);
  291 + double term4 = (energy - mean) / (sigma * sigma);
  292 + double term5 = (norm / (sigma * sigma)) * gammalib::inv_sqrt2pi;
  293 +
  294 + // Compute function value
  295 + double value = term1 * std::exp(- term3);
  296 +
  297 +
  298 + // Compute partial derivatives with respect to the parameter factor
  299 + // values (partial differentials were determined analytically).
  300 +
  301 + double g_norm = term2 * std::exp(- term3);
  302 + double g_mean = term1 * term4 * std::exp(- term3);
  303 + double g_sigma = - term5 * std::exp(- term3) * (1 - (2 * term3));
  304 +
  305 + // Set gradients
  306 + m_norm.factor_gradient(g_norm);
  307 + m_mean.factor_gradient(g_mean);
  308 + m_sigma.factor_gradient(g_sigma);
  309 +
  310 + // Compile option: Check for NaN/Inf
  311 + #if defined(G_NAN_CHECK)
  312 + if (gammalib::is_notanumber(value) || gammalib::is_infinite(value)) {
  313 + std::cout << "*** ERROR: GModelSpectralGauss::eval_gradients";
  314 + std::cout << "(srcEng=" << srcEng;
  315 + std::cout << ", srcTime=" << srcTime << "):";
  316 + std::cout << " NaN/Inf encountered";
  317 + std::cout << " (value=" << value;
  318 + std::cout << ", norm=" << m_last_norm;
  319 + std::cout << ", mean=" << m_last_mean;
  320 + std::cout << ", Sigma=" << m_last_sigma;
  321 + std::cout << ")" << std::endl;
  322 + }
  323 + #endif
  324 +
  325 + // Return
  326 + return value;
  327 + }
  328 +
280 } 329 }
281 330
282 331
@@ -298,6 +347,7 @@ double GModelSpectralGauss::eval_gradients(const GEnergy&amp; srcEng, @@ -298,6 +347,7 @@ double GModelSpectralGauss::eval_gradients(const GEnergy&amp; srcEng,
298 * - \f$S_{\rm E}(E | t)\f$ is the spectral model (ph/cm2/s/MeV). 347 * - \f$S_{\rm E}(E | t)\f$ is the spectral model (ph/cm2/s/MeV).
299 * The integration is done analytically. 348 * The integration is done analytically.
300 ***************************************************************************/ 349 ***************************************************************************/
  350 +
301 double GModelSpectralGauss::flux(const GEnergy& emin, 351 double GModelSpectralGauss::flux(const GEnergy& emin,
302 const GEnergy& emax) const 352 const GEnergy& emax) const
303 { 353 {
@@ -412,15 +462,14 @@ GEnergy GModelSpectralGauss::mc(const GEnergy&amp; emin, @@ -412,15 +462,14 @@ GEnergy GModelSpectralGauss::mc(const GEnergy&amp; emin,
412 } 462 }
413 463
414 // Get uniform value between 0 and 1 464 // Get uniform value between 0 and 1
415 - double u = ran.uniform();  
416 465
417 // Do ... 466 // Do ...
418 do { 467 do {
419 double x1; 468 double x1;
420 double w; 469 double w;
421 do{ 470 do{
422 - x1 = 2.0 * u - 1.0;  
423 - double x2 = 2.0 * u - 1.0; 471 + x1 = 2.0 * ran.uniform() - 1.0;
  472 + double x2 = 2.0 * ran.uniform() - 1.0;
424 w = x1 * x1 + x2 * x2; 473 w = x1 * x1 + x2 * x2;
425 } while (w >= 1.0); 474 } while (w >= 1.0);
426 475
@@ -431,7 +480,7 @@ GEnergy GModelSpectralGauss::mc(const GEnergy&amp; emin, @@ -431,7 +480,7 @@ GEnergy GModelSpectralGauss::mc(const GEnergy&amp; emin,
431 double val = x1 * w; 480 double val = x1 * w;
432 481
433 // Map into [emin,emax] range 482 // Map into [emin,emax] range
434 - energy = (xmax - xmin) * val + xmin; 483 + energy = m_sigma.value() * val + m_mean.value();
435 484
436 } while ((xmin <= energy) && (energy < xmax)); 485 } while ((xmin <= energy) && (energy < xmax));
437 486
@@ -439,7 +488,6 @@ GEnergy GModelSpectralGauss::mc(const GEnergy&amp; emin, @@ -439,7 +488,6 @@ GEnergy GModelSpectralGauss::mc(const GEnergy&amp; emin,
439 return GEnergy(energy, "MeV"); 488 return GEnergy(energy, "MeV");
440 } 489 }
441 490
442 -  
443 /***********************************************************************//** 491 /***********************************************************************//**
444 * @brief Read model from XML element 492 * @brief Read model from XML element
445 * 493 *
@@ -509,7 +557,6 @@ void GModelSpectralGauss::read(const GXmlElement&amp; xml) @@ -509,7 +557,6 @@ void GModelSpectralGauss::read(const GXmlElement&amp; xml)
509 return; 557 return;
510 } 558 }
511 559
512 -  
513 /***********************************************************************//** 560 /***********************************************************************//**
514 * @brief Write model into XML element 561 * @brief Write model into XML element
515 * 562 *
@@ -672,6 +719,11 @@ void GModelSpectralGauss::init_members(void) @@ -672,6 +719,11 @@ void GModelSpectralGauss::init_members(void)
672 m_sigma.gradient(0.0); 719 m_sigma.gradient(0.0);
673 m_sigma.has_grad(false); 720 m_sigma.has_grad(false);
674 721
  722 + // Initialise eval cache
  723 + m_last_norm = 0.0;
  724 + m_last_mean = 0.0;
  725 + m_last_sigma = 0.0;
  726 +
675 // Set parameter pointer(s) 727 // Set parameter pointer(s)
676 m_pars.clear(); 728 m_pars.clear();
677 m_pars.push_back(&m_norm); 729 m_pars.push_back(&m_norm);
@@ -701,6 +753,11 @@ void GModelSpectralGauss::copy_members(const GModelSpectralGauss&amp; model) @@ -701,6 +753,11 @@ void GModelSpectralGauss::copy_members(const GModelSpectralGauss&amp; model)
701 m_pars.push_back(&m_mean); 753 m_pars.push_back(&m_mean);
702 m_pars.push_back(&m_sigma); 754 m_pars.push_back(&m_sigma);
703 755
  756 + // Copy eval cache
  757 + m_last_norm = model.m_last_norm;
  758 + m_last_mean = model.m_last_mean;
  759 + m_last_sigma = model.m_last_sigma;
  760 +
704 // Return 761 // Return
705 return; 762 return;
706 } 763 }
@@ -730,3 +787,44 @@ double GModelSpectralGauss::eflux_kernel::eval(const double&amp; energy) @@ -730,3 +787,44 @@ double GModelSpectralGauss::eflux_kernel::eval(const double&amp; energy)
730 // Return value 787 // Return value
731 return value; 788 return value;
732 } 789 }
  790 +
  791 +/***********************************************************************//**
  792 + * @brief Update eval precomputation cache
  793 + *
  794 + * @param[in] energy Energy.
  795 + *
  796 + * Updates the precomputation cache for eval() and eval_gradients() methods.
  797 + *
  798 + ***************************************************************************/
  799 +void GModelSpectralGauss::update_eval_cache(const GEnergy& energy) const
  800 +{
  801 + // Get parameter values (takes 3 multiplications which are difficult
  802 + // to avoid)
  803 + double norm = m_norm.value();
  804 + double mean = m_mean.value();
  805 + double sigma = m_sigma.value();
  806 +
  807 + // If the energy or one of the parameters norm, mean or sigma
  808 + // energy has changed then recompute the cache
  809 + if ((m_last_norm != norm) ||
  810 + (m_last_mean != mean) ||
  811 + (m_last_sigma != sigma)) {
  812 +
  813 + // Store actual energy and parameter values
  814 + m_last_norm = norm;
  815 + m_last_mean = mean;
  816 + m_last_sigma = sigma;
  817 +
  818 + // Compute and store value
  819 + double eng = energy.MeV();
  820 + m_last_norm = eng / m_last_norm;
  821 + m_last_mean = eng / m_last_mean;
  822 + m_last_sigma = eng/ m_last_sigma;
  823 +
  824 + } // endif: recomputation was required
  825 +
  826 + // Return
  827 + return;
  828 +}
  829 +
  830 +