From 6f1583ea922ab82d2710e75f78dc3d859f1580d3 Mon Sep 17 00:00:00 2001 From: Jürgen Knödlseder Date: Tue, 15 Mar 2016 23:34:54 +0100 Subject: [PATCH] Implement cslightcrv unit test. --- cscripts/Makefile.am | 14 +++++++++----- cscripts/cslightcrv.py | 43 +++++++++++++++++++++++++++---------------- test/test_cscaldb.py | 13 +++++++++---- test/test_cslightcrv.py | 271 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ test/test_ctbin.py | 40 ++++++++++++++++++++-------------------- test/test_python_cscripts.py | 20 +++++++++++++------- 6 files changed, 349 insertions(+), 52 deletions(-) create mode 100755 test/test_cslightcrv.py diff --git a/cscripts/Makefile.am b/cscripts/Makefile.am index 7fbfd42..5400d1c 100644 --- a/cscripts/Makefile.am +++ b/cscripts/Makefile.am @@ -26,13 +26,17 @@ linkscripts = cspull cssens cstsdist cscaldb csresmap \ # Test scripts to be distributed in $(pyexecdir)/cscripts/tests testscripts = tests/__init__.py \ ../test/test_python_cscripts.py \ - ../test/test_cscaldb.py + ../test/test_cscaldb.py \ + ../test/test_cslightcrv.py # Test data to be distributed in $(pyexecdir)/cscripts/tests/data -testdata = +testdata = ../test/data/crab.xml \ + ../test/data/crab_events.fits.gz \ + ../test/data/crab_cntmap.fits.gz \ + ../test/data/exclusion.reg # Test IRFs to be distributed in $(pyexecdir)/cscripts/tests/irf -testirf = +#testirf = # Distribute scripts and parfiles EXTRA_DIST = $(binscripts) $(pythonscripts) @@ -46,8 +50,8 @@ testscriptsdir = $(pyexecdir)/cscripts/tests testscripts_DATA = $(testscripts) testdatadir = $(pyexecdir)/cscripts/tests/data testdata_DATA = $(testdata) -testirfdir = $(pyexecdir)/cscripts/tests/irf -testirf_DATA = $(testirf) +#testirfdir = $(pyexecdir)/cscripts/tests/irf +#testirf_DATA = $(testirf) # Add hook rule to create symbolic links for cscripts install-data-hook: diff --git a/cscripts/cslightcrv.py b/cscripts/cslightcrv.py index a316c83..76ff963 100755 --- a/cscripts/cslightcrv.py +++ b/cscripts/cslightcrv.py @@ -76,12 +76,15 @@ class cslightcrv(ctools.cscript): self._stacked = False # Initialise observation container from constructor arguments. - self._obs = self._set_input_obs(argv) + self._obs, argv = self._set_input_obs(argv) - # Initialise application by calling the appropriate class - # constructor. + # Initialise script by calling the appropriate class constructor. self._init_cscript(argv) + # Set logger properties + self._log_header() + self._log.date(True) + # Return return @@ -91,12 +94,10 @@ class cslightcrv(ctools.cscript): """ Get parameters from parfile. """ - # Set observation if not done before - if self._obs == None or self._obs.size() == 0: - self._require_inobs("cslightcrv::_get_parameters()") - self._obs = self._get_observations() + # Setup observations + self._setup_observations(self._obs) - # Set models if we have none + # Set models if there are none in the container if self._obs.models().size() == 0: self._obs.models(self["inmodel"].filename()) @@ -436,16 +437,26 @@ class cslightcrv(ctools.cscript): if self._logExplicit(): self._log.header3("Creating point spread function cube") - # Create psf cube + # Compute spatial binning for point spread function and + # energy dispersion cubes + binsz = 0.1 * self["binsz"].real() + nxpix = self["nxpix"].integer() / 10 + nypix = self["nypix"].integer() / 10 + if nxpix < 2: + nxpix = 2 + if nypix < 2: + nypix = 2 + + # Create point spread function cube psfcube = ctools.ctpsfcube(obs) psfcube["incube"] = "NONE" psfcube["usepnt"] = False psfcube["ebinalg"] = "LOG" psfcube["xref"] = self["xref"].real() psfcube["yref"] = self["yref"].real() - psfcube["binsz"] = self["binsz"].real() - psfcube["nxpix"] = self["nxpix"].integer() - psfcube["nypix"] = self["nypix"].integer() + psfcube["binsz"] = binsz + psfcube["nxpix"] = nxpix + psfcube["nypix"] = nypix psfcube["enumbins"] = self["enumbins"].integer() psfcube["emin"] = self["emin"].real() psfcube["emax"] = self["emax"].real() @@ -460,16 +471,16 @@ class cslightcrv(ctools.cscript): if self._logExplicit(): self._log.header3("Creating energy dispersion cube") - # Create edisp cube + # Create energy dispersion cube edispcube = ctools.ctedispcube(obs) edispcube["incube"] = "NONE" edispcube["usepnt"] = False edispcube["ebinalg"] = "LOG" edispcube["xref"] = self["xref"].real() edispcube["yref"] = self["yref"].real() - edispcube["binsz"] = self["binsz"].real() - edispcube["nxpix"] = self["nxpix"].integer() - edispcube["nypix"] = self["nypix"].integer() + edispcube["binsz"] = binsz + edispcube["nxpix"] = nxpix + edispcube["nypix"] = nypix edispcube["enumbins"] = self["enumbins"].integer() edispcube["emin"] = self["emin"].real() edispcube["emax"] = self["emax"].real() diff --git a/test/test_cscaldb.py b/test/test_cscaldb.py index c9de3d9..b2d8984 100755 --- a/test/test_cscaldb.py +++ b/test/test_cscaldb.py @@ -18,6 +18,7 @@ # along with this program. If not, see . # # ========================================================================== +import os import gammalib import cscripts @@ -28,7 +29,11 @@ import cscripts class Test(gammalib.GPythonTestSuite): """ Test class for cscaldb script. + + This test class makes unit tests for the cslightcrv script by using it + from the command line and from Python. """ + # Constructor def __init__(self): """ @@ -49,15 +54,15 @@ class Test(gammalib.GPythonTestSuite): self.name("cscaldb") # Append tests - self.append(self._test_functional, "Test cscaldb functionality") + self.append(self._test_python, "Test cscaldb from Python") # Return return - # Test cscaldb functionality - def _test_functional(self): + # Test cscaldb from Python + def _test_python(self): """ - Test cscaldb functionality. + Test cscaldb from Python. """ # Set-up cscaldb caldb = cscripts.cscaldb() diff --git a/test/test_cslightcrv.py b/test/test_cslightcrv.py new file mode 100755 index 0000000..ae459e1 --- /dev/null +++ b/test/test_cslightcrv.py @@ -0,0 +1,271 @@ +#! /usr/bin/env python +# ========================================================================== +# This scripts performs unit tests for the cslightcrv script. +# +# Copyright (C) 2016 Juergen Knoedlseder +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +# ========================================================================== +import os +import gammalib +import cscripts + + +# ================================ # +# Test class for cslightcrv script # +# ================================ # +class Test(gammalib.GPythonTestSuite): + """ + Test class for cslightcrv script. + + This test class makes unit tests for the cslightcrv script by using it + from the command line and from Python. + """ + + # Constructor + def __init__(self): + """ + Constructor. + """ + # Call base class constructor + gammalib.GPythonTestSuite.__init__(self) + + # Set members + self._events_name = "data/crab_events.fits" + self._model_name = "data/crab.xml" + + # Return + return + + # Set test functions + def set(self): + """ + Set all test functions. + """ + # Set test name + self.name("cslightcrv") + + # Append tests + self.append(self._test_cmd, "Test cslightcrv on command line") + self.append(self._test_python, "Test cslightcrv from Python") + + # Return + return + + # Test cslightcrv on command line + def _test_cmd(self): + """ + Test cslightcrv on the command line. + """ + # Kluge to set the command (installed version has no README file) + if os.path.isfile("README"): + cslightcrv = "../cscripts/cslightcrv.py" + else: + cslightcrv = "cslightcrv" + + # Setup cslightcrv command + cmd = cslightcrv+' inobs="'+self._events_name+'"'+ \ + ' inmodel="'+self._model_name+'"'+ \ + ' srcname="Crab" caldb="prod2" irf="South_0.5h"'+ \ + ' tbinalg="LIN" tmin=51544.50 tmax=51544.53'+ \ + ' tbins=3 enumbins=0 emin=0.1 emax=100.0'+ \ + ' outfile="lightcurve_cmd1.fits"' + + # Execute cslightcrv, make sure we catch any exception + try: + rc = os.system(cmd+" &>/dev/null") + except: + pass + + # Check if execution was successful + self.test_assert(rc == 0, + "Successful cslightcrv execution on command line") + + # Check light curve + self._check_light_curve("lightcurve_cmd1.fits", 3) + + # Setup cslightcrv command + cmd = cslightcrv+' inobs="events_that_do_not_exist.fits"'+ \ + ' inmodel="'+self._model_name+'"'+ \ + ' srcname="Crab" caldb="prod2" irf="South_0.5h"'+ \ + ' tbinalg="LIN" tmin=51544.50 tmax=51544.53'+ \ + ' tbins=3 enumbins=0 emin=0.1 emax=100.0'+ \ + ' outfile="lightcurve_cmd1.fits"' + + # Execute cslightcrv, make sure we catch any exception + try: + rc = os.system(cmd+" &>/dev/null") + except: + pass + + # Check if execution failed + self.test_assert(rc != 0, + "Failure of cslightcrv execution on command line") + + # Return + return + + # Test cslightcrv from Python + def _test_python(self): + """ + Test cslightcrv from Python. + """ + # Set-up unbinned cslightcrv + lcrv = cscripts.cslightcrv() + lcrv["inobs"] = self._events_name + lcrv["inmodel"] = self._model_name + lcrv["srcname"] = "Crab" + lcrv["caldb"] = "prod2" + lcrv["irf"] = "South_0.5h" + lcrv["tbinalg"] = "LIN" + lcrv["tmin"] = 51544.50 + lcrv["tmax"] = 51544.53 + lcrv["tbins"] = 3 + lcrv["enumbins"] = 0 + lcrv["emin"] = 0.1 + lcrv["emax"] = 100.0 + lcrv["outfile"] = "lightcurve_py1.fits" + + # Run cslightcrv script and save light curve + lcrv.run() + lcrv.save() + + # Check light curve + self._check_light_curve("lightcurve_py1.fits", 3) + + # Now use FILE as time bin algorithm. For this we need first to + # create an ASCII file. We use now 6 time bins. The ASCII file + # is saved into the file "lightcurve_py2.dat". + csv = gammalib.GCsv(2,2) + tmin = 51544.50 + tdelta = 0.01 + for i in range(csv.nrows()): + csv[i,0] = "%.5f" % (tmin + i * tdelta) + csv[i,1] = "%.5f" % (tmin + (i+1) * tdelta) + csv.save("lightcurve_py2.dat", " ", True) + + # Set-up unbinned cslightcrv + lcrv = cscripts.cslightcrv() + lcrv["inobs"] = self._events_name + lcrv["inmodel"] = self._model_name + lcrv["srcname"] = "Crab" + lcrv["caldb"] = "prod2" + lcrv["irf"] = "South_0.5h" + lcrv["tbinalg"] = "FILE" + lcrv["tbinfile"] = "lightcurve_py2.dat" + lcrv["enumbins"] = 0 + lcrv["emin"] = 0.1 + lcrv["emax"] = 100.0 + lcrv["outfile"] = "lightcurve_py2.fits" + + # Execute cslightcrv script + lcrv.execute() + + # Check light curve + self._check_light_curve("lightcurve_py2.fits", 2) + + # Now we setup an observation container on input. We attached the + # model to the observation container so that cslightcrv should + # no longer query for the parameter. + cta = gammalib.GCTAObservation(self._events_name) + obs = gammalib.GObservations() + obs.append(cta) + obs.models(self._model_name) + + # Set-up unbinned cslightcrv from observation container. Now use + # the GTI algorithm so that we test all timing algorithms. + lcrv = cscripts.cslightcrv(obs) + lcrv["srcname"] = "Crab" + lcrv["caldb"] = "prod2" + lcrv["irf"] = "South_0.5h" + lcrv["tbinalg"] = "GTI" + lcrv["enumbins"] = 0 + lcrv["emin"] = 0.1 + lcrv["emax"] = 100.0 + lcrv["outfile"] = "lightcurve_py3.fits" + + # Execute cslightcrv script + lcrv.execute() + + # Check light curve + self._check_light_curve("lightcurve_py3.fits", 1) + + # Finally we set-up a binned cslightcrv + lcrv = cscripts.cslightcrv() + lcrv["inobs"] = self._events_name + lcrv["inmodel"] = self._model_name + lcrv["srcname"] = "Crab" + lcrv["caldb"] = "prod2" + lcrv["irf"] = "South_0.5h" + lcrv["tbinalg"] = "LIN" + lcrv["tmin"] = 51544.50 + lcrv["tmax"] = 51544.53 + lcrv["tbins"] = 2 + lcrv["emin"] = 0.1 + lcrv["emax"] = 100.0 + lcrv["enumbins"] = 10 + lcrv["coordsys"] = "CEL" + lcrv["proj"] = "TAN" + lcrv["xref"] = 83.63 + lcrv["yref"] = 22.01 + lcrv["nxpix"] = 20 + lcrv["nypix"] = 20 + lcrv["binsz"] = 0.02 + lcrv["outfile"] = "lightcurve_py4.fits" + + # Execute cslightcrv script + lcrv.execute() + + # Check light curve + self._check_light_curve("lightcurve_py4.fits", 2) + + # Return + return + + # Check light curve result file + def _check_light_curve(self, filename, bins): + """ + Check light curve file. + """ + # Expected column names + cols = ["MJD", "e_MJD", "Prefactor", "e_Prefactor", + "Index", "e_Index", "TS", "UpperLimit"] + + # Open FITS file + fits = gammalib.GFits(filename) + + # Check FITS file structure + self.test_value(fits.size(), 2, + 'Check for 2 extensions in light curve FITS file') + self.test_assert(fits.contains("LIGHTCURVE"), + 'FITS file contains "LIGHTCURVE" extension') + + # Get LIGHTCURVE table + table = fits["LIGHTCURVE"] + + # Check FITS table structure + self.test_value(table.ncols(), len(cols), + 'Check for %d columns in light curve FITS table' % len(cols)) + self.test_value(table.nrows(), bins, + 'Check for %d rows in light curve FITS table' % bins) + for col in cols: + self.test_assert(table.contains(col), + 'FITS file contains "'+col+'" column') + + # Close FITS file + fits.close() + + # Return + return diff --git a/test/test_ctbin.py b/test/test_ctbin.py index 95abfbf..c2ec34f 100755 --- a/test/test_ctbin.py +++ b/test/test_ctbin.py @@ -57,14 +57,14 @@ class Test(gammalib.GPythonTestSuite): self.name("ctbin") # Append tests - self.append(self._test_ctbin_cmd, "Test ctbin on command line") - self.append(self._test_ctbin_python, "Test ctbin from Python") + self.append(self._test_cmd, "Test ctbin on command line") + self.append(self._test_python, "Test ctbin from Python") # Return return # Test ctbin on command line - def _test_ctbin_cmd(self): + def _test_cmd(self): """ Test ctbin on the command line. """ @@ -93,7 +93,7 @@ class Test(gammalib.GPythonTestSuite): # Load counts cube and check content. evt = gammalib.GCTAEventCube("cntmap_cmd1.fits") - self._test_cube(evt, 5542) + self._check_cube(evt, 5542) # Setup ctbin command cmd = ctbin+' inobs="events_that_do_not_exist.fits"'+ \ @@ -116,7 +116,7 @@ class Test(gammalib.GPythonTestSuite): return # Test ctbin from Python - def _test_ctbin_python(self): + def _test_python(self): """ Test ctbin from Python. """ @@ -140,15 +140,15 @@ class Test(gammalib.GPythonTestSuite): bin.run() # Check content of observation and cube - self._test_observation(bin, 5542) - self._test_cube(bin.cube(), 5542) + self._check_observation(bin, 5542) + self._check_cube(bin.cube(), 5542) # Test copy constructor cpy_bin = bin.copy() # Check content of observation and cube - self._test_observation(cpy_bin, 5542) - self._test_cube(cpy_bin.cube(), 5542) + self._check_observation(cpy_bin, 5542) + self._check_cube(cpy_bin.cube(), 5542) # Run copy of ctbin tool again cpy_bin.run() @@ -157,8 +157,8 @@ class Test(gammalib.GPythonTestSuite): # event cube as on input the observation is binned, and any binned # observation will be skipped, hence the counts cube should be # empty. - self._test_observation(cpy_bin, 0) - self._test_cube(cpy_bin.cube(), 0) + self._check_observation(cpy_bin, 0) + self._check_cube(cpy_bin.cube(), 0) # Clear and run copy of ctbin again #cpy_bin.clear() @@ -172,7 +172,7 @@ class Test(gammalib.GPythonTestSuite): # Load counts cube and check content. evt = gammalib.GCTAEventCube("cntmap.fits") - self._test_cube(evt, 5542) + self._check_cube(evt, 5542) # Prepare observation container for stacked analysis cta = gammalib.GCTAObservation(self._events_name) @@ -204,8 +204,8 @@ class Test(gammalib.GPythonTestSuite): # Check content of observation and cube (need multiplier=3 since # three identical observations have been appended) - self._test_observation(bin, 5542, multiplier=3) - self._test_cube(bin.cube(), 5542, multiplier=3) + self._check_observation(bin, 5542, multiplier=3) + self._check_cube(bin.cube(), 5542, multiplier=3) # Set-up ctbin using an observation container bin = ctools.ctbin(obs) @@ -227,7 +227,7 @@ class Test(gammalib.GPythonTestSuite): # Load counts cube and check content. evt = gammalib.GCTAEventCube("cntmap2.fits") - self._test_cube(evt, 5542, multiplier=3) + self._check_cube(evt, 5542, multiplier=3) # Clear tool #bin.clear() @@ -242,9 +242,9 @@ class Test(gammalib.GPythonTestSuite): return # Check observation - def _test_observation(self, ctbin, nevents, multiplier=1): + def _check_observation(self, ctbin, nevents, multiplier=1): """ - Test content of an observation. + Check content of an observation. Args: ctbin: ctbin instance @@ -270,15 +270,15 @@ class Test(gammalib.GPythonTestSuite): "Pointing Declination is 22.01 deg") # Test event cube - self._test_cube(obs.events(), nevents, multiplier=multiplier) + self._check_cube(obs.events(), nevents, multiplier=multiplier) # Return return # Check event cube - def _test_cube(self, cube, nevents, multiplier=1): + def _check_cube(self, cube, nevents, multiplier=1): """ - Test content of event cube." + Check content of event cube." Args: cube: Event cube diff --git a/test/test_python_cscripts.py b/test/test_python_cscripts.py index c3266af..5067331 100755 --- a/test/test_python_cscripts.py +++ b/test/test_python_cscripts.py @@ -18,12 +18,13 @@ # along with this program. If not, see . # # ========================================================================== +import os +import sys import gammalib import cscripts -import sys -import os sys.path.append(os.path.dirname(os.path.abspath(__file__))) import test_cscaldb +import test_cslightcrv # ================== # @@ -37,13 +38,16 @@ def test(installed=False): suites = gammalib.GTestSuites("cscripts Python module unit testing") # Allocate test suites and append them to the container - suite_cscaldb = test_cscaldb.Test() + suite_cscaldb = test_cscaldb.Test() + suite_cslightcrv = test_cslightcrv.Test() # Setup unit tests suite_cscaldb.set() + suite_cslightcrv.set() # Append tests to container suites.append(suite_cscaldb) + suites.append(suite_cslightcrv) # If we have an installed version then create a temporary directory and # copy over all information that is needed @@ -60,9 +64,13 @@ def test(installed=False): dirname = os.path.dirname(testdir) # Copy over test data and irf - #os.system("cp -r %s %s" % (dirname+"/data", "data")) + os.system("cp -r %s %s" % (dirname+"/data", "data")) #os.system("cp -r %s %s" % (dirname+"/irf", "irf")) + # ... otherwise set the calibration database + else: + os.environ['CALDB'] = "../caldb" + # Set PFILES environment variable try: os.mkdir("pfiles") @@ -97,8 +105,6 @@ def test(installed=False): # Main routine entry point # # ======================== # if __name__ == '__main__': - """ - Perform unit testing for cscripts used from Python. - """ + # Run tests test() -- libgit2 0.21.4