Commit 0c70d44e9ef42b58a3ed881ec2b18a58da57607b

Authored by Jürgen Knödlseder
1 parent e8719183

Add "addbounds" parameter to response cube tools

cscripts/cspull.py
... ... @@ -20,6 +20,7 @@
20 20 # ==========================================================================
21 21 import sys
22 22 import csv
  23 +import math
23 24 import gammalib
24 25 import ctools
25 26 from cscripts import obsutils
... ... @@ -163,6 +164,12 @@ class cspull(ctools.cscript):
163 164 self._log('\n')
164 165 self._log.header1('Compute stacked response')
165 166  
  167 + # Set number of energy bins to at least 30 per energy decade
  168 + enumbins = int((math.log10(self['emax'].real()) -
  169 + math.log10(self['emin'].real())) * 30.0)
  170 + if self._enumbins > enumbins:
  171 + enumbins = self._enumbins
  172 +
166 173 # Get stacked exposure
167 174 expcube = ctools.ctexpcube(self._obs)
168 175 expcube['incube'] = 'NONE'
... ... @@ -171,7 +178,7 @@ class cspull(ctools.cscript):
171 178 expcube['binsz'] = self._binsz
172 179 expcube['nxpix'] = self._npix
173 180 expcube['nypix'] = self._npix
174   - expcube['enumbins'] = self._enumbins
  181 + expcube['enumbins'] = enumbins
175 182 expcube['emin'] = self['emin'].real()
176 183 expcube['emax'] = self['emax'].real()
177 184 expcube['coordsys'] = self._coordsys
... ... @@ -195,7 +202,7 @@ class cspull(ctools.cscript):
195 202 psfcube['binsz'] = self._binsz*10.0
196 203 psfcube['nxpix'] = self._npix/10
197 204 psfcube['nypix'] = self._npix/10
198   - psfcube['enumbins'] = self._enumbins
  205 + psfcube['enumbins'] = enumbins
199 206 psfcube['emin'] = self['emin'].real()
200 207 psfcube['emax'] = self['emax'].real()
201 208 psfcube['coordsys'] = self._coordsys
... ... @@ -220,7 +227,7 @@ class cspull(ctools.cscript):
220 227 edispcube['binsz'] = self._binsz*10.0
221 228 edispcube['nxpix'] = self._npix/10
222 229 edispcube['nypix'] = self._npix/10
223   - edispcube['enumbins'] = self._enumbins
  230 + edispcube['enumbins'] = enumbins
224 231 edispcube['emin'] = self['emin'].real()
225 232 edispcube['emax'] = self['emax'].real()
226 233 edispcube['coordsys'] = self._coordsys
... ... @@ -244,7 +251,7 @@ class cspull(ctools.cscript):
244 251 bkgcube['binsz'] = self._binsz
245 252 bkgcube['nxpix'] = self._npix
246 253 bkgcube['nypix'] = self._npix
247   - bkgcube['enumbins'] = self._enumbins
  254 + bkgcube['enumbins'] = enumbins
248 255 bkgcube['emin'] = self['emin'].real()
249 256 bkgcube['emax'] = self['emax'].real()
250 257 bkgcube['coordsys'] = self._coordsys
... ...
doc/source/users/reference_manual/ctbkgcube.rst
... ... @@ -25,10 +25,14 @@ cube as ``incube`` parameter. This will instruct ctbkgcube to extract the
25 25 background cube definition (such as sky coordinates and projection, number
26 26 of pixels, pixel scale, energy binning) from the counts cube.
27 27  
  28 +Note that there should at least be 25 bins per energy decade in the background
  29 +cube to assure that the energy dependence of the background rate is sufficiently
  30 +well sampled (in particular at low energies).
  31 +
28 32 ctbkgcube generates a background cube FITS file comprising two extensions.
29 33 The primary extension contains a 3-dimensional image that contains the
30   -background cube values. The next extension named ``EBOUNDS`` contains a
31   -binary table that defines the energy boundaries of the background cube.
  34 +background cube values. The next extension named ``ENERGIES`` contains a
  35 +binary table that defines the energies of the background cube.
32 36  
33 37 ctbkgcube generates also an output model XML file that can serve as input
34 38 for a maximum likelihood analysis. The output model XML file is a copy of
... ... @@ -82,7 +86,10 @@ General parameters
82 86  
83 87 ``ebinfile [file]``
84 88 Name of the file containing the energy bin definition.
85   -
  89 +
  90 +``(addbounds = yes) [boolean]``
  91 + Add energies to the background cube at the observation energy boundaries?
  92 +
86 93 ``(usepnt = no) [boolean]``
87 94 Use CTA pointing direction for cube centre instead of xref/yref parameters?
88 95  
... ...
doc/source/users/reference_manual/ctedispcube.rst
... ... @@ -29,13 +29,12 @@ rather coarse spatial binning to keep the file size manageable (with a typical
29 29 value of ``binsz=1.0``).
30 30  
31 31 ctedispcube generates an energy dispersion cube FITS file comprising three
32   -extensions. The primary extension contains a 3-dimensional image that contains
33   -the energy disperison values. The energy and fraction dimensions of the enrgy
34   -dispersion cube are folded into the 3rd dimension of the FITS image. The next
35   -extension named ``ENERGIES`` contains a binary table that defines the energy
36   -values of the energy dispersion cube. The last extension named ``MIGRAS``
37   -contains a binary table that defines the migration values of the cube which
38   -is the ratio between reconstructed and true photon energy.
  32 +extensions. The primary extension contains a 4-dimensional image that contains
  33 +the energy disperison values. The next extension named ``ENERGIES`` contains
  34 +a binary table that defines the energies of the energy dispersion cube. The
  35 +last extension named ``MIGRAS`` contains a binary table that defines the
  36 +migration values of the cube which is the ratio between reconstructed and true
  37 +photon energy.
39 38  
40 39  
41 40 General parameters
... ... @@ -71,6 +70,9 @@ General parameters
71 70 ``ebinfile [file]``
72 71 Name of the file containing the energy bin definition.
73 72  
  73 +``(addbounds = yes) [boolean]``
  74 + Add energies to the energy dispersion cube at the observation energy boundaries?
  75 +
74 76 ``(usepnt = no) [boolean]``
75 77 Use CTA pointing direction for cube centre instead of xref/yref parameters?
76 78  
... ...
doc/source/users/reference_manual/ctexpcube.rst
... ... @@ -20,7 +20,7 @@ that has been used in the generation of the counts cube using :doc:`ctbin`.
20 20  
21 21 The binning of an exposure cube does not need to correspond to the binning
22 22 of a counts cube, as exposure values will in any case be determined by linear
23   -interpolation. For the user's convenience, howver, the counts cube can be
  23 +interpolation. For the user's convenience, however, the counts cube can be
24 24 specified as ``incube`` parameter which will instruct ctexpcube to extract
25 25 the exposure cube definition (such as sky coordinates and projection, number
26 26 of pixels, pixel scale, energy binning) from the counts cube. Note, however,
... ... @@ -32,13 +32,16 @@ due to the point spread function and energy dispersion. By computing the
32 32 exposure cube on the same grid as the counts cube, the spill over of events
33 33 from sources at the edge of cube will not be handled correctly.
34 34  
  35 +Note that there should at least be 25 bins per energy decade in the exposure
  36 +cube to assure that the energy dependence of the exposure is sufficiently
  37 +well sampled (in particular at low energies).
  38 +
35 39 ctexpcube generates an exposure cube FITS file comprising three extensions.
36 40 The primary extension contains a 3-dimensional image that contains the
37   -exposure values. The next extension named ``EBOUNDS`` contains a binary table
38   -that defines the energy boundaries of the exposure cube. The last extension
39   -named ``GTI`` contains a binary table that provides the Good Time Intervals
40   -of all observations that have been used for the computation of the exposure
41   -cube.
  41 +exposure values. The next extension named ``ENERGIES`` contains a binary table
  42 +that defines the energies of the exposure cube. The last extension named ``GTI``
  43 +contains a binary table that provides the Good Time Intervals of all
  44 +observations that have been used for the computation of the exposure cube.
42 45  
43 46  
44 47 General parameters
... ... @@ -73,7 +76,10 @@ General parameters
73 76  
74 77 ``ebinfile [file]``
75 78 Name of the file containing the energy bin definition.
76   -
  79 +
  80 +``(addbounds = yes) [boolean]``
  81 + Add energies to the exposure cube at the observation energy boundaries?
  82 +
77 83 ``(usepnt = no) [boolean]``
78 84 Use CTA pointing direction for cube centre instead of xref/yref parameters?
79 85  
... ...
doc/source/users/reference_manual/ctpsfcube.rst
... ... @@ -29,13 +29,11 @@ recommended to use a rather coarse spatial binning to keep the file size
29 29 manageable (with a typical value of ``binsz=1.0``).
30 30  
31 31 ctpsfcube generates a point spread function cube FITS file comprising three
32   -extensions. The primary extension contains a 3-dimensional image that contains
33   -the point spread function values. The energy and offset angle dimensions
34   -of the point spread function cube are folded into the 3rd dimension of the
35   -FITS image. The next extension named ``EBOUNDS`` contains a binary table
36   -that defines the energy boundaries of the exposure cube. The last extension
37   -named ``DELTAS`` contains a binary table that defines the offset angles
38   -between true and measured arrival direction of the photon.
  32 +extensions. The primary extension contains a 4-dimensional image that contains
  33 +the point spread function values. The next extension named ``ENERGIES`` contains
  34 +a binary table that defines the energies of the point spread function cube.
  35 +The last extension named ``DELTAS`` contains a binary table that defines the
  36 +offset angles between true and measured arrival direction of the photon.
39 37  
40 38  
41 39 General parameters
... ... @@ -71,6 +69,9 @@ General parameters
71 69 ``ebinfile [file]``
72 70 Name of the file containing the energy bin definition.
73 71  
  72 +``(addbounds = yes) [boolean]``
  73 + Add energies to the point spread function cube at the observation energy boundaries?
  74 +
74 75 ``(usepnt = no) [boolean]``
75 76 Use CTA pointing direction for cube centre instead of xref/yref parameters?
76 77  
... ...
src/ctbkgcube/ctbkgcube.cpp
... ... @@ -217,23 +217,46 @@ void ctbkgcube::run(void)
217 217 log << std::endl;
218 218 }
219 219  
  220 + // Warn if there are not enough energy bins
  221 + int n = m_background.energies().size();
  222 + double logEmin = std::log10(m_background.energies()[0].TeV());
  223 + double logEmax = std::log10(m_background.energies()[n-1].TeV());
  224 + int nrequired = int((logEmax - logEmin) * 25.0);
  225 + if ((n < nrequired) && logTerse()) {
  226 + log << std::endl;
  227 + log << "WARNING: ";
  228 + log << "Only " << n-1 << " energy bins have been requested. This may be "
  229 + "too few energy bins.";
  230 + log << std::endl << " ";
  231 + log << "At least 25 bins per decade in energy are recommended, which "
  232 + "in your case";
  233 + log << std::endl << " ";
  234 + log << "would be " << nrequired-1 << " bins. Consider increasing the "
  235 + "number of energy bins.";
  236 + log << std::endl;
  237 + }
  238 +
  239 + // Write header
  240 + if (logTerse()) {
  241 + log << std::endl;
  242 + log.header1("Initialise background cube");
  243 + }
  244 +
  245 + // Initialise exposure cube
  246 + init_cube();
  247 +
220 248 // Write observation(s) into logger
221 249 if (logTerse()) {
222 250 log << std::endl;
223   - if (m_obs.size() > 1) {
224   - log.header1("Observations");
225   - }
226   - else {
227   - log.header1("Observation");
228   - }
229   - log << m_obs << std::endl;
  251 + log.header1(gammalib::number("Observation",m_obs.size()));
  252 + log << m_obs.print(m_chatter) << std::endl;
230 253 }
231 254  
232 255 // Log input model
233 256 if (logTerse()) {
234 257 log << std::endl;
235 258 log.header1("Input model");
236   - log << m_obs.models() << std::endl;
  259 + log << m_obs.models().print(m_chatter) << std::endl;
237 260 }
238 261  
239 262 // Write header
... ... @@ -330,6 +353,11 @@ void ctbkgcube::run(void)
330 353 // Append model to output container
331 354 m_outmdl.append(model);
332 355  
  356 + // Log background cube
  357 + if (logTerse()) {
  358 + log << m_background.print(m_chatter) << std::endl;
  359 + }
  360 +
333 361 // Log output model
334 362 if (logTerse()) {
335 363 log << std::endl;
... ... @@ -445,10 +473,14 @@ void ctbkgcube::publish(const std::string&amp; name)
445 473 ***************************************************************************/
446 474 void ctbkgcube::init_members(void)
447 475 {
448   - // Initialise members
  476 + // Initialise user parameters
449 477 m_outcube.clear();
450 478 m_outmodel.clear();
451   - m_publish = false;
  479 + m_addbounds = true;
  480 + m_publish = false;
  481 + m_chatter = static_cast<GChatter>(2);
  482 +
  483 + // Initialise protected members
452 484 m_obs.clear();
453 485 m_background.clear();
454 486 m_bkgmdl.clear();
... ... @@ -466,10 +498,14 @@ void ctbkgcube::init_members(void)
466 498 ***************************************************************************/
467 499 void ctbkgcube::copy_members(const ctbkgcube& app)
468 500 {
469   - // Copy members
470   - m_outmodel = app.m_outmodel;
471   - m_outcube = app.m_outcube;
472   - m_publish = app.m_publish;
  501 + // Copy user parameters
  502 + m_outmodel = app.m_outmodel;
  503 + m_outcube = app.m_outcube;
  504 + m_addbounds = app.m_addbounds;
  505 + m_publish = app.m_publish;
  506 + m_chatter = app.m_chatter;
  507 +
  508 + // Copy protected members
473 509 m_obs = app.m_obs;
474 510 m_background = app.m_background;
475 511 m_bkgmdl = app.m_bkgmdl;
... ... @@ -558,7 +594,9 @@ void ctbkgcube::get_parameters(void)
558 594 }
559 595  
560 596 // Get remaining parameters
561   - m_publish = (*this)["publish"].boolean();
  597 + m_addbounds = (*this)["addbounds"].boolean();
  598 + m_publish = (*this)["publish"].boolean();
  599 + m_chatter = static_cast<GChatter>((*this)["chatter"].integer());
562 600  
563 601 // Read output filenames (if needed)
564 602 if (read_ahead()) {
... ... @@ -569,3 +607,70 @@ void ctbkgcube::get_parameters(void)
569 607 // Return
570 608 return;
571 609 }
  610 +
  611 +
  612 +/***********************************************************************//**
  613 + * @brief Initialise background cube
  614 + *
  615 + * Initialise the background cube.
  616 + ***************************************************************************/
  617 +void ctbkgcube::init_cube(void)
  618 +{
  619 + // Extract exposure cube definition
  620 + const GWcs* proj = static_cast<const GWcs*>(m_background.cube().projection());
  621 + std::string wcs = m_background.cube().projection()->code();
  622 + std::string coords = m_background.cube().projection()->coordsys();
  623 + double x = proj->crval(0);
  624 + double y = proj->crval(1);
  625 + double dx = proj->cdelt(0);
  626 + double dy = proj->cdelt(1);
  627 + int nx = m_background.cube().nx();
  628 + int ny = m_background.cube().ny();
  629 +
  630 + // Extract energies
  631 + GEnergies energies = m_background.energies();
  632 +
  633 + // If requested, insert energies at all event list energy boundaries
  634 + if (m_addbounds) {
  635 +
  636 + // Set logger
  637 + GLog* logger = NULL;
  638 + if (logTerse()) {
  639 + logger = &log;
  640 + }
  641 +
  642 + // Loop over all observations
  643 + for (int i = 0; i < m_obs.size(); ++i) {
  644 +
  645 + // Get observation and continue only if it is a CTA observation
  646 + const GCTAObservation* cta = dynamic_cast<const GCTAObservation*>
  647 + (m_obs[i]);
  648 +
  649 + // Skip observation if it's not a CTA observation
  650 + if (cta == NULL) {
  651 + continue;
  652 + }
  653 +
  654 + // Skip observation if it does not contain an event list
  655 + if (cta->eventtype() != "EventList") {
  656 + continue;
  657 + }
  658 +
  659 + // Insert energy boundaries
  660 + energies = insert_energy_boundaries(energies, *cta, logger);
  661 +
  662 + } // endfor: looped over all observations
  663 +
  664 + } // endif: energy bin insertion requested
  665 +
  666 + // Setup background cube
  667 + m_background = GCTACubeBackground(wcs, coords, x, y, dx, dy, nx, ny, energies);
  668 +
  669 + // Log background cube
  670 + if (logTerse()) {
  671 + log << m_background.print(m_chatter) << std::endl;
  672 + }
  673 +
  674 + // Return
  675 + return;
  676 +};
... ...
src/ctbkgcube/ctbkgcube.hpp
... ... @@ -69,12 +69,15 @@ protected:
69 69 void copy_members(const ctbkgcube& app);
70 70 void free_members(void);
71 71 void get_parameters(void);
72   - void fill_cube(GCTAObservation* obs);
  72 + void init_cube(void);
  73 + //void fill_cube(GCTAObservation* obs);
73 74  
74 75 // User parameters
75 76 GFilename m_outcube; //!< Filename of output cube
76 77 GFilename m_outmodel; //!< Filename of output XML model
  78 + bool m_addbounds; //!< Add energies at boundaries?
77 79 bool m_publish; //!< Publish background cube?
  80 + GChatter m_chatter; //!< Chattiness
78 81  
79 82 // Protected members
80 83 GObservations m_obs; //!< Observation container
... ...
src/ctbkgcube/ctbkgcube.par
... ... @@ -28,11 +28,12 @@ outmodel, f, a, &quot;NONE&quot;,,, &quot;Output model XML file&quot;
28 28 #
29 29 # Energy binning parameters
30 30 #==========================
31   -ebinalg, s, h, "LOG",FILE|LIN|LOG,, "Algorithm for defining energy bins"
32   -emin, r, a, 0.1,,, "Lower energy limit (TeV)"
33   -emax, r, a, 100.0,,, "Upper energy limit (TeV)"
34   -enumbins, i, a, 20,,, "Number of energy bins"
35   -ebinfile, f, h, "NONE",,, "Name of the file containing the energy bin definition"
  31 +ebinalg, s, h, "LOG",FILE|LIN|LOG,, "Algorithm for defining energy bins"
  32 +emin, r, a, 0.1,,, "Lower energy limit (TeV)"
  33 +emax, r, a, 100.0,,, "Upper energy limit (TeV)"
  34 +enumbins, i, a, 20,,, "Number of energy bins"
  35 +ebinfile, f, h, "NONE",,, "Name of the file containing the energy bin definition"
  36 +addbounds, b, h, yes,,, "Add energies at observation energy boundaries?"
36 37  
37 38 #
38 39 # Spatial binning parameters
... ...
src/ctedispcube/ctedispcube.cpp
... ... @@ -229,16 +229,20 @@ void ctedispcube::run(void)
229 229 log << std::endl;
230 230 }
231 231  
  232 + // Write header
  233 + if (logTerse()) {
  234 + log << std::endl;
  235 + log.header1("Initialise energy dispersion cube");
  236 + }
  237 +
  238 + // Initialise energy dispersion cube
  239 + init_cube();
  240 +
232 241 // Write observation(s) into logger
233 242 if (logTerse()) {
234 243 log << std::endl;
235   - if (m_obs.size() > 1) {
236   - log.header1("Observations");
237   - }
238   - else {
239   - log.header1("Observation");
240   - }
241   - log << m_obs << std::endl;
  244 + log.header1(gammalib::number("Observation",m_obs.size()));
  245 + log << m_obs.print(m_chatter) << std::endl;
242 246 }
243 247  
244 248 // Write header
... ... @@ -247,9 +251,14 @@ void ctedispcube::run(void)
247 251 log.header1("Generate energy dispersion cube");
248 252 }
249 253  
250   - // Fill Edisp
  254 + // Fill energy dispersion cube
251 255 m_edispcube.fill(m_obs, &log);
252 256  
  257 + // Log energy dispersion cube
  258 + if (logTerse()) {
  259 + log << m_edispcube.print(m_chatter) << std::endl;
  260 + }
  261 +
253 262 // Return
254 263 return;
255 264 }
... ... @@ -302,8 +311,12 @@ void ctedispcube::save(void)
302 311 ***************************************************************************/
303 312 void ctedispcube::init_members(void)
304 313 {
305   - // Initialise members
  314 + // Initialise user parameters
306 315 m_outcube.clear();
  316 + m_addbounds = true;
  317 + m_chatter = static_cast<GChatter>(2);
  318 +
  319 + // Initialise protected members
307 320 m_obs.clear();
308 321 m_edispcube.clear();
309 322  
... ... @@ -319,8 +332,12 @@ void ctedispcube::init_members(void)
319 332 ***************************************************************************/
320 333 void ctedispcube::copy_members(const ctedispcube& app)
321 334 {
322   - // Copy members
  335 + // Copy user parameters
323 336 m_outcube = app.m_outcube;
  337 + m_addbounds = app.m_addbounds;
  338 + m_chatter = app.m_chatter;
  339 +
  340 + // Copy protected members
324 341 m_obs = app.m_obs;
325 342 m_edispcube = app.m_edispcube;
326 343  
... ... @@ -380,7 +397,7 @@ void ctedispcube::get_parameters(void)
380 397  
381 398 }
382 399  
383   - // ... otherwise setup the exposure cube from the counts map
  400 + // ... otherwise setup the energy disperison cube from the counts map
384 401 else {
385 402  
386 403 // Load event cube from filename
... ... @@ -391,6 +408,10 @@ void ctedispcube::get_parameters(void)
391 408  
392 409 } // endelse: cube loaded from file
393 410  
  411 + // Get remaining parameters
  412 + m_addbounds = (*this)["addbounds"].boolean();
  413 + m_chatter = static_cast<GChatter>((*this)["chatter"].integer());
  414 +
394 415 // Read output filename (if needed)
395 416 if (read_ahead()) {
396 417 m_outcube = (*this)["outcube"].filename();
... ... @@ -399,3 +420,73 @@ void ctedispcube::get_parameters(void)
399 420 // Return
400 421 return;
401 422 }
  423 +
  424 +
  425 +/***********************************************************************//**
  426 + * @brief Initialise energy dispersion cube
  427 + *
  428 + * Initialise the energy dispersion cube.
  429 + ***************************************************************************/
  430 +void ctedispcube::init_cube(void)
  431 +{
  432 + // Extract energy dispersion definition
  433 + const GWcs* proj = static_cast<const GWcs*>(m_edispcube.cube().projection());
  434 + std::string wcs = m_edispcube.cube().projection()->code();
  435 + std::string coords = m_edispcube.cube().projection()->coordsys();
  436 + double x = proj->crval(0);
  437 + double y = proj->crval(1);
  438 + double dx = proj->cdelt(0);
  439 + double dy = proj->cdelt(1);
  440 + int nx = m_edispcube.cube().nx();
  441 + int ny = m_edispcube.cube().ny();
  442 + double mmax = (*this)["migramax"].real();
  443 + int nmbins = (*this)["migrabins"].integer();
  444 +
  445 + // Extract energies
  446 + GEnergies energies = m_edispcube.energies();
  447 +
  448 + // If requested, insert energies at all event list energy boundaries
  449 + if (m_addbounds) {
  450 +
  451 + // Set logger
  452 + GLog* logger = NULL;
  453 + if (logTerse()) {
  454 + logger = &log;
  455 + }
  456 +
  457 + // Loop over all observations
  458 + for (int i = 0; i < m_obs.size(); ++i) {
  459 +
  460 + // Get observation and continue only if it is a CTA observation
  461 + const GCTAObservation* cta = dynamic_cast<const GCTAObservation*>
  462 + (m_obs[i]);
  463 +
  464 + // Skip observation if it's not a CTA observation
  465 + if (cta == NULL) {
  466 + continue;
  467 + }
  468 +
  469 + // Skip observation if it does not contain an event list
  470 + if (cta->eventtype() != "EventList") {
  471 + continue;
  472 + }
  473 +
  474 + // Insert energy boundaries
  475 + energies = insert_energy_boundaries(energies, *cta, logger);
  476 +
  477 + } // endfor: looped over all observations
  478 +
  479 + } // endif: energy bin insertion requested
  480 +
  481 + // Setup energy dispersion cube
  482 + m_edispcube = GCTACubeEdisp(wcs, coords, x, y, dx, dy, nx, ny, energies,
  483 + mmax, nmbins);
  484 +
  485 + // Log energy dispersion cube
  486 + if (logTerse()) {
  487 + log << m_edispcube.print(m_chatter) << std::endl;
  488 + }
  489 +
  490 + // Return
  491 + return;
  492 +};
... ...
src/ctedispcube/ctedispcube.hpp
... ... @@ -67,9 +67,12 @@ protected:
67 67 void copy_members(const ctedispcube& app);
68 68 void free_members(void);
69 69 void get_parameters(void);
  70 + void init_cube(void);
70 71  
71 72 // User parameters
72   - GFilename m_outcube; //!< Output energy dispersion cube file name
  73 + GFilename m_outcube; //!< Output exposure cube file
  74 + bool m_addbounds; //!< Add energies at boundaries?
  75 + GChatter m_chatter; //!< Chattiness
73 76  
74 77 // Protected members
75 78 GObservations m_obs; //!< Observation container
... ...
src/ctedispcube/ctedispcube.par
... ... @@ -26,11 +26,12 @@ outcube, f, a, &quot;edispcube.fits&quot;,,, &quot;Output energy dispersion cube file&quot;
26 26 #
27 27 # Energy binning parameters
28 28 #==========================
29   -ebinalg, s, h, "LOG", FILE|LIN|LOG,, "Algorithm for defining energy bins"
30   -emin, r, a, 0.1,,, "Lower energy limit (TeV)"
31   -emax, r, a, 100.0,,, "Upper energy limit (TeV)"
32   -enumbins, i, a, 20,,, "Number of energy bins"
33   -ebinfile, f, h, "NONE",,, "Name of the file containing the energy bin definition"
  29 +ebinalg, s, h, "LOG", FILE|LIN|LOG,, "Algorithm for defining energy bins"
  30 +emin, r, a, 0.1,,, "Lower energy limit (TeV)"
  31 +emax, r, a, 100.0,,, "Upper energy limit (TeV)"
  32 +enumbins, i, a, 20,,, "Number of energy bins"
  33 +ebinfile, f, h, "NONE",,, "Name of the file containing the energy bin definition"
  34 +addbounds, b, h, yes,,, "Add energies at observation energy boundaries?"
34 35  
35 36 #
36 37 # Spatial binning parameters
... ...
src/ctexpcube/ctexpcube.cpp
... ... @@ -217,11 +217,39 @@ void ctexpcube::run(void)
217 217 log << std::endl;
218 218 }
219 219  
  220 + // Warn if there are not enough energy bins
  221 + int n = m_expcube.energies().size();
  222 + double logEmin = std::log10(m_expcube.energies()[0].TeV());
  223 + double logEmax = std::log10(m_expcube.energies()[n-1].TeV());
  224 + int nrequired = int((logEmax - logEmin) * 25.0);
  225 + if ((n < nrequired) && logTerse()) {
  226 + log << std::endl;
  227 + log << "WARNING: ";
  228 + log << "Only " << n-1 << " energy bins have been requested. This may be "
  229 + "too few energy bins.";
  230 + log << std::endl << " ";
  231 + log << "At least 25 bins per decade in energy are recommended, which "
  232 + "in your case";
  233 + log << std::endl << " ";
  234 + log << "would be " << nrequired-1 << " bins. Consider increasing the "
  235 + "number of energy bins.";
  236 + log << std::endl;
  237 + }
  238 +
  239 + // Write header
  240 + if (logTerse()) {
  241 + log << std::endl;
  242 + log.header1("Initialise exposure cube");
  243 + }
  244 +
  245 + // Initialise exposure cube
  246 + init_cube();
  247 +
220 248 // Write observation(s) into logger
221 249 if (logTerse()) {
222 250 log << std::endl;
223 251 log.header1(gammalib::number("Observation",m_obs.size()));
224   - log << m_obs << std::endl;
  252 + log << m_obs.print(m_chatter) << std::endl;
225 253 }
226 254  
227 255 // Write header
... ... @@ -230,9 +258,14 @@ void ctexpcube::run(void)
230 258 log.header1("Generate exposure cube");
231 259 }
232 260  
233   - // Fill exposure
  261 + // Fill exposure cube
234 262 m_expcube.fill(m_obs, &log);
235 263  
  264 + // Log exposure cube
  265 + if (logTerse()) {
  266 + log << m_expcube.print(m_chatter) << std::endl;
  267 + }
  268 +
236 269 // Optionally publish exposure cube
237 270 if (m_publish) {
238 271 publish();
... ... @@ -321,9 +354,11 @@ void ctexpcube::publish(const std::string&amp; name)
321 354 ***************************************************************************/
322 355 void ctexpcube::init_members(void)
323 356 {
324   - // Initialise members
  357 + // Initialise user parameters
325 358 m_outcube.clear();
326   - m_publish = false;
  359 + m_addbounds = true;
  360 + m_publish = false;
  361 + m_chatter = static_cast<GChatter>(2);
327 362  
328 363 // Initialise protected members
329 364 m_obs.clear();
... ... @@ -341,9 +376,11 @@ void ctexpcube::init_members(void)
341 376 ***************************************************************************/
342 377 void ctexpcube::copy_members(const ctexpcube& app)
343 378 {
344   - // Copy attributes
345   - m_outcube = app.m_outcube;
346   - m_publish = app.m_publish;
  379 + // Copy user parameters
  380 + m_outcube = app.m_outcube;
  381 + m_addbounds = app.m_addbounds;
  382 + m_publish = app.m_publish;
  383 + m_chatter = app.m_chatter;
347 384  
348 385 // Copy protected members
349 386 m_obs = app.m_obs;
... ... @@ -413,7 +450,9 @@ void ctexpcube::get_parameters(void)
413 450 } // endelse: cube loaded from file
414 451  
415 452 // Get remaining parameters
416   - m_publish = (*this)["publish"].boolean();
  453 + m_addbounds = (*this)["addbounds"].boolean();
  454 + m_publish = (*this)["publish"].boolean();
  455 + m_chatter = static_cast<GChatter>((*this)["chatter"].integer());
417 456  
418 457 // Read output filename (if needed)
419 458 if (read_ahead()) {
... ... @@ -423,3 +462,70 @@ void ctexpcube::get_parameters(void)
423 462 // Return
424 463 return;
425 464 }
  465 +
  466 +
  467 +/***********************************************************************//**
  468 + * @brief Initialise exposure cube
  469 + *
  470 + * Initialise the exposure cube.
  471 + ***************************************************************************/
  472 +void ctexpcube::init_cube(void)
  473 +{
  474 + // Extract exposure cube definition
  475 + const GWcs* proj = static_cast<const GWcs*>(m_expcube.cube().projection());
  476 + std::string wcs = m_expcube.cube().projection()->code();
  477 + std::string coords = m_expcube.cube().projection()->coordsys();
  478 + double x = proj->crval(0);
  479 + double y = proj->crval(1);
  480 + double dx = proj->cdelt(0);
  481 + double dy = proj->cdelt(1);
  482 + int nx = m_expcube.cube().nx();
  483 + int ny = m_expcube.cube().ny();
  484 +
  485 + // Extract energies
  486 + GEnergies energies = m_expcube.energies();
  487 +
  488 + // If requested, insert energies at all event list energy boundaries
  489 + if (m_addbounds) {
  490 +
  491 + // Set logger
  492 + GLog* logger = NULL;
  493 + if (logTerse()) {
  494 + logger = &log;
  495 + }
  496 +
  497 + // Loop over all observations
  498 + for (int i = 0; i < m_obs.size(); ++i) {
  499 +
  500 + // Get observation and continue only if it is a CTA observation
  501 + const GCTAObservation* cta = dynamic_cast<const GCTAObservation*>
  502 + (m_obs[i]);
  503 +
  504 + // Skip observation if it's not a CTA observation
  505 + if (cta == NULL) {
  506 + continue;
  507 + }
  508 +
  509 + // Skip observation if it does not contain an event list
  510 + if (cta->eventtype() != "EventList") {
  511 + continue;
  512 + }
  513 +
  514 + // Insert energy boundaries
  515 + energies = insert_energy_boundaries(energies, *cta, logger);
  516 +
  517 + } // endfor: looped over all observations
  518 +
  519 + } // endif: energy bin insertion requested
  520 +
  521 + // Setup exposure cube
  522 + m_expcube = GCTACubeExposure(wcs, coords, x, y, dx, dy, nx, ny, energies);
  523 +
  524 + // Log exposure cube
  525 + if (logTerse()) {
  526 + log << m_expcube.print(m_chatter) << std::endl;
  527 + }
  528 +
  529 + // Return
  530 + return;
  531 +};
... ...
src/ctexpcube/ctexpcube.hpp
... ... @@ -68,10 +68,13 @@ protected:
68 68 void copy_members(const ctexpcube& app);
69 69 void free_members(void);
70 70 void get_parameters(void);
  71 + void init_cube(void);
71 72  
72 73 // User parameters
73   - GFilename m_outcube; //!< Output exposure cube file
74   - bool m_publish; //!< Publish exposure cube?
  74 + GFilename m_outcube; //!< Output exposure cube file
  75 + bool m_addbounds; //!< Add energies at boundaries?
  76 + bool m_publish; //!< Publish exposure cube?
  77 + GChatter m_chatter; //!< Chattiness
75 78  
76 79 // Protected members
77 80 GObservations m_obs; //!< Observation container
... ...
src/ctexpcube/ctexpcube.par
... ... @@ -26,11 +26,12 @@ outcube, f, a, &quot;expcube.fits&quot;,,, &quot;Output exposure cube file&quot;
26 26 #
27 27 # Energy binning parameters
28 28 #==========================
29   -ebinalg, s, h, "LOG", FILE|LIN|LOG,,"Algorithm for defining energy bins"
30   -emin, r, a, 0.1,,,"Lower energy limit (TeV)"
31   -emax, r, a, 100.0,,, "Upper energy limit (TeV)"
32   -enumbins, i, a, 20,,, "Number of energy bins"
33   -ebinfile, f, h, "NONE",,, "Name of the file containing the energy bin definition"
  29 +ebinalg, s, h, "LOG", FILE|LIN|LOG,,"Algorithm for defining energy bins"
  30 +emin, r, a, 0.1,,,"Lower energy limit (TeV)"
  31 +emax, r, a, 100.0,,, "Upper energy limit (TeV)"
  32 +enumbins, i, a, 20,,, "Number of energy bins"
  33 +ebinfile, f, h, "NONE",,, "Name of the file containing the energy bin definition"
  34 +addbounds, b, h, yes,,, "Add energies at observation energy boundaries?"
34 35  
35 36 #
36 37 # Spatial binning parameters
... ...
src/ctpsfcube/ctpsfcube.cpp
... ... @@ -216,16 +216,20 @@ void ctpsfcube::run(void)
216 216 log << std::endl;
217 217 }
218 218  
  219 + // Write header
  220 + if (logTerse()) {
  221 + log << std::endl;
  222 + log.header1("Initialise PSF cube");
  223 + }
  224 +
  225 + // Initialise exposure cube
  226 + init_cube();
  227 +
219 228 // Write observation(s) into logger
220 229 if (logTerse()) {
221 230 log << std::endl;
222   - if (m_obs.size() > 1) {
223   - log.header1("Observations");
224   - }
225   - else {
226   - log.header1("Observation");
227   - }
228   - log << m_obs << std::endl;
  231 + log.header1(gammalib::number("Observation",m_obs.size()));
  232 + log << m_obs.print(m_chatter) << std::endl;
229 233 }
230 234  
231 235 // Write header
... ... @@ -234,9 +238,14 @@ void ctpsfcube::run(void)
234 238 log.header1("Generate PSF cube");
235 239 }
236 240  
237   - // Fill PSF
  241 + // Fill PSF cube
238 242 m_psfcube.fill(m_obs, &log);
239 243  
  244 + // Log PSF cube
  245 + if (logTerse()) {
  246 + log << m_psfcube.print(m_chatter) << std::endl;
  247 + }
  248 +
240 249 // Return
241 250 return;
242 251 }
... ... @@ -286,8 +295,10 @@ void ctpsfcube::save(void)
286 295 ***************************************************************************/
287 296 void ctpsfcube::init_members(void)
288 297 {
289   - // Initialise members
  298 + // Initialise user parameters
290 299 m_outcube.clear();
  300 + m_addbounds = true;
  301 + m_chatter = static_cast<GChatter>(2);
291 302  
292 303 // Initialise protected members
293 304 m_obs.clear();
... ... @@ -305,12 +316,14 @@ void ctpsfcube::init_members(void)
305 316 ***************************************************************************/
306 317 void ctpsfcube::copy_members(const ctpsfcube& app)
307 318 {
308   - // Copy attributes
309   - m_outcube = app.m_outcube;
  319 + // Copy user parameters
  320 + m_outcube = app.m_outcube;
  321 + m_addbounds = app.m_addbounds;
  322 + m_chatter = app.m_chatter;
310 323  
311 324 // Copy protected members
312   - m_obs = app.m_obs;
313   - m_psfcube = app.m_psfcube;
  325 + m_obs = app.m_obs;
  326 + m_psfcube = app.m_psfcube;
314 327  
315 328 // Return
316 329 return;
... ... @@ -379,6 +392,10 @@ void ctpsfcube::get_parameters(void)
379 392  
380 393 } // endelse: cube loaded from file
381 394  
  395 + // Get remaining parameters
  396 + m_addbounds = (*this)["addbounds"].boolean();
  397 + m_chatter = static_cast<GChatter>((*this)["chatter"].integer());
  398 +
382 399 // Read output filename (if needed)
383 400 if (read_ahead()) {
384 401 m_outcube = (*this)["outcube"].filename();
... ... @@ -387,3 +404,73 @@ void ctpsfcube::get_parameters(void)
387 404 // Return
388 405 return;
389 406 }
  407 +
  408 +
  409 +/***********************************************************************//**
  410 + * @brief Initialise PSF cube
  411 + *
  412 + * Initialise the PSF cube.
  413 + ***************************************************************************/
  414 +void ctpsfcube::init_cube(void)
  415 +{
  416 + // Extract exposure cube definition
  417 + const GWcs* proj = static_cast<const GWcs*>(m_psfcube.cube().projection());
  418 + std::string wcs = m_psfcube.cube().projection()->code();
  419 + std::string coords = m_psfcube.cube().projection()->coordsys();
  420 + double x = proj->crval(0);
  421 + double y = proj->crval(1);
  422 + double dx = proj->cdelt(0);
  423 + double dy = proj->cdelt(1);
  424 + int nx = m_psfcube.cube().nx();
  425 + int ny = m_psfcube.cube().ny();
  426 + double dmax = (*this)["amax"].real();
  427 + int ndbins = (*this)["anumbins"].integer();
  428 +
  429 + // Extract energies
  430 + GEnergies energies = m_psfcube.energies();
  431 +
  432 + // If requested, insert energies at all event list energy boundaries
  433 + if (m_addbounds) {
  434 +
  435 + // Set logger
  436 + GLog* logger = NULL;
  437 + if (logTerse()) {
  438 + logger = &log;
  439 + }
  440 +
  441 + // Loop over all observations
  442 + for (int i = 0; i < m_obs.size(); ++i) {
  443 +
  444 + // Get observation and continue only if it is a CTA observation
  445 + const GCTAObservation* cta = dynamic_cast<const GCTAObservation*>
  446 + (m_obs[i]);
  447 +
  448 + // Skip observation if it's not a CTA observation
  449 + if (cta == NULL) {
  450 + continue;
  451 + }
  452 +
  453 + // Skip observation if it does not contain an event list
  454 + if (cta->eventtype() != "EventList") {
  455 + continue;
  456 + }
  457 +
  458 + // Insert energy boundaries
  459 + energies = insert_energy_boundaries(energies, *cta, logger);
  460 +
  461 + } // endfor: looped over all observations
  462 +
  463 + } // endif: energy bin insertion requested
  464 +
  465 + // Setup PSF cube
  466 + m_psfcube = GCTACubePsf(wcs, coords, x, y, dx, dy, nx, ny, energies,
  467 + dmax, ndbins);
  468 +
  469 + // Log PSF cube
  470 + if (logTerse()) {
  471 + log << m_psfcube.print(m_chatter) << std::endl;
  472 + }
  473 +
  474 + // Return
  475 + return;
  476 +};
... ...
src/ctpsfcube/ctpsfcube.hpp
... ... @@ -67,9 +67,12 @@ protected:
67 67 void copy_members(const ctpsfcube& app);
68 68 void free_members(void);
69 69 void get_parameters(void);
  70 + void init_cube(void);
70 71  
71 72 // User parameters
72   - GFilename m_outcube; //!< Output PSF cube file name
  73 + GFilename m_outcube; //!< Output PSF cube file name
  74 + bool m_addbounds; //!< Add energies at boundaries?
  75 + GChatter m_chatter; //!< Chattiness
73 76  
74 77 // Protected members
75 78 GObservations m_obs; //!< Observation container
... ...
src/ctpsfcube/ctpsfcube.par
... ... @@ -5,7 +5,7 @@
5 5 # ------------------------------------------------------------------------- #
6 6 # #
7 7 # File: ctpsfcube.par #
8   -# Version: 1.0.0 #
  8 +# Version: 1.1.0 #
9 9 # #
10 10 # Author: Chia-Chun Lu #
11 11 # c.lu@mpi-hd.mpg.de #
... ... @@ -26,11 +26,12 @@ outcube, f, a, &quot;psfcube.fits&quot;,,, &quot;Output PSF cube file&quot;
26 26 #
27 27 # Energy binning parameters
28 28 #==========================
29   -ebinalg, s, h, "LOG", FILE|LIN|LOG,, "Algorithm for defining energy bins"
30   -emin, r, a, 0.1,,, "Lower energy limit (TeV)"
31   -emax, r, a, 100.0,,, "Upper energy limit (TeV)"
32   -enumbins, i, a, 20,,, "Number of energy bins"
33   -ebinfile, f, h, "NONE",,, "Name of the file containing the energy bin definition"
  29 +ebinalg, s, h, "LOG", FILE|LIN|LOG,, "Algorithm for defining energy bins"
  30 +emin, r, a, 0.1,,, "Lower energy limit (TeV)"
  31 +emax, r, a, 100.0,,, "Upper energy limit (TeV)"
  32 +enumbins, i, a, 20,,, "Number of energy bins"
  33 +ebinfile, f, h, "NONE",,, "Name of the file containing the energy bin definition"
  34 +addbounds, b, h, yes,,, "Add energies at observation energy boundaries?"
34 35  
35 36 #
36 37 # Spatial binning parameters
... ...
src/support/ctool.cpp
... ... @@ -1303,6 +1303,81 @@ std::string ctool::get_obs_header(const GObservation* obs)
1303 1303  
1304 1304  
1305 1305 /***********************************************************************//**
  1306 + * @brief Insert observation energy boundaries into list of energies
  1307 + *
  1308 + * @param[in] energies Energies.
  1309 + * @param[in] obs Observation container.
  1310 + * @return Energies.
  1311 + ***************************************************************************/
  1312 +GEnergies ctool::insert_energy_boundaries(const GEnergies& energies,
  1313 + const GCTAObservation& obs,
  1314 + GLog* log) const
  1315 +{
  1316 + // Create copy of input energies
  1317 + GEnergies engs = energies;
  1318 +
  1319 + // Get the energy boundaries of the event list
  1320 + GEbounds ebounds = obs.ebounds();
  1321 +
  1322 + // Loop over all boundary energies
  1323 + for (int iebin = 0; iebin <= ebounds.size(); ++iebin) {
  1324 +
  1325 + // Get boundary energy
  1326 + GEnergy energy;
  1327 + if (iebin < ebounds.size()) {
  1328 + energy = ebounds.emin(iebin);
  1329 + }
  1330 + else {
  1331 + energy = ebounds.emax();
  1332 + }
  1333 +
  1334 + // Get index before which the energy should be appended
  1335 + bool insert = true;
  1336 + int index = -1;
  1337 + for (int k = 0; k < engs.size(); ++k) {
  1338 +
  1339 + // If energy exists already then skip the boundary and examine the
  1340 + // next one. We consider here 1 MeV as being sufficiently close.
  1341 + if (std::abs(engs[k].MeV()-energy.MeV()) < 1.0) {
  1342 + insert = false;
  1343 + break;
  1344 + }
  1345 +
  1346 + // If energy is above the boundary energy we found the index
  1347 + if (engs[k] > energy) {
  1348 + index = k;
  1349 + break;
  1350 + }
  1351 +
  1352 + } // endfor: search index for insertion
  1353 +
  1354 + // Insert energy if requested
  1355 + if (insert) {
  1356 +
  1357 + // Insert or append energy
  1358 + if (index != -1) {
  1359 + engs.insert(index, energy);
  1360 + }
  1361 + else {
  1362 + engs.append(energy);
  1363 + }
  1364 +
  1365 + // Log energy insertion
  1366 + if (log != NULL) {
  1367 + *log << gammalib::parformat("Insert energy");
  1368 + *log << energy.print() << std::endl;
  1369 + }
  1370 +
  1371 + } // endif: energy insertion requested
  1372 +
  1373 + } // endfor: looped over all energy boundaries
  1374 +
  1375 + // Return energies
  1376 + return engs;
  1377 +}
  1378 +
  1379 +
  1380 +/***********************************************************************//**
1306 1381 * @brief Dumps help text in the console
1307 1382 ***************************************************************************/
1308 1383 void ctool::provide_help(void) const
... ...
src/support/ctool.hpp
... ... @@ -92,6 +92,9 @@ protected:
92 92 GSkyDir get_mean_pointing(const GObservations& obs);
93 93 size_t get_current_rss(void);
94 94 std::string get_obs_header(const GObservation* obs);
  95 + GEnergies insert_energy_boundaries(const GEnergies& energies,
  96 + const GCTAObservation& obs,
  97 + GLog* log = NULL) const;
95 98  
96 99 // Protected members
97 100 bool m_read_ahead; //!< Read ahead output parameters
... ...