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 864 double arg2 = m_semimajor_rad * sinus;
865 865 double r_ellipse = m_semiminor_rad * m_semimajor_rad /
866 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 868 std::cout << " zero model for (rho,omega)=(";
869 869 std::cout << m_rho*gammalib::rad2deg << ",";
870 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 876 std::cout << std::endl;
873 877 }
874 878 #endif
... ...
src/model/GModelSpatialEllipticalGauss.cpp
... ... @@ -37,7 +37,7 @@
37 37  
38 38 /* __ Constants __________________________________________________________ */
39 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 41 const double c_max_exponent = 0.5 * c_theta_max * c_theta_max;
42 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 53 /* __ Macros _____________________________________________________________ */
54 54  
55 55 /* __ Coding definitions _________________________________________________ */
  56 +#define G_SMALL_ANGLE_APPROXIMATION //!< Use small angle approximation
56 57  
57 58 /* __ Debug definitions __________________________________________________ */
58 59  
... ... @@ -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 85 * @param[in] semimajor Semi-major axis (degrees).
85 86 * @param[in] semiminor Semi-minor axis (degrees).
86 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 90 * (@p dir), the @p semimajor and @p semiminor axes, and the position
90 91 * angle (@p posangle).
91 92 ***************************************************************************/
... ... @@ -114,7 +115,7 @@ GModelSpatialEllipticalGauss::GModelSpatialEllipticalGauss(const GSkyDir&amp; dir,
114 115 *
115 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 119 * element. See the read() method for more information about the expected
119 120 * structure of the XML element.
120 121 ***************************************************************************/
... ... @@ -135,7 +136,7 @@ GModelSpatialEllipticalGauss::GModelSpatialEllipticalGauss(const GXmlElement&amp; xm
135 136 /***********************************************************************//**
136 137 * @brief Copy constructor
137 138 *
138   - * @param[in] model Elliptical gauss model.
  139 + * @param[in] model Elliptical Gaussian model.
139 140 ***************************************************************************/
140 141 GModelSpatialEllipticalGauss::GModelSpatialEllipticalGauss(const GModelSpatialEllipticalGauss& model) :
141 142 GModelSpatialElliptical(model)
... ... @@ -174,7 +175,7 @@ GModelSpatialEllipticalGauss::~GModelSpatialEllipticalGauss(void)
174 175 * @brief Assignment operator
175 176 *
176 177 * @param[in] model Elliptical gauss model.
177   - * @return Elliptical gauss model.
  178 + * @return Elliptical Gaussian model.
178 179 ***************************************************************************/
179 180 GModelSpatialEllipticalGauss& GModelSpatialEllipticalGauss::operator=(const GModelSpatialEllipticalGauss& model)
180 181 {
... ... @@ -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 213 void GModelSpatialEllipticalGauss::clear(void)
213 214 {
... ... @@ -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 235 GModelSpatialEllipticalGauss* GModelSpatialEllipticalGauss::clone(void) const
235 236 {
236   - // Clone elliptical gauss model
  237 + // Clone elliptical Gaussian model
237 238 return new GModelSpatialEllipticalGauss(*this);
238 239 }
239 240  
... ... @@ -249,46 +250,48 @@ GModelSpatialEllipticalGauss* GModelSpatialEllipticalGauss::clone(void) const
249 250 *
250 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 254 * \f$S_{\rm p}(\theta, \phi | E, t)\f$, where
254 255 * \f$\theta\f$ is the angular separation between elliptical Gaussian centre
255 256 * and the actual location and \f$\phi\f$ the position angle with respect to
256 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 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 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 275 * \f$a\f$ is the semi-major axis of the ellipse,
279 276 * \f$b\f$ is the semi-minor axis, and
280 277 * \f$\phi_0\f$ is the position angle of the ellipse, counted
281 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 281 * \f[
286 282 * {\tt m\_norm} = \frac{1}{2 \pi \times a \times b}
287 283 * \f]
288 284 *
289 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 296 double GModelSpatialEllipticalGauss::eval(const double& theta,
294 297 const double& posangle,
... ... @@ -304,6 +307,18 @@ double GModelSpatialEllipticalGauss::eval(const double&amp; theta,
304 307 // Update precomputation cache
305 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 322 // Perform computations
308 323 double sinphi = std::sin(posangle);
309 324 double cosphi = std::cos(posangle);
... ... @@ -314,10 +329,13 @@ double GModelSpatialEllipticalGauss::eval(const double&amp; theta,
314 329 double term3 = m_term3 * sinphi * cosphi;
315 330  
316 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 340 // Compile option: Check for NaN/Inf
323 341 #if defined(G_NAN_CHECK)
... ... @@ -350,7 +368,7 @@ double GModelSpatialEllipticalGauss::eval(const double&amp; theta,
350 368 * @return Model value.
351 369 *
352 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 372 * require the numerical computation of the derivatives.
355 373 *
356 374 * See the eval() method for more information.
... ... @@ -373,10 +391,12 @@ double GModelSpatialEllipticalGauss::eval_gradients(const double&amp; theta,
373 391 * @param[in,out] ran Random number generator.
374 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 401 GSkyDir GModelSpatialEllipticalGauss::mc(const GEnergy& energy,
382 402 const GTime& time,
... ... @@ -448,12 +468,10 @@ bool GModelSpatialEllipticalGauss::contains(const GSkyDir&amp; dir,
448 468 *
449 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 472 * approximate edge of the Gaussian. This limit is of course arbitrary, but
453 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 476 double GModelSpatialEllipticalGauss::theta_max(void) const
459 477 {
... ... @@ -707,7 +725,7 @@ void GModelSpatialEllipticalGauss::init_members(void)
707 725 /***********************************************************************//**
708 726 * @brief Copy class members
709 727 *
710   - * @param[in] model Elliptical gauss model.
  728 + * @param[in] model Elliptical Gaussian model.
711 729 ***************************************************************************/
712 730 void GModelSpatialEllipticalGauss::copy_members(const GModelSpatialEllipticalGauss& model)
713 731 {
... ... @@ -756,9 +774,6 @@ void GModelSpatialEllipticalGauss::free_members(void)
756 774 * @warning
757 775 * The normalization of the elliptical Gaussian is only valid in the small
758 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 778 void GModelSpatialEllipticalGauss::update() const
764 779 {
... ... @@ -792,6 +807,7 @@ void GModelSpatialEllipticalGauss::update() const
792 807 } // endif: update required
793 808  
794 809 // Update chache if position angle changed
  810 + #if !defined(G_SMALL_ANGLE_APPROXIMATION)
795 811 if (m_last_posangle != posangle()) {
796 812  
797 813 // Signal parameter changes
... ... @@ -823,6 +839,7 @@ void GModelSpatialEllipticalGauss::update() const
823 839 m_term3 = 0.5 * (m_sin2pos / m_major2 - m_sin2pos / m_minor2);
824 840  
825 841 } // endif: something has changed
  842 + #endif
826 843  
827 844 // Return
828 845 return;
... ...