GammaLib 2.0.0
Loading...
Searching...
No Matches
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"
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();
224
225 // Initialise 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 ***************************************************************************/
285double 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\".";
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\".";
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 ***************************************************************************/
437double 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 ***************************************************************************/
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 ***************************************************************************/
493void GCOMResponse::load(const std::string& rspname)
494{
495 // Clear instance but conserve calibration database
497 clear();
498 m_caldb = caldb;
499
500 // Save response name
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()) {
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 ***************************************************************************/
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 *
652 std::sin(0.5 * m_phigeo_bin_size * gammalib::deg2rad);
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 ***************************************************************************/
695std::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 ***************************************************************************/
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,
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
1296 model,
1297 irf,
1298 bin,
1299 rot,
1300 drx,
1301 phigeo_bin,
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}
COMPTEL event bin class interface definition.
COMPTEL event bin container class interface definition.
COMPTEL instrument direction class definition.
COMPTEL observation class interface definition.
#define G_IRF
#define G_EBOUNDS
#define G_NROI
#define G_IRF_PTSRC
COMPTEL instrument response function class interface definition.
Calibration database class interface definition.
Energy value class definition.
Abstract event base class definition.
Single precision FITS image class definition.
Abstract FITS image base class definition.
FITS file class interface definition.
Integration class for set of functions interface definition.
Mathematical function definitions.
Generic matrix class definition.
Sky model class interface definition.
Abstract elliptical spatial model base class interface definition.
Point source spatial model class interface definition.
Abstract radial spatial model base class interface definition.
Constant spectral model class interface definition.
Abstract observation base class interface definition.
Photon class definition.
#define G_IRF_ELLIPTICAL
Definition GResponse.cpp:65
#define G_IRF_RADIAL
Definition GResponse.cpp:63
#define G_IRF_DIFFUSE
Definition GResponse.cpp:67
Source class definition.
Time class interface definition.
Gammalib tools definition.
GChatter
Definition GTypemaps.hpp:33
@ EXPLICIT
Definition GTypemaps.hpp:37
@ SILENT
Definition GTypemaps.hpp:34
@ VERBOSE
Definition GTypemaps.hpp:38
Vector class interface definition.
const double & phase_correction(void) const
Return pulsar phase correction factor.
Definition GCOMDri.hpp:418
const double & tof_correction(void) const
Return ToF correction factor.
Definition GCOMDri.hpp:387
const GSkyMap & map(void) const
Return DRI sky map.
Definition GCOMDri.hpp:267
COMPTEL event bin class.
virtual const GTime & time(void) const
Return time of event bin.
virtual const GCOMInstDir & dir(void) const
Return instrument direction of event bin.
virtual const GEnergy & energy(void) const
Return energy of event bin.
COMPTEL event bin container class.
virtual int size(void) const
Return number of bins in event cube.
const GCOMDri & dre(void) const
Return reference to DRE data.
virtual int naxis(const int &axis) const
Return number of bins in axis.
Interface for the COMPTEL instrument direction class.
void dir(const GSkyDir &dir)
Set event scatter direction.
void phibar(const double &phibar)
Set event Compton scatter angle.
Interface class for COMPTEL observations.
virtual double ontime(void) const
Return ontime.
virtual double deadc(const GTime &time=GTime()) const
Return deadtime correction factor.
const GCOMDri & drx(void) const
Return exposure.
const GCOMDri & drg(void) const
Return geometry factors.
Interface for the COMPTEL instrument response function.
virtual double irf(const GEvent &event, const GPhoton &photon, const GObservation &obs) const
Return value of instrument response function.
void free_members(void)
Delete class members.
virtual std::string print(const GChatter &chatter=NORMAL) const
Print COMPTEL response information.
virtual GVector irf_ptsrc(const GModelSky &model, const GObservation &obs, GMatrix *gradients=NULL) const
Return instrument response to point source.
int m_phigeo_bins
Number of Phigeo bins.
double m_phibar_min
Phigeo value of first bin (deg)
void write(GFitsImageFloat &image) const
Write COMPTEL response into FITS image.
GCOMResponse(void)
Void constructor.
virtual GVector irf_radial(const GModelSky &model, const GObservation &obs, GMatrix *gradients=NULL) const
Return instrument response to radial source.
int m_phibar_bins
Number of Phibar bins.
const std::string & rspname(void) const
Return response name.
double m_phibar_bin_size
Phigeo binsize (deg)
void copy_members(const GCOMResponse &rsp)
Copy class members.
std::vector< double > m_iaq
IAQ array.
double m_phigeo_min
Phigeo value of first bin (deg)
virtual ~GCOMResponse(void)
Destructor.
virtual GVector irf_elliptical(const GModelSky &model, const GObservation &obs, GMatrix *gradients=NULL) const
Return instrument response to elliptical source.
const GCaldb & caldb(void) const
Return calibration database.
double m_phibar_ref_value
Phigeo reference value (deg)
void read(const GFitsImage &hdu)
Read COMPTEL response from FITS image.
double m_phigeo_ref_value
Phigeo reference value (deg)
std::string m_rspname
Response name.
virtual GEbounds ebounds(const GEnergy &obsEnergy) const
Return true energy boundaries for a specific observed energy.
GCaldb m_caldb
Calibration database.
void init_members(void)
Initialise class members.
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.
virtual void clear(void)
Clear instance.
double m_phigeo_ref_pixel
Phigeo reference pixel (starting from 1)
double m_phigeo_bin_size
Phigeo binsize (deg)
virtual GVector irf_diffuse(const GModelSky &model, const GObservation &obs, GMatrix *gradients=NULL) const
Return instrument response to diffuse source.
virtual GCOMResponse * clone(void) const
Clone instance.
double m_phibar_ref_pixel
Phigeo reference pixel (starting from 1)
virtual GCOMResponse & operator=(const GCOMResponse &rsp)
Assignment operator.
void load(const std::string &rspname)
Load COMPTEL response.
Calibration database class.
Definition GCaldb.hpp:66
std::string rootdir(void) const
Return path to CALDB root directory.
Definition GCaldb.cpp:257
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 clear(void)
Clear calibration database.
Definition GCaldb.cpp:194
std::string print(const GChatter &chatter=NORMAL) const
Print calibration database information.
Definition GCaldb.cpp:657
Energy boundaries container class.
Definition GEbounds.hpp:60
Class that handles energies in a unit independent way.
Definition GEnergy.hpp:48
Abstract interface for the event classes.
Definition GEvent.hpp:71
Filename class.
Definition GFilename.hpp:62
bool is_fits(void) const
Checks whether file is a FITS file.
bool exists(void) const
Checks whether file exists.
bool is_empty(void) const
Signal if filename is empty.
double real(const std::string &keyname) const
Return card value as double precision.
Definition GFitsHDU.hpp:423
GFitsHeaderCard & card(const int &cardno)
Return header card.
Definition GFitsHDU.hpp:259
Single precision FITS image class.
Abstract FITS image base class.
virtual double pixel(const int &ix) const =0
int naxes(const int &axis) const
Return dimension of an image axis.
const int & naxis(void) const
Return dimension of image.
FITS file class.
Definition GFits.hpp:63
GFitsImage * image(const int &extno)
Get pointer to image HDU.
Definition GFits.cpp:368
void close(void)
Close FITS file.
Definition GFits.cpp:1342
Integration class for set of functions.
GVector romberg(std::vector< double > bounds, const int &order=5)
Perform Romberg integration.
void fixed_iter(const int &iter)
Set fixed number of iterations.
Generic matrix class definition.
Definition GMatrix.hpp:79
void eulerz(const double &angle)
Set Euler rotation matrix around z axis.
Definition GMatrix.cpp:1194
void eulery(const double &angle)
Set Euler rotation matrix around y axis.
Definition GMatrix.cpp:1157
Sky model class.
GModelSpatial * spatial(void) const
Return spatial model component.
Abstract elliptical spatial model base class.
const GSkyDir & dir(void) const
Return position of elliptical spatial model.
virtual double theta_max(void) const =0
Point source spatial model.
Abstract radial spatial model base class.
virtual double theta_max(void) const =0
const GSkyDir & dir(void) const
Return position of radial spatial model.
virtual double eval(const GPhoton &photon, const bool &gradients=false) const =0
Abstract observation base class.
virtual GEvents * events(void)
Return events.
virtual double deadc(const GTime &time=GTime()) const =0
void name(const std::string &name)
Set observation name.
void id(const std::string &id)
Set observation identifier.
Class that handles photons.
Definition GPhoton.hpp:47
const GTime & time(void) const
Return photon time.
Definition GPhoton.hpp:134
const GSkyDir & dir(void) const
Return photon sky direction.
Definition GPhoton.hpp:110
Abstract instrument response base class.
Definition GResponse.hpp:77
void init_members(void)
Initialise class members.
void free_members(void)
Delete class members.
virtual GResponse & operator=(const GResponse &rsp)
Assignment operator.
Sky direction class.
Definition GSkyDir.hpp:62
double dec_deg(void) const
Returns Declination in degrees.
Definition GSkyDir.hpp:256
double ra_deg(void) const
Returns Right Ascension in degrees.
Definition GSkyDir.hpp:229
double dist_deg(const GSkyDir &dir) const
Compute angular distance between sky directions in degrees.
Definition GSkyDir.hpp:286
void rotate(const double &phi, const double &theta)
Rotate sky direction by zenith and azimuth angle.
Definition GSkyDir.cpp:378
double dist(const GSkyDir &dir) const
Compute angular distance between sky directions in radians.
Definition GSkyDir.hpp:271
Sky map class.
Definition GSkyMap.hpp:89
bool contains(const GSkyDir &dir) const
Checks if sky direction falls in map.
Definition GSkyMap.cpp:2023
const double * pixels(void) const
Returns pointer to pixel data.
Definition GSkyMap.hpp:475
Time class.
Definition GTime.hpp:55
Vector class.
Definition GVector.hpp:46
Kernel for rho angle integration of elliptical models.
Kernel for rho angle integration of radial models.
Defintion of COMPTEL helper classes for vector response.
std::string parformat(const std::string &s, const int &indent=0)
Convert string in parameter format.
Definition GTools.cpp:1143
bool is_infinite(const double &x)
Signal if argument is infinite.
Definition GTools.hpp:184
bool is_notanumber(const double &x)
Signal if argument is not a number.
Definition GTools.hpp:201
std::string str(const unsigned short int &value)
Convert unsigned short integer value into string.
Definition GTools.cpp:489
std::string filepath(const std::string &pathname, const std::string &filename)
Build file path from path name and file name.
Definition GTools.cpp:393
const double deg2rad
Definition GMath.hpp:43
const double fourpi
Definition GMath.hpp:37
const double twopi
Definition GMath.hpp:36