Commit a3cb26682d5ca7232b50777357c6cd2b758e8545
1 parent
97940c94
Implement radius gradient for radial disk model (#3391)
Showing
4 changed files
with
150 additions
and
20 deletions
doc/dev/inst/cta/gammalib_cta.tex
| ... | ... | @@ -151,7 +151,7 @@ and |
| 151 | 151 | \end{equation} |
| 152 | 152 | |
| 153 | 153 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
| 154 | -\subsubsection{Radial disk} | |
| 154 | +\paragraph{Radial disk} | |
| 155 | 155 | |
| 156 | 156 | The radial disk function $f(\theta)$ is given by |
| 157 | 157 | \begin{equation} |
| ... | ... | @@ -168,11 +168,16 @@ with the partial derivatives |
| 168 | 168 | \end{equation} |
| 169 | 169 | and |
| 170 | 170 | \begin{equation} |
| 171 | -\frac{\partial f}{\partial \theta_0} = ? . | |
| 171 | +\frac{\partial f}{\partial \theta_0} = \left \{ | |
| 172 | + \begin{array}{l l} | |
| 173 | + \displaystyle -2 \pi f^2(\theta) \sin \theta_0 & \mbox{if $\theta \le \theta_0$} \\ \\ | |
| 174 | + \displaystyle 0 & \mbox{if $\theta > \theta_0$} | |
| 175 | + \end{array} | |
| 176 | + \right . | |
| 172 | 177 | \end{equation} |
| 173 | 178 | |
| 174 | 179 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
| 175 | -\subsubsection{Radial ring} | |
| 180 | +\paragraph{Radial ring} | |
| 176 | 181 | |
| 177 | 182 | The radial ring function $f(\theta)$ is given by |
| 178 | 183 | \begin{equation} |
| ... | ... | @@ -198,7 +203,7 @@ and |
| 198 | 203 | \end{equation} |
| 199 | 204 | |
| 200 | 205 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
| 201 | -\subsubsection{Radial Gaussian} | |
| 206 | +\paragraph{Radial Gaussian} | |
| 202 | 207 | |
| 203 | 208 | The radial Gaussian function $f(\theta)$ is given by |
| 204 | 209 | \begin{equation} |
| ... | ... | @@ -214,7 +219,7 @@ and |
| 214 | 219 | \end{equation} |
| 215 | 220 | |
| 216 | 221 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
| 217 | -\subsubsection{Radial shell} | |
| 222 | +\paragraph{Radial shell} | |
| 218 | 223 | |
| 219 | 224 | The shell function on a sphere is given by |
| 220 | 225 | \begin{equation} | ... | ... |
include/GModelSpatialRadialDisk.hpp
| ... | ... | @@ -86,16 +86,17 @@ protected: |
| 86 | 86 | void init_members(void); |
| 87 | 87 | void copy_members(const GModelSpatialRadialDisk& model); |
| 88 | 88 | void free_members(void); |
| 89 | - void update(void) const; | |
| 89 | + void update(const bool& gradients) const; | |
| 90 | 90 | virtual void set_region(void) const; |
| 91 | 91 | |
| 92 | 92 | // Protected members |
| 93 | 93 | GModelPar m_radius; //!< Disk radius (degrees) |
| 94 | 94 | |
| 95 | 95 | // Cached members used for pre-computations |
| 96 | - mutable double m_last_radius; //!< Last disk radius | |
| 97 | - mutable double m_radius_rad; //!< Radius in radians | |
| 98 | - mutable double m_norm; //!< Normalization | |
| 96 | + mutable double m_last_radius; //!< Last disk radius | |
| 97 | + mutable double m_radius_rad; //!< Radius in radians | |
| 98 | + mutable double m_norm; //!< Normalization | |
| 99 | + mutable double m_factor_gradient; //!< Gradient factor | |
| 99 | 100 | }; |
| 100 | 101 | |
| 101 | 102 | ... | ... |
src/model/GModelSpatialRadialDisk.cpp
| ... | ... | @@ -51,6 +51,7 @@ const GModelSpatialRegistry g_radial_disk_legacy_registry(&g_radial_disk_legac |
| 51 | 51 | /* __ Macros _____________________________________________________________ */ |
| 52 | 52 | |
| 53 | 53 | /* __ Coding definitions _________________________________________________ */ |
| 54 | +//#define G_SMOOTH_RADIUS_GRADIENT //!< Use smooth radius gradient | |
| 54 | 55 | |
| 55 | 56 | /* __ Debug definitions __________________________________________________ */ |
| 56 | 57 | |
| ... | ... | @@ -293,11 +294,36 @@ double GModelSpatialRadialDisk::eval(const double& theta, |
| 293 | 294 | const bool& gradients) const |
| 294 | 295 | { |
| 295 | 296 | // Update precomputation cache |
| 296 | - update(); | |
| 297 | + update(gradients); | |
| 297 | 298 | |
| 298 | 299 | // Set value |
| 299 | 300 | double value = (theta <= m_radius_rad) ? m_norm : 0.0; |
| 300 | 301 | |
| 302 | + // Optionally compute gradients | |
| 303 | + if (gradients) { | |
| 304 | + | |
| 305 | + // Alternative radius | |
| 306 | + #if defined(G_SMOOTH_RADIUS_GRADIENT) | |
| 307 | + double g_radius = 0.0; | |
| 308 | + if (m_radius.is_free()) { | |
| 309 | + const double k = 1000.0; | |
| 310 | + double f1 = std::exp(-k * (m_radius_rad - theta)); | |
| 311 | + double f2 = 1.0 + f1; | |
| 312 | + g_radius = m_norm * k / (f2 * f2) * m_radius.scale() * | |
| 313 | + gammalib::deg2rad; | |
| 314 | + } | |
| 315 | + | |
| 316 | + // Evaluate radius gradient | |
| 317 | + #else | |
| 318 | + double g_radius = (m_radius.is_free() && theta <= m_radius_rad) | |
| 319 | + ? m_factor_gradient : 0.0; | |
| 320 | + #endif | |
| 321 | + | |
| 322 | + // Set radius gradient | |
| 323 | + m_radius.factor_gradient(g_radius); | |
| 324 | + | |
| 325 | + } // endif: gradient computation was requested | |
| 326 | + | |
| 301 | 327 | // Compile option: Check for NaN/Inf |
| 302 | 328 | #if defined(G_NAN_CHECK) |
| 303 | 329 | if (gammalib::is_notanumber(value) || gammalib::is_infinite(value)) { |
| ... | ... | @@ -522,16 +548,21 @@ void GModelSpatialRadialDisk::init_members(void) |
| 522 | 548 | m_radius.free(); |
| 523 | 549 | m_radius.scale(1.0); |
| 524 | 550 | m_radius.gradient(0.0); |
| 525 | - m_radius.has_grad(false); // Radial components never have gradients | |
| 551 | + | |
| 552 | + // Signal that this model provides analytical parameter gradients | |
| 553 | + //m_ra.has_grad(true); // Not yet implemented | |
| 554 | + //m_dec.has_grad(true); // Not yet implemented | |
| 555 | + m_radius.has_grad(true); | |
| 526 | 556 | |
| 527 | 557 | // Set parameter pointer(s) |
| 528 | 558 | m_pars.push_back(&m_radius); |
| 529 | 559 | |
| 530 | 560 | // Initialise precomputation cache. Note that zero values flag |
| 531 | 561 | // uninitialised as a zero radius is not meaningful |
| 532 | - m_last_radius = 0.0; | |
| 533 | - m_radius_rad = 0.0; | |
| 534 | - m_norm = 0.0; | |
| 562 | + m_last_radius = 0.0; | |
| 563 | + m_radius_rad = 0.0; | |
| 564 | + m_norm = 0.0; | |
| 565 | + m_factor_gradient = 0.0; | |
| 535 | 566 | |
| 536 | 567 | // Return |
| 537 | 568 | return; |
| ... | ... | @@ -554,9 +585,10 @@ void GModelSpatialRadialDisk::copy_members(const GModelSpatialRadialDisk& model) |
| 554 | 585 | m_radius = model.m_radius; |
| 555 | 586 | |
| 556 | 587 | // Copy precomputation cache |
| 557 | - m_last_radius = model.m_last_radius; | |
| 558 | - m_radius_rad = model.m_radius_rad; | |
| 559 | - m_norm = model.m_norm; | |
| 588 | + m_last_radius = model.m_last_radius; | |
| 589 | + m_radius_rad = model.m_radius_rad; | |
| 590 | + m_norm = model.m_norm; | |
| 591 | + m_factor_gradient = model.m_factor_gradient; | |
| 560 | 592 | |
| 561 | 593 | // Return |
| 562 | 594 | return; |
| ... | ... | @@ -576,6 +608,8 @@ void GModelSpatialRadialDisk::free_members(void) |
| 576 | 608 | /***********************************************************************//** |
| 577 | 609 | * @brief Update precomputation cache |
| 578 | 610 | * |
| 611 | + * @param[in] gradients Request gradient computation? | |
| 612 | + * | |
| 579 | 613 | * Computes the normalization |
| 580 | 614 | * \f[ |
| 581 | 615 | * {\tt m\_norm} = \frac{1}{2 \pi (1 - \cos r)} |
| ... | ... | @@ -586,7 +620,7 @@ void GModelSpatialRadialDisk::free_members(void) |
| 586 | 620 | * approximation you might have expected: |
| 587 | 621 | * \f[{\tt m\_norm} = \frac{1}{\pi r ^ 2}\f] |
| 588 | 622 | ***************************************************************************/ |
| 589 | -void GModelSpatialRadialDisk::update() const | |
| 623 | +void GModelSpatialRadialDisk::update(const bool& gradients) const | |
| 590 | 624 | { |
| 591 | 625 | // Update if radius has changed |
| 592 | 626 | if (m_last_radius != radius()) { |
| ... | ... | @@ -597,10 +631,26 @@ void GModelSpatialRadialDisk::update() const |
| 597 | 631 | // Compute disk radius in radians |
| 598 | 632 | m_radius_rad = radius() * gammalib::deg2rad; |
| 599 | 633 | |
| 600 | - // Perform precomputations | |
| 601 | - double denom = gammalib::twopi * (1 - std::cos(m_radius_rad)); | |
| 634 | + // Compute sine and cosine of radius, benefitting of the sincos | |
| 635 | + // function | |
| 636 | + double sin_radius; | |
| 637 | + double cos_radius; | |
| 638 | + if (gradients) { | |
| 639 | + sin_radius = std::sin(m_radius_rad); | |
| 640 | + cos_radius = std::cos(m_radius_rad); | |
| 641 | + } | |
| 642 | + else { | |
| 643 | + cos_radius = std::cos(m_radius_rad); | |
| 644 | + } | |
| 645 | + | |
| 646 | + // Precompute radial disk function | |
| 647 | + double denom = gammalib::twopi * (1.0 - cos_radius); | |
| 602 | 648 | m_norm = (denom > 0.0) ? 1.0 / denom : 0.0; |
| 603 | 649 | |
| 650 | + // Precompute factor gradient | |
| 651 | + m_factor_gradient = -gammalib::twopi * m_norm * m_norm * sin_radius * | |
| 652 | + m_radius.scale() * gammalib::deg2rad; | |
| 653 | + | |
| 604 | 654 | } // endif: update required |
| 605 | 655 | |
| 606 | 656 | // Return | ... | ... |
test/dev/test_disk_approximation.py
0 → 100755
| 1 | +#! /usr/bin/env python | |
| 2 | +# ========================================================================== | |
| 3 | +# Approximate radial disk function | |
| 4 | +# -------------------------------------------------------------------------- | |
| 5 | +# | |
| 6 | +# Copyright (C) 2020 Juergen Knoedlseder | |
| 7 | +# | |
| 8 | +# This program is free software: you can redistribute it and/or modify | |
| 9 | +# it under the terms of the GNU General Public License as published by | |
| 10 | +# the Free Software Foundation, either version 3 of the License, or | |
| 11 | +# (at your option) any later version. | |
| 12 | +# | |
| 13 | +# This program is distributed in the hope that it will be useful, | |
| 14 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| 15 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| 16 | +# GNU General Public License for more details. | |
| 17 | +# | |
| 18 | +# You should have received a copy of the GNU General Public License | |
| 19 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. | |
| 20 | +# | |
| 21 | +# ========================================================================== | |
| 22 | +import math | |
| 23 | +import gammalib | |
| 24 | +import matplotlib.pyplot as plt | |
| 25 | + | |
| 26 | + | |
| 27 | +# ========================== # | |
| 28 | +# Compare radial model to MC # | |
| 29 | +# ========================== # | |
| 30 | +def show(theta0, k=1000.0, rad_max=1.0, nrad=1000): | |
| 31 | + """ | |
| 32 | + Show smooth approximation of radial disk function. | |
| 33 | + """ | |
| 34 | + # Get dummy energy and time | |
| 35 | + energy = gammalib.GEnergy() | |
| 36 | + time = gammalib.GTime() | |
| 37 | + | |
| 38 | + # Generate theta vector | |
| 39 | + theta_bin = rad_max/nrad | |
| 40 | + theta = [(i+0.5) * theta_bin for i in range(nrad)] | |
| 41 | + | |
| 42 | + # Generate model vector | |
| 43 | + model = [1.0 / (1.0 + math.exp(-k * (theta0 - t))) for t in theta] | |
| 44 | + disk = [1.0 if t < theta0 else 0.0 for t in theta] | |
| 45 | + | |
| 46 | + # Create figure | |
| 47 | + plt.figure(1) | |
| 48 | + plt.title('Smooth approximation of radial disk function') | |
| 49 | + | |
| 50 | + # Plot data | |
| 51 | + plt.plot(theta, model, 'r-') | |
| 52 | + plt.plot(theta, disk, 'g-') | |
| 53 | + | |
| 54 | + # Set axes | |
| 55 | + plt.xlabel('Offset (deg)') | |
| 56 | + plt.ylabel('Arbitrary units') | |
| 57 | + | |
| 58 | + # Notify | |
| 59 | + print('PLEASE CLOSE WINDOW TO CONTINUE ...') | |
| 60 | + | |
| 61 | + # Show plot | |
| 62 | + plt.show() | |
| 63 | + | |
| 64 | + # Return | |
| 65 | + return | |
| 66 | + | |
| 67 | + | |
| 68 | +# ======================== # | |
| 69 | +# Main routine entry point # | |
| 70 | +# ======================== # | |
| 71 | +if __name__ == '__main__': | |
| 72 | + | |
| 73 | + # Show radial model | |
| 74 | + show(0.5) | ... | ... |