Commit b017e547ad7abd29ab08179d9a8286feee5f3086

Authored by ldivenere
1 parent 6985d99f

added ctprob tests

pyext/Makefile.am
... ... @@ -58,6 +58,7 @@ testscripts = ctools/tests/__init__.py \
58 58 $(top_srcdir)/test/test_ctbutterfly.py \
59 59 $(top_srcdir)/test/test_ctulimit.py \
60 60 $(top_srcdir)/test/test_cterror.py \
  61 + $(top_srcdir)/test/test_ctprob.py \
61 62 $(top_srcdir)/test/test_pipelines.py
62 63  
63 64 # Test data to be distributed in $(pyexecdir)/ctools/tests/data
... ...
pyext/ctools/tools.i
... ... @@ -72,6 +72,7 @@ namespace std {
72 72 %include "ctbutterfly.i"
73 73 %include "ctulimit.i"
74 74 %include "cterror.i"
  75 +%include "ctprob.i"
75 76  
76 77 /* __ Test function ______________________________________________________ */
77 78 %pythoncode %{
... ...
pyext/ctprob.i 0 → 100644
  1 +/***************************************************************************
  2 + * ctprob - Computes probability for a given model *
  3 + * ----------------------------------------------------------------------- *
  4 + * copyright (C) 2012-2016 by Leonardo Di Venere *
  5 + * ----------------------------------------------------------------------- *
  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 +/**
  22 + * @file ctprob.i
  23 + * @brief Computes probability for a given model
  24 + * @author Leonardo Di Venere
  25 + */
  26 +%{
  27 +/* Put headers and other declarations here that are needed for compilation */
  28 +#include "ctprob.hpp"
  29 +%}
  30 +
  31 +
  32 +/***********************************************************************//**
  33 + * @class ctprob
  34 + *
  35 + * @brief Data selection tool
  36 + ***************************************************************************/
  37 +class ctprob : public ctobservation {
  38 +public:
  39 + // Constructors and destructors
  40 + ctprob(void);
  41 + explicit ctprob(const GObservations& obs);
  42 + ctprob(int argc, char *argv[]);
  43 + ctprob(const ctprob& app);
  44 + virtual ~ctprob(void);
  45 +
  46 + // Methods
  47 + void clear(void);
  48 + void run(void);
  49 + void save(void);
  50 + void publish(const std::string& name = "");
  51 +};
  52 +
  53 +
  54 +/***********************************************************************//**
  55 + * @brief Data selection tool Python extension
  56 + ***************************************************************************/
  57 +%extend ctprob {
  58 + ctprob copy() {
  59 + return (*self);
  60 + }
  61 +}
... ...
src/ctprob/ctprob.par
... ... @@ -20,29 +20,11 @@ inobs, f, a, &quot;events.fits&quot;,,, &quot;Input event list or observation definition XML f
20 20 outobs, f, a, "prob_events.fits",,, "Output event list or observation definition XML file"
21 21 prefix, s, h, "prob_",,, "Prefix for event lists in observation definition XML file"
22 22 inmodel, f, a, "$CTOOLS/share/models/crab.xml",,, "Input model definition XML file"
23   -#expcube, f, a, "NONE",,, "Input exposure cube file (only needed for stacked analysis)"
24   -#psfcube, f, a, "NONE",,, "Input PSF cube file (only needed for stacked analysis)"
25   -#edispcube,f, a, "NONE",,, "Input energy dispersion cube file (only needed for stacked analysis)"
26   -#bkgcube, f, a, "NONE",,, "Input background cube file (only needed for stacked analysis)"
27 23 caldb, s, a, "prod2",,, "Calibration database"
28 24 irf, s, a, "South_0.5h",,, "Instrument response function"
29 25 edisp, b, h, no,,, "Apply energy dispersion?"
30 26  
31 27 #
32   -# Selection parameters
33   -#=====================
34   -#usepnt, b, h, no,,, "Use pointing instead of RA/DEC parameters?"
35   -#ra, r, a, 83.63,0,360, "RA for ROI centre (degrees)"
36   -#dec, r, a, 22.01,-90,90, "Dec for ROI centre (degrees)"
37   -#rad, r, a, 3.0,0,180, "Radius of ROI (degrees)"
38   -#tmin, r, a, 0.0,,, "Start time (CTA MET in seconds)"
39   -#tmax, r, a, 0.0,,, "End time (CTA MET in seconds)"
40   -#emin, r, a, 0.1,,, "Lower energy limit (TeV)"
41   -#emax, r, a, 100.0,,, "Upper energy limit (TeV)"
42   -#expr, s, h, "",,, "Additional selection expression"
43   -#usethres,s, h, "NONE",NONE|DEFAULT|USER,, "Energy threshold type"
44   -
45   -#
46 28 # Standard parameters
47 29 #====================
48 30 publish, b, h, no,,, "Publish event list on VO Hub?"
... ...
test/test_ctprob.py 0 → 100755
  1 +#! /usr/bin/env python
  2 +# ==========================================================================
  3 +# This scripts performs unit tests for the ctprob tool.
  4 +#
  5 +# Copyright (C) 2014-2016 Juergen Knoedlseder
  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 ctools
  24 +from testing import test
  25 +
  26 +
  27 +# ============================ #
  28 +# Test class for ctprob tool #
  29 +# ============================ #
  30 +class Test(test):
  31 + """
  32 + Test class for ctprob tool
  33 + """
  34 + # Constructor
  35 + def __init__(self):
  36 + """
  37 + Constructor
  38 + """
  39 + # Call base class constructor
  40 + test.__init__(self)
  41 +
  42 + # Set test data
  43 + self._invalid_events = self._datadir + '/invalid_event_list.fits'
  44 +
  45 + # Return
  46 + return
  47 +
  48 + # Set test functions
  49 + def set(self):
  50 + """
  51 + Set all test functions
  52 + """
  53 + # Set test name
  54 + self.name('ctprob')
  55 +
  56 + # Append tests
  57 + self.append(self._test_cmd, 'Test ctprob on command line')
  58 + self.append(self._test_python, 'Test ctprob from Python')
  59 +
  60 + # Return
  61 + return
  62 +
  63 + # Test ctprob on command line
  64 + def _test_cmd(self):
  65 + """
  66 + Test ctprob on the command line
  67 + """
  68 + # Set tool name
  69 + ctprob = self._tool('ctprob')
  70 +
  71 + # Setup ctprob command
  72 + cmd = ctprob+' inobs="'+self._events+'"'+ \
  73 + ' outobs="ctprob_cmd1.fits"'+ \
  74 + ' inmodel='+self._model+ \
  75 + ' caldb='+self._caldb+' irf='+self._irf+ \
  76 + ' edisp=yes'+ \
  77 + ' logfile="ctprob_cmd1.log" chatter=1'
  78 +
  79 + # Check if execution was successful
  80 + self.test_assert(self._execute(cmd) == 0,
  81 + 'Check successful execution from command line')
  82 +
  83 + # Check result file
  84 + self._check_result_file('ctprob_cmd1.fits')
  85 +
  86 + # Setup ctprob command
  87 + cmd = ctprob+' inobs="event_file_that_does_not_exist.fits"'+ \
  88 + ' outobs="ctprob_cmd2.fits"'+ \
  89 + ' inmodel='+self._model+ \
  90 + ' caldb='+self._caldb+' irf='+self._irf+ \
  91 + ' edisp=yes'+ \
  92 + ' logfile="ctprob_cmd2.log" chatter=1'
  93 +
  94 + # Check if execution failed
  95 + self.test_assert(self._execute(cmd) != 0,
  96 + 'Check invalid input file when executed from command line')
  97 +
  98 + # Setup ctprob --help option
  99 + cmd = ctprob+' --help'
  100 +
  101 + # Check if execution was successful in case that the CTOOLS
  102 + # environment variable was set or failed otherwise
  103 + if 'CTOOLS' in os.environ:
  104 + self.test_value(self._execute(cmd), 0,
  105 + 'Check successful execution with --help option')
  106 + else:
  107 + self.test_assert(self._execute(cmd) != 0,
  108 + 'Check execution failure with --help option')
  109 +
  110 + # Return
  111 + return
  112 +
  113 + # Test ctprob from Python
  114 + def _test_python(self):
  115 + """
  116 + Test ctprob from Python
  117 + """
  118 + # Allocate empty ctprob tool
  119 + prob = ctools.ctprob()
  120 +
  121 + # Check that empty ctprob tool holds an empty observation
  122 + self._check_obs(prob.obs(), nobs=0)
  123 +
  124 + # Check that saving does nothing
  125 + prob['outobs'] = 'ctprob_py0.fits'
  126 + prob['logfile'] = 'ctprob_py0.log'
  127 + prob.logFileOpen()
  128 + prob.save()
  129 + self.test_assert(not os.path.isfile('ctprob_py0.fits'),
  130 + 'Check that no event list has been created')
  131 +
  132 + # Check that clearing does not lead to an exception or segfault
  133 + prob.clear()
  134 +
  135 + # Now set ctprob parameters
  136 + prob['inobs'] = self._events
  137 + prob['outobs'] = 'ctprob_py1.fits'
  138 + prob['inmodel'] = self._model
  139 + prob['caldb'] = self._caldb
  140 + prob['irf'] = self._irf
  141 + prob['edisp'] = True
  142 + prob['logfile'] = 'ctprob_py1.log'
  143 + prob['chatter'] = 2
  144 +
  145 + # Run ctprob tool
  146 + prob.logFileOpen() # Make sure we get a log file
  147 + prob.run()
  148 + prob.save()
  149 +
  150 + # Check result file
  151 + self._check_result_file('ctprob_py1.fits')
  152 +
  153 +
  154 + # Setup ctprob tool for an invalid event file
  155 + prob = ctools.ctprob()
  156 + prob['inobs'] = self._invalid_events
  157 + prob['outobs'] = 'ctprob_py2.fits'
  158 + prob['inmodel'] = self._model
  159 + prob['caldb'] = self._caldb
  160 + prob['irf'] = self._irf
  161 + prob['edisp'] = True
  162 + prob['logfile'] = 'ctprob_py2.log'
  163 + prob['chatter'] = 3
  164 +
  165 + # Execute tool
  166 + prob.logFileOpen() # Needed to get a new log file
  167 + self.test_try('Test invalid event file')
  168 + try:
  169 + prob.execute()
  170 + self.test_try_failure('Exception not thrown')
  171 + except ValueError:
  172 + self.test_try_success()
  173 +
  174 + # Return
  175 + return
  176 +
  177 + # Check result file
  178 + def _check_result_file(self, filename, nevents=6141):
  179 + """
  180 + Check result file
  181 +
  182 + Parameters
  183 + ----------
  184 + filename : str
  185 + Event list file name
  186 + nevents : int, optional
  187 + Expected number of events
  188 + """
  189 + # Open result file
  190 + events = gammalib.GCTAEventList(filename)
  191 +
  192 + # Check event list
  193 + self._check_events(events, nevents=nevents)
  194 +
  195 + # Check that probability columns exist
  196 + colname1 = "PROB_Background"
  197 + colname2 = "PROB_Crab"
  198 + self._check_column(filename, colname1)
  199 + self._check_column(filename, colname2)
  200 +
  201 + # Check that the probability values are correctly normalized
  202 + self._check_normalization(filename, [colname1,colname2])
  203 +
  204 + # Return
  205 + return
  206 +
  207 + # Check observation and event list
  208 + def _check_obs(self, obs, nobs=1, nevents=6141):
  209 + """
  210 + Check observation and event list
  211 +
  212 + Parameters
  213 + ----------
  214 + obs : `~gammalib.GObservations`
  215 + Models
  216 + nobs : int, optional
  217 + Expected number of observations
  218 + nevents : int, optional
  219 + Expected number of events
  220 + """
  221 + # Check number of observations
  222 + self.test_value(obs.size(), nobs, 'Check number of observations')
  223 +
  224 + # If there is an observation then check the event list of the first
  225 + # one
  226 + if obs.size() > 0:
  227 + self._check_events(obs[0].events(), nevents=nevents)
  228 +
  229 + # Return
  230 + return
  231 +
  232 + # Check events
  233 + def _check_events(self, events, nevents=6141):
  234 + """
  235 + Check event list
  236 +
  237 + Parameters
  238 + ----------
  239 + events : `~gammalib.GCTAEventList`
  240 + Event list
  241 + nevents : int, optional
  242 + Expected number of events
  243 + """
  244 + # Check event list
  245 + self.test_value(events.size(), nevents, 'Check number of events')
  246 +
  247 + # Return
  248 + return
  249 +
  250 +
  251 + def _check_column(self, filename, colname):
  252 + """
  253 + Check that column colname exists.
  254 +
  255 + Parameters
  256 + ----------
  257 + filename : str
  258 + Event list file name
  259 + colname : str
  260 + Name of the column to check
  261 + """
  262 + # Check that column exists
  263 + gfits = gammalib.GFits(filename)
  264 + gtable = gfits[1]
  265 + check = int(gtable.contains(colname))
  266 + self.test_value(check, 1, 'Check column '+colname)
  267 +
  268 + # Return
  269 + return
  270 +
  271 +
  272 + def _check_normalization(self, filename, colnames):
  273 + """
  274 + Check that the probability values in columns identified
  275 + by colnames are correctly normalized.
  276 +
  277 + Parameters
  278 + ----------
  279 + filename : str
  280 + Event list file name
  281 + colnames : list
  282 + Names of the columns to check
  283 + """
  284 + # Check that the probability values are correctly normalized
  285 + try:
  286 + import pyfits as fits
  287 + except ImportError:
  288 + try:
  289 + import astropy.io.fits as fits
  290 + except ImportError:
  291 + print("Could not check columns in output file, since pyfits or astropy are necessary.")
  292 + return
  293 + hdu=fits.open(filename)
  294 + data = hdu[1].data
  295 + nevt = len(data)
  296 + src=[]
  297 + for colname in colnames:
  298 + src1 = data.field(colname)
  299 + src.append(src1)
  300 + tot = sum(src)
  301 + self.test_value(sum(tot),nevt, 'Check that probability columns are normalized')
  302 +
  303 + # Return
  304 + return
  305 +
... ...
test/test_python_ctools.py
... ... @@ -39,6 +39,7 @@ import test_ctcubemask
39 39 import test_ctbutterfly
40 40 import test_ctulimit
41 41 import test_cterror
  42 +import test_ctprob
42 43 import test_pipelines
43 44  
44 45  
... ... @@ -116,6 +117,7 @@ def test(installed=False):
116 117 test_ctbutterfly.Test(),
117 118 test_ctulimit.Test(),
118 119 test_cterror.Test(),
  120 + test_ctprob.Test(),
119 121 test_pipelines.Test()]
120 122  
121 123 # Allocate test suite container
... ...