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 * Version 1.5.0 released 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 Use t-type parameters for all "tmin" and "tmax" parameters (#1864) 9 Use t-type parameters for all "tmin" and "tmax" parameters (#1864)
7 10
8 11
1 New Features and Important Changes in ctools 1.5.0 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 Introduction 6 Introduction
@@ -53,7 +53,9 @@ None @@ -53,7 +53,9 @@ None
53 53
54 ctskymap - CTA sky mapping 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 ctmodel - CTA counts cube modelling 61 ctmodel - CTA counts cube modelling
@@ -61,6 +63,11 @@ ctmodel - CTA counts cube modelling @@ -61,6 +63,11 @@ ctmodel - CTA counts cube modelling
61 Use t-type parameters for "tmin" and "tmax" to all specifying times in various 63 Use t-type parameters for "tmin" and "tmax" to all specifying times in various
62 formats (#1864). 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 ctexpcube - CTA exposure cube generation 72 ctexpcube - CTA exposure cube generation
66 ---------------------------------------- 73 ----------------------------------------
@@ -119,6 +126,9 @@ None @@ -119,6 +126,9 @@ None
119 126
120 cscripts 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 Use t-type parameters for "tmin" and "tmax" to all specifying times in various 132 Use t-type parameters for "tmin" and "tmax" to all specifying times in various
123 formats (#1864). 133 formats (#1864).
124 134
cscripts/Makefile.am
@@ -28,6 +28,7 @@ pythonscripts = $(top_builddir)/cscripts/__init__.py \ @@ -28,6 +28,7 @@ pythonscripts = $(top_builddir)/cscripts/__init__.py \
28 $(srcdir)/csmodelinfo.py \ 28 $(srcdir)/csmodelinfo.py \
29 $(srcdir)/csmodelmerge.py \ 29 $(srcdir)/csmodelmerge.py \
30 $(srcdir)/csmodelselect.py \ 30 $(srcdir)/csmodelselect.py \
  31 + $(srcdir)/csmodelsois.py \
31 $(srcdir)/csobs2caldb.py \ 32 $(srcdir)/csobs2caldb.py \
32 $(srcdir)/csobsdef.py \ 33 $(srcdir)/csobsdef.py \
33 $(srcdir)/csobsinfo.py \ 34 $(srcdir)/csobsinfo.py \
@@ -60,6 +61,7 @@ parfiles = $(srcdir)/cscaldb.par \ @@ -60,6 +61,7 @@ parfiles = $(srcdir)/cscaldb.par \
60 $(srcdir)/csmodelinfo.par \ 61 $(srcdir)/csmodelinfo.par \
61 $(srcdir)/csmodelmerge.par \ 62 $(srcdir)/csmodelmerge.par \
62 $(srcdir)/csmodelselect.par \ 63 $(srcdir)/csmodelselect.par \
  64 + $(srcdir)/csmodelsois.par \
63 $(srcdir)/csobs2caldb.par \ 65 $(srcdir)/csobs2caldb.par \
64 $(srcdir)/csobsdef.par \ 66 $(srcdir)/csobsdef.par \
65 $(srcdir)/csobsinfo.par \ 67 $(srcdir)/csobsinfo.par \
@@ -89,6 +91,7 @@ linkscripts = cscaldb \ @@ -89,6 +91,7 @@ linkscripts = cscaldb \
89 csmodelinfo \ 91 csmodelinfo \
90 csmodelmerge \ 92 csmodelmerge \
91 csmodelselect \ 93 csmodelselect \
  94 + csmodelsois \
92 csobs2caldb \ 95 csobs2caldb \
93 csobsdef \ 96 csobsdef \
94 csobsinfo \ 97 csobsinfo \
@@ -123,6 +126,7 @@ testscripts = $(srcdir)/tests/__init__.py \ @@ -123,6 +126,7 @@ testscripts = $(srcdir)/tests/__init__.py \
123 $(top_srcdir)/test/test_csmodelinfo.py \ 126 $(top_srcdir)/test/test_csmodelinfo.py \
124 $(top_srcdir)/test/test_csmodelmerge.py \ 127 $(top_srcdir)/test/test_csmodelmerge.py \
125 $(top_srcdir)/test/test_csmodelselect.py \ 128 $(top_srcdir)/test/test_csmodelselect.py \
  129 + $(top_srcdir)/test/test_csmodelsois.py \
126 $(top_srcdir)/test/test_csobs2caldb.py \ 130 $(top_srcdir)/test/test_csobs2caldb.py \
127 $(top_srcdir)/test/test_csobsdef.py \ 131 $(top_srcdir)/test/test_csobsdef.py \
128 $(top_srcdir)/test/test_csobsinfo.py \ 132 $(top_srcdir)/test/test_csobsinfo.py \
cscripts/__init__.py.in
1 # ========================================================================== 1 # ==========================================================================
2 # cscripts Python module 2 # cscripts Python module
3 # 3 #
4 -# Copyright (C) 2015-2016 Juergen Knoedlseder 4 +# Copyright (C) 2015-2017 Juergen Knoedlseder
5 # 5 #
6 # This program is free software: you can redistribute it and/or modify 6 # This program is free software: you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by 7 # it under the terms of the GNU General Public License as published by
@@ -31,6 +31,7 @@ __all__ = [ @@ -31,6 +31,7 @@ __all__ = [
31 "csmodelinfo", 31 "csmodelinfo",
32 "csmodelmerge", 32 "csmodelmerge",
33 "csmodelselect", 33 "csmodelselect",
  34 + "csmodelsois",
34 "csobs2caldb", 35 "csobs2caldb",
35 "csobsdef", 36 "csobsdef",
36 "csobsinfo", 37 "csobsinfo",
@@ -66,6 +67,7 @@ from cscripts.cslightcrv import cslightcrv @@ -66,6 +67,7 @@ from cscripts.cslightcrv import cslightcrv
66 from cscripts.csmodelinfo import csmodelinfo 67 from cscripts.csmodelinfo import csmodelinfo
67 from cscripts.csmodelmerge import csmodelmerge 68 from cscripts.csmodelmerge import csmodelmerge
68 from cscripts.csmodelselect import csmodelselect 69 from cscripts.csmodelselect import csmodelselect
  70 +from cscripts.csmodelsois import csmodelsois
69 from cscripts.csobs2caldb import csobs2caldb 71 from cscripts.csobs2caldb import csobs2caldb
70 from cscripts.csobsdef import csobsdef 72 from cscripts.csobsdef import csobsdef
71 from cscripts.csobsinfo import csobsinfo 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,11 +22,16 @@ Bug fixes
22 Improvements 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 New features 31 New features
29 ------------ 32 ------------
30 33
  34 +* [`2180 <https://cta-redmine.irap.omp.eu/issues/2180>`_] -
  35 + Add :ref:`csmodelsois` script
31 * [`1864 <https://cta-redmine.irap.omp.eu/issues/1864>`_] - 36 * [`1864 <https://cta-redmine.irap.omp.eu/issues/1864>`_] -
32 Use t-type parameters for all ``tmin`` and ``tmax`` parameters 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,40 +36,40 @@ General parameters
36 36
37 ``ebinalg <FILE|LIN|LOG> [string]`` 37 ``ebinalg <FILE|LIN|LOG> [string]``
38 Algorithm for defining energy bins. 38 Algorithm for defining energy bins.
39 - 39 +
40 ``emin [real]`` 40 ``emin [real]``
41 Lower energy value for first energy bin (in TeV). 41 Lower energy value for first energy bin (in TeV).
42 - 42 +
43 ``emax [real]`` 43 ``emax [real]``
44 Upper energy value for last energy bin (in TeV). 44 Upper energy value for last energy bin (in TeV).
45 - 45 +
46 ``enumbins [integer]`` 46 ``enumbins [integer]``
47 Number of energy bins. 47 Number of energy bins.
48 - 48 +
49 ``ebinfile [file]`` 49 ``ebinfile [file]``
50 Name of the file containing the energy bin definition. 50 Name of the file containing the energy bin definition.
51 - 51 +
52 ``nxpix [integer]`` 52 ``nxpix [integer]``
53 Number of cube bins in Right Ascension or Galactic longitude. 53 Number of cube bins in Right Ascension or Galactic longitude.
54 - 54 +
55 ``nypix [integer]`` 55 ``nypix [integer]``
56 Number of cube bins in Declination or Galactic latitude. 56 Number of cube bins in Declination or Galactic latitude.
57 - 57 +
58 ``binsz [real]`` 58 ``binsz [real]``
59 Image scale (in degrees/pixel). 59 Image scale (in degrees/pixel).
60 - 60 +
61 ``coordsys <CEL|GAL> [string]`` 61 ``coordsys <CEL|GAL> [string]``
62 Coordinate system (CEL - celestial, GAL - galactic). 62 Coordinate system (CEL - celestial, GAL - galactic).
63 - 63 +
64 ``proj <AIT|AZP|CAR|GLS|MER|MOL|SFL|SIN|STG|TAN> [string]`` 64 ``proj <AIT|AZP|CAR|GLS|MER|MOL|SFL|SIN|STG|TAN> [string]``
65 Projection method. 65 Projection method.
66 - 66 +
67 ``xref [real]`` 67 ``xref [real]``
68 Right Ascension / Galactic longitude of cube centre (J2000, in degrees). 68 Right Ascension / Galactic longitude of cube centre (J2000, in degrees).
69 - 69 +
70 ``yref [real]`` 70 ``yref [real]``
71 Declination / Galactic latitude of cube centre (J2000, in degrees). 71 Declination / Galactic latitude of cube centre (J2000, in degrees).
72 - 72 +
73 73
74 Standard parameters 74 Standard parameters
75 ------------------- 75 -------------------
@@ -80,21 +80,21 @@ Standard parameters @@ -80,21 +80,21 @@ Standard parameters
80 ``(chatter = 2) [integer]`` 80 ``(chatter = 2) [integer]``
81 Verbosity of the executable: 81 Verbosity of the executable:
82 ``chatter = 0``: no information will be logged 82 ``chatter = 0``: no information will be logged
83 - 83 +
84 ``chatter = 1``: only errors will be logged 84 ``chatter = 1``: only errors will be logged
85 - 85 +
86 ``chatter = 2``: errors and actions will be logged 86 ``chatter = 2``: errors and actions will be logged
87 - 87 +
88 ``chatter = 3``: report about the task execution 88 ``chatter = 3``: report about the task execution
89 - 89 +
90 ``chatter = 4``: detailed report about the task execution 90 ``chatter = 4``: detailed report about the task execution
91 - 91 +
92 ``(clobber = yes) [boolean]`` 92 ``(clobber = yes) [boolean]``
93 Specifies whether an existing output counts cube file should be overwritten. 93 Specifies whether an existing output counts cube file should be overwritten.
94 - 94 +
95 ``(debug = no) [boolean]`` 95 ``(debug = no) [boolean]``
96 Enables debug mode. In debug mode the executable will dump any log file output to the console. 96 Enables debug mode. In debug mode the executable will dump any log file output to the console.
97 - 97 +
98 ``(mode = ql) [string]`` 98 ``(mode = ql) [string]``
99 Mode of automatic parameters (default is "ql", i.e. "query and learn"). 99 Mode of automatic parameters (default is "ql", i.e. "query and learn").
100 100
@@ -105,4 +105,4 @@ Standard parameters @@ -105,4 +105,4 @@ Standard parameters
105 Related tools or scripts 105 Related tools or scripts
106 ------------------------ 106 ------------------------
107 107
108 -None 108 +:ref:`csmodelsois`
examples/show_spectrum.py
@@ -2,7 +2,7 @@ @@ -2,7 +2,7 @@
2 # ========================================================================== 2 # ==========================================================================
3 # Display spectrum generated by csspec 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 # 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
@@ -91,6 +91,9 @@ def plot_spectrum(filename, plotfile): @@ -91,6 +91,9 @@ def plot_spectrum(filename, plotfile):
91 ul_ed_engs.append(c_ed.real(row)) 91 ul_ed_engs.append(c_ed.real(row))
92 ul_eu_engs.append(c_eu.real(row)) 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 # Plot the spectrum 97 # Plot the spectrum
95 plt.figure() 98 plt.figure()
96 plt.loglog() 99 plt.loglog()
@@ -98,7 +101,7 @@ def plot_spectrum(filename, plotfile): @@ -98,7 +101,7 @@ def plot_spectrum(filename, plotfile):
98 plt.errorbar(energies, flux, yerr=e_flux, xerr=[ed_engs, eu_engs], 101 plt.errorbar(energies, flux, yerr=e_flux, xerr=[ed_engs, eu_engs],
99 fmt='ro') 102 fmt='ro')
100 plt.errorbar(ul_energies, ul_flux, xerr=[ul_ed_engs, ul_eu_engs], 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 plt.xlabel('Energy (TeV)') 105 plt.xlabel('Energy (TeV)')
103 plt.ylabel(r'E $\times$ dN/dE (erg cm$^{-2}$ s$^{-1}$)') 106 plt.ylabel(r'E $\times$ dN/dE (erg cm$^{-2}$ s$^{-1}$)')
104 107
pyext/ctobservation.i
1 /*************************************************************************** 1 /***************************************************************************
2 * ctobservation - Base class for observation tools * 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 * 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 *
@@ -89,6 +89,7 @@ public: @@ -89,6 +89,7 @@ public:
89 89
90 // Public methods 90 // Public methods
91 const GObservations& obs(void) const; 91 const GObservations& obs(void) const;
  92 + void write_ogip_keywords(GFitsHDU* hdu) const;
92 93
93 // Make methods private in Python by prepending an underscore 94 // Make methods private in Python by prepending an underscore
94 %rename(_first_unbinned_observation) first_unbinned_observation; 95 %rename(_first_unbinned_observation) first_unbinned_observation;
src/ctmodel/ctmodel.cpp
@@ -235,48 +235,55 @@ void ctmodel::run(void) @@ -235,48 +235,55 @@ void ctmodel::run(void)
235 log_header1(TERSE, "Generate model cube"); 235 log_header1(TERSE, "Generate model cube");
236 236
237 // Loop over all observations in the container 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 // Write model cube into header 287 // Write model cube into header
281 log_header1(NORMAL, "Model cube"); 288 log_header1(NORMAL, "Model cube");
282 log_string(NORMAL, m_cube.print()); 289 log_string(NORMAL, m_cube.print());
@@ -789,7 +796,7 @@ void ctmodel::extract_cube_properties(void) @@ -789,7 +796,7 @@ void ctmodel::extract_cube_properties(void)
789 * GTI of the model cube so that cube GTI is a list of the GTIs of all 796 * GTI of the model cube so that cube GTI is a list of the GTIs of all
790 * observations that were used to generate the model cube. 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 // Get number of spatial and spectral bins in counts cube 801 // Get number of spatial and spectral bins in counts cube
795 int npix = m_cube.npix(); 802 int npix = m_cube.npix();
@@ -819,8 +826,8 @@ void ctmodel::fill_cube(const GCTAObservation* obs) @@ -819,8 +826,8 @@ void ctmodel::fill_cube(const GCTAObservation* obs)
819 int num_outside_roi = 0; 826 int num_outside_roi = 0;
820 827
821 // Extract copy of models from observation container 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 // Get pointer to event cube pixels 831 // Get pointer to event cube pixels
825 double* pixels = const_cast<double*>(m_cube.counts().pixels()); 832 double* pixels = const_cast<double*>(m_cube.counts().pixels());
826 833
@@ -877,6 +884,9 @@ void ctmodel::fill_cube(const GCTAObservation* obs) @@ -877,6 +884,9 @@ void ctmodel::fill_cube(const GCTAObservation* obs)
877 884
878 } // endfor: looped over all cube bins 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 // Update GTIs 890 // Update GTIs
881 #pragma omp critical(ctmodel_fill_cube) 891 #pragma omp critical(ctmodel_fill_cube)
882 { 892 {
@@ -905,3 +915,66 @@ void ctmodel::fill_cube(const GCTAObservation* obs) @@ -905,3 +915,66 @@ void ctmodel::fill_cube(const GCTAObservation* obs)
905 // Return 915 // Return
906 return; 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,14 +74,15 @@ public:
74 74
75 protected: 75 protected:
76 // Protected methods 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 // User parameters 87 // User parameters
87 GFilename m_outcube; //!< Output model cube 88 GFilename m_outcube; //!< Output model cube
src/ctskymap/ctskymap.cpp
@@ -281,23 +281,39 @@ void ctskymap::save(void) @@ -281,23 +281,39 @@ void ctskymap::save(void)
281 GFits fits; 281 GFits fits;
282 282
283 // Write sky map into FITS file 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 // If background subtraction is requested then write background map 290 // If background subtraction is requested then write background map
287 // and significance map to FITS file 291 // and significance map to FITS file
288 if (m_bkgsubtract != "NONE") { 292 if (m_bkgsubtract != "NONE") {
289 293
290 // Write background map into FITS file 294 // Write background map into FITS file
291 - m_bkgmap.write(fits); 295 + hdu = m_bkgmap.write(fits);
292 296
293 // Set background map extension name 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 // Write significance map into FITS file 306 // Write significance map into FITS file
297 - m_sigmap.write(fits); 307 + hdu = m_sigmap.write(fits);
298 308
299 // Set significance map extension name 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 } // endif: background subtraction was requested 318 } // endif: background subtraction was requested
303 319
@@ -525,7 +541,7 @@ void ctskymap::map_events(GCTAObservation* obs) @@ -525,7 +541,7 @@ void ctskymap::map_events(GCTAObservation* obs)
525 // Make sure that the observation holds a CTA event list. If this 541 // Make sure that the observation holds a CTA event list. If this
526 // is not the case then throw an exception. 542 // is not the case then throw an exception.
527 if (events == NULL) { 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 // Setup energy range covered by data 547 // Setup energy range covered by data
@@ -647,7 +663,7 @@ void ctskymap::map_background_irf(GCTAObservation* obs) @@ -647,7 +663,7 @@ void ctskymap::map_background_irf(GCTAObservation* obs)
647 get_obs_header(obs)+" to compute IRF background. " 663 get_obs_header(obs)+" to compute IRF background. "
648 "Please specify response information or use " 664 "Please specify response information or use "
649 "another background subtraction method."; 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 // Get IRF background template 669 // Get IRF background template
@@ -659,7 +675,7 @@ void ctskymap::map_background_irf(GCTAObservation* obs) @@ -659,7 +675,7 @@ void ctskymap::map_background_irf(GCTAObservation* obs)
659 "response function for "+ 675 "response function for "+
660 get_obs_header(obs)+". Please specify an instrument " 676 get_obs_header(obs)+". Please specify an instrument "
661 "response function containing a background template."; 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 // Compute natural logarithm of energy range in MeV 681 // Compute natural logarithm of energy range in MeV
@@ -860,6 +876,30 @@ void ctskymap::map_background_ring(GCTAObservation* obs) @@ -860,6 +876,30 @@ void ctskymap::map_background_ring(GCTAObservation* obs)
860 return; 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 * @brief Computes the bin-by-bin significance if background subtraction requested 905 * @brief Computes the bin-by-bin significance if background subtraction requested
src/ctskymap/ctskymap.hpp
@@ -80,6 +80,7 @@ protected: @@ -80,6 +80,7 @@ protected:
80 const GSkyMap& sensitivity, 80 const GSkyMap& sensitivity,
81 const GSkyDir& position, 81 const GSkyDir& position,
82 double& non, double& noff, double& alpha); 82 double& non, double& noff, double& alpha);
  83 + void write_hdu_keywords(GFitsHDU* hdu) const;
83 84
84 // Background integration kernel 85 // Background integration kernel
85 class irf_kern : public GFunction { 86 class irf_kern : public GFunction {
src/support/ctobservation.cpp
@@ -192,6 +192,53 @@ ctobservation&amp; ctobservation::operator=(const ctobservation&amp; app) @@ -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,6 +255,13 @@ void ctobservation::init_members(void)
208 m_obs.clear(); 255 m_obs.clear();
209 256
210 // Initialise private members 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 m_index_unbinned = 0; 265 m_index_unbinned = 0;
212 266
213 // Return 267 // Return
@@ -226,6 +280,13 @@ void ctobservation::copy_members(const ctobservation&amp; app) @@ -226,6 +280,13 @@ void ctobservation::copy_members(const ctobservation&amp; app)
226 m_obs = app.m_obs; 280 m_obs = app.m_obs;
227 281
228 // Copy private members 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 m_index_unbinned = app.m_index_unbinned; 290 m_index_unbinned = app.m_index_unbinned;
230 291
231 // Return 292 // Return
@@ -258,6 +319,15 @@ const GCTAObservation* ctobservation::first_unbinned_observation(void) const @@ -258,6 +319,15 @@ const GCTAObservation* ctobservation::first_unbinned_observation(void) const
258 // Initialise index 319 // Initialise index
259 m_index_unbinned = 0; 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 // Get next unbinned CTA observation 331 // Get next unbinned CTA observation
262 const GCTAObservation* obs = next_unbinned_observation(); 332 const GCTAObservation* obs = next_unbinned_observation();
263 333
@@ -323,6 +393,31 @@ const GCTAObservation* ctobservation::next_unbinned_observation(void) const @@ -323,6 +393,31 @@ const GCTAObservation* ctobservation::next_unbinned_observation(void) const
323 393
324 } // endfor: looped over all observation 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 // Return next CTA observation 421 // Return next CTA observation
327 return obs; 422 return obs;
328 } 423 }
src/support/ctobservation.hpp
@@ -63,6 +63,7 @@ public: @@ -63,6 +63,7 @@ public:
63 63
64 // Methods 64 // Methods
65 const GObservations& obs(void) const; 65 const GObservations& obs(void) const;
  66 + void write_ogip_keywords(GFitsHDU* hdu) const;
66 67
67 #ifndef SWIG 68 #ifndef SWIG
68 protected: 69 protected:
@@ -82,11 +83,18 @@ protected: @@ -82,11 +83,18 @@ protected:
82 void save_events_xml(void); 83 void save_events_xml(void);
83 84
84 // Protected members 85 // Protected members
85 - GObservations m_obs; //!< Observation container 86 + GObservations m_obs; //!< Observation container
86 87
87 private: 88 private:
88 // Private members 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,7 +221,7 @@ class Test(test):
221 skymap['proj'] = 'CAR' 221 skymap['proj'] = 'CAR'
222 skymap['xref'] = 184.5575 222 skymap['xref'] = 184.5575
223 skymap['yref'] = -5.7844 223 skymap['yref'] = -5.7844
224 - skymap['bkgsubtract'] = 'IRF' 224 + skymap['bkgsubtract'] = 'IRF'
225 skymap['caldb'] = self._caldb 225 skymap['caldb'] = self._caldb
226 skymap['irf'] = self._irf 226 skymap['irf'] = self._irf
227 skymap['outmap'] = 'ctskymap_py4.fits' 227 skymap['outmap'] = 'ctskymap_py4.fits'
@@ -234,6 +234,8 @@ class Test(test): @@ -234,6 +234,8 @@ class Test(test):
234 234
235 # Check result file 235 # Check result file
236 self._check_result_file('ctskymap_py4.fits') 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 # TEST RING BACKGROUND SUBTRACTION 241 # TEST RING BACKGROUND SUBTRACTION
@@ -268,6 +270,8 @@ class Test(test): @@ -268,6 +270,8 @@ class Test(test):
268 270
269 # Check result file 271 # Check result file
270 self._check_result_file('ctskymap_py5.fits', 100, 100) 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 # Return 276 # Return
273 return 277 return
test/test_python_cscripts.py
@@ -32,6 +32,7 @@ import test_cslightcrv @@ -32,6 +32,7 @@ import test_cslightcrv
32 import test_csmodelinfo 32 import test_csmodelinfo
33 import test_csmodelmerge 33 import test_csmodelmerge
34 import test_csmodelselect 34 import test_csmodelselect
  35 +import test_csmodelsois
35 import test_csobs2caldb 36 import test_csobs2caldb
36 import test_csobsdef 37 import test_csobsdef
37 import test_csobsinfo 38 import test_csobsinfo
@@ -121,6 +122,7 @@ def test(installed=False, debug=False): @@ -121,6 +122,7 @@ def test(installed=False, debug=False):
121 test_csmodelinfo.Test(), 122 test_csmodelinfo.Test(),
122 test_csmodelmerge.Test(), 123 test_csmodelmerge.Test(),
123 test_csmodelselect.Test(), 124 test_csmodelselect.Test(),
  125 + test_csmodelsois.Test(),
124 test_csobs2caldb.Test(), 126 test_csobs2caldb.Test(),
125 test_csobsdef.Test(), 127 test_csobsdef.Test(),
126 test_csobsinfo.Test(), 128 test_csobsinfo.Test(),
@@ -145,7 +147,7 @@ def test(installed=False, debug=False): @@ -145,7 +147,7 @@ def test(installed=False, debug=False):
145 147
146 # Check for VHEFITS environment variable 148 # Check for VHEFITS environment variable
147 if 'VHEFITS' in os.environ: 149 if 'VHEFITS' in os.environ:
148 - 150 +
149 # If the environment variable exists then unset it for the test 151 # If the environment variable exists then unset it for the test
150 # cases. Since Python is executed in a subprocess this will not 152 # cases. Since Python is executed in a subprocess this will not
151 # impact the environment variable in the parent shell. 153 # impact the environment variable in the parent shell.