GammaLib  2.1.0.dev
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
GCTAModelSpatial.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  * GCTAModelSpatial.cpp - Spatial model abstract base class *
3  * ----------------------------------------------------------------------- *
4  * copyright (C) 2018-2020 by Jurgen Knodlseder *
5  * ----------------------------------------------------------------------- *
6  * *
7  * This program is free software: you can redistribute it and/or modify *
8  * it under the terms of the GNU General Public License as published by *
9  * the Free Software Foundation, either version 3 of the License, or *
10  * (at your option) any later version. *
11  * *
12  * This program is distributed in the hope that it will be useful, *
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
15  * GNU General Public License for more details. *
16  * *
17  * You should have received a copy of the GNU General Public License *
18  * along with this program. If not, see <http://www.gnu.org/licenses/>. *
19  * *
20  ***************************************************************************/
21 /**
22  * @file GCTAModelSpatial.cpp
23  * @brief Abstract spatial model class implementation
24  * @author Juergen Knoedlseder
25  */
26 
27 /* __ Includes ___________________________________________________________ */
28 #ifdef HAVE_CONFIG_H
29 #include <config.h>
30 #endif
31 #include "GException.hpp"
32 #include "GTools.hpp"
33 #include "GIntegral.hpp"
34 #include "GObservation.hpp"
35 #include "GCTASupport.hpp"
36 #include "GCTAEventList.hpp"
37 #include "GCTAInstDir.hpp"
38 #include "GCTAModelSpatial.hpp"
39 #include "GCTAResponse_helpers.hpp"
40 
41 /* __ Method name definitions ____________________________________________ */
42 #define G_ACCESS1 "GCTAModelSpatial::operator[](int&)"
43 #define G_ACCESS2 "GCTAModelSpatial::operator[](std::string&)"
44 #define G_MC "GCTAModelSpatial::mc(GEnergy&, GTime&, GCTAObservation&, "\
45  "GRan&)"
46 #define G_NPRED "GCTAModelSpatial::npred(GEnergy&, GTime&, GObservation&)"
47 
48 /* __ Macros _____________________________________________________________ */
49 
50 /* __ Coding definitions _________________________________________________ */
51 
52 /* __ Debug definitions __________________________________________________ */
53 
54 /* __ Constants __________________________________________________________ */
55 const double g_npred_resolution = 0.1*gammalib::deg2rad; //!< Scale of bkg.
56  // variation
57 
58 
59 /*==========================================================================
60  = =
61  = Constructors/destructors =
62  = =
63  ==========================================================================*/
64 
65 /***********************************************************************//**
66  * @brief Void constructor
67  ***************************************************************************/
69 {
70  // Initialise members
71  init_members();
72 
73  // Return
74  return;
75 }
76 
77 
78 /***********************************************************************//**
79  * @brief Copy constructor
80  *
81  * @param[in] model Spatial model.
82  ***************************************************************************/
84 {
85  // Initialise members
86  init_members();
87 
88  // Copy members
89  copy_members(model);
90 
91  // Return
92  return;
93 }
94 
95 
96 /***********************************************************************//**
97  * @brief Destructor
98  ***************************************************************************/
100 {
101  // Free members
102  free_members();
103 
104  // Return
105  return;
106 }
107 
108 
109 /*==========================================================================
110  = =
111  = Operators =
112  = =
113  ==========================================================================*/
114 
115 /***********************************************************************//**
116  * @brief Assignment operator
117  *
118  * @param[in] model Spatial model.
119  ***************************************************************************/
121 {
122  // Execute only if object is not identical
123  if (this != &model) {
124 
125  // Free members
126  free_members();
127 
128  // Initialise members
129  init_members();
130 
131  // Copy members
132  copy_members(model);
133 
134  } // endif: object was not identical
135 
136  // Return
137  return *this;
138 }
139 
140 
141 /***********************************************************************//**
142  * @brief Returns model parameter
143  *
144  * @param[in] index Parameter index [0,...,size()-1].
145  *
146  * @exception GException::out_of_range
147  * Parameter index is out of range.
148  ***************************************************************************/
150 {
151  // Compile option: raise exception if index is out of range
152  #if defined(G_RANGE_CHECK)
153  if (index < 0 || index >= size()) {
154  throw GException::out_of_range(G_ACCESS1, "Spatial parameter index",
155  index, size());
156  }
157  #endif
158 
159  // Return reference
160  return *(m_pars[index]);
161 }
162 
163 
164 /***********************************************************************//**
165  * @brief Returns model parameter (const version)
166  *
167  * @param[in] index Parameter index [0,...,size()-1].
168  *
169  * @exception GException::out_of_range
170  * Parameter index is out of range.
171  ***************************************************************************/
172 const GModelPar& GCTAModelSpatial::operator[](const int& index) const
173 {
174  // Compile option: raise exception if index is out of range
175  #if defined(G_RANGE_CHECK)
176  if (index < 0 || index >= size()) {
177  throw GException::out_of_range(G_ACCESS1, "Spatial parameter index",
178  index, size());
179  }
180  #endif
181 
182  // Return reference
183  return *(m_pars[index]);
184 }
185 
186 
187 /***********************************************************************//**
188  * @brief Returns reference to model parameter
189  *
190  * @param[in] name Parameter name.
191  *
192  * @exception GException::invalid_argument
193  * Parameter with specified name not found in container.
194  ***************************************************************************/
195 GModelPar& GCTAModelSpatial::operator[](const std::string& name)
196 {
197  // Get parameter index
198  int index = 0;
199  for (; index < size(); ++index) {
200  if (m_pars[index]->name() == name)
201  break;
202  }
203 
204  // Throw exception if parameter name was not found
205  if (index >= size()) {
206  std::string msg = "Parameter \""+name+"\" not found in spatial "
207  "component of background model.";
209  }
210 
211  // Return reference
212  return *(m_pars[index]);
213 }
214 
215 
216 /***********************************************************************//**
217  * @brief Returns reference to model parameter (const version)
218  *
219  * @param[in] name Parameter name.
220  *
221  * @exception GException::invalid_argument
222  * Parameter with specified name not found in container.
223  ***************************************************************************/
224 const GModelPar& GCTAModelSpatial::operator[](const std::string& name) const
225 {
226  // Get parameter index
227  int index = 0;
228  for (; index < size(); ++index) {
229  if (m_pars[index]->name() == name)
230  break;
231  }
232 
233  // Throw exception if parameter name was not found
234  if (index >= size()) {
235  std::string msg = "Parameter \""+name+"\" not found in spatial "
236  "component of background model.";
238  }
239 
240  // Return reference
241  return *(m_pars[index]);
242 }
243 
244 
245 /*==========================================================================
246  = =
247  = Public methods =
248  = =
249  ==========================================================================*/
250 
251 /***********************************************************************//**
252  * @brief Returns MC instrument direction
253  *
254  * @param[in] energy Event energy.
255  * @param[in] time Event time.
256  * @param[in] obs CTA Observation.
257  * @param[in,out] ran Random number generator.
258  * @return Instrument direction
259  *
260  * @exception GException::invalid_return_value
261  * Spatial background model is empty, or method repeatedly
262  * encounters zero model values.
263  *
264  * Return random instrument direction.
265  ***************************************************************************/
267  const GTime& time,
268  const GCTAObservation& obs,
269  GRan& ran) const
270 {
271  // Initialise instrument direction
272  GCTAInstDir dir;
273 
274  // Get maximum value for Monte Carlo simulations (include some margin)
275  double mc_max_value = 1.5 * this->mc_max_value(obs);
276 
277  // Throw an exception if maximum value is zero
278  if (mc_max_value <= 0.0) {
279  std::string msg = "Spatial background model is empty. Please provide "
280  "a valid spatial background model.";
282  }
283 
284  // Get DETX and DETY value of RoI centre in radians
285  GCTARoi roi = obs.roi();
286  GCTAInstDir roi_centre = roi.centre();
287  double detx_centre = roi_centre.detx();
288  double dety_centre = roi_centre.dety();
289 
290  // Get DETX and DETY minima and maximum in radians
291  double radius = obs.roi().radius() * gammalib::deg2rad;
292  double detx_min = detx_centre - radius;
293  double detx_max = detx_centre + radius;
294  double dety_min = dety_centre - radius;
295  double dety_max = dety_centre + radius;
296  double detx_width = detx_max - detx_min;
297  double dety_width = dety_max - dety_min;
298 
299  // Initialise rejection method
300  double f = 0.0;
301  double ftest = 1.0;
302  const int max_subsequent_zeros = 10;
303 
304  // Find instrument direction by rejection method
305  while (ftest > f) {
306 
307  // Draw random DETX/DETY until a non-zero function value is found.
308  // Subsequent zero values may indicate that there is an issue with
309  // the model. After a limited number of subsequent zeros the loop
310  // is terminated with an exception
311  int zeros = 0;
312  do {
313 
314  // Draw random DETX and DETY coordinates
315  double detx = detx_min + detx_width * ran.uniform();
316  double dety = dety_min + dety_width * ran.uniform();
317 
318  // Set instrument direction
319  dir.detx(detx);
320  dir.dety(dety);
321 
322  // Derive sky direction from instrument coordinates
323  GSkyDir skydir = obs.pointing().skydir(dir);
324 
325  // Set sky direction in GCTAInstDir object
326  dir.dir(skydir);
327 
328  // Compute function value if the coordinate is comprised in
329  // the RoI
330  if (roi.contains(dir)) {
331  f = eval(dir, energy, time, false);
332  }
333  else {
334  f = 0.0;
335  }
336 
337  // Handle zero function value
338  if (f == 0.0) {
339  zeros++;
340  if (zeros > max_subsequent_zeros) {
341  std::string msg = "Method repeatedly encounters zero values "
342  "of spatial background model. Make sure "
343  "that the background model is correct.";
345  }
346  }
347 
348  } while (f == 0);
349 
350  // Get uniform random value between zero and the maximum model value.
351  // The loop is quit if the random number is smaller than the function
352  // value.
353  ftest = ran.uniform() * mc_max_value;
354 
355  } // endwhile
356 
357  // Return instrument direction
358  return dir;
359 }
360 
361 
362 /***********************************************************************//**
363  * @brief Return integral of spatial model component
364  *
365  * @param[in] energy Measured event energy.
366  * @param[in] time Measured event time.
367  * @param[in] obs Observation.
368  * @return Integral of spatial model component.
369  *
370  * Spatially integrates the spatial background model component for a given
371  * measured event energy and event time over the region of interest (RoI)
372  * of a given observation.
373  *
374  * The method uses a 2D Romberg integration to numerically integrate the
375  * spatial background model component.
376  ***************************************************************************/
377 double GCTAModelSpatial::npred(const GEnergy& energy,
378  const GTime& time,
379  const GObservation& obs) const
380 {
381  // Set number of iterations for Romberg integration.
382  static const int min_iter_theta = 5;
383  static const int min_iter_phi = 5;
384  static const int max_iter_theta = 8;
385  static const int max_iter_phi = 8;
386 
387  // Get reference on CTA pointing and event list from observation
388  const GCTAPointing& pnt = gammalib::cta_pnt(G_NPRED, obs);
389  const GCTAEventList& events = gammalib::cta_event_list(G_NPRED, obs);
390 
391  // Get instrument direction of RoI centre
392  GCTAInstDir roi_centre = pnt.instdir(events.roi().centre().dir());
393 
394  // Get RoI radius in radians
395  double roi_radius = events.roi().radius() * gammalib::deg2rad;
396 
397  // Set number of radial integration iterations
398  int iter_theta = gammalib::iter_rho(roi_radius, g_npred_resolution,
399  min_iter_theta, max_iter_theta);
400 
401  // Setup integration function
403  energy,
404  time,
405  roi_centre,
406  min_iter_phi,
407  max_iter_phi);
408 
409  // Setup integration
410  GIntegral integral(&integrand);
411 
412  // Set fixed number of iterations
413  integral.fixed_iter(iter_theta);
414 
415  // Spatially integrate radial component (assumes that RoI centre is
416  // at DETX=DETY=0)
417  double npred = integral.romberg(0.0, roi_radius);
418 
419  // Debug: Check for NaN
420  #if defined(G_NAN_CHECK)
421  if (gammalib::is_notanumber(npred) || gammalib::is_infinite(npred)) {
422  std::string origin = "GCTAModelSpatial::npred";
423  std::string message = " NaN/Inf encountered (npred=" +
424  gammalib::str(npred) + ", roi_radius=" +
425  gammalib::str(roi_radius) + ")";
426  gammalib::warning(origin, message);
427  }
428  #endif
429 
430  // Return Npred
431  return npred;
432 }
433 
434 
435 /*==========================================================================
436  = =
437  = Private methods =
438  = =
439  ==========================================================================*/
440 
441 /***********************************************************************//**
442  * @brief Initialise class members
443  ***************************************************************************/
445 {
446  // Initialise members
447  m_pars.clear();
448 
449  // Return
450  return;
451 }
452 
453 
454 /***********************************************************************//**
455  * @brief Copy class members
456  *
457  * @param[in] model Spatial background model component.
458  ***************************************************************************/
460 {
461  // Copy members
462  m_pars = model.m_pars;
463 
464  // Return
465  return;
466 }
467 
468 
469 /***********************************************************************//**
470  * @brief Delete class members
471  ***************************************************************************/
473 {
474  // Return
475  return;
476 }
477 
478 
479 /***********************************************************************//**
480  * @brief Kernel for offset angle integration of spatial component
481  *
482  * @param[in] theta Offset angle from RoI centre (radians).
483  *
484  * Computes
485  *
486  * \f[
487  * K(\rho | E, t) = \sin \theta \times
488  * \int_{0}^{2\pi}
489  * B(\theta,\phi | E, t) d\phi
490  * \f]
491  *
492  * where \f$B(\theta,\phi | E, t)\f$ is the spatial component of the
493  * background model for a specific observed energy \f$E\f$ and time \f$t\f$.
494  ***************************************************************************/
496 {
497  // Initialise value
498  double value = 0.0;
499 
500  // Continue only if offset angle is positive
501  if (theta > 0.0) {
502 
503  // Setup phi integration kernel
505  m_energy,
506  m_time,
507  m_roi_centre,
508  theta);
509 
510  // Setup integration
511  GIntegral integral(&integrand);
512 
513  // Set number of azimuthal integration iterations
514  int iter = gammalib::iter_phi(theta, g_npred_resolution,
516 
517  // Set fixed number of iterations
518  integral.fixed_iter(iter);
519 
520  // Integrate over phi
521  value = integral.romberg(0.0, gammalib::twopi) * std::sin(theta);
522 
523  // Debug: Check for NaN
524  #if defined(G_NAN_CHECK)
525  if (gammalib::is_notanumber(value) || gammalib::is_infinite(value)) {
526  std::string origin = "GCTAModelSpatial::npred_roi_kern_theta::eval"
527  "(" + gammalib::str(theta) + ")";
528  std::string message = " NaN/Inf encountered (value=" +
529  gammalib::str(value) + ")";
530  gammalib::warning(origin, message);
531  }
532  #endif
533 
534  } // endif: offset angle was positive
535 
536  // Return value
537  return value;
538 }
539 
540 
541 /***********************************************************************//**
542  * @brief Kernel for azimuth angle integration of spatial component
543  *
544  * @param[in] phi Azimuth angle around RoI centre (radians).
545  *
546  * Computes
547  *
548  * \f[
549  * B(\theta, \phi | E, t)
550  * \f]
551  *
552  * using
553  *
554  * \f[ {\rm detx} = \theta \cos \phi \f]
555  * \f[ {\rm dety} = \theta \sin \phi \f]
556  *
557  * @todo Verify correct orientation of detx and dety with respect to phi
558  ***************************************************************************/
560 {
561  // Compute detx and dety in radians
562  double detx = m_roi_centre.detx();
563  double dety = m_roi_centre.dety();
564  if (m_theta > 0.0 ) {
565  detx += m_theta * std::cos(phi);
566  dety += m_theta * std::sin(phi);
567  }
568 
569  // Setup CTA instrument direction
570  GCTAInstDir dir(detx, dety);
571 
572  // Get background value
573  double value = m_spatial->eval(dir, m_energy, m_time);
574 
575  // Debug: Check for NaN
576  #if defined(G_NAN_CHECK)
577  if (gammalib::is_notanumber(value) || gammalib::is_infinite(value)) {
578  std::string origin = "GCTAModelSpatial::npred_roi_kern_phi::eval"
579  "(" + gammalib::str(phi) + ")";
580  std::string message = " NaN/Inf encountered (value=" +
581  gammalib::str(value) + ", detx=" +
582  gammalib::str(detx) + ", dety=" +
583  gammalib::str(dety) + ")";
584  gammalib::warning(origin, message);
585  }
586  #endif
587 
588  // Return Npred
589  return value;
590 }
virtual GCTAInstDir mc(const GEnergy &energy, const GTime &time, const GCTAObservation &obs, GRan &ran) const
Returns MC instrument direction.
Abstract spatial model class.
CTA response helper classes definition.
#define G_NPRED
CTA event list class interface definition.
double romberg(std::vector< double > bounds, const int &order=5)
Perform Romberg integration.
Definition: GIntegral.cpp:382
void detx(const double &x)
Set DETX coordinate (in radians)
void warning(const std::string &origin, const std::string &message)
Emits warning.
Definition: GTools.cpp:1386
const double g_npred_resolution
Scale of bkg.
virtual void roi(const GRoi &roi)
Set ROI.
void copy_members(const GCTAModelSpatial &model)
Copy class members.
double eval(const double &phi)
Kernel for azimuth angle integration of spatial component.
GVector cos(const GVector &vector)
Computes cosine of vector elements.
Definition: GVector.cpp:1190
GCTAModelSpatial(void)
Void constructor.
const GCTAPointing & cta_pnt(const std::string &origin, const GObservation &obs)
Retrieve CTA pointing from generic observation.
virtual double mc_max_value(const GCTAObservation &obs) const =0
CTA event list class.
virtual GCTAModelSpatial & operator=(const GCTAModelSpatial &model)
Assignment operator.
const double & radius(void) const
Returns radius of region of interest in degrees.
Definition: GCTARoi.hpp:125
Random number generator class.
Definition: GRan.hpp:44
Interface for the CTA region of interest class.
Definition: GCTARoi.hpp:49
Time class.
Definition: GTime.hpp:55
Gammalib tools definition.
GIntegral class interface definition.
Definition: GIntegral.hpp:46
void pointing(const GCTAPointing &pointing)
Set CTA pointing.
void dir(const GSkyDir &dir)
Set sky direction.
int m_min_iter
Minimum number of Romberg iterations.
bool is_notanumber(const double &x)
Signal if argument is not a number.
Definition: GTools.hpp:201
bool is_infinite(const double &x)
Signal if argument is infinite.
Definition: GTools.hpp:184
Definition of support function used by CTA classes.
Model parameter class.
Definition: GModelPar.hpp:87
int m_max_iter
Maximum number of Romberg iterations.
const double deg2rad
Definition: GMath.hpp:43
#define G_ACCESS1
virtual ~GCTAModelSpatial(void)
Destructor.
GCTARoi roi(void) const
Get Region of Interest.
#define G_ACCESS2
const GCTAInstDir & centre(void) const
Returns region of interest centre.
Definition: GCTARoi.hpp:111
void free_members(void)
Delete class members.
const GCTAModelSpatial * m_spatial
Pointer to spatial component.
GCTAInstDir instdir(const GSkyDir &skydir) const
Get instrument direction from sky direction.
double uniform(void)
Returns random double precision floating value in range 0 to 1.
Definition: GRan.cpp:242
void init_members(void)
Initialise class members.
CTA pointing class.
CTA instrument direction class interface definition.
const GCTAEventList & cta_event_list(const std::string &origin, const GObservation &obs)
Retrieve CTA event list from generic observation.
virtual bool contains(const GEvent &event) const
Check if region of interest contains an event.
Definition: GCTARoi.cpp:215
int size(void) const
Return number of model parameters.
virtual GModelPar & operator[](const int &index)
Returns model parameter.
Abstract observation base class.
Abstract observation base class interface definition.
int iter_phi(const double &rho, const double &resolution, const int &iter_min, const int &iter_max)
Determine number of azimuthal Romberg iterations.
Abstract spatial model class interface definition.
GVector sin(const GVector &vector)
Computes sine of vector elements.
Definition: GVector.cpp:1316
Exception handler interface definition.
virtual double eval(const GCTAInstDir &dir, const GEnergy &energy, const GTime &time, const bool &gradients=false) const =0
void fixed_iter(const int &iter)
Set fixed number of iterations.
Definition: GIntegral.hpp:189
#define G_MC
CTA instrument direction class.
Definition: GCTAInstDir.hpp:63
void dety(const double &y)
Set DETY coordinate (in radians)
const double twopi
Definition: GMath.hpp:36
CTA observation class.
std::vector< GModelPar * > m_pars
Parameter pointers.
virtual double npred(const GEnergy &energy, const GTime &time, const GObservation &obs) const
Return integral of spatial model component.
double eval(const double &theta)
Kernel for offset angle integration of spatial component.
Integration class interface definition.
int iter_rho(const double &rho_max, const double &resolution, const int &iter_min, const int &iter_max)
Determine number of radial Romberg iterations.
Sky direction class.
Definition: GSkyDir.hpp:62
Class that handles energies in a unit independent way.
Definition: GEnergy.hpp:48
std::string str(const unsigned short int &value)
Convert unsigned short integer value into string.
Definition: GTools.cpp:489