Commit 27979db87a234869bdf032a2155a3e51d1f39c6c

Authored by Jürgen Knödlseder
1 parent 2a49e210

Consistently use small angle approximation in GModelSpatialEllipticalGauss and t…

…runcate ellipse at 3 sigma (#1561).
inst/cta/src/GCTAResponse_helpers.cpp
@@ -864,11 +864,15 @@ double cta_irf_elliptical_kern_omega::eval(const double& omega) @@ -864,11 +864,15 @@ double cta_irf_elliptical_kern_omega::eval(const double& omega)
864 double arg2 = m_semimajor_rad * sinus; 864 double arg2 = m_semimajor_rad * sinus;
865 double r_ellipse = m_semiminor_rad * m_semimajor_rad / 865 double r_ellipse = m_semiminor_rad * m_semimajor_rad /
866 std::sqrt(arg1*arg1 + arg2*arg2); 866 std::sqrt(arg1*arg1 + arg2*arg2);
867 - std::cout << "*** WARNING: cta_irf_elliptical_kern_omega::eval"; 867 + std::cout << "*** WARNING: cta_irf_elliptical_kern_omega::eval:";
868 std::cout << " zero model for (rho,omega)=("; 868 std::cout << " zero model for (rho,omega)=(";
869 std::cout << m_rho*gammalib::rad2deg << ","; 869 std::cout << m_rho*gammalib::rad2deg << ",";
870 std::cout << omega*gammalib::rad2deg << ")"; 870 std::cout << omega*gammalib::rad2deg << ")";
871 - std::cout << " rho-r_ellipse=" << (m_rho-r_ellipse) << " radians"; 871 + std::cout << " semiminor=" << m_semiminor_rad;
  872 + std::cout << " semimajor=" << m_semimajor_rad;
  873 + std::cout << " posang=" << m_model->posangle() * gammalib::deg2rad;
  874 + std::cout << " rel_posang=" << diff_angle * gammalib::deg2rad;
  875 + std::cout << " rho/r_ellipse=" << (m_rho/r_ellipse);
872 std::cout << std::endl; 876 std::cout << std::endl;
873 } 877 }
874 #endif 878 #endif
src/model/GModelSpatialEllipticalGauss.cpp
@@ -37,7 +37,7 @@ @@ -37,7 +37,7 @@
37 37
38 /* __ Constants __________________________________________________________ */ 38 /* __ Constants __________________________________________________________ */
39 namespace { 39 namespace {
40 - const double c_theta_max = 2.0; //!< semiaxis multiplied for theta_max() 40 + const double c_theta_max = 3.0; //!< semiaxis multiplied for theta_max()
41 const double c_max_exponent = 0.5 * c_theta_max * c_theta_max; 41 const double c_max_exponent = 0.5 * c_theta_max * c_theta_max;
42 const double c_fraction = 1.0 - std::exp(-c_max_exponent); 42 const double c_fraction = 1.0 - std::exp(-c_max_exponent);
43 } 43 }
@@ -53,6 +53,7 @@ const GModelSpatialRegistry g_elliptical_gauss_registry(&amp;g_elliptical_gauss_seed @@ -53,6 +53,7 @@ const GModelSpatialRegistry g_elliptical_gauss_registry(&amp;g_elliptical_gauss_seed
53 /* __ Macros _____________________________________________________________ */ 53 /* __ Macros _____________________________________________________________ */
54 54
55 /* __ Coding definitions _________________________________________________ */ 55 /* __ Coding definitions _________________________________________________ */
  56 +#define G_SMALL_ANGLE_APPROXIMATION //!< Use small angle approximation
56 57
57 /* __ Debug definitions __________________________________________________ */ 58 /* __ Debug definitions __________________________________________________ */
58 59
@@ -78,14 +79,14 @@ GModelSpatialEllipticalGauss::GModelSpatialEllipticalGauss(void) : @@ -78,14 +79,14 @@ GModelSpatialEllipticalGauss::GModelSpatialEllipticalGauss(void) :
78 79
79 80
80 /***********************************************************************//** 81 /***********************************************************************//**
81 - * @brief Elliptical Gauss constructor 82 + * @brief Elliptical Gaussian constructor
82 * 83 *
83 - * @param[in] dir Sky position of gauss centre. 84 + * @param[in] dir Centre of elliptical Gaussian.
84 * @param[in] semimajor Semi-major axis (degrees). 85 * @param[in] semimajor Semi-major axis (degrees).
85 * @param[in] semiminor Semi-minor axis (degrees). 86 * @param[in] semiminor Semi-minor axis (degrees).
86 * @param[in] posangle Position angle of semi-major axis (degrees). 87 * @param[in] posangle Position angle of semi-major axis (degrees).
87 * 88 *
88 - * Construct elliptical gauss model from sky position of the ellipse centre 89 + * Construct elliptical Gaussian model from the ellipse centre direction
89 * (@p dir), the @p semimajor and @p semiminor axes, and the position 90 * (@p dir), the @p semimajor and @p semiminor axes, and the position
90 * angle (@p posangle). 91 * angle (@p posangle).
91 ***************************************************************************/ 92 ***************************************************************************/
@@ -114,7 +115,7 @@ GModelSpatialEllipticalGauss::GModelSpatialEllipticalGauss(const GSkyDir&amp; dir, @@ -114,7 +115,7 @@ GModelSpatialEllipticalGauss::GModelSpatialEllipticalGauss(const GSkyDir&amp; dir,
114 * 115 *
115 * @param[in] xml XML element. 116 * @param[in] xml XML element.
116 * 117 *
117 - * Constructs elliptical gauss model by extracting information from an XML 118 + * Constructs elliptical Gaussian model by extracting information from an XML
118 * element. See the read() method for more information about the expected 119 * element. See the read() method for more information about the expected
119 * structure of the XML element. 120 * structure of the XML element.
120 ***************************************************************************/ 121 ***************************************************************************/
@@ -135,7 +136,7 @@ GModelSpatialEllipticalGauss::GModelSpatialEllipticalGauss(const GXmlElement&amp; xm @@ -135,7 +136,7 @@ GModelSpatialEllipticalGauss::GModelSpatialEllipticalGauss(const GXmlElement&amp; xm
135 /***********************************************************************//** 136 /***********************************************************************//**
136 * @brief Copy constructor 137 * @brief Copy constructor
137 * 138 *
138 - * @param[in] model Elliptical gauss model. 139 + * @param[in] model Elliptical Gaussian model.
139 ***************************************************************************/ 140 ***************************************************************************/
140 GModelSpatialEllipticalGauss::GModelSpatialEllipticalGauss(const GModelSpatialEllipticalGauss& model) : 141 GModelSpatialEllipticalGauss::GModelSpatialEllipticalGauss(const GModelSpatialEllipticalGauss& model) :
141 GModelSpatialElliptical(model) 142 GModelSpatialElliptical(model)
@@ -174,7 +175,7 @@ GModelSpatialEllipticalGauss::~GModelSpatialEllipticalGauss(void) @@ -174,7 +175,7 @@ GModelSpatialEllipticalGauss::~GModelSpatialEllipticalGauss(void)
174 * @brief Assignment operator 175 * @brief Assignment operator
175 * 176 *
176 * @param[in] model Elliptical gauss model. 177 * @param[in] model Elliptical gauss model.
177 - * @return Elliptical gauss model. 178 + * @return Elliptical Gaussian model.
178 ***************************************************************************/ 179 ***************************************************************************/
179 GModelSpatialEllipticalGauss& GModelSpatialEllipticalGauss::operator=(const GModelSpatialEllipticalGauss& model) 180 GModelSpatialEllipticalGauss& GModelSpatialEllipticalGauss::operator=(const GModelSpatialEllipticalGauss& model)
180 { 181 {
@@ -207,7 +208,7 @@ GModelSpatialEllipticalGauss&amp; GModelSpatialEllipticalGauss::operator=(const GMod @@ -207,7 +208,7 @@ GModelSpatialEllipticalGauss&amp; GModelSpatialEllipticalGauss::operator=(const GMod
207 ==========================================================================*/ 208 ==========================================================================*/
208 209
209 /***********************************************************************//** 210 /***********************************************************************//**
210 - * @brief Clear elliptical gauss model 211 + * @brief Clear elliptical Gaussian model
211 ***************************************************************************/ 212 ***************************************************************************/
212 void GModelSpatialEllipticalGauss::clear(void) 213 void GModelSpatialEllipticalGauss::clear(void)
213 { 214 {
@@ -227,13 +228,13 @@ void GModelSpatialEllipticalGauss::clear(void) @@ -227,13 +228,13 @@ void GModelSpatialEllipticalGauss::clear(void)
227 228
228 229
229 /***********************************************************************//** 230 /***********************************************************************//**
230 - * @brief Clone elliptical gauss model 231 + * @brief Clone elliptical Gaussian model
231 * 232 *
232 - * @return Pointer to deep copy of elliptical gauss model. 233 + * @return Pointer to deep copy of elliptical Gaussian model.
233 ***************************************************************************/ 234 ***************************************************************************/
234 GModelSpatialEllipticalGauss* GModelSpatialEllipticalGauss::clone(void) const 235 GModelSpatialEllipticalGauss* GModelSpatialEllipticalGauss::clone(void) const
235 { 236 {
236 - // Clone elliptical gauss model 237 + // Clone elliptical Gaussian model
237 return new GModelSpatialEllipticalGauss(*this); 238 return new GModelSpatialEllipticalGauss(*this);
238 } 239 }
239 240
@@ -249,46 +250,48 @@ GModelSpatialEllipticalGauss* GModelSpatialEllipticalGauss::clone(void) const @@ -249,46 +250,48 @@ GModelSpatialEllipticalGauss* GModelSpatialEllipticalGauss::clone(void) const
249 * 250 *
250 * Evaluates the spatial component for an elliptical Gaussian source model. 251 * Evaluates the spatial component for an elliptical Gaussian source model.
251 * 252 *
252 - * The source model is an elliptical Gaussian function 253 + * The elliptical Gaussian function is defined by
253 * \f$S_{\rm p}(\theta, \phi | E, t)\f$, where 254 * \f$S_{\rm p}(\theta, \phi | E, t)\f$, where
254 * \f$\theta\f$ is the angular separation between elliptical Gaussian centre 255 * \f$\theta\f$ is the angular separation between elliptical Gaussian centre
255 * and the actual location and \f$\phi\f$ the position angle with respect to 256 * and the actual location and \f$\phi\f$ the position angle with respect to
256 * the model centre, counted counterclockwise from North. 257 * the model centre, counted counterclockwise from North.
257 * 258 *
258 - * The function \f$f(\theta, \phi)\f$ is given by 259 + * The function \f$S_{\rm p}(\theta, \phi | E, t)\f$ is given by
259 * 260 *
260 * \f[ 261 * \f[
261 - * S_{\rm p}(\theta, \phi | E, t) = \left \{  
262 - * \begin{array}{l l}  
263 - * {\tt m\_norm}  
264 - * & \mbox{if} \, \, \theta \le \theta_0 \\  
265 - * \\  
266 - * 0 & \mbox{else}  
267 - * \end{array}  
268 - * \right . 262 + * S_{\rm p}(\theta, \phi | E, t) = {\tt m\_norm} \times
  263 + * \exp \left( -\frac{\theta^2}{2 r_{rm eff}} \right)
269 * \f] 264 * \f]
270 * 265 *
271 - * where \f$\theta_0\f$ is the effective radius of the ellipse on the sphere  
272 - * given by  
273 - *  
274 - * \f[\theta_0\ =  
275 - * \frac{ab}{\sqrt{b^2 \cos^2(\phi-\phi_0) + a^2 \sin^2(\phi-\phi_0)}}\f] 266 + * where the effective ellipse radius \f$r_{rm eff}\f$ towards a given
  267 + * position angle is given by
276 * 268 *
277 - * and 269 + * \f[
  270 + * r_{rm eff} = \frac{ab}
  271 + * {\sqrt{\left( a \sin (\phi - \phi_0) \right)^2 +
  272 + * \sqrt{\left( b \cos (\phi - \phi_0) \right)^2}
  273 + * \f]
  274 + * and
278 * \f$a\f$ is the semi-major axis of the ellipse, 275 * \f$a\f$ is the semi-major axis of the ellipse,
279 * \f$b\f$ is the semi-minor axis, and 276 * \f$b\f$ is the semi-minor axis, and
280 * \f$\phi_0\f$ is the position angle of the ellipse, counted 277 * \f$\phi_0\f$ is the position angle of the ellipse, counted
281 * counterclockwise from North. 278 * counterclockwise from North.
  279 + * \f${\tt m\_norm}\f$ is a normalization constant given by
282 * 280 *
283 - * The normalisation constant \f${\tt m\_norm}\f$ which is the inverse of the  
284 - * solid angle subtended by an ellipse is given by  
285 * \f[ 281 * \f[
286 * {\tt m\_norm} = \frac{1}{2 \pi \times a \times b} 282 * {\tt m\_norm} = \frac{1}{2 \pi \times a \times b}
287 * \f] 283 * \f]
288 * 284 *
289 * @warning 285 * @warning
290 - * The normalization of the elliptical Gaussian is only valid in the small  
291 - * angle approximation. 286 + * The above formula for an elliptical Gaussian are accurate for small
  287 + * angles, with semimajor and semiminor axes below a few degrees. This
  288 + * should be fine for most use cases, but you should be aware that the
  289 + * ellipse will get distorted for larger angles.
  290 + *
  291 + * @warning
  292 + * For numerical reasons the elliptical Gaussian will be truncated for
  293 + * \f$\theta\f$ angles that correspond to 3 times the effective ellipse
  294 + * radius.
292 ***************************************************************************/ 295 ***************************************************************************/
293 double GModelSpatialEllipticalGauss::eval(const double& theta, 296 double GModelSpatialEllipticalGauss::eval(const double& theta,
294 const double& posangle, 297 const double& posangle,
@@ -304,6 +307,18 @@ double GModelSpatialEllipticalGauss::eval(const double&amp; theta, @@ -304,6 +307,18 @@ double GModelSpatialEllipticalGauss::eval(const double&amp; theta,
304 // Update precomputation cache 307 // Update precomputation cache
305 update(); 308 update();
306 309
  310 + // Perform computations to compute exponent
  311 + #if defined(G_SMALL_ANGLE_APPROXIMATION)
  312 + double rel_posangle = posangle - this->posangle() * gammalib::deg2rad;
  313 + double cosinus = std::cos(rel_posangle);
  314 + double sinus = std::sin(rel_posangle);
  315 + double arg1 = m_minor_rad * cosinus;
  316 + double arg2 = m_major_rad * sinus;
  317 + double r_ellipse = m_minor_rad * m_major_rad /
  318 + std::sqrt(arg1*arg1 + arg2*arg2);
  319 + double r_relative = theta/r_ellipse;
  320 + double exponent = 0.5*r_relative*r_relative;
  321 + #else
307 // Perform computations 322 // Perform computations
308 double sinphi = std::sin(posangle); 323 double sinphi = std::sin(posangle);
309 double cosphi = std::cos(posangle); 324 double cosphi = std::cos(posangle);
@@ -314,10 +329,13 @@ double GModelSpatialEllipticalGauss::eval(const double&amp; theta, @@ -314,10 +329,13 @@ double GModelSpatialEllipticalGauss::eval(const double&amp; theta,
314 double term3 = m_term3 * sinphi * cosphi; 329 double term3 = m_term3 * sinphi * cosphi;
315 330
316 // Compute exponent 331 // Compute exponent
317 - double exponent = theta * theta * (term1 + term2 + term3); 332 + double exponent = theta * theta * (term1 + term2 + term3);
  333 + #endif
318 334
319 - // Set value  
320 - value = m_norm * std::exp(-exponent); 335 + // Set value if the exponent is within the truncation region
  336 + if (exponent <= c_max_exponent) {
  337 + value = m_norm * std::exp(-exponent);
  338 + }
321 339
322 // Compile option: Check for NaN/Inf 340 // Compile option: Check for NaN/Inf
323 #if defined(G_NAN_CHECK) 341 #if defined(G_NAN_CHECK)
@@ -350,7 +368,7 @@ double GModelSpatialEllipticalGauss::eval(const double&amp; theta, @@ -350,7 +368,7 @@ double GModelSpatialEllipticalGauss::eval(const double&amp; theta,
350 * @return Model value. 368 * @return Model value.
351 * 369 *
352 * Evaluates the function value. No gradient computation is implemented as 370 * Evaluates the function value. No gradient computation is implemented as
353 - * Elliptical models will be convolved with the instrument response and thus 371 + * elliptical models will be convolved with the instrument response and thus
354 * require the numerical computation of the derivatives. 372 * require the numerical computation of the derivatives.
355 * 373 *
356 * See the eval() method for more information. 374 * See the eval() method for more information.
@@ -373,10 +391,12 @@ double GModelSpatialEllipticalGauss::eval_gradients(const double&amp; theta, @@ -373,10 +391,12 @@ double GModelSpatialEllipticalGauss::eval_gradients(const double&amp; theta,
373 * @param[in,out] ran Random number generator. 391 * @param[in,out] ran Random number generator.
374 * @return Sky direction. 392 * @return Sky direction.
375 * 393 *
376 - * Draws an arbitrary sky direction from the 2D ellipital Gaussian model.  
377 - * The drawn sky direction will be truncated to \f$2 \sigma\f$ offsets from  
378 - * the ellipse centre, where \f$\sigma\f$ is the effective Gaussian size  
379 - * in the direction of a specific position angle. 394 + * Draws an arbitrary sky direction from the elliptical Gaussian model.
  395 + *
  396 + * @warning
  397 + * For numerical reasons the elliptical Gaussian will be truncated for
  398 + * \f$\theta\f$ angles that correspond to 3 times the effective ellipse
  399 + * radius.
380 ***************************************************************************/ 400 ***************************************************************************/
381 GSkyDir GModelSpatialEllipticalGauss::mc(const GEnergy& energy, 401 GSkyDir GModelSpatialEllipticalGauss::mc(const GEnergy& energy,
382 const GTime& time, 402 const GTime& time,
@@ -448,12 +468,10 @@ bool GModelSpatialEllipticalGauss::contains(const GSkyDir&amp; dir, @@ -448,12 +468,10 @@ bool GModelSpatialEllipticalGauss::contains(const GSkyDir&amp; dir,
448 * 468 *
449 * @return Returns maximum model radius. 469 * @return Returns maximum model radius.
450 * 470 *
451 - * Returns the maximum of \f$2\f$ semimajor() and \f$2\f$ semiminor() as 471 + * Returns the maximum of \f$3\f$ semimajor() and \f$3\f$ semiminor() as
452 * approximate edge of the Gaussian. This limit is of course arbitrary, but 472 * approximate edge of the Gaussian. This limit is of course arbitrary, but
453 * allows to limit the integration region for response computation. The value 473 * allows to limit the integration region for response computation. The value
454 - * of 2 has been determined by experiment. Ideally, a large value should  
455 - * be used to properly take into account the tails of the distribution, but  
456 - * this apparently leads to fit convergence problems (#1561). 474 + * of 3 has been determined by experiment (#1561).
457 ***************************************************************************/ 475 ***************************************************************************/
458 double GModelSpatialEllipticalGauss::theta_max(void) const 476 double GModelSpatialEllipticalGauss::theta_max(void) const
459 { 477 {
@@ -707,7 +725,7 @@ void GModelSpatialEllipticalGauss::init_members(void) @@ -707,7 +725,7 @@ void GModelSpatialEllipticalGauss::init_members(void)
707 /***********************************************************************//** 725 /***********************************************************************//**
708 * @brief Copy class members 726 * @brief Copy class members
709 * 727 *
710 - * @param[in] model Elliptical gauss model. 728 + * @param[in] model Elliptical Gaussian model.
711 ***************************************************************************/ 729 ***************************************************************************/
712 void GModelSpatialEllipticalGauss::copy_members(const GModelSpatialEllipticalGauss& model) 730 void GModelSpatialEllipticalGauss::copy_members(const GModelSpatialEllipticalGauss& model)
713 { 731 {
@@ -756,9 +774,6 @@ void GModelSpatialEllipticalGauss::free_members(void) @@ -756,9 +774,6 @@ void GModelSpatialEllipticalGauss::free_members(void)
756 * @warning 774 * @warning
757 * The normalization of the elliptical Gaussian is only valid in the small 775 * The normalization of the elliptical Gaussian is only valid in the small
758 * angle approximation. 776 * angle approximation.
759 - *  
760 - * @todo Generalize normalization to the sphere (for the moment only accurate  
761 - * in the small angle approximation).  
762 ***************************************************************************/ 777 ***************************************************************************/
763 void GModelSpatialEllipticalGauss::update() const 778 void GModelSpatialEllipticalGauss::update() const
764 { 779 {
@@ -792,6 +807,7 @@ void GModelSpatialEllipticalGauss::update() const @@ -792,6 +807,7 @@ void GModelSpatialEllipticalGauss::update() const
792 } // endif: update required 807 } // endif: update required
793 808
794 // Update chache if position angle changed 809 // Update chache if position angle changed
  810 + #if !defined(G_SMALL_ANGLE_APPROXIMATION)
795 if (m_last_posangle != posangle()) { 811 if (m_last_posangle != posangle()) {
796 812
797 // Signal parameter changes 813 // Signal parameter changes
@@ -823,6 +839,7 @@ void GModelSpatialEllipticalGauss::update() const @@ -823,6 +839,7 @@ void GModelSpatialEllipticalGauss::update() const
823 m_term3 = 0.5 * (m_sin2pos / m_major2 - m_sin2pos / m_minor2); 839 m_term3 = 0.5 * (m_sin2pos / m_major2 - m_sin2pos / m_minor2);
824 840
825 } // endif: something has changed 841 } // endif: something has changed
  842 + #endif
826 843
827 // Return 844 // Return
828 return; 845 return;