Commit cfd3f245f0c333cf15c46ab32927866231f54937

Authored by Jürgen Knödlseder
1 parent f29ec62f

Add csadd2caldb.py script

cscripts/csadd2caldb.py 0 → 100755
  1 +#! /usr/bin/env python
  2 +# ==========================================================================
  3 +# Add IRFs to CALDB
  4 +#
  5 +# Copyright (C) 2021 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 sys
  23 +import glob
  24 +import tarfile
  25 +import gammalib
  26 +import ctools
  27 +
  28 +
  29 +# ================= #
  30 +# csadd2caldb class #
  31 +# ================= #
  32 +class csadd2caldb(ctools.cscript):
  33 + """
  34 + Add IRFs to CALDB
  35 + """
  36 +
  37 + # Constructor
  38 + def __init__(self, *argv):
  39 + """
  40 + Constructor
  41 + """
  42 + # Make sure that parfile exists
  43 + self._parfile()
  44 +
  45 + # Initialise application by calling the base class constructor
  46 + self._init_cscript(self.__class__.__name__, ctools.__version__, argv)
  47 +
  48 + # Return
  49 + return
  50 +
  51 +
  52 + # Private methods
  53 + def _parfile(self):
  54 + """
  55 + Check if parfile exists. If parfile does not exist then create a
  56 + default parfile. This kluge avoids shipping the cscript with a parfile.
  57 + """
  58 + # Set parfile name
  59 + parfile = self.__class__.__name__+'.par'
  60 +
  61 + # Load parfile or create
  62 + try:
  63 + pars = gammalib.GApplicationPars(parfile)
  64 + except:
  65 + # Signal that parfile was not found
  66 + print('Parfile '+parfile+' not found. Create default parfile.')
  67 +
  68 + # Create default parfile
  69 + pars = gammalib.GApplicationPars()
  70 + pars.append(gammalib.GApplicationPar('indir','f','a','cta-prod5-zenodo-fitsonly-v0','','','Input IRF folder'))
  71 + pars.append(gammalib.GApplicationPar('outdir','f','a','$CALDB','','','Output caldb folder'))
  72 + pars.append_standard()
  73 + pars.save(parfile)
  74 +
  75 + # Return
  76 + return
  77 +
  78 + def _get_parameters(self):
  79 + """
  80 + Get parameters from parfile
  81 + """
  82 + # Query parameters
  83 + self['indir'].filename()
  84 + self['outdir'].filename()
  85 +
  86 + # Write input parameters into logger
  87 + self._log_parameters(gammalib.TERSE)
  88 +
  89 + # Return
  90 + return
  91 +
  92 + def _collect_tarfiles(self, indir):
  93 + """
  94 + Collect tarfiles
  95 +
  96 + Parameters
  97 + ----------
  98 + indir : string
  99 + Input directory
  100 +
  101 + Returns
  102 + -------
  103 + tarfiles : list of str
  104 + List of tarfiles
  105 + """
  106 + # Initialise list of tarfiles
  107 + tarfiles = []
  108 +
  109 + # Search for tarfiles in current directory
  110 + files = glob.glob('%s/*.FITS.tar.gz' % indir.url())
  111 + tarfiles.extend(files)
  112 +
  113 + # Search for tarfiles in 'files' subdirectory
  114 + files = glob.glob('%s/fits/*.FITS.tar.gz' % indir.url())
  115 + tarfiles.extend(files)
  116 +
  117 + # Return tarfiles
  118 + return tarfiles
  119 +
  120 + def _extract_irfs(self, file, outdir):
  121 + """
  122 + Collect IRFs from tarfile
  123 +
  124 + Parameters
  125 + ----------
  126 + file : str
  127 + Tarfile with IRFs
  128 + outdir : str
  129 + Output directory
  130 +
  131 + Returns
  132 + -------
  133 + irfs : list
  134 + List of IRF dictionaries
  135 + """
  136 + # Initialise list of extracted IRFs
  137 + irfs = []
  138 +
  139 + # Open tarfile
  140 + tar = tarfile.open(file)
  141 +
  142 + # Loop over all members in tarfile
  143 + for member in tar.getmembers():
  144 +
  145 + # Extract Instrument
  146 + if 'South' in member.name:
  147 + site = 'South'
  148 + else:
  149 + site = 'North'
  150 + if '180000s' in member.name:
  151 + duration = '50h'
  152 + elif '18000s' in member.name:
  153 + duration = '5h'
  154 + elif '1800s' in member.name:
  155 + duration = '0.5h'
  156 + if 'SouthAz' in member.name:
  157 + orientation = '_S'
  158 + azimuth = 180.0
  159 + elif 'NorthAz' in member.name:
  160 + orientation = '_N'
  161 + azimuth = 0.0
  162 + else:
  163 + orientation = ''
  164 + azimuth = 90.0
  165 + elements = member.name.split('-')
  166 + zenith = [e for e in elements if 'deg' in e][0].strip('deg')
  167 + instrument = '%s_z%s%s_%s' % (site, zenith, orientation, duration)
  168 +
  169 + # Build output directory
  170 + path = '%s/%s' % (outdir, instrument)
  171 +
  172 + # Write header
  173 + self._log_header3(gammalib.TERSE, 'Extract IRFs from "%s"' % member.name)
  174 + self._log_value(gammalib.NORMAL, 'Instrument', instrument)
  175 +
  176 + # Extract file
  177 + tar.extract(member, path='%s/%s' % (self['outdir'].filename().url(), path))
  178 +
  179 + # Create IRF record
  180 + record = {'path': path, 'instrument': instrument, 'file': member.name,
  181 + 'site': site, 'duration': duration, 'zenith': zenith, 'azimuth': azimuth,
  182 + 'orientation': orientation}
  183 +
  184 + # Add record
  185 + irfs.append(record)
  186 +
  187 + # Close tarfile
  188 + tar.close()
  189 +
  190 + # Return
  191 + return irfs
  192 +
  193 + def _add_irf_to_cif(self, prod, hdu, irf):
  194 + """
  195 + Add IRF to calibration index file HDU
  196 +
  197 + Parameters
  198 + ----------
  199 + prod : str
  200 + Production
  201 + hdu : ~gammalib.GFitsBinTable
  202 + CIF HDU table
  203 + irf : dict
  204 + IRF dictionary
  205 + """
  206 + # Set list of IRF component names
  207 + #names = ['EA', 'PSF', 'EDISP', 'BKG']
  208 + components = [{'name': 'EA', 'CNAM': 'EFF_AREA', 'DESC': 'CTA effective area'},
  209 + {'name': 'PSF', 'CNAM': 'RPSF', 'DESC': 'CTA point spread function'},
  210 + {'name': 'EDISP', 'CNAM': 'EDISP', 'DESC': 'CTA energy dispersion'},
  211 + {'name': 'BKG', 'CNAM': 'BKG', 'DESC': 'CTA background'}]
  212 +
  213 + # Initialise CIF row index
  214 + row = hdu.nrows()
  215 +
  216 + # Append rows for all components to CIF extension
  217 + hdu.append_rows(len(components))
  218 +
  219 + # Add information for all components
  220 + for component in components:
  221 +
  222 + # Set calibration information
  223 + cal_name = 'NAME(%s)' % irf['instrument']
  224 + cal_version = 'VERSION(%s)' % prod
  225 + cal_cut = 'CLASS(BEST)'
  226 + cal_analysis = 'ANALYSIS(CTA)'
  227 + cal_zenith = 'ZENITH(%.3f)deg' % float(irf['zenith'])
  228 + cal_azimuth = 'AZIMUTH(%.3f)deg' % float(irf['azimuth'])
  229 + cal_bounds = [cal_name, cal_version, cal_cut, cal_analysis, \
  230 + cal_zenith, cal_azimuth]
  231 +
  232 + # Set generic information
  233 + hdu['TELESCOP'][row] = 'CTA'
  234 + hdu['INSTRUME'][row] = gammalib.toupper(prod)
  235 + hdu['DETNAM'][row] = 'NONE'
  236 + hdu['FILTER'][row] = 'NONE'
  237 + hdu['CAL_DEV'][row] = 'ONLINE'
  238 + hdu['CAL_CLAS'][row] = 'BCF'
  239 + hdu['CAL_DTYP'][row] = 'DATA'
  240 + hdu['CAL_VSD'][row] = '2014-01-30'
  241 + hdu['CAL_VST'][row] = '00:00:00'
  242 + hdu['REF_TIME'][row] = 51544.0
  243 + hdu['CAL_QUAL'][row] = 0 # 0=good, 1=bad, 2=dubious, ...
  244 + hdu['CAL_DATE'][row] = '14/01/30'
  245 +
  246 + # Set component specific information
  247 + hdu['CAL_DIR'][row] = irf['path']
  248 + hdu['CAL_FILE'][row] = irf['file']
  249 + hdu['CAL_CNAM'][row] = component['CNAM']
  250 + hdu['CAL_DESC'][row] = component['DESC']
  251 + hdu['CAL_XNO'][row] = 1
  252 + for i in range(9):
  253 + if i >= len(cal_bounds):
  254 + hdu['CAL_CBD'][row,i] = 'NONE'
  255 + else:
  256 + hdu['CAL_CBD'][row,i] = cal_bounds[i]
  257 +
  258 + # Increment row index
  259 + row += 1
  260 +
  261 + # Return
  262 + return
  263 +
  264 + def _create_cif_table(self):
  265 + """
  266 + Create Calibration Database Index File binary table
  267 +
  268 + Returns
  269 + -------
  270 + table : ~gammalib.GFitsBinTable
  271 + Calibration Database Index File binary table
  272 + """
  273 + # Create binary table
  274 + table = gammalib.GFitsBinTable()
  275 +
  276 + # Append columns. Reference: CAL/GEN/92-008
  277 + table.append(gammalib.GFitsTableStringCol('TELESCOP', 0, 10))
  278 + table.append(gammalib.GFitsTableStringCol('INSTRUME', 0, 10))
  279 + table.append(gammalib.GFitsTableStringCol('DETNAM', 0, 20))
  280 + table.append(gammalib.GFitsTableStringCol('FILTER', 0, 10))
  281 + table.append(gammalib.GFitsTableStringCol('CAL_DEV', 0, 20))
  282 + table.append(gammalib.GFitsTableStringCol('CAL_DIR', 0, 70))
  283 + table.append(gammalib.GFitsTableStringCol('CAL_FILE', 0, 70)) # Extend beyond standard
  284 + table.append(gammalib.GFitsTableStringCol('CAL_CLAS', 0, 3))
  285 + table.append(gammalib.GFitsTableStringCol('CAL_DTYP', 0, 4))
  286 + table.append(gammalib.GFitsTableStringCol('CAL_CNAM', 0, 20))
  287 + table.append(gammalib.GFitsTableStringCol('CAL_CBD', 0, 70, 9))
  288 + table.append(gammalib.GFitsTableShortCol('CAL_XNO', 0))
  289 + table.append(gammalib.GFitsTableStringCol('CAL_VSD', 0, 10))
  290 + table.append(gammalib.GFitsTableStringCol('CAL_VST', 0, 8))
  291 + table.append(gammalib.GFitsTableDoubleCol('REF_TIME', 0))
  292 + table.append(gammalib.GFitsTableShortCol('CAL_QUAL', 0))
  293 + table.append(gammalib.GFitsTableStringCol('CAL_DATE', 0, 8))
  294 + table.append(gammalib.GFitsTableStringCol('CAL_DESC', 0, 70))
  295 +
  296 + # Set keywords. Reference: CAL/GEN/92-008
  297 + table.extname('CIF')
  298 + table.card('CIFVERSN', '1992a', 'Version of CIF format')
  299 +
  300 + # Return table
  301 + return table
  302 +
  303 + def _add_irfs_to_cif(self, prod, irfs):
  304 + """
  305 + Add IRFs to calibration index file
  306 +
  307 + Parameters
  308 + ----------
  309 + prod : str
  310 + Production
  311 + irfs : list
  312 + List of IRFs
  313 + """
  314 + # Build calibration database index name
  315 + fname = '%s/data/cta/%s/caldb.indx' % (self['outdir'].filename().url(), prod)
  316 +
  317 + # Open calibration database index file
  318 + cif = gammalib.GFits(fname, True)
  319 +
  320 + # If file has no CIF extension than create it now
  321 + if not cif.contains('CIF'):
  322 + cif.append(self._create_cif_table())
  323 +
  324 + # Get calibration database index HDU
  325 + cif_hdu = cif.table('CIF')
  326 +
  327 + # Loop over all IRFs and add information to CIF
  328 + for irf in irfs:
  329 + self._add_irf_to_cif(prod, cif_hdu, irf)
  330 +
  331 + # Save and close CIF
  332 + cif.save(True)
  333 + cif.close()
  334 +
  335 + # Return
  336 + return
  337 +
  338 + def _add_irfs(self):
  339 + """
  340 + Add IRFs to calibration database
  341 + """
  342 + # Collect tarfiles
  343 + tarfiles = self._collect_tarfiles(self['indir'].filename())
  344 +
  345 + # Add IRFs in tarfiles
  346 + for file in tarfiles:
  347 +
  348 + # Extract Production
  349 + fname = os.path.basename(file)
  350 + elements = fname.split('-')
  351 + prod = [e for e in elements if 'prod' in e][0]
  352 + version = [e for e in elements if 'v' in e][0]
  353 + prod = '%s-%s' % (prod, version)
  354 +
  355 + # Build output directory
  356 + outdir = 'data/cta/%s/bcf' % (prod)
  357 +
  358 + # Write header and log parameters
  359 + self._log_header2(gammalib.TERSE, 'Extract IRFs')
  360 + self._log_value(gammalib.NORMAL, 'Tarfile', file)
  361 + self._log_value(gammalib.NORMAL, 'Production', prod)
  362 + self._log_value(gammalib.NORMAL, 'Target directory', outdir)
  363 +
  364 + # Extract IRFs from tarfile
  365 + irfs = self._extract_irfs(file, outdir)
  366 +
  367 + # Add IRFs to calibration index database
  368 + self._add_irfs_to_cif(prod, irfs)
  369 +
  370 + # Return
  371 + return
  372 +
  373 +
  374 + # Public methods
  375 + def run(self):
  376 + """
  377 + Run the script
  378 + """
  379 + # Switch screen logging on in debug mode
  380 + if self._logDebug():
  381 + self._log.cout(True)
  382 +
  383 + # Get parameters
  384 + self._get_parameters()
  385 +
  386 + # Write header
  387 + self._log_header1(gammalib.TERSE, 'Add IRFs to CALDB')
  388 +
  389 + # Add IRFs to CALDB
  390 + self._add_irfs()
  391 +
  392 + # Return
  393 + return
  394 +
  395 +
  396 +# ======================== #
  397 +# Main routine entry point #
  398 +# ======================== #
  399 +if __name__ == '__main__':
  400 +
  401 + # Create instance of application
  402 + app = csadd2caldb(sys.argv)
  403 +
  404 + # Execute application
  405 + app.execute()
... ...
doc/source/users/user_manual/csadd2caldb.py 0 → 100755
  1 +#! /usr/bin/env python
  2 +# ==========================================================================
  3 +# Add IRFs to CALDB
  4 +#
  5 +# Copyright (C) 2021 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 sys
  23 +import glob
  24 +import tarfile
  25 +import gammalib
  26 +import ctools
  27 +
  28 +
  29 +# ================= #
  30 +# csadd2caldb class #
  31 +# ================= #
  32 +class csadd2caldb(ctools.cscript):
  33 + """
  34 + Add IRFs to CALDB
  35 + """
  36 +
  37 + # Constructor
  38 + def __init__(self, *argv):
  39 + """
  40 + Constructor
  41 + """
  42 + # Make sure that parfile exists
  43 + self._parfile()
  44 +
  45 + # Initialise application by calling the base class constructor
  46 + self._init_cscript(self.__class__.__name__, ctools.__version__, argv)
  47 +
  48 + # Return
  49 + return
  50 +
  51 +
  52 + # Private methods
  53 + def _parfile(self):
  54 + """
  55 + Check if parfile exists. If parfile does not exist then create a
  56 + default parfile. This kluge avoids shipping the cscript with a parfile.
  57 + """
  58 + # Set parfile name
  59 + parfile = self.__class__.__name__+'.par'
  60 +
  61 + # Load parfile or create
  62 + try:
  63 + pars = gammalib.GApplicationPars(parfile)
  64 + except:
  65 + # Signal that parfile was not found
  66 + print('Parfile '+parfile+' not found. Create default parfile.')
  67 +
  68 + # Create default parfile
  69 + pars = gammalib.GApplicationPars()
  70 + pars.append(gammalib.GApplicationPar('indir','f','a','cta-prod5-zenodo-fitsonly-v0','','','Input IRF folder'))
  71 + pars.append(gammalib.GApplicationPar('outdir','f','a','$CALDB','','','Output caldb folder'))
  72 + pars.append_standard()
  73 + pars.save(parfile)
  74 +
  75 + # Return
  76 + return
  77 +
  78 + def _get_parameters(self):
  79 + """
  80 + Get parameters from parfile
  81 + """
  82 + # Query parameters
  83 + self['indir'].filename()
  84 + self['outdir'].filename()
  85 +
  86 + # Write input parameters into logger
  87 + self._log_parameters(gammalib.TERSE)
  88 +
  89 + # Return
  90 + return
  91 +
  92 + def _collect_tarfiles(self, indir):
  93 + """
  94 + Collect tarfiles
  95 +
  96 + Parameters
  97 + ----------
  98 + indir : string
  99 + Input directory
  100 +
  101 + Returns
  102 + -------
  103 + tarfiles : list of str
  104 + List of tarfiles
  105 + """
  106 + # Initialise list of tarfiles
  107 + tarfiles = []
  108 +
  109 + # Search for tarfiles in current directory
  110 + files = glob.glob('%s/*.FITS.tar.gz' % indir.url())
  111 + tarfiles.extend(files)
  112 +
  113 + # Search for tarfiles in 'files' subdirectory
  114 + files = glob.glob('%s/fits/*.FITS.tar.gz' % indir.url())
  115 + tarfiles.extend(files)
  116 +
  117 + # Return tarfiles
  118 + return tarfiles
  119 +
  120 + def _extract_irfs(self, file, outdir):
  121 + """
  122 + Collect IRFs from tarfile
  123 +
  124 + Parameters
  125 + ----------
  126 + file : str
  127 + Tarfile with IRFs
  128 + outdir : str
  129 + Output directory
  130 +
  131 + Returns
  132 + -------
  133 + irfs : list
  134 + List of IRF dictionaries
  135 + """
  136 + # Initialise list of extracted IRFs
  137 + irfs = []
  138 +
  139 + # Open tarfile
  140 + tar = tarfile.open(file)
  141 +
  142 + # Loop over all members in tarfile
  143 + for member in tar.getmembers():
  144 +
  145 + # Extract Instrument
  146 + if 'South' in member.name:
  147 + site = 'South'
  148 + else:
  149 + site = 'North'
  150 + if '180000s' in member.name:
  151 + duration = '50h'
  152 + elif '18000s' in member.name:
  153 + duration = '5h'
  154 + elif '1800s' in member.name:
  155 + duration = '0.5h'
  156 + if 'SouthAz' in member.name:
  157 + orientation = '_S'
  158 + azimuth = 180.0
  159 + elif 'NorthAz' in member.name:
  160 + orientation = '_N'
  161 + azimuth = 0.0
  162 + else:
  163 + orientation = ''
  164 + azimuth = 90.0
  165 + elements = member.name.split('-')
  166 + zenith = [e for e in elements if 'deg' in e][0].strip('deg')
  167 + instrument = '%s_z%s%s_%s' % (site, zenith, orientation, duration)
  168 +
  169 + # Build output directory
  170 + path = '%s/%s' % (outdir, instrument)
  171 +
  172 + # Write header
  173 + self._log_header3(gammalib.TERSE, 'Extract IRFs from "%s"' % member.name)
  174 + self._log_value(gammalib.NORMAL, 'Instrument', instrument)
  175 +
  176 + # Extract file
  177 + tar.extract(member, path='%s/%s' % (self['outdir'].filename().url(), path))
  178 +
  179 + # Create IRF record
  180 + record = {'path': path, 'instrument': instrument, 'file': member.name,
  181 + 'site': site, 'duration': duration, 'zenith': zenith, 'azimuth': azimuth,
  182 + 'orientation': orientation}
  183 +
  184 + # Add record
  185 + irfs.append(record)
  186 +
  187 + # Close tarfile
  188 + tar.close()
  189 +
  190 + # Return
  191 + return irfs
  192 +
  193 + def _add_irf_to_cif(self, prod, hdu, irf):
  194 + """
  195 + Add IRF to calibration index file HDU
  196 +
  197 + Parameters
  198 + ----------
  199 + prod : str
  200 + Production
  201 + hdu : ~gammalib.GFitsBinTable
  202 + CIF HDU table
  203 + irf : dict
  204 + IRF dictionary
  205 + """
  206 + # Set list of IRF component names
  207 + #names = ['EA', 'PSF', 'EDISP', 'BKG']
  208 + components = [{'name': 'EA', 'CNAM': 'EFF_AREA', 'DESC': 'CTA effective area'},
  209 + {'name': 'PSF', 'CNAM': 'RPSF', 'DESC': 'CTA point spread function'},
  210 + {'name': 'EDISP', 'CNAM': 'EDISP', 'DESC': 'CTA energy dispersion'},
  211 + {'name': 'BKG', 'CNAM': 'BKG', 'DESC': 'CTA background'}]
  212 +
  213 + # Initialise CIF row index
  214 + row = hdu.nrows()
  215 +
  216 + # Append rows for all components to CIF extension
  217 + hdu.append_rows(len(components))
  218 +
  219 + # Add information for all components
  220 + for component in components:
  221 +
  222 + # Set calibration information
  223 + cal_name = 'NAME(%s)' % irf['instrument']
  224 + cal_version = 'VERSION(%s)' % prod
  225 + cal_cut = 'CLASS(BEST)'
  226 + cal_analysis = 'ANALYSIS(CTA)'
  227 + cal_zenith = 'ZENITH(%.3f)deg' % float(irf['zenith'])
  228 + cal_azimuth = 'AZIMUTH(%.3f)deg' % float(irf['azimuth'])
  229 + cal_bounds = [cal_name, cal_version, cal_cut, cal_analysis, \
  230 + cal_zenith, cal_azimuth]
  231 +
  232 + # Set generic information
  233 + hdu['TELESCOP'][row] = 'CTA'
  234 + hdu['INSTRUME'][row] = gammalib.toupper(prod)
  235 + hdu['DETNAM'][row] = 'NONE'
  236 + hdu['FILTER'][row] = 'NONE'
  237 + hdu['CAL_DEV'][row] = 'ONLINE'
  238 + hdu['CAL_CLAS'][row] = 'BCF'
  239 + hdu['CAL_DTYP'][row] = 'DATA'
  240 + hdu['CAL_VSD'][row] = '2014-01-30'
  241 + hdu['CAL_VST'][row] = '00:00:00'
  242 + hdu['REF_TIME'][row] = 51544.0
  243 + hdu['CAL_QUAL'][row] = 0 # 0=good, 1=bad, 2=dubious, ...
  244 + hdu['CAL_DATE'][row] = '14/01/30'
  245 +
  246 + # Set component specific information
  247 + hdu['CAL_DIR'][row] = irf['path']
  248 + hdu['CAL_FILE'][row] = irf['file']
  249 + hdu['CAL_CNAM'][row] = component['CNAM']
  250 + hdu['CAL_DESC'][row] = component['DESC']
  251 + hdu['CAL_XNO'][row] = 1
  252 + for i in range(9):
  253 + if i >= len(cal_bounds):
  254 + hdu['CAL_CBD'][row,i] = 'NONE'
  255 + else:
  256 + hdu['CAL_CBD'][row,i] = cal_bounds[i]
  257 +
  258 + # Increment row index
  259 + row += 1
  260 +
  261 + # Return
  262 + return
  263 +
  264 + def _create_cif_table(self):
  265 + """
  266 + Create Calibration Database Index File binary table
  267 +
  268 + Returns
  269 + -------
  270 + table : ~gammalib.GFitsBinTable
  271 + Calibration Database Index File binary table
  272 + """
  273 + # Create binary table
  274 + table = gammalib.GFitsBinTable()
  275 +
  276 + # Append columns. Reference: CAL/GEN/92-008
  277 + table.append(gammalib.GFitsTableStringCol('TELESCOP', 0, 10))
  278 + table.append(gammalib.GFitsTableStringCol('INSTRUME', 0, 10))
  279 + table.append(gammalib.GFitsTableStringCol('DETNAM', 0, 20))
  280 + table.append(gammalib.GFitsTableStringCol('FILTER', 0, 10))
  281 + table.append(gammalib.GFitsTableStringCol('CAL_DEV', 0, 20))
  282 + table.append(gammalib.GFitsTableStringCol('CAL_DIR', 0, 70))
  283 + table.append(gammalib.GFitsTableStringCol('CAL_FILE', 0, 70)) # Extend beyond standard
  284 + table.append(gammalib.GFitsTableStringCol('CAL_CLAS', 0, 3))
  285 + table.append(gammalib.GFitsTableStringCol('CAL_DTYP', 0, 4))
  286 + table.append(gammalib.GFitsTableStringCol('CAL_CNAM', 0, 20))
  287 + table.append(gammalib.GFitsTableStringCol('CAL_CBD', 0, 70, 9))
  288 + table.append(gammalib.GFitsTableShortCol('CAL_XNO', 0))
  289 + table.append(gammalib.GFitsTableStringCol('CAL_VSD', 0, 10))
  290 + table.append(gammalib.GFitsTableStringCol('CAL_VST', 0, 8))
  291 + table.append(gammalib.GFitsTableDoubleCol('REF_TIME', 0))
  292 + table.append(gammalib.GFitsTableShortCol('CAL_QUAL', 0))
  293 + table.append(gammalib.GFitsTableStringCol('CAL_DATE', 0, 8))
  294 + table.append(gammalib.GFitsTableStringCol('CAL_DESC', 0, 70))
  295 +
  296 + # Set keywords. Reference: CAL/GEN/92-008
  297 + table.extname('CIF')
  298 + table.card('CIFVERSN', '1992a', 'Version of CIF format')
  299 +
  300 + # Return table
  301 + return table
  302 +
  303 + def _add_irfs_to_cif(self, prod, irfs):
  304 + """
  305 + Add IRFs to calibration index file
  306 +
  307 + Parameters
  308 + ----------
  309 + prod : str
  310 + Production
  311 + irfs : list
  312 + List of IRFs
  313 + """
  314 + # Build calibration database index name
  315 + fname = '%s/data/cta/%s/caldb.indx' % (self['outdir'].filename().url(), prod)
  316 +
  317 + # Open calibration database index file
  318 + cif = gammalib.GFits(fname, True)
  319 +
  320 + # If file has no CIF extension than create it now
  321 + if not cif.contains('CIF'):
  322 + cif.append(self._create_cif_table())
  323 +
  324 + # Get calibration database index HDU
  325 + cif_hdu = cif.table('CIF')
  326 +
  327 + # Loop over all IRFs and add information to CIF
  328 + for irf in irfs:
  329 + self._add_irf_to_cif(prod, cif_hdu, irf)
  330 +
  331 + # Save and close CIF
  332 + cif.save(True)
  333 + cif.close()
  334 +
  335 + # Return
  336 + return
  337 +
  338 + def _add_irfs(self):
  339 + """
  340 + Add IRFs to calibration database
  341 + """
  342 + # Collect tarfiles
  343 + tarfiles = self._collect_tarfiles(self['indir'].filename())
  344 +
  345 + # Add IRFs in tarfiles
  346 + for file in tarfiles:
  347 +
  348 + # Extract Production
  349 + fname = os.path.basename(file)
  350 + elements = fname.split('-')
  351 + prod = [e for e in elements if 'prod' in e][0]
  352 + version = [e for e in elements if 'v' in e][0]
  353 + prod = '%s-%s' % (prod, version)
  354 +
  355 + # Build output directory
  356 + outdir = 'data/cta/%s/bcf' % (prod)
  357 +
  358 + # Write header and log parameters
  359 + self._log_header2(gammalib.TERSE, 'Extract IRFs')
  360 + self._log_value(gammalib.NORMAL, 'Tarfile', file)
  361 + self._log_value(gammalib.NORMAL, 'Production', prod)
  362 + self._log_value(gammalib.NORMAL, 'Target directory', outdir)
  363 +
  364 + # Extract IRFs from tarfile
  365 + irfs = self._extract_irfs(file, outdir)
  366 +
  367 + # Add IRFs to calibration index database
  368 + self._add_irfs_to_cif(prod, irfs)
  369 +
  370 + # Return
  371 + return
  372 +
  373 +
  374 + # Public methods
  375 + def run(self):
  376 + """
  377 + Run the script
  378 + """
  379 + # Switch screen logging on in debug mode
  380 + if self._logDebug():
  381 + self._log.cout(True)
  382 +
  383 + # Get parameters
  384 + self._get_parameters()
  385 +
  386 + # Write header
  387 + self._log_header1(gammalib.TERSE, 'Add IRFs to CALDB')
  388 +
  389 + # Add IRFs to CALDB
  390 + self._add_irfs()
  391 +
  392 + # Return
  393 + return
  394 +
  395 +
  396 +# ======================== #
  397 +# Main routine entry point #
  398 +# ======================== #
  399 +if __name__ == '__main__':
  400 +
  401 + # Create instance of application
  402 + app = csadd2caldb(sys.argv)
  403 +
  404 + # Execute application
  405 + app.execute()
... ...
doc/source/users/user_manual/irf_cta.rst
... ... @@ -89,23 +89,26 @@ In total, the following six instrument response functions are available:
89 89 provided by the CTA Consortium and Observatory.*
90 90  
91 91 The latest CTA reponse functions can be downloaded from
92   - `this link (get the FITS tarball under "Source Files") <https://www.cta-observatory.org/science/cta-performance/>`_. Assuming that the name of the FITS tarball is
93   - ``CTA-Performance-prod3b-v2-FITS.tar.gz`` (which corresponds to the
94   - prod3b-v2 response functions), you install the response functions by
95   - typing
  92 + `this link <https://zenodo.org/record/5499840/files/cta-prod5-zenodo-fitsonly-v0.1.zip?download=1>`_.
  93 +
  94 + This will download the file ``cta-prod5-zenodo-fitsonly-v0.1.zip`` in your
  95 + current working directory that you should unzip to get a folder named
  96 + ``cta-prod5-zenodo-fitsonly-v0``.
  97 +
  98 + Download the script :download:`csadd2caldb.py <csadd2caldb.py>` and execute
  99 + it as follows to add the IRFs to your calibration database:
96 100  
97 101 .. code-block:: bash
98 102  
99   - $ tar xvfz CTA-Performance-prod3b-v2-FITS.tar.gz -C $CTOOLS/share
  103 + $ ./csadd2caldb.py debug=yes
  104 + Parfile csadd2caldb.par not found. Create default parfile.
  105 + Input IRF folder [cta-prod5-zenodo-fitsonly-v0]
  106 + Output caldb folder [$CALDB]
100 107  
101   - or
102   -
103   - .. code-block:: bash
104   -
105   - $ sudo tar xvfz CTA-Performance-prod3b-v2-FITS.tar.gz -C $CTOOLS/share
106   -
107   - if you need root priviledges to install in your ``$CTOOLS`` directory.
108   - To use the prod3b-v2 response functions, specify ``prod3b-v2`` if a tool
  108 + Use ``sudo`` to run ``csadd2caldb.py`` if the installation of the IRFs requires
  109 + root privileges.
  110 +
  111 + To use the prod5-v0.1 functions, specify ``prod5-v0.1`` if a tool
109 112 or script queries for the calibration database.
110 113  
111 114  
... ...