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) | ... | ... |