Commit a28f64882575ebe84bd413a8d4613d11cebedb06

Authored by Josh Cardenzana
2 parents 9a32c2d4 5a2e99f2

Merge branch 'devel' of https://cta-gitlab.irap.omp.eu/ctools/ctools into 2207-c…

…tskymap_RING_bkgsubtract
ChangeLog
1   -2017-08-08
  1 +2017-10-11
2 2  
3 3 * Version 1.5.0 released
4 4 ========================
5 5  
  6 + Add keywords to sky maps produced by ctskymap (#2194)
  7 + Enhance efficiency of computations in ctmodel (#2189)
  8 + Add csmodelsois script (#2180)
6 9 Use t-type parameters for all "tmin" and "tmax" parameters (#1864)
7 10  
8 11  
... ...
1 1 New Features and Important Changes in ctools 1.5.0
2 2  
3   -8 August 2017
  3 +11 October 2017
4 4  
5 5  
6 6 Introduction
... ... @@ -53,7 +53,9 @@ None
53 53  
54 54 ctskymap - CTA sky mapping
55 55 --------------------------
56   -None
  56 +Header keywords informing about the dates and times covered by the observations
  57 +used to generate the skymap, as well as the event energy range, are now appended
  58 +as header keywords to the FITS file (#2194).
57 59  
58 60  
59 61 ctmodel - CTA counts cube modelling
... ... @@ -61,6 +63,11 @@ ctmodel - CTA counts cube modelling
61 63 Use t-type parameters for "tmin" and "tmax" to all specifying times in various
62 64 formats (#1864).
63 65  
  66 +The tool now also checks which models actually overlap with the RoI of a given
  67 +observation and then only use these models to compute the model cube. The
  68 +parallelization of the tool has also been optimised so that diffuse models are
  69 +not loaded over and over again (#2189).
  70 +
64 71  
65 72 ctexpcube - CTA exposure cube generation
66 73 ----------------------------------------
... ... @@ -119,6 +126,9 @@ None
119 126  
120 127 cscripts
121 128 --------
  129 +Add csmodelsois script that replaces selected models in a model container by
  130 +a diffuse map cube (#2180).
  131 +
122 132 Use t-type parameters for "tmin" and "tmax" to all specifying times in various
123 133 formats (#1864).
124 134  
... ...
cscripts/Makefile.am
... ... @@ -28,6 +28,7 @@ pythonscripts = $(top_builddir)/cscripts/__init__.py \
28 28 $(srcdir)/csmodelinfo.py \
29 29 $(srcdir)/csmodelmerge.py \
30 30 $(srcdir)/csmodelselect.py \
  31 + $(srcdir)/csmodelsois.py \
31 32 $(srcdir)/csobs2caldb.py \
32 33 $(srcdir)/csobsdef.py \
33 34 $(srcdir)/csobsinfo.py \
... ... @@ -60,6 +61,7 @@ parfiles = $(srcdir)/cscaldb.par \
60 61 $(srcdir)/csmodelinfo.par \
61 62 $(srcdir)/csmodelmerge.par \
62 63 $(srcdir)/csmodelselect.par \
  64 + $(srcdir)/csmodelsois.par \
63 65 $(srcdir)/csobs2caldb.par \
64 66 $(srcdir)/csobsdef.par \
65 67 $(srcdir)/csobsinfo.par \
... ... @@ -89,6 +91,7 @@ linkscripts = cscaldb \
89 91 csmodelinfo \
90 92 csmodelmerge \
91 93 csmodelselect \
  94 + csmodelsois \
92 95 csobs2caldb \
93 96 csobsdef \
94 97 csobsinfo \
... ... @@ -123,6 +126,7 @@ testscripts = $(srcdir)/tests/__init__.py \
123 126 $(top_srcdir)/test/test_csmodelinfo.py \
124 127 $(top_srcdir)/test/test_csmodelmerge.py \
125 128 $(top_srcdir)/test/test_csmodelselect.py \
  129 + $(top_srcdir)/test/test_csmodelsois.py \
126 130 $(top_srcdir)/test/test_csobs2caldb.py \
127 131 $(top_srcdir)/test/test_csobsdef.py \
128 132 $(top_srcdir)/test/test_csobsinfo.py \
... ...
cscripts/__init__.py.in
1 1 # ==========================================================================
2 2 # cscripts Python module
3 3 #
4   -# Copyright (C) 2015-2016 Juergen Knoedlseder
  4 +# Copyright (C) 2015-2017 Juergen Knoedlseder
5 5 #
6 6 # This program is free software: you can redistribute it and/or modify
7 7 # it under the terms of the GNU General Public License as published by
... ... @@ -31,6 +31,7 @@ __all__ = [
31 31 "csmodelinfo",
32 32 "csmodelmerge",
33 33 "csmodelselect",
  34 + "csmodelsois",
34 35 "csobs2caldb",
35 36 "csobsdef",
36 37 "csobsinfo",
... ... @@ -66,6 +67,7 @@ from cscripts.cslightcrv import cslightcrv
66 67 from cscripts.csmodelinfo import csmodelinfo
67 68 from cscripts.csmodelmerge import csmodelmerge
68 69 from cscripts.csmodelselect import csmodelselect
  70 +from cscripts.csmodelsois import csmodelsois
69 71 from cscripts.csobs2caldb import csobs2caldb
70 72 from cscripts.csobsdef import csobsdef
71 73 from cscripts.csobsinfo import csobsinfo
... ...
cscripts/csmodelsois.par 0 → 100644
  1 +#############################################################################
  2 +# #
  3 +# CTA Science Analysis Tools #
  4 +# #
  5 +# ------------------------------------------------------------------------- #
  6 +# #
  7 +# File: csmodelsois.par #
  8 +# #
  9 +# Author: Josh Cardenzana #
  10 +# josh.cardenzana@irap.omp.eu #
  11 +# IRAP #
  12 +# #
  13 +# Purpose: Parameter file for the csmodelsois application #
  14 +# #
  15 +#############################################################################
  16 +#
  17 +# General parameters
  18 +#===================
  19 +inmodel, f, a, $CTOOLS/share/models/crab.xml,,, "Input model definition XML file"
  20 +outcube, f, a, mapcube.fits,,, "Output map cube file"
  21 +ptsrcsig, r, h, 1.0,0,60, "Point source Gaussian sigma in arcmin"
  22 +soilist, s, a, ,,, "CSV list of sources to exclude from cube model"
  23 +outmodel, f, a, model.xml,,, "Output model definition XML file"
  24 +
  25 +#
  26 +# Energy binning parameters
  27 +#==========================
  28 +ebinalg, s, a, LOG,FILE|LIN|LOG,, "Algorithm for defining energy bins"
  29 +emin, r, a, 0.1,,, "Start value for first energy bin in TeV"
  30 +emax, r, a, 100.0,,, "Stop value for last energy bin in TeV"
  31 +enumbins, i, a, 20,1,200, "Number of energy bins"
  32 +ebinfile, f, a, NONE,,, "Name of the file containing the energy bin definition"
  33 +
  34 +#
  35 +# Spatial binning parameters
  36 +#===========================
  37 +nxpix, i, a, 200,,, "Size of the X axis in pixels"
  38 +nypix, i, a, 200,,, "Size of the Y axis in pixels"
  39 +binsz, r, a, 0.02,,, "Image scale (in degrees/pixel)"
  40 +coordsys, s, a, CEL,CEL|GAL,,"Coordinate system (CEL - celestial, GAL - galactic)"
  41 +proj, s, a, CAR,AIT|AZP|CAR|GLS|MER|MOL|SFL|SIN|STG|TAN,, "Projection method"
  42 +ra, r, a, 83.63,0,360, "Right ascension of image center (in degrees)"
  43 +dec, r, a, 22.01,-90,90, "Declination of image center (in degrees)"
  44 +glon, r, a, 184.55,0,360, "Galactic longitude of image center (in degrees)"
  45 +glat, r, a, -5.80,-90,90, "Galactic latitude of image center (in degrees)"
  46 +
  47 +#
  48 +# Standard parameters
  49 +#====================
  50 +publish, b, h, no,,, "Publish map cube on VO Hub?"
  51 +chatter, i, h, 2,0,4, "Chattiness of output"
  52 +clobber, b, h, yes,,, "Overwrite existing output files with new output files?"
  53 +debug, b, h, no,,, "Debugging mode activated"
  54 +mode, s, h, "ql",,, "Mode of automatic parameters"
  55 +logfile, f, h, "csmodelsois.log",,, "Log filename"
... ...
cscripts/csmodelsois.py 0 → 100755
  1 +#! /usr/bin/env python
  2 +# ==========================================================================
  3 +# Puts subset of sources in diffuse model cube
  4 +#
  5 +# Copyright (C) 2017 Josh Cardenzana
  6 +#
  7 +# This program is free software: you can redistribute it and/or modify
  8 +# it under the terms of the GNU General Public License as published by
  9 +# the Free Software Foundation, either version 3 of the License, or
  10 +# (at your option) any later version.
  11 +#
  12 +# This program is distributed in the hope that it will be useful,
  13 +# but WITHOUT ANY WARRANTY; without even the implied warranty of
  14 +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15 +# GNU General Public License for more details.
  16 +#
  17 +# You should have received a copy of the GNU General Public License
  18 +# along with this program. If not, see <http://www.gnu.org/licenses/>.
  19 +#
  20 +# ==========================================================================
  21 +import sys
  22 +import gammalib
  23 +import ctools
  24 +
  25 +
  26 +# ================= #
  27 +# csmodelsois class #
  28 +# ================= #
  29 +class csmodelsois(ctools.cscript):
  30 + """
  31 + Puts subset of sources in diffuse model cube
  32 +
  33 + The csmodelsois class puts a subset of sources in a model definition
  34 + XML file into a model cube and generates a new model definition XML file
  35 + in which the subset of sources is replaced by a model of type
  36 + DiffuseMapCube.
  37 + """
  38 +
  39 + # Constructor
  40 + def __init__(self, *argv):
  41 + """
  42 + Constructor
  43 +
  44 + Parameters
  45 + ----------
  46 + argv : list of parameters
  47 +
  48 + Raises
  49 + ------
  50 + TypeError
  51 + An invalid number of command line arguments was provided.
  52 + """
  53 + # Set name and version
  54 + self._name = 'csmodelsois'
  55 + self._version = ctools.__version__
  56 +
  57 + # Initialize parameters
  58 + self._init_members()
  59 +
  60 + # Initialise application by calling the appropriate class constructor
  61 + self._init_cscript(argv)
  62 +
  63 + # Return
  64 + return
  65 +
  66 +
  67 + # Private methods
  68 + def _init_members(self):
  69 + """
  70 + Initialize the parameters for this object
  71 + """
  72 + # Initialise parameters
  73 + self.cubegen = ctools.ctmapcube() # Implements ctmapcube for generate the cube
  74 + self.m_models = gammalib.GModels() # Stores the full list of input models
  75 + self.cubemodels = gammalib.GModels() # Stores the list of models to be written to file
  76 +
  77 + # Return
  78 + return
  79 +
  80 + def _get_parameters(self):
  81 + """
  82 + Get parameters from parfile
  83 + """
  84 + # Query input parameters
  85 + self['inmodel'].filename()
  86 + self['soilist'].string()
  87 +
  88 + # Read input parameters
  89 + ptsrcsig = self['ptsrcsig'].real()
  90 +
  91 + # Get the cube centre coordinates
  92 + self.cubegen['coordsys'].string(self['coordsys'].string())
  93 + if self['coordsys'].string() == 'CEL':
  94 + self.cubegen['xref'].real(self['ra'].real())
  95 + self.cubegen['yref'].real(self['dec'].real())
  96 + else:
  97 + self.cubegen['xref'].real(self['glon'].real())
  98 + self.cubegen['yref'].real(self['glat'].real())
  99 +
  100 + # Get spatial binning parameters
  101 + self.cubegen['binsz'].real(self['binsz'].real())
  102 + self.cubegen['nxpix'].integer(self['nxpix'].integer())
  103 + self.cubegen['nypix'].integer(self['nypix'].integer())
  104 + self.cubegen['proj'].string(self['proj'].string())
  105 +
  106 + # Get the energy binning parameters
  107 + self.cubegen['ebinalg'].string(self['ebinalg'].string())
  108 + if self['ebinalg'].string() == 'FILE':
  109 + self.cubegen['ebinfile'].filename(self['ebinfile'].filename())
  110 + else:
  111 + self.cubegen['emin'].real(self['emin'].real())
  112 + self.cubegen['emax'].real(self['emax'].real())
  113 + self.cubegen['enumbins'].integer(self['enumbins'].integer())
  114 +
  115 + # Get remaining hidden parameters
  116 + self.cubegen['ptsrcsig'].real(ptsrcsig)
  117 +
  118 + # Read optionally output cube filenames
  119 + if self._read_ahead():
  120 + self.cubegen['outcube'].filename(self['outcube'].filename())
  121 + self['outmodel'].filename()
  122 +
  123 + # Write parameters into logger
  124 + self._log_parameters(gammalib.TERSE);
  125 +
  126 + return
  127 +
  128 + def _gen_cubemodel(self):
  129 + """
  130 + Generates a binned model from the model file using ctmapcube
  131 + """
  132 + # Get a list of sources we DONT want to put into the cube
  133 + sources = self['soilist'].string().split(',')
  134 +
  135 + # Load the model into a model container
  136 + if (self.m_models.size() == 0):
  137 + self.m_models = gammalib.GModels(self['inmodel'].filename())
  138 +
  139 + # Store a copy of the models
  140 + self.cubemodels = self.m_models.copy()
  141 +
  142 + # Loop through models and pull out all models not in the list
  143 + models2remove = []
  144 + for model in self.m_models:
  145 +
  146 + # If model is not a sky model then continue
  147 + if model.classname() != 'GModelSky':
  148 + self.cubemodels.remove(model.name())
  149 +
  150 + # ... otherwise, if model is in list of sources the remove it
  151 + # from the container
  152 + elif model.name() in sources:
  153 + self.cubemodels.remove(model.name())
  154 +
  155 + # Log the number of models to be put into the cube
  156 + self._log_value(self['chatter'].integer(), 'Numner of cube models',
  157 + self.cubemodels.size())
  158 +
  159 + # Return
  160 + return
  161 +
  162 +
  163 + # Public methods
  164 + def run(self):
  165 + """
  166 + Implements the actual bulk of the script's tasks
  167 + """
  168 + # Switch screen logging on in debug mode
  169 + if self._logDebug():
  170 + self._log.cout(True)
  171 +
  172 + # Get parameters
  173 + self._get_parameters()
  174 +
  175 + # Set the models to put into the cube
  176 + self._gen_cubemodel()
  177 +
  178 + # Only run if there still models to be filled into the cube
  179 + if not self.cubemodels.is_empty():
  180 +
  181 + # Log action
  182 + self._log_string(gammalib.NORMAL, 'Generating model cube')
  183 +
  184 + # Fill the cube models into the ctmapcube object
  185 + self.cubegen.models(self.cubemodels)
  186 +
  187 + # Run the map generating method
  188 + self.cubegen.run()
  189 +
  190 + else:
  191 + self._log_string(gammalib.NORMAL,
  192 + 'List of models is empty, nothing will be done')
  193 +
  194 + # Return
  195 + return
  196 +
  197 + def execute(self):
  198 + """
  199 + Execute the script
  200 + """
  201 + # Open logfile
  202 + self.logFileOpen()
  203 +
  204 + # Read ahead output parameters
  205 + self._read_ahead(True)
  206 +
  207 + # Run the script
  208 + self.run()
  209 +
  210 + # Save model
  211 + self.save()
  212 +
  213 + # Return
  214 + return
  215 +
  216 + def save(self):
  217 + """
  218 + Save the cube and optionally the updated XML model file
  219 + """
  220 + # Save the generated cube if the cube and filename are not empty
  221 + if ((not self.cubegen.mapcube().cube().is_empty()) and
  222 + self._is_valid_filename(self['outcube'].filename())):
  223 + self.cubegen.mapcube().save(self['outcube'].filename(),
  224 + self['clobber'].boolean())
  225 +
  226 + # If requested, save the updated list of models
  227 + if self._is_valid_filename(self['outmodel'].filename()):
  228 +
  229 + # Generate a list of models that will be output to file
  230 + outmodels = gammalib.GModels(self.m_models)
  231 +
  232 + # Remove all models used in the generated cube
  233 + for model in self.cubemodels:
  234 + outmodels.remove(model.name())
  235 +
  236 + # Generate the actual cube model
  237 + spat = gammalib.GModelSpatialDiffuseCube(self['outcube'].filename())
  238 + spec = gammalib.GModelSpectralConst()
  239 + newmodel = gammalib.GModelSky(spat, spec)
  240 +
  241 + # Give the model a default name
  242 + newmodel.name(self.cubemodelname())
  243 +
  244 + # Now append the model to the list of output models
  245 + outmodels.append(newmodel)
  246 +
  247 + # Save the model list
  248 + outmodels.save(self['outmodel'].filename())
  249 +
  250 + # Return
  251 + return
  252 +
  253 + def mapcube(self):
  254 + """
  255 + Return the mapcube generated by the underlying 'ctmapcube' object
  256 + """
  257 + # Return
  258 + return self.cubegen.mapcube()
  259 +
  260 + def cubemodelname(self):
  261 + """
  262 + Return the name to be given to the cube model in the output XML file
  263 + """
  264 + # Return
  265 + return 'FixedSourcesCube'
  266 +
  267 + def models(self, models):
  268 + """
  269 + Set the models
  270 +
  271 + Parameters
  272 + -------
  273 + models : `~gammalib.GModels`
  274 + Model container
  275 + """
  276 + # Set models
  277 + self.m_models = models
  278 +
  279 + # Return
  280 + return
  281 +
  282 +
  283 +# ======================== #
  284 +# Main routine entry point #
  285 +# ======================== #
  286 +if __name__ == '__main__':
  287 +
  288 + # Create instance of application
  289 + app = csmodelsois(sys.argv)
  290 +
  291 + # Execute application
  292 + app.execute()
... ...
doc/source/admin/release_history/1.5.rst
... ... @@ -22,11 +22,16 @@ Bug fixes
22 22 Improvements
23 23 ------------
24 24  
25   -* TBW
  25 +* [`2194 <https://cta-redmine.irap.omp.eu/issues/2194>`_] -
  26 + Add keywords to sky maps produced by :ref:`ctskymap`
  27 +* [`2189 <https://cta-redmine.irap.omp.eu/issues/2189>`_] -
  28 + Enhance efficiency of computations in :ref:`ctmodel`
26 29  
27 30  
28 31 New features
29 32 ------------
30 33  
  34 +* [`2180 <https://cta-redmine.irap.omp.eu/issues/2180>`_] -
  35 + Add :ref:`csmodelsois` script
31 36 * [`1864 <https://cta-redmine.irap.omp.eu/issues/1864>`_] -
32 37 Use t-type parameters for all ``tmin`` and ``tmax`` parameters
... ...
doc/source/users/reference_manual/csmodelsois.rst 0 → 100644
  1 +.. _csmodelsois:
  2 +
  3 +csmodelsois
  4 +===========
  5 +
  6 +Generate a map cube from a subset of models in an input model.
  7 +
  8 +
  9 +Synopsis
  10 +----------
  11 +
  12 +This tool generates a map cube from a subset of sky model components contained
  13 +in an input model. A map cube is a series of sky maps, each spanned by Right
  14 +Ascension or Galactic longitude and Declination or Galactic latitude. Each
  15 +map corresponds to a specific photon energy. The energy binning of the map
  16 +cube may be either linear, logarithmic, or custom defined using an input
  17 +file.
  18 +
  19 +csmodelsois generates a map cube FITS file comprising two extensions. The
  20 +primary extension contains a 3-dimensional image containing the map cube
  21 +values. A second extension named ``ENERGIES`` contains a binary table that
  22 +defines the energy values of each map in the cube.
  23 +
  24 +Optionally, csmodelsois can also output an updated model XML file in which all
  25 +sources from the input model XML file used to generate the map cube are
  26 +replaced with an updated model that uses the generated map cube. This is done
  27 +by specifying the 'outmodel' parameter. The 'soilist' parameter specifies a
  28 +list of source names (separated by ',') to be excluded from the generated
  29 +cube.
  30 +
  31 +General parameters
  32 +------------------
  33 +
  34 +``inmodel [file]``
  35 + Input model XML file.
  36 +
  37 +``outmodel [file]``
  38 + Output model XML file (not generated if set to ``NONE``).
  39 +
  40 +``outcube [file]``
  41 + Output map cube file.
  42 +
  43 +``soilist [string]``
  44 + CSV list of sources to be excluded from the generated map cube.
  45 +
  46 +``(ptsrcsig = 1.0) [real]``
  47 + Sigma of Gaussian to be used for point sources (in arcmin).
  48 +
  49 +``ebinalg <FILE|LIN|LOG> [string]``
  50 + Algorithm for defining energy bins.
  51 +
  52 +``emin [real]``
  53 + Lower energy value for first energy bin (in TeV).
  54 +
  55 +``emax [real]``
  56 + Upper energy value for last energy bin (in TeV).
  57 +
  58 +``enumbins [integer]``
  59 + Number of energy bins.
  60 +
  61 +``ebinfile [file]``
  62 + Name of the file containing the energy bin definition.
  63 +
  64 +``nxpix [integer]``
  65 + Number of cube bins in Right Ascension or Galactic longitude.
  66 +
  67 +``nypix [integer]``
  68 + Number of cube bins in Declination or Galactic latitude.
  69 +
  70 +``binsz [real]``
  71 + Image scale (in degrees/pixel).
  72 +
  73 +``coordsys <CEL|GAL> [string]``
  74 + Coordinate system (CEL - celestial, GAL - galactic).
  75 +
  76 +``proj <AIT|AZP|CAR|GLS|MER|MOL|SFL|SIN|STG|TAN> [string]``
  77 + Projection method.
  78 +
  79 +``ra [real]``
  80 + Right Ascension of cube centre (J2000, in degrees).
  81 +
  82 +``dec [real]``
  83 + Declination of cube centre (J2000, in degrees).
  84 +
  85 +``glon [real]``
  86 + Galactic longitude of cube centre (in degrees).
  87 +
  88 +``glat [real]``
  89 + Galactic latitude of cube centre (in degrees).
  90 +
  91 +
  92 +Standard parameters
  93 +-------------------
  94 +
  95 +``(publish = no) [boolean]``
  96 + Specifies whether the map cube should be published on VO Hub.
  97 +
  98 +``(chatter = 2) [integer]``
  99 + Verbosity of the executable:
  100 + ``chatter = 0``: no information will be logged
  101 +
  102 + ``chatter = 1``: only errors will be logged
  103 +
  104 + ``chatter = 2``: errors and actions will be logged
  105 +
  106 + ``chatter = 3``: report about the task execution
  107 +
  108 + ``chatter = 4``: detailed report about the task execution
  109 +
  110 +``(clobber = yes) [boolean]``
  111 + Specifies whether an existing output counts cube file should be overwritten.
  112 +
  113 +``(debug = no) [boolean]``
  114 + Enables debug mode. In debug mode the executable will dump any log file output to the console.
  115 +
  116 +``(mode = ql) [string]``
  117 + Mode of automatic parameters (default is "ql", i.e. "query and learn").
  118 +
  119 +``(logfile = csmodelsois.log) [string]``
  120 + Name of log file.
  121 +
  122 +
  123 +Related tools or scripts
  124 +------------------------
  125 +
  126 +:ref:`ctmapcube`
... ...
doc/source/users/reference_manual/ctmapcube.rst
... ... @@ -36,40 +36,40 @@ General parameters
36 36  
37 37 ``ebinalg <FILE|LIN|LOG> [string]``
38 38 Algorithm for defining energy bins.
39   -
  39 +
40 40 ``emin [real]``
41 41 Lower energy value for first energy bin (in TeV).
42   -
  42 +
43 43 ``emax [real]``
44 44 Upper energy value for last energy bin (in TeV).
45   -
  45 +
46 46 ``enumbins [integer]``
47 47 Number of energy bins.
48   -
  48 +
49 49 ``ebinfile [file]``
50 50 Name of the file containing the energy bin definition.
51   -
  51 +
52 52 ``nxpix [integer]``
53 53 Number of cube bins in Right Ascension or Galactic longitude.
54   -
  54 +
55 55 ``nypix [integer]``
56 56 Number of cube bins in Declination or Galactic latitude.
57   -
  57 +
58 58 ``binsz [real]``
59 59 Image scale (in degrees/pixel).
60   -
  60 +
61 61 ``coordsys <CEL|GAL> [string]``
62 62 Coordinate system (CEL - celestial, GAL - galactic).
63   -
  63 +
64 64 ``proj <AIT|AZP|CAR|GLS|MER|MOL|SFL|SIN|STG|TAN> [string]``
65 65 Projection method.
66   -
  66 +
67 67 ``xref [real]``
68 68 Right Ascension / Galactic longitude of cube centre (J2000, in degrees).
69   -
  69 +
70 70 ``yref [real]``
71 71 Declination / Galactic latitude of cube centre (J2000, in degrees).
72   -
  72 +
73 73  
74 74 Standard parameters
75 75 -------------------
... ... @@ -80,21 +80,21 @@ Standard parameters
80 80 ``(chatter = 2) [integer]``
81 81 Verbosity of the executable:
82 82 ``chatter = 0``: no information will be logged
83   -
  83 +
84 84 ``chatter = 1``: only errors will be logged
85   -
  85 +
86 86 ``chatter = 2``: errors and actions will be logged
87   -
  87 +
88 88 ``chatter = 3``: report about the task execution
89   -
  89 +
90 90 ``chatter = 4``: detailed report about the task execution
91   -
  91 +
92 92 ``(clobber = yes) [boolean]``
93 93 Specifies whether an existing output counts cube file should be overwritten.
94   -
  94 +
95 95 ``(debug = no) [boolean]``
96 96 Enables debug mode. In debug mode the executable will dump any log file output to the console.
97   -
  97 +
98 98 ``(mode = ql) [string]``
99 99 Mode of automatic parameters (default is "ql", i.e. "query and learn").
100 100  
... ... @@ -105,4 +105,4 @@ Standard parameters
105 105 Related tools or scripts
106 106 ------------------------
107 107  
108   -None
  108 +:ref:`csmodelsois`
... ...
examples/show_spectrum.py
... ... @@ -2,7 +2,7 @@
2 2 # ==========================================================================
3 3 # Display spectrum generated by csspec
4 4 #
5   -# Copyright (C) 2015-2016 Juergen Knoedlseder
  5 +# Copyright (C) 2015-2017 Juergen Knoedlseder
6 6 #
7 7 # This program is free software: you can redistribute it and/or modify
8 8 # it under the terms of the GNU General Public License as published by
... ... @@ -91,6 +91,9 @@ def plot_spectrum(filename, plotfile):
91 91 ul_ed_engs.append(c_ed.real(row))
92 92 ul_eu_engs.append(c_eu.real(row))
93 93  
  94 + # Set upper limit errors
  95 + yerr = [0.6 * x for x in ul_flux]
  96 +
94 97 # Plot the spectrum
95 98 plt.figure()
96 99 plt.loglog()
... ... @@ -98,7 +101,7 @@ def plot_spectrum(filename, plotfile):
98 101 plt.errorbar(energies, flux, yerr=e_flux, xerr=[ed_engs, eu_engs],
99 102 fmt='ro')
100 103 plt.errorbar(ul_energies, ul_flux, xerr=[ul_ed_engs, ul_eu_engs],
101   - yerr=1.0e-11, uplims=True, fmt='ro')
  104 + yerr=yerr, uplims=True, fmt='ro')
102 105 plt.xlabel('Energy (TeV)')
103 106 plt.ylabel(r'E $\times$ dN/dE (erg cm$^{-2}$ s$^{-1}$)')
104 107  
... ...
pyext/ctobservation.i
1 1 /***************************************************************************
2 2 * ctobservation - Base class for observation tools *
3 3 * ----------------------------------------------------------------------- *
4   - * copyright (C) 2016 by Juergen Knoedlseder *
  4 + * copyright (C) 2016-2017 by Juergen Knoedlseder *
5 5 * ----------------------------------------------------------------------- *
6 6 * *
7 7 * This program is free software: you can redistribute it and/or modify *
... ... @@ -89,6 +89,7 @@ public:
89 89  
90 90 // Public methods
91 91 const GObservations& obs(void) const;
  92 + void write_ogip_keywords(GFitsHDU* hdu) const;
92 93  
93 94 // Make methods private in Python by prepending an underscore
94 95 %rename(_first_unbinned_observation) first_unbinned_observation;
... ...
src/ctmodel/ctmodel.cpp
... ... @@ -235,48 +235,55 @@ void ctmodel::run(void)
235 235 log_header1(TERSE, "Generate model cube");
236 236  
237 237 // Loop over all observations in the container
238   - #pragma omp parallel for
239   - for (int i = 0; i < m_obs.size(); ++i) {
  238 + #pragma omp parallel
  239 + {
  240 + // Create a models object for this core
  241 + GModels models(m_obs.models());
240 242  
241   - // Get CTA observation
242   - GCTAObservation* obs = dynamic_cast<GCTAObservation*>(m_obs[i]);
243   -
244   - // Skip observation if it's not CTA
245   - if (obs == NULL) {
246   - log_header3(TERSE, get_obs_header(m_obs[i]));
247   - std::string msg = " Skipping "+m_obs[i]->instrument()+
248   - " observation";
249   - log_string(NORMAL, msg);
250   - }
251   -
252   - // Fill cube and leave loop if we are binned mode (meaning we
253   - // only have one binned observation)
254   - else if (m_binned) {
255   - fill_cube(obs);
256   - i = m_obs.size();
257   - }
258   -
259   - // Skip observation if we have a binned observation
260   - else if (obs->eventtype() == "CountsCube") {
261   - log_header3(TERSE, get_obs_header(m_obs[i]));
262   - std::string msg = " Skipping binned "+m_obs[i]->instrument()+
263   - " observation";
264   - log_string(NORMAL, msg);
265   - }
266   -
267   - // Otherwise, everything seems to be fine, so fill the cube from obs
268   - else {
269   - // Fill the cube
270   - fill_cube(obs);
  243 + #pragma omp for schedule(static,1)
  244 + for (int i = 0; i < m_obs.size(); ++i) {
271 245  
272   - // Dispose events to free memory if event file exists on disk
273   - if (obs->eventfile().length() > 0 && obs->eventfile().exists()) {
274   - obs->dispose_events();
  246 + // Get CTA observation
  247 + GCTAObservation* obs = dynamic_cast<GCTAObservation*>(m_obs[i]);
  248 +
  249 + // Skip observation if it's not CTA
  250 + if (obs == NULL) {
  251 + log_header3(TERSE, get_obs_header(m_obs[i]));
  252 + std::string msg = " Skipping "+m_obs[i]->instrument()+
  253 + " observation";
  254 + log_string(NORMAL, msg);
275 255 }
276   - }
277   -
278   - } // endfor: looped over observations
  256 +
  257 + // Fill cube and leave loop if we are binned mode (meaning we
  258 + // only have one binned observation)
  259 + else if (m_binned) {
  260 + fill_cube(obs, models);
  261 + i = m_obs.size();
  262 + }
  263 +
  264 + // Skip observation if we have a binned observation
  265 + else if (obs->eventtype() == "CountsCube") {
  266 + log_header3(TERSE, get_obs_header(m_obs[i]));
  267 + std::string msg = " Skipping binned "+m_obs[i]->instrument()+
  268 + " observation";
  269 + log_string(NORMAL, msg);
  270 + }
  271 +
  272 + // Otherwise, everything seems to be fine, so fill the cube from obs
  273 + else {
  274 + // Fill the cube
  275 + fill_cube(obs, models);
  276 +
  277 + // Dispose events to free memory if event file exists on disk
  278 + if (obs->eventfile().length() > 0 && obs->eventfile().exists()) {
  279 + obs->dispose_events();
  280 + }
  281 + }
  282 +
  283 + } // endfor: looped over observations
279 284  
  285 + } // end openmp parallel section
  286 +
280 287 // Write model cube into header
281 288 log_header1(NORMAL, "Model cube");
282 289 log_string(NORMAL, m_cube.print());
... ... @@ -789,7 +796,7 @@ void ctmodel::extract_cube_properties(void)
789 796 * GTI of the model cube so that cube GTI is a list of the GTIs of all
790 797 * observations that were used to generate the model cube.
791 798 ***************************************************************************/
792   -void ctmodel::fill_cube(const GCTAObservation* obs)
  799 +void ctmodel::fill_cube(const GCTAObservation* obs, GModels& models)
793 800 {
794 801 // Get number of spatial and spectral bins in counts cube
795 802 int npix = m_cube.npix();
... ... @@ -819,8 +826,8 @@ void ctmodel::fill_cube(const GCTAObservation* obs)
819 826 int num_outside_roi = 0;
820 827  
821 828 // Extract copy of models from observation container
822   - GModels models(m_obs.models());
823   -
  829 + GModels ex_models = trim_models(models, roi);
  830 +
824 831 // Get pointer to event cube pixels
825 832 double* pixels = const_cast<double*>(m_cube.counts().pixels());
826 833  
... ... @@ -877,6 +884,9 @@ void ctmodel::fill_cube(const GCTAObservation* obs)
877 884  
878 885 } // endfor: looped over all cube bins
879 886  
  887 + // Re-append the models to the main model
  888 + models.extend(ex_models);
  889 +
880 890 // Update GTIs
881 891 #pragma omp critical(ctmodel_fill_cube)
882 892 {
... ... @@ -905,3 +915,66 @@ void ctmodel::fill_cube(const GCTAObservation* obs)
905 915 // Return
906 916 return;
907 917 }
  918 +
  919 +
  920 +/***********************************************************************//**
  921 + * @brief Find the models falling inside a defined region of interest.
  922 + *
  923 + * @param[in,out] all_models Model container to be trimmed.
  924 + * @param[in] roi Observation region of interest.
  925 + * @return New model container containing only models inside RoI.
  926 + *
  927 + * Note that a buffer is added to the observation region to ensure that
  928 + * point sources (which have radius=0) are appropriately included if they
  929 + * fall near the edge of the observation.
  930 + ***************************************************************************/
  931 +GModels ctmodel::trim_models(GModels& all_models, const GCTARoi& roi)
  932 +{
  933 + // Create the model container containing only models in RoI
  934 + GModels excluded_models;
  935 +
  936 + // Do nothing if roi is not valid. This will be the case for binned data
  937 + // sets as they have the default roi radius of 0. In this case we would
  938 + // remove ALL models, which is obviously incorrect. So we keep all models
  939 + // when filling based on a counts cube.
  940 + if (!roi.is_valid()) {
  941 + return excluded_models;
  942 + }
  943 +
  944 + // Remove all models that dont overlap with the region of interest. Note
  945 + // that an extra factor is used since point sources have regions of
  946 + // radius 0, which is a problem when they fall just barely outside the
  947 + // RoI. This ensures point sources are appropriately included.
  948 + GSkyRegionCircle obsreg(roi.centre().dir(), roi.radius()+0.5);
  949 +
  950 + // Loop over the models in the passed container
  951 + for (int i = 0; i < all_models.size(); ++i) {
  952 +
  953 + // Cast the model to a GModelSky object
  954 + GModelSky* model = dynamic_cast<GModelSky*>(all_models[i]);
  955 +
  956 + // If model is not a GModelSky model than skip
  957 + if (model == NULL) {
  958 + continue;
  959 + }
  960 +
  961 + // Otherwise, exclude the model if it isn't a diffuse cube and it
  962 + // doesn't overlap with the observation.
  963 + if (!(model->spatial()->code() == GMODEL_SPATIAL_DIFFUSE) &&
  964 + (!model->spatial()->region()->overlaps(obsreg))) {
  965 +
  966 + // Append model to the excluded models
  967 + excluded_models.append(*model);
  968 +
  969 + // Remove model
  970 + all_models.remove(i);
  971 +
  972 + // Decrement to prevent skipping models
  973 + i--;
  974 + }
  975 +
  976 + } // endfor: looped over all models
  977 +
  978 + // Return excluded models
  979 + return excluded_models;
  980 +}
... ...
src/ctmodel/ctmodel.hpp
... ... @@ -74,14 +74,15 @@ public:
74 74  
75 75 protected:
76 76 // Protected methods
77   - void init_members(void);
78   - void copy_members(const ctmodel& app);
79   - void free_members(void);
80   - void get_parameters(void);
81   - void get_obs(void);
82   - void extract_cube_properties(void);
83   - void fill_cube(const GCTAObservation* obs);
84   - bool has_cube(void) const;
  77 + void init_members(void);
  78 + void copy_members(const ctmodel& app);
  79 + void free_members(void);
  80 + void get_parameters(void);
  81 + void get_obs(void);
  82 + void extract_cube_properties(void);
  83 + void fill_cube(const GCTAObservation* obs, GModels& models);
  84 + bool has_cube(void) const;
  85 + GModels trim_models(GModels& all_models, const GCTARoi& roi);
85 86  
86 87 // User parameters
87 88 GFilename m_outcube; //!< Output model cube
... ...
src/ctskymap/ctskymap.cpp
... ... @@ -281,23 +281,39 @@ void ctskymap::save(void)
281 281 GFits fits;
282 282  
283 283 // Write sky map into FITS file
284   - m_skymap.write(fits);
  284 + GFitsHDU* hdu = m_skymap.write(fits);
  285 +
  286 + // Write keywords into sky map extension
  287 + write_ogip_keywords(hdu);
  288 + write_hdu_keywords(hdu);
285 289  
286 290 // If background subtraction is requested then write background map
287 291 // and significance map to FITS file
288 292 if (m_bkgsubtract != "NONE") {
289 293  
290 294 // Write background map into FITS file
291   - m_bkgmap.write(fits);
  295 + hdu = m_bkgmap.write(fits);
292 296  
293 297 // Set background map extension name
294   - fits[fits.size()-1]->extname("BACKGROUND");
  298 + if (hdu != NULL) {
  299 + hdu->extname("BACKGROUND");
  300 + }
  301 +
  302 + // Write keywords into background extension
  303 + write_ogip_keywords(hdu);
  304 + write_hdu_keywords(hdu);
295 305  
296 306 // Write significance map into FITS file
297   - m_sigmap.write(fits);
  307 + hdu = m_sigmap.write(fits);
298 308  
299 309 // Set significance map extension name
300   - fits[fits.size()-1]->extname("SIGNIFICANCE");
  310 + if (hdu != NULL) {
  311 + hdu->extname("SIGNIFICANCE");
  312 + }
  313 +
  314 + // Write keywords into significance extension
  315 + write_ogip_keywords(hdu);
  316 + write_hdu_keywords(hdu);
301 317  
302 318 } // endif: background subtraction was requested
303 319  
... ... @@ -525,7 +541,7 @@ void ctskymap::map_events(GCTAObservation* obs)
525 541 // Make sure that the observation holds a CTA event list. If this
526 542 // is not the case then throw an exception.
527 543 if (events == NULL) {
528   - throw GException::no_list(G_BIN_EVENTS);
  544 + throw GException::no_list(G_MAP_EVENTS);
529 545 }
530 546  
531 547 // Setup energy range covered by data
... ... @@ -647,7 +663,7 @@ void ctskymap::map_background_irf(GCTAObservation* obs)
647 663 get_obs_header(obs)+" to compute IRF background. "
648 664 "Please specify response information or use "
649 665 "another background subtraction method.";
650   - throw GException::invalid_value(G_BKG_SUBTRACT_IRF, msg);
  666 + throw GException::invalid_value(G_MAP_BACKGROUND_IRF, msg);
651 667 }
652 668  
653 669 // Get IRF background template
... ... @@ -659,7 +675,7 @@ void ctskymap::map_background_irf(GCTAObservation* obs)
659 675 "response function for "+
660 676 get_obs_header(obs)+". Please specify an instrument "
661 677 "response function containing a background template.";
662   - throw GException::invalid_value(G_BKG_SUBTRACT_IRF, msg);
  678 + throw GException::invalid_value(G_MAP_BACKGROUND_IRF, msg);
663 679 }
664 680  
665 681 // Compute natural logarithm of energy range in MeV
... ... @@ -860,6 +876,30 @@ void ctskymap::map_background_ring(GCTAObservation* obs)
860 876 return;
861 877 }
862 878  
  879 +/***********************************************************************//**
  880 + * @brief Write keywords in FITS HDU
  881 + *
  882 + * @param[in,out] hdu Pointer to FITS HDU.
  883 + *
  884 + * Writes keywords in FITS HDU.
  885 + ***************************************************************************/
  886 +void ctskymap::write_hdu_keywords(GFitsHDU* hdu) const
  887 +{
  888 + // Continue only if pointer is valid
  889 + if (hdu != NULL) {
  890 +
  891 + // Set keywords
  892 + hdu->card("BKGSUB", m_bkgsubtract, "Background substraction method");
  893 + hdu->card("E_MIN", m_emin, "[TeV] Lower energy boundary");
  894 + hdu->card("E_MAX", m_emax, "[TeV] Upper energy boundary");
  895 + hdu->card("EUNIT", "TeV", "Units for E_MIN and E_MAX");
  896 +
  897 + } // endif: pointer was valid
  898 +
  899 + // Return
  900 + return;
  901 +}
  902 +
863 903  
864 904 /***********************************************************************//**
865 905 * @brief Computes the bin-by-bin significance if background subtraction requested
... ...
src/ctskymap/ctskymap.hpp
... ... @@ -80,6 +80,7 @@ protected:
80 80 const GSkyMap& sensitivity,
81 81 const GSkyDir& position,
82 82 double& non, double& noff, double& alpha);
  83 + void write_hdu_keywords(GFitsHDU* hdu) const;
83 84  
84 85 // Background integration kernel
85 86 class irf_kern : public GFunction {
... ...
src/support/ctobservation.cpp
... ... @@ -192,6 +192,53 @@ ctobservation&amp; ctobservation::operator=(const ctobservation&amp; app)
192 192 = =
193 193 ==========================================================================*/
194 194  
  195 +/***********************************************************************//**
  196 + * @brief Write OGIP keywords in FITS HDU
  197 + *
  198 + * @param[in,out] hdu Pointer to FITS HDU.
  199 + *
  200 + * Writes OGIP keywords in FITS HDU.
  201 + ***************************************************************************/
  202 +void ctobservation::write_ogip_keywords(GFitsHDU* hdu) const
  203 +{
  204 + // Continue only if pointer is valid
  205 + if (hdu != NULL) {
  206 +
  207 + // Set creator
  208 + std::string creator = this->name() + " v" + this->version();
  209 +
  210 + // Compute times
  211 + std::string utc_obs = m_ogip_tstart.utc();
  212 + std::string utc_end = m_ogip_tstop.utc();
  213 + std::string date_obs = utc_obs.substr(0, 10);
  214 + std::string time_obs = utc_obs.substr(11, 8);
  215 + std::string date_end = utc_end.substr(0, 10);
  216 + std::string time_end = utc_end.substr(11, 8);
  217 +
  218 + // Compute deadtime correction
  219 + double deadc = (m_ogip_ontime > 0.0) ? m_ogip_livetime / m_ogip_ontime : 1.0;
  220 +
  221 + // Set observation information
  222 + hdu->card("CREATOR", creator, "Program which created the file");
  223 + hdu->card("TELESCOP", m_ogip_telescope, "Telescope");
  224 +
  225 + // Set observation time information
  226 + hdu->card("DATE_OBS", date_obs, "Observation start date");
  227 + hdu->card("TIME_OBS", time_obs, "Observation start time");
  228 + hdu->card("DATE_END", date_end, "Observation end date");
  229 + hdu->card("TIME_END", time_end, "Observation end time");
  230 + hdu->card("TELAPSE", m_ogip_telapse, "[s] Elapsed time");
  231 + hdu->card("ONTIME", m_ogip_ontime, "[s] Total good time including deadtime");
  232 + hdu->card("LIVETIME", m_ogip_livetime, "[s] Total livetime");
  233 + hdu->card("EXPOSURE", m_ogip_exposure, "[s] Exposure time");
  234 + hdu->card("DEADC", deadc, "Deadtime correction factor");
  235 +
  236 + } // endif: pointer was valid
  237 +
  238 + // Return
  239 + return;
  240 +}
  241 +
195 242  
196 243 /*==========================================================================
197 244 = =
... ... @@ -208,6 +255,13 @@ void ctobservation::init_members(void)
208 255 m_obs.clear();
209 256  
210 257 // Initialise private members
  258 + m_ogip_telescope.clear();
  259 + m_ogip_tstart.clear();
  260 + m_ogip_tstop.clear();
  261 + m_ogip_telapse = 0.0;
  262 + m_ogip_exposure = 0.0;
  263 + m_ogip_ontime = 0.0;
  264 + m_ogip_livetime = 0.0;
211 265 m_index_unbinned = 0;
212 266  
213 267 // Return
... ... @@ -226,6 +280,13 @@ void ctobservation::copy_members(const ctobservation&amp; app)
226 280 m_obs = app.m_obs;
227 281  
228 282 // Copy private members
  283 + m_ogip_telescope = app.m_ogip_telescope;
  284 + m_ogip_tstart = app.m_ogip_tstart;
  285 + m_ogip_tstop = app.m_ogip_tstop;
  286 + m_ogip_telapse = app.m_ogip_telapse;
  287 + m_ogip_exposure = app.m_ogip_exposure;
  288 + m_ogip_ontime = app.m_ogip_ontime;
  289 + m_ogip_livetime = app.m_ogip_livetime;
229 290 m_index_unbinned = app.m_index_unbinned;
230 291  
231 292 // Return
... ... @@ -258,6 +319,15 @@ const GCTAObservation* ctobservation::first_unbinned_observation(void) const
258 319 // Initialise index
259 320 m_index_unbinned = 0;
260 321  
  322 + // Initialise OGIP members
  323 + m_ogip_telescope.clear();
  324 + m_ogip_tstart.clear();
  325 + m_ogip_tstop.clear();
  326 + m_ogip_telapse = 0.0;
  327 + m_ogip_exposure = 0.0;
  328 + m_ogip_ontime = 0.0;
  329 + m_ogip_livetime = 0.0;
  330 +
261 331 // Get next unbinned CTA observation
262 332 const GCTAObservation* obs = next_unbinned_observation();
263 333  
... ... @@ -323,6 +393,31 @@ const GCTAObservation* ctobservation::next_unbinned_observation(void) const
323 393  
324 394 } // endfor: looped over all observation
325 395  
  396 + // Update OGIP members
  397 + if (obs != NULL) {
  398 + if (m_ogip_telapse == 0.0) {
  399 + m_ogip_telescope = obs->instrument();
  400 + m_ogip_tstart = obs->gti().tstart();
  401 + m_ogip_tstop = obs->gti().tstop();
  402 + m_ogip_telapse = obs->gti().telapse();
  403 + m_ogip_exposure = obs->ontime();
  404 + m_ogip_ontime = obs->ontime();
  405 + m_ogip_livetime = obs->livetime();
  406 + }
  407 + else {
  408 + if (obs->gti().tstart() < m_ogip_tstart) {
  409 + m_ogip_tstart = obs->gti().tstart();
  410 + }
  411 + if (obs->gti().tstop() > m_ogip_tstop) {
  412 + m_ogip_tstop = obs->gti().tstop();
  413 + }
  414 + m_ogip_telapse += obs->gti().telapse();
  415 + m_ogip_exposure += obs->ontime();
  416 + m_ogip_ontime += obs->ontime();
  417 + m_ogip_livetime += obs->livetime();
  418 + }
  419 + }
  420 +
326 421 // Return next CTA observation
327 422 return obs;
328 423 }
... ...
src/support/ctobservation.hpp
... ... @@ -63,6 +63,7 @@ public:
63 63  
64 64 // Methods
65 65 const GObservations& obs(void) const;
  66 + void write_ogip_keywords(GFitsHDU* hdu) const;
66 67  
67 68 #ifndef SWIG
68 69 protected:
... ... @@ -82,11 +83,18 @@ protected:
82 83 void save_events_xml(void);
83 84  
84 85 // Protected members
85   - GObservations m_obs; //!< Observation container
  86 + GObservations m_obs; //!< Observation container
86 87  
87 88 private:
88 89 // Private members
89   - mutable int m_index_unbinned; //!< Current index of unbinned observation
  90 + mutable std::string m_ogip_telescope; //!< Name of telescope
  91 + mutable GTime m_ogip_tstart; //!< Start time for OGIP keywords
  92 + mutable GTime m_ogip_tstop; //!< Stop time for OGIP keywords
  93 + mutable double m_ogip_telapse; //!< Elapsed time
  94 + mutable double m_ogip_exposure; //!< Exposure time
  95 + mutable double m_ogip_ontime; //!< Ontime for OGIP keywords
  96 + mutable double m_ogip_livetime; //!< Livetime for OGIP keywords
  97 + mutable int m_index_unbinned; //!< Current index of unbinned observation
90 98 };
91 99  
92 100  
... ...
test/test_csmodelsois.py 0 → 100755
  1 +#! /usr/bin/env python
  2 +# ==========================================================================
  3 +# This scripts performs unit tests for the csmodelsois tool.
  4 +#
  5 +# Copyright (C) 2017 Josh Cardenzana
  6 +#
  7 +# This program is free software: you can redistribute it and/or modify
  8 +# it under the terms of the GNU General Public License as published by
  9 +# the Free Software Foundation, either version 3 of the License, or
  10 +# (at your option) any later version.
  11 +#
  12 +# This program is distributed in the hope that it will be useful,
  13 +# but WITHOUT ANY WARRANTY; without even the implied warranty of
  14 +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15 +# GNU General Public License for more details.
  16 +#
  17 +# You should have received a copy of the GNU General Public License
  18 +# along with this program. If not, see <http://www.gnu.org/licenses/>.
  19 +#
  20 +# ==========================================================================
  21 +import os
  22 +import gammalib
  23 +import cscripts
  24 +from testing import test
  25 +
  26 +
  27 +# =============================== #
  28 +# Test class for csmodelsois tool #
  29 +# =============================== #
  30 +class Test(test):
  31 + """
  32 + Test class for csmodelsois tool
  33 +
  34 + This test class makes unit tests for the csmodelsois tool by using it from
  35 + the command line and from Python.
  36 + """
  37 +
  38 + # Constructor
  39 + def __init__(self):
  40 + """
  41 + Constructor
  42 + """
  43 + # Call base class constructor
  44 + test.__init__(self)
  45 +
  46 + # Return
  47 + return
  48 +
  49 + # Set test functions
  50 + def set(self):
  51 + """
  52 + Set all test functions
  53 + """
  54 + # Set test name
  55 + self.name('csmodelsois')
  56 +
  57 + # Append tests
  58 + self.append(self._test_cmd, 'Test csmodelsois on command line')
  59 + self.append(self._test_python, 'Test csmodelsois from Python')
  60 +
  61 + # Return
  62 + return
  63 +
  64 + # Test csmodelsois on command line
  65 + def _test_cmd(self):
  66 + """
  67 + Test csmodelsois on the command line
  68 + """
  69 + # Set tool name
  70 + csmodelsois = self._script('csmodelsois')
  71 +
  72 + # Setup csmodelsois command
  73 + cmd = csmodelsois+' inmodel="'+self._model+'"'+ \
  74 + ' outcube="csmodelsois_cmd1.fits"'+\
  75 + ' emin=0.1 emax=100.0 enumbins=10 ebinalg=LOG'+ \
  76 + ' nxpix=100 nypix=100 binsz=0.04 coordsys=CEL'+ \
  77 + ' soilist="" outmodel=NONE'+ \
  78 + ' ra=83.63 dec=22.01 proj=CAR'+ \
  79 + ' logfile="csmodelsois_cmd1.log" chatter=1'
  80 +
  81 + # Check if execution of wrong command fails
  82 + self.test_assert(self._execute('command_that_does_not_exist') != 0,
  83 + 'Self test of test script')
  84 +
  85 + # Check if execution was successful
  86 + self.test_assert(self._execute(cmd) == 0,
  87 + 'Check successful execution from command line')
  88 +
  89 + # Check map cube
  90 + self._check_result_file('csmodelsois_cmd1.fits')
  91 +
  92 + # Setup csmodelsois command
  93 + cmd = csmodelsois+' inmodel="events_that_do_not_exist.fits"'+ \
  94 + ' outcube="ccsmodelsois_cmd2.fits"'+\
  95 + ' emin=0.1 emax=100.0 enumbins=10 ebinalg=LOG'+ \
  96 + ' nxpix=100 nypix=100 binsz=0.04 coordsys=CEL'+ \
  97 + ' soilist="" outmodel=NONE'+ \
  98 + ' ra=83.63 dec=22.01 proj=CAR'+ \
  99 + ' logfile="csmodelsois_cmd2.log"'
  100 +
  101 + # Check if execution failed
  102 + self.test_assert(self._execute(cmd) != 0,
  103 + 'Check invalid input file when executed from command line')
  104 +
  105 + # Check csmodelsois --help
  106 + self._check_help(csmodelsois)
  107 +
  108 + # Return
  109 + return
  110 +
  111 + # Test csmodelsois from Python
  112 + def _test_python(self):
  113 + """
  114 + Test csmodelsois from Python
  115 + """
  116 + # Allocate csmodelsois
  117 + modelsois = cscripts.csmodelsois()
  118 +
  119 + # Check that empty csmodelsois tool holds an map cube that has no
  120 + # energy bins
  121 + self._check_cube(modelsois.mapcube(), nmaps=0, npixels=0)
  122 +
  123 + # Check that saving does nothing
  124 + modelsois['outcube'] = 'csmodelsois_py0.fits'
  125 + modelsois['outmodel'] = 'NONE'
  126 + modelsois['logfile'] = 'csmodelsois_py0.log'
  127 + modelsois.logFileOpen()
  128 + modelsois.save()
  129 + self.test_assert(not os.path.isfile('csmodelsois_py0.fits'),
  130 + 'Check that no map cube has been created')
  131 +
  132 + # Check that clearing does not lead to an exception or segfault
  133 + modelsois.clear()
  134 +
  135 + # Now set csmodelsois parameters
  136 + modelsois['inmodel'] = self._model
  137 + modelsois['ebinalg'] = 'LOG'
  138 + modelsois['emin'] = 0.1
  139 + modelsois['emax'] = 100.0
  140 + modelsois['enumbins'] = 10
  141 + modelsois['nxpix'] = 100
  142 + modelsois['nypix'] = 100
  143 + modelsois['binsz'] = 0.04
  144 + modelsois['coordsys'] = 'CEL'
  145 + modelsois['proj'] = 'CAR'
  146 + modelsois['ra'] = 83.63
  147 + modelsois['dec'] = 22.01
  148 + modelsois['ptsrcsig'] = 1.0
  149 + modelsois['outcube'] = 'csmodelsois_py1.fits'
  150 + modelsois['logfile'] = 'csmodelsois_py1.log'
  151 + modelsois['chatter'] = 2
  152 + modelsois['soilist'] = ''
  153 + modelsois['outmodel'] = 'NONE'
  154 +
  155 + # Run csmodelsois tool
  156 + modelsois.logFileOpen() # Make sure we get a log file
  157 + modelsois.run()
  158 +
  159 + # Save map cube
  160 + modelsois.save()
  161 +
  162 + # Check map cube
  163 + self._check_result_file('csmodelsois_py1.fits')
  164 +
  165 + # Allocate csmodelsois scripts, set models and do now a linear
  166 + # binning; also do not use Gaussian point sources
  167 + modelsois = cscripts.csmodelsois()
  168 + modelsois['inmodel'] = self._model
  169 + modelsois['ebinalg'] = 'LIN'
  170 + modelsois['emin'] = 0.1
  171 + modelsois['emax'] = 100.0
  172 + modelsois['enumbins'] = 10
  173 + modelsois['nxpix'] = 100
  174 + modelsois['nypix'] = 100
  175 + modelsois['binsz'] = 0.04
  176 + modelsois['coordsys'] = 'CEL'
  177 + modelsois['proj'] = 'CAR'
  178 + modelsois['ra'] = 83.63
  179 + modelsois['dec'] = 22.01
  180 + modelsois['ptsrcsig'] = 0.0
  181 + modelsois['outcube'] = 'csmodelsois_py2.fits'
  182 + modelsois['logfile'] = 'csmodelsois_py2.log'
  183 + modelsois['chatter'] = 4
  184 + modelsois['soilist'] = ''
  185 + modelsois['outmodel'] = 'NONE'
  186 +
  187 + # Execute mapcube
  188 + modelsois.logFileOpen() # Needed to get a new log file
  189 + modelsois.execute()
  190 +
  191 + # Check result file
  192 + self._check_result_file('csmodelsois_py2.fits')
  193 +
  194 + # Update output filenames and output a new model file
  195 + modelsois['outcube'] = 'csmodelsois_py3.fits'
  196 + modelsois['logfile'] = 'csmodelsois_py3.log'
  197 + modelsois['outmodel'] = 'csmodelsois_py3.xml'
  198 +
  199 + # Execute the file
  200 + modelsois.logFileOpen()
  201 + modelsois.execute()
  202 +
  203 + # Now check that the output model file doesnt contain a Crab Sources
  204 + models3 = gammalib.GModels('csmodelsois_py3.xml')
  205 + self.test_assert(not models3.contains('Crab'),
  206 + 'Check Crab model is not present')
  207 + self.test_assert(models3.contains(modelsois.cubemodelname()),
  208 + 'Check cube model is present')
  209 + self._check_result_file('csmodelsois_py3.fits')
  210 +
  211 + # Return
  212 + return
  213 +
  214 + # Check result file
  215 + def _check_result_file(self, filename):
  216 + """
  217 + Check content of map cube
  218 +
  219 + Parameters
  220 + ----------
  221 + filename : str
  222 + Map cube file name
  223 + """
  224 + # Load map cube
  225 + cube = gammalib.GModelSpatialDiffuseCube(filename)
  226 +
  227 + # Check map cube
  228 + self._check_cube(cube)
  229 +
  230 + # Return
  231 + return
  232 +
  233 + # Check map cube
  234 + def _check_cube(self, cube, nmaps=11, npixels=10000):
  235 + """
  236 + Check map cube
  237 +
  238 + Parameters
  239 + ----------
  240 + cube : `~gammalib.GModelSpatialDiffuseCube`
  241 + Map cube
  242 + nmaps : int, optional
  243 + Number of maps
  244 + npixels : int, optional
  245 + Number of pixels
  246 + """
  247 + # Get energies (this forces loading in case the map cube is not
  248 + # loaded)
  249 + energies = cube.energies()
  250 + self.test_value(len(cube.energies()), nmaps, 'Check number of energies')
  251 +
  252 + # Check dimensions
  253 + self.test_value(cube.maps(), nmaps, 'Check number of maps')
  254 + self.test_value(cube.pixels(), npixels, 'Check number of pixels')
  255 +
  256 + # Return
  257 + return
... ...
test/test_ctskymap.py
... ... @@ -221,7 +221,7 @@ class Test(test):
221 221 skymap['proj'] = 'CAR'
222 222 skymap['xref'] = 184.5575
223 223 skymap['yref'] = -5.7844
224   - skymap['bkgsubtract'] = 'IRF'
  224 + skymap['bkgsubtract'] = 'IRF'
225 225 skymap['caldb'] = self._caldb
226 226 skymap['irf'] = self._irf
227 227 skymap['outmap'] = 'ctskymap_py4.fits'
... ... @@ -234,6 +234,8 @@ class Test(test):
234 234  
235 235 # Check result file
236 236 self._check_result_file('ctskymap_py4.fits')
  237 + self._check_result_file('ctskymap_py4.fits[BACKGROUND]')
  238 + self._check_result_file('ctskymap_py4.fits[SIGNIFICANCE]')
237 239  
238 240 # ================================
239 241 # TEST RING BACKGROUND SUBTRACTION
... ... @@ -268,6 +270,8 @@ class Test(test):
268 270  
269 271 # Check result file
270 272 self._check_result_file('ctskymap_py5.fits', 100, 100)
  273 + self._check_result_file('ctskymap_py5.fits[BACKGROUND]', 100, 100)
  274 + self._check_result_file('ctskymap_py5.fits[SIGNIFICANCE]', 100, 100)
271 275  
272 276 # Return
273 277 return
... ...
test/test_python_cscripts.py
... ... @@ -32,6 +32,7 @@ import test_cslightcrv
32 32 import test_csmodelinfo
33 33 import test_csmodelmerge
34 34 import test_csmodelselect
  35 +import test_csmodelsois
35 36 import test_csobs2caldb
36 37 import test_csobsdef
37 38 import test_csobsinfo
... ... @@ -121,6 +122,7 @@ def test(installed=False, debug=False):
121 122 test_csmodelinfo.Test(),
122 123 test_csmodelmerge.Test(),
123 124 test_csmodelselect.Test(),
  125 + test_csmodelsois.Test(),
124 126 test_csobs2caldb.Test(),
125 127 test_csobsdef.Test(),
126 128 test_csobsinfo.Test(),
... ... @@ -145,7 +147,7 @@ def test(installed=False, debug=False):
145 147  
146 148 # Check for VHEFITS environment variable
147 149 if 'VHEFITS' in os.environ:
148   -
  150 +
149 151 # If the environment variable exists then unset it for the test
150 152 # cases. Since Python is executed in a subprocess this will not
151 153 # impact the environment variable in the parent shell.
... ...