GammaLib 2.0.0
Loading...
Searching...
No Matches
GCOMIaq.cpp
Go to the documentation of this file.
1/***************************************************************************
2 * GCOMIaq.cpp - COMPTEL instrument response representation *
3 * ----------------------------------------------------------------------- *
4 * copyright (C) 2017-2021 by Juergen Knoedlseder *
5 * ----------------------------------------------------------------------- *
6 * *
7 * This program is free software: you can redistribute it and/or modify *
8 * it under the terms of the GNU General Public License as published by *
9 * the Free Software Foundation, either version 3 of the License, or *
10 * (at your option) any later version. *
11 * *
12 * This program is distributed in the hope that it will be useful, *
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
15 * GNU General Public License for more details. *
16 * *
17 * You should have received a copy of the GNU General Public License *
18 * along with this program. If not, see <http://www.gnu.org/licenses/>. *
19 * *
20 ***************************************************************************/
21/**
22 * @file GCOMIaq.cpp
23 * @brief COMPTEL instrument response representation class implementation
24 * @author Juergen Knoedlseder
25 */
26
27/* __ Includes ___________________________________________________________ */
28#ifdef HAVE_CONFIG_H
29#include <config.h>
30#endif
31#include "GTools.hpp"
32#include "GFits.hpp"
33#include "GFilename.hpp"
34#include "GIntegral.hpp"
35#include "GEbounds.hpp"
36#include "GModelSpectral.hpp"
40#include "GCOMIaq.hpp"
41#include "GCOMSupport.hpp"
42
43/* __ Method name definitions ____________________________________________ */
44
45/* __ Macros _____________________________________________________________ */
46
47/* __ Coding definitions _________________________________________________ */
48//#define G_COMPASS_LOCPSR
49#define G_RESPONSE_KERNEL_LIMITS
50//#define G_APPLY_PSD_CORRECTION_IN_RESPONSE_KERNEL
51#define G_COMPUTE_IAQ_BIN_NO_WARNINGS
52#define G_LOCATION_SMEARING_NO_WARNINGS
53
54/* __ Debug definitions __________________________________________________ */
55//#define G_DEBUG_COMPTON_KINEMATICS
56//#define G_DEBUG_COMPUTE_IAQ_BIN
57//#define G_DEBUG_KLEIN_NISHINA
58//#define G_DEBUG_KLEIN_NISHINA_INTEGRAL
59//#define G_DEBUG_RESPONSE_KERNEL
60//#define G_DEBUG_WEIGHT_IAQ
61//#define G_DEBUG_SET_CONTINUUM
62//#define G_DEBUG_LOCATION_SMEARING
63//#define G_DEBUG_LOCATION_SPREAD
64
65/* __ Constants __________________________________________________________ */
66
67
68/*==========================================================================
69 = =
70 = Constructors/destructors =
71 = =
72 ==========================================================================*/
73
74/***********************************************************************//**
75 * @brief Void constructor
76 *
77 * Creates an empty COMPTEL instrument response representation.
78 ***************************************************************************/
80{
81 // Initialise members
83
84 // Return
85 return;
86}
87
88
89/***********************************************************************//**
90 * @brief Copy constructor
91 *
92 * @param[in] iaq COMPTEL instrument response representation.
93 **************************************************************************/
95{
96 // Initialise members
98
99 // Copy members
100 copy_members(iaq);
101
102 // Return
103 return;
104}
105
106
107/***********************************************************************//**
108 * @brief COMPTEL instrument response representation constructor
109 *
110 * @param[in] phigeo_max Maximum geometrical scatter angle (deg).
111 * @param[in] phigeo_bin_size Bin size in geometrical scatter angle (deg).
112 * @param[in] phibar_max Maximum Compton scatter angle (deg).
113 * @param[in] phibar_bin_size Bin size in Compton scatter angle (deg).
114 *
115 * @todo Test input argument validity.
116 **************************************************************************/
117GCOMIaq::GCOMIaq(const double& phigeo_max, const double& phigeo_bin_size,
118 const double& phibar_max, const double& phibar_bin_size)
119{
120 // Initialise members
121 init_members();
122
123 // Compute the number of bins in phigeo and phibar
124 int nphigeo = int(phigeo_max / phigeo_bin_size + 0.5);
125 int nphibar = int(phibar_max / phibar_bin_size + 0.5);
126
127 // Store the bin definition. Use the number of bins to re-derive the
128 // maximum angles
129 m_phigeo_bin_size = phigeo_bin_size;
130 m_phibar_bin_size = phibar_bin_size;
131 m_phigeo_max = phigeo_bin_size * double(nphigeo);
132 m_phibar_max = phibar_bin_size * double(nphibar);
133
134 // Allocate FITS image
135 m_iaq = GFitsImageFloat(nphigeo, nphibar);
136
137 // Set FITS image keywords
138 m_iaq.card("CTYPE1", "Phigeo", "Geometrical scatter angle");
139 m_iaq.card("CRVAL1", 0.5*m_phigeo_bin_size, "[deg] Geometrical scatter angle for reference bin");
140 m_iaq.card("CDELT1", m_phigeo_bin_size, "[deg] Geometrical scatter angle bin size");
141 m_iaq.card("CRPIX1", 1.0, "Reference bin of geometrical scatter angle");
142 m_iaq.card("CTYPE2", "Phibar", "Compton scatter angle");
143 m_iaq.card("CRVAL2", 0.5*m_phibar_bin_size, "[deg] Compton scatter angle for reference bin");
144 m_iaq.card("CDELT2", m_phibar_bin_size, "[deg] Compton scatter angle bin size");
145 m_iaq.card("CRPIX2", 1.0, "Reference bin of Compton scatter angle");
146 m_iaq.card("BUNIT", "Probability per bin", "Unit of bins");
147 m_iaq.card("TELESCOP", "GRO", "Name of telescope");
148 m_iaq.card("INSTRUME", "COMPTEL", "Name of instrument");
149 m_iaq.card("ORIGIN", "GammaLib", "Origin of FITS file");
150 m_iaq.card("OBSERVER", "Unknown", "Observer that created FITS file");
151
152 // Return
153 return;
154}
155
156
157/***********************************************************************//**
158 * @brief Destructor
159 *
160 * Destroys instance of COMPTEL response object.
161 ***************************************************************************/
163{
164 // Free members
165 free_members();
166
167 // Return
168 return;
169}
170
171
172/*==========================================================================
173 = =
174 = Operators =
175 = =
176 ==========================================================================*/
177
178/***********************************************************************//**
179 * @brief Assignment operator
180 *
181 * @param[in] iaq COMPTEL instrument response representation.
182 * @return COMPTEL instrument response representation.
183 ***************************************************************************/
185{
186 // Execute only if object is not identical
187 if (this != &iaq) {
188
189 // Free members
190 free_members();
191
192 // Initialise members
193 init_members();
194
195 // Copy members
196 copy_members(iaq);
197
198 } // endif: object was not identical
199
200 // Return this object
201 return *this;
202}
203
204
205/*==========================================================================
206 = =
207 = Public methods =
208 = =
209 ==========================================================================*/
210
211/***********************************************************************//**
212 * @brief Clear instance
213 *
214 * Clears COMPTEL instrument response representation by resetting all members
215 * to an initial state. Any information that was present in the object before
216 * will be lost.
217 ***************************************************************************/
219{
220 // Free class members
221 free_members();
222
223 // Initialise members
224 init_members();
225
226 // Return
227 return;
228}
229
230
231/***********************************************************************//**
232 * @brief Clone instance
233 *
234 * @return Pointer to deep copy of COMPTEL instrument response
235 * representation.
236 ***************************************************************************/
238{
239 return new GCOMIaq(*this);
240}
241
242
243/***********************************************************************//**
244 * @brief Set mono-energetic IAQ
245 *
246 * @param[in] energy Input photon energy.
247 * @param[in] ebounds Boundaries of observed energy.
248 *
249 * The code implemented is based on the COMPASS RESPSIT2 function SPCIAQ.F
250 * (release 1.0, 18-DEC-92).
251 *
252 * @todo Implement geometrical smearing.
253 ***************************************************************************/
254void GCOMIaq::set(const GEnergy& energy, const GEbounds& ebounds)
255{
256 // Store the energy boundaries
257 m_ebounds = ebounds;
258
259 // Initialise COMPTEL response information and instrument characteristics
261
262 // Remove any extra header cards
263 remove_cards();
264
265 // Generate IAQ using Compton kinematics
266 compton_kinematics(energy.MeV());
267
268 // Multiply IAQ with Klein-Nishina factors
269 klein_nishina(energy.MeV());
270
271 // Weight IAQ
272 weight_iaq(energy.MeV());
273
274 // Apply location smearing
276
277 // Set source photon energy
278 m_iaq.card("ENERGY", energy.MeV(), "[MeV] Source photon energy");
279
280 // Return
281 return;
282}
283
284
285/***********************************************************************//**
286 * @brief Set continuum IAQ
287 *
288 * @param[in] spectrum Input spectrum.
289 * @param[in] ebounds Boundaries of observed energy.
290 *
291 * Computes the continuum IAQ based on an input @p spectrum. The method
292 * computes the line IAQ for a m_num_energies logarithmically spaced energies
293 * within an energy range that conceivably covers the observed energy band.
294 *
295 * The code implementation is loosly based on the COMPASS RESPSIT2 functions
296 * SPCIAQ.F, GENWEI.F and DERFAN.F (release 1.0, 18-DEC-92). The code was
297 * much simplified.
298 *
299 * @todo Implement geometrical smearing.
300 ***************************************************************************/
301void GCOMIaq::set(const GModelSpectral& spectrum, const GEbounds& ebounds)
302{
303 // Store the energy boundaries
304 m_ebounds = ebounds;
305
306 // Initialise COMPTEL response information and instrument characteristics
308
309 // Remove any extra header cards
310 remove_cards();
311
312 // Get minimum and maximum input energy for continuum IAQ
313 double energy_min = 0.7 * m_ebounds.emin().MeV();
314 double energy_max = (m_response_d1.emax() < m_response_d2.emax()) ?
316
317 // Debug
318 #if defined(G_DEBUG_SET_CONTINUUM)
319 std::cout << "Minimum energy=" << energy_min << std::endl;
320 std::cout << "Maximum energy=" << energy_max << std::endl;
321 #endif
322
323 // Setup array of energy boundaries
324 GEbounds ebds(m_num_energies, GEnergy(energy_min, "MeV"),
325 GEnergy(energy_max, "MeV"));
326
327 // Compute flux over measured total energy interval
328 double flux_total = spectrum.flux(m_ebounds.emin(), m_ebounds.emax());
329
330 // Store empty copy of IAQ
332 for (int k = 0; k < iaq.npix(); ++k) {
333 iaq(k) = 0.0;
334 }
335
336 // Loop over energy bins
337 for (int i = 0; i < ebds.size(); ++i) {
338
339 // Get log mean energy for bin
340 GEnergy energy = ebds.elogmean(i);
341
342 // Get weight for this bin
343 double weight = spectrum.flux(ebds.emin(i), ebds.emax(i)) /
344 flux_total;
345
346 // Debug: Initialise sum
347 #if defined(G_DEBUG_SET_CONTINUUM)
348 double sum = 0.0;
349 #endif
350
351 // Continue only if we have weight
352 if (weight > 0.0) {
353
354 // Generate IAQ using Compton kinematics
355 compton_kinematics(energy.MeV());
356
357 // Multiply IAQ with Klein-Nishina factors
358 klein_nishina(energy.MeV());
359
360 // Weight IAQ
361 weight_iaq(energy.MeV());
362
363 // Copy weigthed IAQ values
364 for (int k = 0; k < iaq.npix(); ++k) {
365 double value = m_iaq(k) * weight;
366 iaq(k) += value;
367 #if defined(G_DEBUG_SET_CONTINUUM)
368 sum += value;
369 #endif
370 }
371
372 } // endif: we has weight
373
374 // Debug: print results
375 #if defined(G_DEBUG_SET_CONTINUUM)
376 std::cout << "Energy=" << energy;
377 std::cout << " weight=" << weight;
378 std::cout << " sum=" << sum << std::endl;
379 #endif
380
381 } // endfor: looped over energy bins
382
383 // Store IAQ matrix
384 m_iaq = iaq;
385
386 // Apply location smearing
388
389 // Set parameters
390 m_iaq.card("SPECTRUM", spectrum.classname(), "Source spectrum");
391 const GModelSpectralPlaw* plaw =
392 dynamic_cast<const GModelSpectralPlaw*>(&spectrum);
393 const GModelSpectralPlawPhotonFlux* pplaw =
394 dynamic_cast<const GModelSpectralPlawPhotonFlux*>(&spectrum);
395 const GModelSpectralPlawEnergyFlux* eplaw =
396 dynamic_cast<const GModelSpectralPlawEnergyFlux*>(&spectrum);
397 if (plaw != NULL) {
398 m_iaq.card("PLAWINX", plaw->index(), "Power law spectral index");
399 }
400 else if (pplaw != NULL) {
401 m_iaq.card("PLAWINX", pplaw->index(), "Power law spectral index");
402 }
403 else if (eplaw != NULL) {
404 m_iaq.card("PLAWINX", eplaw->index(), "Power law spectral index");
405 }
406 m_iaq.card("NENG", m_num_energies, "Number of incident energies");
407 m_iaq.card("EIMIN", energy_min, "[MeV] Minimum incident energy");
408 m_iaq.card("EIMAX", energy_max, "[MeV] Maximum incident energy");
409
410 // Return
411 return;
412}
413
414
415/***********************************************************************//**
416 * @brief Save COMPTEL instrument response representation into FITS file
417 *
418 * @param[in] filename FITS file name.
419 * @param[in] clobber Overwrite existing file?
420 *
421 * Saves the COMPTEL instrument response representation into a FITS file. If
422 * the file exists already and the @p clobber parameter is true, the method
423 * will overwrite the content of the existing file. Otherwise, an exception
424 * is thrown.
425 ***************************************************************************/
426void GCOMIaq::save(const GFilename& filename, const bool& clobber) const
427{
428 // Work on a copy of the image to set the extension name
430
431 // Set extension name
432 if (filename.has_extname()) {
433 iaq.extname(filename.extname());
434 }
435
436 // Set FITS image keywords
437 iaq.card("EMIN", m_ebounds.emin().MeV(), "[MeV] Minimum measured photon energy");
438 iaq.card("EMAX", m_ebounds.emax().MeV(), "[MeV] Maximum measured photon energy");
439 iaq.card("E1MIN", m_e1min, "[MeV] Minimum D1 energy deposit");
440 iaq.card("E1MAX", m_e1max, "[MeV] Maximum D1 energy deposit");
441 iaq.card("E2MIN", m_e1min, "[MeV] Minimum D2 energy deposit");
442 iaq.card("E2MAX", m_e1max, "[MeV] Maximum D2 energy deposit");
443 iaq.card("ZENITH", m_zenith, "[deg] Zenith angle for location smearing");
444 iaq.card("PSDCORR", m_psd_correct, "PSD correction for 0-110");
445 iaq.card("PHIRES", m_phibar_resolution, "[deg] Phibar resolution in computation");
446
447 // Initialise empty FITS file
448 GFits fits;
449
450 // Append IAQ to FITS file
451 fits.append(iaq);
452
453 // Save FITS file (without extension name which was extracted earlier
454 // and set in the HDU)
455 fits.saveto(filename.url(), clobber);
456
457 // Return
458 return;
459}
460
461
462/***********************************************************************//**
463 * @brief Print COMPTEL instrument response representation information
464 *
465 * @param[in] chatter Chattiness.
466 * @return String containing COMPTEL instrument response representation
467 * information.
468 ***************************************************************************/
469std::string GCOMIaq::print(const GChatter& chatter) const
470{
471 // Initialise result string
472 std::string result;
473
474 // Continue only if chatter is not silent
475 if (chatter != SILENT) {
476
477 // Append header
478 result.append("=== GCOMIaq ===");
479
480 // Append information
481 result.append("\n"+gammalib::parformat("Number of Phigeo bins"));
482 if (m_iaq.naxis() > 0) {
483 result.append(gammalib::str(m_iaq.naxes(0)));
484 }
485 else {
486 result.append("0");
487 }
488 result.append("\n"+gammalib::parformat("Number of Phibar bins"));
489 if (m_iaq.naxis() > 1) {
490 result.append(gammalib::str(m_iaq.naxes(1)));
491 }
492 else {
493 result.append("0");
494 }
495 result.append("\n"+gammalib::parformat("Maximum Phigeo value"));
496 result.append(gammalib::str(m_phigeo_max)+" deg");
497 result.append("\n"+gammalib::parformat("Maximum Phibar value"));
498 result.append(gammalib::str(m_phibar_max)+" deg");
499 result.append("\n"+gammalib::parformat("Phigeo bin size"));
500 result.append(gammalib::str(m_phigeo_bin_size)+" deg");
501 result.append("\n"+gammalib::parformat("Phibar bin size"));
502 result.append(gammalib::str(m_phibar_bin_size)+" deg");
503 result.append("\n"+gammalib::parformat("Energy range"));
504 result.append(m_ebounds.emin().print()+" - ");
505 result.append(m_ebounds.emax().print());
506
507 // Append parameters
508 result.append("\n"+gammalib::parformat("D1 energy range"));
509 result.append(gammalib::str(m_e1min)+" MeV - ");
510 result.append(gammalib::str(m_e1max)+" MeV");
511 result.append("\n"+gammalib::parformat("D2 energy range"));
512 result.append(gammalib::str(m_e2min)+" MeV - ");
513 result.append(gammalib::str(m_e2max)+" MeV");
514 result.append("\n"+gammalib::parformat("Zenith angle"));
515 result.append(gammalib::str(m_zenith)+" deg");
516 result.append("\n"+gammalib::parformat("Phibar resolution"));
517 result.append(gammalib::str(m_phibar_resolution)+" deg");
518 result.append("\n"+gammalib::parformat("PSD correction"));
519 if (m_psd_correct) {
520 result.append("yes");
521 }
522 else {
523 result.append("no");
524 }
525 result.append("\n"+gammalib::parformat("Number of input energies"));
526 result.append(gammalib::str(m_num_energies));
527
528 // Append IAQ integral
529 double sum = 0.0;
530 for (int i = 0; i < m_iaq.npix(); ++i) {
531 sum += m_iaq(i);
532 }
533 result.append("\n"+gammalib::parformat("Response integral"));
534 result.append(gammalib::str(sum));
535
536 } // endif: chatter was not silent
537
538 // Return result
539 return result;
540}
541
542
543/*==========================================================================
544 = =
545 = Private methods =
546 = =
547 ==========================================================================*/
548
549/***********************************************************************//**
550 * @brief Initialise class members
551 ***************************************************************************/
553{
554 // Initialise members
555 m_iaq.clear();
559 m_ict.clear();
560 m_phigeo_max = 0.0;
561 m_phibar_max = 0.0;
562 m_phigeo_bin_size = 0.0;
563 m_phibar_bin_size = 0.0;
564
565 // Initialise parameters
566 m_phibar_resolution = 0.25; //!< Default: 0.25 deg
567 m_e1min = 0.070; //!< Default: 70 keV
568 m_e1max = 20.0; //!< Default: 20 MeV
569 m_e2min = 0.650; //!< Default: 650 keV
570 m_e2max = 30.0; //!< Default: 30 MeV
571 m_num_energies = 50; //!< Default: 50 input energies
572 m_psd_correct = true; //!< Default: use PSD correction
573 m_zenith = 25.0; //!< Default: 25 deg
574
575 // Return
576 return;
577}
578
579
580/***********************************************************************//**
581 * @brief Copy class members
582 *
583 * @param[in] iaq COMPTEL instrument response representation.
584 ***************************************************************************/
586{
587 // Copy attributes
588 m_iaq = iaq.m_iaq;
589 m_ebounds = iaq.m_ebounds;
592 m_ict = iaq.m_ict;
597
598 // Copy parameters
600 m_e1min = iaq.m_e1min;
601 m_e1max = iaq.m_e1max;
602 m_e2min = iaq.m_e2min;
603 m_e2max = iaq.m_e2max;
606 m_zenith = iaq.m_zenith;
607
608 // Return
609 return;
610}
611
612
613/***********************************************************************//**
614 * @brief Delete class members
615 ***************************************************************************/
617{
618 // Return
619 return;
620}
621
622
623/***********************************************************************//**
624 * @brief Initialise COMPTEL response function and instrument characteristics
625 ***************************************************************************/
627{
628 // Initialise COMPTEL response function and instrument characteristics
629 GCaldb caldb("cgro","comptel");
630 m_response_d1 = GCOMD1Response(caldb, "DEFAULT");
631 m_response_d2 = GCOMD2Response(caldb, "DEFAULT");
632 m_ict = GCOMInstChars(caldb, "DEFAULT");
633
634 // Return
635 return;
636}
637
638
639/***********************************************************************//**
640 * @brief Remove any extra header cards
641 *
642 * Remove all header cards that may have been attached by the set() methods.
643 ***************************************************************************/
645{
646 // Remove extra header cards
647 if (m_iaq.has_card("ENERGY")) {
648 const_cast<GFitsHeader&>(m_iaq.header()).remove("ENERGY");
649 }
650 if (m_iaq.has_card("SPECTRUM")) {
651 const_cast<GFitsHeader&>(m_iaq.header()).remove("SPECTRUM");
652 }
653 if (m_iaq.has_card("PLAWINX")) {
654 const_cast<GFitsHeader&>(m_iaq.header()).remove("PLAWINX");
655 }
656 if (m_iaq.has_card("NENG")) {
657 const_cast<GFitsHeader&>(m_iaq.header()).remove("NENG");
658 }
659 if (m_iaq.has_card("EIMIN")) {
660 const_cast<GFitsHeader&>(m_iaq.header()).remove("EIMIN");
661 }
662 if (m_iaq.has_card("EIMAX")) {
663 const_cast<GFitsHeader&>(m_iaq.header()).remove("EIMAX");
664 }
665
666 // Return
667 return;
668}
669
670
671/***********************************************************************//**
672 * @brief Generate IAQ matrix based on Compton kinematics
673 *
674 * @param[in] energy Input photon energy (MeV).
675 *
676 * Generates an IAQ matrix based on Compton kinematics only. See the
677 * compute_iaq_bin() method for the formula used to compute each bin of the
678 * IAQ matrix.
679 *
680 * The Phibar dimension of the IAQ is sampled at a resolution that is set
681 * by the m_phibar_resolution member.
682 *
683 * The code implemented is based on the COMPASS RESPSIT2 function IAQWEI.F
684 * (release 1.0, 24-FEB-93).
685 ***************************************************************************/
686void GCOMIaq::compton_kinematics(const double& energy)
687{
688 // Get IAQ dimensions
689 int n_phigeo = m_iaq.naxes(0);
690 int n_phibar = m_iaq.naxes(1);
691
692 // Set the phibar step size for the internal computation. The phibar
693 // step size is set by the m_phibar_resolution member. This ensures that
694 // the computation is done with a sufficient resolution in phibar.
695 int n_fine = int(m_phibar_bin_size / m_phibar_resolution + 0.5);
696 if (n_fine < 1) {
697 n_fine = 1;
698 }
699 double dphibar = m_phibar_bin_size / double(n_fine);
700 double dphibar_rad = dphibar * gammalib::deg2rad;
701
702 // Loop over phigeo
703 for (int i_phigeo = 0; i_phigeo < n_phigeo; ++i_phigeo) {
704
705 // Get geometrical scatter angle (deg)
706 double phigeo = (double(i_phigeo) + 0.5) * m_phigeo_bin_size;
707
708 // Compute the true D1 and D2 energy deposits based on the
709 // geometrical scatter angle
710 double etrue2 = gammalib::com_energy2(energy, phigeo);
711 double etrue1 = energy - etrue2;
712
713 // Debug
714 #if defined(G_DEBUG_COMPTON_KINEMATICS)
715 std::cout << "phigeo=" << phigeo;
716 std::cout << " etrue1=" << etrue1;
717 std::cout << " etrue2=" << etrue2;
718 #endif
719
720 // Initialise sum
721 #if defined(G_DEBUG_COMPTON_KINEMATICS)
722 double sum = 0.0;
723 #endif
724
725 // Loop over phibar
726 for (int i_phibar = 0; i_phibar < n_phibar; ++i_phibar) {
727
728 // Initialise start of phibar for this layer
729 double phibar = double(i_phibar) * m_phibar_bin_size + 0.5 * dphibar;
730
731 // Initialise response for this phibar layer
732 double response = 0.0;
733
734 // Loop fine phibar layers
735 for (int i = 0; i < n_fine; ++i, phibar += dphibar) {
736
737 // Compute IAQ bin
738 response += compute_iaq_bin(etrue1, etrue2, phibar);
739
740 } // endfor: looper over fine phibar layers
741
742 // Multiply response by size of fine phibar layers in radians
743 response *= dphibar_rad;
744
745 // Store result
746 m_iaq(i_phigeo, i_phibar) = response;
747
748 // Add response to sum
749 #if defined(G_DEBUG_COMPTON_KINEMATICS)
750 sum += response;
751 #endif
752
753 } // endfor: looped over phibar
754
755 // Debug
756 #if defined(G_DEBUG_COMPTON_KINEMATICS)
757 std::cout << " Prob=" << sum << std::endl;
758 #endif
759
760 } // endfor: looped over phigeo
761
762 // Return
763 return;
764}
765
766
767/***********************************************************************//**
768 * @brief Computes the IAQ for one bin
769 *
770 * @param[in] etrue1 True D1 energy deposit (MeV).
771 * @param[in] etrue2 True D2 energy deposit (MeV).
772 * @param[in] phibar Compton scatter angle (deg).
773 * @return Response for one IAQ bin
774 *
775 * Compute the integral
776 *
777 * \f[
778 * R(\bar{\varphi}|\hat{E_1},\hat{E_2}) =
779 * \int_{E_1^{\rm min}}^{E_1^{\rm max}}
780 * R(E_1,E_2|\hat{E_1},\hat{E_2}) dE_1
781 * \f]
782 *
783 * where
784 * \f$R(E_1,E_2|\hat{E_1},\hat{E_2})\f$ is computed using
785 * GCOMIaq::response_kernel::eval,
786 *
787 * \f$E_1\f$ and \f$E_2\f$ are the measured D1 and D2 energy deposits,
788 * respectively,
789 * \f$\hat{E_1}\f$ and \f$\hat{E_2}\f$ are the true D1 and D2 energy
790 * deposits, respectively, and \f$\bar{\varphi}\f$ the Compton scatter
791 * angle.
792 *
793 * The code implementation is based on the COMPASS RESPSIT2 functions
794 * RESPSC.F (release 2.0, 14-Mar-91) and RESINT.F (release 3.0, 21-Oct-91).
795 * Since RESPSC.F only defines the threshold while RESINT.F does the real
796 * job, both functions were merged.
797 *
798 * Using a continuum response for 3-10 MeV the following range for the number
799 * of integration kernel calls and integration iterations were determined:
800 *
801 * eps = 1.0e-4 calls = 17-8139 iter = 5-14
802 * eps = 1.0e-5 calls = 17-65537 iter = 5-17
803 * eps = 1.0e-6 calls = 17-524289 iter = 5-20
804 * eps = 1.0e-7 calls = 17-524289 iter = 5-21
805 * eps = 1.0e-8 calls = 17-524289 iter = 5-21
806 *
807 * Graphically there was no difference between the results for any of the
808 * values, so in order to have safety margin, a value of 1.0e-6 was selected.
809 * Note that RESINT.F implemented a trapezoid rule with 100 steps.
810 ***************************************************************************/
811double GCOMIaq::compute_iaq_bin(const double& etrue1,
812 const double& etrue2,
813 const double& phibar)
814{
815 // Initialise response
816 double response = 0.0;
817
818 // Set integration limits
819 double e1min = m_response_d1.emin(etrue1) - 0.5 * m_response_d1.ewidth(etrue1);
820 double e1max = m_response_d1.emax(etrue1);
821
822 // Set integration interval. Do not integrate more than 3 standard
823 // deviations away from photo peak.
824 double position = m_response_d1.position(etrue1);
825 double sigma = m_response_d1.sigma(etrue1);
826 double e_low = position - 3.0 * sigma;
827 double e_high = position + 3.0 * sigma;
828 double e_min = (e1min < e_low) ? e_low : e1min;
829 double e_max = (e1max > e_high) ? e_high : e1max;
830
831 // Continue only if the integration interval is positive
832 if (e_min < e_max) {
833
834 // Setup integration kernel
837 etrue1,
838 etrue2,
839 phibar,
840 m_ebounds.emin().MeV(),
841 m_ebounds.emax().MeV(),
842 m_e1min,
843 m_e1max,
844 m_e2min,
845 m_e2max);
846
847 // Setup integral
848 GIntegral integral(&integrand);
849
850 // Set precision
851 integral.eps(1.0e-6);
852
853 // No warnings
854 #if defined(G_COMPUTE_IAQ_BIN_NO_WARNINGS)
855 integral.silent(true);
856 #endif
857
858 // Perform integration
859 response = integral.romberg(e_min, e_max);
860
861 } // endif: integration interval was positive
862
863 // Debug
864 #if defined(G_DEBUG_COMPUTE_IAQ_BIN)
865 std::cout << " phibar=" << phibar;
866 std::cout << " e1min=" << e1min;
867 std::cout << " e1max=" << e1max;
868 std::cout << " e_min=" << e_min;
869 std::cout << " e_max=" << e_max;
870 std::cout << " response=" << response << std::endl;
871 #endif
872
873 // Return response
874 return response;
875}
876
877
878/***********************************************************************//**
879 * @brief Multiply IAQ matrix by the Klein-Nishina formula
880 *
881 * @param[in] energy Input photon energy (MeV).
882 *
883 * The code implemented is based on the COMPASS RESPSIT2 function IAQWEI.F
884 * (release 1.0, 24-FEB-93).
885 ***************************************************************************/
886void GCOMIaq::klein_nishina(const double& energy)
887{
888 // Get IAQ dimensions
889 int n_phigeo = m_iaq.naxes(0);
890 int n_phibar = m_iaq.naxes(1);
891
892 // Debug
893 #if defined(G_DEBUG_KLEIN_NISHINA)
894 double sum = 0.0;
895 #endif
896
897 // Loop over phigeo
898 for (int i_phigeo = 0; i_phigeo < n_phigeo; ++i_phigeo) {
899
900 // Get geometrical scatter angle (deg)
901 double phigeo = (double(i_phigeo) + 0.5) * m_phigeo_bin_size;
902
903 // Get Klein-Nishina value for Phigeo
904 double prob_kn = klein_nishina_bin(energy, phigeo);
905
906 // Debug
907 #if defined(G_DEBUG_KLEIN_NISHINA)
908 std::cout << "phigeo=" << phigeo << " prob_kn=" << prob_kn << std::endl;
909 sum += prob_kn;
910 #endif
911
912 // Loop over phibar
913 for (int i_phibar = 0; i_phibar < n_phibar; ++i_phibar) {
914
915 // Store result
916 m_iaq(i_phigeo, i_phibar) *= prob_kn;
917
918 } // endfor: looped over phibar
919
920 } // endfor: looped over phigeo
921
922 // Debug
923 #if defined(G_DEBUG_KLEIN_NISHINA)
924 std::cout << "Sum of probabilities = " << sum << std::endl;
925 #endif
926
927 // Return
928 return;
929}
930
931
932/***********************************************************************//**
933 * @brief Computes Klein-Nishina probability for one bin
934 *
935 * @param[in] energy Input photon energy (MeV).
936 * @param[in] phigeo Geometric scatter angle (deg).
937 * @return Klein-Nishina probability.
938 *
939 * Compute the probability that a photon of @p energy is Compton scattered
940 * in a given \f$\varphi_{\rm geo}\f$ bin using
941 *
942 * \f[
943 * P = \frac{\int_{\varphi_{\rm geo,min}}^{\varphi_{\rm geo,max}}
944 * \sigma_{\rm KN}(E, \varphi_{\rm geo}) d\varphi_{\rm geo}}
945 * {\int_{0}^{\pi}
946 * \sigma_{\rm KN}(E, \varphi_{\rm geo}) d\varphi_{\rm geo}}
947 * \f]
948 *
949 * where \f$\sigma_{\rm KN}(E, \varphi_{\rm geo})\f$ is the Klein-Nishina
950 * cross section.
951 *
952 * The code implementation is based on the COMPASS RESPSIT2 function
953 * RESPSG.F (release 2.0, 26-Apr-90).
954 ***************************************************************************/
955double GCOMIaq::klein_nishina_bin(const double& energy, const double& phigeo)
956{
957 // Compute phigeo boundaries in radians. Make sure that they remain in
958 // the range [0,pi].
959 double phigeo_lo = phigeo - 0.5 * m_phigeo_bin_size;
960 double phigeo_hi = phigeo + 0.5 * m_phigeo_bin_size;
961 if (phigeo_lo < 0.0) {
962 phigeo_lo = 0.0;
963 }
964 if (phigeo_hi < 0.0) {
965 phigeo_hi = 0.0;
966 }
967 if (phigeo_lo > 180.0) {
968 phigeo_lo = 180.0;
969 }
970 if (phigeo_hi > 180.0) {
971 phigeo_hi = 180.0;
972 }
973 phigeo_lo *= gammalib::deg2rad;
974 phigeo_hi *= gammalib::deg2rad;
975
976 // Express input photon energy in units of electron rest mass
977 double alpha = energy / gammalib::mec2;
978
979 // Calculate the probability of phigeo bin
980 double v_low = 1.0 - 1.0 / (1.0 + (1.0 - std::cos(phigeo_lo)) * alpha);
981 double v_high = 1.0 - 1.0 / (1.0 + (1.0 - std::cos(phigeo_hi)) * alpha);
982 double r_low = klein_nishina_integral(v_low, alpha);
983 double r_high = klein_nishina_integral(v_high, alpha);
984 double prob_bin = (r_high - r_low);
985
986 // Calculate the probability of [0,pi] range
987 const double v_zero = 0.0;
988 const double v_pi = 1.0 - 1.0 / (1.0 + 2.0 * alpha);
989 const double r_zero = klein_nishina_integral(v_zero, alpha);
990 const double r_pi = klein_nishina_integral(v_pi, alpha);
991 const double prob_tot = (r_pi - r_zero);
992
993 // Calculate the probability of phigeo bin
994 double prob_phigeo = prob_bin / prob_tot;
995
996 // Return probability of phigeo bin
997 return prob_phigeo;
998}
999
1000
1001/***********************************************************************//**
1002 * @brief Computes Klein-Nishina integral
1003 *
1004 * @param[in] v Integration limit.
1005 * @param[in] a Normalized energy.
1006 * @return Integrated Klein-Nishina cross section.
1007 *
1008 * Computes the indefinite integral of the Klein-Nishina cross section
1009 * evaluated at the integration limit @p v.
1010 *
1011 * The code implementation is based on the COMPASS RESPSIT2 function
1012 * RESSLV.F (release 1.0, 23-Nov-88). The formula used in that code is
1013 * the following:
1014 *
1015 * W = V*( 1.D0/A + 1.D0 )**2
1016 * W = W + ( 2.D0/(A**2) + 2.D0/A - 1.D0 )* DLOG(1.D0 - V)
1017 * W = W - (V**2)/2.D0
1018 * W = W + 1.D0/(A**2) * 1.D0/(1.D0 - V)
1019 *
1020 * It has been check that the original formula gives the same results as the
1021 * version that was implemented.
1022 ***************************************************************************/
1023double GCOMIaq::klein_nishina_integral(const double& v, const double& a)
1024{
1025 // Compute integral (hope this is okay :o)
1026 double w = v * (1.0/a + 1.0) * (1.0/a + 1.0) +
1027 (2.0/(a*a) + 2.0/a - 1.0) * std::log(1.0 - v) -
1028 v*v/2.0 +
1029 1.0/(a*a) * 1.0/(1.0 - v);
1030
1031 // Debug
1032 #if defined(G_DEBUG_KLEIN_NISHINA_INTEGRAL)
1033 double w_test;
1034 w_test = v*( 1.0/a + 1.0 )*( 1.0/a + 1.0 );
1035 w_test = w_test + ( 2.0/(a*a) + 2.0/a - 1.0 )* std::log(1.0 - v);
1036 w_test = w_test - (v*v)/2.0;
1037 w_test = w_test + 1.0/(a*a) * 1.0/(1.0 - v);
1038 if (w != w_test) {
1039 std::cout << "w=" << w << " w_test=" << w_test << std::endl;
1040 }
1041 #endif
1042
1043 // Return integral
1044 return w;
1045}
1046
1047
1048/***********************************************************************//**
1049 * @brief Weight IAQ matrix
1050 *
1051 * @param[in] energy Input photon energy (MeV).
1052 *
1053 * The code implemented is based on the COMPASS RESPSIT2 function IAQWEI.F
1054 * (release 1.0, 24-FEB-93).
1055 ***************************************************************************/
1056void GCOMIaq::weight_iaq(const double& energy)
1057{
1058 // Compute the total D1 interaction coefficient for source on axis
1059 double d1prob = m_ict.prob_D1inter(energy);
1060
1061 // Compute the fraction of Compton interactions within the total D1
1062 // interaction probability (COM-RP-ROL-DRG-41)
1063 double cfract = 1.0;
1064 if (energy >= 2.0) {
1065 cfract = 1.067 - 0.0295 * energy + 3.4e-4 * energy * energy;
1066 }
1067 if (cfract > 1.0) {
1068 cfract = 1.0;
1069 }
1070 else if (cfract < 0.0) {
1071 cfract = 0.0;
1072 }
1073
1074 // Compute the transmission of material above D1
1075 double ad1trans = m_ict.trans_D1(energy);
1076
1077 // Compute the transmission of V1 veto dome
1078 double v1trans = m_ict.trans_V1(energy);
1079
1080 // Compute the probability that a photon was not self vetoed assuming
1081 // a source on axis
1082 double sveto = m_ict.prob_no_selfveto(energy, 0.0);
1083
1084 // Compute the probability that an event is not a multihit
1085 double mlhit = m_ict.prob_no_multihit(energy);
1086
1087 // Compute the overall (shape independent) transmission coefficients
1088 double oalltr = ad1trans * v1trans * d1prob * cfract * mlhit * sveto;
1089
1090 // Debug
1091 #if defined(G_DEBUG_WEIGHT_IAQ)
1092 std::cout << "Transmission coefficients:" << std::endl;
1093 std::cout << "==========================" << std::endl;
1094 std::cout << " Above D1 transmission ..........: " << ad1trans << std::endl;
1095 std::cout << " V1 veto dome transmission ......: " << v1trans << std::endl;
1096 std::cout << " D1 interaction probability .....: " << d1prob << std::endl;
1097 std::cout << " Compton scatter fraction .......: " << cfract << std::endl;
1098 std::cout << " Compton interaction probability : " << d1prob*cfract << std::endl;
1099 std::cout << " Multihit transmission ..........: " << mlhit << std::endl;
1100 std::cout << " Self-vetoing transmission ......: " << sveto << std::endl;
1101 std::cout << " Overall shape-independent trans.: " << oalltr << std::endl;
1102 #endif
1103
1104 // Get IAQ dimensions
1105 int n_phigeo = m_iaq.naxes(0);
1106 int n_phibar = m_iaq.naxes(1);
1107
1108 // Loop over phigeo
1109 for (int i_phigeo = 0; i_phigeo < n_phigeo; ++i_phigeo) {
1110
1111 // Get geometrical scatter angle (deg)
1112 double phigeo = (double(i_phigeo) + 0.5) * m_phigeo_bin_size;
1113
1114 // Compute the transmission between D1 and D2
1115 double ad2trans = m_ict.trans_D2(energy, phigeo);
1116
1117 // Compute the transmission of V2+V3 veto domes
1118 double v23trans = m_ict.trans_V23(energy, phigeo);
1119
1120 // Compute the D2 interaction coefficient
1121 double d2prob = m_ict.prob_D2inter(energy, phigeo);
1122
1123 // Compute multi-scatter transmission inside D1 module
1124 double mscatt = m_ict.multi_scatter(energy, phigeo);
1125
1126 // Optionally compute PSD correction for the default corrected PSD
1127 // channel selection 0-110
1128 #if defined(G_APPLY_PSD_CORRECTION_IN_RESPONSE_KERNEL)
1129 double psdtrn = 1.0;
1130 #else
1131 double psdtrn = (m_psd_correct) ? m_ict.psd_correction(energy, phigeo) : 1.0;
1132 #endif
1133
1134 // Compute the overall phigeo dependent correction
1135 double oallpg = ad2trans * v23trans * d2prob * mscatt * psdtrn;
1136
1137 // Compute the overall correction
1138 double weight = oalltr * oallpg;
1139
1140 // Debug
1141 #if defined(G_DEBUG_WEIGHT_IAQ)
1142 std::cout << " Phigeo .........................: " << phigeo << std::endl;
1143 std::cout << " Between D1 & D2 transmission ..: " << ad2trans << std::endl;
1144 std::cout << " V2+V3 veto dome transmission ..: " << v23trans << std::endl;
1145 std::cout << " D2 interaction probability ....: " << d2prob << std::endl;
1146 std::cout << " D1 multi-scatter transmission .: " << mscatt << std::endl;
1147 std::cout << " PSD correction ................: " << psdtrn << std::endl;
1148 std::cout << " Overall shape-dependent trans. : " << oallpg << std::endl;
1149 std::cout << " Overall transmission ..........: " << weight << std::endl;
1150 #endif
1151
1152 // Apply weight to all phibar layers
1153 for (int i_phibar = 0; i_phibar < n_phibar; ++i_phibar) {
1154 m_iaq(i_phigeo, i_phibar) *= weight;
1155 }
1156
1157 } // endfor: looped over phigeo
1158
1159 // Return
1160 return;
1161}
1162
1163
1164#if defined(G_COMPASS_LOCPSR)
1165/***********************************************************************//**
1166 * @brief Perform location smearing
1167 *
1168 * @param[in] zenith Zenith angle of source (deg).
1169 *
1170 * The code implementation is a reimplementation of the COMPASS RESPSIT2
1171 * function LOCSPR.F (release 1.0, 05-JAN-93).
1172 ***************************************************************************/
1173void GCOMIaq::location_smearing(const double& zenith)
1174{
1175 // Set constants
1176 const double ZDIST = 158.0;
1177 const double SIGHD1 = 2.30;
1178 const double SIGHD2 = 1.96;
1179 const double SIGVD1 = 2.45;
1180 const double SIGVD2 = 2.17;
1181 const double CONCRI = 5.0;
1182 const int NFINE = 10;
1183 const int NFINEH = NFINE/2;
1184
1185 // Get IAQ dimensions
1186 int n_phigeo = m_iaq.naxes(0);
1187 int n_phibar = m_iaq.naxes(1);
1188
1189 // Initialise arrays
1190 std::vector<double> phigeo(n_phigeo, 0.0);
1191 std::vector<double> siggeo(n_phigeo, 0.0);
1192 std::vector<double> cnorms(n_phigeo, 0.0);
1193
1194 // Initialise variables
1195 double A1 = std::sin(zenith * gammalib::deg2rad);
1196 double A1SQ = A1*A1;
1197 double A2 = std::cos(zenith * gammalib::deg2rad);
1198 double A2SQ = A2*A2;
1199 double ZDIST2 = ZDIST * ZDIST;
1200 double SIGHT2 = SIGHD1*SIGHD1 + SIGHD2*SIGHD2;
1201 double SIGVT2 = SIGVD1*SIGVD1 + SIGVD2*SIGVD2;
1202 double ROOT2 = std::sqrt(2.0);
1203 double RT2PI = std::sqrt(2.0 * gammalib::pi);
1204 double DINTPO = m_phigeo_bin_size/double(NFINE);
1205
1206 // Compute arrays
1207 for (int i_phigeo = 0; i_phigeo < n_phigeo; ++i_phigeo) {
1208
1209 // Compute Phigeo in degrees
1210 phigeo[i_phigeo] = 0.0 + i_phigeo * m_phigeo_bin_size;
1211
1212 // Compute sigma(Phigeo) in degrees
1213 double CPHIG = std::cos(phigeo[i_phigeo] * gammalib::deg2rad);
1214 double SPHIG = std::sin(phigeo[i_phigeo] * gammalib::deg2rad);
1215 double SPHIG2 = SPHIG*SPHIG;
1216 double CPHIG2 = CPHIG*CPHIG;
1217 double SIGXY2 = A2SQ * CPHIG2 * (4.0 * A1SQ * SPHIG2 + CPHIG2 - A1SQ) +
1218 0.5 * A1SQ * (A2SQ*CPHIG2 +A1SQ*SPHIG2*(1.-0.75*CPHIG2));
1219 SIGXY2 = SIGXY2*SIGHT2/ZDIST2;
1220 double SIGZ2 = A2SQ*A2SQ*SPHIG2*CPHIG2 + A2SQ*A1SQ*(0.5-3.*CPHIG2*SPHIG2) +
1221 3.*A1SQ*A1SQ*SPHIG2*CPHIG2/8.;
1222 SIGZ2 = SIGZ2*SIGVT2/ZDIST2;
1223
1224 // Store sigma(Phigeo)
1225 siggeo[i_phigeo] = std::sqrt(SIGXY2+SIGZ2) * gammalib::rad2deg;
1226
1227 // Compute normalisation of convolution
1228 double X0 = double(phigeo[i_phigeo] / (ROOT2*siggeo[i_phigeo]));
1229 cnorms[i_phigeo] = RT2PI*siggeo[i_phigeo]*(1.000 + gammalib::erf(X0))/2.0;
1230 cnorms[i_phigeo] = 1./cnorms[i_phigeo];
1231
1232 } // endfor: looped over Phigeo
1233
1234 // Loop over phibar
1235 for (int iphib = 0; iphib < n_phibar; ++iphib) {
1236
1237 // Copy phigeo vector
1238 std::vector<double> values;
1239 #if defined(G_DEBUG_LOCATION_SMEARING)
1240 double sum_before = 0.0;
1241 #endif
1242 for (int i_phigeo = 0; i_phigeo < n_phigeo; ++i_phigeo) {
1243 values.push_back(m_iaq(i_phigeo, iphib));
1244 #if defined(G_DEBUG_LOCATION_SMEARING)
1245 sum_before += m_iaq(i_phigeo, iphib);
1246 #endif
1247 }
1248
1249 // Loop over Phigeo
1250 for (int ibase = 0; ibase < n_phigeo; ++ibase) {
1251
1252 // Get Phigeo value
1253 double PHBASE = phigeo[ibase];
1254
1255 // Initialise sum
1256 double SUMBAS = 0.0;
1257
1258 // Loop over convolution
1259 for (int iconv = 0; iconv < n_phigeo; ++iconv) {
1260
1261 // Compute convolution only if we are within 5 sigma of
1262 // the smearing kernel
1263 double DELTA = std::abs(PHBASE-phigeo[iconv]);
1264 if (DELTA < CONCRI * siggeo[iconv]) {
1265
1266 // Compute gradients for interpolation
1267 double PHICEN = phigeo[iconv];
1268 double VALMED = values[iconv];
1269 double SIGMED = siggeo[iconv];
1270 double CNOMED = cnorms[iconv];
1271 double DERPSL = 0.0;
1272 double DERSIL = 0.0;
1273 double DERCNL = 0.0;
1274 double DERPSU = 0.0;
1275 double DERSIU = 0.0;
1276 double DERCNU = 0.0;
1277 if (iconv > 0) {
1278 double VALLOW = values[iconv-1];
1279 double SIGLOW = siggeo[iconv-1];
1280 double CNOLOW = cnorms[iconv-1];
1281 DERPSL = (VALMED-VALLOW)/m_phigeo_bin_size;
1282 DERSIL = (SIGMED-SIGLOW)/m_phigeo_bin_size;
1283 DERCNL = (CNOMED-CNOLOW)/m_phigeo_bin_size;
1284 }
1285 if (iconv < (n_phigeo-1)) {
1286 double VALUPP = values[iconv+1];
1287 double SIGUPP = siggeo[iconv+1];
1288 double CNOUPP = cnorms[iconv+1];
1289 DERPSU = (VALUPP-VALMED)/m_phigeo_bin_size;
1290 DERSIU = (SIGUPP-SIGMED)/m_phigeo_bin_size;
1291 DERCNU = (CNOUPP-CNOMED)/m_phigeo_bin_size;
1292 }
1293 if (iconv == 0) {
1294 DERPSL = DERPSU;
1295 DERSIL = DERSIU;
1296 DERCNL = DERCNU;
1297 }
1298 if (iconv == (n_phigeo-1)) {
1299 DERPSU = DERPSL;
1300 DERSIU = DERSIL;
1301 DERCNU = DERCNL;
1302 }
1303
1304 // Do convolution
1305 for (int ifine = 1; ifine <= NFINE; ++ifine) {
1306
1307 // Declare results
1308 double SIGMAF;
1309 double VALUEF;
1310 double CNORMF;
1311
1312 // Calculate the fine grid phigeo value
1313 double PHIFIN = PHICEN + (ifine-NFINEH-0.5)*DINTPO;
1314 double DELTAF = (ifine-NFINEH-0.5)*DINTPO;
1315 if (ifine <= NFINEH) {
1316 SIGMAF = SIGMED + DERSIL*DELTAF;
1317 VALUEF = VALMED + DERPSL*DELTAF;
1318 CNORMF = CNOMED + DERCNL*DELTAF;
1319 }
1320 else {
1321 SIGMAF = SIGMED + DERSIU*DELTAF;
1322 VALUEF = VALMED + DERPSU*DELTAF;
1323 CNORMF = CNOMED + DERCNU*DELTAF;
1324 }
1325
1326 // Calculate the convolution contribution at PHBASE by
1327 // PHIFIN DINTPO is the "fine" phigeo increment in
1328 // degrees, by which end result is normalized (because
1329 // CNORMS is normalisation factor in 1/degree)
1330 double arg = (PHIFIN-PHBASE)/SIGMAF;
1331 double VALUE = VALUEF * CNORMF * std::exp(-0.5*arg*arg);
1332 SUMBAS = SUMBAS+VALUE;
1333
1334 } // endfor: convolution loop
1335
1336 } // endif: within 5 sigma
1337
1338 } // endfor: end over convolution
1339
1340 // Set value
1341 m_iaq(ibase, iphib) = SUMBAS * DINTPO;
1342
1343 } // endfor: looped over Phigeo pixels
1344
1345 // Restore phigeo vector
1346 #if defined(G_DEBUG_LOCATION_SMEARING)
1347 double sum_after = 0.0;
1348 for (int i_phigeo = 0; i_phigeo < n_phigeo; ++i_phigeo) {
1349 sum_after += convolved_values[i_phigeo];
1350 }
1351
1352 // Debug
1353 std::cout << "phibar=" << (double(i_phibar) + 0.5) * m_phibar_bin_size;
1354 std::cout << " before=" << sum_before;
1355 std::cout << " after=" << sum_after;
1356 if (sum_before > 0.0) {
1357 std::cout << " (" << sum_after/sum_before << ")";
1358 }
1359 std::cout << std::endl;
1360 #endif
1361
1362 } // endfor: looped over phibar
1363
1364 // Return
1365 return;
1366}
1367#else
1368/***********************************************************************//**
1369 * @brief Perform location smearing
1370 *
1371 * @param[in] zenith Zenith angle of source (deg).
1372 *
1373 * The code implementation is based on the COMPASS RESPSIT2 function LOCSPR.F
1374 * (release 1.0, 05-JAN-93).
1375 ***************************************************************************/
1376void GCOMIaq::location_smearing(const double& zenith)
1377{
1378 // Compute location spread for all phigeo angles
1379 std::vector<double> sigmas = location_spread(zenith);
1380
1381 // Get IAQ dimensions
1382 int n_phigeo = m_iaq.naxes(0);
1383 int n_phibar = m_iaq.naxes(1);
1384
1385 // Setup phigeo node array for interpolated
1386 GNodeArray phigeos;
1387 for (int i_phigeo = 0; i_phigeo < n_phigeo; ++i_phigeo) {
1388 phigeos.append((double(i_phigeo) + 0.5) * m_phigeo_bin_size);
1389 }
1390
1391 // Loop over phibar
1392 for (int i_phibar = 0; i_phibar < n_phibar; ++i_phibar) {
1393
1394 // Copy phigeo vector
1395 std::vector<double> values;
1396 #if defined(G_DEBUG_LOCATION_SMEARING)
1397 double sum_before = 0.0;
1398 #endif
1399 for (int i_phigeo = 0; i_phigeo < n_phigeo; ++i_phigeo) {
1400 values.push_back(m_iaq(i_phigeo, i_phibar));
1401 #if defined(G_DEBUG_LOCATION_SMEARING)
1402 sum_before += m_iaq(i_phigeo, i_phibar);
1403 #endif
1404 }
1405
1406 // Compute convolution integral for each phigeo pixel
1407 std::vector<double> convolved_values;
1408 for (int i_phigeo = 0; i_phigeo < n_phigeo; ++i_phigeo) {
1409
1410 // Setup integration kernel
1411 smearing_kernel integrand(phigeos[i_phigeo],
1412 sigmas[i_phigeo],
1413 phigeos,
1414 values);
1415
1416 // Setup integral
1417 GIntegral integral(&integrand);
1418
1419 // Set precision
1420 integral.eps(1.0e-4);
1421
1422 // No warnings
1423 #if defined(G_LOCATION_SMEARING_NO_WARNINGS)
1424 integral.silent(true);
1425 #endif
1426
1427 // Get integration boundaries
1428 double phigeo_min = phigeos[i_phigeo] - 5.0 * sigmas[i_phigeo];
1429 double phigeo_max = phigeos[i_phigeo] + 5.0 * sigmas[i_phigeo];
1430
1431 // Perform integration
1432 double value = integral.romberg(phigeo_min, phigeo_max);
1433
1434 // Store result
1435 convolved_values.push_back(value);
1436
1437 } // endfor: convolution integral
1438
1439 // Restore phigeo vector
1440 #if defined(G_DEBUG_LOCATION_SMEARING)
1441 double sum_after = 0.0;
1442 #endif
1443 for (int i_phigeo = 0; i_phigeo < n_phigeo; ++i_phigeo) {
1444 m_iaq(i_phigeo, i_phibar) = convolved_values[i_phigeo];
1445 #if defined(G_DEBUG_LOCATION_SMEARING)
1446 sum_after += convolved_values[i_phigeo];
1447 #endif
1448 }
1449
1450 // Debug
1451 #if defined(G_DEBUG_LOCATION_SMEARING)
1452 std::cout << "phibar=" << (double(i_phibar) + 0.5) * m_phibar_bin_size;
1453 std::cout << " before=" << sum_before;
1454 std::cout << " after=" << sum_after;
1455 if (sum_before > 0.0) {
1456 std::cout << " (" << sum_after/sum_before << ")";
1457 }
1458 std::cout << std::endl;
1459 #endif
1460
1461 } // endfor: looped over phibar
1462
1463 // Return
1464 return;
1465}
1466#endif
1467
1468
1469/***********************************************************************//**
1470 * @brief Compute location spread vector
1471 *
1472 * @param[in] zenith Zenith angle of source (deg).
1473 * @return Vector of location spreads in Gaussian sigma
1474 *
1475 * The method computes the geometrical smearing widths according to D1 and
1476 * D2 X,Y,Z location uncertainties. The algorithm is based on a constant
1477 * D1, D2 location uncertainty for all energies. This is also why the
1478 * method can be called at the end of the IAQ generation: the location
1479 * spread does not have to be added for each energy separately.
1480 *
1481 * The location spread is a (mild) function of phigeo and source @p zenith
1482 * angle.
1483 *
1484 * The code implementation is based on the COMPASS RESPSIT2 function LOCSPR.F
1485 * (release 1.0, 05-JAN-93).
1486 ***************************************************************************/
1487std::vector<double> GCOMIaq::location_spread(const double& zenith) const
1488{
1489 // Set distance between the detector midplanes of D1 and D2 (cm)
1490 const double zdist = 158.0;
1491
1492 // Set horizontal (x,y) location spread in D1 resp. D2 (cm)
1493 const double sighd1 = 2.30;
1494 const double sighd2 = 1.96;
1495
1496 // Set vertical (z) location spread in D1 resp. D2 based on homogeneous
1497 // event distributions in z-direction (cm)
1498 const double sigvd1 = 2.45;
1499 const double sigvd2 = 2.17;
1500
1501 // Derive constants
1502 const double zdist2 = zdist * zdist;
1503 const double sight2 = sighd1*sighd1 + sighd2*sighd2;
1504 const double sigvt2 = sigvd1*sigvd1 + sigvd2*sigvd2;
1505
1506 // Initialise result
1507 std::vector<double> sigmas;
1508
1509 // Compute terms
1510 double a1 = std::sin(zenith * gammalib::deg2rad);
1511 double a1sq = a1 * a1;
1512 double a2 = std::cos(zenith * gammalib::deg2rad);
1513 double a2sq = a2 * a2;
1514
1515 // Get IAQ dimensions
1516 int n_phigeo = m_iaq.naxes(0);
1517
1518 // Loop over phigeo
1519 for (int i_phigeo = 0; i_phigeo < n_phigeo; ++i_phigeo) {
1520
1521 // Get geometrical scatter angle (deg)
1522 double phigeo = (double(i_phigeo) + 0.5) * m_phigeo_bin_size;
1523
1524 // Compute spread due to (x,y) uncertainty
1525 double cphig = std::cos(phigeo * gammalib::deg2rad);
1526 double sphig = std::sin(phigeo * gammalib::deg2rad);
1527 double cphig2 = cphig*cphig;
1528 double sphig2 = sphig*sphig;
1529 double sigxy2 = (a2sq * cphig2 *
1530 (4.0 * a1sq * sphig2 + cphig2 - a1sq) +
1531 0.5 * a1sq *
1532 (a2sq * cphig2 + a1sq * sphig2 * (1.0 - 0.75 * cphig2))) *
1533 sight2 / zdist2;
1534
1535 // Compute spread due to (z) uncertainty
1536 double sigz2 = (a2sq * a2sq * sphig2 * cphig2 +
1537 a2sq * a1sq * (0.5 - 3.0 * cphig2 * sphig2) +
1538 3.0 * a1sq * a1sq * sphig2 * cphig2 / 8.0) *
1539 sigvt2 / zdist2;
1540
1541 // Calculate sigma in degrees
1542 double siggeo = std::sqrt(sigxy2 + sigz2) * gammalib::rad2deg;
1543
1544 // Append sigma
1545 sigmas.push_back(siggeo);
1546
1547 // Debug
1548 #if defined(G_DEBUG_LOCATION_SPREAD)
1549 std::cout << "phigeo=" << phigeo << " sigma=" << siggeo << std::endl;
1550 #endif
1551
1552 // We should now create a warning if siggeo+zenith is larger than
1553 // 90.0 deg, at least the original code does that
1554
1555 } // endfor: looped over phigeo
1556
1557 // Return sigmas
1558 return sigmas;
1559}
1560
1561
1562/***********************************************************************//**
1563 * @brief Computes product of D1 and D2 responses at energy E1 and phibar
1564 *
1565 * @param[in] energy1 D1 energy deposit (MeV).
1566 *
1567 * The method computes
1568 *
1569 * \f[
1570 * R(E_1,E_2|\hat{E_1},\hat{E_2}) =
1571 * R_1(E_1|\hat{E_1}) \times R_2(E_2|\hat{E_2}) \times J
1572 * \f]
1573 *
1574 * where
1575 * \f$R_1(E_1|\hat{E_1})\f$ is the D1 module response function,
1576 * \f$R_2(E_2|\hat{E_2})\f$ is the D2 module response function,
1577 * \f$E_1\f$ and \f$E_2\f$ are the measured D1 and D2 energy deposits,
1578 * respectively,
1579 * \f$\hat{E_1}\f$ and \f$\hat{E_2}\f$ are the true D1 and D2 energy
1580 * deposits, respectively, and \f$J\f$ is the Jacobian.
1581 *
1582 * The measured D2 energy deposit is computed using
1583 *
1584 * \f[
1585 * E_2 = \frac{1}{2} \times \left(
1586 * \sqrt{\frac{4 m_e c^2 E_1}{1 - \cos \bar{\varphi}} + E_1^2} - E_1
1587 * \right)
1588 * \f]
1589 *
1590 * where
1591 * \f$m_e\,c^2\f$ the electron rest mass, and
1592 * \f$\bar{\varphi}\f$ the Compton scatter angle.
1593 *
1594 * The Jacobian is given by
1595 *
1596 * \f[
1597 * J = \frac{m_e c^2 \sqrt{E_1} \sin \bar{\varphi}}
1598 * {(1 - \cos \bar{\varphi})^2
1599 * \sqrt{\frac{4 m_e c^2}{1 - \cos \bar{\varphi}} + E_1}}
1600 * \f]
1601 *
1602 * The code implementation is based on the COMPASS RESPSIT2 function FUNC.F
1603 * (release 4.0, 21-Oct-91).
1604 ***************************************************************************/
1605double GCOMIaq::response_kernel::eval(const double& energy1)
1606{
1607 // Initialise result
1608 double value = 0.0;
1609
1610 // Set E1 and 1-cos(phibar). If the values are too small, limit to small
1611 // positive numbers.
1612 #if defined(G_RESPONSE_KERNEL_LIMITS)
1613 double e1 = (energy1 < 1.0e-20) ? 1.0e-20 : energy1;
1614 double cp = 1.0 - m_cos_phibar;
1615 if (std::abs(cp) < 1.0e-20) {
1616 cp = 1.0e-20;
1617 }
1618 #else
1619 double e1 = energy1;
1620 double cp = 1.0 - m_cos_phibar;
1621 #endif
1622
1623 // Compute E2 and Jacobian
1624 double term1 = 4.0 * gammalib::mec2 / cp;
1625 double e2 = 0.5 * (std::sqrt(term1 * e1 + e1*e1) - e1);
1626 double jc = gammalib::mec2 * std::sqrt(e1) * m_sin_phibar /
1627 (cp * cp * std::sqrt(term1 + e1));
1628
1629 // Debug
1630 #if defined(G_DEBUG_RESPONSE_KERNEL)
1631 std::cout << " e1=" << e1;
1632 std::cout << " e2=" << e2;
1633 std::cout << " jc=" << jc << std::endl;
1634 #endif
1635
1636 // Allow to break now at any time
1637 do {
1638
1639 // Break if the D1 energy deposit is outside the energy range
1640 if ((e1 < m_e1min) || (e1 > m_e1max)) {
1641 break;
1642 }
1643
1644 // Break if the D2 energy deposit is outside the energy range
1645 if ((e2 < m_e2min) || (e2 > m_e2max)) {
1646 break;
1647 }
1648
1649 // Break if the total energy deposit is outside the energy range
1650 double etot = e1 + e2;
1651 if ((etot < m_etmin) || (etot > m_etmax)) {
1652 break;
1653 }
1654
1655 // Break if the Jacobian precision is too poor
1656 #if defined(G_RESPONSE_KERNEL_LIMITS)
1657 if (jc < 1.0e-20) {
1658 break;
1659 }
1660 #endif
1661
1662 // Get D1 response. Break if it is too small.
1663 double d1 = m_response_d1(m_etrue1, e1);
1664 #if defined(G_RESPONSE_KERNEL_LIMITS)
1665 if (d1 < 1.0e-20) {
1666 break;
1667 }
1668 #endif
1669
1670 // Optionally apply PSD correction
1671 #if defined(G_APPLY_PSD_CORRECTION_IN_RESPONSE_KERNEL)
1672 const double a1 = 1727.9;
1673 const double a2 = 2.530;
1674 d1 *= 1.0 - (1.0 / (a1 * std::pow(e1, a2) + 1.0));
1675 #endif
1676
1677 // Get D2 response. Break if it is too small.
1678 double d2 = m_response_d2(m_etrue2, e2);
1679 #if defined(G_RESPONSE_KERNEL_LIMITS)
1680 if (d2 < 1.0e-20) {
1681 break;
1682 }
1683 #endif
1684
1685 // Compute value
1686 value = jc * d1 * d2;
1687
1688 } while(false);
1689
1690 // Return value
1691 return value;
1692}
1693
1694
1695/***********************************************************************//**
1696 * @brief Convolves response with Gaussian kernel
1697 *
1698 * @param[in] phigeo Geometrical scatter angle.
1699 * @return Convolved response value.
1700 *
1701 ***************************************************************************/
1702double GCOMIaq::smearing_kernel::eval(const double& phigeo)
1703{
1704 // Get interpolated IAQ value for geometrical scatter angle. Make sure
1705 // that the value is positive
1706 double value = m_phigeos.interpolate(phigeo, m_values);
1707 if (value < 0.0) {
1708 value = 0.0;
1709 }
1710
1711 // Multiply with Gaussian
1712 double arg = (m_phigeo - phigeo) * m_wgt;
1713 value *= m_norm * std::exp(-0.5 * arg * arg);
1714
1715 // Return value
1716 return value;
1717}
COMPTEL instrument response representation class interface definition.
Implementation of support function used by COMPTEL classes.
Energy boundaries class interface definition.
Filename class interface definition.
FITS file class interface definition.
Integration class interface definition.
Energy flux normalized power law spectral model class interface definition.
Flux normalized power law spectral model class interface definition.
Power law spectral model class interface definition.
Abstract spectral model base class interface definition.
Gammalib tools definition.
GChatter
Definition GTypemaps.hpp:33
@ SILENT
Definition GTypemaps.hpp:34
double sum(const GVector &vector)
Computes vector sum.
Definition GVector.cpp:944
Interface for the COMPTEL D1 module response class.
double ewidth(const double &etrue) const
Return energy threshold width.
double sigma(const double &etrue) const
Return photo peak standard deviation.
double emin(const double &etrue) const
Return minimum energy.
double emax(const double &etrue) const
Return maximum energy.
double position(const double &etrue) const
Return photo peak position.
void clear(void)
Clear instance.
Interface for the COMPTEL D2 module response class.
double emax(const double &etrue) const
Return maximum energy.
void clear(void)
Clear instance.
const GCOMD1Response & m_response_d1
Reference to D1 module response.
Definition GCOMIaq.hpp:124
double m_etrue1
True D1 energy (MeV)
Definition GCOMIaq.hpp:126
double m_cos_phibar
cos(phibar)
Definition GCOMIaq.hpp:128
double eval(const double &energy1)
Computes product of D1 and D2 responses at energy E1 and phibar.
Definition GCOMIaq.cpp:1605
double m_e1min
Minimum D1 energy (MeV)
Definition GCOMIaq.hpp:132
double m_e1max
Maximum D1 energy (MeV)
Definition GCOMIaq.hpp:133
double m_etrue2
True D2 energy (MeV)
Definition GCOMIaq.hpp:127
double m_e2max
Maximum D2 energy (MeV)
Definition GCOMIaq.hpp:135
const GCOMD2Response & m_response_d2
Reference to D2 module response.
Definition GCOMIaq.hpp:125
double m_sin_phibar
sin(phibar)
Definition GCOMIaq.hpp:129
double m_etmin
Minimum total energy (MeV)
Definition GCOMIaq.hpp:130
double m_etmax
Maximum total energy (MeV)
Definition GCOMIaq.hpp:131
double m_e2min
Minimum D2 energy (MeV)
Definition GCOMIaq.hpp:134
double eval(const double &phigeo)
Convolves response with Gaussian kernel.
Definition GCOMIaq.cpp:1702
Interface for the COMPTEL instrument response representation class.
Definition GCOMIaq.hpp:52
double klein_nishina_integral(const double &v, const double &a)
Computes Klein-Nishina integral.
Definition GCOMIaq.cpp:1023
void free_members(void)
Delete class members.
Definition GCOMIaq.cpp:616
int m_num_energies
Number of energies for continuum IAQ.
Definition GCOMIaq.hpp:174
double m_phigeo_bin_size
Bin size in geometrical scatter angle (deg)
Definition GCOMIaq.hpp:167
double m_e2max
Maximum D2 energy (MeV)
Definition GCOMIaq.hpp:173
void copy_members(const GCOMIaq &iaq)
Copy class members.
Definition GCOMIaq.cpp:585
double m_zenith
Zenith angle for location smearing (deg)
Definition GCOMIaq.hpp:176
GCOMIaq * clone(void) const
Clone instance.
Definition GCOMIaq.cpp:237
GCOMD2Response m_response_d2
D2 module response.
Definition GCOMIaq.hpp:163
GEbounds m_ebounds
Energy boundaries.
Definition GCOMIaq.hpp:161
~GCOMIaq(void)
Destructor.
Definition GCOMIaq.cpp:162
double m_e2min
Minimum D2 energy (MeV)
Definition GCOMIaq.hpp:172
double m_phibar_max
Maximum Compton scatter angle (deg)
Definition GCOMIaq.hpp:166
double m_phigeo_max
Maximum geometrical scatter angle (deg)
Definition GCOMIaq.hpp:165
void klein_nishina(const double &energy)
Multiply IAQ matrix by the Klein-Nishina formula.
Definition GCOMIaq.cpp:886
GCOMD1Response m_response_d1
D1 module response.
Definition GCOMIaq.hpp:162
GCOMIaq & operator=(const GCOMIaq &iaq)
Assignment operator.
Definition GCOMIaq.cpp:184
GCOMInstChars m_ict
Instrument characteristics.
Definition GCOMIaq.hpp:164
double klein_nishina_bin(const double &energy, const double &phigeo)
Computes Klein-Nishina probability for one bin.
Definition GCOMIaq.cpp:955
void init_response(void)
Initialise COMPTEL response function and instrument characteristics.
Definition GCOMIaq.cpp:626
double m_phibar_resolution
Bin size for oversampling (deg)
Definition GCOMIaq.hpp:169
void weight_iaq(const double &energy)
Weight IAQ matrix.
Definition GCOMIaq.cpp:1056
GFitsImageFloat m_iaq
Response.
Definition GCOMIaq.hpp:160
std::vector< double > location_spread(const double &zenith) const
Compute location spread vector.
Definition GCOMIaq.cpp:1487
void location_smearing(const double &zenith)
Perform location smearing.
Definition GCOMIaq.cpp:1376
void compton_kinematics(const double &energy)
Generate IAQ matrix based on Compton kinematics.
Definition GCOMIaq.cpp:686
std::string print(const GChatter &chatter=NORMAL) const
Print COMPTEL instrument response representation information.
Definition GCOMIaq.cpp:469
void remove_cards(void)
Remove any extra header cards.
Definition GCOMIaq.cpp:644
void save(const GFilename &filename, const bool &clobber=false) const
Save COMPTEL instrument response representation into FITS file.
Definition GCOMIaq.cpp:426
void set(const GEnergy &energy, const GEbounds &ebounds)
Set mono-energetic IAQ.
Definition GCOMIaq.cpp:254
GCOMIaq(void)
Void constructor.
Definition GCOMIaq.cpp:79
void clear(void)
Clear instance.
Definition GCOMIaq.cpp:218
void init_members(void)
Initialise class members.
Definition GCOMIaq.cpp:552
double m_e1min
Minimum D1 energy (MeV)
Definition GCOMIaq.hpp:170
double m_phibar_bin_size
Bin size in Compton scatter angle (deg)
Definition GCOMIaq.hpp:168
double compute_iaq_bin(const double &etrue1, const double &etrue2, const double &phibar)
Computes the IAQ for one bin.
Definition GCOMIaq.cpp:811
double m_e1max
Maximum D1 energy (MeV)
Definition GCOMIaq.hpp:171
bool m_psd_correct
PSD correction usage flag.
Definition GCOMIaq.hpp:175
Interface for the COMPTEL Instrument Characteristics class.
double trans_V1(const double &energy) const
Return V1 veto dome transmission.
double trans_V23(const double &energy, const double &phigeo) const
Return V2+V3 veto dome transmission.
double trans_D1(const double &energy) const
Return transmission above D1.
double multi_scatter(const double &energy, const double &phigeo) const
Return multi-scatter correction.
double prob_no_multihit(const double &energy) const
Return probability that no multihit occured.
double prob_no_selfveto(const double &energy, const double &zenith) const
Return probability that photon was not self vetoed.
double trans_D2(const double &energy, const double &phigeo) const
Return transmission of material between D1 and D2.
double psd_correction(const double &energy, const double &phigeo) const
Return PSD correction.
void clear(void)
Clear instance.
double prob_D1inter(const double &energy) const
Return D1 interaction probability.
double prob_D2inter(const double &energy, const double &phigeo) const
Return D2 interaction probability.
Calibration database class.
Definition GCaldb.hpp:66
Energy boundaries container class.
Definition GEbounds.hpp:60
const GEnergy & emax(void) const
Return maximum energy of all intervals.
Definition GEbounds.hpp:199
int size(void) const
Return number of energy boundaries.
Definition GEbounds.hpp:163
void clear(void)
Clear energy boundaries.
Definition GEbounds.cpp:268
GEnergy elogmean(const int &index) const
Returns logarithmic mean energy for a given energy interval.
const GEnergy & emin(void) const
Return minimum energy of all intervals.
Definition GEbounds.hpp:187
Class that handles energies in a unit independent way.
Definition GEnergy.hpp:48
double MeV(void) const
Return energy in MeV.
Definition GEnergy.cpp:321
std::string print(const GChatter &chatter=NORMAL) const
Print energy.
Definition GEnergy.cpp:748
Filename class.
Definition GFilename.hpp:62
bool has_extname(void) const
Signal if filename has an extension name.
std::string url(void) const
Return Uniform Resource Locator (URL)
std::string extname(const std::string &defaultname="") const
Return extension name.
const GFitsHeader & header(void) const
Return extension header.
Definition GFitsHDU.hpp:205
bool has_card(const int &cardno) const
Check existence of header card.
Definition GFitsHDU.hpp:233
const std::string & extname(void) const
Return extension name.
Definition GFitsHDU.hpp:162
GFitsHeaderCard & card(const int &cardno)
Return header card.
Definition GFitsHDU.hpp:259
Interface for FITS header class.
Single precision FITS image class.
void clear(void)
Clear instance.
int naxes(const int &axis) const
Return dimension of an image axis.
const int & npix(void) const
Return size of pixel array.
const int & naxis(void) const
Return dimension of image.
FITS file class.
Definition GFits.hpp:63
void saveto(const GFilename &filename, const bool &clobber=false)
Saves to specified FITS file.
Definition GFits.cpp:1293
GFitsHDU * append(const GFitsHDU &hdu)
Append HDU to FITS file.
Definition GFits.cpp:678
GIntegral class interface definition.
Definition GIntegral.hpp:46
void eps(const double &eps)
Set relative precision.
double romberg(std::vector< double > bounds, const int &order=5)
Perform Romberg integration.
void silent(const bool &silent)
Set silence flag.
Energy flux normalized power law spectral model class.
double index(void) const
Return power law index.
Photon flux normalized power law spectral model class.
double index(void) const
Return power law index.
Power law spectral model class.
double index(void) const
Return power law index.
Abstract spectral model base class.
virtual double flux(const GEnergy &emin, const GEnergy &emax) const =0
virtual std::string classname(void) const =0
Return class name.
Node array class.
void append(const double &node)
Append one node to array.
std::string parformat(const std::string &s, const int &indent=0)
Convert string in parameter format.
Definition GTools.cpp:1143
std::string str(const unsigned short int &value)
Convert unsigned short integer value into string.
Definition GTools.cpp:489
const double pi
Definition GMath.hpp:35
double erf(const double &arg)
Computes error function.
Definition GMath.cpp:417
const double deg2rad
Definition GMath.hpp:43
const double mec2
Definition GTools.hpp:52
double com_energy2(const double &energy, const double &phigeo)
Return D2 energy deposit.
const double rad2deg
Definition GMath.hpp:44