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,6 +20,7 @@
20 # ========================================================================== 20 # ==========================================================================
21 import sys 21 import sys
22 import csv 22 import csv
  23 +import math
23 import gammalib 24 import gammalib
24 import ctools 25 import ctools
25 from cscripts import obsutils 26 from cscripts import obsutils
@@ -163,6 +164,12 @@ class cspull(ctools.cscript): @@ -163,6 +164,12 @@ class cspull(ctools.cscript):
163 self._log('\n') 164 self._log('\n')
164 self._log.header1('Compute stacked response') 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 # Get stacked exposure 173 # Get stacked exposure
167 expcube = ctools.ctexpcube(self._obs) 174 expcube = ctools.ctexpcube(self._obs)
168 expcube['incube'] = 'NONE' 175 expcube['incube'] = 'NONE'
@@ -171,7 +178,7 @@ class cspull(ctools.cscript): @@ -171,7 +178,7 @@ class cspull(ctools.cscript):
171 expcube['binsz'] = self._binsz 178 expcube['binsz'] = self._binsz
172 expcube['nxpix'] = self._npix 179 expcube['nxpix'] = self._npix
173 expcube['nypix'] = self._npix 180 expcube['nypix'] = self._npix
174 - expcube['enumbins'] = self._enumbins 181 + expcube['enumbins'] = enumbins
175 expcube['emin'] = self['emin'].real() 182 expcube['emin'] = self['emin'].real()
176 expcube['emax'] = self['emax'].real() 183 expcube['emax'] = self['emax'].real()
177 expcube['coordsys'] = self._coordsys 184 expcube['coordsys'] = self._coordsys
@@ -195,7 +202,7 @@ class cspull(ctools.cscript): @@ -195,7 +202,7 @@ class cspull(ctools.cscript):
195 psfcube['binsz'] = self._binsz*10.0 202 psfcube['binsz'] = self._binsz*10.0
196 psfcube['nxpix'] = self._npix/10 203 psfcube['nxpix'] = self._npix/10
197 psfcube['nypix'] = self._npix/10 204 psfcube['nypix'] = self._npix/10
198 - psfcube['enumbins'] = self._enumbins 205 + psfcube['enumbins'] = enumbins
199 psfcube['emin'] = self['emin'].real() 206 psfcube['emin'] = self['emin'].real()
200 psfcube['emax'] = self['emax'].real() 207 psfcube['emax'] = self['emax'].real()
201 psfcube['coordsys'] = self._coordsys 208 psfcube['coordsys'] = self._coordsys
@@ -220,7 +227,7 @@ class cspull(ctools.cscript): @@ -220,7 +227,7 @@ class cspull(ctools.cscript):
220 edispcube['binsz'] = self._binsz*10.0 227 edispcube['binsz'] = self._binsz*10.0
221 edispcube['nxpix'] = self._npix/10 228 edispcube['nxpix'] = self._npix/10
222 edispcube['nypix'] = self._npix/10 229 edispcube['nypix'] = self._npix/10
223 - edispcube['enumbins'] = self._enumbins 230 + edispcube['enumbins'] = enumbins
224 edispcube['emin'] = self['emin'].real() 231 edispcube['emin'] = self['emin'].real()
225 edispcube['emax'] = self['emax'].real() 232 edispcube['emax'] = self['emax'].real()
226 edispcube['coordsys'] = self._coordsys 233 edispcube['coordsys'] = self._coordsys
@@ -244,7 +251,7 @@ class cspull(ctools.cscript): @@ -244,7 +251,7 @@ class cspull(ctools.cscript):
244 bkgcube['binsz'] = self._binsz 251 bkgcube['binsz'] = self._binsz
245 bkgcube['nxpix'] = self._npix 252 bkgcube['nxpix'] = self._npix
246 bkgcube['nypix'] = self._npix 253 bkgcube['nypix'] = self._npix
247 - bkgcube['enumbins'] = self._enumbins 254 + bkgcube['enumbins'] = enumbins
248 bkgcube['emin'] = self['emin'].real() 255 bkgcube['emin'] = self['emin'].real()
249 bkgcube['emax'] = self['emax'].real() 256 bkgcube['emax'] = self['emax'].real()
250 bkgcube['coordsys'] = self._coordsys 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,10 +25,14 @@ cube as ``incube`` parameter. This will instruct ctbkgcube to extract the
25 background cube definition (such as sky coordinates and projection, number 25 background cube definition (such as sky coordinates and projection, number
26 of pixels, pixel scale, energy binning) from the counts cube. 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 ctbkgcube generates a background cube FITS file comprising two extensions. 32 ctbkgcube generates a background cube FITS file comprising two extensions.
29 The primary extension contains a 3-dimensional image that contains the 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 ctbkgcube generates also an output model XML file that can serve as input 37 ctbkgcube generates also an output model XML file that can serve as input
34 for a maximum likelihood analysis. The output model XML file is a copy of 38 for a maximum likelihood analysis. The output model XML file is a copy of
@@ -82,7 +86,10 @@ General parameters @@ -82,7 +86,10 @@ General parameters
82 86
83 ``ebinfile [file]`` 87 ``ebinfile [file]``
84 Name of the file containing the energy bin definition. 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 ``(usepnt = no) [boolean]`` 93 ``(usepnt = no) [boolean]``
87 Use CTA pointing direction for cube centre instead of xref/yref parameters? 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,13 +29,12 @@ rather coarse spatial binning to keep the file size manageable (with a typical
29 value of ``binsz=1.0``). 29 value of ``binsz=1.0``).
30 30
31 ctedispcube generates an energy dispersion cube FITS file comprising three 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 General parameters 40 General parameters
@@ -71,6 +70,9 @@ General parameters @@ -71,6 +70,9 @@ General parameters
71 ``ebinfile [file]`` 70 ``ebinfile [file]``
72 Name of the file containing the energy bin definition. 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 ``(usepnt = no) [boolean]`` 76 ``(usepnt = no) [boolean]``
75 Use CTA pointing direction for cube centre instead of xref/yref parameters? 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,7 +20,7 @@ that has been used in the generation of the counts cube using :doc:`ctbin`.
20 20
21 The binning of an exposure cube does not need to correspond to the binning 21 The binning of an exposure cube does not need to correspond to the binning
22 of a counts cube, as exposure values will in any case be determined by linear 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 specified as ``incube`` parameter which will instruct ctexpcube to extract 24 specified as ``incube`` parameter which will instruct ctexpcube to extract
25 the exposure cube definition (such as sky coordinates and projection, number 25 the exposure cube definition (such as sky coordinates and projection, number
26 of pixels, pixel scale, energy binning) from the counts cube. Note, however, 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,13 +32,16 @@ due to the point spread function and energy dispersion. By computing the
32 exposure cube on the same grid as the counts cube, the spill over of events 32 exposure cube on the same grid as the counts cube, the spill over of events
33 from sources at the edge of cube will not be handled correctly. 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 ctexpcube generates an exposure cube FITS file comprising three extensions. 39 ctexpcube generates an exposure cube FITS file comprising three extensions.
36 The primary extension contains a 3-dimensional image that contains the 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 General parameters 47 General parameters
@@ -73,7 +76,10 @@ General parameters @@ -73,7 +76,10 @@ General parameters
73 76
74 ``ebinfile [file]`` 77 ``ebinfile [file]``
75 Name of the file containing the energy bin definition. 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 ``(usepnt = no) [boolean]`` 83 ``(usepnt = no) [boolean]``
78 Use CTA pointing direction for cube centre instead of xref/yref parameters? 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,13 +29,11 @@ recommended to use a rather coarse spatial binning to keep the file size
29 manageable (with a typical value of ``binsz=1.0``). 29 manageable (with a typical value of ``binsz=1.0``).
30 30
31 ctpsfcube generates a point spread function cube FITS file comprising three 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 General parameters 39 General parameters
@@ -71,6 +69,9 @@ General parameters @@ -71,6 +69,9 @@ General parameters
71 ``ebinfile [file]`` 69 ``ebinfile [file]``
72 Name of the file containing the energy bin definition. 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 ``(usepnt = no) [boolean]`` 75 ``(usepnt = no) [boolean]``
75 Use CTA pointing direction for cube centre instead of xref/yref parameters? 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,23 +217,46 @@ void ctbkgcube::run(void)
217 log << std::endl; 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 // Write observation(s) into logger 248 // Write observation(s) into logger
221 if (logTerse()) { 249 if (logTerse()) {
222 log << std::endl; 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 // Log input model 255 // Log input model
233 if (logTerse()) { 256 if (logTerse()) {
234 log << std::endl; 257 log << std::endl;
235 log.header1("Input model"); 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 // Write header 262 // Write header
@@ -330,6 +353,11 @@ void ctbkgcube::run(void) @@ -330,6 +353,11 @@ void ctbkgcube::run(void)
330 // Append model to output container 353 // Append model to output container
331 m_outmdl.append(model); 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 // Log output model 361 // Log output model
334 if (logTerse()) { 362 if (logTerse()) {
335 log << std::endl; 363 log << std::endl;
@@ -445,10 +473,14 @@ void ctbkgcube::publish(const std::string&amp; name) @@ -445,10 +473,14 @@ void ctbkgcube::publish(const std::string&amp; name)
445 ***************************************************************************/ 473 ***************************************************************************/
446 void ctbkgcube::init_members(void) 474 void ctbkgcube::init_members(void)
447 { 475 {
448 - // Initialise members 476 + // Initialise user parameters
449 m_outcube.clear(); 477 m_outcube.clear();
450 m_outmodel.clear(); 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 m_obs.clear(); 484 m_obs.clear();
453 m_background.clear(); 485 m_background.clear();
454 m_bkgmdl.clear(); 486 m_bkgmdl.clear();
@@ -466,10 +498,14 @@ void ctbkgcube::init_members(void) @@ -466,10 +498,14 @@ void ctbkgcube::init_members(void)
466 ***************************************************************************/ 498 ***************************************************************************/
467 void ctbkgcube::copy_members(const ctbkgcube& app) 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 m_obs = app.m_obs; 509 m_obs = app.m_obs;
474 m_background = app.m_background; 510 m_background = app.m_background;
475 m_bkgmdl = app.m_bkgmdl; 511 m_bkgmdl = app.m_bkgmdl;
@@ -558,7 +594,9 @@ void ctbkgcube::get_parameters(void) @@ -558,7 +594,9 @@ void ctbkgcube::get_parameters(void)
558 } 594 }
559 595
560 // Get remaining parameters 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 // Read output filenames (if needed) 601 // Read output filenames (if needed)
564 if (read_ahead()) { 602 if (read_ahead()) {
@@ -569,3 +607,70 @@ void ctbkgcube::get_parameters(void) @@ -569,3 +607,70 @@ void ctbkgcube::get_parameters(void)
569 // Return 607 // Return
570 return; 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,12 +69,15 @@ protected:
69 void copy_members(const ctbkgcube& app); 69 void copy_members(const ctbkgcube& app);
70 void free_members(void); 70 void free_members(void);
71 void get_parameters(void); 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 // User parameters 75 // User parameters
75 GFilename m_outcube; //!< Filename of output cube 76 GFilename m_outcube; //!< Filename of output cube
76 GFilename m_outmodel; //!< Filename of output XML model 77 GFilename m_outmodel; //!< Filename of output XML model
  78 + bool m_addbounds; //!< Add energies at boundaries?
77 bool m_publish; //!< Publish background cube? 79 bool m_publish; //!< Publish background cube?
  80 + GChatter m_chatter; //!< Chattiness
78 81
79 // Protected members 82 // Protected members
80 GObservations m_obs; //!< Observation container 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,11 +28,12 @@ outmodel, f, a, &quot;NONE&quot;,,, &quot;Output model XML file&quot;
28 # 28 #
29 # Energy binning parameters 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 # Spatial binning parameters 39 # Spatial binning parameters
src/ctedispcube/ctedispcube.cpp
@@ -229,16 +229,20 @@ void ctedispcube::run(void) @@ -229,16 +229,20 @@ void ctedispcube::run(void)
229 log << std::endl; 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 // Write observation(s) into logger 241 // Write observation(s) into logger
233 if (logTerse()) { 242 if (logTerse()) {
234 log << std::endl; 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 // Write header 248 // Write header
@@ -247,9 +251,14 @@ void ctedispcube::run(void) @@ -247,9 +251,14 @@ void ctedispcube::run(void)
247 log.header1("Generate energy dispersion cube"); 251 log.header1("Generate energy dispersion cube");
248 } 252 }
249 253
250 - // Fill Edisp 254 + // Fill energy dispersion cube
251 m_edispcube.fill(m_obs, &log); 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 // Return 262 // Return
254 return; 263 return;
255 } 264 }
@@ -302,8 +311,12 @@ void ctedispcube::save(void) @@ -302,8 +311,12 @@ void ctedispcube::save(void)
302 ***************************************************************************/ 311 ***************************************************************************/
303 void ctedispcube::init_members(void) 312 void ctedispcube::init_members(void)
304 { 313 {
305 - // Initialise members 314 + // Initialise user parameters
306 m_outcube.clear(); 315 m_outcube.clear();
  316 + m_addbounds = true;
  317 + m_chatter = static_cast<GChatter>(2);
  318 +
  319 + // Initialise protected members
307 m_obs.clear(); 320 m_obs.clear();
308 m_edispcube.clear(); 321 m_edispcube.clear();
309 322
@@ -319,8 +332,12 @@ void ctedispcube::init_members(void) @@ -319,8 +332,12 @@ void ctedispcube::init_members(void)
319 ***************************************************************************/ 332 ***************************************************************************/
320 void ctedispcube::copy_members(const ctedispcube& app) 333 void ctedispcube::copy_members(const ctedispcube& app)
321 { 334 {
322 - // Copy members 335 + // Copy user parameters
323 m_outcube = app.m_outcube; 336 m_outcube = app.m_outcube;
  337 + m_addbounds = app.m_addbounds;
  338 + m_chatter = app.m_chatter;
  339 +
  340 + // Copy protected members
324 m_obs = app.m_obs; 341 m_obs = app.m_obs;
325 m_edispcube = app.m_edispcube; 342 m_edispcube = app.m_edispcube;
326 343
@@ -380,7 +397,7 @@ void ctedispcube::get_parameters(void) @@ -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 else { 401 else {
385 402
386 // Load event cube from filename 403 // Load event cube from filename
@@ -391,6 +408,10 @@ void ctedispcube::get_parameters(void) @@ -391,6 +408,10 @@ void ctedispcube::get_parameters(void)
391 408
392 } // endelse: cube loaded from file 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 // Read output filename (if needed) 415 // Read output filename (if needed)
395 if (read_ahead()) { 416 if (read_ahead()) {
396 m_outcube = (*this)["outcube"].filename(); 417 m_outcube = (*this)["outcube"].filename();
@@ -399,3 +420,73 @@ void ctedispcube::get_parameters(void) @@ -399,3 +420,73 @@ void ctedispcube::get_parameters(void)
399 // Return 420 // Return
400 return; 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,9 +67,12 @@ protected:
67 void copy_members(const ctedispcube& app); 67 void copy_members(const ctedispcube& app);
68 void free_members(void); 68 void free_members(void);
69 void get_parameters(void); 69 void get_parameters(void);
  70 + void init_cube(void);
70 71
71 // User parameters 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 // Protected members 77 // Protected members
75 GObservations m_obs; //!< Observation container 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,11 +26,12 @@ outcube, f, a, &quot;edispcube.fits&quot;,,, &quot;Output energy dispersion cube file&quot;
26 # 26 #
27 # Energy binning parameters 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 # Spatial binning parameters 37 # Spatial binning parameters
src/ctexpcube/ctexpcube.cpp
@@ -217,11 +217,39 @@ void ctexpcube::run(void) @@ -217,11 +217,39 @@ void ctexpcube::run(void)
217 log << std::endl; 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 // Write observation(s) into logger 248 // Write observation(s) into logger
221 if (logTerse()) { 249 if (logTerse()) {
222 log << std::endl; 250 log << std::endl;
223 log.header1(gammalib::number("Observation",m_obs.size())); 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 // Write header 255 // Write header
@@ -230,9 +258,14 @@ void ctexpcube::run(void) @@ -230,9 +258,14 @@ void ctexpcube::run(void)
230 log.header1("Generate exposure cube"); 258 log.header1("Generate exposure cube");
231 } 259 }
232 260
233 - // Fill exposure 261 + // Fill exposure cube
234 m_expcube.fill(m_obs, &log); 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 // Optionally publish exposure cube 269 // Optionally publish exposure cube
237 if (m_publish) { 270 if (m_publish) {
238 publish(); 271 publish();
@@ -321,9 +354,11 @@ void ctexpcube::publish(const std::string&amp; name) @@ -321,9 +354,11 @@ void ctexpcube::publish(const std::string&amp; name)
321 ***************************************************************************/ 354 ***************************************************************************/
322 void ctexpcube::init_members(void) 355 void ctexpcube::init_members(void)
323 { 356 {
324 - // Initialise members 357 + // Initialise user parameters
325 m_outcube.clear(); 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 // Initialise protected members 363 // Initialise protected members
329 m_obs.clear(); 364 m_obs.clear();
@@ -341,9 +376,11 @@ void ctexpcube::init_members(void) @@ -341,9 +376,11 @@ void ctexpcube::init_members(void)
341 ***************************************************************************/ 376 ***************************************************************************/
342 void ctexpcube::copy_members(const ctexpcube& app) 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 // Copy protected members 385 // Copy protected members
349 m_obs = app.m_obs; 386 m_obs = app.m_obs;
@@ -413,7 +450,9 @@ void ctexpcube::get_parameters(void) @@ -413,7 +450,9 @@ void ctexpcube::get_parameters(void)
413 } // endelse: cube loaded from file 450 } // endelse: cube loaded from file
414 451
415 // Get remaining parameters 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 // Read output filename (if needed) 457 // Read output filename (if needed)
419 if (read_ahead()) { 458 if (read_ahead()) {
@@ -423,3 +462,70 @@ void ctexpcube::get_parameters(void) @@ -423,3 +462,70 @@ void ctexpcube::get_parameters(void)
423 // Return 462 // Return
424 return; 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,10 +68,13 @@ protected:
68 void copy_members(const ctexpcube& app); 68 void copy_members(const ctexpcube& app);
69 void free_members(void); 69 void free_members(void);
70 void get_parameters(void); 70 void get_parameters(void);
  71 + void init_cube(void);
71 72
72 // User parameters 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 // Protected members 79 // Protected members
77 GObservations m_obs; //!< Observation container 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,11 +26,12 @@ outcube, f, a, &quot;expcube.fits&quot;,,, &quot;Output exposure cube file&quot;
26 # 26 #
27 # Energy binning parameters 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 # Spatial binning parameters 37 # Spatial binning parameters
src/ctpsfcube/ctpsfcube.cpp
@@ -216,16 +216,20 @@ void ctpsfcube::run(void) @@ -216,16 +216,20 @@ void ctpsfcube::run(void)
216 log << std::endl; 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 // Write observation(s) into logger 228 // Write observation(s) into logger
220 if (logTerse()) { 229 if (logTerse()) {
221 log << std::endl; 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 // Write header 235 // Write header
@@ -234,9 +238,14 @@ void ctpsfcube::run(void) @@ -234,9 +238,14 @@ void ctpsfcube::run(void)
234 log.header1("Generate PSF cube"); 238 log.header1("Generate PSF cube");
235 } 239 }
236 240
237 - // Fill PSF 241 + // Fill PSF cube
238 m_psfcube.fill(m_obs, &log); 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 // Return 249 // Return
241 return; 250 return;
242 } 251 }
@@ -286,8 +295,10 @@ void ctpsfcube::save(void) @@ -286,8 +295,10 @@ void ctpsfcube::save(void)
286 ***************************************************************************/ 295 ***************************************************************************/
287 void ctpsfcube::init_members(void) 296 void ctpsfcube::init_members(void)
288 { 297 {
289 - // Initialise members 298 + // Initialise user parameters
290 m_outcube.clear(); 299 m_outcube.clear();
  300 + m_addbounds = true;
  301 + m_chatter = static_cast<GChatter>(2);
291 302
292 // Initialise protected members 303 // Initialise protected members
293 m_obs.clear(); 304 m_obs.clear();
@@ -305,12 +316,14 @@ void ctpsfcube::init_members(void) @@ -305,12 +316,14 @@ void ctpsfcube::init_members(void)
305 ***************************************************************************/ 316 ***************************************************************************/
306 void ctpsfcube::copy_members(const ctpsfcube& app) 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 // Copy protected members 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 // Return 328 // Return
316 return; 329 return;
@@ -379,6 +392,10 @@ void ctpsfcube::get_parameters(void) @@ -379,6 +392,10 @@ void ctpsfcube::get_parameters(void)
379 392
380 } // endelse: cube loaded from file 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 // Read output filename (if needed) 399 // Read output filename (if needed)
383 if (read_ahead()) { 400 if (read_ahead()) {
384 m_outcube = (*this)["outcube"].filename(); 401 m_outcube = (*this)["outcube"].filename();
@@ -387,3 +404,73 @@ void ctpsfcube::get_parameters(void) @@ -387,3 +404,73 @@ void ctpsfcube::get_parameters(void)
387 // Return 404 // Return
388 return; 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,9 +67,12 @@ protected:
67 void copy_members(const ctpsfcube& app); 67 void copy_members(const ctpsfcube& app);
68 void free_members(void); 68 void free_members(void);
69 void get_parameters(void); 69 void get_parameters(void);
  70 + void init_cube(void);
70 71
71 // User parameters 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 // Protected members 77 // Protected members
75 GObservations m_obs; //!< Observation container 78 GObservations m_obs; //!< Observation container
src/ctpsfcube/ctpsfcube.par
@@ -5,7 +5,7 @@ @@ -5,7 +5,7 @@
5 # ------------------------------------------------------------------------- # 5 # ------------------------------------------------------------------------- #
6 # # 6 # #
7 # File: ctpsfcube.par # 7 # File: ctpsfcube.par #
8 -# Version: 1.0.0 # 8 +# Version: 1.1.0 #
9 # # 9 # #
10 # Author: Chia-Chun Lu # 10 # Author: Chia-Chun Lu #
11 # c.lu@mpi-hd.mpg.de # 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,11 +26,12 @@ outcube, f, a, &quot;psfcube.fits&quot;,,, &quot;Output PSF cube file&quot;
26 # 26 #
27 # Energy binning parameters 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 # Spatial binning parameters 37 # Spatial binning parameters
src/support/ctool.cpp
@@ -1303,6 +1303,81 @@ std::string ctool::get_obs_header(const GObservation* obs) @@ -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 * @brief Dumps help text in the console 1381 * @brief Dumps help text in the console
1307 ***************************************************************************/ 1382 ***************************************************************************/
1308 void ctool::provide_help(void) const 1383 void ctool::provide_help(void) const
src/support/ctool.hpp
@@ -92,6 +92,9 @@ protected: @@ -92,6 +92,9 @@ protected:
92 GSkyDir get_mean_pointing(const GObservations& obs); 92 GSkyDir get_mean_pointing(const GObservations& obs);
93 size_t get_current_rss(void); 93 size_t get_current_rss(void);
94 std::string get_obs_header(const GObservation* obs); 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 // Protected members 99 // Protected members
97 bool m_read_ahead; //!< Read ahead output parameters 100 bool m_read_ahead; //!< Read ahead output parameters