Commit 12c0b27caa6ffa31a4e315cc9a73bde1c898e687
1 parent
81862a72
Integrate analytical gradients for energy dispersion computation (#3333)
Showing
10 changed files
with
235 additions
and
78 deletions
ChangeLog
NEWS
| 1 | 1 | New Features and Important Changes in GammaLib 1.8.0 |
| 2 | 2 | |
| 3 | -4 September 2020 | |
| 3 | +11 September 2020 | |
| 4 | 4 | |
| 5 | 5 | |
| 6 | 6 | 1. Introduction |
| ... | ... | @@ -76,7 +76,14 @@ of functions (#3328). |
| 76 | 76 | |
| 77 | 77 | 10. Observation module |
| 78 | 78 | ---------------------- |
| 79 | -None | |
| 79 | +The GResponse::edisp_kern integration kernel class that is used for the integration | |
| 80 | +over true energies in case that energy dispersion should be considered now derives | |
| 81 | +from GFunctions. The actual function value is returned in the first slot of the | |
| 82 | +GNdarray returned by the GResponse::edisp_kern::eval() method, while the other slots | |
| 83 | +contain the analytical parameter gradients. The GIntegrals class is now used in the | |
| 84 | +GResponse::convolve() method to perform the true energy integration, integrating not | |
| 85 | +only the function but also any available parameter gradients (#3333). | |
| 86 | + | |
| 80 | 87 | |
| 81 | 88 | |
| 82 | 89 | 11. Optimization module | ... | ... |
README.md
| 1 | 1 | GammaLib information |
| 2 | 2 | ==================== |
| 3 | -* Version: 1.8.0.dev (4 September 2020) | |
| 3 | +* Version: 1.8.0.dev (11 September 2020) | |
| 4 | 4 | |
| 5 | 5 | [](https://cta-jenkins.irap.omp.eu/job/gammalib-integrate-os/) |
| 6 | 6 | ... | ... |
doc/source/admin/release_history/1.8.rst
| 1 | 1 | .. _1.8: |
| 2 | 2 | |
| 3 | -GammaLib 1.8 release (3 September 2020) | |
| 4 | -======================================= | |
| 3 | +GammaLib 1.8 release (11 September 2020) | |
| 4 | +======================================== | |
| 5 | 5 | |
| 6 | 6 | Overview |
| 7 | 7 | -------- |
| ... | ... | @@ -21,6 +21,8 @@ Bug fixes |
| 21 | 21 | Improvements |
| 22 | 22 | ------------ |
| 23 | 23 | |
| 24 | +* [`3333 <https://cta-redmine.irap.omp.eu/issues/3333>`_] - | |
| 25 | + Integrate analytical gradients for energy dispersion computation | |
| 24 | 26 | |
| 25 | 27 | New features |
| 26 | 28 | ------------ | ... | ... |
include/GIntegrals.hpp
include/GResponse.hpp
| ... | ... | @@ -33,6 +33,8 @@ |
| 33 | 33 | #include "GFunction.hpp" |
| 34 | 34 | #include "GTime.hpp" |
| 35 | 35 | #include "GResponseCache.hpp" |
| 36 | +#include "GFunctions.hpp" | |
| 37 | +#include "GNdarray.hpp" | |
| 36 | 38 | |
| 37 | 39 | /* __ Forward declarations _______________________________________________ */ |
| 38 | 40 | class GMatrix; |
| ... | ... | @@ -43,6 +45,7 @@ class GSkyDir; |
| 43 | 45 | class GEnergy; |
| 44 | 46 | class GEbounds; |
| 45 | 47 | class GObservation; |
| 48 | +class GModelPar; | |
| 46 | 49 | class GModelSky; |
| 47 | 50 | class GModelSpatialRadial; |
| 48 | 51 | class GModelSpatialElliptical; |
| ... | ... | @@ -136,28 +139,25 @@ protected: |
| 136 | 139 | const GObservation& obs) const; |
| 137 | 140 | |
| 138 | 141 | // Protected classes |
| 139 | - class edisp_kern : public GFunction { | |
| 142 | + class edisp_kern : public GFunctions { | |
| 140 | 143 | public: |
| 141 | 144 | edisp_kern(const GResponse* parent, |
| 142 | 145 | const GObservation* obs, |
| 143 | 146 | const GModelSky* model, |
| 144 | 147 | const GEvent* event, |
| 145 | 148 | const GTime& srcTime, |
| 146 | - const bool& grad) : | |
| 147 | - m_parent(parent), | |
| 148 | - m_obs(obs), | |
| 149 | - m_model(model), | |
| 150 | - m_event(event), | |
| 151 | - m_srcTime(srcTime), | |
| 152 | - m_grad(grad) { } | |
| 153 | - double eval(const double& etrue); | |
| 149 | + const bool& grad); | |
| 150 | + const GNdarray& array(void) const; | |
| 151 | + GNdarray eval(const double& etrue); | |
| 154 | 152 | protected: |
| 155 | - const GResponse* m_parent; //!< Response | |
| 156 | - const GObservation* m_obs; //!< Observation | |
| 157 | - const GModelSky* m_model; //!< Sky model | |
| 158 | - const GEvent* m_event; //!< Event | |
| 159 | - GTime m_srcTime; //!< True arrival time | |
| 160 | - bool m_grad; //!< Gradient flag | |
| 153 | + const GResponse* m_parent; //!< Response | |
| 154 | + const GObservation* m_obs; //!< Observation | |
| 155 | + const GModelSky* m_model; //!< Sky model | |
| 156 | + const GEvent* m_event; //!< Event | |
| 157 | + GNdarray m_array; //!< Array of values and gradients | |
| 158 | + std::vector<GModelPar*> m_pars; //!< Parameter pointers | |
| 159 | + GTime m_srcTime; //!< True arrival time | |
| 160 | + bool m_grad; //!< Gradient flag | |
| 161 | 161 | }; |
| 162 | 162 | class irf_radial_kern_theta : public GFunction { |
| 163 | 163 | public: | ... | ... |
include/Makefile.am
src/numerics/GIntegrals.cpp
| ... | ... | @@ -303,11 +303,12 @@ GNdarray GIntegrals::romberg(const double& a, const double& b, const int& order) |
| 303 | 303 | } |
| 304 | 304 | |
| 305 | 305 | // Allocate temporal storage |
| 306 | - GNdarray* s = new GNdarray[max_iter+2]; | |
| 307 | 306 | double* h = new double[max_iter+2]; |
| 307 | + GNdarray* s = new GNdarray[max_iter+2]; | |
| 308 | 308 | |
| 309 | - // Initialise step size | |
| 309 | + // Initialise step size and initial result | |
| 310 | 310 | h[1] = 1.0; |
| 311 | + s[0] = GNdarray(m_kernels->array().shape()); | |
| 311 | 312 | |
| 312 | 313 | // Iterative loop |
| 313 | 314 | for (m_iter = 1; m_iter <= max_iter; ++m_iter) { |
| ... | ... | @@ -333,9 +334,10 @@ GNdarray GIntegrals::romberg(const double& a, const double& b, const int& order) |
| 333 | 334 | |
| 334 | 335 | // Compute result using polynom interpolation |
| 335 | 336 | for (int i = 0; i < result.size(); ++i) { |
| 336 | - result(i) = polint(&h[m_iter-order], | |
| 337 | - &s[m_iter-order], | |
| 338 | - order, i, 0.0, &dss); | |
| 337 | + double dy = 0; | |
| 338 | + result(i) = polint(&h[m_iter-order], &s[m_iter-order], | |
| 339 | + order, i, 0.0, &dy); | |
| 340 | + dss(i) = dy; | |
| 339 | 341 | } |
| 340 | 342 | |
| 341 | 343 | // If a fixed number of iterations has been requested and if |
| ... | ... | @@ -369,8 +371,8 @@ GNdarray GIntegrals::romberg(const double& a, const double& b, const int& order) |
| 369 | 371 | m_abserr = abs(dss); |
| 370 | 372 | m_relerr = m_abserr; |
| 371 | 373 | for (int i = 0; i < result.size(); ++i) { |
| 372 | - double abs_result = abs(result(i)); | |
| 373 | - if (abs_result != 0.0) { | |
| 374 | + double abs_result = std::abs(result(i)); | |
| 375 | + if (abs_result > 0.0) { | |
| 374 | 376 | m_relerr(i) /= abs_result; |
| 375 | 377 | m_has_relerr = true; |
| 376 | 378 | } |
| ... | ... | @@ -388,6 +390,10 @@ GNdarray GIntegrals::romberg(const double& a, const double& b, const int& order) |
| 388 | 390 | |
| 389 | 391 | } // endfor: iterative loop |
| 390 | 392 | |
| 393 | + // Delete temporal storage | |
| 394 | + delete [] s; | |
| 395 | + delete [] h; | |
| 396 | + | |
| 391 | 397 | // Set status and optionally dump warning |
| 392 | 398 | if (!converged) { |
| 393 | 399 | m_isvalid = false; |
| ... | ... | @@ -405,10 +411,6 @@ GNdarray GIntegrals::romberg(const double& a, const double& b, const int& order) |
| 405 | 411 | } |
| 406 | 412 | } |
| 407 | 413 | |
| 408 | - // Delete temporal storage | |
| 409 | - delete [] s; | |
| 410 | - delete [] h; | |
| 411 | - | |
| 412 | 414 | } // endif: integration range was valid |
| 413 | 415 | |
| 414 | 416 | // Return result |
| ... | ... | @@ -478,7 +480,7 @@ GNdarray GIntegrals::trapzd(const double& a, |
| 478 | 480 | m_calls += 2; |
| 479 | 481 | |
| 480 | 482 | // Compute result |
| 481 | - result = 0.5*(b-a)*(y_a + y_b); | |
| 483 | + result = 0.5 * (b-a) * (y_a + y_b); | |
| 482 | 484 | |
| 483 | 485 | } // endif: only a single step was requested |
| 484 | 486 | |
| ... | ... | @@ -526,22 +528,20 @@ GNdarray GIntegrals::trapzd(const double& a, |
| 526 | 528 | } |
| 527 | 529 | |
| 528 | 530 | // Sum up values |
| 529 | - double x = a + 0.5*del; | |
| 531 | + double x = a + 0.5 * del; | |
| 530 | 532 | GNdarray sum(result.shape()); |
| 531 | - for (int j = 0; j < it; ++j, x+=del) { | |
| 533 | + for (int j = 0; j < it; ++j, x += del) { | |
| 532 | 534 | |
| 533 | - // Evaluate integrand | |
| 534 | - GNdarray y = m_kernels->eval(x); | |
| 535 | + // Evaluate and add integrand | |
| 536 | + sum += m_kernels->eval(x); | |
| 535 | 537 | m_calls++; |
| 536 | 538 | |
| 537 | - // Add integrand | |
| 538 | - sum += y; | |
| 539 | - | |
| 540 | 539 | } // endfor: looped over steps |
| 541 | 540 | |
| 542 | 541 | // Set result |
| 543 | - result = 0.5*(result + (b-a)*sum/tnm); | |
| 544 | - } | |
| 542 | + result = 0.5 * (result + del * sum); | |
| 543 | + | |
| 544 | + } // endelse: Case B | |
| 545 | 545 | |
| 546 | 546 | } // endelse: trapeziodal rule was applied |
| 547 | 547 | |
| ... | ... | @@ -708,7 +708,7 @@ double GIntegrals::polint(const double* xa, |
| 708 | 708 | const int& n, |
| 709 | 709 | const int& index, |
| 710 | 710 | const double& x, |
| 711 | - GNdarray* dy) | |
| 711 | + double* dy) | |
| 712 | 712 | { |
| 713 | 713 | // Initialise result |
| 714 | 714 | double y = 0.0; |
| ... | ... | @@ -728,8 +728,9 @@ double GIntegrals::polint(const double* xa, |
| 728 | 728 | ns = i; |
| 729 | 729 | dif = dift; |
| 730 | 730 | } |
| 731 | - c[i] = ya[i+1](index); | |
| 732 | - d[i] = ya[i+1](index); | |
| 731 | + double value = ya[i+1](index); | |
| 732 | + c[i] = value; | |
| 733 | + d[i] = value; | |
| 733 | 734 | } |
| 734 | 735 | |
| 735 | 736 | // Get initial approximation to y |
| ... | ... | @@ -760,10 +761,10 @@ double GIntegrals::polint(const double* xa, |
| 760 | 761 | } |
| 761 | 762 | |
| 762 | 763 | // Compute y correction |
| 763 | - (*dy)(index) = (2*(ns+1) < (n-m)) ? c[ns+1] : d[ns--]; | |
| 764 | + *dy = (2*(ns+1) < (n-m)) ? c[ns+1] : d[ns--]; | |
| 764 | 765 | |
| 765 | 766 | // Update y |
| 766 | - y += (*dy)(index); | |
| 767 | + y += *dy; | |
| 767 | 768 | |
| 768 | 769 | } // endfor: looped over columns of tableau |
| 769 | 770 | ... | ... |
src/obs/GObservation.cpp
| ... | ... | @@ -271,9 +271,12 @@ double GObservation::model(const GModels& models, |
| 271 | 271 | int igrad = 0; // Reset gradient counter |
| 272 | 272 | int grad_size = 0; // Reset gradient size |
| 273 | 273 | |
| 274 | + // Set gradient usage flag | |
| 275 | + bool use_grad = (gradient != NULL); | |
| 276 | + | |
| 274 | 277 | // If gradient is available then reset gradient vector elements to 0 |
| 275 | 278 | // and determine vector size |
| 276 | - if (gradient != NULL) { | |
| 279 | + if (use_grad) { | |
| 277 | 280 | (*gradient) = 0.0; |
| 278 | 281 | grad_size = gradient->size(); |
| 279 | 282 | } |
| ... | ... | @@ -289,19 +292,11 @@ double GObservation::model(const GModels& models, |
| 289 | 292 | // observation identifier |
| 290 | 293 | if (mptr->is_valid(instrument(), id())) { |
| 291 | 294 | |
| 292 | - // Compute value and add to model. If energy dispersion | |
| 293 | - // is used, don't compute model gradients as we cannot | |
| 294 | - // use them. This is somehow a kluge, but makes the | |
| 295 | - // code faster | |
| 296 | - model += mptr->eval(event, *this, !response()->use_edisp()); | |
| 297 | - | |
| 298 | - // Optionally determine model gradients. If the model has a | |
| 299 | - // gradient then use it, unless we have energy dispersion. | |
| 300 | - // For energy dispersion, no gradients are available as we | |
| 301 | - // have not implemented code that integrates the gradients | |
| 302 | - // over the energy dispersion. Here it's simpler to just use | |
| 303 | - // numerical gradients. | |
| 304 | - if (gradient != NULL) { | |
| 295 | + // Compute value and add to model | |
| 296 | + model += mptr->eval(event, *this, use_grad); | |
| 297 | + | |
| 298 | + // Optionally determine model gradients | |
| 299 | + if (use_grad) { | |
| 305 | 300 | |
| 306 | 301 | // Make sure that we have a slot for the gradient |
| 307 | 302 | #if defined(G_RANGE_CHECK) |
| ... | ... | @@ -334,7 +329,7 @@ double GObservation::model(const GModels& models, |
| 334 | 329 | |
| 335 | 330 | // Set gradient |
| 336 | 331 | if (par.is_free()) { |
| 337 | - if (par.has_grad() && !response()->use_edisp()) { | |
| 332 | + if (par.has_grad()) { | |
| 338 | 333 | (*gradient)[igrad+ipar] = par.factor_gradient(); |
| 339 | 334 | } |
| 340 | 335 | else { |
| ... | ... | @@ -360,7 +355,7 @@ double GObservation::model(const GModels& models, |
| 360 | 355 | |
| 361 | 356 | // Set gradient |
| 362 | 357 | if (par.is_free()) { |
| 363 | - if (par.has_grad() && !response()->use_edisp()) { | |
| 358 | + if (par.has_grad()) { | |
| 364 | 359 | (*gradient)[igrad+ipar] = par.factor_gradient(); |
| 365 | 360 | } |
| 366 | 361 | else { |
| ... | ... | @@ -512,7 +507,7 @@ double GObservation::npred(const GModels& models, GVector* gradient) const |
| 512 | 507 | * S(p,E,t) \times R(p',E',t'|p,E,t) \, dp \, dE \, dt |
| 513 | 508 | * \f] |
| 514 | 509 | * |
| 515 | - * where | |
| 510 | + * where | |
| 516 | 511 | * \f$S(p,E,t)\f$ is the source model, |
| 517 | 512 | * \f$R(p',E',t'|p,E,t)\f$ is the instrument response function, |
| 518 | 513 | * \f$p'\f$ is the measured photon direction, | ... | ... |
src/obs/GResponse.cpp
| ... | ... | @@ -36,6 +36,7 @@ |
| 36 | 36 | #include "GVector.hpp" |
| 37 | 37 | #include "GMatrix.hpp" |
| 38 | 38 | #include "GIntegral.hpp" |
| 39 | +#include "GIntegrals.hpp" | |
| 39 | 40 | #include "GResponse.hpp" |
| 40 | 41 | #include "GEvent.hpp" |
| 41 | 42 | #include "GPhoton.hpp" |
| ... | ... | @@ -165,7 +166,7 @@ GResponse& GResponse::operator=(const GResponse& rsp) |
| 165 | 166 | * @param[in] model Sky model. |
| 166 | 167 | * @param[in] event Event. |
| 167 | 168 | * @param[in] obs Observation. |
| 168 | - * @param[in] grad Should model gradients be computed? (default: true) | |
| 169 | + * @param[in] grad Should model gradients be computed? | |
| 169 | 170 | * @return Event probability. |
| 170 | 171 | * |
| 171 | 172 | * Computes the event probability |
| ... | ... | @@ -202,6 +203,9 @@ double GResponse::convolve(const GModelSky& model, |
| 202 | 203 | // Retrieve true energy boundaries |
| 203 | 204 | GEbounds ebounds = this->ebounds(event.energy()); |
| 204 | 205 | |
| 206 | + // Initialise Ndarray array | |
| 207 | + GNdarray array; | |
| 208 | + | |
| 205 | 209 | // Loop over all boundaries |
| 206 | 210 | for (int i = 0; i < ebounds.size(); ++i) { |
| 207 | 211 | |
| ... | ... | @@ -214,19 +218,67 @@ double GResponse::convolve(const GModelSky& model, |
| 214 | 218 | |
| 215 | 219 | // Setup integration function |
| 216 | 220 | edisp_kern integrand(this, &obs, &model, &event, srcTime, grad); |
| 217 | - GIntegral integral(&integrand); | |
| 221 | + GIntegrals integral(&integrand); | |
| 218 | 222 | |
| 219 | 223 | // Set number of iterations |
| 220 | 224 | integral.fixed_iter(iter); |
| 221 | 225 | |
| 222 | 226 | // Do Romberg integration |
| 223 | - prob += integral.romberg(etrue_min, etrue_max); | |
| 227 | + if (array.size() == 0) { | |
| 228 | + array = integral.romberg(etrue_min, etrue_max); | |
| 229 | + } | |
| 230 | + else { | |
| 231 | + array += integral.romberg(etrue_min, etrue_max); | |
| 232 | + } | |
| 224 | 233 | |
| 225 | 234 | } // endif: interval was valid |
| 226 | 235 | |
| 227 | 236 | } // endfor: looped over intervals |
| 228 | 237 | |
| 229 | - } | |
| 238 | + // Initialise array index | |
| 239 | + int index = 0; | |
| 240 | + | |
| 241 | + // Get probability | |
| 242 | + prob = array(index++); | |
| 243 | + | |
| 244 | + // Set gradients | |
| 245 | + if (grad) { | |
| 246 | + | |
| 247 | + // Set spectral gradients | |
| 248 | + if (model.spectral() != NULL) { | |
| 249 | + for (int i = 0; i < model.spectral()->size(); ++i) { | |
| 250 | + GModelPar& par = (*(model.spectral()))[i]; | |
| 251 | + if (par.is_free() && par.has_grad()) { | |
| 252 | + par.factor_gradient(array(index++)); | |
| 253 | + } | |
| 254 | + } | |
| 255 | + } | |
| 256 | + | |
| 257 | + // Set temporal gradients | |
| 258 | + if (model.temporal() != NULL) { | |
| 259 | + for (int i = 0; i < model.temporal()->size(); ++i) { | |
| 260 | + GModelPar& par = (*(model.temporal()))[i]; | |
| 261 | + if (par.is_free() && par.has_grad()) { | |
| 262 | + par.factor_gradient(array(index++)); | |
| 263 | + } | |
| 264 | + } | |
| 265 | + } | |
| 266 | + | |
| 267 | + // Optionally set scale gradient for instrument | |
| 268 | + if (model.has_scales()) { | |
| 269 | + for (int i = 0; i < model.scales(); ++i) { | |
| 270 | + const GModelPar& par = model.scale(i); | |
| 271 | + if (par.name() == obs.instrument()) { | |
| 272 | + if (par.is_free() && par.has_grad()) { | |
| 273 | + par.factor_gradient(array(index++)); | |
| 274 | + } | |
| 275 | + } | |
| 276 | + } | |
| 277 | + } | |
| 278 | + | |
| 279 | + } // endif: set gradients | |
| 280 | + | |
| 281 | + } // endif: Case A | |
| 230 | 282 | |
| 231 | 283 | // Case B: No integration (assume no energy dispersion) |
| 232 | 284 | else { |
| ... | ... | @@ -237,7 +289,7 @@ double GResponse::convolve(const GModelSky& model, |
| 237 | 289 | // Evaluate probability |
| 238 | 290 | prob = eval_prob(model, event, srcEng, srcTime, obs, grad); |
| 239 | 291 | |
| 240 | - } | |
| 292 | + } // endelse: Case B | |
| 241 | 293 | |
| 242 | 294 | // Compile option: Check for NaN/Inf |
| 243 | 295 | #if defined(G_NAN_CHECK) |
| ... | ... | @@ -1032,36 +1084,133 @@ double GResponse::eval_prob(const GModelSky& model, |
| 1032 | 1084 | |
| 1033 | 1085 | |
| 1034 | 1086 | /***********************************************************************//** |
| 1035 | - * @brief Integration kernel for GResponse::edisp_kern() class | |
| 1087 | + * @brief Constructor for energy dispersion integration kernel class | |
| 1088 | + * | |
| 1089 | + * @param[in] parent Pointer to response. | |
| 1090 | + * @param[in] obs Pointer to observation. | |
| 1091 | + * @param[in] model Pointer to sky model. | |
| 1092 | + * @param[in] event Pointer to event. | |
| 1093 | + * @param[in] srcTime True time. | |
| 1094 | + * @param[in] grad Evaluate gradients? | |
| 1095 | + * | |
| 1096 | + * This method constructs the integration kernel needed for the energy | |
| 1097 | + * dispersion computation. | |
| 1098 | + ***************************************************************************/ | |
| 1099 | +GResponse::edisp_kern::edisp_kern(const GResponse* parent, | |
| 1100 | + const GObservation* obs, | |
| 1101 | + const GModelSky* model, | |
| 1102 | + const GEvent* event, | |
| 1103 | + const GTime& srcTime, | |
| 1104 | + const bool& grad) | |
| 1105 | +{ | |
| 1106 | + // Set members | |
| 1107 | + m_parent = parent; | |
| 1108 | + m_obs = obs; | |
| 1109 | + m_model = model; | |
| 1110 | + m_event = event; | |
| 1111 | + m_srcTime = srcTime; | |
| 1112 | + m_grad = grad; | |
| 1113 | + | |
| 1114 | + // If gradients are requested then put pointers to all relevant parameter | |
| 1115 | + // in the parameter pointer vector | |
| 1116 | + m_pars.clear(); | |
| 1117 | + if (m_grad) { | |
| 1118 | + if (m_model->spectral() != NULL) { | |
| 1119 | + for (int i = 0; i < m_model->spectral()->size(); ++i) { | |
| 1120 | + GModelPar& par = (*(m_model->spectral()))[i]; | |
| 1121 | + if (par.is_free() && par.has_grad()) { | |
| 1122 | + m_pars.push_back(&par); | |
| 1123 | + } | |
| 1124 | + } | |
| 1125 | + } | |
| 1126 | + if (m_model->temporal() != NULL) { | |
| 1127 | + for (int i = 0; i < m_model->temporal()->size(); ++i) { | |
| 1128 | + GModelPar& par = (*(m_model->temporal()))[i]; | |
| 1129 | + if (par.is_free() && par.has_grad()) { | |
| 1130 | + m_pars.push_back(&par); | |
| 1131 | + } | |
| 1132 | + } | |
| 1133 | + } | |
| 1134 | + if (m_model->has_scales()) { | |
| 1135 | + for (int i = 0; i < m_model->scales(); ++i) { | |
| 1136 | + GModelPar& par = const_cast<GModelPar&>(m_model->scale(i)); | |
| 1137 | + if (par.name() == m_obs->instrument()) { | |
| 1138 | + if (par.is_free() && par.has_grad()) { | |
| 1139 | + m_pars.push_back(&par); | |
| 1140 | + } | |
| 1141 | + } | |
| 1142 | + } | |
| 1143 | + } | |
| 1144 | + } | |
| 1145 | + | |
| 1146 | + // Allocate Ndarray to be returned as return type | |
| 1147 | + m_array = GNdarray(m_pars.size()+1); | |
| 1148 | + | |
| 1149 | + // Return | |
| 1150 | + return; | |
| 1151 | +} | |
| 1152 | + | |
| 1153 | + | |
| 1154 | +/***********************************************************************//** | |
| 1155 | + * @brief Return energy dispersion integration kernel | |
| 1156 | + * | |
| 1157 | + * @return Ndarray. | |
| 1158 | + * | |
| 1159 | + * This method returns an Ndarray with the last values that were computed | |
| 1160 | + * by the eval() method. In case that eval() was not called before, the | |
| 1161 | + * method returns an Ndarray with the right dimension and with all values | |
| 1162 | + * set to zero. | |
| 1163 | + ***************************************************************************/ | |
| 1164 | +const GNdarray& GResponse::edisp_kern::array(void) const | |
| 1165 | +{ | |
| 1166 | + // Return Ndarray | |
| 1167 | + return m_array; | |
| 1168 | +} | |
| 1169 | + | |
| 1170 | + | |
| 1171 | +/***********************************************************************//** | |
| 1172 | + * @brief Evaluate energy dispersion integration kernel | |
| 1036 | 1173 | * |
| 1037 | 1174 | * @param[in] etrue True photon energy in MeV. |
| 1038 | 1175 | * |
| 1039 | 1176 | * This method implements the integration kernel needed for the |
| 1040 | 1177 | * GResponse::edisp_kern() class. |
| 1041 | 1178 | ***************************************************************************/ |
| 1042 | -double GResponse::edisp_kern::eval(const double& etrue) | |
| 1179 | +GNdarray GResponse::edisp_kern::eval(const double& etrue) | |
| 1043 | 1180 | { |
| 1181 | + // Initialise Ndarray array index | |
| 1182 | + int index = 0; | |
| 1183 | + | |
| 1044 | 1184 | // Set true energy |
| 1045 | 1185 | GEnergy srcEng; |
| 1046 | 1186 | srcEng.MeV(etrue); |
| 1047 | 1187 | |
| 1048 | 1188 | // Get function value |
| 1049 | - double value = m_parent->eval_prob(*m_model, *m_event, | |
| 1050 | - srcEng, m_srcTime, *m_obs, m_grad); | |
| 1189 | + m_array(index++) = m_parent->eval_prob(*m_model, *m_event, | |
| 1190 | + srcEng, m_srcTime, *m_obs, | |
| 1191 | + m_grad); | |
| 1192 | + | |
| 1193 | + // If gradients are requested then extract them and put them into the | |
| 1194 | + // array | |
| 1195 | + if (m_grad) { | |
| 1196 | + for (int i = 0; i < m_pars.size(); ++i, ++index) { | |
| 1197 | + m_array(index) = m_pars[i]->factor_gradient(); | |
| 1198 | + } | |
| 1199 | + } | |
| 1051 | 1200 | |
| 1052 | 1201 | // Compile option: Check for NaN |
| 1053 | 1202 | #if defined(G_NAN_CHECK) |
| 1054 | - if (gammalib::is_notanumber(value) || gammalib::is_infinite(value)) { | |
| 1203 | + if (gammalib::is_notanumber(m_array(0)) || gammalib::is_infinite(m_array(0))) { | |
| 1055 | 1204 | std::cout << "*** ERROR: GResponse::edisp_kern::eval"; |
| 1056 | 1205 | std::cout << "(etrue=" << etrue << "): "; |
| 1057 | 1206 | std::cout << " NaN/Inf encountered"; |
| 1058 | - std::cout << " (value=" << value; | |
| 1207 | + std::cout << " (value=" << m_array(0); | |
| 1059 | 1208 | std::cout << ")" << std::endl; |
| 1060 | 1209 | } |
| 1061 | 1210 | #endif |
| 1062 | 1211 | |
| 1063 | - // Return value | |
| 1064 | - return value; | |
| 1212 | + // Return array | |
| 1213 | + return m_array; | |
| 1065 | 1214 | } |
| 1066 | 1215 | |
| 1067 | 1216 | ... | ... |