Commit d0e0c47b2cc6d12bde1ddbefbdd608662f68a531

Authored by Jürgen Knödlseder
2 parents 44a68b8f 22bfe352

Merge branch '1579-add-csobs2caldb' into integration

# Conflicts:
#	cscripts/Makefile.am
#	doc/source/reference_manual/index.rst
.gitignore
... ... @@ -63,6 +63,7 @@ src/ctbutterfly/ctbutterfly
63 63 src/ctulimit/ctulimit
64 64 src/cterror/cterror
65 65 test/pfiles/
  66 +test/caldb/
66 67 test/*.fits
67 68 test/*.dat
68 69 test/*.xml
... ...
cscripts/Makefile.am
... ... @@ -5,12 +5,14 @@ pythonscripts = __init__.py \
5 5 cspull.py cssens.py cstsdist.py cscaldb.py csresmap.py \
6 6 cshessobs.py csobsdef.py csspec.py cslightcrv.py csobsinfo.py \
7 7 csinfo.py csmodelinfo.py csmodelmerge.py cstsmapmerge.py \
  8 + csobs2caldb.py \
8 9 obsutils.py
9 10  
10 11 # Scripts for which symbolic links will be created in bindir
11 12 linkscripts = cspull cssens cstsdist cscaldb csresmap \
12 13 cshessobs csobsdef csspec cslightcrv csobsinfo \
13 14 csinfo csmodelinfo csmodelmerge cstsmapmerge
  15 + csobs2caldb
14 16  
15 17 # Distribute scripts
16 18 EXTRA_DIST = $(binscripts) $(pythonscripts)
... ...
cscripts/csobs2caldb.py 0 → 100755
  1 +#!/usr/bin/env python
  2 +# ==========================================================================
  3 +# Generation of an caldb entry from general IACT observation.
  4 +#
  5 +# Copyright (C) 2015 Michael Mayer
  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 gammalib
  22 +import ctools
  23 +import sys
  24 +import os
  25 +import datetime
  26 +
  27 +
  28 +# ================= #
  29 +# csobs2caldb class #
  30 +# ================= #
  31 +class csobs2caldb(ctools.cscript):
  32 + """
  33 + This class implements the creation of a caldb entry for a particular
  34 + observation, which might be helpful for running simulations for current
  35 + IACTs. It takes an observation definition file as input and uses the
  36 + first observation to create a caldb entry which can be used for
  37 + simulations and sensitivity studies later on.
  38 + """
  39 + def __init__(self, *argv):
  40 + """
  41 + Constructor.
  42 + """
  43 + # Set name
  44 + self.name = "csobs2caldb"
  45 + self.version = "1.1.0"
  46 +
  47 + # Make sure that parfile exists
  48 + file = self.parfile()
  49 +
  50 + # Initialise application
  51 + if len(argv) == 0:
  52 + ctools.cscript.__init__(self, self.name, self.version)
  53 + elif len(argv) == 1:
  54 + ctools.cscript.__init__(self, self.name, self.version, *argv)
  55 + else:
  56 + raise TypeError("Invalid number of arguments given.")
  57 +
  58 + # Set logger properties
  59 + self.log_header()
  60 + self.log.date(True)
  61 +
  62 + # Return
  63 + return
  64 +
  65 + def __del__(self):
  66 + """
  67 + Destructor.
  68 + """
  69 + # Return
  70 + return
  71 +
  72 + def parfile(self):
  73 + """
  74 + Check if parfile exists. If parfile does not exist then create a
  75 + default parfile. This kluge avoids shipping the cscript with a
  76 + parfile.
  77 + """
  78 +
  79 + # Set parfile name
  80 + parfile = self.name+".par"
  81 +
  82 + try:
  83 + pars = gammalib.GApplicationPars(parfile)
  84 + except:
  85 + # Signal that parfile was not found
  86 + print("Parfile \""+parfile+"\" not found. Create default parfile.")
  87 +
  88 + # Create default parfile
  89 + pars = gammalib.GApplicationPars()
  90 + pars.append(gammalib.GApplicationPar("inobs","f","a","obs.xml","","","Input observation definition file"))
  91 + pars.append(gammalib.GApplicationPar("rspname","s","a","NONE","","","Response output name (e.g. Zenith50)"))
  92 + pars.append(gammalib.GApplicationPar("outirfs","f","h","irf_file.fits","","","Output IRF file name"))
  93 + pars.append(gammalib.GApplicationPar("index","i","h","0","","","Index of observation to be used from XML file"))
  94 + pars.append_standard()
  95 + pars.append(gammalib.GApplicationPar("logfile","f","h","csobs2caldb.log","","","Log filename"))
  96 + pars.save(parfile)
  97 +
  98 + # Return
  99 + return
  100 +
  101 + def get_parameters(self):
  102 + """
  103 + Get parameters from parfile and setup the observation.
  104 + """
  105 + # Get parameters
  106 + self.inobs = self["inobs"].filename()
  107 + self.obs = gammalib.GObservations(self.inobs)
  108 +
  109 + # Get index of observation to be used (0=default)
  110 + inx = self["index"].integer()
  111 +
  112 + # Get observation
  113 + self.observation = self.obs[inx]
  114 +
  115 + # Set instrument mission to cta
  116 + # This is to be compliant with standard gammalib caldb
  117 + self.m_mission = "CTA"
  118 +
  119 + # Get instrument name
  120 + self.m_instrument = self.observation.instrument()
  121 +
  122 + # Get config and file name
  123 + self.m_rspname = self["rspname"].string()
  124 + self.m_outfile = self["outirfs"].filename()
  125 +
  126 + # Make sure we have a cta observation
  127 + if not self.observation.classname() == "GCTAObservation":
  128 + raise gammalib.Exception.runtime_error("Input observation not of type \"GCTAObservation\"")
  129 +
  130 + # Make sure we have an irf response
  131 + if not self.observation.response().classname() == "GCTAResponseIrf":
  132 + raise gammalib.Exception.runtime_error("Response of input observation not of type \"GCTAResponseIrf\"")
  133 +
  134 + # Get other parameters
  135 + self.m_clobber = self["clobber"].boolean()
  136 +
  137 + # Return
  138 + return
  139 +
  140 + def make_irf_file(self):
  141 +
  142 + # retrieve response component
  143 + rsp = self.observation.response()
  144 +
  145 + # Initialise FITS file
  146 + fits = gammalib.GFits()
  147 +
  148 + # Open FITS files of response components
  149 + fits_aeff = gammalib.GFits(rsp.aeff().filename())
  150 + fits_psf = gammalib.GFits(rsp.psf().filename())
  151 + fits_edisp = gammalib.GFits(rsp.edisp().filename())
  152 + fits_bkg = gammalib.GFits(rsp.background().filename())
  153 +
  154 + # Bundle IRFs into one file
  155 + fits.append(fits_aeff["EFFECTIVE AREA"])
  156 + fits.append(fits_psf["POINT SPREAD FUNCTION"])
  157 + fits.append(fits_edisp["ENERGY DISPERSION"])
  158 + fits.append(fits_bkg["BACKGROUND"])
  159 +
  160 + # Return fits file
  161 + return fits
  162 +
  163 + def make_caldb_index(self):
  164 +
  165 + # Open calibration database index
  166 + indx_file = self.base_dir+"/caldb.indx"
  167 +
  168 + # Check if indx file exists (create if not existent)
  169 + if os.path.isfile(indx_file):
  170 + cif = gammalib.GFits(indx_file)
  171 + else:
  172 + cif = gammalib.GFits(indx_file, True)
  173 +
  174 + # Retrieve "CIF" table
  175 + if cif.contains("CIF"):
  176 + table = cif.table("CIF")
  177 + else:
  178 + # Create binary table if not available
  179 + bintable = gammalib.GFitsBinTable()
  180 + bintable.extname("CIF")
  181 + bintable.card("CIFVERSN", "1992a", "Version of CIF format")
  182 + cif.append(bintable)
  183 +
  184 + table = cif.table("CIF")
  185 +
  186 + # Copied from cta_root2caldb.py
  187 + # Attach columns. Reference: CAL/GEN/92-008
  188 + table.append(gammalib.GFitsTableStringCol("TELESCOP", 0, 10))
  189 + table.append(gammalib.GFitsTableStringCol("INSTRUME", 0, 10))
  190 + table.append(gammalib.GFitsTableStringCol("DETNAM", 0, 20))
  191 + table.append(gammalib.GFitsTableStringCol("FILTER", 0, 10))
  192 + table.append(gammalib.GFitsTableStringCol("CAL_DEV", 0, 20))
  193 + table.append(gammalib.GFitsTableStringCol("CAL_DIR", 0, 70))
  194 + table.append(gammalib.GFitsTableStringCol("CAL_FILE", 0, 40))
  195 + table.append(gammalib.GFitsTableStringCol("CAL_CLAS", 0, 3))
  196 + table.append(gammalib.GFitsTableStringCol("CAL_DTYP", 0, 4))
  197 + table.append(gammalib.GFitsTableStringCol("CAL_CNAM", 0, 20))
  198 + table.append(gammalib.GFitsTableStringCol("CAL_CBD", 0, 70, 9))
  199 + table.append(gammalib.GFitsTableShortCol("CAL_XNO", 0))
  200 + table.append(gammalib.GFitsTableStringCol("CAL_VSD", 0, 10))
  201 + table.append(gammalib.GFitsTableStringCol("CAL_VST", 0, 8))
  202 + table.append(gammalib.GFitsTableDoubleCol("REF_TIME", 0))
  203 + table.append(gammalib.GFitsTableShortCol("CAL_QUAL", 0))
  204 + table.append(gammalib.GFitsTableStringCol("CAL_DATE", 0, 8))
  205 + table.append(gammalib.GFitsTableStringCol("CAL_DESC", 0, 70))
  206 +
  207 + # Check if output config already exist
  208 + has_config = False
  209 + row_index = -1
  210 + for caldir in table["CAL_DIR"]:
  211 + row_index += 1
  212 + if caldir == self.cal_dir:
  213 + has_config = True
  214 + break
  215 +
  216 + # Create columns if not available
  217 + if not has_config:
  218 + # Append 4 rows to CIF extension
  219 + table.append_rows(4)
  220 + row_index = table.nrows()
  221 + else:
  222 + row_index += 4
  223 +
  224 + # Add generic information for these 4 rows
  225 + for i in range(4):
  226 +
  227 + # Set row number
  228 + row = i + row_index-4
  229 +
  230 + # Set date
  231 + now = str(datetime.datetime.now())
  232 + today, time = now.split()
  233 +
  234 + # Set element
  235 + table["TELESCOP"][row] = self.m_mission
  236 + table["INSTRUME"][row] = self.m_instrument
  237 + table["DETNAM"][row] = "NONE"
  238 + table["FILTER"][row] = "NONE"
  239 + table["CAL_DEV"][row] = "ONLINE"
  240 + table["CAL_CLAS"][row] = "BCF"
  241 + table["CAL_DTYP"][row] = "DATA"
  242 + table["CAL_VSD"][row] = today
  243 + table["CAL_VST"][row] = time.split(".")[0]
  244 + table["REF_TIME"][row] = 51544.0
  245 + table["CAL_QUAL"][row] = 0
  246 + table["CAL_CBD"][row] = "NAME("+self.m_rspname+")"
  247 + table["CAL_DATE"][row] = today.replace("-","/")[2:]
  248 + table["CAL_DIR"][row] = self.cal_dir
  249 + table["CAL_FILE"][row] = self.m_outfile
  250 +
  251 + # Add effective area information
  252 + row = row_index-4
  253 + table["CAL_CNAM"][row] = "EFF_AREA"
  254 + table["CAL_DESC"][row] = self.m_instrument+" effective area"
  255 +
  256 + # Add point spread function information
  257 + row = row_index-3
  258 + table["CAL_CNAM"][row] = "RPSF"
  259 + table["CAL_DESC"][row] = self.m_instrument+" point spread function"
  260 +
  261 + # Add energy dispersion information
  262 + row = row_index-2
  263 + table["CAL_CNAM"][row] = "EDISP"
  264 + table["CAL_DESC"][row] = self.m_instrument+" energy dispersion"
  265 +
  266 + # Add background information
  267 + row = row_index-1
  268 + table["CAL_CNAM"][row] = "BGD"
  269 + table["CAL_DESC"][row] = self.m_instrument+" background"
  270 +
  271 + # Return CIF FITS file
  272 + return cif
  273 +
  274 + def make_dirs(self):
  275 +
  276 + # Create gammalib Caldb
  277 + caldb = gammalib.GCaldb()
  278 +
  279 + # Set calibration directory
  280 + self.cal_dir = "data"
  281 + self.cal_dir += "/"+self.m_mission.lower()
  282 + self.cal_dir += "/"+self.m_instrument.lower()
  283 + self.cal_dir += "/bcf/"+self.m_rspname
  284 +
  285 + # Set absolute path
  286 + self.base_dir = caldb.rootdir() +"/data"
  287 + self.base_dir += "/"+self.m_mission.lower()
  288 + self.base_dir += "/"+self.m_instrument.lower()
  289 +
  290 + # Set directory for irf file
  291 + self.rsp_dir = caldb.rootdir() + "/" + self.cal_dir
  292 +
  293 + # Create directories and log information
  294 + if not os.path.isdir(self.rsp_dir):
  295 + if self.logExplicit():
  296 + self.log(gammalib.parformat("Directory"))
  297 + self.log(self.rsp_dir)
  298 + self.log("\n")
  299 + os.makedirs(self.rsp_dir)
  300 + else:
  301 + if self.logExplicit():
  302 + self.log(gammalib.parformat("Directory (existing)"))
  303 + self.log(self.rsp_dir)
  304 + self.log("\n")
  305 +
  306 + # Return
  307 + return
  308 +
  309 + def run(self):
  310 + """
  311 + Run the script.
  312 + """
  313 + # Switch screen logging on in debug mode
  314 + if self.logDebug():
  315 + self.log.cout(True)
  316 +
  317 + # Get parameters
  318 + self.get_parameters()
  319 +
  320 + # Write input parameters into logger
  321 + if self.logTerse():
  322 + self.log_parameters()
  323 + self.log("\n\n")
  324 + self.log.header1("Creating directory structure")
  325 +
  326 + # Create directory structure
  327 + self.make_dirs()
  328 +
  329 + # Logging
  330 + if self.logTerse():
  331 + self.log("\n")
  332 + self.log.header1("Updating calibration database")
  333 +
  334 + # Create/update calibration database
  335 + self.caldb_inx = self.make_caldb_index()
  336 +
  337 + # Logging
  338 + if self.logTerse():
  339 + self.log("\n")
  340 + self.log.header1("Creating response file")
  341 +
  342 + # Create IRF file
  343 + self.irf_fits = self.make_irf_file()
  344 +
  345 + # Return
  346 + return
  347 +
  348 + def save(self):
  349 +
  350 + # Save caldb index file
  351 + self.caldb_inx.save(self.m_clobber)
  352 +
  353 + # Save response file
  354 + self.irf_fits.saveto(self.rsp_dir + "/" + self.m_outfile, self.m_clobber)
  355 +
  356 + # Return
  357 + return
  358 +
  359 + def execute(self):
  360 + """
  361 + Execute the script.
  362 + """
  363 + # Run the script
  364 + self.run()
  365 +
  366 + # Save to disk
  367 + self.save()
  368 +
  369 + # Return
  370 + return
  371 +
  372 +
  373 +# ======================== #
  374 +# Main routine entry point #
  375 +# ======================== #
  376 +if __name__ == '__main__':
  377 + """
  378 + Generates caldb entry.
  379 + """
  380 + # Create instance of application
  381 + app = csobs2caldb(sys.argv)
  382 +
  383 + # Open logfile
  384 + app.logFileOpen()
  385 +
  386 + # Execute application
  387 + app.execute()
... ...
doc/source/reference_manual/csobs2caldb.rst 0 → 100644
  1 +.. _csobs2caldb:
  2 +
  3 +csobs2caldb
  4 +===========
  5 +
  6 +Creates a caldb entry from an input observation.
  7 +
  8 +
  9 +Synopsis
  10 +--------
  11 +
  12 +This script creates an entry in the local calibration database from an
  13 +input observation container. The caldb entry can be used for simulations
  14 +with :doc:`ctobssim` later on. On default csobs2caldb uses the first observation
  15 +in the given container. This can be modified using the hidden parameter "index"
  16 +(e.g. index=3 instructs the tool to use the third observation in the container).
  17 +Note that for the moment, the script only works for unbinned observations which
  18 +use the latest CTA IRF format (i.e. 2D/3D FITS binary tables).
  19 +
  20 +In particular this script might be useful for simulations of current IACT data.
  21 +Provided a specific observation with IRFs e.g. at certain zenith/azimuth angle,
  22 +the user can create a caldb entry using this tool and subsequently simulate e.g.
  23 +50 hours using this configuration.
  24 +
  25 +General parameters
  26 +------------------
  27 +
  28 +``inobs [file]``
  29 + Input event list, counts cube or observation definition XML file.
  30 +
  31 +``rspname [string]``
  32 + Response output name (e.g. Zenith50).
  33 +
  34 +``(outirfs = irf_file.fits) [file]``
  35 + Output IRF file name.
  36 +
  37 +``(index = 0) [integer]``
  38 + Index of observation to be used from XML file.
  39 +
  40 +
  41 +Standard parameters
  42 +-------------------
  43 +
  44 +``(chatter = 2) [integer]``
  45 + Verbosity of the executable:
  46 + chatter = 0: no information will be logged
  47 +
  48 + chatter = 1: only errors will be logged
  49 +
  50 + chatter = 2: errors and actions will be logged
  51 +
  52 + chatter = 3: report about the task execution
  53 +
  54 + chatter = 4: detailed report about the task execution
  55 +
  56 +``(clobber = yes) [boolean]``
  57 + Specifies whether an existing output counts cube should be overwritten.
  58 +
  59 +``(debug = no) [boolean]``
  60 + Enables debug mode. In debug mode the executable will dump any log file output to the console.
  61 +
  62 +``(mode = ql) [string]``
  63 + Mode of automatic parameters (default is "ql", i.e. "query and learn").
  64 +
  65 +``(logfile = csobs2caldb.log) [filename]``
  66 + Log filename.
  67 +
  68 +
  69 +Related tools or scripts
  70 +------------------------
  71 +
  72 +:doc:`cscaldb`
... ...
doc/source/reference_manual/index.rst
... ... @@ -49,3 +49,4 @@ cscripts
49 49 csmodelinfo --- Shows model container content <csmodelinfo>
50 50 csmodelmerge --- Merges several model containers into one file <csmodelmerge>
51 51 cstsmapmerge --- Merges slices from ts map computations <cstsmapmerge>
  52 + csobs2caldb --- Creates a caldb entry from an input observation <csobs2caldb>
... ...
test/data/obs_unbinned.xml 0 → 100644
  1 +<?xml version="1.0" encoding="UTF-8" standalone="no"?>
  2 +<observation_list title="observation list">
  3 + <observation name="" id="00001" instrument="CTA">
  4 + <parameter name="EventList" file="data/crab_events.fits" />
  5 + <parameter name="EffectiveArea" file="irf/irf_file.fits" />
  6 + <parameter name="PointSpreadFunction" file="irf/irf_file.fits" />
  7 + <parameter name="EnergyDispersion" file="irf/irf_file.fits" />
  8 + <parameter name="Background" file="irf/irf_file.fits" />
  9 + </observation>
  10 +</observation_list>
... ...
test/irf/irf_file.fits.gz 0 → 100644
No preview for this file type
test/test_cscripts.sh.in
... ... @@ -42,11 +42,13 @@ rm -rf *.fits *.xml *.dat *.reg
42 42 #
43 43 # Creates pfiles directory
44 44 # ========================
45   -rm -rf pfiles
46   -mkdir -p pfiles
47   -cp -r ../src/*/*.par pfiles/
48 45 export PFILES=pfiles
49   -
  46 +export CALDB=$PWD/caldb
  47 +rm -rf $PFILES
  48 +mkdir -p $PFILES
  49 +cp -r ../src/*/*.par pfiles/
  50 +rm -rf $CALDB
  51 +mkdir -p $CALDB
50 52  
51 53 #
52 54 # Test csobsdef
... ... @@ -72,6 +74,24 @@ else
72 74 fi
73 75 $ECHO " ok"
74 76  
  77 +#
  78 +# Test csobs2caldb
  79 +# ================
  80 +$ECHO -n "Test csobs2caldb: "
  81 +chmod +x @abs_top_srcdir@/cscripts/csobs2caldb.py
  82 +@abs_top_srcdir@/cscripts/csobs2caldb.py \
  83 + inobs="data/obs_unbinned.xml" \
  84 + rspname="test_irfs" \
  85 + chatter=4 #> csobs2caldb.log
  86 +$ECHO -n "."
  87 +if [ -s "$CALDB/data/cta/cta/bcf/test_irfs/irf_file.fits" ]
  88 +then
  89 + $ECHO -n "."
  90 +else
  91 + $ECHO " $CALDB/data/cta/cta/bcf/test_irfs/irf_file.fits file is not valid"
  92 + exit 1
  93 +fi
  94 +$ECHO " ok"
75 95  
76 96 #
77 97 # Test csobsinfo
... ... @@ -279,8 +299,8 @@ chmod +x @abs_top_srcdir@/cscripts/cspull.py
279 299 inmodel="data/crab.xml" \
280 300 outfile="pull.dat" \
281 301 ntrials="3" \
282   - caldb="irf" \
283   - irf="cta_dummy_irf" \
  302 + caldb="cta" \
  303 + irf="test_irfs" \
284 304 ra=83.6331 \
285 305 dec=22.0145 \
286 306 emin=0.1 \
... ...