GammaLib  2.1.0.dev
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
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"
37 #include "GModelSpectralPlaw.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
82  init_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
97  init_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  **************************************************************************/
117 GCOMIaq::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  ***************************************************************************/
218 void GCOMIaq::clear(void)
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  ***************************************************************************/
254 void 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
260  init_response();
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  ***************************************************************************/
301 void 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
307  init_response();
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
331  GFitsImageFloat iaq = m_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  ***************************************************************************/
426 void GCOMIaq::save(const GFilename& filename, const bool& clobber) const
427 {
428  // Work on a copy of the image to set the extension name
429  GFitsImageFloat iaq = m_iaq;
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  ***************************************************************************/
469 std::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();
556  m_ebounds.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  ***************************************************************************/
686 void 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  ***************************************************************************/
811 double 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
835  response_kernel integrand(m_response_d1,
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  ***************************************************************************/
886 void 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  ***************************************************************************/
955 double 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  ***************************************************************************/
1023 double 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  ***************************************************************************/
1056 void 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  ***************************************************************************/
1173 void 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  ***************************************************************************/
1376 void 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  ***************************************************************************/
1487 std::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  ***************************************************************************/
1605 double 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  ***************************************************************************/
1702 double 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 }
const double mec2
Definition: GTools.hpp:52
double m_phigeo_bin_size
Bin size in geometrical scatter angle (deg)
Definition: GCOMIaq.hpp:167
void clear(void)
Clear instance.
void compton_kinematics(const double &energy)
Generate IAQ matrix based on Compton kinematics.
Definition: GCOMIaq.cpp:686
Photon flux normalized power law spectral model class.
const int & naxis(void) const
Return dimension of image.
Definition: GFitsImage.hpp:156
bool has_card(const int &cardno) const
Check existence of header card.
Definition: GFitsHDU.hpp:233
Node array class.
Definition: GNodeArray.hpp:60
double romberg(std::vector< double > bounds, const int &order=5)
Perform Romberg integration.
Definition: GIntegral.cpp:382
double prob_no_multihit(const double &energy) const
Return probability that no multihit occured.
double m_phibar_bin_size
Bin size in Compton scatter angle (deg)
Definition: GCOMIaq.hpp:168
Abstract spectral model base class.
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 trans_D2(const double &energy, const double &phigeo) const
Return transmission of material between D1 and D2.
double prob_D2inter(const double &energy, const double &phigeo) const
Return D2 interaction probability.
double emax(const double &etrue) const
Return maximum energy.
const double pi
Definition: GMath.hpp:35
double erf(const double &arg)
Computes error function.
Definition: GMath.cpp:417
void init_response(void)
Initialise COMPTEL response function and instrument characteristics.
Definition: GCOMIaq.cpp:626
double m_e1min
Minimum D1 energy (MeV)
Definition: GCOMIaq.hpp:132
double trans_D1(const double &energy) const
Return transmission above D1.
GVector abs(const GVector &vector)
Computes absolute of vector elements.
Definition: GVector.cpp:1253
double m_etrue1
True D1 energy (MeV)
Definition: GCOMIaq.hpp:126
double prob_D1inter(const double &energy) const
Return D1 interaction probability.
int size(void) const
Return number of energy boundaries.
Definition: GEbounds.hpp:163
void save(const GFilename &filename, const bool &clobber=false) const
Save COMPTEL instrument response representation into FITS file.
Definition: GCOMIaq.cpp:426
double index(void) const
Return power law index.
GVector cos(const GVector &vector)
Computes cosine of vector elements.
Definition: GVector.cpp:1190
double com_energy2(const double &energy, const double &phigeo)
Return D2 energy deposit.
double m_e1max
Maximum D1 energy (MeV)
Definition: GCOMIaq.hpp:133
bool has_extname(void) const
Signal if filename has an extension name.
Definition: GFilename.hpp:255
double m_phigeo_max
Maximum geometrical scatter angle (deg)
Definition: GCOMIaq.hpp:165
void set(const GEnergy &energy, const GEbounds &ebounds)
Set mono-energetic IAQ.
Definition: GCOMIaq.cpp:254
double m_e1max
Maximum D1 energy (MeV)
Definition: GCOMIaq.hpp:171
double m_sin_phibar
sin(phibar)
Definition: GCOMIaq.hpp:129
GCOMIaq(void)
Void constructor.
Definition: GCOMIaq.cpp:79
std::vector< double > location_spread(const double &zenith) const
Compute location spread vector.
Definition: GCOMIaq.cpp:1487
double sum(const GVector &vector)
Computes vector sum.
Definition: GVector.cpp:944
std::string extname(const std::string &defaultname="") const
Return extension name.
Definition: GFilename.cpp:385
Interface for the COMPTEL Instrument Characteristics class.
double MeV(void) const
Return energy in MeV.
Definition: GEnergy.cpp:321
double eval(const double &energy1)
Computes product of D1 and D2 responses at energy E1 and phibar.
Definition: GCOMIaq.cpp:1605
Gammalib tools definition.
double m_phibar_resolution
Bin size for oversampling (deg)
Definition: GCOMIaq.hpp:169
GIntegral class interface definition.
Definition: GIntegral.hpp:46
FITS file class.
Definition: GFits.hpp:63
double m_phibar_max
Maximum Compton scatter angle (deg)
Definition: GCOMIaq.hpp:166
const GFitsHeader & header(void) const
Return extension header.
Definition: GFitsHDU.hpp:205
FITS file class interface definition.
void klein_nishina(const double &energy)
Multiply IAQ matrix by the Klein-Nishina formula.
Definition: GCOMIaq.cpp:886
Energy flux normalized power law spectral model class interface definition.
Power law spectral model class.
virtual std::string classname(void) const =0
Return class name.
Implementation of support function used by COMPTEL classes.
COMPTEL instrument response representation class interface definition.
void location_smearing(const double &zenith)
Perform location smearing.
Definition: GCOMIaq.cpp:1376
double m_e2max
Maximum D2 energy (MeV)
Definition: GCOMIaq.hpp:135
double psd_correction(const double &energy, const double &phigeo) const
Return PSD correction.
void saveto(const GFilename &filename, const bool &clobber=false)
Saves to specified FITS file.
Definition: GFits.cpp:1293
double m_etmin
Minimum total energy (MeV)
Definition: GCOMIaq.hpp:130
double m_e2max
Maximum D2 energy (MeV)
Definition: GCOMIaq.hpp:173
void clear(void)
Clear instance.
Interface for FITS header class.
Definition: GFitsHeader.hpp:49
const double deg2rad
Definition: GMath.hpp:43
GVector sqrt(const GVector &vector)
Computes square root of vector elements.
Definition: GVector.cpp:1358
Calibration database class.
Definition: GCaldb.hpp:66
Energy boundaries container class.
Definition: GEbounds.hpp:60
std::string print(const GChatter &chatter=NORMAL) const
Print energy.
Definition: GEnergy.cpp:748
Interface for the COMPTEL D2 module response class.
Interface for the COMPTEL D1 module response class.
double compute_iaq_bin(const double &etrue1, const double &etrue2, const double &phibar)
Computes the IAQ for one bin.
Definition: GCOMIaq.cpp:811
Filename class.
Definition: GFilename.hpp:62
double klein_nishina_bin(const double &energy, const double &phigeo)
Computes Klein-Nishina probability for one bin.
Definition: GCOMIaq.cpp:955
const GEnergy & emin(void) const
Return minimum energy of all intervals.
Definition: GEbounds.hpp:187
GEbounds m_ebounds
Energy boundaries.
Definition: GCOMIaq.hpp:161
GVector log(const GVector &vector)
Computes natural logarithm of vector elements.
Definition: GVector.cpp:1274
double sigma(const double &etrue) const
Return photo peak standard deviation.
double ewidth(const double &etrue) const
Return energy threshold width.
GCOMD1Response m_response_d1
D1 module response.
Definition: GCOMIaq.hpp:162
bool m_psd_correct
PSD correction usage flag.
Definition: GCOMIaq.hpp:175
Power law spectral model class interface definition.
GChatter
Definition: GTypemaps.hpp:33
GEnergy elogmean(const int &index) const
Returns logarithmic mean energy for a given energy interval.
Definition: GEbounds.cpp:1126
int m_num_energies
Number of energies for continuum IAQ.
Definition: GCOMIaq.hpp:174
GCOMD2Response m_response_d2
D2 module response.
Definition: GCOMIaq.hpp:163
double m_etrue2
True D2 energy (MeV)
Definition: GCOMIaq.hpp:127
double m_e1min
Minimum D1 energy (MeV)
Definition: GCOMIaq.hpp:170
Single precision FITS image class.
GCOMIaq & operator=(const GCOMIaq &iaq)
Assignment operator.
Definition: GCOMIaq.cpp:184
const std::string & extname(void) const
Return extension name.
Definition: GFitsHDU.hpp:162
void clear(void)
Clear energy boundaries.
Definition: GEbounds.cpp:268
Flux normalized power law spectral model class interface definition.
GCOMIaq * clone(void) const
Clone instance.
Definition: GCOMIaq.cpp:237
double m_cos_phibar
cos(phibar)
Definition: GCOMIaq.hpp:128
void silent(const bool &silent)
Set silence flag.
Definition: GIntegral.hpp:251
Abstract spectral model base class interface definition.
int naxes(const int &axis) const
Return dimension of an image axis.
Definition: GFitsImage.cpp:344
GCOMInstChars m_ict
Instrument characteristics.
Definition: GCOMIaq.hpp:164
double prob_no_selfveto(const double &energy, const double &zenith) const
Return probability that photon was not self vetoed.
double m_e2min
Minimum D2 energy (MeV)
Definition: GCOMIaq.hpp:134
void eps(const double &eps)
Set relative precision.
Definition: GIntegral.hpp:214
std::string url(void) const
Return Uniform Resource Locator (URL)
Definition: GFilename.hpp:189
const int & npix(void) const
Return size of pixel array.
Definition: GFitsImage.hpp:136
double m_zenith
Zenith angle for location smearing (deg)
Definition: GCOMIaq.hpp:176
double position(const double &etrue) const
Return photo peak position.
double m_e2min
Minimum D2 energy (MeV)
Definition: GCOMIaq.hpp:172
void free_members(void)
Delete class members.
Definition: GCOMIaq.cpp:616
const GCOMD1Response & m_response_d1
Reference to D1 module response.
Definition: GCOMIaq.hpp:124
double index(void) const
Return power law index.
void weight_iaq(const double &energy)
Weight IAQ matrix.
Definition: GCOMIaq.cpp:1056
GFitsImageFloat m_iaq
Response.
Definition: GCOMIaq.hpp:160
double emax(const double &etrue) const
Return maximum energy.
std::string print(const GChatter &chatter=NORMAL) const
Print COMPTEL instrument response representation information.
Definition: GCOMIaq.cpp:469
GVector pow(const GVector &vector, const double &power)
Computes tanh of vector elements.
Definition: GVector.cpp:1422
GVector sin(const GVector &vector)
Computes sine of vector elements.
Definition: GVector.cpp:1316
Energy boundaries class interface definition.
GFitsHDU * append(const GFitsHDU &hdu)
Append HDU to FITS file.
Definition: GFits.cpp:678
void init_members(void)
Initialise class members.
Definition: GCOMIaq.cpp:552
double index(void) const
Return power law index.
void clear(void)
Clear instance.
GVector exp(const GVector &vector)
Computes exponential of vector elements.
Definition: GVector.cpp:1232
void remove_cards(void)
Remove any extra header cards.
Definition: GCOMIaq.cpp:644
void clear(void)
Clear instance.
double emin(const double &etrue) const
Return minimum energy.
~GCOMIaq(void)
Destructor.
Definition: GCOMIaq.cpp:162
const GCOMD2Response & m_response_d2
Reference to D2 module response.
Definition: GCOMIaq.hpp:125
const GEnergy & emax(void) const
Return maximum energy of all intervals.
Definition: GEbounds.hpp:199
virtual double flux(const GEnergy &emin, const GEnergy &emax) const =0
const double rad2deg
Definition: GMath.hpp:44
double trans_V1(const double &energy) const
Return V1 veto dome transmission.
double m_etmax
Maximum total energy (MeV)
Definition: GCOMIaq.hpp:131
std::string parformat(const std::string &s, const int &indent=0)
Convert string in parameter format.
Definition: GTools.cpp:1143
void copy_members(const GCOMIaq &iaq)
Copy class members.
Definition: GCOMIaq.cpp:585
void append(const double &node)
Append one node to array.
Definition: GNodeArray.cpp:351
Integration class interface definition.
GFitsHeaderCard & card(const int &cardno)
Return header card.
Definition: GFitsHDU.hpp:259
Energy flux normalized power law spectral model class.
double trans_V23(const double &energy, const double &phigeo) const
Return V2+V3 veto dome transmission.
void clear(void)
Clear instance.
Definition: GCOMIaq.cpp:218
double klein_nishina_integral(const double &v, const double &a)
Computes Klein-Nishina integral.
Definition: GCOMIaq.cpp:1023
Filename class interface definition.
double multi_scatter(const double &energy, const double &phigeo) const
Return multi-scatter correction.
Class that handles energies in a unit independent way.
Definition: GEnergy.hpp:48
std::string str(const unsigned short int &value)
Convert unsigned short integer value into string.
Definition: GTools.cpp:489