GammaLib  2.1.0.dev
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
GCOMInstChars.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  * GCOMInstChars.cpp - COMPTEL Instrument Characteristics class *
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 GCOMInstChars.cpp
23  * @brief COMPTEL Instrument Characteristics class implementation
24  * @author Juergen Knoedlseder
25  */
26 
27 /* __ Includes ___________________________________________________________ */
28 #ifdef HAVE_CONFIG_H
29 #include <config.h>
30 #endif
31 #include <algorithm>
32 #include "GMath.hpp"
33 #include "GEnergy.hpp"
34 #include "GCOMInstChars.hpp"
35 #include "GCOMSupport.hpp"
36 
37 /* __ Method name definitions ____________________________________________ */
38 
39 /* __ Macros _____________________________________________________________ */
40 
41 /* __ Coding definitions _________________________________________________ */
42 #define USE_ICT_MEAN_FREE_PATH
43 
44 /* __ Debug definitions __________________________________________________ */
45 //#define G_DEBUG_READ_SELFVETO
46 
47 /* __ Constants __________________________________________________________ */
48 
49 
50 /*==========================================================================
51  = =
52  = Constructors/destructors =
53  = =
54  ==========================================================================*/
55 
56 /***********************************************************************//**
57  * @brief Void constructor
58  *
59  * Creates empty COMPTEL instrument characteristics.
60  ***************************************************************************/
62 {
63  // Initialise members
64  init_members();
65 
66  // Return
67  return;
68 }
69 
70 
71 /***********************************************************************//**
72  * @brief Copy constructor
73  *
74  * @param[in] ict COMPTEL instrument characteristics.
75  **************************************************************************/
77 {
78  // Initialise members
79  init_members();
80 
81  // Copy members
82  copy_members(ict);
83 
84  // Return
85  return;
86 }
87 
88 
89 /***********************************************************************//**
90  * @brief Response constructor
91  *
92  * @param[in] caldb Calibration database.
93  * @param[in] ictname ICT response name.
94  *
95  * Create COMPTEL instrument characteristics by loading an ICT file from a
96  * calibration database.
97  ***************************************************************************/
98 GCOMInstChars::GCOMInstChars(const GCaldb& caldb, const std::string& ictname)
99 {
100  // Initialise members
101  init_members();
102 
103  // Set calibration database
104  this->caldb(caldb);
105 
106  // Load instrument characteristics
107  this->load(ictname);
108 
109  // Return
110  return;
111 }
112 
113 
114 /***********************************************************************//**
115  * @brief Destructor
116  *
117  * Destroys instance of COMPTEL instrument characteristics.
118  ***************************************************************************/
120 {
121  // Free members
122  free_members();
123 
124  // Return
125  return;
126 }
127 
128 
129 /*==========================================================================
130  = =
131  = Operators =
132  = =
133  ==========================================================================*/
134 
135 /***********************************************************************//**
136  * @brief Assignment operator
137  *
138  * @param[in] ict COMPTEL instrument characteristics.
139  * @return COMPTEL instrument characteristics.
140  ***************************************************************************/
142 {
143  // Execute only if object is not identical
144  if (this != &ict) {
145 
146  // Free members
147  free_members();
148 
149  // Initialise members
150  init_members();
151 
152  // Copy members
153  copy_members(ict);
154 
155  } // endif: object was not identical
156 
157  // Return this object
158  return *this;
159 }
160 
161 
162 /*==========================================================================
163  = =
164  = Public methods =
165  = =
166  ==========================================================================*/
167 
168 /***********************************************************************//**
169  * @brief Clear instance
170  *
171  * Clears COMPTEL instrument characteristics object by resetting all members
172  * to an initial state. Any information that was present in the object before
173  * will be lost.
174  ***************************************************************************/
176 {
177  // Free class members
178  free_members();
179 
180  // Initialise members
181  init_members();
182 
183  // Return
184  return;
185 }
186 
187 
188 /***********************************************************************//**
189  * @brief Clone instance
190  *
191  * @return Pointer to deep copy of COMPTEL instrument characteristics.
192  ***************************************************************************/
194 {
195  return new GCOMInstChars(*this);
196 }
197 
198 
199 /***********************************************************************//**
200  * @brief Load COMPTEL instrument characteristics.
201  *
202  * @param[in] ictname COMPTEL instrument characteristics.
203  *
204  * Loads the COMPTEL instrument characteristics with specified name
205  * @p ictname. The method first searchs for an appropriate response in the
206  * calibration database. If no appropriate response is found, the method
207  * takes the database root path and response name to build the full path to
208  * the response file, and tries to load the response from these paths.
209  ***************************************************************************/
210 void GCOMInstChars::load(const std::string& ictname)
211 {
212  // Clear instance but conserve calibration database
213  GCaldb caldb = m_caldb;
214  clear();
215  m_caldb = caldb;
216 
217  // First attempt reading the response using the GCaldb interface
218  GFilename filename = m_caldb.filename("","","ICT","","",ictname);
219 
220  // If filename is empty then build filename from CALDB root path and
221  // response name
222  if (filename.is_empty()) {
223  filename = gammalib::filepath(m_caldb.rootdir(), ictname);
224  if (!filename.exists()) {
225  GFilename testname = filename + ".fits";
226  if (testname.exists()) {
227  filename = testname;
228  }
229  }
230  }
231 
232  // Open FITS file
233  GFits fits(filename);
234 
235  // Get ICT tables
236  const GFitsTable& d1inter = *fits.table("D1INTER");
237  const GFitsTable& d1dens = *fits.table("D1DENS");
238  const GFitsTable& d1pos = *fits.table("D1POS");
239  const GFitsTable& d1rad = *fits.table("D1RAD");
240  const GFitsTable& d1thick = *fits.table("D1THICK");
241  const GFitsTable& d2inter = *fits.table("D2INTER");
242  const GFitsTable& d2dens = *fits.table("D2DENS");
243  const GFitsTable& d2pos = *fits.table("D2POS");
244  const GFitsTable& d2rad = *fits.table("D2RAD");
245  const GFitsTable& d2thick = *fits.table("D2THICK");
246  const GFitsTable& thbar = *fits.table("THBAR");
247  const GFitsTable& delz = *fits.table("DELZ");
248  const GFitsTable& alu = *fits.table("ALU");
249  const GFitsTable& aldens = *fits.table("ALDENS");
250  const GFitsTable& althick = *fits.table("ALTHICK");
251  const GFitsTable& aboved1 = *fits.table("ABOVED1");
252  const GFitsTable& abdens = *fits.table("ABDENS");
253  const GFitsTable& abthick = *fits.table("ABTHICK");
254  const GFitsTable& vetodome = *fits.table("VETODOME");
255  const GFitsTable& vetodens = *fits.table("VETODENS");
256  const GFitsTable& v1thick = *fits.table("V1THICK");
257  const GFitsTable& vthick = *fits.table("VTHICK");
258  const GFitsTable& selfveto = *fits.table("SELFVETO");
259  const GFitsTable& d1multi = *fits.table("D1MULTI");
260  const GFitsTable& d2multi = *fits.table("D2MULTI");
261 
262  // Read information from ICT tables
270  read_pos(d1pos, m_d1pos_x, m_d1pos_y);
271  read_pos(d2pos, m_d2pos_x, m_d2pos_y);
272  read_selfveto(selfveto);
273 
274  // Read constants
275  m_d1dens = d1dens["DENSITY"]->real(0);
276  m_d1rad = d1rad["RADIUS"]->real(0);
277  m_d1thick = d1thick["THICKNESS"]->real(0);
278  m_d2dens = d2dens["DENSITY"]->real(0);
279  m_d2rad = d2rad["RADIUS"]->real(0);
280  m_d2thick = d2thick["THICKNESS"]->real(0);
281  m_thbar = thbar["ANGLE"]->real(0);
282  m_delz = delz["DISTANCE"]->real(0);
283  m_aldens = aldens["DENSITY"]->real(0);
284  m_althick = althick["THICKNESS"]->real(0);
285  m_abdens = abdens["DENSITY"]->real(0);
286  m_abthick = abthick["THICKNESS"]->real(0);
287  m_vetodens = vetodens["DENSITY"]->real(0);
288  m_v1thick = v1thick["THICKNESS"]->real(0);
289  m_vthick = vthick["THICKNESS"]->real(0);
290 
291  // Close ICT FITS file
292  fits.close();
293 
294  // Return
295  return;
296 }
297 
298 
299 /***********************************************************************//**
300  * @brief Return transmission above D1
301  *
302  * @param[in] energy Input photon energy (MeV).
303  * @return Transmission above D1.
304  *
305  * Computes the transmission of material above D1 as function of energy
306  * using
307  *
308  * \f[
309  * T(E) = \exp \left(-\mu(E) l \right)
310  * \f]
311  *
312  * where
313  * \f$\mu(E)\f$ is the energy-dependent interaction coefficient for material
314  * above D1 in units of \f$cm^{-1}\f$ that is interpolated using a log-log
315  * interpolation of the ICT table values, and
316  * \f$l\f$ is the thickness of the material above D1 in \f$cm\f$.
317  ***************************************************************************/
318 double GCOMInstChars::trans_D1(const double& energy) const
319 {
320  // Initialise transmission
321  double transmission = 1.0;
322 
323  // Continue only if there are energies
324  if (m_aboved1_energies.size() > 0) {
325 
326  // Get log of energy
327  double logE = std::log(energy);
328 
329  // Get attenuation coefficient
330  double logc = m_aboved1_energies.interpolate(logE, m_aboved1_coeffs);
331  double coeff = std::exp(logc) * m_abthick;
332 
333  // Compute transmission
334  transmission = std::exp(-coeff);
335 
336  }
337 
338  // Return transmission
339  return transmission;
340 }
341 
342 
343 /***********************************************************************//**
344  * @brief Return V1 veto dome transmission
345  *
346  * @param[in] energy Input photon energy (MeV).
347  * @return V1 veto dome transmission.
348  *
349  * Computes the V1 veto dome transmission as function of energy
350  * using
351  *
352  * \f[
353  * T(E) = \exp \left(-\mu(E) l \right)
354  * \f]
355  *
356  * where
357  * \f$\mu(E)\f$ is the energy-dependent interaction coefficient of the V1
358  * veto dome in units of \f$cm^{-1}\f$ that is interpolated using a log-log
359  * interpolation of the ICT table values, and
360  * \f$l\f$ is the thickness of the V1 veto dome in \f$cm\f$.
361  ***************************************************************************/
362 double GCOMInstChars::trans_V1(const double& energy) const
363 {
364  // Initialise transmission
365  double transmission = 1.0;
366 
367  // Continue only if there are energies
368  if (m_veto_energies.size() > 0) {
369 
370  // Get log of energy
371  double logE = std::log(energy);
372 
373  // Get attenuation coefficient
374  double logc = m_veto_energies.interpolate(logE, m_veto_coeffs);
375  double coeff = std::exp(logc) * m_v1thick;
376 
377  // Compute transmission
378  transmission = std::exp(-coeff);
379 
380  }
381 
382  // Return transmission
383  return transmission;
384 }
385 
386 
387 /***********************************************************************//**
388  * @brief Return D1 interaction probability
389  *
390  * @param[in] energy Input photon energy (MeV).
391  * @return D1 interaction probability.
392  *
393  * Computes the D1 interaction probability as function of energy using
394  *
395  * \f[
396  * P(E) = 1 - \exp \left(-\mu_m(E) \, l \right)
397  * \f]
398  *
399  * where
400  * \f$-\mu(E)\f$ is the energy-dependent D1 attenuation coefficient in units
401  * of \f$1/cm\f$ that is interpolated using a log-log interpolation of the
402  * ICT table values, and \f$l\f$ is the thickness of the D1 module in
403  * \f$cm\f$.
404  ***************************************************************************/
405 double GCOMInstChars::prob_D1inter(const double& energy) const
406 {
407  // Initialise probability
408  double prob = 0.0;
409 
410  // Continue only if there are energies
411  if (m_d1inter_energies.size() > 0) {
412 
413  // Get log of energy
414  double logE = std::log(energy);
415 
416  // Get interaction coefficient
417  double logc = m_d1inter_energies.interpolate(logE, m_d1inter_coeffs);
418  double coeff = std::exp(logc) * m_d1thick;
419 
420  // Compute interaction probability
421  prob = 1.0 - std::exp(-coeff);
422 
423  }
424 
425  // Return probability
426  return prob;
427 }
428 
429 
430 /***********************************************************************//**
431  * @brief Return probability that no multihit occured
432  *
433  * @param[in] energy Input photon energy (MeV).
434  * @return Probability that no multihit occured.
435  *
436  * Returns the probability that there is no multihit. The probability is
437  * directly interpolated using a log-log interpolation from the D1 values
438  * that are given in the ICT table. The D2 values are not used.
439  ***************************************************************************/
440 double GCOMInstChars::prob_no_multihit(const double& energy) const
441 {
442  // Initialise probability
443  double prob = 1.0;
444 
445  // Continue only if there are energies
446  if (m_d1multi_energies.size() > 0) {
447 
448  // Get log of energy
449  double logE = std::log(energy);
450 
451  // Compute probability that there is no multihit
453 
454  // Make sure that probability does not exceed 1
455  if (prob > 1.0) {
456  prob = 1.0;
457  }
458 
459  }
460 
461  // Return probability
462  return prob;
463 }
464 
465 
466 /***********************************************************************//**
467  * @brief Return probability that photon was not self vetoed
468  *
469  * @param[in] energy Input photon energy (MeV).
470  * @param[in] zenith Zenith angle (deg).
471  * @return Probability that photon was not self vetoed.
472  *
473  * Returns the probability that the photon was not self vetoed. The
474  * probability is directly bi-linearly interpolated from the values that
475  * are given in the ICT table.
476  ***************************************************************************/
477 double GCOMInstChars::prob_no_selfveto(const double& energy, const double& zenith) const
478 {
479  // Initialise probability
480  double prob = 1.0;
481 
482  // Get number of energies and zenith angles in array
483  int n_energies = m_selfveto_energies.size();
484  int n_zeniths = m_selfveto_zeniths.size();
485 
486  // Continue only if there are energies and zenith angles
487  if ((n_energies > 0) && (n_zeniths > 0)) {
488 
489  // Set energy interpolation
491 
492  // Set zenith angle interpolation
494 
495  // Set array indices for bi-linear interpolation
496  int inx_zenith_left = m_selfveto_zeniths.inx_left() * n_energies;
497  int inx_zenith_right = m_selfveto_zeniths.inx_right() * n_energies;
498  int inx1 = m_selfveto_energies.inx_left() + inx_zenith_left;
499  int inx2 = m_selfveto_energies.inx_left() + inx_zenith_right;
500  int inx3 = m_selfveto_energies.inx_right() + inx_zenith_left;
501  int inx4 = m_selfveto_energies.inx_right() + inx_zenith_right;
502 
503  // Set weighting factors for bi-linear interpolation
508 
509  // Perform bi-linear interpolation
510  prob = wgt1 * m_selfveto_coeffs[inx1] +
511  wgt2 * m_selfveto_coeffs[inx2] +
512  wgt3 * m_selfveto_coeffs[inx3] +
513  wgt4 * m_selfveto_coeffs[inx4];
514 
515  // Make sure that probability does is within [0,1]
516  if (prob < 0.0) {
517  prob = 0.0;
518  }
519  else if (prob > 1.0) {
520  prob = 1.0;
521  }
522 
523  // And now convert into a no self veto probability
524  prob = 1.0 - prob;
525 
526  }
527 
528  // Return probability
529  return prob;
530 }
531 
532 
533 /***********************************************************************//**
534  * @brief Return transmission of material between D1 and D2
535  *
536  * @param[in] energy Input photon energy (MeV).
537  * @param[in] phigeo Geometrical scatter angle (deg).
538  * @return Transmission of material between D1 and D2.
539  *
540  * Computes the transmission of material between D1 and D2 as function of
541  * energy using
542  *
543  * \f[
544  * T(E) = \exp \left(\frac{-\mu(E_2) \, l \right)
545  * \f]
546  *
547  * with
548  *
549  * \f[
550  * E_2 = \frac{E_{\gamma}}
551  * {(1 - \cos \varphi_{\rm geo}) \frac{E_{\gamma}}{m_e c^2} + 1}
552  * \f]
553  *
554  * where
555  * \f$E_{\gamma}\f$ is the input photon @p energy in MeV,
556  * \f$\varphi_{\rm geo}\f$ is the geometrical scatter angle,
557  * \f$\mu(E_2)\f$ is the energy-dependent interaction coefficient of the
558  * material between D1 and D2 in units of \f$cm^{-1}\f$ that is interpolated
559  * using a log-log interpolation of the ICT table values, and
560  * \f$l\f$ is the thickness of the material between D1 and D2 in \f$cm\f$.
561  ***************************************************************************/
562 double GCOMInstChars::trans_D2(const double& energy, const double& phigeo) const
563 {
564  // Initialise transmission
565  double transmission = 1.0;
566 
567  // Continue only if there are energies
568  if (m_alu_energies.size() > 0) {
569 
570  // Compute log of D2 energy deposit
571  double logE2 = std::log(gammalib::com_energy2(energy, phigeo));
572 
573  // Get attenuation coefficient
574  double logc = m_alu_energies.interpolate(logE2, m_alu_coeffs);
575  double coeff = std::exp(logc) * m_althick;
576 
577  // Compute transmission
578  transmission = std::exp(-coeff);
579 
580  }
581 
582  // Return transmission
583  return transmission;
584 }
585 
586 
587 /***********************************************************************//**
588  * @brief Return V2+V3 veto dome transmission
589  *
590  * @param[in] energy Input photon energy (MeV).
591  * @param[in] phigeo Geometrical scatter angle (deg).
592  * @return V2+V3 veto dome transmission.
593  *
594  * Computes the V2+V3 veto dome transmission as function of energy
595  * using
596  *
597  * \f[
598  * T(E) = \exp \left(-\mu(E) \, l \right)
599  * \f]
600  *
601  * \f[
602  * E_2 = \frac{E_{\gamma}}
603  * {(1 - \cos \varphi_{\rm geo}) \frac{E_{\gamma}}{m_e c^2} + 1}
604  * \f]
605  *
606  * where
607  * \f$E_{\gamma}\f$ is the input photon @p energy in MeV,
608  * \f$\varphi_{\rm geo}\f$ is the geometrical scatter angle,
609  * \f$\mu(E_2)\f$ is the energy-dependent interaction coefficient of the V2
610  * and V3 veto domes in units of \f$cm^{-1}\f$ that is interpolated using a
611  * log-log interpolation of the ICT table values, and \f$l\f$ is the thickness
612  * of the V2+V3 veto domes in \f$cm\f$.
613  ***************************************************************************/
614 double GCOMInstChars::trans_V23(const double& energy, const double& phigeo) const
615 {
616  // Initialise transmission
617  double transmission = 1.0;
618 
619  // Continue only if there are energies
620  if (m_veto_energies.size() > 0) {
621 
622  // Compute log of D2 energy deposit
623  double logE2 = std::log(gammalib::com_energy2(energy, phigeo));
624 
625  // Get attenuation coefficient
626  double logc = m_veto_energies.interpolate(logE2, m_veto_coeffs);
627  double coeff = std::exp(logc) * m_vthick;
628 
629  // Compute transmission
630  transmission = std::exp(-coeff);
631 
632  }
633 
634  // Return transmission
635  return transmission;
636 }
637 
638 
639 /***********************************************************************//**
640  * @brief Return D2 interaction probability
641  *
642  * @param[in] energy Input photon energy (MeV).
643  * @param[in] phigeo Geometrical scatter angle (deg).
644  * @return D2 interaction probability.
645  *
646  * Computes the D2 interaction probability as function of energy using
647  *
648  * \f[
649  * P(E) = 1 - \exp \left(-\mu_m(E_2) \, l \right)
650  * \f]
651  *
652  * with
653  *
654  * \f[
655  * E_2 = \frac{E_{\gamma}}
656  * {(1 - \cos \varphi_{\rm geo}) \frac{E_{\gamma}}{m_e c^2} + 1}
657  * \f]
658  *
659  * where
660  * \f$E_{\gamma}\f$ is the input photon @p energy in MeV,
661  * \f$\varphi_{\rm geo}\f$ is the geometrical scatter angle,
662  * \f$\mu(E_2)\f$ is the energy-dependent D2 attenuation coefficient in units
663  * of \f$cm^{-1}\f$ that is interpolated using a log-log interpolation of the
664  * ICT table values, and \f$l\f$ is the thickness of the D2 module in
665  * \f$cm\f$.
666  ***************************************************************************/
667 double GCOMInstChars::prob_D2inter(const double& energy, const double& phigeo) const
668 {
669  // Initialise probability
670  double prob = 0.0;
671 
672  // Continue only if there are energies
673  if (m_d2inter_energies.size() > 0) {
674 
675  // Compute log of D2 energy deposit
676  double logE2 = std::log(gammalib::com_energy2(energy, phigeo));
677 
678  // Get interaction coefficient
679  double logc = m_d2inter_energies.interpolate(logE2, m_d2inter_coeffs);
680  double coeff = std::exp(logc) * m_d2thick;
681 
682  // Compute interaction probability
683  prob = 1.0 - std::exp(-coeff);
684 
685  }
686 
687  // Return probability
688  return prob;
689 }
690 
691 
692 /***********************************************************************//**
693  * @brief Return multi-scatter correction
694  *
695  * @param[in] energy Input photon energy (MeV).
696  * @param[in] phigeo Geometrical scatter angle (deg).
697  * @return Correction factor due to multi-scatter.
698  *
699  * Returns the fraction of photons that have undergone a single scatter
700  * and which leave the D1 module unattenuated (no second interaction when
701  * traversing the remaining path inside the same module).
702  *
703  * RES already calculates the fraction of photons which undergo a single
704  * scatter for VERTICALLY indicent photons, based on the Klein-Nishina
705  * cross-section and the composition of NE213A. Therefore the above mentioned
706  * transmission can be applied as a multiplicative correction per phigeo.
707  * However, in order to calculate that correction, an integral over the
708  * module must be performed, which properly takes into account the radiative
709  * transfer inside the module at all R and z. We again use the assumption
710  * of vertical photon incidence, which simplifies the calculation. The
711  * integral is done over the location of the first interaction.
712  *
713  * Note that the transmission calculated is conservative : in reality it will
714  * be a bit higher because of a fraction of the photons which undergo a
715  * second scatter have a final scatter angle after escaping the module within
716  * the phigeo-range of the PSF. These events will, however, mainly be located
717  * at large phibar (large D1E deposit).
718  *
719  * The code implementation is based on the COMPASS RESPSIT2 function
720  * MULTIS.F (release ?, 27-NOV-92).
721  ***************************************************************************/
722 double GCOMInstChars::multi_scatter(const double& energy, const double& phigeo) const
723 {
724  // Set integration step size
725  const int nz = 10; //!< Number of steps in z-direction
726  const int nphi = 20; //!< Number of azimuthal steps
727 
728  // Compute stuff
729  double alpha = energy / gammalib::mec2;
730  double sth = std::sin(phigeo * gammalib::deg2rad);
731  double cth = std::cos(phigeo * gammalib::deg2rad);
732 
733  // Get the attenuation coefficient for NE213A
734  double mu = 1.0 / ne213a_mfpath(energy);
735 
736  // Compute the energy after scattering and the corresponding attenuation
737  double e_scattered = energy / (1.0 + alpha * (1.0 - cth));
738  double mu_scattered = 1.0 / ne213a_mfpath(e_scattered);
739 
740  // Compute the full vertical optical depth of the module
741  double tau0 = mu * m_d1thick;
742 
743  // Compute the fraction of photons that are interacting
744  double total_interactions = 1.0 - std::exp(-tau0);
745 
746  // Perform integration per rho-value (symmetry!) over
747  // (1) z (the geometrical depth in the module)
748  // (2) phi (azimuth of scatter)
749  // at geometrical scatter angle phigeo.
750  double deltaz = m_d1thick / double(nz);
751  double deltau = deltaz * mu;
752  double dltphi = gammalib::pi / double(nphi);
753 
754  // Initialise integration loop. The binning in radius is based on
755  // constant surface per cylinder
756  double rho = 1.0;
757  double delrho = 2.0 * rho;
758  double rholow = rho - 0.5 * delrho; // Should be 0
759  double rhoupp = rho + 0.5 * delrho; // Should be 2
760  double surface = 2.0 * delrho * rho; // Should be 4
761 
762  // Compute the integration normalisation value
763  double kappa = deltau / double(nphi);
764 
765  // Integration loop over the module radius
766  double contribution_rho = 0.0;
767  double total_surface = surface;
768  int klast = 0; // Will be >0 if the loop should be exited
769  for (int krho = 0; krho < 100; ++krho) {
770 
771  // If we have reached the end then exit the loop now
772  if (klast > 0) {
773  break;
774  }
775 
776  // Determine radius limits. If D1 module radius is reached or
777  // exceeded, limit the upper rho value to the D1 module radius,
778  // re-compute the surface, and signal to exit the loop in the
779  // next round.
780  if (krho > 0) {
781  rholow = rhoupp;
782  rhoupp = std::sqrt(surface + rholow*rholow);
783  rho = 0.5 * (rhoupp + rholow);
784  if (rhoupp > m_d1rad) {
785  rhoupp = m_d1rad;
786  rho = 0.5 * (rhoupp + rholow);
787  surface = (rhoupp*rhoupp - rholow*rholow);
788  klast = krho;
789  }
790  total_surface += surface;
791  }
792 
793  // Initialise azimuthal results
794  std::vector<double> r(nphi, 0.0);
795 
796  // Compute remaining radius as function of azimuth angle if the
797  // first interaction was at radius rho.
798  for (int lphi = 0; lphi < nphi; ++lphi) {
799  double phi = dltphi * (lphi + 0.5);
800  double term = rho * std::sin(phi);
801  r[lphi] = -rho * std::cos(phi) +
802  std::sqrt(m_d1rad*m_d1rad - term*term);
803  }
804 
805  // Perform integration over depth
806  double contribution_z = 0.0;
807  for (int kz = 0; kz < nz; ++kz) {
808 
809  // Calculate depth
810  double z = (kz + 0.5) * deltaz;
811  double tau = z * mu;
812 
813  // Compute the remaining length in the module after the first
814  // interaction. Test for foward/backward scattering although
815  // this should not be relevant for any reasonable phigeo values.
816  double length0 = 1000.0;
817  if (sth < 0.99) { // True for all reasonable phigeo's
818  if (cth < 0.0) { // False for all reasonable phigeo's
819  length0 = z/std::abs(cth);
820  }
821  else {
822  length0 = (m_d1thick-z)/cth; // All reasonable phigeo's
823  }
824  }
825 
826  // Perform integration over azimuth
827  double contribution_phi = 0.0;
828  for (int kphi = 0; kphi < nphi; ++kphi) {
829 
830  // Compute the actual remaining length for a given azimuth
831  // angle phi. Limit the length to the remaining radius.
832  double length = length0;
833  if (length * sth > r[kphi]) {
834  length = r[kphi] / sth;
835  }
836 
837  // Now compute the probability that the photon is not
838  // absorbed during the remaining path through the detector.
839  contribution_phi += std::exp(-length * mu_scattered);
840 
841  } // endfor: looped over azimuth
842 
843  // Average contribution must later be divided by nphi for
844  // phi-pixels and multiplied by deltau; this is done by
845  // multiplying with kappa. Do this at end, to save computation
846  contribution_z += contribution_phi * std::exp(-tau);
847 
848  } // endfor: looped over depth
849 
850  // Add the average contribution of radius rho
851  contribution_rho += contribution_z * kappa * surface;
852 
853  } // endfor: looped over radius
854 
855  // Compute average
856  double transmission = contribution_rho / (total_surface * total_interactions);
857 
858  // Return transmission
859  return transmission;
860 }
861 
862 
863 /***********************************************************************//**
864  * @brief Return PSD correction
865  *
866  * @param[in] energy Input photon energy (MeV).
867  * @param[in] phigeo Geometrical scatter angle (deg).
868  * @return Correction factor due to PSD selection.
869  *
870  * Returns the D1 energy dependent PSD correction as described in
871  * COM-RP-ROL-DRG-016 and COM-RP-ROL-DRG-35. It applies to a standard PSD
872  * selection of 0-110.
873  *
874  * The acceptance probability fit formula
875  *
876  * \f[
877  * P_{\rm acc} = 1 - \frac{1}{a_1 \times E_1^{a_2} + 1}
878  * \f]
879  *
880  * where \f$a_1=1727.9\f$, \f$a_2=2.53\f$ and \f$E_1\f$ is the D1 energy
881  * deposit in MeV. Coefficients are taken from Boron calibration data
882  * (ZA=1.14) to remain consistent with Rob van Dijk's SIMPSF corrections.
883  *
884  * The code implementation is based on the COMPASS RESPSIT2 function
885  * PSDACP.F (release 1.0, 11-DEC-92).
886  ***************************************************************************/
887 double GCOMInstChars::psd_correction(const double& energy, const double& phigeo) const
888 {
889  // Set constants
890  const double a1 = 1727.9;
891  const double a2 = 2.530;
892 
893  // Compute D1 energy deposit
894  double e1 = gammalib::com_energy1(energy, phigeo);
895 
896  // Original COMPASS code
897  double psdacp = 1.0 - (1.0 / (a1 * std::pow(e1, a2) + 1.0));
898 
899  // Return fraction
900  return psdacp;
901 }
902 
903 
904 /***********************************************************************//**
905  * @brief Print COMPTEL instrument characteristics
906  *
907  * @param[in] chatter Chattiness.
908  * @return String containing COMPTEL instrument characteristics.
909  ***************************************************************************/
910 std::string GCOMInstChars::print(const GChatter& chatter) const
911 {
912  // Initialise result string
913  std::string result;
914 
915  // Continue only if chatter is not silent
916  if (chatter != SILENT) {
917 
918  // Append header
919  result.append("=== GCOMInstChars ===");
920 
921  // Append D1INTER information
922  result.append("\n"+gammalib::parformat("D1 interaction coeffs."));
923  if (m_d1inter_energies.size() > 0) {
924  int last = m_d1inter_energies.size()-1;
925  result.append(gammalib::str(std::exp(m_d1inter_energies[0]))+ " - ");
926  result.append(gammalib::str(std::exp(m_d1inter_energies[last]))+ " MeV");
927  result.append(" [");
929  result.append(" - ");
931  result.append("] cm^2/g");
932  }
933  else {
934  result.append("not defined");
935  }
936 
937  // Append D2INTER information
938  result.append("\n"+gammalib::parformat("D2 interaction coeffs."));
939  if (m_d2inter_energies.size() > 0) {
940  int last = m_d2inter_energies.size()-1;
941  result.append(gammalib::str(std::exp(m_d2inter_energies[0]))+ " - ");
942  result.append(gammalib::str(std::exp(m_d2inter_energies[last]))+ " MeV");
943  result.append(" [");
945  result.append(" - ");
947  result.append("] cm^2/g");
948  }
949  else {
950  result.append("not defined");
951  }
952 
953  // Append ALU information
954  result.append("\n"+gammalib::parformat("Al interaction coeffs."));
955  if (m_alu_energies.size() > 0) {
956  result.append(gammalib::str(std::exp(m_alu_energies[0]))+ " - ");
957  result.append(gammalib::str(std::exp(m_alu_energies[m_alu_energies.size()-1]))+ " MeV");
958  result.append(" [");
959  result.append(gammalib::str(std::exp(min_coeff(m_alu_coeffs))));
960  result.append(" - ");
961  result.append(gammalib::str(std::exp(max_coeff(m_alu_coeffs))));
962  result.append("] 1/cm");
963  }
964  else {
965  result.append("not defined");
966  }
967 
968  // Append ABOVED1 information
969  result.append("\n"+gammalib::parformat("Above D1 atten. coeffs."));
970  if (m_aboved1_energies.size() > 0) {
971  result.append(gammalib::str(std::exp(m_aboved1_energies[0]))+ " - ");
972  result.append(gammalib::str(std::exp(m_aboved1_energies[m_aboved1_energies.size()-1]))+ " MeV");
973  result.append(" [");
975  result.append(" - ");
977  result.append("] 1/cm");
978  }
979  else {
980  result.append("not defined");
981  }
982 
983  // Append VETODOME information
984  result.append("\n"+gammalib::parformat("Vetodome atten. coeffs."));
985  if (m_veto_energies.size() > 0) {
986  result.append(gammalib::str(std::exp(m_veto_energies[0]))+ " - ");
987  result.append(gammalib::str(std::exp(m_veto_energies[m_veto_energies.size()-1]))+ " MeV");
988  result.append(" [");
989  result.append(gammalib::str(std::exp(min_coeff(m_veto_coeffs))));
990  result.append(" - ");
991  result.append(gammalib::str(std::exp(max_coeff(m_veto_coeffs))));
992  result.append("] cm^2/g");
993  }
994  else {
995  result.append("not defined");
996  }
997 
998  // Append SELFVETO information
999  result.append("\n"+gammalib::parformat("Selfveto probabilities"));
1000  if (m_selfveto_energies.size() > 0) {
1001  result.append(gammalib::str(m_selfveto_energies[0])+ " - ");
1002  result.append(gammalib::str(m_selfveto_energies[m_selfveto_energies.size()-1])+ " MeV x ");
1003  if (m_selfveto_zeniths.size() > 0) {
1004  result.append(gammalib::str(m_selfveto_zeniths[0])+ " - ");
1005  result.append(gammalib::str(m_selfveto_zeniths[m_selfveto_zeniths.size()-1])+ " deg");
1006  }
1007  result.append(" [");
1008  result.append(gammalib::str(min_coeff(m_selfveto_coeffs)));
1009  result.append(" - ");
1010  result.append(gammalib::str(max_coeff(m_selfveto_coeffs)));
1011  result.append("]");
1012  }
1013  else {
1014  result.append("not defined");
1015  }
1016 
1017  // Append D1MULTI information
1018  result.append("\n"+gammalib::parformat("D1 multihit probabilities"));
1019  if (m_d1multi_energies.size() > 0) {
1020  result.append(gammalib::str(std::exp(m_d1multi_energies[0]))+ " - ");
1021  result.append(gammalib::str(std::exp(m_d1multi_energies[m_d1multi_energies.size()-1]))+ " MeV");
1022  result.append(" [");
1024  result.append(" - ");
1026  result.append("]");
1027  }
1028  else {
1029  result.append("not defined");
1030  }
1031 
1032  // Append D2MULTI information
1033  result.append("\n"+gammalib::parformat("D2 multihit probabilities"));
1034  if (m_d2multi_energies.size() > 0) {
1035  result.append(gammalib::str(std::exp(m_d2multi_energies[0]))+ " - ");
1036  result.append(gammalib::str(std::exp(m_d2multi_energies[m_d2multi_energies.size()-1]))+ " MeV");
1037  result.append(" [");
1039  result.append(" - ");
1041  result.append("]");
1042  }
1043  else {
1044  result.append("not defined");
1045  }
1046 
1047  // Append D1POS information
1048  result.append("\n"+gammalib::parformat("D1 positions"));
1049  result.append(gammalib::str(m_d1pos_x.size())+"x & ");
1050  result.append(gammalib::str(m_d1pos_y.size())+"y");
1051 
1052  // Append D2POS information
1053  result.append("\n"+gammalib::parformat("D2 positions"));
1054  result.append(gammalib::str(m_d2pos_x.size())+"x & ");
1055  result.append(gammalib::str(m_d2pos_y.size())+"y");
1056 
1057  // Append D1DENS information
1058  result.append("\n"+gammalib::parformat("D1 density"));
1059  result.append(gammalib::str(m_d1dens)+ " g/cm^-3");
1060 
1061  // Append D1RAD information
1062  result.append("\n"+gammalib::parformat("D1 radius"));
1063  result.append(gammalib::str(m_d1rad)+ " cm");
1064 
1065  // Append D1THICK information
1066  result.append("\n"+gammalib::parformat("D1 thickness"));
1067  result.append(gammalib::str(m_d1thick)+ " cm");
1068 
1069  // Append D2DENS information
1070  result.append("\n"+gammalib::parformat("D2 density"));
1071  result.append(gammalib::str(m_d2dens)+ " g/cm^-3");
1072 
1073  // Append D2RAD information
1074  result.append("\n"+gammalib::parformat("D2 radius"));
1075  result.append(gammalib::str(m_d2rad)+ " cm");
1076 
1077  // Append D2THICK information
1078  result.append("\n"+gammalib::parformat("D2 thickness"));
1079  result.append(gammalib::str(m_d2thick)+ " cm");
1080 
1081  // Append THBAR information
1082  result.append("\n"+gammalib::parformat("Average D2 incident angle"));
1083  result.append(gammalib::str(m_thbar)+ " deg");
1084 
1085  // Append DELZ information
1086  result.append("\n"+gammalib::parformat("Distance between D1 and D2"));
1087  result.append(gammalib::str(m_delz)+ " cm");
1088 
1089  // Append ALDENS information
1090  result.append("\n"+gammalib::parformat("Dens. of Al plate above D2"));
1091  result.append(gammalib::str(m_aldens)+ " g/cm^-3");
1092 
1093  // Append ALTHICK information
1094  result.append("\n"+gammalib::parformat("Thk. of Al plate above D2"));
1095  result.append(gammalib::str(m_althick)+ " cm");
1096 
1097  // Append ABDENS information
1098  result.append("\n"+gammalib::parformat("Density above D1"));
1099  result.append(gammalib::str(m_abdens)+ " g/cm^-3");
1100 
1101  // Append ABTHICK information
1102  result.append("\n"+gammalib::parformat("Thickness above D1"));
1103  result.append(gammalib::str(m_abthick)+ " cm");
1104 
1105  // Append VETODENS information
1106  result.append("\n"+gammalib::parformat("Density of veto domes"));
1107  result.append(gammalib::str(m_vetodens)+ " g/cm^-3");
1108 
1109  // Append V1THICK information
1110  result.append("\n"+gammalib::parformat("Thickness of V1"));
1111  result.append(gammalib::str(m_v1thick)+ " cm");
1112 
1113  // Append VTHICK information
1114  result.append("\n"+gammalib::parformat("Thickness of V2+V3"));
1115  result.append(gammalib::str(m_vthick)+ " cm");
1116 
1117  // Append calibration database
1118  result.append("\n"+m_caldb.print(chatter));
1119 
1120  // Append information
1121 
1122  } // endif: chatter was not silent
1123 
1124  // Return result
1125  return result;
1126 }
1127 
1128 
1129 /*==========================================================================
1130  = =
1131  = Private methods =
1132  = =
1133  ==========================================================================*/
1134 
1135 /***********************************************************************//**
1136  * @brief Initialise class members
1137  ***************************************************************************/
1139 {
1140  // Initialise members
1141  m_caldb.clear();
1143  m_d1inter_coeffs.clear();
1145  m_d2inter_coeffs.clear();
1147  m_alu_coeffs.clear();
1149  m_aboved1_coeffs.clear();
1151  m_veto_coeffs.clear();
1154  m_selfveto_coeffs.clear();
1156  m_d1multi_coeffs.clear();
1158  m_d2multi_coeffs.clear();
1159  m_d1pos_x.clear();
1160  m_d1pos_y.clear();
1161  m_d2pos_x.clear();
1162  m_d2pos_y.clear();
1163  m_d1dens = 0.0;
1164  m_d1rad = 0.0;
1165  m_d1thick = 0.0;
1166  m_d2dens = 0.0;
1167  m_d2rad = 0.0;
1168  m_d2thick = 0.0;
1169  m_thbar = 0.0;
1170  m_delz = 0.0;
1171  m_aldens = 0.0;
1172  m_althick = 0.0;
1173  m_abdens = 0.0;
1174  m_abthick = 0.0;
1175  m_vetodens = 0.0;
1176  m_v1thick = 0.0;
1177  m_vthick = 0.0;
1178 
1179  // Return
1180  return;
1181 }
1182 
1183 
1184 /***********************************************************************//**
1185  * @brief Copy class members
1186  *
1187  * @param[in] ict COMPTEL instrument characteristics.
1188  ***************************************************************************/
1190 {
1191  // Copy attributes
1192  m_caldb = ict.m_caldb;
1198  m_alu_coeffs = ict.m_alu_coeffs;
1210  m_d1pos_x = ict.m_d1pos_x;
1211  m_d1pos_y = ict.m_d1pos_y;
1212  m_d2pos_x = ict.m_d2pos_x;
1213  m_d2pos_y = ict.m_d2pos_y;
1214  m_d1dens = ict.m_d1dens;
1215  m_d1rad = ict.m_d1rad;
1216  m_d1thick = ict.m_d1thick;
1217  m_d2dens = ict.m_d2dens;
1218  m_d2rad = ict.m_d2rad;
1219  m_d2thick = ict.m_d2thick;
1220  m_thbar = ict.m_thbar;
1221  m_delz = ict.m_delz;
1222  m_aldens = ict.m_aldens;
1223  m_althick = ict.m_althick;
1224  m_abdens = ict.m_abdens;
1225  m_abthick = ict.m_abthick;
1226  m_vetodens = ict.m_vetodens;
1227  m_v1thick = ict.m_v1thick;
1228  m_vthick = ict.m_vthick;
1229 
1230  // Return
1231  return;
1232 }
1233 
1234 
1235 /***********************************************************************//**
1236  * @brief Delete class members
1237  ***************************************************************************/
1239 {
1240  // Return
1241  return;
1242 }
1243 
1244 
1245 /***********************************************************************//**
1246  * @brief Read energy dependent coefficients.
1247  *
1248  * @param[in] table FITS table.
1249  * @param[out] energies Energy node array.
1250  * @param[out] coeffs Coefficients.
1251  *
1252  * Read energy dependent coefficients from FITS table and store their natural
1253  * logarithm in the @p energies and @p coeffs vectors.
1254  ***************************************************************************/
1256  GNodeArray& energies,
1257  std::vector<double>& coeffs)
1258 {
1259  // Initialise energies and coefficients
1260  energies.clear();
1261  coeffs.clear();
1262 
1263  // Extract number of entries in table
1264  int num = table.nrows();
1265 
1266  // If there are entries then read them
1267  if (num > 0) {
1268 
1269  // By default use "COEFFICIENT" column for coefficients, but if
1270  // table contains a "PROBABILITY" column then use that column
1271  std::string coeff_name = "COEFFICIENT";
1272  if (table.contains("PROBABILITY")) {
1273  coeff_name = "PROBABILITY";
1274  }
1275 
1276  // Get column pointers
1277  const GFitsTableCol* ptr_energy = table["ENERGY"];
1278  const GFitsTableCol* ptr_coeff = table[coeff_name];
1279 
1280  // Copy data from table into vectors. Skip any non-positive values
1281  // since we store the logarithms for a log-log interpolation.
1282  for (int i = 0; i < num; ++i) {
1283  double energy = ptr_energy->real(i);
1284  double coeff = ptr_coeff->real(i);
1285  if ((energy > 0.0) && (coeff > 0.0)) {
1286  energies.append(std::log(energy));
1287  coeffs.push_back(std::log(coeff));
1288  }
1289  }
1290 
1291  } // endif: there were entries
1292 
1293  // Return
1294  return;
1295 }
1296 
1297 
1298 /***********************************************************************//**
1299  * @brief Read module positions.
1300  *
1301  * @param[in] table FITS table.
1302  * @param[out] x X-positions of module.
1303  * @param[out] y Y-positions of module.
1304  *
1305  * Read module positions from FITS table.
1306  ***************************************************************************/
1307 void GCOMInstChars::read_pos(const GFitsTable& table, std::vector<double>& x,
1308  std::vector<double>& y)
1309 {
1310  // Initialise positions
1311  x.clear();
1312  y.clear();
1313 
1314  // Extract number of entries in table
1315  int num = table.nrows();
1316 
1317  // If there are entries then read them
1318  if (num > 0) {
1319 
1320  // Get column pointers
1321  const GFitsTableCol* ptr_detnum = table["DETNUM"];
1322  const GFitsTableCol* ptr_x = table["X"];
1323  const GFitsTableCol* ptr_y = table["Y"];
1324 
1325  // Copy data from table into vectors
1326  for (int i = 0; i < num; ++i) {
1327  int detnum = ptr_detnum->integer(i) - 1;
1328  x.push_back(ptr_x->real(detnum));
1329  y.push_back(ptr_y->real(detnum));
1330  }
1331 
1332  } // endif: there were entries
1333 
1334  // Return
1335  return;
1336 }
1337 
1338 
1339 /***********************************************************************//**
1340  * @brief Read selfveto coefficients.
1341  *
1342  * @param[in] table FITS table.
1343  *
1344  * Read selfveto coefficients from FITS table. The selfveto coefficients
1345  * depend on energy and zenith angle, and the input data are not necessarily
1346  * regularly sampled. Therefore the method will form a regular grid from
1347  * the provided values and compute the interpolated values from that grid.
1348  ***************************************************************************/
1350 {
1351  // Initialise selfveto vectors
1354  m_selfveto_coeffs.clear();
1355 
1356  // Extract number of entries in table
1357  int num = table.nrows();
1358 
1359  // If there are entries then read them
1360  if (num > 0) {
1361 
1362  // Get column pointers
1363  const GFitsTableCol* ptr_energies = table["ENERGY"];
1364  const GFitsTableCol* ptr_zeniths = table["ZENITH"];
1365  const GFitsTableCol* ptr_coeffs = table["PROBABILITY"];
1366 
1367  // Initialise energies and zenith vectors for sorting before creating
1368  // node arrays
1369  std::vector<double> energies;
1370  std::vector<double> zeniths;
1371 
1372  // Determine all energies and zenith angles in table
1373  for (int i = 0; i < num; ++i) {
1374 
1375  // Add energy if it is not yet in array
1376  double energy = ptr_energies->real(i);
1377  bool add = true;
1378  for (int k = 0; k < energies.size(); ++k) {
1379  if (energies[k] == energy) {
1380  add = false;
1381  break;
1382  }
1383  }
1384  if (add) {
1385  energies.push_back(energy);
1386  }
1387 
1388  // Add zenith angle if it is not yet in array
1389  double zenith = ptr_zeniths->real(i);
1390  add = true;
1391  for (int k = 0; k < m_selfveto_zeniths.size(); ++k) {
1392  if (m_selfveto_zeniths[k] == zenith) {
1393  add = false;
1394  break;
1395  }
1396  }
1397  if (add) {
1398  zeniths.push_back(zenith);
1399  }
1400 
1401  } // endfor: collected all energies and zenith angles
1402 
1403  // Sort energies and zenith angles
1404  std::sort(energies.begin(), energies.end());
1405  std::sort(zeniths.begin(), zeniths.end());
1406 
1407  // Set node arrays
1408  m_selfveto_energies.nodes(energies);
1409  m_selfveto_zeniths.nodes(zeniths);
1410 
1411  // Get array size
1412  int neng = m_selfveto_energies.size();
1413  int nzenith = m_selfveto_zeniths.size();
1414 
1415  // Fill all coefficients with -1, meaning that there is no value
1416  m_selfveto_coeffs.assign(neng*nzenith, -1.0);
1417 
1418  // Fill all coefficients
1419  for (int i = 0; i < num; ++i) {
1420 
1421  // Find nearest energy
1422  int ieng = 0;
1423  double energy = ptr_energies->real(i);
1424  double delta = std::abs(energy - m_selfveto_energies[0]);
1425  for (int k = 1; k < neng; ++k) {
1426  double d = std::abs(energy - m_selfveto_energies[k]);
1427  if (d < delta) {
1428  delta = d;
1429  ieng = k;
1430  }
1431  }
1432 
1433  // Find nearest zenith angle
1434  int izenith = 0;
1435  double zenith = ptr_zeniths->real(i);
1436  delta = std::abs(zenith - m_selfveto_zeniths[0]);
1437  for (int k = 1; k < nzenith; ++k) {
1438  double d = std::abs(zenith - m_selfveto_zeniths[k]);
1439  if (d < delta) {
1440  delta = d;
1441  izenith = k;
1442  }
1443  }
1444 
1445  // Compute index
1446  int index = ieng + neng*izenith;
1447 
1448  // Set coefficient
1449  m_selfveto_coeffs[index] = ptr_coeffs->real(i);
1450 
1451  } // endfor: filled all coefficients
1452 
1453  // Now loop over all energies and fill the missing zenith angles
1454  for (int ieng = 0; ieng < neng; ++ieng) {
1455 
1456  // Debug: print header
1457  #if defined(G_DEBUG_READ_SELFVETO)
1458  std::cout << "Energy: " << m_selfveto_energies[ieng] << std::endl;
1459  std::cout << " Zenith angles:";
1460  #endif
1461 
1462  // Set up arrays for interpolation
1463  GNodeArray nodes;
1464  std::vector<double> values;
1465  for (int izenith = 0; izenith < nzenith; ++izenith) {
1466  int index = ieng + neng*izenith;
1467  if (m_selfveto_coeffs[index] > -0.5) {
1468  nodes.append(m_selfveto_zeniths[izenith]);
1469  values.push_back(m_selfveto_coeffs[index]);
1470  #if defined(G_DEBUG_READ_SELFVETO)
1471  std::cout << " " << m_selfveto_zeniths[izenith];
1472  std::cout << "(" << m_selfveto_coeffs[index] << ")";
1473  #endif
1474  }
1475  }
1476 
1477  // Debug: print header
1478  #if defined(G_DEBUG_READ_SELFVETO)
1479  std::cout << std::endl;
1480  std::cout << " Interpolated zenith angles:";
1481  #endif
1482 
1483  // Interpolate coefficients for zenith angles
1484  for (int izenith = 0; izenith < nzenith; ++izenith) {
1485  int index = ieng + neng*izenith;
1486  if (m_selfveto_coeffs[index] < -0.5) {
1487  double coeffs = nodes.interpolate(m_selfveto_zeniths[izenith], values);
1488  if (coeffs < 0.0) {
1489  coeffs = 0.0;
1490  }
1491  else if (coeffs > 1.0) {
1492  coeffs = 1.0;
1493  }
1494  m_selfveto_coeffs[index] = coeffs;
1495  #if defined(G_DEBUG_READ_SELFVETO)
1496  std::cout << " " << m_selfveto_zeniths[izenith];
1497  std::cout << "(" << m_selfveto_coeffs[index] << ")";
1498  #endif
1499  }
1500  }
1501 
1502  // Debug: print line feed
1503  #if defined(G_DEBUG_READ_SELFVETO)
1504  std::cout << std::endl;
1505  #endif
1506 
1507  } // endfor: looped over all energies
1508 
1509  } // endif: there were entries
1510 
1511  // Return
1512  return;
1513 }
1514 
1515 
1516 /***********************************************************************//**
1517  * @brief Return NE213A mean free path
1518  *
1519  * @param[in] energy Energy (MeV)
1520  * @return Mean free path (cm)
1521  *
1522  * The mean free path as a function of energy, linearly interpolated between
1523  * data-points. Table of mean free paths for NE213A taken from L. Kuiper
1524  * calculated in JOB ROL-SPDSD1-43. This includes (1) Compton scattering and
1525  * (2) Pair production.
1526  ***************************************************************************/
1527 double GCOMInstChars::ne213a_mfpath(const double& energy) const
1528 {
1529  #if defined(USE_ICT_MEAN_FREE_PATH)
1530  // Get log of energy
1531  double logE = std::log(energy);
1532 
1533  // Get log of interaction coefficient
1534  double logc = m_d1inter_energies.interpolate(logE, m_d1inter_coeffs);
1535 
1536  // Convert into mean free path
1537  double result = 1.0 / std::exp(logc);
1538 
1539  #else
1540  // NE213A mean free path values for energies from 1,2,...,30 MeV
1541  const double mfpath[] = {16.19, 23.21, 29.01, 34.04, 38.46,
1542  42.35, 45.64, 48.81, 51.53, 54.17,
1543  56.15, 58.09, 59.96, 61.74, 63.41,
1544  64.62, 65.80, 66.94, 68.04, 69.09,
1545  69.86, 70.61, 71.33, 72.03, 72.70,
1546  73.33, 73.93, 74.50, 75.03, 75.51};
1547 
1548  // Normalisation constant for low-energy extrapolation
1549  const double norm = mfpath[1] * kn_cross_section(2.0 / gammalib::mec2);
1550 
1551  // Initialise mfpath
1552  double result = 0.0;
1553 
1554  // Do a Klein-Nishina extrapolation for low energies
1555  if (energy < 2.0) {
1556  result = norm / kn_cross_section(energy / gammalib::mec2);
1557  }
1558 
1559  // ... otherwise do a linear interpolation
1560  else {
1561  int index = int(energy) - 1;
1562  if (index >= 29) {
1563  index = 28;
1564  }
1565  double slope = mfpath[index+1] - mfpath[index];
1566  result = mfpath[index] + slope * (energy - double(index + 1));
1567  }
1568  #endif
1569 
1570  // Return result
1571  return result;
1572 }
1573 
1574 
1575 /***********************************************************************//**
1576  * @brief Return integrated Klein-Nishina cross section
1577  *
1578  * @param[in] k \f$E/ m_e c^2\f$
1579  * @return Integrated Klein-Nishina cross section
1580  *
1581  * Computes
1582  *
1583  * \f[
1584  * \sigma_{\rm KN}(k) = \frac{1+k}{k^2}
1585  * \left[
1586  * \frac{2(1+k)}{1+2k} - \frac{\ln(1+2k)}{k}
1587  * \right] + \frac{\ln(1+2k)}{2k} -
1588  * \frac{1+3k}{(1+2k)^2}
1589  * \f]
1590  *
1591  * where \f$k = E/ m_e c^2\f$.
1592  ***************************************************************************/
1593 double GCOMInstChars::kn_cross_section(const double& k) const
1594 {
1595  // Compute integrated Klein-Nishina cross section
1596  double k1 = k + 1.0;
1597  double k2p1 = 1.0 + 2.0*k;
1598  double logk2p1 = std::log(k2p1);
1599  double kn = k1/(k*k) * (2.0*k1/k2p1 - logk2p1/k) +
1600  logk2p1/(2.0*k) - (1.0+3.0*k)/(k2p1*k2p1);
1601 
1602  // Return
1603  return kn;
1604 }
1605 
1606 
1607 /***********************************************************************//**
1608  * @brief Return minimum coefficient.
1609  *
1610  * @param[in] coeffs Coefficients.
1611  * @return Minimum coefficient.
1612  *
1613  * Returns minimum coefficient.
1614  ***************************************************************************/
1615 double GCOMInstChars::min_coeff(const std::vector<double>& coeffs) const
1616 {
1617  // Initialise minimum
1618  double min = 0.0;
1619 
1620  // Continue only if there are coefficients
1621  if (coeffs.size() > 0) {
1622 
1623  // Set first value as minimum
1624  min = coeffs[0];
1625 
1626  // Search for minimum
1627  for (int i = 1; i < coeffs.size(); ++i) {
1628  if (coeffs[i] < min) {
1629  min = coeffs[i];
1630  }
1631  }
1632 
1633  }
1634 
1635  // Return minimum
1636  return min;
1637 }
1638 
1639 
1640 /***********************************************************************//**
1641  * @brief Return maximum coefficient.
1642  *
1643  * @param[in] coeffs Coefficients.
1644  * @return Maximum coefficient.
1645  *
1646  * Returns maximum coefficient.
1647  ***************************************************************************/
1648 double GCOMInstChars::max_coeff(const std::vector<double>& coeffs) const
1649 {
1650  // Initialise maximum
1651  double max = 0.0;
1652 
1653  // Continue only if there are coefficients
1654  if (coeffs.size() > 0) {
1655 
1656  // Set first value as maximum
1657  max = coeffs[0];
1658 
1659  // Search for maximum
1660  for (int i = 1; i < coeffs.size(); ++i) {
1661  if (coeffs[i] > max) {
1662  max = coeffs[i];
1663  }
1664  }
1665 
1666  }
1667 
1668  // Return maximum
1669  return max;
1670 }
const double mec2
Definition: GTools.hpp:52
COMPTEL Instrument Characteristics interface definition.
double m_delz
Distance between D1 and D2 levels (cm)
int size(void) const
Return number of nodes in node array.
Definition: GNodeArray.hpp:192
std::vector< double > m_d2pos_x
D2 x-position (cm)
GFitsTable * table(const int &extno)
Get pointer to table HDU.
Definition: GFits.cpp:482
double norm(const GVector &vector)
Computes vector norm.
Definition: GVector.cpp:864
Node array class.
Definition: GNodeArray.hpp:60
void read_selfveto(const GFitsTable &table)
Read selfveto coefficients.
double prob_no_multihit(const double &energy) const
Return probability that no multihit occured.
Energy value class definition.
std::vector< double > m_d2inter_coeffs
D2 interaction coefficients.
GNodeArray m_d2inter_energies
D2 interaction coefficient energies (MeV)
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.
const double pi
Definition: GMath.hpp:35
double trans_D1(const double &energy) const
Return transmission above D1.
double max_coeff(const std::vector< double > &coeffs) const
Return maximum coefficient.
double m_abdens
Density above D1 (g/cm^-3)
GVector abs(const GVector &vector)
Computes absolute of vector elements.
Definition: GVector.cpp:1253
double prob_D1inter(const double &energy) const
Return D1 interaction probability.
std::vector< double > m_d2multi_coeffs
D2 multihit attenuation coefficients (probability)
GNodeArray m_alu_energies
Al interaction coefficient energies (MeV)
GVector cos(const GVector &vector)
Computes cosine of vector elements.
Definition: GVector.cpp:1190
double m_vetodens
Density of veto domes (g/cm^-3)
GCaldb m_caldb
Calibration database.
double com_energy2(const double &energy, const double &phigeo)
Return D2 energy deposit.
bool is_empty(void) const
Signal if filename is empty.
Definition: GFilename.hpp:160
bool contains(const std::string &colname) const
Checks the presence of a column in table.
Definition: GFitsTable.cpp:759
GFilename filename(const std::string &detector, const std::string &filter, const std::string &codename, const std::string &date, const std::string &time, const std::string &expr)
Return calibration file name based on selection parameters.
Definition: GCaldb.cpp:524
const double & wgt_left(void) const
Returns left node weight.
Definition: GNodeArray.hpp:264
void free_members(void)
Delete class members.
void clear(void)
Clear node array.
Definition: GNodeArray.cpp:235
void read_pos(const GFitsTable &table, std::vector< double > &x, std::vector< double > &y)
Read module positions.
Interface for the COMPTEL Instrument Characteristics class.
GNodeArray m_aboved1_energies
Above D1 attenuation coefficient energies (MeV)
GNodeArray m_d1multi_energies
D1 multihit attenuation coefficient energies (MeV)
double m_aldens
Density of aluminium plate above D2 (g/cm^-3)
FITS file class.
Definition: GFits.hpp:63
void set_value(const double &value) const
Set indices and weighting factors for interpolation.
Definition: GNodeArray.cpp:587
double min(const GVector &vector)
Computes minimum vector element.
Definition: GVector.cpp:886
double m_d2dens
D2 density (g/cm^-3)
void copy_members(const GCOMInstChars &ict)
Copy class members.
Implementation of support function used by COMPTEL classes.
double m_vthick
Thickness of V2 and V3 veto domes together (cm)
GCOMInstChars * clone(void) const
Clone instance.
double psd_correction(const double &energy, const double &phigeo) const
Return PSD correction.
double real(const std::string &keyname) const
Return card value as double precision.
Definition: GFitsHDU.hpp:423
std::vector< double > m_d1pos_y
D1 y-position (cm)
double ne213a_mfpath(const double &energy) const
Return NE213A mean free path.
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
const double & wgt_right(void) const
Returns right node weight.
Definition: GNodeArray.hpp:279
std::vector< double > m_d2pos_y
D2 y-position (cm)
double m_abthick
Thickness above D1 (cm)
Filename class.
Definition: GFilename.hpp:62
Abstract interface for FITS table column.
double m_v1thick
Thickness of V1 veto dome (cm)
void load(const std::string &ictname)
Load COMPTEL instrument characteristics.
bool exists(void) const
Checks whether file exists.
Definition: GFilename.cpp:223
GVector log(const GVector &vector)
Computes natural logarithm of vector elements.
Definition: GVector.cpp:1274
void read_coeffs(const GFitsTable &table, GNodeArray &energies, std::vector< double > &coeffs)
Read energy dependent coefficients.
std::vector< double > m_d1pos_x
D1 x-position (cm)
double interpolate(const double &value, const std::vector< double > &vector) const
Interpolate value.
Definition: GNodeArray.cpp:536
double m_d1dens
D1 density (g/cm^-3)
std::string print(const GChatter &chatter=NORMAL) const
Print COMPTEL instrument characteristics.
Abstract interface for FITS table.
Definition: GFitsTable.hpp:44
void init_members(void)
Initialise class members.
GNodeArray m_selfveto_energies
Selfveto energies (MeV)
GNodeArray m_d2multi_energies
D2 multihit attenuation coefficient energies (MeV)
GChatter
Definition: GTypemaps.hpp:33
const int & inx_left(void) const
Returns left node index.
Definition: GNodeArray.hpp:235
const GCaldb & caldb(void) const
Return calibration database.
double m_d2thick
D2 thickness (cm)
std::vector< double > m_d1multi_coeffs
D1 multihit attenuation coefficients (probability)
double max(const GVector &vector)
Computes maximum vector element.
Definition: GVector.cpp:915
void nodes(const int &num, const double *array)
Set node array.
Definition: GNodeArray.cpp:325
const int & inx_right(void) const
Returns right node index.
Definition: GNodeArray.hpp:249
const int & nrows(void) const
Return number of rows in table.
Definition: GFitsTable.hpp:119
std::vector< double > m_aboved1_coeffs
Above D1 attenuation coefficients.
GNodeArray m_selfveto_zeniths
Selfveto zenith angle (deg)
double prob_no_selfveto(const double &energy, const double &zenith) const
Return probability that photon was not self vetoed.
GCOMInstChars(void)
Void constructor.
virtual int integer(const int &row, const int &inx=0) const =0
virtual double real(const int &row, const int &inx=0) const =0
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
std::string print(const GChatter &chatter=NORMAL) const
Print calibration database information.
Definition: GCaldb.cpp:657
double m_d1thick
D1 thickness (cm)
void clear(void)
Clear instance.
double m_d1rad
D1 radius (cm)
GVector exp(const GVector &vector)
Computes exponential of vector elements.
Definition: GVector.cpp:1232
double kn_cross_section(const double &k) const
Return integrated Klein-Nishina cross section.
std::vector< double > m_selfveto_coeffs
Selfveto coefficients (probability)
double m_althick
Thickness of aluminium plate above D2 (cm)
std::string filepath(const std::string &pathname, const std::string &filename)
Build file path from path name and file name.
Definition: GTools.cpp:393
double trans_V1(const double &energy) const
Return V1 veto dome transmission.
std::string parformat(const std::string &s, const int &indent=0)
Convert string in parameter format.
Definition: GTools.cpp:1143
double m_thbar
Average D2 incident angle (deg)
double m_d2rad
D2 radius (cm)
void append(const double &node)
Append one node to array.
Definition: GNodeArray.cpp:351
double com_energy1(const double &energy, const double &phigeo)
Return D1 energy deposit.
GNodeArray m_d1inter_energies
D1 interaction coefficient energies (MeV)
double trans_V23(const double &energy, const double &phigeo) const
Return V2+V3 veto dome transmission.
void close(void)
Close FITS file.
Definition: GFits.cpp:1342
std::vector< double > m_alu_coeffs
Al interaction coefficients.
GCOMInstChars & operator=(const GCOMInstChars &ict)
Assignment operator.
std::vector< double > m_d1inter_coeffs
D1 interaction coefficients.
void clear(void)
Clear calibration database.
Definition: GCaldb.cpp:194
std::vector< double > m_veto_coeffs
Veto dome attenuation coefficients.
double min_coeff(const std::vector< double > &coeffs) const
Return minimum coefficient.
std::string rootdir(void) const
Return path to CALDB root directory.
Definition: GCaldb.cpp:257
GNodeArray m_veto_energies
Veto dome attenuation coefficient energies (MeV)
Mathematical function definitions.
double multi_scatter(const double &energy, const double &phigeo) const
Return multi-scatter correction.
std::string str(const unsigned short int &value)
Convert unsigned short integer value into string.
Definition: GTools.cpp:489
~GCOMInstChars(void)
Destructor.