ctools 2.1.0.dev
Loading...
Searching...
No Matches
csadd2caldb.py
Go to the documentation of this file.
1#! /usr/bin/env python
2# ==========================================================================
3# Add IRFs to CALDB
4#
5# Copyright (C) 2021-2022 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# ==========================================================================
21import os
22import sys
23import glob
24import tarfile
25import gammalib
26import ctools
27
28
29# ================= #
30# csadd2caldb class #
31# ================= #
32class csadd2caldb(ctools.cscript):
33 """
34 Add IRFs to CALDB
35 """
36
37 # Constructor
38 def __init__(self, *argv):
39 """
40 Constructor
41 """
42 # Initialise application by calling the base class constructor
43 self._init_cscript(self.__class__.__name__, ctools.__version__, argv)
44
45 # Return
46 return
47
48
49 # Private methods
50 def _get_parameters(self):
51 """
52 Get parameters from parfile
53 """
54 # Query parameters
55 self['indir'].filename()
56 self['outdir'].filename()
57
58 # Write input parameters into logger
59 self._log_parameters(gammalib.TERSE)
60
61 # Return
62 return
63
64 def _collect_tarfiles(self, indir):
65 """
66 Collect tarfiles
67
68 Parameters
69 ----------
70 indir : string
71 Input directory
72
73 Returns
74 -------
75 tarfiles : list of str
76 List of tarfiles
77 """
78 # Initialise list of tarfiles
79 tarfiles = []
80
81 # Search for tarfiles in current directory
82 files = glob.glob('%s/*.FITS.tar*' % indir.url())
83 tarfiles.extend(files)
84
85 # Search for tarfiles in 'fits' subdirectory
86 files = glob.glob('%s/fits/*.FITS.tar*' % indir.url())
87 tarfiles.extend(files)
88
89 # Return tarfiles
90 return tarfiles
91
92 def _extract_irfs(self, file, outdir):
93 """
94 Collect IRFs from tarfile
95
96 Parameters
97 ----------
98 file : str
99 Tarfile with IRFs
100 outdir : str
101 Output directory
102
103 Returns
104 -------
105 irfs : list
106 List of IRF dictionaries
107 """
108 # Initialise list of extracted IRFs
109 irfs = []
110
111 # Open tarfile
112 tar = tarfile.open(file)
113
114 # Loop over all members in tarfile
115 for member in tar.getmembers():
116
117 # Skip member names starting with a dot
118 if member.name[0] == '.':
119 continue
120
121 # Get subarray
122 subarrays = ''
123 nsubarrays = 0
124 if 'LST' in member.name:
125 subarrays += '_LST'
126 nsubarrays += 1
127 if 'MST' in member.name:
128 subarrays += '_MST'
129 nsubarrays += 1
130 if 'SST' in member.name:
131 subarrays += '_SST'
132 nsubarrays += 1
133 if nsubarrays > 1:
134 subarrays = ''
135
136 # Extract attributes
137 if 'South-' in member.name:
138 site = 'South'
139 else:
140 site = 'North'
141 if '180000s' in member.name:
142 duration = '50h'
143 elif '18000s' in member.name:
144 duration = '5h'
145 elif '1800s' in member.name:
146 duration = '0.5h'
147 if 'SouthAz' in member.name:
148 orientation = '_S'
149 azimuth = 180.0
150 elif 'NorthAz' in member.name:
151 orientation = '_N'
152 azimuth = 0.0
153 else:
154 orientation = ''
155 azimuth = 90.0
156 if 'D25' in member.name:
157 suffix = '_D25'
158 elif 'D27' in member.name:
159 suffix = '_D27'
160 else:
161 suffix = ''
162
163 elements = member.name.split('-')
164 zenith = [e for e in elements if 'deg' in e][0].strip('deg')
165 instrument = '%s%s_z%s%s_%s%s' % (site, suffix, zenith, orientation, duration, subarrays)
166
167 # Build output directory
168 path = '%s/%s' % (outdir, instrument)
169
170 # Write header
171 self._log_header3(gammalib.TERSE, 'Extract IRFs from "%s"' % member.name)
172 self._log_value(gammalib.NORMAL, 'Instrument', instrument)
173
174 # Extract file
175 tar.extract(member, path='%s/%s' % (self['outdir'].filename().url(), path))
176
177 # Create IRF record
178 record = {'path': path, 'instrument': instrument, 'file': member.name,
179 'site': site, 'duration': duration, 'zenith': zenith, 'azimuth': azimuth,
180 'orientation': orientation}
181
182 # Add record
183 irfs.append(record)
184
185 # Close tarfile
186 tar.close()
187
188 # Return
189 return irfs
190
191 def _add_irf_to_cif(self, prod, hdu, irf):
192 """
193 Add IRF to calibration index file HDU
194
195 Parameters
196 ----------
197 prod : str
198 Production
199 hdu : ~gammalib.GFitsBinTable
200 CIF HDU table
201 irf : dict
202 IRF dictionary
203 """
204 # Set list of IRF component names
205 #names = ['EA', 'PSF', 'EDISP', 'BKG']
206 components = [{'name': 'EA', 'CNAM': 'EFF_AREA', 'DESC': 'CTA effective area'},
207 {'name': 'PSF', 'CNAM': 'RPSF', 'DESC': 'CTA point spread function'},
208 {'name': 'EDISP', 'CNAM': 'EDISP', 'DESC': 'CTA energy dispersion'},
209 {'name': 'BKG', 'CNAM': 'BKG', 'DESC': 'CTA background'}]
210
211 # Initialise CIF row index
212 row = hdu.nrows()
213
214 # Append rows for all components to CIF extension
215 hdu.append_rows(len(components))
216
217 # Add information for all components
218 for component in components:
219
220 # Set calibration information
221 cal_name = 'NAME(%s)' % irf['instrument']
222 cal_version = 'VERSION(%s)' % prod
223 cal_cut = 'CLASS(BEST)'
224 cal_analysis = 'ANALYSIS(CTA)'
225 cal_zenith = 'ZENITH(%.3f)deg' % float(irf['zenith'])
226 cal_azimuth = 'AZIMUTH(%.3f)deg' % float(irf['azimuth'])
227 cal_bounds = [cal_name, cal_version, cal_cut, cal_analysis, \
228 cal_zenith, cal_azimuth]
229
230 # Set generic information
231 hdu['TELESCOP'][row] = 'CTA'
232 hdu['INSTRUME'][row] = gammalib.toupper(prod)
233 hdu['DETNAM'][row] = 'NONE'
234 hdu['FILTER'][row] = 'NONE'
235 hdu['CAL_DEV'][row] = 'ONLINE'
236 hdu['CAL_CLAS'][row] = 'BCF'
237 hdu['CAL_DTYP'][row] = 'DATA'
238 hdu['CAL_VSD'][row] = '2014-01-30'
239 hdu['CAL_VST'][row] = '00:00:00'
240 hdu['REF_TIME'][row] = 51544.0
241 hdu['CAL_QUAL'][row] = 0 # 0=good, 1=bad, 2=dubious, ...
242 hdu['CAL_DATE'][row] = '14/01/30'
243
244 # Set component specific information
245 hdu['CAL_DIR'][row] = irf['path']
246 hdu['CAL_FILE'][row] = irf['file']
247 hdu['CAL_CNAM'][row] = component['CNAM']
248 hdu['CAL_DESC'][row] = component['DESC']
249 hdu['CAL_XNO'][row] = 1
250 for i in range(9):
251 if i >= len(cal_bounds):
252 hdu['CAL_CBD'][row,i] = 'NONE'
253 else:
254 hdu['CAL_CBD'][row,i] = cal_bounds[i]
255
256 # Increment row index
257 row += 1
258
259 # Return
260 return
261
263 """
264 Create Calibration Database Index File binary table
265
266 Returns
267 -------
268 table : ~gammalib.GFitsBinTable
269 Calibration Database Index File binary table
270 """
271 # Create binary table
272 table = gammalib.GFitsBinTable()
273
274 # Append columns. Reference: CAL/GEN/92-008
275 table.append(gammalib.GFitsTableStringCol('TELESCOP', 0, 10))
276 table.append(gammalib.GFitsTableStringCol('INSTRUME', 0, 10))
277 table.append(gammalib.GFitsTableStringCol('DETNAM', 0, 20))
278 table.append(gammalib.GFitsTableStringCol('FILTER', 0, 10))
279 table.append(gammalib.GFitsTableStringCol('CAL_DEV', 0, 20))
280 table.append(gammalib.GFitsTableStringCol('CAL_DIR', 0, 70))
281 table.append(gammalib.GFitsTableStringCol('CAL_FILE', 0, 128)) # Extend beyond standard
282 table.append(gammalib.GFitsTableStringCol('CAL_CLAS', 0, 3))
283 table.append(gammalib.GFitsTableStringCol('CAL_DTYP', 0, 4))
284 table.append(gammalib.GFitsTableStringCol('CAL_CNAM', 0, 20))
285 table.append(gammalib.GFitsTableStringCol('CAL_CBD', 0, 70, 9))
286 table.append(gammalib.GFitsTableShortCol('CAL_XNO', 0))
287 table.append(gammalib.GFitsTableStringCol('CAL_VSD', 0, 10))
288 table.append(gammalib.GFitsTableStringCol('CAL_VST', 0, 8))
289 table.append(gammalib.GFitsTableDoubleCol('REF_TIME', 0))
290 table.append(gammalib.GFitsTableShortCol('CAL_QUAL', 0))
291 table.append(gammalib.GFitsTableStringCol('CAL_DATE', 0, 8))
292 table.append(gammalib.GFitsTableStringCol('CAL_DESC', 0, 70))
293
294 # Set keywords. Reference: CAL/GEN/92-008
295 table.extname('CIF')
296 table.card('CIFVERSN', '1992a', 'Version of CIF format')
297
298 # Return table
299 return table
300
301 def _add_irfs_to_cif(self, prod, irfs):
302 """
303 Add IRFs to calibration index file
304
305 Parameters
306 ----------
307 prod : str
308 Production
309 irfs : list
310 List of IRFs
311 """
312 # Build calibration database index name
313 fname = '%s/data/cta/%s/caldb.indx' % (self['outdir'].filename().url(), prod)
314
315 # Open calibration database index file
316 cif = gammalib.GFits(fname, True)
317
318 # If file has no CIF extension than create it now
319 if not cif.contains('CIF'):
320 cif.append(self._create_cif_table())
321
322 # Get calibration database index HDU
323 cif_hdu = cif.table('CIF')
324
325 # Loop over all IRFs and add information to CIF
326 for irf in irfs:
327 self._add_irf_to_cif(prod, cif_hdu, irf)
328
329 # Save and close CIF
330 cif.save(True)
331 cif.close()
332
333 # Return
334 return
335
336 def _add_irfs(self):
337 """
338 Add IRFs to calibration database
339 """
340 # Collect tarfiles
341 tarfiles = self._collect_tarfiles(self['indir'].filename())
342
343 # Raise an exception if no tarfiles were found
344 if len(tarfiles) == 0:
345 msg = ('No tarfiles found in folder "'+self['indir'].filename().url()+'. '
346 'Please specify a folder that contains IRF tarfiles.')
347 raise RuntimeError(msg)
348
349 # Add IRFs in tarfiles
350 for file in tarfiles:
351
352 # Extract Production
353 fname = os.path.basename(file)
354 elements = fname.split('-')
355 prod = [e for e in elements if 'prod' in e][0]
356 version = [e for e in elements if 'v' in e][0]
357 prod = '%s-%s' % (prod, version)
358
359 # Build output directory
360 outdir = 'data/cta/%s/bcf' % (prod)
361
362 # Write header and log parameters
363 self._log_header2(gammalib.TERSE, 'Extract IRFs')
364 self._log_value(gammalib.NORMAL, 'Tarfile', file)
365 self._log_value(gammalib.NORMAL, 'Production', prod)
366 self._log_value(gammalib.NORMAL, 'Target directory', outdir)
367
368 # Extract IRFs from tarfile
369 irfs = self._extract_irfs(file, outdir)
370
371 # Add IRFs to calibration index database
372 self._add_irfs_to_cif(prod, irfs)
373
374 # Return
375 return
376
377
378 # Public methods
379 def process(self):
380 """
381 Process the script
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# ======================== #
399if __name__ == '__main__':
400
401 # Create instance of application
402 app = csadd2caldb(sys.argv)
403
404 # Execute application
405 app.execute()
_add_irfs_to_cif(self, prod, irfs)
_add_irf_to_cif(self, prod, hdu, irf)
_extract_irfs(self, file, outdir)