GammaLib  2.0.0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
GCOMResponse.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  * GCOMResponse.cpp - COMPTEL Response class *
3  * ----------------------------------------------------------------------- *
4  * copyright (C) 2012-2022 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 GCOMResponse.cpp
23  * @brief COMPTEL response class implementation
24  * @author Juergen Knoedlseder
25  */
26 
27 /* __ Includes ___________________________________________________________ */
28 #ifdef HAVE_CONFIG_H
29 #include <config.h>
30 #endif
31 #include <string>
32 #include <typeinfo>
33 #include "GTools.hpp"
34 #include "GMath.hpp"
35 #include "GVector.hpp"
36 #include "GMatrix.hpp"
37 #include "GIntegrals.hpp"
38 #include "GFits.hpp"
39 #include "GCaldb.hpp"
40 #include "GEvent.hpp"
41 #include "GPhoton.hpp"
42 #include "GSource.hpp"
43 #include "GEnergy.hpp"
44 #include "GTime.hpp"
45 #include "GObservation.hpp"
46 #include "GFitsImage.hpp"
47 #include "GFitsImageFloat.hpp"
48 #include "GModelSky.hpp"
50 #include "GModelSpatialRadial.hpp"
52 #include "GModelSpectralConst.hpp"
53 #include "GCOMResponse.hpp"
54 #include "GCOMObservation.hpp"
55 #include "GCOMEventCube.hpp"
56 #include "GCOMEventBin.hpp"
57 #include "GCOMInstDir.hpp"
59 
60 
61 /* __ Method name definitions ____________________________________________ */
62 #define G_IRF "GCOMResponse::irf(GEvent&, GPhoton&, GObservation&)"
63 #define G_IRF_SPATIAL "GCOMResponse::irf_spatial(GEvent&, GSource&, "\
64  "GObservation&)"
65 #define G_NROI "GCOMResponse::nroi(GModelSky&, GEnergy&, GTime&, "\
66  "GObservation&)"
67 #define G_EBOUNDS "GCOMResponse::ebounds(GEnergy&)"
68 #define G_IRF_PTSRC "GCOMResponse::irf_ptsrc(GModelSky&, GObservation&, "\
69  "GMatrix*)"
70 #define G_IRF_RADIAL "GCOMResponse::irf_radial(GModelSky&, GObservation&, "\
71  "GMatrix*)"
72 #define G_IRF_ELLIPTICAL "GCOMResponse::irf_elliptical(GModelSky&, "\
73  " GObservation&, GMatrix*)"
74 #define G_IRF_DIFFUSE "GCOMResponse::irf_diffuse(GModelSky&, GObservation&, "\
75  "GMatrix*)"
76 
77 /* __ Macros _____________________________________________________________ */
78 
79 /* __ Coding definitions _________________________________________________ */
80 
81 /* __ Debug definitions __________________________________________________ */
82 
83 /* __ Constants __________________________________________________________ */
84 
85 
86 /*==========================================================================
87  = =
88  = Constructors/destructors =
89  = =
90  ==========================================================================*/
91 
92 /***********************************************************************//**
93  * @brief Void constructor
94  *
95  * Creates an empty COMPTEL response.
96  ***************************************************************************/
98 {
99  // Initialise members
100  init_members();
101 
102  // Return
103  return;
104 }
105 
106 
107 /***********************************************************************//**
108  * @brief Copy constructor
109  *
110  * @param[in] rsp COM response.
111  **************************************************************************/
113 {
114  // Initialise members
115  init_members();
116 
117  // Copy members
118  copy_members(rsp);
119 
120  // Return
121  return;
122 }
123 
124 
125 /***********************************************************************//**
126  * @brief Response constructor
127  *
128  * @param[in] caldb Calibration database.
129  * @param[in] iaqname IAQ file name.
130  *
131  * Create COMPTEL response by loading an IAQ file from a calibration
132  * database.
133  ***************************************************************************/
135  const std::string& iaqname) : GResponse()
136 {
137  // Initialise members
138  init_members();
139 
140  // Set calibration database
141  this->caldb(caldb);
142 
143  // Load IRF
144  this->load(iaqname);
145 
146  // Return
147  return;
148 }
149 
150 
151 /***********************************************************************//**
152  * @brief Destructor
153  *
154  * Destroys instance of COMPTEL response object.
155  ***************************************************************************/
157 {
158  // Free members
159  free_members();
160 
161  // Return
162  return;
163 }
164 
165 
166 /*==========================================================================
167  = =
168  = Operators =
169  = =
170  ==========================================================================*/
171 
172 /***********************************************************************//**
173  * @brief Assignment operator
174  *
175  * @param[in] rsp COMPTEL response.
176  * @return COMPTEL response.
177  *
178  * Assigns COMPTEL response object to another COMPTEL response object. The
179  * assignment performs a deep copy of all information, hence the original
180  * object from which the assignment has been performed can be destroyed after
181  * this operation without any loss of information.
182  ***************************************************************************/
184 {
185  // Execute only if object is not identical
186  if (this != &rsp) {
187 
188  // Copy base class members
189  this->GResponse::operator=(rsp);
190 
191  // Free members
192  free_members();
193 
194  // Initialise members
195  init_members();
196 
197  // Copy members
198  copy_members(rsp);
199 
200  } // endif: object was not identical
201 
202  // Return this object
203  return *this;
204 }
205 
206 
207 /*==========================================================================
208  = =
209  = Public methods =
210  = =
211  ==========================================================================*/
212 
213 /***********************************************************************//**
214  * @brief Clear instance
215  *
216  * Clears COMPTEL response object by resetting all members to an initial
217  * state. Any information that was present in the object before will be lost.
218  ***************************************************************************/
220 {
221  // Free class members (base and derived classes, derived class first)
222  free_members();
223  this->GResponse::free_members();
224 
225  // Initialise members
226  this->GResponse::init_members();
227  init_members();
228 
229  // Return
230  return;
231 }
232 
233 
234 /***********************************************************************//**
235  * @brief Clone instance
236  *
237  * @return Pointer to deep copy of COMPTEL response.
238  ***************************************************************************/
240 {
241  return new GCOMResponse(*this);
242 }
243 
244 
245 /***********************************************************************//**
246  * @brief Return value of instrument response function
247  *
248  * @param[in] event Observed event.
249  * @param[in] photon Incident photon.
250  * @param[in] obs Observation.
251  * @return Instrument response function (\f$cm^2 sr^{-1}\f$)
252  *
253  * @exception GException::invalid_argument
254  * Observation is not a COMPTEL observation.
255  * Event is not a COMPTEL event bin.
256  * @exception GException::invalid_value
257  * Response not initialised with a valid IAQ
258  *
259  * Returns the instrument response function for a given observed photon
260  * direction as function of the assumed true photon direction. The result
261  * is given by
262  *
263  * \f[
264  * {\tt IRF} = \frac{{\tt IAQ} \times {\tt DRG} \times {\tt DRX}}
265  * {T \times {\tt TOFCOR}} \times {\tt PHASECOR}
266  * \f]
267  *
268  * where
269  * - \f${\tt IRF}\f$ is the instrument response function (\f$cm^2 sr^{-1}\f$),
270  * - \f${\tt IAQ}\f$ is the COMPTEL response matrix (\f$sr^{-1}\f$),
271  * - \f${\tt DRG}\f$ is the geometry factor (probability),
272  * - \f${\tt DRX}\f$ is the exposure (\f$cm^2 s\f$),
273  * - \f$T\f$ is the ontime (\f$s\f$), and
274  * - \f${\tt TOFCOR}\f$ is a correction that accounts for the Time of Flight
275  * selection window.
276  * - \f${\tt PHASECOR}\f$ is a correction that accounts for pulsar phase
277  * selection.
278  *
279  * The observed photon direction is spanned by the three values \f$\Chi\f$,
280  * \f$\Psi\f$, and \f$\bar{\varphi})\f$. \f$\Chi\f$ and \f$\Psi\f$ is the
281  * scatter direction of the event, given in sky coordinates.
282  * \f$\bar{\varphi}\f$ is the Compton scatter angle, computed from the
283  * energy deposits in the two detector planes.
284  ***************************************************************************/
285 double GCOMResponse::irf(const GEvent& event,
286  const GPhoton& photon,
287  const GObservation& obs) const
288 {
289  // Extract COMPTEL observation
290  const GCOMObservation* observation = dynamic_cast<const GCOMObservation*>(&obs);
291  if (observation == NULL) {
292  std::string cls = std::string(typeid(&obs).name());
293  std::string msg = "Observation of type \""+cls+"\" is not a COMPTEL "
294  "observations. Please specify a COMPTEL observation "
295  "as argument.";
297  }
298 
299  // Extract COMPTEL event cube
300  const GCOMEventCube* cube = dynamic_cast<const GCOMEventCube*>(observation->events());
301  if (cube == NULL) {
302  std::string cls = std::string(typeid(&cube).name());
303  std::string msg = "Event cube of type \""+cls+"\" is not a COMPTEL "
304  "event cube. Please specify a COMPTEL event cube "
305  "as argument.";
307  }
308 
309  // Extract COMPTEL event bin
310  const GCOMEventBin* bin = dynamic_cast<const GCOMEventBin*>(&event);
311  if (bin == NULL) {
312  std::string cls = std::string(typeid(&event).name());
313  std::string msg = "Event of type \""+cls+"\" is not a COMPTEL event. "
314  "Please specify a COMPTEL event as argument.";
316  }
317 
318  // Throw an exception if COMPTEL response is not set or if
319  if (m_iaq.empty()) {
320  std::string msg = "COMPTEL response is empty. Please initialise the "
321  "response with an \"IAQ\".";
322  throw GException::invalid_value(G_IRF, msg);
323  }
324  else if (m_phigeo_bin_size == 0.0) {
325  std::string msg = "COMPTEL response has a zero Phigeo bin size. "
326  "Please initialise the response with a valid "
327  "\"IAQ\".";
328  throw GException::invalid_value(G_IRF, msg);
329  }
330 
331  // Extract event parameters
332  const GCOMInstDir& obsDir = bin->dir();
333 
334  // Extract photon parameters
335  const GSkyDir& srcDir = photon.dir();
336  const GTime& srcTime = photon.time();
337 
338  // Compute angle between true photon arrival direction and scatter
339  // direction (Chi,Psi)
340  double phigeo = srcDir.dist_deg(obsDir.dir());
341 
342  // Compute scatter angle index
343  int iphibar = int(obsDir.phibar() / m_phibar_bin_size);
344 
345  // Initialise IRF
346  double iaq = 0.0;
347  double irf = 0.0;
348 
349  // Extract IAQ value by linear inter/extrapolation in Phigeo
350  if (iphibar < m_phibar_bins) {
351  double phirat = phigeo / m_phigeo_bin_size; // 0.5 at bin centre
352  int iphigeo = int(phirat); // index into which Phigeo falls
353  double eps = phirat - iphigeo - 0.5; // 0.0 at bin centre [-0.5, 0.5[
354  if (iphigeo < m_phigeo_bins) {
355  int i = iphibar * m_phigeo_bins + iphigeo;
356  if (eps < 0.0) { // interpolate towards left
357  if (iphigeo > 0) {
358  iaq = (1.0 + eps) * m_iaq[i] - eps * m_iaq[i-1];
359  }
360  else {
361  iaq = (1.0 - eps) * m_iaq[i] + eps * m_iaq[i+1];
362  }
363  }
364  else { // interpolate towards right
365  if (iphigeo < m_phigeo_bins-1) {
366  iaq = (1.0 - eps) * m_iaq[i] + eps * m_iaq[i+1];
367  }
368  else {
369  iaq = (1.0 + eps) * m_iaq[i] - eps * m_iaq[i-1];
370  }
371  }
372  }
373  }
374 
375  // Continue only if IAQ is positive
376  if (iaq > 0.0) {
377 
378  // Get DRG value (unit: probability)
379  double drg = observation->drg().map()(obsDir.dir(), iphibar);
380 
381  // Get DRX value (unit: cm^2 sec)
382  double drx = observation->drx().map()(srcDir);
383 
384  // Get ontime (unit: s)
385  double ontime = observation->ontime();
386 
387  // Get ToF correction
388  double tofcor = cube->dre().tof_correction();
389 
390  // Get pulsar phase correction
391  double phasecor = cube->dre().phase_correction();
392 
393  // Compute IRF value
394  irf = iaq * drg * drx / (ontime * tofcor) * phasecor;
395 
396  // Apply deadtime correction
397  irf *= obs.deadc(srcTime);
398 
399  // Make sure that IRF is positive
400  if (irf < 0.0) {
401  irf = 0.0;
402  }
403 
404  // Compile option: Check for NaN/Inf
405  #if defined(G_NAN_CHECK)
407  std::cout << "*** ERROR: GCOMResponse::irf:";
408  std::cout << " NaN/Inf encountered";
409  std::cout << " (irf=" << irf;
410  std::cout << ", iaq=" << iaq;
411  std::cout << ", drg=" << drg;
412  std::cout << ", drx=" << drx;
413  std::cout << ")";
414  std::cout << std::endl;
415  }
416  #endif
417 
418  } // endif: IAQ was positive
419 
420  // Return IRF value
421  return irf;
422 }
423 
424 
425 /***********************************************************************//**
426  * @brief Return integral of event probability for a given sky model over ROI
427  *
428  * @param[in] model Sky model.
429  * @param[in] obsEng Observed photon energy.
430  * @param[in] obsTime Observed photon arrival time.
431  * @param[in] obs Observation.
432  * @return 0.0
433  *
434  * @exception GException::feature_not_implemented
435  * Method is not implemented.
436  ***************************************************************************/
437 double GCOMResponse::nroi(const GModelSky& model,
438  const GEnergy& obsEng,
439  const GTime& obsTime,
440  const GObservation& obs) const
441 {
442  // Method is not implemented
443  std::string msg = "Spatial integration of sky model over the data space "
444  "is not implemented.";
446 
447  // Return Npred
448  return (0.0);
449 }
450 
451 
452 /***********************************************************************//**
453  * @brief Return true energy boundaries for a specific observed energy
454  *
455  * @param[in] obsEnergy Observed Energy.
456  * @return True energy boundaries for given observed energy.
457  *
458  * @exception GException::feature_not_implemented
459  * Method is not implemented.
460  ***************************************************************************/
461 GEbounds GCOMResponse::ebounds(const GEnergy& obsEnergy) const
462 {
463  // Initialise an empty boundary object
465 
466  // Throw an exception
467  std::string msg = "Energy dispersion not implemented.";
469 
470  // Return energy boundaries
471  return ebounds;
472 }
473 
474 
475 /***********************************************************************//**
476  * @brief Load COMPTEL response.
477  *
478  * @param[in] rspname COMPTEL response name.
479  *
480  * Loads the COMPTEL response with specified name @p rspname.
481  *
482  * The method first attempts to interpret @p rspname as a file name and to
483  * load the corresponding response.
484  *
485  * If @p rspname is not a FITS file the method searches for an appropriate
486  * response in the calibration database. If no appropriate response is found,
487  * the method takes the CALDB root path and response name to build the full
488  * path to the response file, and tries to load the response from these
489  * paths.
490  *
491  * If also this fails an exception is thrown.
492  ***************************************************************************/
493 void GCOMResponse::load(const std::string& rspname)
494 {
495  // Clear instance but conserve calibration database
496  GCaldb caldb = m_caldb;
497  clear();
498  m_caldb = caldb;
499 
500  // Save response name
501  m_rspname = rspname;
502 
503  // Interpret response name as a FITS file name
504  GFilename filename(rspname);
505 
506  // If the filename does not exist the try getting the response from the
507  // calibration database
508  if (!filename.is_fits()) {
509 
510  // Get GCaldb response
511  filename = m_caldb.filename("","","IAQ","","",rspname);
512 
513  // If filename is empty then build filename from CALDB root path
514  // and response name
515  if (filename.is_empty()) {
516  filename = gammalib::filepath(m_caldb.rootdir(), rspname);
517  if (!filename.exists()) {
518  GFilename testname = filename + ".fits";
519  if (testname.exists()) {
520  filename = testname;
521  }
522  }
523  }
524 
525  } // endif: response name is not a FITS file
526 
527  // Open FITS file
528  GFits fits(filename);
529 
530  // Get IAQ image
531  const GFitsImage& iaq = *fits.image(0);
532 
533  // Read IAQ
534  read(iaq);
535 
536  // Close IAQ FITS file
537  fits.close();
538 
539  // Return
540  return;
541 }
542 
543 
544 /***********************************************************************//**
545  * @brief Read COMPTEL response from FITS image.
546  *
547  * @param[in] image FITS image.
548  *
549  * Read the COMPTEL response from IAQ FITS file and convert the IAQ values
550  * into a probability per steradian.
551  *
552  * The IAQ values are divided by the solid angle of a Phigeo bin which is
553  * given by
554  *
555  * \f{eqnarray*}{
556  * \Omega & = & 2 \pi \left[
557  * \left(
558  * 1 - \cos \left( \varphi_{\rm geo} +
559  * \frac{1}{2} \Delta \varphi_{\rm geo} \right)
560  * \right) -
561  * \left(
562  * 1 - \cos \left( \varphi_{\rm geo} -
563  * \frac{1}{2} \Delta \varphi_{\rm geo} \right)
564  * \right) \right] \\
565  * &=& 2 \pi \left[
566  * \cos \left( \varphi_{\rm geo} -
567  * \frac{1}{2} \Delta \varphi_{\rm geo} \right) -
568  * \cos \left( \varphi_{\rm geo} +
569  * \frac{1}{2} \Delta \varphi_{\rm geo} \right)
570  * \right] \\
571  * &=& 4 \pi \sin \left( \varphi_{\rm geo} \right)
572  * \sin \left( \frac{1}{2} \Delta \varphi_{\rm geo} \right)
573  * \f}
574  ***************************************************************************/
575 void GCOMResponse::read(const GFitsImage& image)
576 {
577  // Continue only if there are two image axis
578  if (image.naxis() == 2) {
579 
580  // Store IAQ dimensions
581  m_phigeo_bins = image.naxes(0);
582  m_phibar_bins = image.naxes(1);
583 
584  // Store IAQ axes definitions
585  m_phigeo_ref_value = image.real("CRVAL1");
586  m_phigeo_ref_pixel = image.real("CRPIX1");
587  m_phigeo_bin_size = image.real("CDELT1");
588  m_phibar_ref_value = image.real("CRVAL2");
589  m_phibar_ref_pixel = image.real("CRPIX2");
590  m_phibar_bin_size = image.real("CDELT2");
591 
592  // Get axes minima (values of first bin)
597 
598  // Compute IAQ size. Continue only if size is positive
599  int size = m_phigeo_bins * m_phibar_bins;
600  if (size > 0) {
601 
602  // Allocate memory for IAQ
603  m_iaq.assign(size, 0.0);
604 
605  // Copy over IAQ values
606  for (int i = 0; i < size; ++i) {
607  m_iaq[i] = image.pixel(i);
608  }
609 
610  } // endif: size was positive
611 
612  // Precompute variable for conversion
613  double phigeo_min = m_phigeo_min * gammalib::deg2rad;
614  double phigeo_bin = m_phigeo_bin_size * gammalib::deg2rad;
615  double omega0 = gammalib::fourpi * std::sin(0.5 * phigeo_bin);
616 
617  // Convert IAQ matrix from probability per Phigeo bin into a
618  // probability per steradian
619  for (int iphigeo = 0; iphigeo < m_phigeo_bins; ++iphigeo) {
620  double phigeo = iphigeo * phigeo_bin + phigeo_min;
621  double omega = omega0 * std::sin(phigeo);
622  for (int iphibar = 0; iphibar < m_phibar_bins; ++iphibar) {
623  m_iaq[iphigeo+iphibar*m_phigeo_bins] /= omega;
624  }
625  }
626 
627  } // endif: image had two axes
628 
629  // Return
630  return;
631 }
632 
633 
634 /***********************************************************************//**
635  * @brief Write COMPTEL response into FITS image.
636  *
637  * @param[in] image FITS image.
638  *
639  * Writes the COMPTEL response into an IAQ FITS file.
640  ***************************************************************************/
642 {
643  // Continue only if response is not empty
644  if (m_phigeo_bins > 0 && m_phibar_bins > 0) {
645 
646  // Initialise image
648 
649  // Convert IAQ matrix from probability per steradian into
650  //probability per Phigeo bin
651  double omega0 = gammalib::fourpi *
653  for (int iphigeo = 0; iphigeo < m_phigeo_bins; ++iphigeo) {
654  double phigeo = iphigeo * m_phigeo_bin_size + m_phigeo_min;
655  double omega = omega0 * std::sin(phigeo * gammalib::deg2rad);
656  for (int iphibar = 0; iphibar < m_phibar_bins; ++iphibar) {
657  int i = iphigeo + iphibar*m_phigeo_bins;
658  image(iphigeo, iphibar) = m_iaq[i] * omega;
659  }
660  }
661 
662  // Set header keywords
663  image.card("CTYPE1", "Phigeo", "Geometrical scatter angle");
664  image.card("CRVAL1", m_phigeo_ref_value,
665  "[deg] Geometrical scatter angle for reference bin");
666  image.card("CDELT1", m_phigeo_bin_size,
667  "[deg] Geometrical scatter angle bin size");
668  image.card("CRPIX1", m_phigeo_ref_pixel,
669  "Reference bin of geometrical scatter angle");
670  image.card("CTYPE2", "Phibar", "Compton scatter angle");
671  image.card("CRVAL2", m_phibar_ref_value,
672  "[deg] Compton scatter angle for reference bin");
673  image.card("CDELT2", m_phibar_bin_size, "[deg] Compton scatter angle bin size");
674  image.card("CRPIX2", m_phibar_ref_pixel,
675  "Reference bin of Compton scatter angle");
676  image.card("BUNIT", "Probability per bin", "Unit of bins");
677  image.card("TELESCOP", "GRO", "Name of telescope");
678  image.card("INSTRUME", "COMPTEL", "Name of instrument");
679  image.card("ORIGIN", "GammaLib", "Origin of FITS file");
680  image.card("OBSERVER", "Unknown", "Observer that created FITS file");
681 
682  } // endif: response was not empty
683 
684  // Return
685  return;
686 }
687 
688 
689 /***********************************************************************//**
690  * @brief Print COMPTEL response information
691  *
692  * @param[in] chatter Chattiness.
693  * @return String containing COMPTEL response information.
694  ***************************************************************************/
695 std::string GCOMResponse::print(const GChatter& chatter) const
696 {
697  // Initialise result string
698  std::string result;
699 
700  // Continue only if chatter is not silent
701  if (chatter != SILENT) {
702 
703  // Append header
704  result.append("=== GCOMResponse ===");
705 
706  // Append response name
707  result.append("\n"+gammalib::parformat("Response name")+m_rspname);
708 
709  // EXPLICIT: Append detailed information
710  if (chatter >= EXPLICIT) {
711 
712  // Append information
713  result.append("\n"+gammalib::parformat("Number of Phigeo bins"));
714  result.append(gammalib::str(m_phigeo_bins));
715  result.append("\n"+gammalib::parformat("Number of Phibar bins"));
716  result.append(gammalib::str(m_phibar_bins));
717  result.append("\n"+gammalib::parformat("Phigeo reference value"));
718  result.append(gammalib::str(m_phigeo_ref_value)+" deg");
719  result.append("\n"+gammalib::parformat("Phigeo reference pixel"));
720  result.append(gammalib::str(m_phigeo_ref_pixel));
721  result.append("\n"+gammalib::parformat("Phigeo bin size"));
722  result.append(gammalib::str(m_phigeo_bin_size)+" deg");
723  result.append("\n"+gammalib::parformat("Phigeo first bin value"));
724  result.append(gammalib::str(m_phigeo_min)+" deg");
725  result.append("\n"+gammalib::parformat("Phibar reference value"));
726  result.append(gammalib::str(m_phibar_ref_value)+" deg");
727  result.append("\n"+gammalib::parformat("Phibar reference pixel"));
728  result.append(gammalib::str(m_phibar_ref_pixel));
729  result.append("\n"+gammalib::parformat("Phibar bin size"));
730  result.append(gammalib::str(m_phibar_bin_size)+" deg");
731  result.append("\n"+gammalib::parformat("Phibar first bin value"));
732  result.append(gammalib::str(m_phibar_min)+" deg");
733 
734  }
735 
736  // VERBOSE: Append calibration database
737  if (chatter == VERBOSE) {
738  result.append("\n"+m_caldb.print(chatter));
739  }
740 
741  } // endif: chatter was not silent
742 
743  // Return result
744  return result;
745 }
746 
747 
748 /*==========================================================================
749  = =
750  = Private methods =
751  = =
752  ==========================================================================*/
753 
754 /***********************************************************************//**
755  * @brief Initialise class members
756  ***************************************************************************/
758 {
759  // Initialise members
760  m_caldb.clear();
761  m_rspname.clear();
762  m_iaq.clear();
763  m_phigeo_bins = 0;
764  m_phibar_bins = 0;
765  m_phigeo_ref_value = 0.0;
766  m_phigeo_ref_pixel = 0.0;
767  m_phigeo_bin_size = 0.0;
768  m_phigeo_min = 0.0;
769  m_phibar_ref_value = 0.0;
770  m_phibar_ref_pixel = 0.0;
771  m_phibar_bin_size = 0.0;
772  m_phibar_min = 0.0;
773 
774  // Return
775  return;
776 }
777 
778 
779 /***********************************************************************//**
780  * @brief Copy class members
781  *
782  * @param[in] rsp COMPTEL response.
783  ***************************************************************************/
785 {
786  // Copy attributes
787  m_caldb = rsp.m_caldb;
788  m_rspname = rsp.m_rspname;
789  m_iaq = rsp.m_iaq;
800 
801  // Return
802  return;
803 }
804 
805 
806 /***********************************************************************//**
807  * @brief Delete class members
808  ***************************************************************************/
810 {
811  // Return
812  return;
813 }
814 
815 
816 /***********************************************************************//**
817  * @brief Return instrument response to point source
818  *
819  * @param[in] model Sky model.
820  * @param[in] obs Observation.
821  * @param[out] gradients Gradients matrix.
822  * @return Instrument response to point source for all events in
823  * observation (\f$cm^2\f$).
824  *
825  * @exception GException::invalid_argument
826  * Observation is not a COMPTEL observation.
827  * Event is not a COMPTEL event bin.
828  * @exception GException::invalid_value
829  * Response not initialised with a valid IAQ
830  * Incompatible IAQ encountered
831  *
832  * Returns the instrument response to a point source for all events in the
833  * observations.
834  *
835  * @p gradients is an optional matrix where the number of rows corresponds
836  * to the number of events in the observation and the number of columns
837  * corresponds to the number of spatial model parameters. Since for point
838  * sources no gradients are computed, the method does not alter the
839  * content of @p gradients.
840  ***************************************************************************/
842  const GObservation& obs,
843  GMatrix* gradients) const
844 {
845  // Extract COMPTEL observation
846  const GCOMObservation* obs_ptr = dynamic_cast<const GCOMObservation*>(&obs);
847  if (obs_ptr == NULL) {
848  std::string cls = std::string(typeid(&obs).name());
849  std::string msg = "Observation of type \""+cls+"\" is not a COMPTEL "
850  "observations. Please specify a COMPTEL observation "
851  "as argument.";
853  }
854 
855  // Extract COMPTEL event cube
856  const GCOMEventCube* cube = dynamic_cast<const GCOMEventCube*>(obs_ptr->events());
857  if (cube == NULL) {
858  std::string msg = "Observation \""+obs.name()+"\" ("+obs.id()+") does "
859  "not contain a COMPTEL event cube. Please specify "
860  "a COMPTEL observation containing and event cube.";
862  }
863 
864  // Throw an exception if COMPTEL response is not set or if
865  if (m_iaq.empty()) {
866  std::string msg = "COMPTEL response is empty. Please initialise the "
867  "response with an \"IAQ\".";
869  }
870  else if (m_phigeo_bin_size == 0.0) {
871  std::string msg = "COMPTEL response has a zero Phigeo bin size. "
872  "Please initialise the response with a valid "
873  "\"IAQ\".";
875  }
876 
877  // Get number of Chi/Psi pixels, Phibar layers and event bins
878  int npix = cube->naxis(0) * cube->naxis(1);
879  int nphibar = cube->naxis(2);
880  int nevents = cube->size();
881 
882  // Throw an exception if the number of Phibar bins does not match the
883  // response
884  if (nphibar != m_phibar_bins) {
885  std::string msg = "DRE has "+gammalib::str(nphibar)+" Phibar layers "
886  "but IAQ has "+gammalib::str(m_phibar_bins)+" Phibar "
887  "bins. Please specify a compatible IAQ.";
889  }
890 
891  // Initialise result
892  GVector irfs(nevents);
893 
894  // Get point source direction
895  GSkyDir srcDir =
896  static_cast<const GModelSpatialPointSource*>(model.spatial())->dir();
897 
898  // Get IAQ normalisation (cm2): DRX (cm2 s) * DEADC / ONTIME (s)
899  double iaq_norm = obs_ptr->drx().map()(srcDir) * obs_ptr->deadc() /
900  (obs_ptr->ontime() * cube->dre().tof_correction()) *
901  cube->dre().phase_correction();
902 
903  // Get pointer to DRG pixels
904  const double* drg = obs_ptr->drg().map().pixels();
905 
906  // Loop over Chi and Psi
907  for (int ipix = 0; ipix < npix; ++ipix) {
908 
909  // Get pointer to event bin
910  const GCOMEventBin* bin = (*cube)[ipix];
911 
912  // Get reference to instrument direction
913  const GCOMInstDir& obsDir = bin->dir();
914 
915  // Get reference to Phigeo sky direction
916  const GSkyDir& phigeoDir = obsDir.dir();
917 
918  // Compute angle between true photon arrival direction and scatter
919  // direction (Chi,Psi)
920  double phigeo = srcDir.dist_deg(phigeoDir);
921 
922  // Compute interpolation factors
923  double phirat = phigeo / m_phigeo_bin_size; // 0.5 at bin centre
924  int iphigeo = int(phirat); // index into which Phigeo falls
925  double eps = phirat - iphigeo - 0.5; // 0.0 at bin centre [-0.5, 0.5[
926 
927  // If Phigeo is in range then compute the IRF value for all
928  // Phibar layers
929  if (iphigeo < m_phigeo_bins) {
930 
931  // Loop over Phibar
932  for (int iphibar = 0; iphibar < nphibar; ++iphibar) {
933 
934  // Get IAQ index
935  int i = iphibar * m_phigeo_bins + iphigeo;
936 
937  // Initialise IAQ
938  double iaq = 0.0;
939 
940  // Compute IAQ
941  if (eps < 0.0) { // interpolate towards left
942  if (iphigeo > 0) {
943  iaq = (1.0 + eps) * m_iaq[i] - eps * m_iaq[i-1];
944  }
945  else {
946  iaq = (1.0 - eps) * m_iaq[i] + eps * m_iaq[i+1];
947  }
948  }
949  else { // interpolate towards right
950  if (iphigeo < m_phigeo_bins-1) {
951  iaq = (1.0 - eps) * m_iaq[i] + eps * m_iaq[i+1];
952  }
953  else {
954  iaq = (1.0 + eps) * m_iaq[i] - eps * m_iaq[i-1];
955  }
956  }
957 
958  // Continue only if IAQ is positive
959  if (iaq > 0.0) {
960 
961  // Get DRI index
962  int idri = ipix + iphibar * npix;
963 
964  // Compute IRF value
965  double irf = iaq * drg[idri] * iaq_norm;
966 
967  // Make sure that IRF is positive
968  if (irf < 0.0) {
969  irf = 0.0;
970  }
971 
972  // Store IRF value
973  irfs[idri] = irf;
974 
975  } // endif: IAQ was positive
976 
977  } // endfor: looped over Phibar
978 
979  } // endif: Phigeo was in valid range
980 
981  } // endfor: looped over Chi and Psi
982 
983  // Return IRF vector
984  return irfs;
985 }
986 
987 
988 /***********************************************************************//**
989  * @brief Return instrument response to radial source
990  *
991  * @param[in] model Sky model.
992  * @param[in] obs Observation.
993  * @param[out] gradients Gradients matrix.
994  * @return Instrument response to radial source for all events in
995  * observation (\f$cm^2\f$).
996  *
997  * @exception GException::invalid_argument
998  * Observation is not a COMPTEL observation.
999  * Event is not a COMPTEL event bin.
1000  * @exception GException::invalid_value
1001  * Response not initialised with a valid IAQ
1002  * Incompatible IAQ encountered
1003  *
1004  * Returns the instrument response to a radial source for all events in
1005  * the observations. See GCOMResponse::irf_extended for more information.
1006  ***************************************************************************/
1008  const GObservation& obs,
1009  GMatrix* gradients) const
1010 {
1011  // Set constants
1012  static const int iter_rho = 5;
1013  static const int iter_omega = 5;
1014 
1015  // Extract COMPTEL observation
1016  const GCOMObservation* obs_ptr = dynamic_cast<const GCOMObservation*>(&obs);
1017  if (obs_ptr == NULL) {
1018  std::string cls = std::string(typeid(&obs).name());
1019  std::string msg = "Observation of type \""+cls+"\" is not a COMPTEL "
1020  "observations. Please specify a COMPTEL observation "
1021  "as argument.";
1023  }
1024 
1025  // Extract COMPTEL event cube
1026  const GCOMEventCube* cube = dynamic_cast<const GCOMEventCube*>(obs_ptr->events());
1027  if (cube == NULL) {
1028  std::string msg = "Observation \""+obs.name()+"\" ("+obs.id()+") does "
1029  "not contain a COMPTEL event cube. Please specify "
1030  "a COMPTEL observation containing and event cube.";
1032  }
1033 
1034  // Throw an exception if COMPTEL response is not set or if
1035  if (m_iaq.empty()) {
1036  std::string msg = "COMPTEL response is empty. Please initialise the "
1037  "response with an \"IAQ\".";
1039  }
1040  else if (m_phigeo_bin_size == 0.0) {
1041  std::string msg = "COMPTEL response has a zero Phigeo bin size. "
1042  "Please initialise the response with a valid "
1043  "\"IAQ\".";
1045  }
1046 
1047  // Get pointer to radial model
1048  const GModelSpatialRadial* radial =
1049  static_cast<const GModelSpatialRadial*>(model.spatial());
1050 
1051  // Get references to model direction and maximum model radius. Reduce
1052  // the maximum model radius by a small amount to avoid rounding
1053  // problems at the boundary of a sharp edged model.
1054  const GSkyDir& model_dir = radial->dir();
1055  const double& theta_max = radial->theta_max() - 1.0e-12;
1056 
1057  // Get number of Chi/Psi pixels, Phibar layers and event bins
1058  int npix = cube->naxis(0) * cube->naxis(1);
1059  int nphibar = cube->naxis(2);
1060  int nevents = cube->size();
1061 
1062  // Throw an exception if the number of Phibar bins does not match the
1063  // response
1064  if (nphibar != m_phibar_bins) {
1065  std::string msg = "DRE has "+gammalib::str(nphibar)+" Phibar layers "
1066  "but IAQ has "+gammalib::str(m_phibar_bins)+" Phibar "
1067  "bins. Please specify a compatible IAQ.";
1069  }
1070 
1071  // Initialise result
1072  GVector irfs(nevents);
1073 
1074  // Initialise some variables
1075  double phigeo_bin = m_phigeo_bin_size * gammalib::deg2rad;
1076  double phigeo_min = m_phigeo_min * gammalib::deg2rad - 0.5 * phigeo_bin;
1077  double phigeo_max = phigeo_min + phigeo_bin * m_phigeo_bins;
1078  const GSkyMap& drx = obs_ptr->drx().map();
1079  const double* drg = obs_ptr->drg().map().pixels();
1080 
1081  // Compute IAQ normalisation (1/s): DEADC / ONTIME (s)
1082  double iaq_norm = obs_ptr->deadc() /
1083  (obs_ptr->ontime() * cube->dre().tof_correction()) *
1084  cube->dre().phase_correction();
1085 
1086  // Setup rotation matrix for conversion towards sky coordinates
1087  GMatrix ry;
1088  GMatrix rz;
1089  ry.eulery(model_dir.dec_deg() - 90.0);
1090  rz.eulerz(-model_dir.ra_deg());
1091  GMatrix rot = (ry * rz).transpose();
1092 
1093  // Loop over Chi and Psi
1094  for (int ipix = 0; ipix < npix; ++ipix) {
1095 
1096  // Get pointer to event bin
1097  const GCOMEventBin* bin = (*cube)[ipix];
1098 
1099  // Get reference to instrument direction
1100  const GCOMInstDir& obsDir = bin->dir();
1101 
1102  // Get reference to Phigeo sky direction
1103  const GSkyDir& phigeoDir = obsDir.dir();
1104 
1105  // Compute angle between model centre and Phigeo sky direction (radians)
1106  // and position angle of model with respect to Phigeo sky direction (radians)
1107  double zeta = phigeoDir.dist(model_dir);
1108 
1109  // Set radial model zenith angle range
1110  double rho_min = (zeta > phigeo_max) ? zeta - phigeo_max : 0.0;
1111  double rho_max = zeta + phigeo_max;
1112  if (rho_min > theta_max) {
1113  rho_min = theta_max;
1114  }
1115  if (rho_max > theta_max) {
1116  rho_max = theta_max;
1117  }
1118 
1119  // Perform model zenith angle integration if interval is valid
1120  if (rho_max > rho_min) {
1121 
1122  // Initialise IRF vector
1123  GVector irf(nphibar);
1124 
1125  // Setup integration kernel
1126  com_radial_kerns_rho integrands(m_iaq,
1127  *radial,
1128  irf,
1129  bin,
1130  rot,
1131  drx,
1132  phigeo_bin,
1133  m_phigeo_bins,
1134  nphibar,
1135  iter_omega);
1136 
1137  // Setup integrator
1138  GIntegrals integral(&integrands);
1139  integral.fixed_iter(iter_rho);
1140 
1141  // Integrate over Phigeo
1142  irf = integral.romberg(rho_min, rho_max, iter_rho);
1143 
1144  // Add IRF to result
1145  for (int iphibar = 0; iphibar < nphibar; ++iphibar) {
1146  int idri = ipix + iphibar * npix;
1147  irfs[idri] = irf[iphibar] * drg[idri] * iaq_norm;
1148  }
1149 
1150  } // endif: Phigeo angle interval was valid
1151 
1152  } // endfor: looped over Chi and Psi pixels
1153 
1154  // Return IRF vector
1155  return irfs;
1156 }
1157 
1158 
1159 /***********************************************************************//**
1160  * @brief Return instrument response to elliptical source
1161  *
1162  * @param[in] model Sky model.
1163  * @param[in] obs Observation.
1164  * @param[out] gradients Gradients matrix.
1165  * @return Instrument response to elliptical source for all events in
1166  * observation (\f$cm^2\f$).
1167  *
1168  * @exception GException::invalid_argument
1169  * Observation is not a COMPTEL observation.
1170  * Event is not a COMPTEL event bin.
1171  * @exception GException::invalid_value
1172  * Response not initialised with a valid IAQ
1173  * Incompatible IAQ encountered
1174  *
1175  * Returns the instrument response to an elliptical source for all events in
1176  * the observations. See GCOMResponse::irf_extended for more information.
1177  ***************************************************************************/
1179  const GObservation& obs,
1180  GMatrix* gradients) const
1181 {
1182  // Set constants
1183  static const int iter_rho = 5;
1184  static const int iter_omega = 5;
1185 
1186  // Extract COMPTEL observation
1187  const GCOMObservation* obs_ptr = dynamic_cast<const GCOMObservation*>(&obs);
1188  if (obs_ptr == NULL) {
1189  std::string cls = std::string(typeid(&obs).name());
1190  std::string msg = "Observation of type \""+cls+"\" is not a COMPTEL "
1191  "observations. Please specify a COMPTEL observation "
1192  "as argument.";
1194  }
1195 
1196  // Extract COMPTEL event cube
1197  const GCOMEventCube* cube = dynamic_cast<const GCOMEventCube*>(obs_ptr->events());
1198  if (cube == NULL) {
1199  std::string msg = "Observation \""+obs.name()+"\" ("+obs.id()+") does "
1200  "not contain a COMPTEL event cube. Please specify "
1201  "a COMPTEL observation containing and event cube.";
1203  }
1204 
1205  // Throw an exception if COMPTEL response is not set or if
1206  if (m_iaq.empty()) {
1207  std::string msg = "COMPTEL response is empty. Please initialise the "
1208  "response with an \"IAQ\".";
1210  }
1211  else if (m_phigeo_bin_size == 0.0) {
1212  std::string msg = "COMPTEL response has a zero Phigeo bin size. "
1213  "Please initialise the response with a valid "
1214  "\"IAQ\".";
1216  }
1217 
1218  // Get pointer to elliptical model
1219  const GModelSpatialElliptical* elliptical =
1220  static_cast<const GModelSpatialElliptical*>(model.spatial());
1221 
1222  // Get references to model direction and maximum model radius
1223  const GSkyDir& model_dir = elliptical->dir();
1224  const double& theta_max = elliptical->theta_max();
1225 
1226  // Get number of Chi/Psi pixels, Phibar layers and event bins
1227  int npix = cube->naxis(0) * cube->naxis(1);
1228  int nphibar = cube->naxis(2);
1229  int nevents = cube->size();
1230 
1231  // Throw an exception if the number of Phibar bins does not match the
1232  // response
1233  if (nphibar != m_phibar_bins) {
1234  std::string msg = "DRE has "+gammalib::str(nphibar)+" Phibar layers "
1235  "but IAQ has "+gammalib::str(m_phibar_bins)+" Phibar "
1236  "bins. Please specify a compatible IAQ.";
1238  }
1239 
1240  // Initialise result
1241  GVector irfs(nevents);
1242 
1243  // Initialise some variables
1244  double phigeo_bin = m_phigeo_bin_size * gammalib::deg2rad;
1245  double phigeo_min = m_phigeo_min * gammalib::deg2rad - 0.5 * phigeo_bin;
1246  double phigeo_max = phigeo_min + phigeo_bin * m_phigeo_bins;
1247  const GSkyMap& drx = obs_ptr->drx().map();
1248  const double* drg = obs_ptr->drg().map().pixels();
1249 
1250  // Compute IAQ normalisation (1/s): DEADC / ONTIME (s)
1251  double iaq_norm = obs_ptr->deadc() /
1252  (obs_ptr->ontime() * cube->dre().tof_correction()) *
1253  cube->dre().phase_correction();
1254 
1255  // Setup rotation matrix for conversion towards sky coordinates
1256  GMatrix ry;
1257  GMatrix rz;
1258  ry.eulery(model_dir.dec_deg() - 90.0);
1259  rz.eulerz(-model_dir.ra_deg());
1260  GMatrix rot = (ry * rz).transpose();
1261 
1262  // Loop over Chi and Psi
1263  for (int ipix = 0; ipix < npix; ++ipix) {
1264 
1265  // Get pointer to event bin
1266  const GCOMEventBin* bin = (*cube)[ipix];
1267 
1268  // Get reference to instrument direction
1269  const GCOMInstDir& obsDir = bin->dir();
1270 
1271  // Get reference to Phigeo sky direction
1272  const GSkyDir& phigeoDir = obsDir.dir();
1273 
1274  // Compute angle between model centre and Phigeo sky direction (radians)
1275  // and position angle of model with respect to Phigeo sky direction (radians)
1276  double zeta = phigeoDir.dist(model_dir);
1277 
1278  // Set radial model zenith angle range
1279  double rho_min = (zeta > phigeo_max) ? zeta - phigeo_max : 0.0;
1280  double rho_max = zeta + phigeo_max;
1281  if (rho_min > theta_max) {
1282  rho_min = theta_max;
1283  }
1284  if (rho_max > theta_max) {
1285  rho_max = theta_max;
1286  }
1287 
1288  // Perform model zenith angle integration if interval is valid
1289  if (rho_max > rho_min) {
1290 
1291  // Initialise IRF vector
1292  GVector irf(nphibar);
1293 
1294  // Setup integration kernel
1295  com_elliptical_kerns_rho integrands(m_iaq,
1296  model,
1297  irf,
1298  bin,
1299  rot,
1300  drx,
1301  phigeo_bin,
1302  m_phigeo_bins,
1303  nphibar,
1304  iter_omega);
1305 
1306  // Setup integrator
1307  GIntegrals integral(&integrands);
1308  integral.fixed_iter(iter_rho);
1309 
1310  // Integrate over Phigeo
1311  irf = integral.romberg(rho_min, rho_max, iter_rho);
1312 
1313  // Add IRF to result
1314  for (int iphibar = 0; iphibar < nphibar; ++iphibar) {
1315  int idri = ipix + iphibar * npix;
1316  irfs[idri] = irf[iphibar] * drg[idri] * iaq_norm;
1317  }
1318 
1319  } // endif: Phigeo angle interval was valid
1320 
1321  } // endfor: looped over Chi and Psi pixels
1322 
1323  // Return IRF vector
1324  return irfs;
1325 }
1326 
1327 
1328 /***********************************************************************//**
1329  * @brief Return instrument response to diffuse source
1330  *
1331  * @param[in] model Sky model.
1332  * @param[in] obs Observation.
1333  * @param[out] gradients Gradients matrix.
1334  * @return Instrument response to diffuse source for all events in
1335  * observation (\f$cm^2\f$).
1336  *
1337  * @exception GException::invalid_argument
1338  * Observation is not a COMPTEL observation.
1339  * @exception GException::invalid_value
1340  * Response not initialised with a valid IAQ
1341  * Incompatible IAQ encountered
1342  *
1343  * Returns the instrument response to a diffuse source for all events in
1344  * the observations. The diffuse source may be energy dependent.
1345  *
1346  * The computation is done by integrating the diffuse model for each pixel
1347  * in Chi and Psi over a circular region centred on the Chi/Psi pixel with
1348  * a radius equal to the maximum Phigeo value. The radial integration is
1349  * done by looping over all Phigeo bins of the response. For each Phigeo
1350  * value, the azimuthal integration is done by stepping with an angular step
1351  * size that corresponds to the Phigeo step size (which typically is 1 deg).
1352  *
1353  * @p gradients is an optional matrix where the number of rows corresponds
1354  * to the number of events in the observation and the number of columns
1355  * corresponds to the number of spatial model parameters. Since for point
1356  * sources no gradients are computed, the method does not alter the
1357  * content of @p gradients.
1358  ***************************************************************************/
1360  const GObservation& obs,
1361  GMatrix* gradients) const
1362 {
1363  // Extract COMPTEL observation
1364  const GCOMObservation* obs_ptr = dynamic_cast<const GCOMObservation*>(&obs);
1365  if (obs_ptr == NULL) {
1366  std::string cls = std::string(typeid(&obs).name());
1367  std::string msg = "Observation of type \""+cls+"\" is not a COMPTEL "
1368  "observations. Please specify a COMPTEL observation "
1369  "as argument.";
1371  }
1372 
1373  // Extract COMPTEL event cube
1374  const GCOMEventCube* cube = dynamic_cast<const GCOMEventCube*>(obs_ptr->events());
1375  if (cube == NULL) {
1376  std::string msg = "Observation \""+obs.name()+"\" ("+obs.id()+") does "
1377  "not contain a COMPTEL event cube. Please specify "
1378  "a COMPTEL observation containing and event cube.";
1380  }
1381 
1382  // Throw an exception if COMPTEL response is not set or if
1383  if (m_iaq.empty()) {
1384  std::string msg = "COMPTEL response is empty. Please initialise the "
1385  "response with an \"IAQ\".";
1387  }
1388  else if (m_phigeo_bin_size == 0.0) {
1389  std::string msg = "COMPTEL response has a zero Phigeo bin size. "
1390  "Please initialise the response with a valid "
1391  "\"IAQ\".";
1393  }
1394 
1395  // Get number of Chi/Psi pixels, Phibar layers and event bins
1396  int npix = cube->naxis(0) * cube->naxis(1);
1397  int nphibar = cube->naxis(2);
1398  int nevents = cube->size();
1399 
1400  // Throw an exception if the number of Phibar bins does not match the
1401  // response
1402  if (nphibar != m_phibar_bins) {
1403  std::string msg = "DRE has "+gammalib::str(nphibar)+" Phibar layers "
1404  "but IAQ has "+gammalib::str(m_phibar_bins)+" Phibar "
1405  "bins. Please specify a compatible IAQ.";
1407  }
1408 
1409  // Initialise result
1410  GVector irfs(nevents);
1411 
1412  // Initialise some variables
1413  double phigeo_min = m_phigeo_min * gammalib::deg2rad;
1414  double phigeo_bin = m_phigeo_bin_size * gammalib::deg2rad;
1415  double omega0 = 2.0 * std::sin(0.5 * phigeo_bin);
1416  const GSkyMap& drx = obs_ptr->drx().map();
1417  const double* drg = obs_ptr->drg().map().pixels();
1418 
1419  // Compute IAQ normalisation (1/s): DEADC / ONTIME (s)
1420  double iaq_norm = obs_ptr->deadc() /
1421  (obs_ptr->ontime() * cube->dre().tof_correction()) *
1422  cube->dre().phase_correction();
1423 
1424  // Loop over Chi and Psi
1425  for (int ipix = 0; ipix < npix; ++ipix) {
1426 
1427  // Get pointer to event bin
1428  const GCOMEventBin* bin = (*cube)[ipix];
1429 
1430  // Get reference to instrument direction
1431  const GCOMInstDir& obsDir = bin->dir();
1432 
1433  // Get reference to Phigeo sky direction
1434  const GSkyDir& phigeoDir = obsDir.dir();
1435 
1436  // Loop over Phigeo
1437  for (int iphigeo = 0; iphigeo < m_phigeo_bins; ++iphigeo) {
1438 
1439  // Determine Phigeo value in radians. Note that phigeo_min is the value
1440  // for bin zero.
1441  double phigeo = phigeo_min + double(iphigeo) * phigeo_bin;
1442  double sin_phigeo = std::sin(phigeo);
1443 
1444  // Determine number of azimuthal integration steps and step size
1445  // in radians
1446  double length = gammalib::twopi * sin_phigeo;
1447  int naz = int(length / phigeo_bin + 0.5);
1448  if (naz < 2) {
1449  naz = 2;
1450  }
1451  double az_step = gammalib::twopi / double(naz);
1452 
1453  // Computes solid angle of integration bin multiplied by Jaccobian
1454  double omega = omega0 * az_step * sin_phigeo;
1455 
1456  // Loop over azimuth angle
1457  double az = 0.0;
1458  for (int iaz = 0; iaz < naz; ++iaz, az += az_step) {
1459 
1460  // Get sky direction
1461  GSkyDir skyDir = phigeoDir;
1462  skyDir.rotate(az, phigeo);
1463 
1464  // Fall through if sky direction is not contained in DRX
1465  if (!drx.contains(skyDir)) {
1466  continue;
1467  }
1468 
1469  // Set photon
1470  GPhoton photon(skyDir, bin->energy(), bin->time());
1471 
1472  // Get model sky intensity for photon (unit: sr^-1)
1473  double intensity = model.spatial()->eval(photon);
1474 
1475  // Fall through if intensity is zero
1476  if (intensity == 0.0) {
1477  continue;
1478  }
1479 
1480  // Multiply intensity by DRX value (unit: cm^2 s sr^-1)
1481  intensity *= drx(skyDir);
1482 
1483  // Multiply intensity by solid angle (unit: cm^2 s)
1484  intensity *= omega;
1485 
1486  // Loop over Phibar
1487  for (int iphibar = 0; iphibar < nphibar; ++iphibar) {
1488 
1489  // Get IAQ index
1490  int i = iphibar * m_phigeo_bins + iphigeo;
1491 
1492  // Get IAQ value
1493  double iaq = m_iaq[i];
1494 
1495  // Fall through if IAQ is not positive
1496  if (iaq <= 0.0) {
1497  continue;
1498  }
1499 
1500  // Get DRI index
1501  int idri = ipix + iphibar * npix;
1502 
1503  // Compute IRF value (unit: cm^2)
1504  double irf = iaq * drg[idri] * iaq_norm * intensity;
1505 
1506  // Add IRF value if it is positive
1507  if (irf > 0.0) {
1508  irfs[idri] += irf;
1509  }
1510 
1511  } // endfor: looped over Phibar
1512 
1513  } // endfor: looped over azimuth angle around locus of IAQ
1514 
1515  } // endfor: looped over Phigeo angles
1516 
1517  } // endfor: looped over Chi and Psi pixels
1518 
1519  // Return IRF vector
1520  return irfs;
1521 }
virtual GCOMResponse * clone(void) const
Clone instance.
void phibar(const double &phibar)
Set event Compton scatter angle.
virtual GVector irf_radial(const GModelSky &model, const GObservation &obs, GMatrix *gradients=NULL) const
Return instrument response to radial source.
double m_phibar_ref_value
Phigeo reference value (deg)
const std::string & rspname(void) const
Return response name.
GCaldb m_caldb
Calibration database.
Sky map class.
Definition: GSkyMap.hpp:89
double dist_deg(const GSkyDir &dir) const
Compute angular distance between sky directions in degrees.
Definition: GSkyDir.hpp:286
#define G_EBOUNDS
Abstract FITS image base class.
Definition: GFitsImage.hpp:43
const int & naxis(void) const
Return dimension of image.
Definition: GFitsImage.hpp:156
virtual std::string print(const GChatter &chatter=NORMAL) const
Print COMPTEL response information.
virtual void clear(void)
Clear instance.
double dec_deg(void) const
Returns Declination in degrees.
Definition: GSkyDir.hpp:256
Point source spatial model class interface definition.
double m_phigeo_bin_size
Phigeo binsize (deg)
Energy value class definition.
Abstract elliptical spatial model base class.
virtual double eval(const GPhoton &photon, const bool &gradients=false) const =0
int m_phibar_bins
Number of Phibar bins.
virtual double nroi(const GModelSky &model, const GEnergy &obsEng, const GTime &obsTime, const GObservation &obs) const
Return integral of event probability for a given sky model over ROI.
std::string m_rspname
Response name.
std::vector< double > m_iaq
IAQ array.
Single precision FITS image class definition.
Generic matrix class definition.
const GCOMDri & drx(void) const
Return exposure.
double m_phibar_min
Phigeo value of first bin (deg)
Abstract radial spatial model base class interface definition.
#define G_IRF_DIFFUSE
void init_members(void)
Initialise class members.
virtual int size(void) const
Return number of bins in event cube.
virtual double ontime(void) const
Return ontime.
bool is_empty(void) const
Signal if filename is empty.
Definition: GFilename.hpp:160
Kernel for rho angle integration of elliptical models.
COMPTEL observation class interface definition.
Abstract interface for the event classes.
Definition: GEvent.hpp:71
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
void fixed_iter(const int &iter)
Set fixed number of iterations.
Definition: GIntegrals.hpp:179
const double & tof_correction(void) const
Return ToF correction factor.
Definition: GCOMDri.hpp:387
const GSkyDir & dir(void) const
Return position of elliptical spatial model.
virtual GVector irf_ptsrc(const GModelSky &model, const GObservation &obs, GMatrix *gradients=NULL) const
Return instrument response to point source.
Interface for the COMPTEL instrument response function.
double m_phigeo_ref_value
Phigeo reference value (deg)
Time class.
Definition: GTime.hpp:55
Gammalib tools definition.
FITS file class.
Definition: GFits.hpp:63
virtual const GTime & time(void) const
Return time of event bin.
FITS file class interface definition.
void init_members(void)
Initialise class members.
Definition: GResponse.cpp:787
COMPTEL event bin class.
double m_phibar_ref_pixel
Phigeo reference pixel (starting from 1)
virtual double deadc(const GTime &time=GTime()) const
Return deadtime correction factor.
Source class definition.
void eulery(const double &angle)
Set Euler rotation matrix around y axis.
Definition: GMatrix.cpp:1157
virtual double theta_max(void) const =0
void id(const std::string &id)
Set observation identifier.
COMPTEL event bin class interface definition.
Abstract FITS image base class definition.
double m_phigeo_min
Phigeo value of first bin (deg)
bool is_notanumber(const double &x)
Signal if argument is not a number.
Definition: GTools.hpp:201
virtual GCOMResponse & operator=(const GCOMResponse &rsp)
Assignment operator.
Class that handles photons.
Definition: GPhoton.hpp:47
bool is_infinite(const double &x)
Signal if argument is infinite.
Definition: GTools.hpp:184
double real(const std::string &keyname) const
Return card value as double precision.
Definition: GFitsHDU.hpp:423
const double & phase_correction(void) const
Return pulsar phase correction factor.
Definition: GCOMDri.hpp:418
const double deg2rad
Definition: GMath.hpp:43
const GCOMDri & drg(void) const
Return geometry factors.
Calibration database class.
Definition: GCaldb.hpp:66
const GSkyDir & dir(void) const
Return position of radial spatial model.
Energy boundaries container class.
Definition: GEbounds.hpp:60
COMPTEL instrument response function class interface definition.
void dir(const GSkyDir &dir)
Set event scatter direction.
Filename class.
Definition: GFilename.hpp:62
void eulerz(const double &angle)
Set Euler rotation matrix around z axis.
Definition: GMatrix.cpp:1194
Integration class for set of functions interface definition.
const GSkyMap & map(void) const
Return DRI sky map.
Definition: GCOMDri.hpp:267
bool exists(void) const
Checks whether file exists.
Definition: GFilename.cpp:223
Constant spectral model class interface definition.
virtual double pixel(const int &ix) const =0
void rotate(const double &phi, const double &theta)
Rotate sky direction by zenith and azimuth angle.
Definition: GSkyDir.cpp:378
Abstract event base class definition.
void write(GFitsImageFloat &image) const
Write COMPTEL response into FITS image.
bool contains(const GSkyDir &dir) const
Checks if sky direction falls in map.
Definition: GSkyMap.cpp:2023
virtual double deadc(const GTime &time=GTime()) const =0
double ra_deg(void) const
Returns Right Ascension in degrees.
Definition: GSkyDir.hpp:229
virtual GVector irf_diffuse(const GModelSky &model, const GObservation &obs, GMatrix *gradients=NULL) const
Return instrument response to diffuse source.
#define G_IRF_PTSRC
const GTime & time(void) const
Return photon time.
Definition: GPhoton.hpp:134
GChatter
Definition: GTypemaps.hpp:33
bool is_fits(void) const
Checks whether file is a FITS file.
Definition: GFilename.cpp:313
Abstract observation base class.
Vector class interface definition.
Single precision FITS image class.
COMPTEL event bin container class.
Photon class definition.
Abstract observation base class interface definition.
virtual double irf(const GEvent &event, const GPhoton &photon, const GObservation &obs) const
Return value of instrument response function.
Calibration database class interface definition.
Point source spatial model.
void free_members(void)
Delete class members.
Definition: GResponse.cpp:835
Defintion of COMPTEL helper classes for vector response.
Abstract elliptical spatial model base class interface definition.
int naxes(const int &axis) const
Return dimension of an image axis.
Definition: GFitsImage.cpp:344
const GCOMDri & dre(void) const
Return reference to DRE data.
virtual GResponse & operator=(const GResponse &rsp)
Assignment operator.
Definition: GResponse.cpp:139
void name(const std::string &name)
Set observation name.
GFitsImage * image(const int &extno)
Get pointer to image HDU.
Definition: GFits.cpp:368
const GCaldb & caldb(void) const
Return calibration database.
void free_members(void)
Delete class members.
double m_phigeo_ref_pixel
Phigeo reference pixel (starting from 1)
Sky model class interface definition.
Sky model class.
Definition: GModelSky.hpp:122
#define G_IRF_ELLIPTICAL
virtual const GCOMInstDir & dir(void) const
Return instrument direction of event bin.
virtual GEbounds ebounds(const GEnergy &obsEnergy) const
Return true energy boundaries for a specific observed energy.
GCOMResponse(void)
Void constructor.
Interface class for COMPTEL observations.
Kernel for rho angle integration of radial models.
#define G_NROI
int m_phigeo_bins
Number of Phigeo bins.
double m_phibar_bin_size
Phigeo binsize (deg)
GVector sin(const GVector &vector)
Computes sine of vector elements.
Definition: GVector.cpp:1316
virtual GEvents * events(void)
Return events.
std::string print(const GChatter &chatter=NORMAL) const
Print calibration database information.
Definition: GCaldb.cpp:657
GVector romberg(std::vector< double > bounds, const int &order=5)
Perform Romberg integration.
Definition: GIntegrals.cpp:210
virtual double theta_max(void) const =0
double dist(const GSkyDir &dir) const
Compute angular distance between sky directions in radians.
Definition: GSkyDir.hpp:271
GModelSpatial * spatial(void) const
Return spatial model component.
Definition: GModelSky.hpp:295
const double fourpi
Definition: GMath.hpp:37
Abstract radial spatial model base class.
const double twopi
Definition: GMath.hpp:36
Generic matrix class definition.
Definition: GMatrix.hpp:79
Vector class.
Definition: GVector.hpp:46
std::string filepath(const std::string &pathname, const std::string &filename)
Build file path from path name and file name.
Definition: GTools.cpp:393
virtual GVector irf_elliptical(const GModelSky &model, const GObservation &obs, GMatrix *gradients=NULL) const
Return instrument response to elliptical source.
void copy_members(const GCOMResponse &rsp)
Copy class members.
COMPTEL instrument direction class definition.
Integration class for set of functions.
Definition: GIntegrals.hpp:47
Abstract instrument response base class.
Definition: GResponse.hpp:77
std::string parformat(const std::string &s, const int &indent=0)
Convert string in parameter format.
Definition: GTools.cpp:1143
COMPTEL event bin container class interface definition.
virtual ~GCOMResponse(void)
Destructor.
#define G_IRF
int iter_rho(const double &rho_max, const double &resolution, const int &iter_min, const int &iter_max)
Determine number of radial Romberg iterations.
GFitsHeaderCard & card(const int &cardno)
Return header card.
Definition: GFitsHDU.hpp:259
Sky direction class.
Definition: GSkyDir.hpp:62
void close(void)
Close FITS file.
Definition: GFits.cpp:1342
virtual int naxis(const int &axis) const
Return number of bins in axis.
const GSkyDir & dir(void) const
Return photon sky direction.
Definition: GPhoton.hpp:110
#define G_IRF_RADIAL
Time class interface definition.
void clear(void)
Clear calibration database.
Definition: GCaldb.cpp:194
const double * pixels(void) const
Returns pointer to pixel data.
Definition: GSkyMap.hpp:475
void read(const GFitsImage &hdu)
Read COMPTEL response from FITS image.
std::string rootdir(void) const
Return path to CALDB root directory.
Definition: GCaldb.cpp:257
Interface for the COMPTEL instrument direction class.
Definition: GCOMInstDir.hpp:45
Mathematical function definitions.
Class that handles energies in a unit independent way.
Definition: GEnergy.hpp:48
void load(const std::string &rspname)
Load COMPTEL response.
virtual const GEnergy & energy(void) const
Return energy of event bin.
std::string str(const unsigned short int &value)
Convert unsigned short integer value into string.
Definition: GTools.cpp:489