Commit f8c119e28148efb29d91812af4d1abee2f351b90

Authored by Jürgen Knödlseder
2 parents e86c3204 cba4f988

Merge branch 'integration' into devel

@@ -15,5 +15,6 @@ Michael Mayer (michael.mayer@physik.hu-berlin.de) @@ -15,5 +15,6 @@ Michael Mayer (michael.mayer@physik.hu-berlin.de)
15 Stefan Ohm (stefan.ohm81@gmail.com) 15 Stefan Ohm (stefan.ohm81@gmail.com)
16 David Sanchez (david.sanchez@lapp.in2p3.fr) 16 David Sanchez (david.sanchez@lapp.in2p3.fr)
17 Hubert Siejkowski (h.siejkowski@cyfronet.pl) 17 Hubert Siejkowski (h.siejkowski@cyfronet.pl)
  18 +Andreas Specovius (andreas.specovius@fau.de)
18 Luigi Tibaldo (luigi.tibaldo@irap.omp.eu) 19 Luigi Tibaldo (luigi.tibaldo@irap.omp.eu)
19 Alexander Ziegler (alexander.ziegler@fau.de) 20 Alexander Ziegler (alexander.ziegler@fau.de)
ChangeLog
1 -2018-01-09 1 +2018-01-12
2 2
3 * Version 1.5.0 released 3 * Version 1.5.0 released
4 ======================== 4 ========================
5 5
  6 + Add CUSTOM background method to csphagen script (#2284)
6 Add csresspec script (#2289) 7 Add csresspec script (#2289)
7 Add ctools::time_reference constant and remove ctools::time_reference() method (#2295) 8 Add ctools::time_reference constant and remove ctools::time_reference() method (#2295)
8 Correctly strip .gz suffix from output filenames (#2287) 9 Correctly strip .gz suffix from output filenames (#2287)
1 -New Features and Important Changes in ctools 1.5.0  
2 -  
3 -9 January 2018 1 +12 January 2018
4 2
5 3
6 Introduction 4 Introduction
@@ -228,6 +226,11 @@ None @@ -228,6 +226,11 @@ None
228 226
229 csphagen - Generate On/Off observations 227 csphagen - Generate On/Off observations
230 --------------------------------------- 228 ---------------------------------------
  229 +The CUSTOM background method was added to allow specifying On and Off regions
  230 +from a region file or FITS WCS map. For multiple observations, a custom Off
  231 +region per observation can be specified in the observation definition XML file
  232 +using the OffRegions parameter (#2284).
  233 +
231 The script was added to allow the generation of PHA, ARF and RMF datasets for classical On/Off IACT analysis (#2208). 234 The script was added to allow the generation of PHA, ARF and RMF datasets for classical On/Off IACT analysis (#2208).
232 235
233 236
cscripts/Makefile.am
1 ############################################################################# 1 #############################################################################
2 # Makefile for cscripts scripts # 2 # Makefile for cscripts scripts #
3 # ------------------------------------------------------------------------- # 3 # ------------------------------------------------------------------------- #
4 -# Copyright (C) 2016-2017 Juergen Knoedlseder # 4 +# Copyright (C) 2016-2018 Juergen Knoedlseder #
5 # ------------------------------------------------------------------------- # 5 # ------------------------------------------------------------------------- #
6 # # 6 # #
7 # This program is free software: you can redistribute it and/or modify # 7 # This program is free software: you can redistribute it and/or modify #
@@ -172,6 +172,8 @@ testdata = $(top_srcdir)/test/data/crab.xml \ @@ -172,6 +172,8 @@ testdata = $(top_srcdir)/test/data/crab.xml \
172 $(top_srcdir)/test/data/crab_offaxis1.fits.gz \ 172 $(top_srcdir)/test/data/crab_offaxis1.fits.gz \
173 $(top_srcdir)/test/data/crab_offaxis2.fits.gz \ 173 $(top_srcdir)/test/data/crab_offaxis2.fits.gz \
174 $(top_srcdir)/test/data/crab_exclusion.fits.gz \ 174 $(top_srcdir)/test/data/crab_exclusion.fits.gz \
  175 + $(top_srcdir)/test/data/crab_src_reg.reg \
  176 + $(top_srcdir)/test/data/crab_bkg_reg.reg \
175 $(top_srcdir)/test/data/tsmap_0.fits \ 177 $(top_srcdir)/test/data/tsmap_0.fits \
176 $(top_srcdir)/test/data/tsmap_1.fits \ 178 $(top_srcdir)/test/data/tsmap_1.fits \
177 $(top_srcdir)/test/data/exclusion.reg \ 179 $(top_srcdir)/test/data/exclusion.reg \
cscripts/csphagen.par
@@ -26,25 +26,27 @@ prefix, s, h, onoff,,, "Prefix for output files" @@ -26,25 +26,27 @@ prefix, s, h, onoff,,, "Prefix for output files"
26 # 26 #
27 # Script parameters 27 # Script parameters
28 #================== 28 #==================
29 -emin, r, a, 0.1,,, "Lower energy limit (TeV)"  
30 -emax, r, a, 100.0,,, "Upper energy limit (TeV)"  
31 -enumbins, i, a, 120,,, "Number of energy bins"  
32 -ebinalg, s, a, LOG,LIN|LOG|FILE,, "Binning algorithm"  
33 -ebinfile, f, a, NONE,,, "Name of the file containing the energy bin definition"  
34 -srcshape, s, h, CIRCLE,CIRCLE,, "Shape of the source region"  
35 -coordsys, s, a, CEL,CEL|GAL,, "Coordinate system (CEL - celestial, GAL - galactic)"  
36 -ra, r, a, 83.63,0,360, "Right Ascension of source region centre (deg)"  
37 -dec, r, a, 22.01,-90,90, "Declination of source region centre (deg)"  
38 -glon, r, a, 184.56,0,360, "Galactic longitude of source region centre (deg)"  
39 -glat, r, a, -5.79,-90,90, "Galactic latitude of source region centre (deg)"  
40 -rad, r, a, 0.2,0,180, "Radius of source region circle (deg)"  
41 -bkgmethod, s, h, REFLECTED,REFLECTED,, "Method for background estimation"  
42 -bkgregmin, i, h, 2,1,1000, "Minimum number of background regions"  
43 -maxoffset, r, h, 4.0,,, "Maximum offset of source from camera center to accept observation"  
44 -stack, b, a, no,,, "Stack multiple observations into single PHA, ARF and RMF files?"  
45 -etruemin, r, h, 0.01,,, "Minimum true energy (TeV)"  
46 -etruemax, r, h, 300.0,,, "Maximum true energy (TeV)"  
47 -etruebins, i, h, 30,,, "Number of bins per decade for true energy bins" 29 +emin, r, a, 0.1,,, "Lower energy limit (TeV)"
  30 +emax, r, a, 100.0,,, "Upper energy limit (TeV)"
  31 +enumbins, i, a, 120,,, "Number of energy bins"
  32 +ebinalg, s, a, LOG,LIN|LOG|FILE,, "Binning algorithm"
  33 +ebinfile, f, a, NONE,,, "Name of the file containing the energy bin definition"
  34 +srcshape, s, h, CIRCLE,CIRCLE,, "Shape of the source region"
  35 +coordsys, s, a, CEL,CEL|GAL,, "Coordinate system (CEL - celestial, GAL - galactic)"
  36 +ra, r, a, 83.63,0,360, "Right Ascension of source region centre (deg)"
  37 +dec, r, a, 22.01,-90,90, "Declination of source region centre (deg)"
  38 +glon, r, a, 184.56,0,360, "Galactic longitude of source region centre (deg)"
  39 +glat, r, a, -5.79,-90,90, "Galactic latitude of source region centre (deg)"
  40 +rad, r, a, 0.2,0,180, "Radius of source region circle (deg)"
  41 +srcregfile, f, a, on.reg,,, "Source region file (ds9 or FITS WCS)"
  42 +bkgmethod, s, a, REFLECTED,REFLECTED|CUSTOM,, "Method for background estimation"
  43 +bkgregfile, f, a, bkg.reg,,, "Background regions file (ds9 or FITS WCS)"
  44 +bkgregmin, i, h, 2,1,1000, "Minimum number of background regions"
  45 +maxoffset, r, h, 4.0,,, "Maximum offset of source from camera center to accept observation"
  46 +stack, b, a, no,,, "Stack multiple observations into single PHA, ARF and RMF files?"
  47 +etruemin, r, h, 0.01,,, "Minimum true energy (TeV)"
  48 +etruemax, r, h, 300.0,,, "Maximum true energy (TeV)"
  49 +etruebins, i, h, 30,,, "Number of bins per decade for true energy bins"
48 50
49 # 51 #
50 # Standard parameters 52 # Standard parameters
cscripts/csphagen.py
@@ -3,7 +3,7 @@ @@ -3,7 +3,7 @@
3 # Computes the PHA spectra for source/background and ARF/RMF files using the 3 # Computes the PHA spectra for source/background and ARF/RMF files using the
4 # reflected region method 4 # reflected region method
5 # 5 #
6 -# Copyright (C) 2017 Luigi Tibaldo 6 +# Copyright (C) 2017-2018 Luigi Tibaldo
7 # 7 #
8 # This program is free software: you can redistribute it and/or modify 8 # This program is free software: you can redistribute it and/or modify
9 # it under the terms of the GNU General Public License as published by 9 # it under the terms of the GNU General Public License as published by
@@ -54,21 +54,14 @@ class csphagen(ctools.csobservation): @@ -54,21 +54,14 @@ class csphagen(ctools.csobservation):
54 # Return 54 # Return
55 return 55 return
56 56
  57 +
57 # Private methods 58 # Private methods
58 - def _get_parameters(self): 59 + def _query_src_direction(self):
59 """ 60 """
60 - Get parameters from parfile and setup observations 61 + Set up the source direction parameter.
  62 + Query relevant parameters.
61 """ 63 """
62 - # Setup observations (require response and allow event list, don't  
63 - # allow counts cube)  
64 - self._setup_observations(self.obs(), True, True, False)  
65 -  
66 - # Set energy bounds  
67 - self._ebounds = self._create_ebounds()  
68 -  
69 - # Initialise source position/region querying relevant parameters  
70 self._src_dir = gammalib.GSkyDir() 64 self._src_dir = gammalib.GSkyDir()
71 - self._src_reg = gammalib.GSkyRegions()  
72 coordsys = self['coordsys'].string() 65 coordsys = self['coordsys'].string()
73 if coordsys == 'CEL': 66 if coordsys == 'CEL':
74 ra = self['ra'].real() 67 ra = self['ra'].real()
@@ -78,17 +71,100 @@ class csphagen(ctools.csobservation): @@ -78,17 +71,100 @@ class csphagen(ctools.csobservation):
78 glon = self['glon'].real() 71 glon = self['glon'].real()
79 glat = self['glat'].real() 72 glat = self['glat'].real()
80 self._src_dir.lb_deg(glon, glat) 73 self._src_dir.lb_deg(glon, glat)
  74 +
  75 + def _get_parameters_bkgmethod_reflected(self):
  76 + """
  77 + Get parameters for REFLECTED background method
  78 + """
  79 + # Get source shape
81 self._srcshape = self['srcshape'].string() 80 self._srcshape = self['srcshape'].string()
  81 +
  82 + # Set source region (so far only CIRCLE is supported)
82 if self._srcshape == 'CIRCLE': 83 if self._srcshape == 'CIRCLE':
  84 +
  85 + # Query source direction
  86 + self._query_src_direction()
  87 +
  88 + # Query minimum number of background regions
  89 + self['bkgregmin'].integer()
  90 +
  91 + # Set circular source region
83 self._rad = self['rad'].real() 92 self._rad = self['rad'].real()
84 self._src_reg.append(gammalib.GSkyRegionCircle(self._src_dir, self._rad)) 93 self._src_reg.append(gammalib.GSkyRegionCircle(self._src_dir, self._rad))
85 94
86 - # Query background estimation method and parameters 95 + # Return
  96 + return
  97 +
  98 + def _get_parameters_bkgmethod_custom(self):
  99 + """
  100 + Get parameters for CUSTOM background method
  101 +
  102 + Raises
  103 + ------
  104 + RuntimeError
  105 + Only one On region is allowed
  106 + """
  107 + # Set up source region
  108 + filename = self['srcregfile'].filename()
  109 + self._src_reg = gammalib.GSkyRegions(filename)
  110 +
  111 + # Raise an exception if there is more than one source region
  112 + if len(self._src_reg) != 1:
  113 + raise RuntimeError('Only one On region is allowed')
  114 +
  115 + # Set up source direction. Query parameters if neccessary.
  116 + if isinstance(self._src_reg[0], gammalib.GSkyRegionCircle):
  117 + self._src_dir = self._src_reg[0].centre()
  118 + self._rad = self._src_reg[0].radius()
  119 + else:
  120 + self._query_src_direction()
  121 +
  122 + # Make sure that all CTA observations have an Off region by loading the
  123 + # Off region region the parameter 'bkgregfile' for all CTA observations
  124 + # without Off region
  125 + for obs in self.obs():
  126 + if obs.instrument() == 'CTA':
  127 + if obs.off_regions().is_empty():
  128 + filename = self['bkgregfile'].filename()
  129 + regions = gammalib.GSkyRegions(filename)
  130 + obs.off_regions(regions)
  131 +
  132 + # Return
  133 + return
  134 +
  135 + def _get_parameters_bkgmethod(self):
  136 + """
  137 + Get background method parameters
  138 + """
  139 + # Get background method
87 bkgmethod = self['bkgmethod'].string() 140 bkgmethod = self['bkgmethod'].string()
  141 +
  142 + # Get background method dependent parameters
88 if bkgmethod == 'REFLECTED': 143 if bkgmethod == 'REFLECTED':
89 - self['bkgregmin'].integer() 144 + self._get_parameters_bkgmethod_reflected()
  145 + elif bkgmethod == 'CUSTOM':
  146 + self._get_parameters_bkgmethod_custom()
  147 +
  148 + # Query parameters that are needed for all background methods
90 self['maxoffset'].real() 149 self['maxoffset'].real()
91 150
  151 + # Return
  152 + return
  153 +
  154 + def _get_parameters(self):
  155 + """
  156 + Get parameters from parfile and setup observations
  157 + """
  158 + # Setup observations (require response and allow event list, don't
  159 + # allow counts cube)
  160 + self._setup_observations(self.obs(), True, True, False)
  161 +
  162 + # Set energy bounds
  163 + self._ebounds = self._create_ebounds()
  164 +
  165 + # Initialize empty src regions container
  166 + self._src_reg = gammalib.GSkyRegions()
  167 +
92 # Exclusion map 168 # Exclusion map
93 if self['inexclusion'].filename().is_fits(): 169 if self['inexclusion'].filename().is_fits():
94 self._excl_reg = gammalib.GSkyRegionMap(self['inexclusion'].filename()) 170 self._excl_reg = gammalib.GSkyRegionMap(self['inexclusion'].filename())
@@ -109,6 +185,10 @@ class csphagen(ctools.csobservation): @@ -109,6 +185,10 @@ class csphagen(ctools.csobservation):
109 if self.obs().is_empty(): 185 if self.obs().is_empty():
110 self.obs(self._get_observations(False)) 186 self.obs(self._get_observations(False))
111 187
  188 + # Get background method parameters (have to come after setting up of
  189 + # observations)
  190 + self._get_parameters_bkgmethod()
  191 +
112 # Write input parameters into logger 192 # Write input parameters into logger
113 self._log_parameters(gammalib.TERSE) 193 self._log_parameters(gammalib.TERSE)
114 194
@@ -247,6 +327,38 @@ class csphagen(ctools.csobservation): @@ -247,6 +327,38 @@ class csphagen(ctools.csobservation):
247 # Return energy boundaries 327 # Return energy boundaries
248 return ebounds 328 return ebounds
249 329
  330 + def _set_background_regions(self, obs):
  331 + """
  332 + Set background regions for an observation
  333 +
  334 + Parameters
  335 + ----------
  336 + obs : `~gammalib.GCTAObservation()`
  337 + CTA observation
  338 +
  339 + Returns
  340 + -------
  341 + regions : `~gammalib.GSkyRegions()`
  342 + Background regions
  343 + """
  344 + # Initialise empty background regions for this observation
  345 + bkg_reg = gammalib.GSkyRegions()
  346 +
  347 + # If reflected background is requested then created reflected
  348 + # background regions
  349 + if self['bkgmethod'].string() == 'REFLECTED':
  350 + bkg_reg = self._reflected_regions(obs)
  351 +
  352 + # ... otherwise if custom background is requested then get the
  353 + # background regions from the observation. We use a copy here since
  354 + # otherwise the background regions go out of scope once the observations
  355 + # are replaced by the On/Off observations.
  356 + elif self['bkgmethod'].string() == 'CUSTOM':
  357 + bkg_reg = obs.off_regions().copy()
  358 +
  359 + # Return background regions
  360 + return bkg_reg
  361 +
250 362
251 # Public methods 363 # Public methods
252 def run(self): 364 def run(self):
@@ -286,15 +398,20 @@ class csphagen(ctools.csobservation): @@ -286,15 +398,20 @@ class csphagen(ctools.csobservation):
286 # Loop through observations and generate pha, arf, rmf files 398 # Loop through observations and generate pha, arf, rmf files
287 for obs in self.obs(): 399 for obs in self.obs():
288 400
289 - # Initialise background regions for this observation  
290 - bkg_reg = gammalib.GSkyRegions() 401 + # Skip non CTA observations
  402 + if obs.instrument() != 'CTA':
  403 + self._log_string(gammalib.NORMAL, 'Skip %s observation "%s"' % \
  404 + (obs.instrument(), obs.id()))
  405 + continue
  406 +
  407 + # Log current observation
  408 + self._log_string(gammalib.NORMAL, 'Process observation "%s"' % \
  409 + (obs.id()))
291 410
292 - # If reflected background is requested then created reflected  
293 - # background regions  
294 - if self['bkgmethod'].string() == 'REFLECTED':  
295 - bkg_reg = self._reflected_regions(obs) 411 + # Set background regions for this observation
  412 + bkg_reg = self._set_background_regions(obs)
296 413
297 - # If there are reflected regions then create On/Off observation 414 + # If there are background regions then create On/Off observation
298 # and append it to the output container 415 # and append it to the output container
299 if bkg_reg.size() >= self['bkgregmin'].integer(): 416 if bkg_reg.size() >= self['bkgregmin'].integer():
300 onoff = gammalib.GCTAOnOffObservation(obs, self._src_dir, 417 onoff = gammalib.GCTAOnOffObservation(obs, self._src_dir,
cscripts/obsutils.py
@@ -130,6 +130,7 @@ def sim(obs, log=False, debug=False, chatter=2, edisp=False, seed=0, @@ -130,6 +130,7 @@ def sim(obs, log=False, debug=False, chatter=2, edisp=False, seed=0,
130 phagen['rad'] = onrad 130 phagen['rad'] = onrad
131 phagen['stack'] = False 131 phagen['stack'] = False
132 phagen['inexclusion'] = 'NONE' 132 phagen['inexclusion'] = 'NONE'
  133 + phagen['bkgmethod'] = 'REFLECTED'
133 134
134 # Optionally open the log file 135 # Optionally open the log file
135 if log: 136 if log:
doc/source/admin/release_history/1.5.rst
@@ -29,6 +29,8 @@ Improvements @@ -29,6 +29,8 @@ Improvements
29 * [`2295 <https://cta-redmine.irap.omp.eu/issues/2295>`_] - 29 * [`2295 <https://cta-redmine.irap.omp.eu/issues/2295>`_] -
30 Add ``ctools::time_reference`` constant and remove ``ctools::time_reference()`` 30 Add ``ctools::time_reference`` constant and remove ``ctools::time_reference()``
31 method 31 method
  32 +* [`2284 <https://cta-redmine.irap.omp.eu/issues/2284>`_] -
  33 + Add ``CUSTOM`` background method to :ref:`csphagen` script
32 * [`2273 <https://cta-redmine.irap.omp.eu/issues/2273>`_] - 34 * [`2273 <https://cta-redmine.irap.omp.eu/issues/2273>`_] -
33 Support On/Off analysis in :ref:`cslightcrv` and :ref:`csphasecrv` 35 Support On/Off analysis in :ref:`cslightcrv` and :ref:`csphasecrv`
34 * [`2267 <https://cta-redmine.irap.omp.eu/issues/2267>`_] - 36 * [`2267 <https://cta-redmine.irap.omp.eu/issues/2267>`_] -
doc/source/users/reference_manual/csphagen.rst
@@ -13,8 +13,9 @@ Synopsis @@ -13,8 +13,9 @@ Synopsis
13 This script can be used to derive from an observation or a set of observations 13 This script can be used to derive from an observation or a set of observations
14 the count spectra in a source region and in background regions, as well as the 14 the count spectra in a source region and in background regions, as well as the
15 detector response (effective area, energy redistribution matrix), that can be 15 detector response (effective area, energy redistribution matrix), that can be
16 -used to perform a classical 1D spectral analysis. The output files are saved in  
17 -the OGIP format normally used in X-ray astronomy (PHA, ARF, RMF). 16 +used to perform a classical 1D spectral analysis. Regions can be placed
  17 +automatically or by hand. The output files are saved in the OGIP format normally
  18 +used in X-ray astronomy (PHA, ARF, RMF).
18 `See here <https://heasarc.gsfc.nasa.gov/docs/heasarc/ofwg/docs/spectra/ogip_92_007/node5.html>`__. 19 `See here <https://heasarc.gsfc.nasa.gov/docs/heasarc/ofwg/docs/spectra/ogip_92_007/node5.html>`__.
19 20
20 The outputs are: 21 The outputs are:
@@ -30,50 +31,48 @@ General parameters @@ -30,50 +31,48 @@ General parameters
30 ------------------ 31 ------------------
31 32
32 ``inobs [file]`` 33 ``inobs [file]``
33 - Input event list or observation definition XML file. 34 + Input event list or observation definition XML file
34 35
35 ``caldb [string]`` 36 ``caldb [string]``
36 - Calibration database. 37 + Calibration database
37 38
38 ``irf [string]`` 39 ``irf [string]``
39 - Instrument response function. 40 + Instrument response function
40 41
41 ``(inexclusion = NONE) [file]`` 42 ``(inexclusion = NONE) [file]``
42 Optional FITS file containing a WCS map in the first hdu that defines sky 43 Optional FITS file containing a WCS map in the first hdu that defines sky
43 - regions not to be used for background estimation (where map value != 0). 44 + regions not to be used for background estimation (where map value != 0)
44 45
45 ``outobs [string]`` 46 ``outobs [string]``
46 - Output observation definition XML file. 47 + Output observation definition XML file
47 48
48 ``(prefix = onoff) [string]`` 49 ``(prefix = onoff) [string]``
49 - Prefix of the file name for output PHA, ARF, RMF, XML, and DS9 region files. 50 + Prefix of the file name for output PHA, ARF, RMF, XML, and DS9 region files
50 51
51 ``emin [real]`` 52 ``emin [real]``
52 - Lower energy limit (TeV) if ``LIN`` or ``LOG`` binning algorithms are used. 53 + Lower energy limit (TeV) if ``LIN`` or ``LOG`` binning algorithms are used
53 54
54 ``emax [real]`` 55 ``emax [real]``
55 - Upper energy limit (TeV) if ``LIN`` or ``LOG`` binning algorithms are used. 56 + Upper energy limit (TeV) if ``LIN`` or ``LOG`` binning algorithms are used
56 57
57 ``enumbins [integer]`` 58 ``enumbins [integer]``
58 Number of energy bins. At least 30 bins per decade are recommended for 59 Number of energy bins. At least 30 bins per decade are recommended for
59 proper evaluation of the instrument response. 60 proper evaluation of the instrument response.
60 61
61 -``ebinalg [string]``  
62 - Energy binning algorithm:  
63 - ``LOG`` : logarithmically spaced energy bins;  
64 - ``LIN`` : linearly spaced energy bins;  
65 - ``FILE``: energy bounds retrieved from file. 62 +``ebinalg <FILE|LIN|LOG> [string]``
  63 + Algorithm for defining energy bins (``FILE``: energy bounds retrieved from
  64 + file, see ``ebinfile`` parameter; ``LIN``: linearly spaced energy bins;
  65 + ``LOG``: logarithmically spaced energy bins)
66 66
67 ``ebinfile [file]`` 67 ``ebinfile [file]``
68 Name of the file containing the energy bin definition if ``FILE`` algorithm 68 Name of the file containing the energy bin definition if ``FILE`` algorithm
69 - is used. 69 + is used
70 70
71 -``(srcshape = CIRCLE) [string]``  
72 - Shape of the source region.  
73 - ``CIRCLE``: circular region around given position. 71 +``(srcshape = CIRCLE) <CIRCLE> [string]``
  72 + Shape of the source region (``CIRCLE``: circular region around given position)
74 73
75 -``coordsys [string]``  
76 - Coordinate system (``CEL`` - celestial, ``GAL`` - galactic) 74 +``coordsys <CEL|GAL> [string]``
  75 + Coordinate system (``CEL``: celestial; ``GAL``: galactic)
77 76
78 ``ra [real]`` 77 ``ra [real]``
79 Right Ascension of source region centre (deg) 78 Right Ascension of source region centre (deg)
@@ -90,10 +89,25 @@ General parameters @@ -90,10 +89,25 @@ General parameters
90 ``rad [real]`` 89 ``rad [real]``
91 Radius of source region circle (deg) 90 Radius of source region circle (deg)
92 91
93 -``(bkgmethod = REFLECTED) [string]``  
94 - Method for background estimation.  
95 - ``REFLECTED:`` background evaluated in regions with the same shape as  
96 - source region reflected w.r.t. pointing direction for each observation. 92 +``srcregfile [file]``
  93 + Source region file (ds9 or FITS WCS map)
  94 +
  95 +``bkgmethod <REFLECTED|CUSTOM> [string]``
  96 + Method for background estimation:
  97 +
  98 + - ``REFLECTED``: background is evaluated in regions with the same shape as
  99 + the source region reflected w.r.t. pointing direction for each observation
  100 +
  101 + - ``CUSTOM``: background is evaluated in regions specified by user. For an
  102 + event list or a single observation in the observation definition XML file
  103 + a region file will be queried (see ``bkgregfile`` parameter). For multiple
  104 + observations specified in the observation definition XML file the name of
  105 + the region file will be extracted from the ``OffRegions`` parameter that
  106 + needs to be specified for each observation in the observation definition
  107 + XML file. Off region files can be either ds9 region files or FITS WCS maps.
  108 +
  109 +``bkgregfile [file]``
  110 + Background regions file (ds9 or FITS WCS map)
97 111
98 ``(bkgregmin = 2) [integer]`` 112 ``(bkgregmin = 2) [integer]``
99 Minimum number of background regions that are required for an observation. 113 Minimum number of background regions that are required for an observation.
@@ -102,20 +116,20 @@ General parameters @@ -102,20 +116,20 @@ General parameters
102 116
103 ``(maxoffset = 4.0) [real]`` 117 ``(maxoffset = 4.0) [real]``
104 Maximum offset in degrees of source from camera center to accept the 118 Maximum offset in degrees of source from camera center to accept the
105 - observation. 119 + observation
106 120
107 ``stack [boolean]`` 121 ``stack [boolean]``
108 Specifies whether multiple observations should be stacked (``yes``) or 122 Specifies whether multiple observations should be stacked (``yes``) or
109 - whether run-wise PHA, ARF and RMF files should be produced (``no``). 123 + whether run-wise PHA, ARF and RMF files should be produced (``no``)
110 124
111 ``(etruemin = 0.01) [real]`` 125 ``(etruemin = 0.01) [real]``
112 - Minimum true energy (TeV). 126 + Minimum true energy (TeV)
113 127
114 ``(etruemax = 0.01) [real]`` 128 ``(etruemax = 0.01) [real]``
115 - Maximum true energy (TeV). 129 + Maximum true energy (TeV)
116 130
117 ``(etruebins = 30) [integer]`` 131 ``(etruebins = 30) [integer]``
118 - Number of bins per decade for true energy bins. 132 + Number of bins per decade for true energy bins
119 133
120 134
121 Standard parameters 135 Standard parameters
test/data/crab_bkg_reg.reg 0 → 100644
  1 +# Region file format: DS9 version 4.1
  2 +global color=green dashlist=8 3 width=1
  3 +fk5;circle(82.2412081162728,22.7975361111674,0.2)
  4 +fk5;circle(82.5360705305998,23.1293202741537,0.2)
  5 +fk5;circle(82.980058098929,23.260640435668,0.2)
  6 +fk5;circle(83.4304728956696,23.1492236356079,0.2)
  7 +fk5;circle(83.7424610541293,22.8309432073396,0.2)
test/data/crab_src_reg.reg 0 → 100644
  1 +# Region file format: DS9 version 4.1
  2 +global color=green dashlist=8 3 width=1
  3 +fk5;circle(83.633,22.0145,0.2)
test/test_csphagen.py
@@ -2,7 +2,7 @@ @@ -2,7 +2,7 @@
2 # ========================================================================== 2 # ==========================================================================
3 # This scripts performs unit tests for the csphagen script. 3 # This scripts performs unit tests for the csphagen script.
4 # 4 #
5 -# Copyright (C) 2017 Luigi Tibaldo 5 +# Copyright (C) 2017-2018 Luigi Tibaldo
6 # 6 #
7 # This program is free software: you can redistribute it and/or modify 7 # This program is free software: you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by 8 # it under the terms of the GNU General Public License as published by
@@ -48,10 +48,11 @@ class Test(test): @@ -48,10 +48,11 @@ class Test(test):
48 self._exclusion = self._datadir + '/crab_exclusion.fits' 48 self._exclusion = self._datadir + '/crab_exclusion.fits'
49 self._nreg_with_excl = 5 49 self._nreg_with_excl = 5
50 self._nreg_wo_excl = 8 50 self._nreg_wo_excl = 8
  51 + self._nreg_bkg_reg = 5
51 self._nreg_mul = [self._nreg_with_excl, 6] 52 self._nreg_mul = [self._nreg_with_excl, 6]
52 self._nbins = 10 53 self._nbins = 10
53 - # number of expected background regions with/wo exclusion,  
54 - # and for two different runs 54 + self._regfile_src = self._datadir + '/crab_src_reg.reg'
  55 + self._regfile_bkg = self._datadir + '/crab_bkg_reg.reg'
55 56
56 # Return 57 # Return
57 return 58 return
@@ -88,7 +89,8 @@ class Test(test): @@ -88,7 +89,8 @@ class Test(test):
88 ' coordsys="CEL" ra=83.633 dec=22.0145' + \ 89 ' coordsys="CEL" ra=83.633 dec=22.0145' + \
89 ' rad=0.2 stack="no" inexclusion="' + \ 90 ' rad=0.2 stack="no" inexclusion="' + \
90 self._exclusion + \ 91 self._exclusion + \
91 - '" outobs="csphagen_cmd1.xml" prefix="csphagen_cmd1" ' + \ 92 + '" bkgmethod="REFLECTED" '+ \
  93 + 'outobs="csphagen_cmd1.xml" prefix="csphagen_cmd1" ' + \
92 'logfile="csphagen_cmd1.log" chatter=2' 94 'logfile="csphagen_cmd1.log" chatter=2'
93 95
94 # Check if execution of wrong command fails 96 # Check if execution of wrong command fails
@@ -112,7 +114,8 @@ class Test(test): @@ -112,7 +114,8 @@ class Test(test):
112 ' coordsys="CEL" ra=83.633 dec=22.0145' + \ 114 ' coordsys="CEL" ra=83.633 dec=22.0145' + \
113 ' rad=0.2 stack="no" inexclusion="' + \ 115 ' rad=0.2 stack="no" inexclusion="' + \
114 self._exclusion + \ 116 self._exclusion + \
115 - '" outobs="csphagen_cmd2.xml" prefix="csphagen_cmd2" ' + \ 117 + '" bkgmethod="REFLECTED" '+ \
  118 + 'outobs="csphagen_cmd2.xml" prefix="csphagen_cmd2" ' + \
116 'logfile="csphagen_cmd2.log" debug=yes chatter=1' 119 'logfile="csphagen_cmd2.log" debug=yes chatter=1'
117 120
118 # Check if execution failed 121 # Check if execution failed
@@ -144,6 +147,7 @@ class Test(test): @@ -144,6 +147,7 @@ class Test(test):
144 phagen['rad'] = 0.2 147 phagen['rad'] = 0.2
145 phagen['stack'] = False 148 phagen['stack'] = False
146 phagen['inexclusion'] = self._exclusion 149 phagen['inexclusion'] = self._exclusion
  150 + phagen['bkgmethod'] = 'REFLECTED'
147 phagen['etruemin'] = 0.05 151 phagen['etruemin'] = 0.05
148 phagen['etruemax'] = 150.0 152 phagen['etruemax'] = 150.0
149 phagen['etruebins'] = 5 153 phagen['etruebins'] = 5
@@ -152,7 +156,7 @@ class Test(test): @@ -152,7 +156,7 @@ class Test(test):
152 phagen['logfile'] = 'csphagen_py1.log' 156 phagen['logfile'] = 'csphagen_py1.log'
153 phagen['chatter'] = 1 157 phagen['chatter'] = 1
154 158
155 - # Run script 159 + # Execute script
156 phagen.execute() 160 phagen.execute()
157 161
158 # Check output 162 # Check output
@@ -173,6 +177,7 @@ class Test(test): @@ -173,6 +177,7 @@ class Test(test):
173 phagen['dec'] = 22.0145 177 phagen['dec'] = 22.0145
174 phagen['rad'] = 0.2 178 phagen['rad'] = 0.2
175 phagen['stack'] = False 179 phagen['stack'] = False
  180 + phagen['bkgmethod'] = 'REFLECTED'
176 phagen['etruemin'] = 0.05 181 phagen['etruemin'] = 0.05
177 phagen['etruemax'] = 150.0 182 phagen['etruemax'] = 150.0
178 phagen['etruebins'] = 5 183 phagen['etruebins'] = 5
@@ -181,7 +186,7 @@ class Test(test): @@ -181,7 +186,7 @@ class Test(test):
181 phagen['logfile'] = 'csphagen_py2.log' 186 phagen['logfile'] = 'csphagen_py2.log'
182 phagen['chatter'] = 2 187 phagen['chatter'] = 2
183 188
184 - # Run script 189 + # Execute script
185 phagen.execute() 190 phagen.execute()
186 191
187 # Check output 192 # Check output
@@ -209,6 +214,7 @@ class Test(test): @@ -209,6 +214,7 @@ class Test(test):
209 phagen['rad'] = 0.2 214 phagen['rad'] = 0.2
210 phagen['stack'] = False 215 phagen['stack'] = False
211 phagen['inexclusion'] = self._exclusion 216 phagen['inexclusion'] = self._exclusion
  217 + phagen['bkgmethod'] = 'REFLECTED'
212 phagen['etruemin'] = 0.05 218 phagen['etruemin'] = 0.05
213 phagen['etruemax'] = 150.0 219 phagen['etruemax'] = 150.0
214 phagen['etruebins'] = 5 220 phagen['etruebins'] = 5
@@ -226,8 +232,7 @@ class Test(test): @@ -226,8 +232,7 @@ class Test(test):
226 self._nreg_mul[s]) 232 self._nreg_mul[s])
227 self._check_outobs('csphagen_py3', 2) 233 self._check_outobs('csphagen_py3', 2)
228 234
229 - # Setup csphagen for test with multiple input observations and  
230 - # stacking 235 + # Setup csphagen for test with multiple input observations and stacking
231 phagen = cscripts.csphagen(obs) 236 phagen = cscripts.csphagen(obs)
232 phagen['ebinalg'] = 'LOG' 237 phagen['ebinalg'] = 'LOG'
233 phagen['emin'] = 0.1 238 phagen['emin'] = 0.1
@@ -239,6 +244,7 @@ class Test(test): @@ -239,6 +244,7 @@ class Test(test):
239 phagen['rad'] = 0.2 244 phagen['rad'] = 0.2
240 phagen['stack'] = True 245 phagen['stack'] = True
241 phagen['inexclusion'] = self._exclusion 246 phagen['inexclusion'] = self._exclusion
  247 + phagen['bkgmethod'] = 'REFLECTED'
242 phagen['etruemin'] = 0.05 248 phagen['etruemin'] = 0.05
243 phagen['etruemax'] = 150.0 249 phagen['etruemax'] = 150.0
244 phagen['etruebins'] = 5 250 phagen['etruebins'] = 5
@@ -247,7 +253,7 @@ class Test(test): @@ -247,7 +253,7 @@ class Test(test):
247 phagen['logfile'] = 'csphagen_py4.log' 253 phagen['logfile'] = 'csphagen_py4.log'
248 phagen['chatter'] = 4 254 phagen['chatter'] = 4
249 255
250 - # Run script 256 + # Execute script
251 phagen.execute() 257 phagen.execute()
252 258
253 # Check output 259 # Check output
@@ -256,6 +262,66 @@ class Test(test): @@ -256,6 +262,66 @@ class Test(test):
256 0, check_regions=False) 262 0, check_regions=False)
257 self._check_outobs('csphagen_py4', 1) 263 self._check_outobs('csphagen_py4', 1)
258 264
  265 + # Setup csphagen for test with custom On and Off regions provided
  266 + phagen = cscripts.csphagen()
  267 + phagen['inobs'] = self._myevents1
  268 + phagen['caldb'] = self._caldb
  269 + phagen['irf'] = self._irf
  270 + phagen['ebinalg'] = 'LOG'
  271 + phagen['emin'] = 0.1
  272 + phagen['emax'] = 100.0
  273 + phagen['enumbins'] = self._nbins
  274 + phagen['bkgmethod'] = 'CUSTOM'
  275 + phagen['srcregfile'] = self._regfile_src
  276 + phagen['bkgregfile'] = self._regfile_bkg
  277 + phagen['etruemin'] = 0.05
  278 + phagen['etruemax'] = 150.0
  279 + phagen['etruebins'] = 5
  280 + phagen['stack'] = False
  281 + phagen['outobs'] = 'csphagen_py5.xml'
  282 + phagen['prefix'] = 'csphagen_py5'
  283 + phagen['logfile'] = 'csphagen_py5.log'
  284 + phagen['chatter'] = 2
  285 +
  286 + # Execute script
  287 + phagen.execute()
  288 +
  289 + # Check output
  290 + self._check_output('csphagen_py5', self._nbins, self._nreg_bkg_reg)
  291 + self._check_outobs('csphagen_py5', 1)
  292 +
  293 + # Append off regions to observation container
  294 + for run in obs:
  295 + run.off_regions(gammalib.GSkyRegions(self._regfile_bkg))
  296 +
  297 + # Setup csphagen for test with multiple input observations and stacking
  298 + phagen = cscripts.csphagen(obs)
  299 + phagen['ebinalg'] = 'LOG'
  300 + phagen['emin'] = 0.1
  301 + phagen['emax'] = 100.0
  302 + phagen['enumbins'] = self._nbins
  303 + phagen['coordsys'] = 'CEL'
  304 + phagen['stack'] = True
  305 + phagen['inexclusion'] = self._exclusion
  306 + phagen['bkgmethod'] = 'CUSTOM'
  307 + phagen['srcregfile'] = self._regfile_src
  308 + phagen['etruemin'] = 0.05
  309 + phagen['etruemax'] = 150.0
  310 + phagen['etruebins'] = 5
  311 + phagen['outobs'] = 'csphagen_py6.xml'
  312 + phagen['prefix'] = 'csphagen_py6'
  313 + phagen['logfile'] = 'csphagen_py6.log'
  314 + phagen['chatter'] = 4
  315 +
  316 + # Execute script
  317 + phagen.execute()
  318 +
  319 + # Check output
  320 + for s in range(2):
  321 + self._check_output('csphagen_py6_stacked', self._nbins,
  322 + 0, check_regions=False)
  323 + self._check_outobs('csphagen_py6', 1)
  324 +
259 # Return 325 # Return
260 return 326 return
261 327
@@ -309,7 +375,7 @@ class Test(test): @@ -309,7 +375,7 @@ class Test(test):
309 self.test_assert(table.contains(col), 375 self.test_assert(table.contains(col),
310 'FITS file contains "' + col + '" column') 376 'FITS file contains "' + col + '" column')
311 377
312 - # Check SPECTRUM table 378 + # Check EBOUNDS table
313 table = fits['EBOUNDS'] 379 table = fits['EBOUNDS']
314 self._check_ebounds(table, bins) 380 self._check_ebounds(table, bins)
315 381
@@ -433,3 +499,26 @@ class Test(test): @@ -433,3 +499,26 @@ class Test(test):
433 499
434 # Return 500 # Return
435 return 501 return
  502 +
  503 + def _read_pha_counts(self, filename):
  504 + """
  505 + Read and integrate the counts in a pha file.
  506 + Pha file structure already tested in _check_pha()
  507 + """
  508 + # Open FITS file
  509 + fits = gammalib.GFits(filename)
  510 +
  511 + # Get SPECTRUM table
  512 + table = fits['SPECTRUM']
  513 +
  514 + # Integrate counts
  515 + counts_col = table['COUNTS']
  516 + counts = 0
  517 + for channel in range( counts_col.nrows() ):
  518 + counts += counts_col[channel]
  519 +
  520 + # Close FITS file
  521 + fits.close()
  522 +
  523 + # Return
  524 + return counts