Commit b1be222c8b2e3fe5f0df46031cb3a0e5cea7218a
Merge branch '1561-fix-ellipse-gauss' into integration
Showing
2 changed files
with
95 additions
and
56 deletions
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
| ... | ... | @@ -36,6 +36,11 @@ |
| 36 | 36 | #include "GModelSpatialRegistry.hpp" |
| 37 | 37 | |
| 38 | 38 | /* __ Constants __________________________________________________________ */ |
| 39 | +namespace { | |
| 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; | |
| 42 | + const double c_fraction = 1.0 - std::exp(-c_max_exponent); | |
| 43 | +} | |
| 39 | 44 | |
| 40 | 45 | /* __ Globals ____________________________________________________________ */ |
| 41 | 46 | const GModelSpatialEllipticalGauss g_elliptical_gauss_seed; |
| ... | ... | @@ -48,6 +53,7 @@ const GModelSpatialRegistry g_elliptical_gauss_registry(&g_elliptical_gauss_seed |
| 48 | 53 | /* __ Macros _____________________________________________________________ */ |
| 49 | 54 | |
| 50 | 55 | /* __ Coding definitions _________________________________________________ */ |
| 56 | +#define G_SMALL_ANGLE_APPROXIMATION //!< Use small angle approximation | |
| 51 | 57 | |
| 52 | 58 | /* __ Debug definitions __________________________________________________ */ |
| 53 | 59 | |
| ... | ... | @@ -73,14 +79,14 @@ GModelSpatialEllipticalGauss::GModelSpatialEllipticalGauss(void) : |
| 73 | 79 | |
| 74 | 80 | |
| 75 | 81 | /***********************************************************************//** |
| 76 | - * @brief Elliptical Gauss constructor | |
| 82 | + * @brief Elliptical Gaussian constructor | |
| 77 | 83 | * |
| 78 | - * @param[in] dir Sky position of gauss centre. | |
| 84 | + * @param[in] dir Centre of elliptical Gaussian. | |
| 79 | 85 | * @param[in] semimajor Semi-major axis (degrees). |
| 80 | 86 | * @param[in] semiminor Semi-minor axis (degrees). |
| 81 | 87 | * @param[in] posangle Position angle of semi-major axis (degrees). |
| 82 | 88 | * |
| 83 | - * Construct elliptical gauss model from sky position of the ellipse centre | |
| 89 | + * Construct elliptical Gaussian model from the ellipse centre direction | |
| 84 | 90 | * (@p dir), the @p semimajor and @p semiminor axes, and the position |
| 85 | 91 | * angle (@p posangle). |
| 86 | 92 | ***************************************************************************/ |
| ... | ... | @@ -109,7 +115,7 @@ GModelSpatialEllipticalGauss::GModelSpatialEllipticalGauss(const GSkyDir& dir, |
| 109 | 115 | * |
| 110 | 116 | * @param[in] xml XML element. |
| 111 | 117 | * |
| 112 | - * Constructs elliptical gauss model by extracting information from an XML | |
| 118 | + * Constructs elliptical Gaussian model by extracting information from an XML | |
| 113 | 119 | * element. See the read() method for more information about the expected |
| 114 | 120 | * structure of the XML element. |
| 115 | 121 | ***************************************************************************/ |
| ... | ... | @@ -130,7 +136,7 @@ GModelSpatialEllipticalGauss::GModelSpatialEllipticalGauss(const GXmlElement& xm |
| 130 | 136 | /***********************************************************************//** |
| 131 | 137 | * @brief Copy constructor |
| 132 | 138 | * |
| 133 | - * @param[in] model Elliptical gauss model. | |
| 139 | + * @param[in] model Elliptical Gaussian model. | |
| 134 | 140 | ***************************************************************************/ |
| 135 | 141 | GModelSpatialEllipticalGauss::GModelSpatialEllipticalGauss(const GModelSpatialEllipticalGauss& model) : |
| 136 | 142 | GModelSpatialElliptical(model) |
| ... | ... | @@ -169,7 +175,7 @@ GModelSpatialEllipticalGauss::~GModelSpatialEllipticalGauss(void) |
| 169 | 175 | * @brief Assignment operator |
| 170 | 176 | * |
| 171 | 177 | * @param[in] model Elliptical gauss model. |
| 172 | - * @return Elliptical gauss model. | |
| 178 | + * @return Elliptical Gaussian model. | |
| 173 | 179 | ***************************************************************************/ |
| 174 | 180 | GModelSpatialEllipticalGauss& GModelSpatialEllipticalGauss::operator=(const GModelSpatialEllipticalGauss& model) |
| 175 | 181 | { |
| ... | ... | @@ -202,7 +208,7 @@ GModelSpatialEllipticalGauss& GModelSpatialEllipticalGauss::operator=(const GMod |
| 202 | 208 | ==========================================================================*/ |
| 203 | 209 | |
| 204 | 210 | /***********************************************************************//** |
| 205 | - * @brief Clear elliptical gauss model | |
| 211 | + * @brief Clear elliptical Gaussian model | |
| 206 | 212 | ***************************************************************************/ |
| 207 | 213 | void GModelSpatialEllipticalGauss::clear(void) |
| 208 | 214 | { |
| ... | ... | @@ -222,13 +228,13 @@ void GModelSpatialEllipticalGauss::clear(void) |
| 222 | 228 | |
| 223 | 229 | |
| 224 | 230 | /***********************************************************************//** |
| 225 | - * @brief Clone elliptical gauss model | |
| 231 | + * @brief Clone elliptical Gaussian model | |
| 226 | 232 | * |
| 227 | - * @return Pointer to deep copy of elliptical gauss model. | |
| 233 | + * @return Pointer to deep copy of elliptical Gaussian model. | |
| 228 | 234 | ***************************************************************************/ |
| 229 | 235 | GModelSpatialEllipticalGauss* GModelSpatialEllipticalGauss::clone(void) const |
| 230 | 236 | { |
| 231 | - // Clone elliptical gauss model | |
| 237 | + // Clone elliptical Gaussian model | |
| 232 | 238 | return new GModelSpatialEllipticalGauss(*this); |
| 233 | 239 | } |
| 234 | 240 | |
| ... | ... | @@ -242,47 +248,50 @@ GModelSpatialEllipticalGauss* GModelSpatialEllipticalGauss::clone(void) const |
| 242 | 248 | * @param[in] time Photon arrival time. |
| 243 | 249 | * @return Model value. |
| 244 | 250 | * |
| 245 | - * Evaluates the spatial component for an elliptical gauss source model. The | |
| 246 | - * source model is an elliptical gaussian function | |
| 251 | + * Evaluates the spatial component for an elliptical Gaussian source model. | |
| 252 | + * | |
| 253 | + * The elliptical Gaussian function is defined by | |
| 247 | 254 | * \f$S_{\rm p}(\theta, \phi | E, t)\f$, where |
| 248 | - * \f$\theta\f$ is the angular separation between elliptical gauss centre and | |
| 249 | - * the actual location and \f$\phi\f$ the position angle with respect to the | |
| 250 | - * model centre, counted counterclockwise from North. | |
| 255 | + * \f$\theta\f$ is the angular separation between elliptical Gaussian centre | |
| 256 | + * and the actual location and \f$\phi\f$ the position angle with respect to | |
| 257 | + * the model centre, counted counterclockwise from North. | |
| 251 | 258 | * |
| 252 | - * 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 | |
| 253 | 260 | * |
| 254 | 261 | * \f[ |
| 255 | - * S_{\rm p}(\theta, \phi | E, t) = \left \{ | |
| 256 | - * \begin{array}{l l} | |
| 257 | - * {\tt m\_norm} | |
| 258 | - * & \mbox{if} \, \, \theta \le \theta_0 \\ | |
| 259 | - * \\ | |
| 260 | - * 0 & \mbox{else} | |
| 261 | - * \end{array} | |
| 262 | - * \right . | |
| 262 | + * S_{\rm p}(\theta, \phi | E, t) = {\tt m\_norm} \times | |
| 263 | + * \exp \left( -\frac{\theta^2}{2 r_{rm eff}} \right) | |
| 263 | 264 | * \f] |
| 264 | 265 | * |
| 265 | - * where \f$\theta_0\f$ is the effective radius of the ellipse on the sphere | |
| 266 | - * given by | |
| 266 | + * where the effective ellipse radius \f$r_{rm eff}\f$ towards a given | |
| 267 | + * position angle is given by | |
| 267 | 268 | * |
| 268 | - * \f[\theta_0\ = | |
| 269 | - * \frac{ab}{\sqrt{b^2 \cos^2(\phi-\phi_0) + a^2 \sin^2(\phi-\phi_0)}}\f] | |
| 270 | - * | |
| 271 | - * 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 | |
| 272 | 275 | * \f$a\f$ is the semi-major axis of the ellipse, |
| 273 | 276 | * \f$b\f$ is the semi-minor axis, and |
| 274 | 277 | * \f$\phi_0\f$ is the position angle of the ellipse, counted |
| 275 | 278 | * counterclockwise from North. |
| 279 | + * \f${\tt m\_norm}\f$ is a normalization constant given by | |
| 276 | 280 | * |
| 277 | - * The normalisation constant \f${\tt m\_norm}\f$ which is the inverse of the | |
| 278 | - * solid angle subtended by an ellipse is given by | |
| 279 | 281 | * \f[ |
| 280 | 282 | * {\tt m\_norm} = \frac{1}{2 \pi \times a \times b} |
| 281 | 283 | * \f] |
| 282 | 284 | * |
| 283 | 285 | * @warning |
| 284 | - * The normalization of the elliptical Gaussian is only valid in the small | |
| 285 | - * 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.0 times the effective ellipse | |
| 294 | + * radius. | |
| 286 | 295 | ***************************************************************************/ |
| 287 | 296 | double GModelSpatialEllipticalGauss::eval(const double& theta, |
| 288 | 297 | const double& posangle, |
| ... | ... | @@ -298,6 +307,18 @@ double GModelSpatialEllipticalGauss::eval(const double& theta, |
| 298 | 307 | // Update precomputation cache |
| 299 | 308 | update(); |
| 300 | 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); //!< small angle | |
| 319 | + double r_relative = theta/r_ellipse; | |
| 320 | + double exponent = 0.5*r_relative*r_relative; | |
| 321 | + #else | |
| 301 | 322 | // Perform computations |
| 302 | 323 | double sinphi = std::sin(posangle); |
| 303 | 324 | double cosphi = std::cos(posangle); |
| ... | ... | @@ -308,10 +329,13 @@ double GModelSpatialEllipticalGauss::eval(const double& theta, |
| 308 | 329 | double term3 = m_term3 * sinphi * cosphi; |
| 309 | 330 | |
| 310 | 331 | // Compute exponent |
| 311 | - double exponent = theta * theta * (term1 + term2 + term3); | |
| 332 | + double exponent = theta * theta * (term1 + term2 + term3); | |
| 333 | + #endif | |
| 312 | 334 | |
| 313 | - // Set value | |
| 314 | - 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 | + } | |
| 315 | 339 | |
| 316 | 340 | // Compile option: Check for NaN/Inf |
| 317 | 341 | #if defined(G_NAN_CHECK) |
| ... | ... | @@ -344,7 +368,7 @@ double GModelSpatialEllipticalGauss::eval(const double& theta, |
| 344 | 368 | * @return Model value. |
| 345 | 369 | * |
| 346 | 370 | * Evaluates the function value. No gradient computation is implemented as |
| 347 | - * Elliptical models will be convolved with the instrument response and thus | |
| 371 | + * elliptical models will be convolved with the instrument response and thus | |
| 348 | 372 | * require the numerical computation of the derivatives. |
| 349 | 373 | * |
| 350 | 374 | * See the eval() method for more information. |
| ... | ... | @@ -367,7 +391,12 @@ double GModelSpatialEllipticalGauss::eval_gradients(const double& theta, |
| 367 | 391 | * @param[in,out] ran Random number generator. |
| 368 | 392 | * @return Sky direction. |
| 369 | 393 | * |
| 370 | - * Draws an arbitrary sky position from the 2D gauss distribution. | |
| 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.0 times the effective ellipse | |
| 399 | + * radius. | |
| 371 | 400 | ***************************************************************************/ |
| 372 | 401 | GSkyDir GModelSpatialEllipticalGauss::mc(const GEnergy& energy, |
| 373 | 402 | const GTime& time, |
| ... | ... | @@ -382,10 +411,18 @@ GSkyDir GModelSpatialEllipticalGauss::mc(const GEnergy& energy, |
| 382 | 411 | photon.time(time); |
| 383 | 412 | |
| 384 | 413 | // Draw gaussian offset from each axis |
| 385 | - double theta1 = semimajor() * ran.normal(); | |
| 386 | - double theta2 = semiminor() * ran.normal(); | |
| 387 | - | |
| 388 | - // Compute total offset from model centre | |
| 414 | + double ran_major; | |
| 415 | + double ran_minor; | |
| 416 | + do { | |
| 417 | + ran_major = ran.normal(); | |
| 418 | + } while (ran_major > c_theta_max); | |
| 419 | + do { | |
| 420 | + ran_minor = ran.normal(); | |
| 421 | + } while (ran_minor > c_theta_max); | |
| 422 | + double theta1 = semimajor() * ran_major; | |
| 423 | + double theta2 = semiminor() * ran_minor; | |
| 424 | + | |
| 425 | + // Compute total offset from model centre in small angle approximation | |
| 389 | 426 | double theta = std::sqrt(theta1 * theta1 + theta2 * theta2); |
| 390 | 427 | |
| 391 | 428 | // Compute rotation angle, taking into account given position angle |
| ... | ... | @@ -431,19 +468,17 @@ bool GModelSpatialEllipticalGauss::contains(const GSkyDir& dir, |
| 431 | 468 | * |
| 432 | 469 | * @return Returns maximum model radius. |
| 433 | 470 | * |
| 434 | - * Returns the maximum of \f$2.5\f$ semimajor() and \f$2.5\f$ semiminor() as | |
| 471 | + * Returns the maximum of 3.0 semimajor() and 3.0 semiminor() as | |
| 435 | 472 | * approximate edge of the Gaussian. This limit is of course arbitrary, but |
| 436 | 473 | * allows to limit the integration region for response computation. The value |
| 437 | - * of 2.5 has been determined by experiment. Ideally, a large value should | |
| 438 | - * be used to properly take into account the tails of the distribution, but | |
| 439 | - * this apparently leads to fit convergence problems (#1561). | |
| 474 | + * of 3.0 has been determined by experiment (#1561). | |
| 440 | 475 | ***************************************************************************/ |
| 441 | 476 | double GModelSpatialEllipticalGauss::theta_max(void) const |
| 442 | 477 | { |
| 443 | 478 | // Set maximum model radius |
| 444 | 479 | double theta_max = (semimajor() > semiminor()) |
| 445 | - ? semimajor() * gammalib::deg2rad * 2.5 | |
| 446 | - : semiminor() * gammalib::deg2rad * 2.5; | |
| 480 | + ? semimajor() * gammalib::deg2rad * c_theta_max | |
| 481 | + : semiminor() * gammalib::deg2rad * c_theta_max; | |
| 447 | 482 | |
| 448 | 483 | // Return value |
| 449 | 484 | return theta_max; |
| ... | ... | @@ -690,7 +725,7 @@ void GModelSpatialEllipticalGauss::init_members(void) |
| 690 | 725 | /***********************************************************************//** |
| 691 | 726 | * @brief Copy class members |
| 692 | 727 | * |
| 693 | - * @param[in] model Elliptical gauss model. | |
| 728 | + * @param[in] model Elliptical Gaussian model. | |
| 694 | 729 | ***************************************************************************/ |
| 695 | 730 | void GModelSpatialEllipticalGauss::copy_members(const GModelSpatialEllipticalGauss& model) |
| 696 | 731 | { |
| ... | ... | @@ -739,9 +774,6 @@ void GModelSpatialEllipticalGauss::free_members(void) |
| 739 | 774 | * @warning |
| 740 | 775 | * The normalization of the elliptical Gaussian is only valid in the small |
| 741 | 776 | * angle approximation. |
| 742 | - * | |
| 743 | - * @todo Generalize normalization to the sphere (for the moment only accurate | |
| 744 | - * in the small angle approximation). | |
| 745 | 777 | ***************************************************************************/ |
| 746 | 778 | void GModelSpatialEllipticalGauss::update() const |
| 747 | 779 | { |
| ... | ... | @@ -768,12 +800,14 @@ void GModelSpatialEllipticalGauss::update() const |
| 768 | 800 | // approximation. |
| 769 | 801 | m_minor2 = m_minor_rad * m_minor_rad; |
| 770 | 802 | m_major2 = m_major_rad * m_major_rad; |
| 771 | - double denom = gammalib::twopi * m_minor_rad * m_major_rad; | |
| 803 | + double denom = gammalib::twopi * m_minor_rad * m_major_rad * | |
| 804 | + c_fraction; | |
| 772 | 805 | m_norm = (denom > 0.0) ? 1.0 / denom : 0.0; |
| 773 | 806 | |
| 774 | 807 | } // endif: update required |
| 775 | 808 | |
| 776 | 809 | // Update chache if position angle changed |
| 810 | + #if !defined(G_SMALL_ANGLE_APPROXIMATION) | |
| 777 | 811 | if (m_last_posangle != posangle()) { |
| 778 | 812 | |
| 779 | 813 | // Signal parameter changes |
| ... | ... | @@ -805,6 +839,7 @@ void GModelSpatialEllipticalGauss::update() const |
| 805 | 839 | m_term3 = 0.5 * (m_sin2pos / m_major2 - m_sin2pos / m_minor2); |
| 806 | 840 | |
| 807 | 841 | } // endif: something has changed |
| 842 | + #endif | |
| 808 | 843 | |
| 809 | 844 | // Return |
| 810 | 845 | return; | ... | ... |