Commit 4bd3d7d42bea706610c46aaf84bb7fe04344272a

Authored by Jürgen Knödlseder
1 parent 12d04917

GCTAAeff2D no longer extrapolates beyond response table boundaries (#1784)

ChangeLog
... ... @@ -3,6 +3,7 @@
3 3 * Version 1.1.0 released
4 4 =========================
5 5  
  6 + GCTAAeff2D no longer extrapolates beyond response table (#1784)
6 7 Add GWcsSIN projection class (#1798)
7 8 CTA cube response no longer selects on RoI and energies (#1789)
8 9 Add weights to CTA event cube and event bin (#1789)
... ...
... ... @@ -416,6 +416,9 @@ The cube response classes GCTACubeExposure, GCTACubePsf, GCTACubeEdisp and
416 416 GCTACubeBackground do no longer select on RoI and energy boundaries as this is now
417 417 handled by the weight array (#1789).
418 418  
  419 +The GCTAAeff2D class no longer extrapolates the effective area beyond the boundaries
  420 +of the response table, but sets the effective area values to zero (#1784).
  421 +
419 422  
420 423 21. COMPTEL interface
421 424 ---------------------
... ...
inst/cta/include/GCTAAeff2D.hpp
... ... @@ -89,6 +89,8 @@ private:
89 89 void init_members(void);
90 90 void copy_members(const GCTAAeff2D& aeff);
91 91 void free_members(void);
  92 + void set_indices(void);
  93 + void set_boundaries(void);
92 94  
93 95 // Members
94 96 GFilename m_filename; //!< Name of Aeff response file
... ... @@ -97,6 +99,10 @@ private:
97 99 int m_inx_theta; //!< Theta index
98 100 int m_inx_aeff; //!< Effective area (true energy)
99 101 int m_inx_aeff_reco; //!< Effective area (reconstructed energy)
  102 + double m_logE_min; //!< Minimum logE (log10(E/TeV))
  103 + double m_logE_max; //!< Maximum logE (log10(E/TeV))
  104 + double m_theta_min; //!< Minimum theta (radians)
  105 + double m_theta_max; //!< Maximum theta (radians)
100 106 };
101 107  
102 108  
... ... @@ -120,7 +126,6 @@ std::string GCTAAeff2D::classname(void) const
120 126 inline
121 127 GFilename GCTAAeff2D::filename(void) const
122 128 {
123   - // Return filename
124 129 return m_filename;
125 130 }
126 131  
... ... @@ -129,6 +134,9 @@ GFilename GCTAAeff2D::filename(void) const
129 134 * @brief Return response table
130 135 *
131 136 * @return Response table.
  137 + *
  138 + * Returns the response table of the effective area. The effective area
  139 + * values are given in units of cm2.
132 140 ***************************************************************************/
133 141 inline
134 142 const GCTAResponseTable& GCTAAeff2D::table(void) const
... ... @@ -136,16 +144,4 @@ const GCTAResponseTable&amp; GCTAAeff2D::table(void) const
136 144 return m_aeff;
137 145 }
138 146  
139   -
140   -/***********************************************************************//**
141   - * @brief Assign response table
142   - *
143   - * @param[in] table Response table.
144   - ***************************************************************************/
145   -inline
146   -void GCTAAeff2D::table(const GCTAResponseTable& table)
147   -{
148   - m_aeff = table;
149   -}
150   -
151 147 #endif /* GCTAAEFF2D_HPP */
... ...
inst/cta/src/GCTAAeff2D.cpp
... ... @@ -29,6 +29,7 @@
29 29 #include <config.h>
30 30 #endif
31 31 #include "GTools.hpp"
  32 +#include "GMath.hpp"
32 33 #include "GException.hpp"
33 34 #include "GFilename.hpp"
34 35 #include "GFits.hpp"
... ... @@ -170,13 +171,14 @@ GCTAAeff2D&amp; GCTAAeff2D::operator=(const GCTAAeff2D&amp; aeff)
170 171 * @param[in] phi Azimuth angle in camera system (rad).
171 172 * @param[in] zenith Zenith angle in Earth system (rad).
172 173 * @param[in] azimuth Azimuth angle in Earth system (rad).
173   - * @param[in] etrue Use true energy (default: true).
  174 + * @param[in] etrue Use true energy?
174 175 * @return Effective area in cm2.
175 176 *
176 177 * Returns the effective area in units of cm2 for a given energy and
177 178 * offset angle. The effective area is bi-linearly interpolated in the
178 179 * log10(energy) - offset angle plane. The method assures that the effective
179   - * area value never becomes negative.
  180 + * area value never becomes negative. Outside the logE - theta range covered
  181 + * by the response table the effective area will be set to zero.
180 182 *
181 183 * The method supports true and reconstructed energies for logE. To access
182 184 * the effective area as function of true energy, specify etrue=true
... ... @@ -190,17 +192,26 @@ double GCTAAeff2D::operator()(const double&amp; logE,
190 192 const double& azimuth,
191 193 const bool& etrue) const
192 194 {
193   - // Set parameter index
194   - int index = (etrue) ? m_inx_aeff : m_inx_aeff_reco;
  195 + // Initialise effective area
  196 + double aeff(0.0);
195 197  
196   - // Get effective area value in cm2
197   - double aeff = (m_inx_energy == 0) ? m_aeff(index, logE, theta) :
198   - m_aeff(index, theta, logE);
  198 + // Continue only if logE and theta are in validity range
  199 + if ((logE >= m_logE_min) && (logE <= m_logE_max) &&
  200 + (theta >= m_theta_min) && (theta <= m_theta_max)) {
199 201  
200   - // Make sure that effective area is not negative
201   - if (aeff < 0.0) {
202   - aeff = 0.0;
203   - }
  202 + // Set parameter index
  203 + int index = (etrue) ? m_inx_aeff : m_inx_aeff_reco;
  204 +
  205 + // Get effective area value in cm2
  206 + aeff = (m_inx_energy == 0) ? m_aeff(index, logE, theta) :
  207 + m_aeff(index, theta, logE);
  208 +
  209 + // Make sure that effective area is not negative
  210 + if (aeff < 0.0) {
  211 + aeff = 0.0;
  212 + }
  213 +
  214 + } // endif: logE and theta in validity range
204 215  
205 216 // Return effective area value
206 217 return aeff;
... ... @@ -281,15 +292,6 @@ void GCTAAeff2D::read(const GFitsTable&amp; table)
281 292 // Read effective area table
282 293 m_aeff.read(table);
283 294  
284   - // Get mandatory indices (throw exception if not found)
285   - m_inx_energy = m_aeff.axis("ENERG");
286   - m_inx_theta = m_aeff.axis("THETA");
287   - m_inx_aeff = m_aeff.table("EFFAREA");
288   -
289   - // Get optional index (use "EFFAREA" if "EFFAREA_RECO" does not exist)
290   - m_inx_aeff_reco = (m_aeff.has_table("EFFAREA_RECO")) ?
291   - m_aeff.table("EFFAREA_RECO") : m_inx_aeff;
292   -
293 295 // Throw an exception if the table is not two-dimensional
294 296 if (m_aeff.axes() != 2) {
295 297 std::string msg = "Expected two-dimensional effective area response "
... ... @@ -299,6 +301,9 @@ void GCTAAeff2D::read(const GFitsTable&amp; table)
299 301 throw GException::invalid_value(G_READ, msg);
300 302 }
301 303  
  304 + // Set table indices
  305 + set_indices();
  306 +
302 307 // Set energy axis to logarithmic scale
303 308 m_aeff.axis_log10(m_inx_energy);
304 309  
... ... @@ -310,6 +315,9 @@ void GCTAAeff2D::read(const GFitsTable&amp; table)
310 315 m_aeff.scale(i, 1.0e4);
311 316 }
312 317  
  318 + // Set table boundaries
  319 + set_boundaries();
  320 +
313 321 // Return
314 322 return;
315 323 }
... ... @@ -347,10 +355,8 @@ void GCTAAeff2D::write(GFitsBinTable&amp; table) const
347 355 *
348 356 * @param[in] filename FITS file name.
349 357 *
350   - * Loads the effective area from a FITS file.
351   - *
352   - * If no extension name is provided, the effective area will be loaded from
353   - * the "EFFECTIVE AREA" extension.
  358 + * Loads the effective area from a FITS file. If no extension name is given
  359 + * the effective area will be loaded from the "EFFECTIVE AREA" extension.
354 360 ***************************************************************************/
355 361 void GCTAAeff2D::load(const GFilename& filename)
356 362 {
... ... @@ -375,15 +381,15 @@ void GCTAAeff2D::load(const GFilename&amp; filename)
375 381  
376 382  
377 383 /***********************************************************************//**
378   - * @brief Save effectiva area into FITS file
  384 + * @brief Save effective area into FITS file
379 385 *
380 386 * @param[in] filename FITS file name.
381   - * @param[in] clobber Overwrite existing file? (default: false)
  387 + * @param[in] clobber Overwrite existing file?
382 388 *
383 389 * Saves effectiva area into a FITS file. If a file with the given
384 390 * @p filename does not yet exist it will be created, otherwise the method
385 391 * opens the existing file. The method will create a (or replace an existing)
386   - * effectiva area extension. The extension name can be specified as part
  392 + * effective area extension. The extension name can be specified as part
387 393 * of the @p filename, or if no extension name is given, is assumed to be
388 394 * "EFFECTIVE AREA".
389 395 *
... ... @@ -430,7 +436,7 @@ void GCTAAeff2D::save(const GFilename&amp; filename, const bool&amp; clobber) const
430 436 * @param[in] logE Log10 of the true photon energy (TeV).
431 437 * @param[in] zenith Zenith angle in Earth system (rad).
432 438 * @param[in] azimuth Azimuth angle in Earth system (rad).
433   - * @param[in] etrue Use true energy (default: true).
  439 + * @param[in] etrue Use true energy?
434 440 * @return Maximum effective area (cm2).
435 441 *
436 442 * Returns the maximum effective area for a given energy, zenith and azimuth
... ... @@ -453,9 +459,9 @@ double GCTAAeff2D::max(const double&amp; logE,
453 459 // Loop over theta values
454 460 for (int i = 0; i < n_theta; ++i) {
455 461  
456   - // Compute lower and upper theta bin values
457   - double theta_lo = m_aeff.axis_lo(m_inx_theta, i);
458   - double theta_hi = m_aeff.axis_hi(m_inx_theta, i);
  462 + // Compute lower and upper theta bin values (radians)
  463 + double theta_lo = m_aeff.axis_lo(m_inx_theta, i) * gammalib::deg2rad;
  464 + double theta_hi = m_aeff.axis_hi(m_inx_theta, i) * gammalib::deg2rad;
459 465  
460 466 // Get effective area value in cm2
461 467 double aeff_lo = (m_inx_energy == 0) ? m_aeff(index, logE, theta_lo) :
... ... @@ -480,9 +486,39 @@ double GCTAAeff2D::max(const double&amp; logE,
480 486  
481 487  
482 488 /***********************************************************************//**
  489 + * @brief Assign response table
  490 + *
  491 + * @param[in] table Response table.
  492 + *
  493 + * Assigns the response table for an effective area. The effective area
  494 + * values are given in units of cm2.
  495 + ***************************************************************************/
  496 +void GCTAAeff2D::table(const GCTAResponseTable& table)
  497 +{
  498 + // Assign response table
  499 + m_aeff = table;
  500 +
  501 + // Set indices
  502 + set_indices();
  503 +
  504 + // Set energy axis to logarithmic scale
  505 + m_aeff.axis_log10(m_inx_energy);
  506 +
  507 + // Set offset angle axis to radians
  508 + m_aeff.axis_radians(m_inx_theta);
  509 +
  510 + // Set table boundaries
  511 + set_boundaries();
  512 +
  513 + // Return
  514 + return;
  515 +}
  516 +
  517 +
  518 +/***********************************************************************//**
483 519 * @brief Print effective area information
484 520 *
485   - * @param[in] chatter Chattiness (defaults to NORMAL).
  521 + * @param[in] chatter Chattiness.
486 522 * @return String containing effective area information.
487 523 ***************************************************************************/
488 524 std::string GCTAAeff2D::print(const GChatter& chatter) const
... ... @@ -542,6 +578,10 @@ void GCTAAeff2D::init_members(void)
542 578 m_inx_theta = 1;
543 579 m_inx_aeff = 0;
544 580 m_inx_aeff_reco = 1;
  581 + m_logE_min = 0.0;
  582 + m_logE_max = 0.0;
  583 + m_theta_min = 0.0;
  584 + m_theta_max = 0.0;
545 585  
546 586 // Return
547 587 return;
... ... @@ -562,6 +602,10 @@ void GCTAAeff2D::copy_members(const GCTAAeff2D&amp; aeff)
562 602 m_inx_theta = aeff.m_inx_theta;
563 603 m_inx_aeff = aeff.m_inx_aeff;
564 604 m_inx_aeff_reco = aeff.m_inx_aeff_reco;
  605 + m_logE_min = aeff.m_logE_min;
  606 + m_logE_max = aeff.m_logE_max;
  607 + m_theta_min = aeff.m_theta_min;
  608 + m_theta_max = aeff.m_theta_max;
565 609  
566 610 // Return
567 611 return;
... ... @@ -576,3 +620,50 @@ void GCTAAeff2D::free_members(void)
576 620 // Return
577 621 return;
578 622 }
  623 +
  624 +
  625 +/***********************************************************************//**
  626 + * @brief Set table indices
  627 + *
  628 + * Sets the data members m_inx_energy, m_inx_theta, m_inx_aeff and optionally
  629 + * m_inx_aeff_reco.
  630 + ***************************************************************************/
  631 +void GCTAAeff2D::set_indices(void)
  632 +{
  633 + // Get mandatory indices (throw exception if not found)
  634 + m_inx_energy = m_aeff.axis("ENERG");
  635 + m_inx_theta = m_aeff.axis("THETA");
  636 + m_inx_aeff = m_aeff.table("EFFAREA");
  637 +
  638 + // Get optional index (use "EFFAREA" if "EFFAREA_RECO" does not exist)
  639 + m_inx_aeff_reco = (m_aeff.has_table("EFFAREA_RECO")) ?
  640 + m_aeff.table("EFFAREA_RECO") : m_inx_aeff;
  641 +
  642 + // Return
  643 + return;
  644 +}
  645 +
  646 +
  647 +/***********************************************************************//**
  648 + * @brief Set effective area boundaries
  649 + *
  650 + * Sets the data members m_logE_min, m_logE_max, m_theta_min and m_theta_max
  651 + * that define the validity range of the effective area.
  652 + ***************************************************************************/
  653 +void GCTAAeff2D::set_boundaries(void)
  654 +{
  655 + // Compute minimum and maximum logE boundaries
  656 + m_logE_min = std::log10(m_aeff.axis_lo(m_inx_aeff, 0));
  657 + m_logE_max = std::log10(m_aeff.axis_hi(m_inx_aeff,
  658 + m_aeff.axis_bins(m_inx_aeff)-1));
  659 +
  660 + // Compute minimum and maximum theta boundaries
  661 + m_theta_min = m_aeff.axis_lo(m_inx_theta, 0) *
  662 + gammalib::deg2rad;
  663 + m_theta_max = m_aeff.axis_hi(m_inx_theta, m_aeff.axis_bins(m_inx_theta)-1) *
  664 + gammalib::deg2rad;
  665 +
  666 + // Return
  667 + return;
  668 +}
  669 +
... ...
inst/cta/test/test_CTA.py
... ... @@ -82,10 +82,28 @@ class Test(gammalib.GPythonTestSuite):
82 82 aeff = gammalib.GCTAAeff2D(filename)
83 83  
84 84 # Test Aeff values
85   - self.test_value(aeff(0.0, 0.0), 5535774176.75, 0.1)
86   - self.test_value(aeff(1.0, 0.0), 20732069462.7, 0.1)
87   - self.test_value(aeff(0.0, 0.01745), 5682897797.76, 0.1)
88   - self.test_value(aeff(1.0, 0.01745), 18446656815.1, 0.1)
  85 + self.test_value(aeff(0.0, 0.0), 5535774176.75, 0.1,
  86 + 'Test reference effective area value')
  87 + self.test_value(aeff(1.0, 0.0), 20732069462.7, 0.1,
  88 + 'Test reference effective area value')
  89 + self.test_value(aeff(0.0, 0.01745), 5682897797.76, 0.1,
  90 + 'Test reference effective area value')
  91 + self.test_value(aeff(1.0, 0.01745), 18446656815.1, 0.1,
  92 + 'Test reference effective area value')
  93 +
  94 + # Test that Aeff values outside boundaries are zero
  95 + self.test_value(aeff(-1.80001, 0.0), 0.0, 1.0e-6,
  96 + 'Test that effective area is zero for energy below'
  97 + ' minimum energy')
  98 + self.test_value(aeff(+2.20001, 0.0), 0.0, 1.0e-6,
  99 + 'Test that effective area is zero for energy above'
  100 + ' maximum energy')
  101 + self.test_value(aeff(0.0, -0.00001), 0.0, 1.0e-6,
  102 + 'Test that effective area is zero for offset angle'
  103 + ' below minimum offset angle')
  104 + self.test_value(aeff(0.0, 0.13963), 0.0, 1.0e-6,
  105 + 'Test that effective area is zero for offset angle'
  106 + ' above maximum offset angle')
89 107  
90 108 # Test GCTAAeffPerfTable file constructor
91 109 filename = self.caldb + '/cta_dummy_irf.dat'
... ...