GammaLib  2.1.0.dev
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
GModelSpatialRadialRing.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  * GModelSpatialRadialRing.cpp - Radial ring source model class *
3  * ----------------------------------------------------------------------- *
4  * copyright (C) 2020-2022 by Pierrick Martin *
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 GModelSpatialRadialRing.cpp
23  * @brief Radial ring model class implementation
24  * @author Pierrick Martin
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 "GMath.hpp"
36 
37 /* __ Constants __________________________________________________________ */
38 
39 /* __ Globals ____________________________________________________________ */
41 const GModelSpatialRegistry g_radial_ring_registry(&g_radial_ring_seed);
42 
43 /* __ Method name definitions ____________________________________________ */
44 #define G_CONSTRUCTOR "GModelSpatialRadialRing::GModelSpatialRadialRing("\
45  "GSkyDir&, double&, double&, std::string&)"
46 #define G_READ "GModelSpatialRadialRing::read(GXmlElement&)"
47 #define G_WRITE "GModelSpatialRadialRing::write(GXmlElement&)"
48 
49 /* __ Macros _____________________________________________________________ */
50 
51 /* __ Coding definitions _________________________________________________ */
52 
53 /* __ Debug definitions __________________________________________________ */
54 
55 
56 /*==========================================================================
57  = =
58  = Constructors/destructors =
59  = =
60  ==========================================================================*/
61 
62 /***********************************************************************//**
63  * @brief Void constructor
64  *
65  * Constructs empty radial ring model.
66  ***************************************************************************/
68 {
69  // Initialise members
70  init_members();
71 
72  // Return
73  return;
74 }
75 
76 
77 /***********************************************************************//**
78  * @brief Ring constructor
79  *
80  * @param[in] dir Sky position of ring centre.
81  * @param[in] radius Ring inner radius (degrees).
82  * @param[in] width Ring width (degrees).
83  * @param[in] coordsys Coordinate system (either "CEL" or "GAL")
84  *
85  * @exception GException::invalid_argument
86  * Invalid @p coordsys argument specified.
87  *
88  * Constructs radial ring model from the sky position of the ring centre
89  * (@p dir) and the ring inner radius (@p radius) and width (@p width) in
90  * degrees. The @p coordsys parameter specifies whether the sky direction
91  * should be interpreted in the celestial or Galactic coordinate system.
92  ***************************************************************************/
94  const double& radius,
95  const double& width,
96  const std::string& coordsys) :
98 {
99  // Throw an exception if the coordinate system is invalid
100  if ((coordsys != "CEL") && (coordsys != "GAL")) {
101  std::string msg = "Invalid coordinate system \""+coordsys+"\" "
102  "specified. Please specify either \"CEL\" or "
103  "\"GAL\".";
105  }
106 
107  // Initialise members
108  init_members();
109 
110  // Set parameter names
111  if (coordsys == "CEL") {
112  m_lon.name("RA");
113  m_lat.name("DEC");
114  }
115  else {
116  m_lon.name("GLON");
117  m_lat.name("GLAT");
118  }
119 
120  // Assign center location, radius and width
121  this->dir(dir);
122  this->radius(radius);
123  this->width(width);
124 
125  // Return
126  return;
127 }
128 
129 
130 /***********************************************************************//**
131  * @brief XML constructor
132  *
133  * @param[in] xml XML element.
134  *
135  * Creates instance of radial ring model by extracting information from an
136  * XML element. See the read() method for more information about the expected
137  * structure of the XML element.
138  ***************************************************************************/
141 {
142  // Initialise members
143  init_members();
144 
145  // Read information from XML element
146  read(xml);
147 
148  // Return
149  return;
150 }
151 
152 
153 /***********************************************************************//**
154  * @brief Copy constructor
155  *
156  * @param[in] model Radial ring model.
157  ***************************************************************************/
159  GModelSpatialRadial(model)
160 {
161  // Initialise members
162  init_members();
163 
164  // Copy members
165  copy_members(model);
166 
167  // Return
168  return;
169 }
170 
171 
172 /***********************************************************************//**
173  * @brief Destructor
174  ***************************************************************************/
176 {
177  // Free members
178  free_members();
179 
180  // Return
181  return;
182 }
183 
184 
185 /*==========================================================================
186  = =
187  = Operators =
188  = =
189  ==========================================================================*/
190 
191 /***********************************************************************//**
192  * @brief Assignment operator
193  *
194  * @param[in] model Radial ring model.
195  * @return Radial ring model.
196  ***************************************************************************/
198 {
199  // Execute only if object is not identical
200  if (this != &model) {
201 
202  // Copy base class members
203  this->GModelSpatialRadial::operator=(model);
204 
205  // Free members
206  free_members();
207 
208  // Initialise members
209  init_members();
210 
211  // Copy members
212  copy_members(model);
213 
214  } // endif: object was not identical
215 
216  // Return
217  return *this;
218 }
219 
220 
221 /*==========================================================================
222  = =
223  = Public methods =
224  = =
225  ==========================================================================*/
226 
227 /***********************************************************************//**
228  * @brief Clear radial ring model
229  ***************************************************************************/
231 {
232  // Free class members (base and derived classes, derived class first)
233  free_members();
236 
237  // Initialise members
240  init_members();
241 
242  // Return
243  return;
244 }
245 
246 
247 /***********************************************************************//**
248  * @brief Clone radial ring model
249  *
250  * @return Pointer to deep copy of radial ring model.
251  ***************************************************************************/
253 {
254  // Clone radial ring model
255  return new GModelSpatialRadialRing(*this);
256 }
257 
258 
259 /***********************************************************************//**
260  * @brief Evaluate function (in units of sr^-1)
261  *
262  * @param[in] theta Angular distance from ring centre (radians).
263  * @param[in] energy Photon energy.
264  * @param[in] time Photon arrival time.
265  * @param[in] gradients Compute gradients?
266  * @return Model value.
267  *
268  * Evaluates the spatial component for a ring source model using
269  *
270  * \f[
271  * S_{\rm p}(\vec{p} | E, t) = \left \{
272  * \begin{array}{l l}
273  * \displaystyle
274  * {\tt m\_norm}
275  * & \mbox{if $\theta \le $ outer radius and $\theta \ge $ inner radius} \\
276  * \\
277  * \displaystyle
278  * 0 & \mbox{if $\theta > $ outer radius or $\theta < $ inner radius}
279  * \end{array}
280  * \right .
281  * \f]
282  *
283  * where
284  * - \f$\theta\f$ is the angular separation between ring centre and the
285  * sky direction of interest, and
286  * - \f${\tt m\_norm} = \frac{1}{2 \pi (\cos r_i - \cos r_o)} \f$ is a normalization
287  * constant (see the update() method for details).
288  *
289  * The method will not compute analytical parameter gradients, even if the
290  * @p gradients argument is set to true. Radial ring parameter gradients
291  * need to be computed numerically.
292  ***************************************************************************/
293 double GModelSpatialRadialRing::eval(const double& theta,
294  const GEnergy& energy,
295  const GTime& time,
296  const bool& gradients) const
297 {
298  // Update precomputation cache
299  update();
300 
301  // Set value
302  double value = 0.0;
303  if ((theta >= m_inner_radius_rad) && (theta <= m_outer_radius_rad)) {
304  value = m_norm;
305  }
306 
307  // Compile option: Check for NaN/Inf
308  #if defined(G_NAN_CHECK)
309  if (gammalib::is_notanumber(value) || gammalib::is_infinite(value)) {
310  std::cout << "*** ERROR: GModelSpatialRadialRing::eval";
311  std::cout << "(theta=" << theta << "): NaN/Inf encountered";
312  std::cout << " (value=" << value;
313  std::cout << ", m_inner_radius_rad=" << m_inner_radius_rad;
314  std::cout << ", m_outer_radius_rad=" << m_outer_radius_rad;
315  std::cout << ", m_norm=" << m_norm;
316  std::cout << ")" << std::endl;
317  }
318  #endif
319 
320  // Return value
321  return value;
322 }
323 
324 
325 /***********************************************************************//**
326  * @brief Return MC sky direction
327  *
328  * @param[in] energy Photon energy.
329  * @param[in] time Photon arrival time.
330  * @param[in,out] ran Random number generator.
331  * @return Sky direction.
332  *
333  * Draws an arbitrary sky direction from the 2D ring distribution as function
334  * of the photon @p energy and arrival @p time.
335  ***************************************************************************/
337  const GTime& time,
338  GRan& ran) const
339 {
340  // Update precomputation cache
341  update();
342 
343  // Simulate offset from photon arrival direction
344  double theta = std::acos(m_cos_inner_radius_rad - ran.uniform() *
347  double phi = 360.0 * ran.uniform();
348 
349  // Rotate sky direction by offset
350  GSkyDir sky_dir = dir();
351  sky_dir.rotate_deg(phi, theta);
352 
353  // Return sky direction
354  return sky_dir;
355 }
356 
357 
358 /***********************************************************************//**
359  * @brief Checks whether model contains specified sky direction
360  *
361  * @param[in] dir Sky direction.
362  * @param[in] margin Margin to be added to sky direction (degrees)
363  * @return True if the model contains the sky direction.
364  *
365  * Signals whether a sky direction is contained in the radial ring model.
366  ***************************************************************************/
368  const double& margin) const
369 {
370  // Compute distance to centre (radians)
371  double distance = dir.dist(this->dir());
372 
373  // Compute margin in radians
374  double margin_rad = margin * gammalib::deg2rad;
375  double distance_min = theta_min() - margin_rad;
376  double distance_max = theta_max() + margin_rad;
377  if (distance_min < 0.0) {
378  distance_min = 0.0;
379  }
380 
381  // Return flag
382  return ((distance >= distance_min) && (distance <= distance_max));
383 }
384 
385 
386 /***********************************************************************//**
387  * @brief Return minimum model radius (in radians)
388  *
389  * @return Minimum model radius (in radians).
390  ***************************************************************************/
392 {
393  // Return value
394  return (radius() * gammalib::deg2rad);
395 }
396 
397 
398 /***********************************************************************//**
399  * @brief Return maximum model radius (in radians)
400  *
401  * @return Maximum model radius (in radians).
402  ***************************************************************************/
404 {
405  // Return value
406  return ((radius()+width()) * gammalib::deg2rad);
407 }
408 
409 
410 /***********************************************************************//**
411  * @brief Read model from XML element
412  *
413  * @param[in] xml XML element.
414  *
415  * Reads the radial ring model information from an XML element. The XML
416  * element shall have either the format
417  *
418  * <spatialModel type="RadialRing">
419  * <parameter name="RA" scale="1.0" value="83.6331" min="-360" max="360" free="1"/>
420  * <parameter name="DEC" scale="1.0" value="22.0145" min="-90" max="90" free="1"/>
421  * <parameter name="Radius" scale="1.0" value="0.45" min="0.01" max="10" free="1"/>
422  * <parameter name="Width" scale="1.0" value="0.15" min="0.01" max="10" free="1"/>
423  * </spatialModel>
424  *
425  * or
426  *
427  * <spatialModel type="RadialRing">
428  * <parameter name="GLON" scale="1.0" value="83.6331" min="-360" max="360" free="1"/>
429  * <parameter name="GLAT" scale="1.0" value="22.0145" min="-90" max="90" free="1"/>
430  * <parameter name="Radius" scale="1.0" value="0.45" min="0.01" max="10" free="1"/>
431  * <parameter name="Width" scale="1.0" value="0.15" min="0.01" max="10" free="1"/>
432  * </spatialModel>
433  *
434  * @todo Implement a test of the radius and width. Both parameters should
435  * be >0.
436  ***************************************************************************/
438 {
439  // Verify number of model parameters
441 
442  // Read ring center location
444 
445  // Get parameters
448 
449  // Read parameters
450  m_radius.read(*radius);
451  m_width.read(*width);
452 
453  // Return
454  return;
455 }
456 
457 
458 /***********************************************************************//**
459  * @brief Write model into XML element
460  *
461  * @param[in] xml XML element into which model information is written.
462  *
463  * Writes the radial ring model information into an XML element. The XML
464  * element will have the format
465  *
466  * <spatialModel type="RadialRing">
467  * <parameter name="RA" scale="1.0" value="83.6331" min="-360" max="360" free="1"/>
468  * <parameter name="DEC" scale="1.0" value="22.0145" min="-90" max="90" free="1"/>
469  * <parameter name="Radius" scale="1.0" value="0.45" min="0.01" max="10" free="1"/>
470  * <parameter name="Width" scale="1.0" value="0.15" min="0.01" max="10" free="1"/>
471  * </spatialModel>
472  *
473  ***************************************************************************/
475 {
476  // Verify model type
478 
479  // Write ring center location
481 
482  // Get or create parameters
485 
486  // Write parameters
487  m_radius.write(*radius);
488  m_width.write(*width);
489 
490  // Return
491  return;
492 }
493 
494 
495 /***********************************************************************//**
496  * @brief Print information
497  *
498  * @param[in] chatter Chattiness.
499  * @return String containing model information.
500  ***************************************************************************/
501 std::string GModelSpatialRadialRing::print(const GChatter& chatter) const
502 {
503  // Initialise result string
504  std::string result;
505 
506  // Continue only if chatter is not silent
507  if (chatter != SILENT) {
508 
509  // Append header
510  result.append("=== GModelSpatialRadialRing ===");
511 
512  // Append parameters
513  result.append("\n"+gammalib::parformat("Number of parameters"));
514  result.append(gammalib::str(size()));
515  for (int i = 0; i < size(); ++i) {
516  result.append("\n"+m_pars[i]->print(chatter));
517  }
518 
519  } // endif: chatter was not silent
520 
521  // Return result
522  return result;
523 }
524 
525 
526 /*==========================================================================
527  = =
528  = Private methods =
529  = =
530  ==========================================================================*/
531 
532 /***********************************************************************//**
533  * @brief Initialise class members
534  ***************************************************************************/
536 {
537  // Initialise model type
538  m_type = "RadialRing";
539 
540  // Initialise Radius
541  m_radius.clear();
542  m_radius.name("Radius");
543  m_radius.unit("deg");
544  m_radius.value(2.778e-4); // 1 arcsec
545  m_radius.min(2.778e-4); // 1 arcsec
546  m_radius.free();
547  m_radius.scale(1.0);
548  m_radius.gradient(0.0);
549  m_radius.has_grad(false); // Radial components never have gradients
550 
551  // Initialise Width
552  m_width.clear();
553  m_width.name("Width");
554  m_width.unit("deg");
555  m_width.value(2.778e-4); // 1 arcsec
556  m_width.min(2.778e-4); // 1 arcsec
557  m_width.free();
558  m_width.scale(1.0);
559  m_width.gradient(0.0);
560  m_width.has_grad(false); // Radial components never have gradients
561 
562  // Set parameter pointer(s)
563  m_pars.push_back(&m_radius);
564  m_pars.push_back(&m_width);
565 
566  // Initialise precomputation cache
567  m_last_radius = 0.0;
568  m_last_width = 0.0;
569  m_inner_radius_rad = 0.0;
570  m_outer_radius_rad = 0.0;
573  m_norm = 0.0;
574 
575  // Return
576  return;
577 }
578 
579 
580 /***********************************************************************//**
581  * @brief Copy class members
582  *
583  * @param[in] model Radial ring model.
584  *
585  * We do not have to push back the members on the parameter stack as this
586  * should have been done by init_members() that was called before. Otherwise
587  * we would have the radius twice on the stack.
588  ***************************************************************************/
590 {
591  // Copy members
592  m_type = model.m_type; // Needed to conserve model type
593  m_radius = model.m_radius;
594  m_width = model.m_width;
595 
596  // Copy precomputation cache
598  m_last_width = model.m_last_width;
603  m_norm = model.m_norm;
604 
605  // Return
606  return;
607 }
608 
609 
610 /***********************************************************************//**
611  * @brief Delete class members
612  ***************************************************************************/
614 {
615  // Return
616  return;
617 }
618 
619 
620 /***********************************************************************//**
621  * @brief Update precomputation cache
622  *
623  * Computes the normalization
624  * \f[
625  * {\tt m\_norm} = \frac{1}{2 \pi (\cos r_i - \cos r_o)}
626  * \f]
627  *
628  * This is the correct normalization on the sphere for any radii.
629  ***************************************************************************/
631 {
632  // Update if radius or width has changed
633  if ((m_last_radius != radius()) || (m_last_width != width())) {
634 
635  // Store last values
636  m_last_radius = radius();
637  m_last_width = width();
638 
639  // Compute ring parameters in radians
644 
645  // Perform precomputations
646  double denom = gammalib::twopi * (m_cos_inner_radius_rad -
648  m_norm = (denom > 0.0) ? 1.0 / denom : 0.0;
649 
650  } // endif: update required
651 
652  // Return
653  return;
654 }
655 
656 
657 /***********************************************************************//**
658  * @brief Set boundary sky region
659  ***************************************************************************/
661 {
662  // Set sky region circle (maximum Gaussian sigma times a scaling
663  // factor (actually 3))
665 
666  // Set region (circumvent const correctness)
667  const_cast<GModelSpatialRadialRing*>(this)->m_region = region;
668 
669  // Return
670  return;
671 }
double m_outer_radius_rad
Outer radius in radians.
const std::string & name(void) const
Return parameter name.
double gradient(void) const
Return parameter gradient.
int size(void) const
Return number of parameters.
void write(GXmlElement &xml) const
Set or update parameter attributes in XML element.
Definition: GModelPar.cpp:351
virtual double theta_min(void) const
Return minimum model radius (in radians)
GVector cos(const GVector &vector)
Computes cosine of vector elements.
Definition: GVector.cpp:1190
virtual std::string print(const GChatter &chatter=NORMAL) const
Print information.
GModelPar m_width
Ring width (degrees)
XML element node class.
Definition: GXmlElement.hpp:48
double m_last_width
Last ring width.
double acos(const double &arg)
Computes acos by avoiding NaN due to rounding errors.
Definition: GMath.cpp:69
Random number generator class.
Definition: GRan.hpp:44
void init_members(void)
Initialise class members.
Time class.
Definition: GTime.hpp:55
Gammalib tools definition.
Interface for the circular sky region class.
Interface definition for the spatial model registry class.
double min(void) const
Return parameter minimum boundary.
void init_members(void)
Initialise class members.
void free_members(void)
Delete class members.
virtual void read(const GXmlElement &xml)
Read model from XML element.
virtual GModelSpatialRadialRing * clone(void) const
Clone radial ring model.
GSkyRegionCircle m_region
Bounding circle.
virtual ~GModelSpatialRadialRing(void)
Destructor.
GModelPar m_radius
Ring inner radius (degrees)
bool is_notanumber(const double &x)
Signal if argument is not a number.
Definition: GTools.hpp:201
const double & scale(void) const
Return parameter scale.
bool is_infinite(const double &x)
Signal if argument is infinite.
Definition: GTools.hpp:184
const double deg2rad
Definition: GMath.hpp:43
virtual double theta_max(void) const
Return maximum model radius (in radians)
void update(void) const
Update precomputation cache.
bool has_grad(void) const
Signal if parameter gradient is computed analytically.
#define G_WRITE
virtual void write(GXmlElement &xml) const
Write model into XML element.
virtual GModelSpatialRadialRing & operator=(const GModelSpatialRadialRing &model)
Assignment operator.
virtual bool contains(const GSkyDir &dir, const double &margin=0.0) const
Checks whether model contains specified sky direction.
const GSkyDir & dir(void) const
Return position of radial spatial model.
void free_members(void)
Delete class members.
void free(void)
Free a parameter.
#define G_CONSTRUCTOR
std::vector< GModelPar * > m_pars
Parameter pointers.
void rotate_deg(const double &phi, const double &theta)
Rotate sky direction by zenith and azimuth angle.
Definition: GSkyDir.cpp:424
double width(void) const
Return ring width.
GXmlElement * xml_need_par(const std::string &origin, GXmlElement &xml, const std::string &name)
Return pointer to parameter with given name in XML element.
Definition: GTools.cpp:1637
virtual void read(const GXmlElement &xml)
Read model from XML element.
Radial ring model class interface definition.
double uniform(void)
Returns random double precision floating value in range 0 to 1.
Definition: GRan.cpp:242
void clear(void)
Clear parameter.
Spatial model registry class definition.
virtual double eval(const double &theta, const GEnergy &energy, const GTime &time, const bool &gradients=false) const
Evaluate function (in units of sr^-1)
GChatter
Definition: GTypemaps.hpp:33
void copy_members(const GModelSpatialRadialRing &model)
Copy class members.
std::string type(void) const
Return model type.
void xml_check_parnum(const std::string &origin, const GXmlElement &xml, const int &number)
Checks number of parameters.
Definition: GTools.cpp:1777
virtual void set_region(void) const
Set boundary sky region.
virtual void write(GXmlElement &xml) const
Write model into XML element.
double m_cos_outer_radius_rad
Cosine of outer radius in radians.
const GSkyRegion * region(void) const
Return boundary sky region.
virtual GModelSpatialRadial & operator=(const GModelSpatialRadial &model)
Assignment operator.
void free_members(void)
Delete class members.
std::string m_type
Spatial model type.
GModelPar m_lat
Declination or Galactic latitude (deg)
virtual GSkyDir mc(const GEnergy &energy, const GTime &time, GRan &ran) const
Return MC sky direction.
#define G_READ
void read(const GXmlElement &xml)
Extract parameter attributes from XML element.
Definition: GModelPar.cpp:229
double value(void) const
Return parameter value.
GModelPar m_lon
Right Ascension or Galactic longitude (deg)
const std::string & unit(void) const
Return parameter unit.
Exception handler interface definition.
double dist(const GSkyDir &dir) const
Compute angular distance between sky directions in radians.
Definition: GSkyDir.hpp:271
Abstract radial spatial model base class.
void init_members(void)
Initialise class members.
const GModelSpatialRadialRing g_radial_ring_seed
double radius(void) const
Return ring inner radius.
const double twopi
Definition: GMath.hpp:36
double m_last_radius
Last ring radius.
const double rad2deg
Definition: GMath.hpp:44
double m_inner_radius_rad
Inner radius in radians.
double m_cos_inner_radius_rad
Cosine of inner radius in radians.
std::string parformat(const std::string &s, const int &indent=0)
Convert string in parameter format.
Definition: GTools.cpp:1143
virtual void clear(void)
Clear radial ring model.
Sky direction class.
Definition: GSkyDir.hpp:62
GModelSpatialRadialRing(void)
Void constructor.
const GXmlElement * xml_get_par(const std::string &origin, const GXmlElement &xml, const std::string &name)
Return pointer to parameter with given name in XML element.
Definition: GTools.cpp:1689
Mathematical function definitions.
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
void xml_check_type(const std::string &origin, GXmlElement &xml, const std::string &type)
Checks the model type.
Definition: GTools.cpp:1819