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